diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..d13c6abb6 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +**/*.js +/out +/test \ No newline at end of file diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 000000000..b29217be7 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,45 @@ +{ + "env": { + "browser": true, + "es6": true, + "node": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/eslint-recommended" + ], + "globals": { + "Atomics": "readonly", + "SharedArrayBuffer": "readonly" + }, + "parserOptions": { + "ecmaVersion": 2018, + "sourceType": "module", + "project": "./tsconfig.json" + }, + "parser": "@typescript-eslint/parser", + "rules": { + "@typescript-eslint/indent": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-namespace": [ + "error", + { + "allowDeclarations": true + } + ], + "@typescript-eslint/no-non-null-assertion": "off", + "arrow-body-style": [ + "error", + "as-needed" + ], + "curly": "error", + "eqeqeq": "error", + "no-duplicate-imports": "error", + "no-else-return": "error", + "no-multi-spaces": "error", + "no-redeclare": "error", + "no-useless-escape": "off", + "prefer-template": "error" + } +} \ No newline at end of file diff --git a/.github/workflows/backcompat.yml b/.github/workflows/backcompat.yml index 6e09776ae..5456dd3da 100644 --- a/.github/workflows/backcompat.yml +++ b/.github/workflows/backcompat.yml @@ -2,9 +2,9 @@ name: Back Compatibility CI on: push: - branches: [ develop, master ] + branches: [ develop ] pull_request: - branches: [ develop, master ] + branches: [ develop ] jobs: backcompat: @@ -21,5 +21,5 @@ jobs: uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - - run: npm ci + - run: npm i - run: npm run backcompattest diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index a5cb320f8..3df5888d2 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -2,9 +2,9 @@ name: Integration Tests CI on: push: - branches: [ develop, master ] + branches: [ develop, beta ] pull_request: - branches: [ develop, master ] + branches: [ develop, beta ] jobs: integration: @@ -13,13 +13,15 @@ jobs: strategy: matrix: - node-version: [10.x, 12.x] + # TODO: Enable Node 14.x when we update the pipeline to support AbortController + node-version: [16.x] steps: - uses: actions/checkout@v2 + - run: openssl req -x509 -nodes -newkey rsa -keyout ./test/certs/server-key.pem -out ./test/certs/server-cert.pem -days 1 -subj "/C=CL/ST=RM/L=OpenTelemetryTest/O=Root/OU=Test/CN=ca" - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - - run: npm ci + - run: npm i - run: npm run functionaltest diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index ee69a266e..66e6a6246 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -2,9 +2,9 @@ name: Node.js CI on: push: - branches: [ develop, master ] + branches: [ develop, beta ] pull_request: - branches: [ develop, master ] + branches: [ develop, beta ] jobs: build: @@ -14,14 +14,18 @@ jobs: strategy: matrix: os: [ubuntu-latest] - node-version: [8.x, 10.x, 12.x, 14.x] + # TODO: Enable Node 14.x when we update the pipeline to support AbortController + node-version: [16.x, 18.x] steps: - uses: actions/checkout@v2 + - run: openssl req -x509 -nodes -newkey rsa -keyout ./test/certs/server-key.pem -out ./test/certs/server-cert.pem -days 1 -subj "/C=CL/ST=RM/L=OpenTelemetryTest/O=Root/OU=Test/CN=ca" - name: (${{ matrix.os }}) on Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - - run: npm ci + - run: npm run clean + - run: npm i - run: npm run build --if-present - - run: npm test + - run: npm run lint + - run: npm test \ No newline at end of file diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 3b4a39878..f8eab1ce0 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -29,7 +29,7 @@ jobs: with: node-version: 12 registry-url: https://registry.npmjs.org/ - - run: npm ci + - run: npm i - run: npm publish env: NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} diff --git a/.gitignore b/.gitignore index 1c744c759..5ba2b4e68 100644 --- a/.gitignore +++ b/.gitignore @@ -8,12 +8,12 @@ obj/ # Ignore Node files node_modules/** -Tests/BackCompatibility/OldTSC/node_modules/** -Tests/BackCompatibility/Node10Types/node_modules/** -Tests/FunctionalTests/Runner/node_modules/** -Tests/FunctionalTests/TestApp/node_modules/** -Tests/FunctionalTests/Runner/package-lock.json -Tests/FunctionalTests/TestApp/package-lock.json +test/backCompatibility/oldTSC/node_modules/** +test/backCompatibility/node10Types/node_modules/** +test/functionalTests/runner/node_modules/** +test/functionalTests/testApp/node_modules/** +test/functionalTests/runner/package-lock.json +test/functionalTests/testApp/package-lock.json # Ignore Visual Studio files *.suo @@ -32,3 +32,11 @@ Tests/FunctionalTests/TestApp/package-lock.json # Ignore log files npm-debug.log undefined/temp/appInsights-node/applicationinsights.log + + +.nyc_output/* +test/certs/server-key.pem +# Ignore folder created during test runs +undefined/* +node_modules +dist-esm \ No newline at end of file diff --git a/.npmignore b/.npmignore index 30157b06a..f6d664da8 100644 --- a/.npmignore +++ b/.npmignore @@ -9,8 +9,10 @@ obj .vscode/ .github +.nyc_output + # Ignore test files -Tests +test # Don't publish source files that aren't needed in package *.ts @@ -19,7 +21,8 @@ Tests .travis.yml .ntvs_analysis.dat -/Declarations + +/declarations Schema diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..505c675a5 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +**/src/declarations/generated/ +**/*.d.ts diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 000000000..84d6f7b54 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,9 @@ +{ + "arrowParens": "always", + "bracketSpacing": true, + "endOfLine": "lf", + "printWidth": 100, + "semi": true, + "singleQuote": false, + "tabWidth": 4 +} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 476904e41..000000000 --- a/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -sudo: required -language: node_js -services: - - docker -env: - - TRAVIS_EXTENDED_METRICS=true - - TRAVIS_EXTENDED_METRICS=false -node_js: - - "node" - - "13" - - "12" - - "10" - - "8" -before_install: - - npm install -g npm - # Simulate optionalDevDependency for applicationinsights-native-metrics - - npm uninstall applicationinsights-native-metrics --save-dev - - if [ "$TRAVIS_EXTENDED_METRICS" = "true" ]; then npm install applicationinsights-native-metrics --save-optional || true; fi -script: - - npm clean - # Build and test - - npm test - # Run funcs for LTS releases (currently 6, 8, 10) + current release (12) - - if [ "$TRAVIS_NODE_VERSION" = "12" ]; then npm run functionaltest; fi - - if [ "$TRAVIS_NODE_VERSION" = "10" ]; then npm run functionaltest; fi - - if [ "$TRAVIS_NODE_VERSION" = "8" ]; then npm run functionaltest; fi - - # Only run backcompat tests once - - if [ "$TRAVIS_NODE_VERSION" = "8" ] && [ "$TRAVIS_EXTENDED_METRICS" != "true" ]; then npm run backcompattest; fi diff --git a/AutoCollection/AsyncHooksScopeManager.ts b/AutoCollection/AsyncHooksScopeManager.ts deleted file mode 100644 index 6be8a2ea8..000000000 --- a/AutoCollection/AsyncHooksScopeManager.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { SpanContext } from "@opentelemetry/api"; -import { Span } from "@opentelemetry/sdk-trace-base"; - -import { CorrelationContextManager, CorrelationContext } from "./CorrelationContextManager" -import { EventEmitter } from "events"; - -export class OpenTelemetryScopeManagerWrapper { - private _activeSymbol: symbol | undefined; - - public active() { - const context = CorrelationContextManager.getCurrentContext() as any; - return { - ...context, - getValue: (key: symbol) => { - // todo: lazy import activeSymbol from opentelemetry/api - if (!this._activeSymbol) { - this._activeSymbol = key; - return context; - } - - if (key === this._activeSymbol) { - return context; - } - return false; - }, - setValue: () => { } - }; - } - - public with(span: Span, fn: () => any) { - const parentSpanId = span.parentSpanId; - const name = span.name; - const correlationContext = OpenTelemetryScopeManagerWrapper._spanToContext(span, parentSpanId, name); - return CorrelationContextManager.runWithContext(correlationContext, fn)(); - } - - public bind(target: T): T { - if (typeof target === "function") { - return CorrelationContextManager.wrapCallback(target); - } else if (target instanceof EventEmitter) { - CorrelationContextManager.wrapEmitter(target); - } - return target; - } - - public enable(): this { - CorrelationContextManager.enable(); - return this; - } - - public disable(): this { - CorrelationContextManager.disable(); - return this; - } - - private static _spanToContext(span: Span, parentSpanId?: string, name?: string): CorrelationContext { - const spanContext = span.spanContext ? span.spanContext() : (span).context(); // context is available in OT API void}; - - public static INSTANCE: AutoCollectConsole; - private static _methodNames = ["debug", "info", "log", "warn", "error"]; - - private _client: TelemetryClient; - private _isInitialized: boolean; - - constructor(client: TelemetryClient) { - if(!!AutoCollectConsole.INSTANCE) { - throw new Error("Console logging adapter tracking should be configured from the applicationInsights object"); - } - - this._client = client; - AutoCollectConsole.INSTANCE = this; - } - - public enable(isEnabled: boolean, collectConsoleLog: boolean) { - if (DiagChannel.IsInitialized) { - require("./diagnostic-channel/console.sub").enable(isEnabled && collectConsoleLog, this._client); - require("./diagnostic-channel/bunyan.sub").enable(isEnabled, this._client); - require("./diagnostic-channel/winston.sub").enable(isEnabled, this._client); - } - } - - public isInitialized() { - return this._isInitialized; - } - - public dispose() { - AutoCollectConsole.INSTANCE = null; - this.enable(false, false); - } -} - -export = AutoCollectConsole; diff --git a/AutoCollection/CorrelationContextManager.ts b/AutoCollection/CorrelationContextManager.ts deleted file mode 100644 index 226e7b36b..000000000 --- a/AutoCollection/CorrelationContextManager.ts +++ /dev/null @@ -1,355 +0,0 @@ -import events = require("events"); -import Logging = require("../Library/Logging"); - -import * as DiagChannel from "./diagnostic-channel/initialization"; -import * as azureFunctionsTypes from "../Library/Functions"; - -// Don't reference modules from these directly. Use only for types. -import * as cls from "cls-hooked"; -import * as http from "http"; -import Traceparent = require("../Library/Traceparent"); -import Tracestate = require("../Library/Tracestate"); -import HttpRequestParser = require("./HttpRequestParser"); -import { SpanContext } from "@opentelemetry/api"; -import { Span } from "@opentelemetry/sdk-trace-base"; - -export interface CustomProperties { - /** - * Get a custom property from the correlation context - */ - getProperty(key: string): string; - /** - * Store a custom property in the correlation context. - * Do not store sensitive information here. - * Properties stored here are exposed via outgoing HTTP headers for correlating data cross-component. - * The characters ',' and '=' are disallowed within keys or values. - */ - setProperty(key: string, value: string): void; -} - -export interface PrivateCustomProperties extends CustomProperties { - addHeaderData(header: string): void; - serializeToHeader(): string; -} - -export interface CorrelationContext { - operation: { - name: string; - id: string; - parentId: string; // Always used for dependencies, may be ignored in favor of incoming headers for requests - traceparent?: Traceparent; // w3c context trace - tracestate?: Tracestate; // w3c context state - }; - - /** Do not store sensitive information here. - * Properties here are exposed via outgoing HTTP headers for correlating data cross-component. - */ - customProperties: CustomProperties -} - -export class CorrelationContextManager { - private static enabled: boolean = false; - private static hasEverEnabled: boolean = false; - private static forceClsHooked: boolean = undefined; // true: use cls-hooked, false: use cls, undefined: choose based on node version - private static session: cls.Namespace; - private static cls: typeof cls; - private static CONTEXT_NAME = "ApplicationInsights-Context"; - - /** - * Provides the current Context. - * The context is the most recent one entered into for the current - * logical chain of execution, including across asynchronous calls. - */ - public static getCurrentContext(): CorrelationContext | null { - if (!CorrelationContextManager.enabled) { - return null; - } - const context = CorrelationContextManager.session.get(CorrelationContextManager.CONTEXT_NAME); - - if (context === undefined) { // cast undefined to null - return null; - } - return context; - } - - /** - * A helper to generate objects conforming to the CorrelationContext interface - */ - public static generateContextObject(operationId: string, parentId?: string, operationName?: string, correlationContextHeader?: string, traceparent?: Traceparent, tracestate?: Tracestate): CorrelationContext { - parentId = parentId || operationId; - - if (this.enabled) { - return { - operation: { - name: operationName, - id: operationId, - parentId: parentId, - traceparent, - tracestate - }, - customProperties: new CustomPropertiesImpl(correlationContextHeader) - }; - } - - return null; - } - - public static spanToContextObject(spanContext: SpanContext, parentId?: string, name?: string): CorrelationContext { - const traceContext = new Traceparent(); - traceContext.traceId = spanContext.traceId; - traceContext.spanId = spanContext.spanId; - traceContext.traceFlag = Traceparent.formatOpenTelemetryTraceFlags(spanContext.traceFlags) || Traceparent.DEFAULT_TRACE_FLAG; - traceContext.parentId = parentId; - return CorrelationContextManager.generateContextObject(traceContext.traceId, traceContext.parentId, name, null, traceContext); - } - - /** - * Runs a function inside a given Context. - * All logical children of the execution path that entered this Context - * will receive this Context object on calls to GetCurrentContext. - */ - public static runWithContext(context: CorrelationContext, fn: () => any): any { - if (CorrelationContextManager.enabled) { - return CorrelationContextManager.session.bind(fn, { [CorrelationContextManager.CONTEXT_NAME]: context })(); - } else { - return fn(); - } - } - - /** - * Wrapper for cls-hooked bindEmitter method - */ - public static wrapEmitter(emitter: events.EventEmitter): void { - if (CorrelationContextManager.enabled) { - CorrelationContextManager.session.bindEmitter(emitter); - } - } - - /** - * Patches a callback to restore the correct Context when getCurrentContext - * is run within it. This is necessary if automatic correlation fails to work - * with user-included libraries. - * - * The supplied callback will be given the same context that was present for - * the call to wrapCallback. */ - public static wrapCallback(fn: T, context?: CorrelationContext): T { - if (CorrelationContextManager.enabled) { - return CorrelationContextManager.session.bind(fn, context ? { - [CorrelationContextManager.CONTEXT_NAME]: context - } : undefined); - } - return fn; - } - - /** - * Enables the CorrelationContextManager. - */ - public static enable(forceClsHooked?: boolean) { - if (this.enabled) { - return; - } - - if (!this.isNodeVersionCompatible()) { - this.enabled = false; - return; - } - if (!CorrelationContextManager.hasEverEnabled) { - this.forceClsHooked = forceClsHooked; - this.hasEverEnabled = true; - - if (typeof this.cls === "undefined") { - if ((CorrelationContextManager.forceClsHooked === true) || (CorrelationContextManager.forceClsHooked === undefined && CorrelationContextManager.shouldUseClsHooked())) { - this.cls = require('cls-hooked'); - } else { - this.cls = require('continuation-local-storage'); - } - } - - CorrelationContextManager.session = this.cls.createNamespace("AI-CLS-Session"); - - DiagChannel.registerContextPreservation((cb) => { - return CorrelationContextManager.session.bind(cb); - }); - } - - this.enabled = true; - } - - /** - * Create new correlation context. - */ - public static startOperation( - input: azureFunctionsTypes.Context | (http.IncomingMessage | azureFunctionsTypes.HttpRequest) | SpanContext | Span, - request?: azureFunctionsTypes.HttpRequest | string) - : CorrelationContext | null { - const traceContext = input && (input as azureFunctionsTypes.Context).traceContext || null; - const span = input && (input as Span).spanContext ? input as Span : null; - const spanContext = input && (input as SpanContext).traceId ? input as SpanContext : null; - const headers = input && (input as http.IncomingMessage | azureFunctionsTypes.HttpRequest).headers; - - // OpenTelemetry Span - if (span) { - return this.spanToContextObject(span.spanContext(), span.parentSpanId, span.name); - } - - // OpenTelemetry SpanContext - if (spanContext) { - return this.spanToContextObject(spanContext, `|${spanContext.traceId}.${spanContext.spanId}.`, typeof request === "string" ? request : ""); - } - - // AzFunction TraceContext - if (traceContext) { - let traceparent = null; - let tracestate = null; - if ((request as azureFunctionsTypes.HttpRequest).headers) { - if ((request as azureFunctionsTypes.HttpRequest).headers.traceparent) { - traceparent = new Traceparent((request as azureFunctionsTypes.HttpRequest).headers.traceparent); - } else if ((request as azureFunctionsTypes.HttpRequest).headers["request-id"]) { - traceparent = new Traceparent(null, (request as azureFunctionsTypes.HttpRequest).headers["request-id"]); - } - if ((request as azureFunctionsTypes.HttpRequest).headers.tracestate) { - tracestate = new Tracestate((request as azureFunctionsTypes.HttpRequest).headers.tracestate); - } - } - if (!traceparent) { - traceparent = new Traceparent(traceContext.traceparent); - } - if (!tracestate) { - tracestate = new Tracestate(traceContext.tracestate); - } - const parser = typeof request === "object" - ? new HttpRequestParser(request) - : null; - const correlationContext = CorrelationContextManager.generateContextObject( - traceparent.traceId, - traceparent.parentId, - typeof request === "string" - ? request - : parser.getOperationName({}), - parser && parser.getCorrelationContextHeader() || undefined, - traceparent, - tracestate, - ); - - return correlationContext; - } - - // No TraceContext available, parse as http.IncomingMessage - if (headers) { - const traceparent = new Traceparent(headers.traceparent ? headers.traceparent.toString() : null); - const tracestate = new Tracestate(headers.tracestate ? headers.tracestate.toString() : null); - const parser = new HttpRequestParser(input as http.IncomingMessage | azureFunctionsTypes.HttpRequest); - const correlationContext = CorrelationContextManager.generateContextObject( - traceparent.traceId, - traceparent.parentId, - parser.getOperationName({}), - parser.getCorrelationContextHeader(), - traceparent, - tracestate, - ); - - return correlationContext; - } - - Logging.warn("startOperation was called with invalid arguments", arguments); - return null; - } - - /** - * Disables the CorrelationContextManager. - */ - public static disable() { - this.enabled = false; - } - - /** - * Reset the namespace - */ - public static reset() { - if (CorrelationContextManager.hasEverEnabled) { - CorrelationContextManager.session = null; - CorrelationContextManager.session = this.cls.createNamespace('AI-CLS-Session'); - } - } - - /** - * Reports if CorrelationContextManager is able to run in this environment - */ - public static isNodeVersionCompatible() { - var nodeVer = process.versions.node.split("."); - return parseInt(nodeVer[0]) > 3 || (parseInt(nodeVer[0]) > 2 && parseInt(nodeVer[1]) > 2); - - } - - /** - * We only want to use cls-hooked when it uses async_hooks api (8.2+), else - * use async-listener (plain -cls) - */ - public static shouldUseClsHooked() { - var nodeVer = process.versions.node.split("."); - return (parseInt(nodeVer[0]) > 8) || (parseInt(nodeVer[0]) >= 8 && parseInt(nodeVer[1]) >= 2); - } - - /** - * A TypeError is triggered by cls-hooked for node [8.0, 8.2) - * @internal Used in tests only - */ - public static canUseClsHooked() { - var nodeVer = process.versions.node.split("."); - var greater800 = (parseInt(nodeVer[0]) > 8) || (parseInt(nodeVer[0]) >= 8 && parseInt(nodeVer[1]) >= 0); - var less820 = (parseInt(nodeVer[0]) < 8) || (parseInt(nodeVer[0]) <= 8 && parseInt(nodeVer[1]) < 2) - var greater470 = parseInt(nodeVer[0]) > 4 || (parseInt(nodeVer[0]) >= 4 && parseInt(nodeVer[1]) >= 7) // cls-hooked requires node 4.7+ - return !(greater800 && less820) && greater470; - } -} - -class CustomPropertiesImpl implements PrivateCustomProperties { - private static bannedCharacters = /[,=]/; - private props: { key: string, value: string }[] = []; - - public constructor(header: string) { - this.addHeaderData(header); - } - - public addHeaderData(header?: string) { - const keyvals = header ? header.split(", ") : []; - this.props = keyvals.map((keyval) => { - const parts = keyval.split("="); - return { key: parts[0], value: parts[1] }; - }).concat(this.props); - } - - public serializeToHeader() { - return this.props.map((keyval) => { - return `${keyval.key}=${keyval.value}` - }).join(", "); - } - - public getProperty(prop: string) { - for (let i = 0; i < this.props.length; ++i) { - const keyval = this.props[i] - if (keyval.key === prop) { - return keyval.value; - } - } - return; - } - - // TODO: Strictly according to the spec, properties which are recieved from - // an incoming request should be left untouched, while we may add our own new - // properties. The logic here will need to change to track that. - public setProperty(prop: string, val: string) { - if (CustomPropertiesImpl.bannedCharacters.test(prop) || CustomPropertiesImpl.bannedCharacters.test(val)) { - Logging.warn("Correlation context property keys and values must not contain ',' or '='. setProperty was called with key: " + prop + " and value: " + val); - return; - } - for (let i = 0; i < this.props.length; ++i) { - const keyval = this.props[i]; - if (keyval.key === prop) { - keyval.value = val; - return; - } - } - this.props.push({ key: prop, value: val }); - } -} \ No newline at end of file diff --git a/AutoCollection/Exceptions.ts b/AutoCollection/Exceptions.ts deleted file mode 100644 index 5962ddef5..000000000 --- a/AutoCollection/Exceptions.ts +++ /dev/null @@ -1,95 +0,0 @@ -import Contracts = require("../Declarations/Contracts"); -import TelemetryClient = require("../Library/TelemetryClient"); - - -class AutoCollectExceptions { - - public static INSTANCE: AutoCollectExceptions = null; - public static UNCAUGHT_EXCEPTION_MONITOR_HANDLER_NAME = "uncaughtExceptionMonitor"; - public static UNCAUGHT_EXCEPTION_HANDLER_NAME = "uncaughtException"; - public static UNHANDLED_REJECTION_HANDLER_NAME = "unhandledRejection"; - - private static _RETHROW_EXIT_MESSAGE = "Application Insights Rethrow Exception Handler"; - private static _FALLBACK_ERROR_MESSAGE = "A promise was rejected without providing an error. Application Insights generated this error stack for you."; - private static _canUseUncaughtExceptionMonitor = false; - private _exceptionListenerHandle: (reThrow: boolean, error: Error) => void; - private _rejectionListenerHandle: (reThrow: boolean, error: Error) => void; - private _client: TelemetryClient; - private _isInitialized: boolean; - - constructor(client: TelemetryClient) { - if (!!AutoCollectExceptions.INSTANCE) { - throw new Error("Exception tracking should be configured from the applicationInsights object"); - } - - AutoCollectExceptions.INSTANCE = this; - this._client = client; - - // Only use for 13.7.0+ - const nodeVer = process.versions.node.split("."); - AutoCollectExceptions._canUseUncaughtExceptionMonitor = parseInt(nodeVer[0]) > 13 || (parseInt(nodeVer[0]) === 13 && parseInt(nodeVer[1]) >= 7); - } - - public isInitialized() { - return this._isInitialized; - } - - public enable(isEnabled: boolean) { - if (isEnabled) { - this._isInitialized = true; - var self = this; - if (!this._exceptionListenerHandle) { - // For scenarios like Promise.reject(), an error won't be passed to the handle. Create a placeholder - // error for these scenarios. - var handle = (reThrow: boolean, name: string, error: Error = new Error(AutoCollectExceptions._FALLBACK_ERROR_MESSAGE)) => { - let exceptionTelemetry: Contracts.ExceptionTelemetry = { exception: error }; - // Add full error in context so it could used in telemetryProcessors - exceptionTelemetry.contextObjects = {}; - exceptionTelemetry.contextObjects["Error"] = error; - this._client.trackException(exceptionTelemetry); - this._client.flush({ isAppCrashing: true }); - // only rethrow when we are the only listener - if (reThrow && name && (process).listeners(name).length === 1) { - console.error(error); - process.exit(1); - } - }; - - if (AutoCollectExceptions._canUseUncaughtExceptionMonitor) { - // Node.js >= 13.7.0, use uncaughtExceptionMonitor. It handles both promises and exceptions - this._exceptionListenerHandle = handle.bind(this, false, undefined); // never rethrows - (process).on(AutoCollectExceptions.UNCAUGHT_EXCEPTION_MONITOR_HANDLER_NAME, this._exceptionListenerHandle); - } else { - this._exceptionListenerHandle = handle.bind(this, true, AutoCollectExceptions.UNCAUGHT_EXCEPTION_HANDLER_NAME); - this._rejectionListenerHandle = handle.bind(this, false, undefined); // never rethrows - (process).on(AutoCollectExceptions.UNCAUGHT_EXCEPTION_HANDLER_NAME, this._exceptionListenerHandle); - (process).on(AutoCollectExceptions.UNHANDLED_REJECTION_HANDLER_NAME, this._rejectionListenerHandle); - } - } - - } else { - if (this._exceptionListenerHandle) { - if (AutoCollectExceptions._canUseUncaughtExceptionMonitor) { - process.removeListener(AutoCollectExceptions.UNCAUGHT_EXCEPTION_MONITOR_HANDLER_NAME, this._exceptionListenerHandle); - } else { - process.removeListener(AutoCollectExceptions.UNCAUGHT_EXCEPTION_HANDLER_NAME, this._exceptionListenerHandle); - process.removeListener(AutoCollectExceptions.UNHANDLED_REJECTION_HANDLER_NAME, this._rejectionListenerHandle); - } - this._exceptionListenerHandle = undefined; - this._rejectionListenerHandle = undefined; - delete this._exceptionListenerHandle; - delete this._rejectionListenerHandle; - } - } - } - - public dispose() { - AutoCollectExceptions.INSTANCE = null; - this.enable(false); - this._isInitialized = false; - } -} - - - -export = AutoCollectExceptions; diff --git a/AutoCollection/HeartBeat.ts b/AutoCollection/HeartBeat.ts deleted file mode 100644 index f02c17ec0..000000000 --- a/AutoCollection/HeartBeat.ts +++ /dev/null @@ -1,94 +0,0 @@ -import os = require("os"); -import Vm = require("../Library/AzureVirtualMachine"); -import TelemetryClient = require("../Library/TelemetryClient"); -import Constants = require("../Declarations/Constants"); -import Config = require("../Library/Config"); -import Context = require("../Library/Context"); - -class HeartBeat { - - public static INSTANCE: HeartBeat; - - private _collectionInterval: number = 900000; - private _client: TelemetryClient; - private _handle: NodeJS.Timer | null; - private _isEnabled: boolean; - private _isInitialized: boolean; - private _isVM: boolean; - - constructor(client: TelemetryClient) { - if (!HeartBeat.INSTANCE) { - HeartBeat.INSTANCE = this; - } - this._isInitialized = false; - this._client = client; - } - - public enable(isEnabled: boolean) { - this._isEnabled = isEnabled; - if (this._isEnabled && !this._isInitialized) { - this._isInitialized = true; - } - - if (isEnabled) { - if (!this._handle) { - this._handle = setInterval(() => this.trackHeartBeat(this._client.config, () => { }), this._collectionInterval); - this._handle.unref(); // Allow the app to terminate even while this loop is going on - } - } else { - if (this._handle) { - clearInterval(this._handle); - this._handle = null; - } - } - } - - public isInitialized() { - return this._isInitialized; - } - - public static isEnabled() { - return HeartBeat.INSTANCE && HeartBeat.INSTANCE._isEnabled; - } - - public trackHeartBeat(config: Config, callback: () => void) { - let waiting: boolean = false; - let properties: { [key: string]: string } = {}; - const sdkVersion = Context.sdkVersion; // "node" or "node-nativeperf" - properties["sdk"] = sdkVersion; - properties["osType"] = os.type(); - if (process.env.WEBSITE_SITE_NAME) { // Web apps - properties["appSrv_SiteName"] = process.env.WEBSITE_SITE_NAME || ""; - properties["appSrv_wsStamp"] = process.env.WEBSITE_HOME_STAMPNAME || ""; - properties["appSrv_wsHost"] = process.env.WEBSITE_HOSTNAME || ""; - } else if (process.env.FUNCTIONS_WORKER_RUNTIME) { // Function apps - properties["azfunction_appId"] = process.env.WEBSITE_HOSTNAME; - } else if (config) { - if (this._isVM === undefined) { - waiting = true; - Vm.AzureVirtualMachine.getAzureComputeMetadata(config, (vmInfo) => { - this._isVM = vmInfo.isVM; - if (this._isVM) { - properties["azInst_vmId"] = vmInfo.id; - properties["azInst_subscriptionId"] = vmInfo.subscriptionId; - properties["azInst_osType"] = vmInfo.osType; - } - this._client.trackMetric({ name: Constants.HeartBeatMetricName, value: 0, properties: properties }); - callback(); - }); - } - } - if (!waiting) { - this._client.trackMetric({ name: Constants.HeartBeatMetricName, value: 0, properties: properties }); - callback(); - } - } - - public dispose() { - HeartBeat.INSTANCE = null; - this.enable(false); - this._isInitialized = false; - } -} - -export = HeartBeat; diff --git a/AutoCollection/HttpDependencies.ts b/AutoCollection/HttpDependencies.ts deleted file mode 100644 index 56b90e69e..000000000 --- a/AutoCollection/HttpDependencies.ts +++ /dev/null @@ -1,253 +0,0 @@ -import http = require("http"); -import https = require("https"); -import url = require("url"); - -import Contracts = require("../Declarations/Contracts"); -import TelemetryClient = require("../Library/TelemetryClient"); -import Logging = require("../Library/Logging"); -import Util = require("../Library/Util"); -import RequestResponseHeaders = require("../Library/RequestResponseHeaders"); -import HttpDependencyParser = require("./HttpDependencyParser"); -import { CorrelationContextManager, CorrelationContext, PrivateCustomProperties } from "./CorrelationContextManager"; -import CorrelationIdManager = require("../Library/CorrelationIdManager"); -import Traceparent = require("../Library/Traceparent"); - -import * as DiagChannel from "./diagnostic-channel/initialization"; - -class AutoCollectHttpDependencies { - public static disableCollectionRequestOption = 'disableAppInsightsAutoCollection'; - - public static INSTANCE: AutoCollectHttpDependencies; - - private static requestNumber = 1; - private static alreadyAutoCollectedFlag = '_appInsightsAutoCollected'; - - private _client: TelemetryClient; - private _isEnabled: boolean; - private _isInitialized: boolean; - - constructor(client: TelemetryClient) { - if (!!AutoCollectHttpDependencies.INSTANCE) { - throw new Error("Client request tracking should be configured from the applicationInsights object"); - } - - AutoCollectHttpDependencies.INSTANCE = this; - this._client = client; - } - - public enable(isEnabled: boolean) { - this._isEnabled = isEnabled; - if (this._isEnabled && !this._isInitialized) { - this._initialize(); - } - if (DiagChannel.IsInitialized) { - require("./diagnostic-channel/azure-coretracing.sub").enable(true, this._client); - require("./diagnostic-channel/mongodb.sub").enable(isEnabled, this._client); - require("./diagnostic-channel/mysql.sub").enable(isEnabled, this._client); - require("./diagnostic-channel/redis.sub").enable(isEnabled, this._client); - require("./diagnostic-channel/postgres.sub").enable(isEnabled, this._client); - } - } - - public isInitialized() { - return this._isInitialized; - } - - private _initialize() { - this._isInitialized = true; - - const originalRequest = http.request; - const originalHttpsRequest = https.request; - - const clientRequestPatch = (request: http.ClientRequest, options: string | URL | http.RequestOptions | https.RequestOptions) => { - var shouldCollect = !(options)[AutoCollectHttpDependencies.disableCollectionRequestOption] && - !(request)[AutoCollectHttpDependencies.alreadyAutoCollectedFlag]; - - // If someone else patched traceparent headers onto this request - if ((options).headers && (options).headers['user-agent'] && (options).headers['user-agent'].toString().indexOf('azsdk-js') !== -1) { - shouldCollect = false; - } - - (request)[AutoCollectHttpDependencies.alreadyAutoCollectedFlag] = true; - - if (request && options && shouldCollect) { - CorrelationContextManager.wrapEmitter(request); - // If there is no context create one, this apply when no request is triggering the dependency - if (!CorrelationContextManager.getCurrentContext()) { - // Create correlation context and wrap execution - let operationId = null; - if (CorrelationIdManager.w3cEnabled) { - let traceparent = new Traceparent(); - operationId = traceparent.traceId; - } - else { - let requestId = CorrelationIdManager.generateRequestId(null); - operationId = CorrelationIdManager.getRootId(requestId); - } - let correlationContext = CorrelationContextManager.generateContextObject(operationId); - CorrelationContextManager.runWithContext(correlationContext, () => { - AutoCollectHttpDependencies.trackRequest(this._client, { options: options, request: request }); - }); - } - else { - AutoCollectHttpDependencies.trackRequest(this._client, { options: options, request: request }); - } - } - }; - - // On node >= v0.11.12 and < 9.0 (excluding 8.9.0) https.request just calls http.request (with additional options). - // On node < 0.11.12, 8.9.0, and 9.0 > https.request is handled separately - // Patch both and leave a flag to not double-count on versions that just call through - // We add the flag to both http and https to protect against strange double collection in other scenarios - http.request = (options, ...requestArgs: any[]) => { - const request: http.ClientRequest = originalRequest.call(http, options, ...requestArgs); - clientRequestPatch(request, options); - return request; - }; - - https.request = (options, ...requestArgs: any[]) => { - const request: http.ClientRequest = originalHttpsRequest.call(https, options, ...requestArgs); - clientRequestPatch(request, options); - return request; - }; - - // Node 8 calls http.request from http.get using a local reference! - // We have to patch .get manually in this case and can't just assume request is enough - // We have to replace the entire method in this case. We can't call the original. - // This is because calling the original will give us no chance to set headers as it internally does .end(). - http.get = (options, ...requestArgs: any[]) => { - const request: http.ClientRequest = http.request.call(http, options, ...requestArgs); - request.end(); - return request; - }; - https.get = (options, ...requestArgs: any[]) => { - const request: http.ClientRequest = https.request.call(https, options, ...requestArgs); - request.end(); - return request; - }; - } - - /** - * Tracks an outgoing request. Because it may set headers this method must be called before - * writing content to or ending the request. - */ - public static trackRequest(client: TelemetryClient, telemetry: Contracts.NodeHttpDependencyTelemetry) { - if (!telemetry.options || !telemetry.request || !client) { - Logging.info("AutoCollectHttpDependencies.trackRequest was called with invalid parameters: ", !telemetry.options, !telemetry.request, !client); - return; - } - - let requestParser = new HttpDependencyParser(telemetry.options, telemetry.request); - - const currentContext = CorrelationContextManager.getCurrentContext(); - let uniqueRequestId: string; - let uniqueTraceparent: string; - if (currentContext && currentContext.operation && currentContext.operation.traceparent && Traceparent.isValidTraceId(currentContext.operation.traceparent.traceId)) { - currentContext.operation.traceparent.updateSpanId(); - uniqueRequestId = currentContext.operation.traceparent.getBackCompatRequestId(); - } else if (CorrelationIdManager.w3cEnabled) { - // Start an operation now so that we can include the w3c headers in the outgoing request - const traceparent = new Traceparent(); - uniqueTraceparent = traceparent.toString(); - uniqueRequestId = traceparent.getBackCompatRequestId(); - } else { - uniqueRequestId = currentContext && currentContext.operation && (currentContext.operation.parentId + AutoCollectHttpDependencies.requestNumber++ + '.'); - } - - // Add the source correlationId to the request headers, if a value was not already provided. - // The getHeader/setHeader methods aren't available on very old Node versions, and - // are not included in the v0.10 type declarations currently used. So check if the - // methods exist before invoking them. - if (Util.canIncludeCorrelationHeader(client, requestParser.getUrl()) && telemetry.request.getHeader && telemetry.request.setHeader) { - if (client.config && client.config.correlationId) { - // getHeader returns "any" type in newer versions of node. In basic scenarios, this will be , but could be modified to anything else via middleware - const correlationHeader = telemetry.request.getHeader(RequestResponseHeaders.requestContextHeader) - try { - Util.safeIncludeCorrelationHeader(client, telemetry.request, correlationHeader); - } catch (err) { - Logging.warn("Request-Context header could not be set. Correlation of requests may be lost", err); - } - - if (currentContext && currentContext.operation) { - try { - telemetry.request.setHeader(RequestResponseHeaders.requestIdHeader, uniqueRequestId); - // Also set legacy headers - if (!client.config.ignoreLegacyHeaders) { - telemetry.request.setHeader(RequestResponseHeaders.parentIdHeader, currentContext.operation.id); - telemetry.request.setHeader(RequestResponseHeaders.rootIdHeader, uniqueRequestId); - } - - // Set W3C headers, if available - if (uniqueTraceparent || currentContext.operation.traceparent) { - telemetry.request.setHeader(RequestResponseHeaders.traceparentHeader, uniqueTraceparent || currentContext.operation.traceparent.toString()); - } else if (CorrelationIdManager.w3cEnabled) { - // should never get here since we set uniqueTraceparent above for the w3cEnabled scenario - const traceparent = new Traceparent().toString(); - telemetry.request.setHeader(RequestResponseHeaders.traceparentHeader, traceparent); - } - if (currentContext.operation.tracestate) { - const tracestate = currentContext.operation.tracestate.toString(); - if (tracestate) { - telemetry.request.setHeader(RequestResponseHeaders.traceStateHeader, tracestate); - } - } - - const correlationContextHeader = (currentContext.customProperties).serializeToHeader(); - if (correlationContextHeader) { - telemetry.request.setHeader(RequestResponseHeaders.correlationContextHeader, correlationContextHeader); - } - } catch (err) { - Logging.warn("Correlation headers could not be set. Correlation of requests may be lost.", err); - } - } - } - } - - // Collect dependency telemetry about the request when it finishes. - if (telemetry.request.on) { - telemetry.request.on('response', (response: http.ClientResponse) => { - requestParser.onResponse(response); - - var dependencyTelemetry = requestParser.getDependencyTelemetry(telemetry, uniqueRequestId); - - dependencyTelemetry.contextObjects = dependencyTelemetry.contextObjects || {}; - dependencyTelemetry.contextObjects["http.RequestOptions"] = telemetry.options; - dependencyTelemetry.contextObjects["http.ClientRequest"] = telemetry.request; - dependencyTelemetry.contextObjects["http.ClientResponse"] = response; - - client.trackDependency(dependencyTelemetry); - }); - telemetry.request.on('error', (error: Error) => { - requestParser.onError(error); - - var dependencyTelemetry = requestParser.getDependencyTelemetry(telemetry, uniqueRequestId); - - dependencyTelemetry.contextObjects = dependencyTelemetry.contextObjects || {}; - dependencyTelemetry.contextObjects["http.RequestOptions"] = telemetry.options; - dependencyTelemetry.contextObjects["http.ClientRequest"] = telemetry.request; - dependencyTelemetry.contextObjects["Error"] = error; - - client.trackDependency(dependencyTelemetry); - }); - telemetry.request.on('abort', () => { - requestParser.onError(new Error()); - - var dependencyTelemetry = requestParser.getDependencyTelemetry(telemetry, uniqueRequestId); - - dependencyTelemetry.contextObjects = dependencyTelemetry.contextObjects || {}; - dependencyTelemetry.contextObjects["http.RequestOptions"] = telemetry.options; - dependencyTelemetry.contextObjects["http.ClientRequest"] = telemetry.request; - - client.trackDependency(dependencyTelemetry); - }); - } - } - - public dispose() { - AutoCollectHttpDependencies.INSTANCE = null; - this.enable(false); - this._isInitialized = false; - } -} - -export = AutoCollectHttpDependencies; diff --git a/AutoCollection/HttpDependencyParser.ts b/AutoCollection/HttpDependencyParser.ts deleted file mode 100644 index bf1e36d50..000000000 --- a/AutoCollection/HttpDependencyParser.ts +++ /dev/null @@ -1,197 +0,0 @@ -import http = require("http"); -import https = require("https"); -import url = require("url"); -import Contracts = require("../Declarations/Contracts"); -import Util = require("../Library/Util"); -import RequestResponseHeaders = require("../Library/RequestResponseHeaders"); -import RequestParser = require("./RequestParser"); -import CorrelationIdManager = require("../Library/CorrelationIdManager"); - -/** - * Helper class to read data from the request/response objects and convert them into the telemetry contract - */ -class HttpDependencyParser extends RequestParser { - private correlationId: string; - - constructor(requestOptions: object | string | http.RequestOptions | https.RequestOptions, request: http.ClientRequest) { - super(); - if (request && (request).method && requestOptions) { - // The ClientRequest.method property isn't documented, but is always there. - this.method = (request).method; - - this.url = HttpDependencyParser._getUrlFromRequestOptions(requestOptions, request); - this.startTime = +new Date(); - } - } - - /** - * Called when the ClientRequest emits an error event. - */ - public onError(error: Error) { - this._setStatus(undefined, error); - } - - /** - * Called when the ClientRequest emits a response event. - */ - public onResponse(response: http.ClientResponse) { - this._setStatus(response.statusCode, undefined); - this.correlationId = Util.getCorrelationContextTarget(response, RequestResponseHeaders.requestContextTargetKey); - } - - /** - * Gets a dependency data contract object for a completed ClientRequest. - */ - public getDependencyTelemetry(baseTelemetry?: Contracts.Telemetry, dependencyId?: string): Contracts.DependencyTelemetry { - let dependencyName = this.method.toUpperCase(); - let remoteDependencyType = Contracts.RemoteDependencyDataConstants.TYPE_HTTP; - let remoteDependencyTarget = ""; - try { - let urlObject = new url.URL(this.url); - urlObject.search = undefined; - urlObject.hash = undefined; - dependencyName += " " + urlObject.pathname; - remoteDependencyTarget = urlObject.hostname; - if (urlObject.port) { - remoteDependencyTarget += ":" + urlObject.port; - } - - } - catch (ex) { // Invalid URL - } - if (this.correlationId) { - remoteDependencyType = Contracts.RemoteDependencyDataConstants.TYPE_AI; - if (this.correlationId !== CorrelationIdManager.correlationIdPrefix) { - remoteDependencyTarget += " | " + this.correlationId; - } - } else { - remoteDependencyType = Contracts.RemoteDependencyDataConstants.TYPE_HTTP; - } - - var dependencyTelemetry: Contracts.DependencyTelemetry & Contracts.Identified = { - id: dependencyId, - name: dependencyName, - data: this.url, - duration: this.duration, - success: this._isSuccess(), - resultCode: this.statusCode ? this.statusCode.toString() : null, - properties: this.properties || {}, - dependencyTypeName: remoteDependencyType, - target: remoteDependencyTarget - }; - - if (baseTelemetry && baseTelemetry.time) { - dependencyTelemetry.time = baseTelemetry.time; - } else if (this.startTime) { - dependencyTelemetry.time = new Date(this.startTime); - } - - // We should keep any parameters the user passed in - // Except the fields defined above in requestTelemetry, which take priority - // Except the properties field, where they're merged instead, with baseTelemetry taking priority - if (baseTelemetry) { - // Copy missing fields - for (let key in baseTelemetry) { - if (!(dependencyTelemetry)[key]) { - (dependencyTelemetry)[key] = (baseTelemetry)[key]; - } - } - // Merge properties - if (baseTelemetry.properties) { - for (let key in baseTelemetry.properties) { - dependencyTelemetry.properties[key] = baseTelemetry.properties[key]; - } - } - } - - return dependencyTelemetry; - } - - /** - * Builds a URL from request options, using the same logic as http.request(). This is - * necessary because a ClientRequest object does not expose a url property. - */ - private static _getUrlFromRequestOptions(options: any, request: http.ClientRequest) { - if (typeof options === 'string') { - if (options.indexOf("http://") === 0 || options.indexOf("https://") === 0) { - // protocol exists, parse normally - try { - options = new url.URL(options); - } - catch (ex) { } - } else { - // protocol not found, insert http/https where appropriate - try { - const parsed = new url.URL("http://" + options); - if (parsed.port === "443") { - options = new url.URL("https://" + options); - } else { - options = new url.URL("http://" + options); - } - } - catch (ex) { } - } - } else if (options && typeof url.URL === 'function' && options instanceof url.URL) { - return url.format(options); - } else { - // Avoid modifying the original options object. - let originalOptions = options; - options = {}; - if (originalOptions) { - Object.keys(originalOptions).forEach(key => { - options[key] = originalOptions[key]; - }); - } - } - - // Oddly, url.format ignores path and only uses pathname and search, - // so create them from the path, if path was specified - if (options.path && options.host) { - // need to force a protocol to make parameter valid - base url is required when input is a relative url - try { - const parsedQuery = new url.URL(options.path, 'http://' + options.host + options.path); - options.pathname = parsedQuery.pathname; - options.search = parsedQuery.search; - } - catch (ex) { } - } - - // Sometimes the hostname is provided but not the host - // Add in the path when this occurs - if (options.path && options.hostname && !options.host) { - // need to force a protocol to make parameter valid - base url is required when input is a relative url - try { - const parsedQuery = new url.URL(options.path, 'http://' + options.hostname + options.path); - options.pathname = parsedQuery.pathname; - options.search = parsedQuery.search; - } - catch (ex) { } - } - - // Similarly, url.format ignores hostname and port if host is specified, - // even if host doesn't have the port, but http.request does not work - // this way. It will use the port if one is not specified in host, - // effectively treating host as hostname, but will use the port specified - // in host if it exists. - if (options.host && options.port) { - // Force a protocol so it will parse the host as the host, not path. - // It is discarded and not used, so it doesn't matter if it doesn't match - try { - const parsedHost = new url.URL(`http://${options.host}`); - if (!parsedHost.port && options.port) { - options.hostname = options.host; - delete options.host; - } - } - catch (ex) { } - } - - // Mix in default values used by http.request and others - options.protocol = options.protocol || ((request).agent && (request).agent.protocol) || ((request).protocol) || undefined; - options.hostname = options.hostname || 'localhost'; - - return url.format(options); - } -} - -export = HttpDependencyParser; diff --git a/AutoCollection/HttpRequestParser.ts b/AutoCollection/HttpRequestParser.ts deleted file mode 100644 index a7beb3267..000000000 --- a/AutoCollection/HttpRequestParser.ts +++ /dev/null @@ -1,332 +0,0 @@ -import http = require("http"); -import url = require("url"); -import net = require("net"); - -import Contracts = require("../Declarations/Contracts"); -import Util = require("../Library/Util"); -import RequestResponseHeaders = require("../Library/RequestResponseHeaders"); -import RequestParser = require("./RequestParser"); -import CorrelationIdManager = require("../Library/CorrelationIdManager"); -import Tracestate = require("../Library/Tracestate"); -import Traceparent = require("../Library/Traceparent"); -import { HttpRequest } from "../Library/Functions"; - -/** - * Helper class to read data from the request/response objects and convert them into the telemetry contract - */ -class HttpRequestParser extends RequestParser { - private static keys = new Contracts.ContextTagKeys(); - - private rawHeaders: { [key: string]: string }; - private socketRemoteAddress: string; - private connectionRemoteAddress: string; - private legacySocketRemoteAddress: string; - private userAgent: string; - private sourceCorrelationId: string; - private parentId: string; - private operationId: string; - private requestId: string; - private traceparent: Traceparent; - private tracestate: Tracestate; - private legacyRootId: string; // if original operationId is not w3c compat, move it here - - private correlationContextHeader: string; - - constructor(request: http.IncomingMessage | HttpRequest, requestId?: string) { - super(); - if (request) { - this.method = request.method; - this.url = this._getAbsoluteUrl(request); - this.startTime = +new Date(); - this.socketRemoteAddress = (request).socket && (request).socket.remoteAddress; - this.parseHeaders(request, requestId); - if ((request).connection) { - this.connectionRemoteAddress = ((request).connection as net.Socket).remoteAddress; - this.legacySocketRemoteAddress = ((request).connection)["socket"] && ((request).connection)["socket"].remoteAddress; - } - } - } - - public onError(error: Error | string, ellapsedMilliseconds?: number) { - this._setStatus(undefined, error); - - // This parameter is only for overrides. setStatus handles this internally for the autocollected case - if (ellapsedMilliseconds) { - this.duration = ellapsedMilliseconds; - } - } - - public onResponse(response: http.ServerResponse, ellapsedMilliseconds?: number) { - this._setStatus(response.statusCode, undefined); - - // This parameter is only for overrides. setStatus handles this internally for the autocollected case - if (ellapsedMilliseconds) { - this.duration = ellapsedMilliseconds; - } - } - - public getRequestTelemetry(baseTelemetry?: Contracts.Telemetry): Contracts.RequestTelemetry { - - let name = this.method; - try { - name += " " + new url.URL(this.url).pathname; - } - catch (ex) { // Invalid URL - } - - var requestTelemetry: Contracts.RequestTelemetry & Contracts.Identified = { - id: this.requestId, - name: name, - url: this.url, - /* - See https://github.com/microsoft/ApplicationInsights-dotnet-server/blob/25d695e6a906fbe977f67be3966d25dbf1c50a79/Src/Web/Web.Shared.Net/RequestTrackingTelemetryModule.cs#L250 - for reference - */ - source: this.sourceCorrelationId, - duration: this.duration, - resultCode: this.statusCode ? this.statusCode.toString() : null, - success: this._isSuccess(), - properties: this.properties - }; - - if (baseTelemetry && baseTelemetry.time) { - requestTelemetry.time = baseTelemetry.time; - } else if (this.startTime) { - requestTelemetry.time = new Date(this.startTime); - } - - // We should keep any parameters the user passed in - // Except the fields defined above in requestTelemetry, which take priority - // Except the properties field, where they're merged instead, with baseTelemetry taking priority - if (baseTelemetry) { - // Copy missing fields - for (let key in baseTelemetry) { - if (!(requestTelemetry)[key]) { - (requestTelemetry)[key] = (baseTelemetry)[key]; - } - } - // Merge properties - if (baseTelemetry.properties) { - for (let key in baseTelemetry.properties) { - requestTelemetry.properties[key] = baseTelemetry.properties[key]; - } - } - } - - return requestTelemetry; - } - - public getRequestTags(tags: { [key: string]: string }): { [key: string]: string } { - // create a copy of the context for requests since client info will be used here - var newTags = <{ [key: string]: string }>{}; - for (var key in tags) { - newTags[key] = tags[key]; - } - - // don't override tags if they are already set - newTags[HttpRequestParser.keys.locationIp] = tags[HttpRequestParser.keys.locationIp] || this._getIp(); - newTags[HttpRequestParser.keys.sessionId] = tags[HttpRequestParser.keys.sessionId] || this._getId("ai_session"); - newTags[HttpRequestParser.keys.userId] = tags[HttpRequestParser.keys.userId] || this._getId("ai_user"); - newTags[HttpRequestParser.keys.userAuthUserId] = tags[HttpRequestParser.keys.userAuthUserId] || this._getId("ai_authUser"); - newTags[HttpRequestParser.keys.operationName] = this.getOperationName(tags); - newTags[HttpRequestParser.keys.operationParentId] = this.getOperationParentId(tags); - newTags[HttpRequestParser.keys.operationId] = this.getOperationId(tags); - - return newTags; - } - - public getOperationId(tags: { [key: string]: string }) { - return tags[HttpRequestParser.keys.operationId] || this.operationId; - } - - public getOperationParentId(tags: { [key: string]: string }) { - return tags[HttpRequestParser.keys.operationParentId] || this.parentId || this.getOperationId(tags); - } - - public getOperationName(tags: { [key: string]: string }) { - if(tags[HttpRequestParser.keys.operationName]){ - return tags[HttpRequestParser.keys.operationName]; - } - let pathName = ""; - try { - pathName = new url.URL(this.url).pathname; - } - catch (ex) { // Invalid URL - } - let operationName = this.method; - if (pathName) { - operationName += " " + pathName; - } - return operationName; - } - - public getRequestId() { - return this.requestId; - } - - public getCorrelationContextHeader() { - return this.correlationContextHeader; - } - - public getTraceparent() { - return this.traceparent; - } - - public getTracestate() { - return this.tracestate; - } - - public getLegacyRootId() { - return this.legacyRootId; - } - - private _getAbsoluteUrl(request: http.IncomingMessage | HttpRequest): string { - if (!request.headers) { - return request.url; - } - - var encrypted = (request).connection ? ((request).connection as any).encrypted : null; - - var protocol = (encrypted || request.headers["x-forwarded-proto"] == "https") ? "https" : "http"; - - var baseUrl = protocol + '://' + request.headers.host + '/'; - - var pathName = ""; - var search = ""; - try { - var requestUrl = new url.URL(request.url, baseUrl); - pathName = requestUrl.pathname; - search = requestUrl.search; - } - catch (ex) { } - var absoluteUrl = url.format({ - protocol: protocol, - host: request.headers.host, - pathname: pathName, - search: search - }); - return absoluteUrl; - } - - private _getIp() { - - // regex to match ipv4 without port - // Note: including the port would cause the payload to be rejected by the data collector - var ipMatch = /[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/; - - var check = (str: string): string => { - var results = ipMatch.exec(str); - if (results) { - return results[0]; - } - }; - - var ip = check(this.rawHeaders["x-forwarded-for"]) - || check(this.rawHeaders["x-client-ip"]) - || check(this.rawHeaders["x-real-ip"]) - || check(this.connectionRemoteAddress) - || check(this.socketRemoteAddress) - || check(this.legacySocketRemoteAddress); - - // node v12 returns this if the address is "localhost" - if (!ip - && this.connectionRemoteAddress - && this.connectionRemoteAddress.substr - && this.connectionRemoteAddress.substr(0, 2) === "::") { - ip = "127.0.0.1"; - } - - return ip; - } - - private _getId(name: string) { - var cookie = (this.rawHeaders && this.rawHeaders["cookie"] && - typeof this.rawHeaders["cookie"] === 'string' && this.rawHeaders["cookie"]) || ""; - var value = HttpRequestParser.parseId(Util.getCookie(name, cookie)); - return value; - } - - /** - * Sets this operation's operationId, parentId, requestId (and legacyRootId, if necessary) based on this operation's traceparent - */ - private setBackCompatFromThisTraceContext() { - // Set operationId - this.operationId = this.traceparent.traceId; - if (this.traceparent.legacyRootId) { - this.legacyRootId = this.traceparent.legacyRootId; - } - - // Set parentId with existing spanId - this.parentId = this.traceparent.parentId; - - // Update the spanId and set the current requestId - this.traceparent.updateSpanId(); - this.requestId = this.traceparent.getBackCompatRequestId(); - } - - private parseHeaders(request: http.IncomingMessage | HttpRequest, requestId?: string) { - - this.rawHeaders = request.headers || (request).rawHeaders; - this.userAgent = request.headers && request.headers["user-agent"]; - this.sourceCorrelationId = Util.getCorrelationContextTarget(request, RequestResponseHeaders.requestContextSourceKey); - - if (request.headers) { - const tracestateHeader = request.headers[RequestResponseHeaders.traceStateHeader] ? request.headers[RequestResponseHeaders.traceStateHeader].toString() : null; // w3c header - const traceparentHeader = request.headers[RequestResponseHeaders.traceparentHeader] ? request.headers[RequestResponseHeaders.traceparentHeader].toString() : null; // w3c header - const requestIdHeader = request.headers[RequestResponseHeaders.requestIdHeader] ? request.headers[RequestResponseHeaders.requestIdHeader].toString() : null; // default AI header - const legacy_parentId = request.headers[RequestResponseHeaders.parentIdHeader] ? request.headers[RequestResponseHeaders.parentIdHeader].toString() : null; // legacy AI header - const legacy_rootId = request.headers[RequestResponseHeaders.rootIdHeader] ? request.headers[RequestResponseHeaders.rootIdHeader].toString() : null; // legacy AI header - - this.correlationContextHeader = request.headers[RequestResponseHeaders.correlationContextHeader] ? request.headers[RequestResponseHeaders.correlationContextHeader].toString() : null; - - if (CorrelationIdManager.w3cEnabled && (traceparentHeader || tracestateHeader)) { - // Parse W3C Trace Context headers - this.traceparent = new Traceparent(traceparentHeader ? traceparentHeader.toString() : null); // new traceparent is always created from this - this.tracestate = traceparentHeader && tracestateHeader && new Tracestate(tracestateHeader ? tracestateHeader.toString() : null); // discard tracestate if no traceparent is present - this.setBackCompatFromThisTraceContext(); - } else if (requestIdHeader) { - // Parse AI headers - if (CorrelationIdManager.w3cEnabled) { - this.traceparent = new Traceparent(null, requestIdHeader); - this.setBackCompatFromThisTraceContext(); - } else { - this.parentId = requestIdHeader; - this.requestId = CorrelationIdManager.generateRequestId(this.parentId); - this.operationId = CorrelationIdManager.getRootId(this.requestId); - } - } else { - // Legacy fallback - if (CorrelationIdManager.w3cEnabled) { - this.traceparent = new Traceparent(); - this.traceparent.parentId = legacy_parentId; - this.traceparent.legacyRootId = legacy_rootId || legacy_parentId; - this.setBackCompatFromThisTraceContext(); - } else { - this.parentId = legacy_parentId; - this.requestId = CorrelationIdManager.generateRequestId(legacy_rootId || this.parentId); - this.correlationContextHeader = null; - this.operationId = CorrelationIdManager.getRootId(this.requestId); - } - } - - if (requestId) { - // For the scenarios that don't guarantee an AI-created context, - // override the requestId with the provided one. - this.requestId = requestId; - this.operationId = CorrelationIdManager.getRootId(this.requestId); - } - } - } - - public static parseId(cookieValue: string): string { - const cookieParts = cookieValue.split("|"); - - if (cookieParts.length > 0) { - return cookieParts[0]; - } - - return ""; // old behavior was to return "" for incorrect parsing - } -} - -export = HttpRequestParser; diff --git a/AutoCollection/HttpRequests.ts b/AutoCollection/HttpRequests.ts deleted file mode 100644 index e4bf451b2..000000000 --- a/AutoCollection/HttpRequests.ts +++ /dev/null @@ -1,300 +0,0 @@ -import http = require("http"); -import https = require("https"); -import url = require("url"); - -import Contracts = require("../Declarations/Contracts"); -import TelemetryClient = require("../Library/TelemetryClient"); -import Logging = require("../Library/Logging"); -import Util = require("../Library/Util"); -import RequestResponseHeaders = require("../Library/RequestResponseHeaders"); -import HttpRequestParser = require("./HttpRequestParser"); -import { CorrelationContextManager, CorrelationContext, PrivateCustomProperties } from "./CorrelationContextManager"; -import AutoCollectPerformance = require("./Performance"); - -class AutoCollectHttpRequests { - - public static INSTANCE: AutoCollectHttpRequests; - - private static alreadyAutoCollectedFlag = '_appInsightsAutoCollected'; - - private _client: TelemetryClient; - private _isEnabled: boolean; - private _isInitialized: boolean; - private _isAutoCorrelating: boolean; - - constructor(client: TelemetryClient) { - if (!!AutoCollectHttpRequests.INSTANCE) { - throw new Error("Server request tracking should be configured from the applicationInsights object"); - } - - AutoCollectHttpRequests.INSTANCE = this; - this._client = client; - } - - public enable(isEnabled: boolean) { - this._isEnabled = isEnabled; - - // Autocorrelation requires automatic monitoring of incoming server requests - // Disabling autocollection but enabling autocorrelation will still enable - // request monitoring but will not produce request events - if ((this._isAutoCorrelating || this._isEnabled || AutoCollectPerformance.isEnabled()) && !this._isInitialized) { - this.useAutoCorrelation(this._isAutoCorrelating); - this._initialize(); - } - } - - public useAutoCorrelation(isEnabled: boolean, forceClsHooked?: boolean) { - if (isEnabled && !this._isAutoCorrelating) { - CorrelationContextManager.enable(forceClsHooked); - } else if (!isEnabled && this._isAutoCorrelating) { - CorrelationContextManager.disable(); - } - this._isAutoCorrelating = isEnabled; - } - - public isInitialized() { - return this._isInitialized; - } - - public isAutoCorrelating() { - return this._isAutoCorrelating; - } - - private _generateCorrelationContext(requestParser: HttpRequestParser): CorrelationContext { - if (!this._isAutoCorrelating) { - return; - } - - return CorrelationContextManager.generateContextObject( - requestParser.getOperationId(this._client.context.tags), - requestParser.getRequestId(), - requestParser.getOperationName(this._client.context.tags), - requestParser.getCorrelationContextHeader(), - requestParser.getTraceparent(), - requestParser.getTracestate() - ); - } - - private _initialize() { - this._isInitialized = true; - - const wrapOnRequestHandler: Function = (onRequest?: Function) => { - if (!onRequest) { - return undefined; - } - if (typeof onRequest !== 'function') { - throw new Error('onRequest handler must be a function'); - } - return (request: http.ServerRequest, response: http.ServerResponse) => { - CorrelationContextManager.wrapEmitter(request); - CorrelationContextManager.wrapEmitter(response); - const shouldCollect: boolean = request && !(request)[AutoCollectHttpRequests.alreadyAutoCollectedFlag]; - - if (request && shouldCollect) { - // Set up correlation context - const requestParser = new HttpRequestParser(request); - const correlationContext = this._generateCorrelationContext(requestParser); - - // Note: Check for if correlation is enabled happens within this method. - // If not enabled, function will directly call the callback. - CorrelationContextManager.runWithContext(correlationContext, () => { - if (this._isEnabled) { - // Mark as auto collected - (request)[AutoCollectHttpRequests.alreadyAutoCollectedFlag] = true; - - // Auto collect request - AutoCollectHttpRequests.trackRequest(this._client, { request: request, response: response }, requestParser); - } - - if (typeof onRequest === "function") { - onRequest(request, response); - } - }); - } else { - if (typeof onRequest === "function") { - onRequest(request, response); - } - } - } - } - - // The `http.createServer` function will instantiate a new http.Server object. - // Inside the Server's constructor, it is using addListener to register the - // onRequest handler. So there are two ways to inject the wrapped onRequest handler: - // 1) Overwrite Server.prototype.addListener (and .on()) globally and not patching - // the http.createServer call. Or - // 2) Overwrite the http.createServer method and add a patched addListener to the - // fresh server instance. This seems more stable for possible future changes as - // it also covers the case where the Server might not use addListener to manage - // the callback internally. - // And also as long as the constructor uses addListener to add the handle, it is - // ok to patch the addListener after construction only. Because if we would patch - // the prototype one and the createServer method, we would wrap the handler twice - // in case of the constructor call. - const wrapServerEventHandler: Function = (server: (http.Server | https.Server)) => { - const originalAddListener = server.addListener.bind(server); - server.addListener = (eventType: string, eventHandler: Function) => { - switch (eventType) { - case 'request': - case 'checkContinue': - return originalAddListener(eventType, wrapOnRequestHandler(eventHandler)); - default: - return originalAddListener(eventType, eventHandler); - } - }; - // on is an alias to addListener only - server.on = server.addListener; - } - - const originalHttpServer: any = http.createServer; - - // options parameter was added in Node.js v9.6.0, v8.12.0 - // function createServer(requestListener?: RequestListener): Server; - // function createServer(options: ServerOptions, requestListener?: RequestListener): Server; - http.createServer = (param1?: Object, param2?: Function) => { - // todo: get a pointer to the server so the IP address can be read from server.address - if (param2 && typeof param2 === 'function') { - const server: http.Server = originalHttpServer(param1, wrapOnRequestHandler(param2)); - wrapServerEventHandler(server); - return server; - } - else { - const server: http.Server = originalHttpServer(wrapOnRequestHandler(param1)); - wrapServerEventHandler(server); - return server; - } - } - - const originalHttpsServer = https.createServer; - https.createServer = (options: https.ServerOptions, onRequest?: Function) => { - const server: https.Server = originalHttpsServer(options, wrapOnRequestHandler(onRequest)); - wrapServerEventHandler(server); - return server; - } - } - - /** - * Tracks a request synchronously (doesn't wait for response 'finish' event) - */ - public static trackRequestSync(client: TelemetryClient, telemetry: Contracts.NodeHttpRequestTelemetry) { - if (!telemetry.request || !telemetry.response || !client) { - Logging.info("AutoCollectHttpRequests.trackRequestSync was called with invalid parameters: ", !telemetry.request, !telemetry.response, !client); - return; - } - - AutoCollectHttpRequests.addResponseCorrelationIdHeader(client, telemetry.response); - - // store data about the request - var correlationContext = CorrelationContextManager.getCurrentContext(); - var requestParser = new HttpRequestParser(telemetry.request, (correlationContext && correlationContext.operation.parentId)); - - // Overwrite correlation context with request parser results - if (correlationContext) { - correlationContext.operation.id = requestParser.getOperationId(client.context.tags) || correlationContext.operation.id; - correlationContext.operation.name = requestParser.getOperationName(client.context.tags) || correlationContext.operation.name; - correlationContext.operation.parentId = requestParser.getRequestId() || correlationContext.operation.parentId; - (correlationContext.customProperties).addHeaderData(requestParser.getCorrelationContextHeader()); - } - - AutoCollectHttpRequests.endRequest(client, requestParser, telemetry, telemetry.duration, telemetry.error); - } - - /** - * Tracks a request by listening to the response 'finish' event - */ - public static trackRequest(client: TelemetryClient, telemetry: Contracts.NodeHttpRequestTelemetry, _requestParser?: HttpRequestParser) { - if (!telemetry.request || !telemetry.response || !client) { - Logging.info("AutoCollectHttpRequests.trackRequest was called with invalid parameters: ", !telemetry.request, !telemetry.response, !client); - return; - } - - // store data about the request - var correlationContext = CorrelationContextManager.getCurrentContext(); - var requestParser = _requestParser || new HttpRequestParser(telemetry.request, correlationContext && correlationContext.operation.parentId); - - if (Util.canIncludeCorrelationHeader(client, requestParser.getUrl())) { - AutoCollectHttpRequests.addResponseCorrelationIdHeader(client, telemetry.response); - } - - // Overwrite correlation context with request parser results (if not an automatic track. we've already precalculated the correlation context in that case) - if (correlationContext && !_requestParser) { - correlationContext.operation.id = requestParser.getOperationId(client.context.tags) || correlationContext.operation.id; - correlationContext.operation.name = requestParser.getOperationName(client.context.tags) || correlationContext.operation.name; - correlationContext.operation.parentId = requestParser.getOperationParentId(client.context.tags) || correlationContext.operation.parentId; - (correlationContext.customProperties).addHeaderData(requestParser.getCorrelationContextHeader()); - } - - // response listeners - if (telemetry.response.once) { - telemetry.response.once("finish", () => { - AutoCollectHttpRequests.endRequest(client, requestParser, telemetry, null, null); - }); - } - - // track a failed request if an error is emitted - if (telemetry.request.on) { - telemetry.request.on("error", (error: any) => { - AutoCollectHttpRequests.endRequest(client, requestParser, telemetry, null, error); - }); - } - - // track an aborted request if an aborted event is emitted - if (telemetry.request.on) { - telemetry.request.on("aborted", () => { - const errorMessage = "The request has been aborted and the network socket has closed."; - AutoCollectHttpRequests.endRequest(client, requestParser, telemetry, null, errorMessage); - }); - } - } - - /** - * Add the target correlationId to the response headers, if not already provided. - */ - private static addResponseCorrelationIdHeader(client: TelemetryClient, response: http.ServerResponse) { - if (client.config && client.config.correlationId && - response.getHeader && response.setHeader && !(response).headersSent) { - const correlationHeader = response.getHeader(RequestResponseHeaders.requestContextHeader); - Util.safeIncludeCorrelationHeader(client, response, correlationHeader); - } - } - - private static endRequest(client: TelemetryClient, requestParser: HttpRequestParser, telemetry: Contracts.NodeHttpRequestTelemetry, ellapsedMilliseconds?: number, error?: any) { - if (error) { - requestParser.onError(error, ellapsedMilliseconds); - } else { - requestParser.onResponse(telemetry.response, ellapsedMilliseconds); - } - - var requestTelemetry = requestParser.getRequestTelemetry(telemetry); - - requestTelemetry.tagOverrides = requestParser.getRequestTags(client.context.tags); - if (telemetry.tagOverrides) { - for (let key in telemetry.tagOverrides) { - requestTelemetry.tagOverrides[key] = telemetry.tagOverrides[key]; - } - } - - const legacyRootId = requestParser.getLegacyRootId(); - if (legacyRootId) { - requestTelemetry.properties["ai_legacyRootId"] = legacyRootId; - } - - requestTelemetry.contextObjects = requestTelemetry.contextObjects || {}; - requestTelemetry.contextObjects["http.ServerRequest"] = telemetry.request; - requestTelemetry.contextObjects["http.ServerResponse"] = telemetry.response; - - client.trackRequest(requestTelemetry); - } - - public dispose() { - AutoCollectHttpRequests.INSTANCE = null; - this.enable(false); - this._isInitialized = false; - CorrelationContextManager.disable(); - this._isAutoCorrelating = false; - } -} - - - -export = AutoCollectHttpRequests; diff --git a/AutoCollection/NativePerformance.ts b/AutoCollection/NativePerformance.ts deleted file mode 100644 index 1e9bd4c1e..000000000 --- a/AutoCollection/NativePerformance.ts +++ /dev/null @@ -1,278 +0,0 @@ -import TelemetryClient = require("../Library/TelemetryClient"); -import Constants = require("../Declarations/Constants"); -import Context = require("../Library/Context"); -import Logging = require("../Library/Logging"); -import { IBaseConfig } from "../Declarations/Interfaces"; - -/** - * Interface which defines which specific extended metrics should be disabled - * - * @export - * @interface IDisabledExtendedMetrics - */ -export interface IDisabledExtendedMetrics { - gc?: boolean; - heap?: boolean; - loop?: boolean; -} - -export class AutoCollectNativePerformance { - public static INSTANCE: AutoCollectNativePerformance; - - private static _emitter: any; - private static _metricsAvailable: boolean; // is the native metrics lib installed - private _isEnabled: boolean; - private _isInitialized: boolean; - private _handle: NodeJS.Timer; - private _client: TelemetryClient; - private _disabledMetrics: IDisabledExtendedMetrics = {}; - - constructor(client: TelemetryClient) { - // Note: Only 1 instance of this can exist. So when we reconstruct this object, - // just disable old native instance and reset JS member variables - if (AutoCollectNativePerformance.INSTANCE) { - AutoCollectNativePerformance.INSTANCE.dispose(); - } - AutoCollectNativePerformance.INSTANCE = this; - this._client = client; - } - - /** - * Reports if NativePerformance is able to run in this environment - */ - public static isNodeVersionCompatible() { - var nodeVer = process.versions.node.split("."); - return parseInt(nodeVer[0]) >= 6; - } - - /** - * Start instance of native metrics agent. - * - * @param {boolean} isEnabled - * @param {number} [collectionInterval=60000] - * @memberof AutoCollectNativePerformance - */ - public enable(isEnabled: boolean, disabledMetrics: IDisabledExtendedMetrics = {}, collectionInterval = 60000): void { - if (!AutoCollectNativePerformance.isNodeVersionCompatible()) { - return; - } - - if (AutoCollectNativePerformance._metricsAvailable == undefined && isEnabled && !this._isInitialized) { - // Try to require in the native-metrics library. If it's found initialize it, else do nothing and never try again. - try { - const NativeMetricsEmitters = require("applicationinsights-native-metrics"); - AutoCollectNativePerformance._emitter = new NativeMetricsEmitters(); - AutoCollectNativePerformance._metricsAvailable = true; - Logging.info("Native metrics module successfully loaded!"); - } catch (err) { - // Package not available. Never try again - AutoCollectNativePerformance._metricsAvailable = false; - return; - } - } - - this._isEnabled = isEnabled; - this._disabledMetrics = disabledMetrics - if (this._isEnabled && !this._isInitialized) { - this._isInitialized = true; - } - - // Enable the emitter if we were able to construct one - if (this._isEnabled && AutoCollectNativePerformance._emitter) { - // enable self - AutoCollectNativePerformance._emitter.enable(true, collectionInterval); - if (!this._handle) { - this._handle = setInterval(() => this._trackNativeMetrics(), collectionInterval); - this._handle.unref(); - } - } else if (AutoCollectNativePerformance._emitter) { - // disable self - AutoCollectNativePerformance._emitter.enable(false); - if (this._handle) { - clearInterval(this._handle); - this._handle = undefined; - } - } - } - - /** - * Cleanup this instance of AutoCollectNativePerformance - * - * @memberof AutoCollectNativePerformance - */ - public dispose(): void { - this.enable(false); - } - - /** - * Parse environment variable and overwrite isEnabled based on respective fields being set - * - * @private - * @static - * @param {(boolean | IDisabledExtendedMetrics)} collectExtendedMetrics - * @param {(IBaseConfig)} customConfig - * @returns {(boolean | IDisabledExtendedMetrics)} - * @memberof AutoCollectNativePerformance - */ - public static parseEnabled(collectExtendedMetrics: boolean | IDisabledExtendedMetrics, customConfig: IBaseConfig): { isEnabled: boolean, disabledMetrics: IDisabledExtendedMetrics } { - const disableAll = customConfig.disableAllExtendedMetrics; - const individualOptOuts = customConfig.extendedMetricDisablers; - - // case 1: disable all env var set, RETURN with isEnabled=false - if (disableAll) { - return { isEnabled: false, disabledMetrics: {} }; - } - - // case 2: individual env vars set, RETURN with isEnabled=true, disabledMetrics={...} - if (individualOptOuts) { - const optOutsArr = individualOptOuts.split(","); - const disabledMetrics: any = {}; - if (optOutsArr.length > 0) { - for (const opt of optOutsArr) { - disabledMetrics[opt] = true; - } - } - - // case 2a: collectExtendedMetrics is an object, overwrite existing ones if they exist - if (typeof collectExtendedMetrics === "object") { - return { isEnabled: true, disabledMetrics: { ...collectExtendedMetrics, ...disabledMetrics } }; - } - - // case 2b: collectExtendedMetrics is a boolean, set disabledMetrics as is - return { isEnabled: collectExtendedMetrics, disabledMetrics }; - } - - // case 4: no env vars set, input arg is a boolean, RETURN with isEnabled=collectExtendedMetrics, disabledMetrics={} - if (typeof collectExtendedMetrics === "boolean") { - return { isEnabled: collectExtendedMetrics, disabledMetrics: {} }; - } else { // use else so we don't need to force typing on collectExtendedMetrics - // case 5: no env vars set, input arg is object, RETURN with isEnabled=true, disabledMetrics=collectExtendedMetrics - return { isEnabled: true, disabledMetrics: collectExtendedMetrics }; - } - } - - /** - * Trigger an iteration of native metrics collection - * - * @private - * @memberof AutoCollectNativePerformance - */ - private _trackNativeMetrics() { - let shouldSendAll = true; - if (typeof this._isEnabled !== "object") { - shouldSendAll = this._isEnabled; - } - - if (shouldSendAll) { - this._trackGarbageCollection(); - this._trackEventLoop(); - this._trackHeapUsage(); - } - } - - /** - * Tracks garbage collection stats for this interval. One custom metric is sent per type of garbage - * collection that occurred during this collection interval. - * - * @private - * @memberof AutoCollectNativePerformance - */ - private _trackGarbageCollection(): void { - if (this._disabledMetrics.gc) { - return; - } - - const gcData = AutoCollectNativePerformance._emitter.getGCData(); - - for (let gc in gcData) { - const metrics = gcData[gc].metrics; - const name = `${gc} Garbage Collection Duration`; - const stdDev = Math.sqrt(metrics.sumSquares / metrics.count - Math.pow(metrics.total / metrics.count, 2)) || 0; - this._client.trackMetric({ - name: name, - value: metrics.total, - count: metrics.count, - max: metrics.max, - min: metrics.min, - stdDev: stdDev, - tagOverrides: { - [this._client.context.keys.internalSdkVersion]: "node-nativeperf:" + Context.sdkVersion - } - }); - } - } - - /** - * Tracks event loop ticks per interval as a custom metric. Also included in the metric is min/max/avg - * time spent in event loop for this interval. - * - * @private - * @returns {void} - * @memberof AutoCollectNativePerformance - */ - private _trackEventLoop(): void { - if (this._disabledMetrics.loop) { - return; - } - - const loopData = AutoCollectNativePerformance._emitter.getLoopData(); - const metrics = loopData.loopUsage; - if (metrics.count == 0) { - return; - } - - const name = `Event Loop CPU Time`; - const stdDev = Math.sqrt(metrics.sumSquares / metrics.count - Math.pow(metrics.total / metrics.count, 2)) || 0; - this._client.trackMetric({ - name: name, - value: metrics.total, - count: metrics.count, - min: metrics.min, - max: metrics.max, - stdDev: stdDev, - tagOverrides: { - [this._client.context.keys.internalSdkVersion]: "node-nativeperf:" + Context.sdkVersion - } - }); - } - - /** - * Track heap memory usage metrics as a custom metric. - * - * @private - * @memberof AutoCollectNativePerformance - */ - private _trackHeapUsage(): void { - if (this._disabledMetrics.heap) { - return; - } - - const memoryUsage = process.memoryUsage(); - const { heapUsed, heapTotal, rss } = memoryUsage; - - this._client.trackMetric({ - name: `Memory Usage (Heap)`, - value: heapUsed, - count: 1, - tagOverrides: { - [this._client.context.keys.internalSdkVersion]: "node-nativeperf:" + Context.sdkVersion - } - }); - this._client.trackMetric({ - name: `Memory Total (Heap)`, - value: heapTotal, - count: 1, - tagOverrides: { - [this._client.context.keys.internalSdkVersion]: "node-nativeperf:" + Context.sdkVersion - } - }); - this._client.trackMetric({ - name: `Memory Usage (Non-Heap)`, - value: rss - heapTotal, - count: 1, - tagOverrides: { - [this._client.context.keys.internalSdkVersion]: "node-nativeperf:" + Context.sdkVersion - } - }); - } -} diff --git a/AutoCollection/NetworkStatsbeat.ts b/AutoCollection/NetworkStatsbeat.ts deleted file mode 100644 index 4842bd37d..000000000 --- a/AutoCollection/NetworkStatsbeat.ts +++ /dev/null @@ -1,43 +0,0 @@ -export class NetworkStatsbeat { - - public time: number; - - public lastTime: number; - - public endpoint: number; - - public host: string; - - public totalRequestCount: number; - - public lastRequestCount: number; - - public totalSuccesfulRequestCount: number; - - public totalFailedRequestCount: number; - - public retryCount: number; - - public exceptionCount: number; - - public throttleCount: number; - - public intervalRequestExecutionTime: number; - - public lastIntervalRequestExecutionTime: number; - - constructor(endpoint: number, host: string) { - this.endpoint = endpoint; - this.host = host; - this.totalRequestCount = 0; - this.totalSuccesfulRequestCount = 0; - this.totalFailedRequestCount = 0; - this.retryCount = 0; - this.exceptionCount = 0; - this.throttleCount = 0; - this.intervalRequestExecutionTime = 0; - this.lastIntervalRequestExecutionTime = 0; - this.lastTime = +new Date; - this.lastRequestCount = 0; - } -} \ No newline at end of file diff --git a/AutoCollection/Performance.ts b/AutoCollection/Performance.ts deleted file mode 100644 index c7124adc3..000000000 --- a/AutoCollection/Performance.ts +++ /dev/null @@ -1,336 +0,0 @@ -import os = require("os"); - -import TelemetryClient = require("../Library/TelemetryClient"); -import Constants = require("../Declarations/Constants"); - -class AutoCollectPerformance { - - public static INSTANCE: AutoCollectPerformance; - - private static _totalRequestCount: number = 0; - private static _totalFailedRequestCount: number = 0; - private static _totalDependencyCount: number = 0; - private static _totalFailedDependencyCount: number = 0; - private static _totalExceptionCount: number = 0; - private static _intervalDependencyExecutionTime: number = 0; - private static _intervalRequestExecutionTime: number = 0; - - private _lastIntervalRequestExecutionTime: number = 0; // the sum of durations which took place during from app start until last interval - private _lastIntervalDependencyExecutionTime: number = 0; - private _enableLiveMetricsCounters: boolean; - private _collectionInterval: number; - private _client: TelemetryClient; - private _handle: NodeJS.Timer; - private _isEnabled: boolean; - private _isInitialized: boolean; - private _lastAppCpuUsage: { user: number, system: number }; - private _lastHrtime: number[]; - private _lastCpus: { model: string; speed: number; times: { user: number; nice: number; sys: number; idle: number; irq: number; }; }[]; - private _lastDependencies: { totalDependencyCount: number; totalFailedDependencyCount: number; time: number; }; - private _lastRequests: { totalRequestCount: number; totalFailedRequestCount: number; time: number; }; - private _lastExceptions: { totalExceptionCount: number, time: number }; - - /** - * @param enableLiveMetricsCounters - enable sending additional live metrics information (dependency metrics, exception metrics, committed memory) - */ - constructor(client: TelemetryClient, collectionInterval = 60000, enableLiveMetricsCounters = false) { - if (!AutoCollectPerformance.INSTANCE) { - AutoCollectPerformance.INSTANCE = this; - } - - this._lastRequests = { totalRequestCount: 0, totalFailedRequestCount: 0, time: 0 }; - this._lastDependencies = { totalDependencyCount: 0, totalFailedDependencyCount: 0, time: 0 }; - this._lastExceptions = { totalExceptionCount: 0,time: 0 }; - this._isInitialized = false; - this._client = client; - this._collectionInterval = collectionInterval; - this._enableLiveMetricsCounters = enableLiveMetricsCounters; - } - - public enable(isEnabled: boolean, collectionInterval?: number) { - this._isEnabled = isEnabled; - if (this._isEnabled && !this._isInitialized) { - this._isInitialized = true; - } - - if (isEnabled) { - if (!this._handle) { - this._lastCpus = os.cpus(); - this._lastRequests = { - totalRequestCount: AutoCollectPerformance._totalRequestCount, - totalFailedRequestCount: AutoCollectPerformance._totalFailedRequestCount, - time: +new Date - }; - this._lastDependencies = { - totalDependencyCount: AutoCollectPerformance._totalDependencyCount, - totalFailedDependencyCount: AutoCollectPerformance._totalFailedDependencyCount, - time: +new Date - }; - this._lastExceptions = { - totalExceptionCount: AutoCollectPerformance._totalExceptionCount, - time: +new Date - }; - - if (typeof (process as any).cpuUsage === 'function') { - this._lastAppCpuUsage = (process as any).cpuUsage(); - } - this._lastHrtime = process.hrtime(); - this._collectionInterval = collectionInterval || this._collectionInterval; - this._handle = setInterval(() => this.trackPerformance(), this._collectionInterval); - this._handle.unref(); // Allow the app to terminate even while this loop is going on - } - } else { - if (this._handle) { - clearInterval(this._handle); - this._handle = undefined; - } - } - } - - public static countRequest(duration: number | string, success: boolean) { - let durationMs: number; - if (!AutoCollectPerformance.isEnabled()) { - return; - } - - if (typeof duration === 'string') { - // dependency duration is passed in as "00:00:00.123" by autocollectors - durationMs = +new Date('1970-01-01T' + duration + 'Z'); // convert to num ms, returns NaN if wrong - } else if (typeof duration === 'number') { - durationMs = duration; - } else { - return; - } - - AutoCollectPerformance._intervalRequestExecutionTime += durationMs; - if (success === false) { - AutoCollectPerformance._totalFailedRequestCount++; - } - AutoCollectPerformance._totalRequestCount++; - } - - public static countException() { - AutoCollectPerformance._totalExceptionCount++; - } - - public static countDependency(duration: number | string, success: boolean) { - let durationMs: number; - if (!AutoCollectPerformance.isEnabled()) { - return; - } - - if (typeof duration === 'string') { - // dependency duration is passed in as "00:00:00.123" by autocollectors - durationMs = +new Date('1970-01-01T' + duration + 'Z'); // convert to num ms, returns NaN if wrong - } else if (typeof duration === 'number') { - durationMs = duration; - } else { - return; - } - - AutoCollectPerformance._intervalDependencyExecutionTime += durationMs; - if (success === false) { - AutoCollectPerformance._totalFailedDependencyCount++; - } - AutoCollectPerformance._totalDependencyCount++; - } - - public isInitialized() { - return this._isInitialized; - } - - public static isEnabled() { - return AutoCollectPerformance.INSTANCE && AutoCollectPerformance.INSTANCE._isEnabled; - } - - public trackPerformance() { - this._trackCpu(); - this._trackMemory(); - this._trackNetwork(); - this._trackDependencyRate(); - this._trackExceptionRate(); - } - - private _trackCpu() { - // this reports total ms spent in each category since the OS was booted, to calculate percent it is necessary - // to find the delta since the last measurement - var cpus = os.cpus(); - if (cpus && cpus.length && this._lastCpus && cpus.length === this._lastCpus.length) { - var totalUser = 0; - var totalSys = 0; - var totalNice = 0; - var totalIdle = 0; - var totalIrq = 0; - for (var i = 0; !!cpus && i < cpus.length; i++) { - var cpu = cpus[i]; - var lastCpu = this._lastCpus[i]; - - var name = "% cpu(" + i + ") "; - var model = cpu.model; - var speed = cpu.speed; - var times = cpu.times; - var lastTimes = lastCpu.times; - - // user cpu time (or) % CPU time spent in user space - var user = (times.user - lastTimes.user) || 0; - totalUser += user; - - // system cpu time (or) % CPU time spent in kernel space - var sys = (times.sys - lastTimes.sys) || 0; - totalSys += sys; - - // user nice cpu time (or) % CPU time spent on low priority processes - var nice = (times.nice - lastTimes.nice) || 0; - totalNice += nice; - - // idle cpu time (or) % CPU time spent idle - var idle = (times.idle - lastTimes.idle) || 0; - totalIdle += idle; - - // irq (or) % CPU time spent servicing/handling hardware interrupts - var irq = (times.irq - lastTimes.irq) || 0; - totalIrq += irq; - } - - // Calculate % of total cpu time (user + system) this App Process used (Only supported by node v6.1.0+) - let appCpuPercent: number = undefined; - if (typeof (process as any).cpuUsage === 'function') { - const appCpuUsage = (process as any).cpuUsage(); - const hrtime = process.hrtime(); - - const totalApp = ((appCpuUsage.user - this._lastAppCpuUsage.user) + (appCpuUsage.system - this._lastAppCpuUsage.system)) || 0; - - if (typeof this._lastHrtime !== 'undefined' && this._lastHrtime.length === 2) { - const elapsedTime = ((hrtime[0] - this._lastHrtime[0]) * 1e6 + (hrtime[1] - this._lastHrtime[1]) / 1e3) || 0; // convert to microseconds - - appCpuPercent = 100 * totalApp / (elapsedTime * cpus.length); - } - - // Set previous - this._lastAppCpuUsage = appCpuUsage; - this._lastHrtime = hrtime; - } - - var combinedTotal = (totalUser + totalSys + totalNice + totalIdle + totalIrq) || 1; - - this._client.trackMetric({ name: Constants.PerformanceCounter.PROCESSOR_TIME, value: ((combinedTotal - totalIdle) / combinedTotal) * 100 }); - this._client.trackMetric({ name: Constants.PerformanceCounter.PROCESS_TIME, value: appCpuPercent || ((totalUser / combinedTotal) * 100) }); - } - - this._lastCpus = cpus; - } - - private _trackMemory() { - var freeMem = os.freemem(); - var usedMem = process.memoryUsage().rss; - var committedMemory = os.totalmem() - freeMem; - this._client.trackMetric({ name: Constants.PerformanceCounter.PRIVATE_BYTES, value: usedMem }); - this._client.trackMetric({ name: Constants.PerformanceCounter.AVAILABLE_BYTES, value: freeMem }); - - // Only supported by quickpulse service - if (this._enableLiveMetricsCounters) { - this._client.trackMetric({ name: Constants.QuickPulseCounter.COMMITTED_BYTES, value: committedMemory }); - } - } - - private _trackNetwork() { - // track total request counters - var lastRequests = this._lastRequests; - var requests = { - totalRequestCount: AutoCollectPerformance._totalRequestCount, - totalFailedRequestCount: AutoCollectPerformance._totalFailedRequestCount, - time: +new Date - }; - - var intervalRequests = (requests.totalRequestCount - lastRequests.totalRequestCount) || 0; - var intervalFailedRequests = (requests.totalFailedRequestCount - lastRequests.totalFailedRequestCount) || 0; - var elapsedMs = requests.time - lastRequests.time; - var elapsedSeconds = elapsedMs / 1000; - var averageRequestExecutionTime = ((AutoCollectPerformance._intervalRequestExecutionTime - this._lastIntervalRequestExecutionTime) / intervalRequests) || 0; // default to 0 in case no requests in this interval - this._lastIntervalRequestExecutionTime = AutoCollectPerformance._intervalRequestExecutionTime // reset - - if (elapsedMs > 0) { - var requestsPerSec = intervalRequests / elapsedSeconds; - var failedRequestsPerSec = intervalFailedRequests / elapsedSeconds; - - this._client.trackMetric({ name: Constants.PerformanceCounter.REQUEST_RATE, value: requestsPerSec }); - - // Only send duration to live metrics if it has been updated! - if (!this._enableLiveMetricsCounters || intervalRequests > 0) { - this._client.trackMetric({ name: Constants.PerformanceCounter.REQUEST_DURATION, value: averageRequestExecutionTime }); - } - - // Only supported by quickpulse service - if (this._enableLiveMetricsCounters) { - this._client.trackMetric({ name: Constants.QuickPulseCounter.REQUEST_FAILURE_RATE, value: failedRequestsPerSec }); - } - } - - this._lastRequests = requests; - } - - // Static counter is accumulated externally. Report the rate to client here - // Note: This is currently only used with QuickPulse client - private _trackDependencyRate() { - if (this._enableLiveMetricsCounters) { - var lastDependencies = this._lastDependencies; - var dependencies = { - totalDependencyCount: AutoCollectPerformance._totalDependencyCount, - totalFailedDependencyCount: AutoCollectPerformance._totalFailedDependencyCount, - time: +new Date - }; - - var intervalDependencies = (dependencies.totalDependencyCount - lastDependencies.totalDependencyCount) || 0; - var intervalFailedDependencies = (dependencies.totalFailedDependencyCount - lastDependencies.totalFailedDependencyCount) || 0; - var elapsedMs = dependencies.time - lastDependencies.time; - var elapsedSeconds = elapsedMs / 1000; - var averageDependencyExecutionTime = ((AutoCollectPerformance._intervalDependencyExecutionTime - this._lastIntervalDependencyExecutionTime) / intervalDependencies) || 0; - this._lastIntervalDependencyExecutionTime = AutoCollectPerformance._intervalDependencyExecutionTime // reset - - if (elapsedMs > 0) { - var dependenciesPerSec = intervalDependencies / elapsedSeconds; - var failedDependenciesPerSec = intervalFailedDependencies / elapsedSeconds; - - this._client.trackMetric({ name: Constants.QuickPulseCounter.DEPENDENCY_RATE, value: dependenciesPerSec }); - this._client.trackMetric({ name: Constants.QuickPulseCounter.DEPENDENCY_FAILURE_RATE, value: failedDependenciesPerSec }); - - // redundant check for livemetrics, but kept for consistency w/ requests - // Only send duration to live metrics if it has been updated! - if (!this._enableLiveMetricsCounters || intervalDependencies > 0) { - this._client.trackMetric({ name: Constants.QuickPulseCounter.DEPENDENCY_DURATION, value: averageDependencyExecutionTime }); - } - } - this._lastDependencies = dependencies; - } - } - - // Static counter is accumulated externally. Report the rate to client here - // Note: This is currently only used with QuickPulse client - private _trackExceptionRate() { - if (this._enableLiveMetricsCounters) { - var lastExceptions = this._lastExceptions; - var exceptions = { - totalExceptionCount: AutoCollectPerformance._totalExceptionCount, - time: +new Date - }; - - var intervalExceptions = (exceptions.totalExceptionCount - lastExceptions.totalExceptionCount) || 0; - var elapsedMs = exceptions.time - lastExceptions.time; - var elapsedSeconds = elapsedMs / 1000; - - if (elapsedMs > 0) { - var exceptionsPerSec = intervalExceptions / elapsedSeconds; - this._client.trackMetric({ name: Constants.QuickPulseCounter.EXCEPTION_RATE, value: exceptionsPerSec }); - } - this._lastExceptions = exceptions; - } - } - - public dispose() { - AutoCollectPerformance.INSTANCE = null; - this.enable(false); - this._isInitialized = false; - } -} - -export = AutoCollectPerformance; diff --git a/AutoCollection/PreAggregatedMetrics.ts b/AutoCollection/PreAggregatedMetrics.ts deleted file mode 100644 index 3f96d472a..000000000 --- a/AutoCollection/PreAggregatedMetrics.ts +++ /dev/null @@ -1,289 +0,0 @@ -import TelemetryClient = require("../Library/TelemetryClient"); -import Constants = require("../Declarations/Constants"); - -import { AggregatedMetric } from "../Declarations/Metrics/AggregatedMetric"; -import { AggregatedMetricCounter } from "../Declarations/Metrics/AggregatedMetricCounters"; -import { - MetricBaseDimensions, - MetricDependencyDimensions, - MetricExceptionDimensions, - MetricRequestDimensions, - MetricTraceDimensions, - PreaggregatedMetricPropertyNames, - MetricDimensionTypeKeys -} from "../Declarations/Metrics/AggregatedMetricDimensions"; -import * as Contracts from "../Declarations/Contracts"; - - -class AutoCollectPreAggregatedMetrics { - - public static INSTANCE: AutoCollectPreAggregatedMetrics; - private _collectionInterval: number; - private _client: TelemetryClient; - private _handle: NodeJS.Timer; - private _isEnabled: boolean; - private _isInitialized: boolean; - - private static _dependencyCountersCollection: Array; - private static _requestCountersCollection: Array; - private static _exceptionCountersCollection: Array; - private static _traceCountersCollection: Array; - - /** - * @param client - Telemetry Client - * @param collectionInterval - Metric collection interval in ms - */ - constructor(client: TelemetryClient, collectionInterval = 60000) { - if (!AutoCollectPreAggregatedMetrics.INSTANCE) { - AutoCollectPreAggregatedMetrics.INSTANCE = this; - } - - this._isInitialized = false; - AutoCollectPreAggregatedMetrics._dependencyCountersCollection = []; - AutoCollectPreAggregatedMetrics._requestCountersCollection = []; - AutoCollectPreAggregatedMetrics._exceptionCountersCollection = []; - AutoCollectPreAggregatedMetrics._traceCountersCollection = []; - this._client = client; - this._collectionInterval = collectionInterval; - } - - public enable(isEnabled: boolean, collectionInterval?: number) { - this._isEnabled = isEnabled; - if (this._isEnabled && !this._isInitialized) { - this._isInitialized = true; - } - - if (isEnabled) { - if (!this._handle) { - this._collectionInterval = collectionInterval || this._collectionInterval; - this._handle = setInterval(() => this.trackPreAggregatedMetrics(), this._collectionInterval); - this._handle.unref(); // Allow the app to terminate even while this loop is going on - } - } else { - if (this._handle) { - clearInterval(this._handle); - this._handle = undefined; - } - } - } - - public static countException(dimensions: MetricExceptionDimensions) { - if (!AutoCollectPreAggregatedMetrics.isEnabled()) { - return; - } - let counter: AggregatedMetricCounter = AutoCollectPreAggregatedMetrics._getAggregatedCounter(dimensions, this._exceptionCountersCollection); - counter.totalCount++; - } - - public static countTrace(dimensions: MetricTraceDimensions) { - if (!AutoCollectPreAggregatedMetrics.isEnabled()) { - return; - } - let counter: AggregatedMetricCounter = AutoCollectPreAggregatedMetrics._getAggregatedCounter(dimensions, this._traceCountersCollection); - counter.totalCount++; - } - - public static countRequest(duration: number | string, dimensions: MetricRequestDimensions) { - if (!AutoCollectPreAggregatedMetrics.isEnabled()) { - return; - } - let durationMs: number; - let counter: AggregatedMetricCounter = AutoCollectPreAggregatedMetrics._getAggregatedCounter(dimensions, this._requestCountersCollection); - if (typeof duration === 'string') { - // dependency duration is passed in as "00:00:00.123" by autocollectors - durationMs = +new Date('1970-01-01T' + duration + 'Z'); // convert to num ms, returns NaN if wrong - } else if (typeof duration === 'number') { - durationMs = duration; - } else { - return; - } - counter.intervalExecutionTime += durationMs; - counter.totalCount++; - } - - public static countDependency(duration: number | string, dimensions: MetricDependencyDimensions) { - if (!AutoCollectPreAggregatedMetrics.isEnabled()) { - return; - } - let counter: AggregatedMetricCounter = AutoCollectPreAggregatedMetrics._getAggregatedCounter(dimensions, this._dependencyCountersCollection); - let durationMs: number; - if (typeof duration === 'string') { - // dependency duration is passed in as "00:00:00.123" by autocollectors - durationMs = +new Date('1970-01-01T' + duration + 'Z'); // convert to num ms, returns NaN if wrong - } else if (typeof duration === 'number') { - durationMs = duration; - } else { - return; - } - counter.intervalExecutionTime += durationMs; - counter.totalCount++; - } - - public isInitialized() { - return this._isInitialized; - } - - public static isEnabled() { - return AutoCollectPreAggregatedMetrics.INSTANCE && AutoCollectPreAggregatedMetrics.INSTANCE._isEnabled; - } - - public trackPreAggregatedMetrics() { - this._trackRequestMetrics(); - this._trackDependencyMetrics(); - this._trackExceptionMetrics(); - this._trackTraceMetrics(); - } - - private static _getAggregatedCounter(dimensions: MetricBaseDimensions, counterCollection: Array): AggregatedMetricCounter { - let notMatch = false; - // Check if counter with specified dimensions is available - for (let i = 0; i < counterCollection.length; i++) { - // Same object - if (dimensions === counterCollection[i].dimensions) { - return counterCollection[i]; - } - // Diferent number of keys skip - if (Object.keys(dimensions).length !== Object.keys(counterCollection[i].dimensions).length) { - continue; - } - // Check dimension values - for (let dim in dimensions) { - if ((dimensions)[dim] != (counterCollection[i].dimensions)[dim]) { - notMatch = true; - break; - } - } - if (!notMatch) { // Found - return counterCollection[i]; - } - notMatch = false; - } - // Create a new one if not found - let newCounter = new AggregatedMetricCounter(dimensions); - counterCollection.push(newCounter); - return newCounter; - } - - private _trackRequestMetrics() { - for (let i = 0; i < AutoCollectPreAggregatedMetrics._requestCountersCollection.length; i++) { - var currentCounter = AutoCollectPreAggregatedMetrics._requestCountersCollection[i]; - currentCounter.time = +new Date; - var intervalRequests = (currentCounter.totalCount - currentCounter.lastTotalCount) || 0; - var elapsedMs = currentCounter.time - currentCounter.lastTime; - var averageRequestExecutionTime = ((currentCounter.intervalExecutionTime - currentCounter.lastIntervalExecutionTime) / intervalRequests) || 0; - currentCounter.lastIntervalExecutionTime = currentCounter.intervalExecutionTime; // reset - if (elapsedMs > 0 && intervalRequests > 0) { - this._trackPreAggregatedMetric({ - name: "Server response time", - dimensions: currentCounter.dimensions, - value: averageRequestExecutionTime, - count: intervalRequests, - aggregationInterval: elapsedMs, - metricType: Constants.MetricId.REQUESTS_DURATION, - }); - } - // Set last counters - currentCounter.lastTotalCount = currentCounter.totalCount; - currentCounter.lastTime = currentCounter.time; - } - } - - private _trackDependencyMetrics() { - for (let i = 0; i < AutoCollectPreAggregatedMetrics._dependencyCountersCollection.length; i++) { - var currentCounter = AutoCollectPreAggregatedMetrics._dependencyCountersCollection[i]; - currentCounter.time = +new Date; - var intervalDependencies = (currentCounter.totalCount - currentCounter.lastTotalCount) || 0; - var elapsedMs = currentCounter.time - currentCounter.lastTime; - var averageDependencyExecutionTime = ((currentCounter.intervalExecutionTime - currentCounter.lastIntervalExecutionTime) / intervalDependencies) || 0; - currentCounter.lastIntervalExecutionTime = currentCounter.intervalExecutionTime; // reset - if (elapsedMs > 0 && intervalDependencies > 0) { - this._trackPreAggregatedMetric({ - name: "Dependency duration", - dimensions: currentCounter.dimensions, - value: averageDependencyExecutionTime, - count: intervalDependencies, - aggregationInterval: elapsedMs, - metricType: Constants.MetricId.DEPENDENCIES_DURATION, - }); - } - // Set last counters - currentCounter.lastTotalCount = currentCounter.totalCount; - currentCounter.lastTime = currentCounter.time; - } - } - - private _trackExceptionMetrics() { - for (let i = 0; i < AutoCollectPreAggregatedMetrics._exceptionCountersCollection.length; i++) { - var currentCounter = AutoCollectPreAggregatedMetrics._exceptionCountersCollection[i]; - currentCounter.time = +new Date; - var intervalExceptions = (currentCounter.totalCount - currentCounter.lastTotalCount) || 0; - var elapsedMs = currentCounter.time - currentCounter.lastTime; - if (elapsedMs > 0 && intervalExceptions > 0) { - this._trackPreAggregatedMetric({ - name: "Exceptions", - dimensions: currentCounter.dimensions, - value: intervalExceptions, - count: intervalExceptions, - aggregationInterval: elapsedMs, - metricType: Constants.MetricId.EXCEPTIONS_COUNT, - }); - } - // Set last counters - currentCounter.lastTotalCount = currentCounter.totalCount; - currentCounter.lastTime = currentCounter.time; - } - } - - private _trackTraceMetrics() { - for (let i = 0; i < AutoCollectPreAggregatedMetrics._traceCountersCollection.length; i++) { - var currentCounter = AutoCollectPreAggregatedMetrics._traceCountersCollection[i]; - currentCounter.time = +new Date; - var intervalTraces = (currentCounter.totalCount - currentCounter.lastTotalCount) || 0; - var elapsedMs = currentCounter.time - currentCounter.lastTime; - if (elapsedMs > 0 && intervalTraces > 0) { - this._trackPreAggregatedMetric({ - name: "Traces", - dimensions: currentCounter.dimensions, - value: intervalTraces, - count: intervalTraces, - aggregationInterval: elapsedMs, - metricType: Constants.MetricId.TRACES_COUNT, - }); - } - // Set last counters - currentCounter.lastTotalCount = currentCounter.totalCount; - currentCounter.lastTime = currentCounter.time; - } - } - - private _trackPreAggregatedMetric(metric: AggregatedMetric) { - // Build metric properties - let metricProperties: any = {}; - for (let dim in metric.dimensions) { - metricProperties[PreaggregatedMetricPropertyNames[dim as MetricDimensionTypeKeys]] = metric.dimensions[dim]; - } - metricProperties = { - ...metricProperties, - "_MS.MetricId": metric.metricType, - "_MS.AggregationIntervalMs": String(metric.aggregationInterval), - "_MS.IsAutocollected": "True", - }; - - let telemetry: Contracts.MetricTelemetry = { - name: metric.name, - value: metric.value, - count: metric.count, - properties: metricProperties, - kind: "Aggregation", - }; - this._client.trackMetric(telemetry); - } - - public dispose() { - AutoCollectPreAggregatedMetrics.INSTANCE = null; - this.enable(false); - this._isInitialized = false; - } -} - -export = AutoCollectPreAggregatedMetrics; diff --git a/AutoCollection/RequestParser.ts b/AutoCollection/RequestParser.ts deleted file mode 100644 index 724142bb1..000000000 --- a/AutoCollection/RequestParser.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Base class for helpers that read data from HTTP request/response objects and convert them - * into the telemetry contract objects. - */ -abstract class RequestParser { - protected method: string; - protected url: string; - protected startTime: number; - protected duration: number; - protected statusCode: number; - protected properties: { [key: string]: string }; - - /** - * Gets a url parsed out from request options - */ - public getUrl(): string { - return this.url; - } - - protected RequestParser() { - this.startTime = +new Date(); - } - - protected _setStatus(status: number, error: Error | string) { - let endTime = +new Date(); - this.duration = endTime - this.startTime; - this.statusCode = status; - - let properties: {[key: string]: string} = this.properties || {}; - if (error) { - if (typeof error === "string") { - properties["error"] = error; - } else if (error instanceof Error) { - properties["error"] = error.message; - } else if (typeof error === "object") { - for (var key in error) { - properties[key] = (error)[key] && (error)[key].toString && (error)[key].toString(); - } - } - } - - this.properties = properties; - } - - protected _isSuccess() { - return (0 < this.statusCode) && (this.statusCode < 400); - } -} - -export = RequestParser; diff --git a/AutoCollection/Statsbeat.ts b/AutoCollection/Statsbeat.ts deleted file mode 100644 index 49ce5336e..000000000 --- a/AutoCollection/Statsbeat.ts +++ /dev/null @@ -1,337 +0,0 @@ -import os = require("os"); -import EnvelopeFactory = require("../Library/EnvelopeFactory"); -import Logging = require("../Library/Logging"); -import Sender = require("../Library/Sender"); -import Constants = require("../Declarations/Constants"); -import Contracts = require("../Declarations/Contracts"); -import Vm = require("../Library/AzureVirtualMachine"); -import Config = require("../Library/Config"); -import Context = require("../Library/Context"); -import Network = require("./NetworkStatsbeat"); -import Util = require("../Library/Util"); - -const STATSBEAT_LANGUAGE = "node"; - -class Statsbeat { - - public static CONNECTION_STRING = "InstrumentationKey=c4a29126-a7cb-47e5-b348-11414998b11e;IngestionEndpoint=https://dc.services.visualstudio.com/"; - public static STATS_COLLECTION_SHORT_INTERVAL: number = 900000; // 15 minutes - public static STATS_COLLECTION_LONG_INTERVAL: number = 1440000; // 1 day - - private static TAG = "Statsbeat"; - - private _networkStatsbeatCollection: Array; - private _sender: Sender; - private _context: Context; - private _handle: NodeJS.Timer | null; - private _longHandle: NodeJS.Timer | null; - private _isEnabled: boolean; - private _isInitialized: boolean; - private _config: Config; - private _statsbeatConfig: Config; - private _isVM: boolean | undefined; - private _statbeatMetrics: Array<{ name: string; value: number, properties: {} }>; - - // Custom dimensions - private _resourceProvider: string; - private _resourceIdentifier: string; - private _sdkVersion: string; - private _runtimeVersion: string; - private _os: string; - private _language: string; - private _cikey: string; - private _attach: string = Constants.StatsbeatAttach.sdk; // Default is SDK - private _feature: number = Constants.StatsbeatFeature.NONE; - private _instrumentation: number = Constants.StatsbeatInstrumentation.NONE; - - constructor(config: Config, context?: Context) { - this._isInitialized = false; - this._statbeatMetrics = []; - this._networkStatsbeatCollection = []; - this._config = config; - this._context = context || new Context(); - this._statsbeatConfig = new Config(Statsbeat.CONNECTION_STRING); - this._statsbeatConfig.samplingPercentage = 100; // Do not sample - this._sender = new Sender(this._statsbeatConfig); - } - - public enable(isEnabled: boolean) { - this._isEnabled = isEnabled; - if (this._isEnabled && !this._isInitialized) { - this._getCustomProperties(); - this._isInitialized = true; - } - if (isEnabled) { - if (!this._handle) { - this._handle = setInterval(() => { - this.trackShortIntervalStatsbeats(); - }, Statsbeat.STATS_COLLECTION_SHORT_INTERVAL); - this._handle.unref(); // Allow the app to terminate even while this loop is going on - } - if (!this._longHandle) { - // On first enablement - this.trackLongIntervalStatsbeats(); - this._longHandle = setInterval(() => { - this.trackLongIntervalStatsbeats(); - }, Statsbeat.STATS_COLLECTION_LONG_INTERVAL); - this._longHandle.unref(); // Allow the app to terminate even while this loop is going on - } - } else { - if (this._handle) { - clearInterval(this._handle); - this._handle = null; - } - if (this._longHandle) { - clearInterval(this._longHandle); - this._longHandle = null; - } - } - } - - public isInitialized() { - return this._isInitialized; - } - - public isEnabled() { - return this._isEnabled; - } - - public setCodelessAttach() { - this._attach = Constants.StatsbeatAttach.codeless; - } - - public addFeature(feature: Constants.StatsbeatFeature) { - this._feature |= feature; - } - - public removeFeature(feature: Constants.StatsbeatFeature) { - this._feature &= ~feature; - } - - public addInstrumentation(instrumentation: Constants.StatsbeatInstrumentation) { - this._instrumentation |= instrumentation; - } - - public removeInstrumentation(instrumentation: Constants.StatsbeatInstrumentation) { - this._instrumentation &= ~instrumentation; - } - - public countRequest(endpoint: number, host: string, duration: number, success: boolean) { - if (!this.isEnabled()) { - return; - } - let counter: Network.NetworkStatsbeat = this._getNetworkStatsbeatCounter(endpoint, host); - counter.totalRequestCount++; - counter.intervalRequestExecutionTime += duration; - if (success === false) { - counter.totalFailedRequestCount++; - } - else { - counter.totalSuccesfulRequestCount++; - } - } - - public countException(endpoint: number, host: string) { - if (!this.isEnabled()) { - return; - } - let counter: Network.NetworkStatsbeat = this._getNetworkStatsbeatCounter(endpoint, host); - counter.exceptionCount++; - } - - public countThrottle(endpoint: number, host: string) { - if (!this.isEnabled()) { - return; - } - let counter: Network.NetworkStatsbeat = this._getNetworkStatsbeatCounter(endpoint, host); - counter.throttleCount++; - } - - public countRetry(endpoint: number, host: string) { - if (!this.isEnabled()) { - return; - } - let counter: Network.NetworkStatsbeat = this._getNetworkStatsbeatCounter(endpoint, host); - counter.retryCount++; - } - - public async trackShortIntervalStatsbeats() { - try { - await this._getResourceProvider(); - let networkProperties = { - "os": this._os, - "rp": this._resourceProvider, - "cikey": this._cikey, - "runtimeVersion": this._runtimeVersion, - "language": this._language, - "version": this._sdkVersion, - "attach": this._attach, - } - this._trackRequestDuration(networkProperties); - this._trackRequestsCount(networkProperties); - await this._sendStatsbeats(); - } - catch (error) { - Logging.info(Statsbeat.TAG, "Failed to send Statsbeat metrics: " + Util.dumpObj(error)); - } - } - - public async trackLongIntervalStatsbeats() { - try { - await this._getResourceProvider(); - let commonProperties = { - "os": this._os, - "rp": this._resourceProvider, - "cikey": this._cikey, - "runtimeVersion": this._runtimeVersion, - "language": this._language, - "version": this._sdkVersion, - "attach": this._attach, - }; - let attachProperties = Object.assign({ - "rpId": this._resourceIdentifier, - }, commonProperties); - this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.ATTACH, value: 1, properties: attachProperties }); - if (this._instrumentation != Constants.StatsbeatInstrumentation.NONE) {// Only send if there are some instrumentations enabled - let instrumentationProperties = Object.assign({ "feature": this._instrumentation, "type": Constants.StatsbeatFeatureType.Instrumentation }, commonProperties); - this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.FEATURE, value: 1, properties: instrumentationProperties }); - } - if (this._feature != Constants.StatsbeatFeature.NONE) {// Only send if there are some features enabled - let featureProperties = Object.assign({ "feature": this._feature, "type": Constants.StatsbeatFeatureType.Feature }, commonProperties); - this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.FEATURE, value: 1, properties: featureProperties }); - } - await this._sendStatsbeats(); - } - catch (error) { - Logging.info(Statsbeat.TAG, "Failed to send Statsbeat metrics: " + Util.dumpObj(error)); - } - } - - private _getNetworkStatsbeatCounter(endpoint: number, host: string): Network.NetworkStatsbeat { - // Check if counter is available - for (let i = 0; i < this._networkStatsbeatCollection.length; i++) { - // Same object - if (endpoint === this._networkStatsbeatCollection[i].endpoint && - host === this._networkStatsbeatCollection[i].host) { - return this._networkStatsbeatCollection[i]; - } - } - // Create a new one if not found - let newCounter = new Network.NetworkStatsbeat(endpoint, host); - this._networkStatsbeatCollection.push(newCounter); - return newCounter; - } - - private _trackRequestDuration(commonProperties: {}) { - for (let i = 0; i < this._networkStatsbeatCollection.length; i++) { - var currentCounter = this._networkStatsbeatCollection[i]; - currentCounter.time = +new Date; - var intervalRequests = (currentCounter.totalRequestCount - currentCounter.lastRequestCount) || 0; - var averageRequestExecutionTime = ((currentCounter.intervalRequestExecutionTime - currentCounter.lastIntervalRequestExecutionTime) / intervalRequests) || 0; - currentCounter.lastIntervalRequestExecutionTime = currentCounter.intervalRequestExecutionTime; // reset - if (intervalRequests > 0) { - // Add extra properties - let properties = Object.assign({ "endpoint": this._networkStatsbeatCollection[i].endpoint, "host": this._networkStatsbeatCollection[i].host }, commonProperties); - this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.REQUEST_DURATION, value: averageRequestExecutionTime, properties: properties }); - } - // Set last counters - currentCounter.lastRequestCount = currentCounter.totalRequestCount; - currentCounter.lastTime = currentCounter.time; - } - } - - private _trackRequestsCount(commonProperties: {}) { - for (let i = 0; i < this._networkStatsbeatCollection.length; i++) { - var currentCounter = this._networkStatsbeatCollection[i]; - let properties = Object.assign({ "endpoint": currentCounter.endpoint, "host": currentCounter.host }, commonProperties); - if (currentCounter.totalSuccesfulRequestCount > 0) { - this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.REQUEST_SUCCESS, value: currentCounter.totalSuccesfulRequestCount, properties: properties }); - currentCounter.totalSuccesfulRequestCount = 0; //Reset - } - if (currentCounter.totalFailedRequestCount > 0) { - this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.REQUEST_FAILURE, value: currentCounter.totalFailedRequestCount, properties: properties }); - currentCounter.totalFailedRequestCount = 0; //Reset - } - if (currentCounter.retryCount > 0) { - this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.RETRY_COUNT, value: currentCounter.retryCount, properties: properties }); - currentCounter.retryCount = 0; //Reset - } - if (currentCounter.throttleCount > 0) { - this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.THROTTLE_COUNT, value: currentCounter.throttleCount, properties: properties }); - currentCounter.throttleCount = 0; //Reset - } - if (currentCounter.exceptionCount > 0) { - this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.EXCEPTION_COUNT, value: currentCounter.exceptionCount, properties: properties }); - currentCounter.exceptionCount = 0; //Reset - } - } - } - - private async _sendStatsbeats() { - let envelopes: Array = []; - for (let i = 0; i < this._statbeatMetrics.length; i++) { - let statsbeat: Contracts.MetricTelemetry = { - name: this._statbeatMetrics[i].name, - value: this._statbeatMetrics[i].value, - properties: this._statbeatMetrics[i].properties - }; - let envelope = EnvelopeFactory.createEnvelope(statsbeat, Contracts.TelemetryType.Metric, null, this._context, this._statsbeatConfig); - envelope.name = Constants.StatsbeatTelemetryName; - envelopes.push(envelope); - } - this._statbeatMetrics = []; - await this._sender.send(envelopes); - } - - private _getCustomProperties() { - this._language = STATSBEAT_LANGUAGE; - this._cikey = this._config.instrumentationKey; - this._sdkVersion = Context.sdkVersion; // "node" or "node-nativeperf" - this._os = os.type(); - this._runtimeVersion = process.version; - } - - private _getResourceProvider(): Promise { - return new Promise((resolve, reject) => { - // Check resource provider - let waiting: boolean = false; - this._resourceProvider = Constants.StatsbeatResourceProvider.unknown; - this._resourceIdentifier = Constants.StatsbeatResourceProvider.unknown; - if (process.env.WEBSITE_SITE_NAME) { // Web apps - this._resourceProvider = Constants.StatsbeatResourceProvider.appsvc; - this._resourceIdentifier = process.env.WEBSITE_SITE_NAME; - if (process.env.WEBSITE_HOME_STAMPNAME) { - this._resourceIdentifier += "/" + process.env.WEBSITE_HOME_STAMPNAME; - } - } else if (process.env.FUNCTIONS_WORKER_RUNTIME) { // Function apps - this._resourceProvider = Constants.StatsbeatResourceProvider.functions; - if (process.env.WEBSITE_HOSTNAME) { - this._resourceIdentifier = process.env.WEBSITE_HOSTNAME; - } - } else if (this._config) { - if (this._isVM === undefined || this._isVM == true) { - waiting = true; - Vm.AzureVirtualMachine.getAzureComputeMetadata(this._config, (vmInfo) => { - this._isVM = vmInfo.isVM; - if (this._isVM) { - this._resourceProvider = Constants.StatsbeatResourceProvider.vm; - this._resourceIdentifier = vmInfo.id + "/" + vmInfo.subscriptionId; - // Override OS as VM info have higher precedence - if (vmInfo.osType) { - this._os = vmInfo.osType; - } - } - resolve(); - }); - } else { - this._resourceProvider = Constants.StatsbeatResourceProvider.unknown; - } - } - if (!waiting) { - resolve(); - } - }); - } -} - -export = Statsbeat; diff --git a/AutoCollection/diagnostic-channel/Azure/EventHub.ts b/AutoCollection/diagnostic-channel/Azure/EventHub.ts deleted file mode 100644 index 5577defee..000000000 --- a/AutoCollection/diagnostic-channel/Azure/EventHub.ts +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for details. -import { SpanKind } from "@opentelemetry/api"; -import { hrTimeToMilliseconds } from "@opentelemetry/core"; -import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; -import { ReadableSpan } from "@opentelemetry/sdk-trace-base"; - -import { - TIME_SINCE_ENQUEUED, - ENQUEUED_TIME, - AzNamespace, - MessageBusDestination, - MicrosoftEventHub, - DependencyTypeName -} from "../../../Declarations/Constants"; -import { DependencyTelemetry, Identified, RequestTelemetry } from "../../../Declarations/Contracts"; - -/** - * Average span.links[].attributes.enqueuedTime - */ -const getTimeSinceEnqueued = (span: ReadableSpan) => { - let countEnqueueDiffs = 0; - let sumEnqueueDiffs = 0; - const startTimeMs = hrTimeToMilliseconds(span.startTime); - - span.links.forEach(({ attributes }) => { - const enqueuedTime = attributes?.[ENQUEUED_TIME] as string | number; - if (enqueuedTime) { - countEnqueueDiffs += 1; - sumEnqueueDiffs += startTimeMs - (parseFloat(enqueuedTime.toString()) || 0); - } - }); - - return Math.max(sumEnqueueDiffs / (countEnqueueDiffs || 1), 0); -}; - -/** - * Implementation of Mapping to Azure Monitor - * - * https://gist.github.com/lmolkova/e4215c0f44a49ef824983382762e6b92#file-z_azure_monitor_exporter_mapping-md - */ -export const parseEventHubSpan = (span: ReadableSpan, telemetry: (DependencyTelemetry | RequestTelemetry) & Identified): void => { - const namespace = span.attributes[AzNamespace] as typeof MicrosoftEventHub; - const peerAddress = ((span.attributes[SemanticAttributes.NET_PEER_NAME] || - span.attributes["peer.address"] || - "unknown") as string).replace(/\/$/g, ""); // remove trailing "/" - const messageBusDestination = (span.attributes[MessageBusDestination] || "unknown") as string; - - switch (span.kind) { - case SpanKind.CLIENT: - (telemetry).dependencyTypeName = namespace; - (telemetry).target = `${peerAddress}/${messageBusDestination}`; - break; - case SpanKind.PRODUCER: - (telemetry).dependencyTypeName = `${DependencyTypeName.QueueMessage} | ${namespace}`; - (telemetry).target = `${peerAddress}/${messageBusDestination}`; - break; - case SpanKind.CONSUMER: - (telemetry).source = `${peerAddress}/${messageBusDestination}`; - (telemetry).measurements = { - ...(telemetry).measurements, - [TIME_SINCE_ENQUEUED]: getTimeSinceEnqueued(span) - }; - break; - default: // no op - } -}; \ No newline at end of file diff --git a/AutoCollection/diagnostic-channel/SpanParser.ts b/AutoCollection/diagnostic-channel/SpanParser.ts deleted file mode 100644 index 22608622c..000000000 --- a/AutoCollection/diagnostic-channel/SpanParser.ts +++ /dev/null @@ -1,279 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for details. -import { URL } from "url"; -import { SpanKind, SpanStatusCode, Link } from "@opentelemetry/api"; -import { SemanticAttributes, DbSystemValues } from "@opentelemetry/semantic-conventions"; -import { ReadableSpan } from "@opentelemetry/sdk-trace-base"; - -import * as Contracts from "../../Declarations/Contracts"; -import * as Constants from "../../Declarations/Constants"; -import { parseEventHubSpan } from "./Azure/EventHub"; -import { DependencyTelemetry } from "../../Declarations/Contracts"; -import Util = require ("../../Library/Util"); - -function createPropertiesFromSpan(span: ReadableSpan): { [key: string]: any; } { - const properties: { [key: string]: any; } = {}; - for (const key of Object.keys(span.attributes)) { - if ( - !( - key.startsWith("http.") || - key.startsWith("rpc.") || - key.startsWith("db.") || - key.startsWith("peer.") || - key.startsWith("net.") - ) - ) { - properties[key] = span.attributes[key] as string; - } - } - const links: Array<{ operation_Id: string, id: string }> = span.links.map((link: Link) => ({ - operation_Id: link.context.traceId, - id: link.context.spanId - })); - if (links.length > 0) { - properties["_MS.links"] = Util.stringify(links); - } - return properties; -} - -function isSqlDB(dbSystem: string) { - return ( - dbSystem === DbSystemValues.DB2 || - dbSystem === DbSystemValues.DERBY || - dbSystem === DbSystemValues.MARIADB || - dbSystem === DbSystemValues.MSSQL || - dbSystem === DbSystemValues.ORACLE || - dbSystem === DbSystemValues.SQLITE || - dbSystem === DbSystemValues.OTHER_SQL || - dbSystem === DbSystemValues.HSQLDB || - dbSystem === DbSystemValues.H2 - ); -} - -function getUrl(span: ReadableSpan): string { - const httpMethod = span.attributes[SemanticAttributes.HTTP_METHOD]; - if (httpMethod) { - const httpUrl = span.attributes[SemanticAttributes.HTTP_URL]; - if (httpUrl) { - return String(httpUrl); - } else { - const httpScheme = span.attributes[SemanticAttributes.HTTP_SCHEME]; - const httpTarget = span.attributes[SemanticAttributes.HTTP_TARGET]; - if (httpScheme && httpTarget) { - const httpHost = span.attributes[SemanticAttributes.HTTP_HOST]; - if (httpHost) { - return `${httpScheme}://${httpHost}${httpTarget}`; - } else { - const netPeerPort = span.attributes[SemanticAttributes.NET_PEER_PORT]; - if (netPeerPort) { - const netPeerName = span.attributes[SemanticAttributes.NET_PEER_NAME]; - if (netPeerName) { - return `${httpScheme}://${netPeerName}:${netPeerPort}${httpTarget}`; - } else { - const netPeerIp = span.attributes[SemanticAttributes.NET_PEER_IP]; - if (netPeerIp) { - return `${httpScheme}://${netPeerIp}:${netPeerPort}${httpTarget}`; - } - } - } - } - } - } - } - return ""; -} - -function getDependencyTarget(span: ReadableSpan): string { - const peerService = span.attributes[SemanticAttributes.PEER_SERVICE]; - const httpHost = span.attributes[SemanticAttributes.HTTP_HOST]; - const httpUrl = span.attributes[SemanticAttributes.HTTP_URL]; - const netPeerName = span.attributes[SemanticAttributes.NET_PEER_NAME]; - const netPeerIp = span.attributes[SemanticAttributes.NET_PEER_IP]; - if (peerService) { - return String(peerService); - } else if (httpHost) { - return String(httpHost); - } else if (httpUrl) { - return String(httpUrl); - } else if (netPeerName) { - return String(netPeerName); - } else if (netPeerIp) { - return String(netPeerIp); - } - return ""; -} - -function createDependencyData(span: ReadableSpan): Contracts.DependencyTelemetry { - const remoteDependency: Contracts.DependencyTelemetry = { - name: span.name, - success: span.status.code != SpanStatusCode.ERROR, - resultCode: "0", - duration: 0, - data: "", - dependencyTypeName: "" - }; - if (span.kind === SpanKind.PRODUCER) { - remoteDependency.dependencyTypeName = Constants.DependencyTypeName.QueueMessage; - } - if (span.kind === SpanKind.INTERNAL && span.parentSpanId) { - remoteDependency.dependencyTypeName = Constants.DependencyTypeName.InProc; - } - - const httpMethod = span.attributes[SemanticAttributes.HTTP_METHOD]; - const dbSystem = span.attributes[SemanticAttributes.DB_SYSTEM]; - const rpcSystem = span.attributes[SemanticAttributes.RPC_SYSTEM]; - // HTTP Dependency - if (httpMethod) { - remoteDependency.dependencyTypeName = Constants.DependencyTypeName.Http; - const httpUrl = span.attributes[SemanticAttributes.HTTP_URL]; - if (httpUrl) { - var pathName = ""; - try { - let dependencyUrl = new URL(String(httpUrl)); - pathName = dependencyUrl.pathname; - } - catch (ex) { } - remoteDependency.name = `${httpMethod} ${pathName}`; - } - remoteDependency.data = getUrl(span); - const httpStatusCode = span.attributes[SemanticAttributes.HTTP_STATUS_CODE]; - if (httpStatusCode) { - remoteDependency.resultCode = String(httpStatusCode); - } - let target = getDependencyTarget(span); - if (target) { - try { - // Remove default port - let portRegex = new RegExp(/(https?)(:\/\/.*)(:\d+)(\S*)/); - let res = portRegex.exec(target); - if (res != null) { - let protocol = res[1]; - let port = res[3]; - if ((protocol == "https" && port == ":443") || (protocol == "http" && port == ":80")) { - // Drop port - target = res[1] + res[2] + res[4]; - } - } - } catch (error) { } - remoteDependency.target = `${target}`; - } - } - // DB Dependency - else if (dbSystem) { - // TODO: Remove special logic when Azure UX supports OpenTelemetry dbSystem - if (String(dbSystem) === DbSystemValues.MYSQL) { - remoteDependency.dependencyTypeName = "mysql"; - } else if (String(dbSystem) === DbSystemValues.POSTGRESQL) { - remoteDependency.dependencyTypeName = "postgresql"; - } else if (String(dbSystem) === DbSystemValues.MONGODB) { - remoteDependency.dependencyTypeName = "mongodb"; - } else if (String(dbSystem) === DbSystemValues.REDIS) { - remoteDependency.dependencyTypeName = "redis"; - } else if (isSqlDB(String(dbSystem))) { - remoteDependency.dependencyTypeName = "SQL"; - } else { - remoteDependency.dependencyTypeName = String(dbSystem); - } - const dbStatement = span.attributes[SemanticAttributes.DB_STATEMENT]; - const dbOperation = span.attributes[SemanticAttributes.DB_OPERATION]; - if (dbStatement) { - remoteDependency.data = String(dbStatement); - } - else if (dbOperation) { - remoteDependency.data = String(dbOperation); - } - let target = getDependencyTarget(span); - const dbName = span.attributes[SemanticAttributes.DB_NAME]; - if (target) { - remoteDependency.target = dbName ? `${target}|${dbName}` : `${target}`; - } else { - remoteDependency.target = dbName ? `${dbName}` : `${dbSystem}`; - } - } - // grpc Dependency - else if (rpcSystem) { - remoteDependency.dependencyTypeName = Constants.DependencyTypeName.Grpc; - const grpcStatusCode = span.attributes[SemanticAttributes.RPC_GRPC_STATUS_CODE]; - if (grpcStatusCode) { - remoteDependency.resultCode = String(grpcStatusCode); - } - let target = getDependencyTarget(span); - if (target) { - remoteDependency.target = `${target}`; - } else if (rpcSystem) { - remoteDependency.target = String(rpcSystem); - } - } - return remoteDependency; -} - -function createRequestData(span: ReadableSpan): Contracts.RequestTelemetry { - const requestData: Contracts.RequestTelemetry = { - name: span.name, - success: span.status.code != SpanStatusCode.ERROR, - resultCode: "0", - duration: 0, - url: "", - source: undefined - }; - const httpMethod = span.attributes[SemanticAttributes.HTTP_METHOD]; - const grpcStatusCode = span.attributes[SemanticAttributes.RPC_GRPC_STATUS_CODE]; - if (httpMethod) { - // Try to get request name for server spans - if (span.kind == SpanKind.SERVER) { - const httpRoute = span.attributes[SemanticAttributes.HTTP_ROUTE]; - const httpUrl = span.attributes[SemanticAttributes.HTTP_URL]; - if (httpRoute) { - requestData.name = `${httpMethod as string} ${httpRoute as string}`; - } - else if (httpUrl) { - try { - let url = new URL(String(httpUrl)); - requestData.name = `${httpMethod} ${url.pathname}`; - } - catch (ex) { } - } - } - requestData.url = getUrl(span); - const httpStatusCode = span.attributes[SemanticAttributes.HTTP_STATUS_CODE]; - if (httpStatusCode) { - requestData.resultCode = String(httpStatusCode); - } - } else if (grpcStatusCode) { - requestData.resultCode = String(grpcStatusCode); - } - return requestData; -} - -export function spanToTelemetryContract(span: ReadableSpan): (Contracts.DependencyTelemetry | Contracts.RequestTelemetry) & Contracts.Identified { - let telemetry: (Contracts.DependencyTelemetry | Contracts.RequestTelemetry) & Contracts.Identified; - switch (span.kind) { - case SpanKind.CLIENT: - case SpanKind.PRODUCER: - case SpanKind.INTERNAL: - telemetry = createDependencyData(span); - break; - case SpanKind.SERVER: - case SpanKind.CONSUMER: - telemetry = createRequestData(span); - break; - } - - const spanContext = span.spanContext ? span.spanContext() : (span).context(); // context is available in OT API telemetry).dependencyTypeName = `${Constants.DependencyTypeName.InProc} | ${span.attributes[Constants.AzNamespace]}` - } - if (span.attributes[Constants.AzNamespace] === Constants.MicrosoftEventHub) { - parseEventHubSpan(span, telemetry); - } - } - return telemetry; -} diff --git a/AutoCollection/diagnostic-channel/azure-coretracing.sub.ts b/AutoCollection/diagnostic-channel/azure-coretracing.sub.ts deleted file mode 100644 index 7967007e9..000000000 --- a/AutoCollection/diagnostic-channel/azure-coretracing.sub.ts +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for details. -import { Span } from "@opentelemetry/sdk-trace-base"; -import { SpanKind } from "@opentelemetry/api"; - -import TelemetryClient = require("../../Library/TelemetryClient"); -import { StatsbeatInstrumentation } from "../../Declarations/Constants"; -import { channel, IStandardEvent, trueFilter } from "diagnostic-channel"; - -import * as SpanParser from "./SpanParser"; -import { AsyncScopeManager } from "../AsyncHooksScopeManager"; -import { DependencyTelemetry, RequestTelemetry } from "../../Declarations/Contracts"; - -let clients: TelemetryClient[] = []; - -export const subscriber = (event: IStandardEvent) => { - try { - const span = event.data; - const telemetry = SpanParser.spanToTelemetryContract(span); - AsyncScopeManager.with(span, () => { - clients.forEach((client) => { - if (span.kind === SpanKind.SERVER || span.kind === SpanKind.CONSUMER) { - client.trackRequest(telemetry); - } else if (span.kind === SpanKind.CLIENT || span.kind === SpanKind.INTERNAL || span.kind === SpanKind.PRODUCER) { - client.trackDependency(telemetry); - } - }); - }); - } - catch (err) { { /** ignore errors */ } } -}; - -export function enable(enabled: boolean, client: TelemetryClient) { - if (enabled) { - let clientFound = clients.find(c => c == client); - if (clientFound) { - return; - } - if (clients.length === 0) { - channel.subscribe("azure-coretracing", subscriber, trueFilter, (module, version) => { - let statsbeat = client.getStatsbeat(); - if (statsbeat) { - statsbeat.addInstrumentation(StatsbeatInstrumentation.AZURE_CORE_TRACING); - } - }); - - }; - clients.push(client); - } else { - clients = clients.filter((c) => c != client); - if (clients.length === 0) { - channel.unsubscribe("azure-coretracing", subscriber); - } - } -} diff --git a/AutoCollection/diagnostic-channel/bunyan.sub.ts b/AutoCollection/diagnostic-channel/bunyan.sub.ts deleted file mode 100755 index bd1ee9bad..000000000 --- a/AutoCollection/diagnostic-channel/bunyan.sub.ts +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for details. -import TelemetryClient = require("../../Library/TelemetryClient"); -import { SeverityLevel } from "../../Declarations/Contracts"; -import { StatsbeatInstrumentation } from "../../Declarations/Constants"; - -import { channel, IStandardEvent, trueFilter } from "diagnostic-channel"; - -import { bunyan } from "diagnostic-channel-publishers"; - -let clients: TelemetryClient[] = []; - -// Mapping from bunyan levels defined at https://github.com/trentm/node-bunyan/blob/master/lib/bunyan.js#L256 -const bunyanToAILevelMap: { [key: number]: number } = { - 10: SeverityLevel.Verbose, - 20: SeverityLevel.Verbose, - 30: SeverityLevel.Information, - 40: SeverityLevel.Warning, - 50: SeverityLevel.Error, - 60: SeverityLevel.Critical, -}; - -const subscriber = (event: IStandardEvent) => { - let message = event.data.result as string; - clients.forEach((client) => { - try { - // Try to parse message as Bunyan log is JSON - let log: any = JSON.parse(message); - if (log.err) { - client.trackException({ exception: log.err }); - return; - } - } - catch (err) { } - const AIlevel = bunyanToAILevelMap[event.data.level]; - client.trackTrace({ message: message, severity: AIlevel }); - }); -}; - -export function enable(enabled: boolean, client: TelemetryClient) { - if (enabled) { - let clientFound = clients.find(c => c == client); - if (clientFound) { - return; - } - if (clients.length === 0) { - channel.subscribe("bunyan", subscriber, trueFilter, (module, version) => { - let statsbeat = client.getStatsbeat(); - if (statsbeat) { - statsbeat.addInstrumentation(StatsbeatInstrumentation.BUNYAN); - } - }); - }; - clients.push(client); - } else { - clients = clients.filter((c) => c != client); - if (clients.length === 0) { - channel.unsubscribe("bunyan", subscriber); - } - } -} - -export function dispose() { - channel.unsubscribe("bunyan", subscriber); - clients = []; -} diff --git a/AutoCollection/diagnostic-channel/console.sub.ts b/AutoCollection/diagnostic-channel/console.sub.ts deleted file mode 100755 index 8fe9684e1..000000000 --- a/AutoCollection/diagnostic-channel/console.sub.ts +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for details. -import TelemetryClient = require("../../Library/TelemetryClient"); -import { SeverityLevel } from "../../Declarations/Contracts"; -import { StatsbeatInstrumentation } from "../../Declarations/Constants"; - -import { channel, IStandardEvent, trueFilter } from "diagnostic-channel"; - -import { console as consolePub } from "diagnostic-channel-publishers"; - -let clients: TelemetryClient[] = []; - -const subscriber = (event: IStandardEvent) => { - let message = event.data.message as Error | string; - clients.forEach((client) => { - if (message instanceof Error) { - client.trackException({ exception: message }); - } else { - // Message can have a trailing newline - if (message.lastIndexOf("\n") == message.length - 1) { - message = message.substring(0, message.length - 1); - } - client.trackTrace({ message: message, severity: (event.data.stderr ? SeverityLevel.Warning : SeverityLevel.Information) }); - } - }); -}; - -export function enable(enabled: boolean, client: TelemetryClient) { - if (enabled) { - let clientFound = clients.find(c => c == client); - if (clientFound) { - return; - } - if (clients.length === 0) { - channel.subscribe("console", subscriber, trueFilter, (module, version) => { - let statsbeat = client.getStatsbeat(); - if (statsbeat) { - statsbeat.addInstrumentation(StatsbeatInstrumentation.CONSOLE); - } - }); - }; - clients.push(client); - } else { - clients = clients.filter((c) => c != client); - if (clients.length === 0) { - channel.unsubscribe("console", subscriber); - } - } -} - -export function dispose() { - channel.unsubscribe("console", subscriber); - clients = []; -} diff --git a/AutoCollection/diagnostic-channel/initialization.ts b/AutoCollection/diagnostic-channel/initialization.ts deleted file mode 100755 index 1e108b07d..000000000 --- a/AutoCollection/diagnostic-channel/initialization.ts +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for details. - -// Don't reference modules from these directly. Use only for types. -// This is to avoid requiring the actual module if the NO_DIAGNOSTIC_CHANNEL env is present -import * as DiagChannelPublishers from "diagnostic-channel-publishers"; -import * as DiagChannel from "diagnostic-channel"; -import { AsyncScopeManager } from "../AsyncHooksScopeManager"; -import Logging = require("../../Library/Logging"); -import { JsonConfig } from "../../Library/JsonConfig"; - -export const IsInitialized = !JsonConfig.getInstance().noDiagnosticChannel; -const TAG = "DiagnosticChannel"; - -if (IsInitialized) { - const publishers: typeof DiagChannelPublishers = require("diagnostic-channel-publishers"); - const individualOptOuts: string = JsonConfig.getInstance().noPatchModules; - const unpatchedModules = individualOptOuts.split(","); - const modules: {[key: string] : any} = { - bunyan: publishers.bunyan, - console: publishers.console, - mongodb: publishers.mongodb, - mongodbCore: publishers.mongodbCore, - mysql: publishers.mysql, - redis: publishers.redis, - pg: publishers.pg, - pgPool: publishers.pgPool, - winston: publishers.winston, - azuresdk: publishers.azuresdk - }; - for (const mod in modules) { - if (unpatchedModules.indexOf(mod) === -1) { - modules[mod].enable(); - Logging.info(TAG, `Subscribed to ${mod} events`); - } - } - if (unpatchedModules.length > 0) { - Logging.info(TAG, "Some modules will not be patched", unpatchedModules); - } -} else { - Logging.info(TAG, "Not subscribing to dependency autocollection because APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL was set"); -} - -export function registerContextPreservation(cb: (cb: Function) => Function) { - if (!IsInitialized) { - return; - } - const diagChannel = (require("diagnostic-channel") as typeof DiagChannel); - diagChannel.channel.addContextPreservation(cb); -} diff --git a/AutoCollection/diagnostic-channel/mongodb.sub.ts b/AutoCollection/diagnostic-channel/mongodb.sub.ts deleted file mode 100755 index b065402e2..000000000 --- a/AutoCollection/diagnostic-channel/mongodb.sub.ts +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for details. -import TelemetryClient = require("../../Library/TelemetryClient"); -import { StatsbeatInstrumentation } from "../../Declarations/Constants"; -import { channel, IStandardEvent, trueFilter } from "diagnostic-channel"; - -import { mongodb } from "diagnostic-channel-publishers"; - -let clients: TelemetryClient[] = []; - -export const subscriber = (event: IStandardEvent) => { - if (event.data.event.commandName === "ismaster") { - // suppress noisy ismaster commands - return; - } - clients.forEach((client) => { - const dbName = (event.data.startedData && event.data.startedData.databaseName) || "Unknown database"; - client.trackDependency( - { - target: dbName, - data: event.data.event.commandName, - name: event.data.event.commandName, - duration: event.data.event.duration, - success: event.data.succeeded, - /* TODO: transmit result code from mongo */ - resultCode: event.data.succeeded ? "0" : "1", - time: event.data.startedData.time, - dependencyTypeName: 'mongodb' - }); - }); -}; - -export function enable(enabled: boolean, client: TelemetryClient) { - if (enabled) { - let clientFound = clients.find(c => c == client); - if (clientFound) { - return; - } - if (clients.length === 0) { - channel.subscribe("mongodb", subscriber, trueFilter, (module, version) => { - let statsbeat = client.getStatsbeat(); - if (statsbeat) { - statsbeat.addInstrumentation(StatsbeatInstrumentation.MONGODB); - } - }); - }; - clients.push(client); - } else { - clients = clients.filter((c) => c != client); - if (clients.length === 0) { - channel.unsubscribe("mongodb", subscriber); - } - } -} diff --git a/AutoCollection/diagnostic-channel/mysql.sub.ts b/AutoCollection/diagnostic-channel/mysql.sub.ts deleted file mode 100755 index 881b7ee76..000000000 --- a/AutoCollection/diagnostic-channel/mysql.sub.ts +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for details. -import TelemetryClient = require("../../Library/TelemetryClient"); -import { StatsbeatInstrumentation } from "../../Declarations/Constants"; -import {channel, IStandardEvent, trueFilter} from "diagnostic-channel"; - -import {mysql} from "diagnostic-channel-publishers"; - -let clients: TelemetryClient[] = []; - -export const subscriber = (event: IStandardEvent) => { - clients.forEach((client) => { - const queryObj = event.data.query || {}; - const sqlString = queryObj.sql || "Unknown query"; - const success = !event.data.err; - - const connection = queryObj._connection || {}; - const connectionConfig = connection.config || {}; - const dbName = connectionConfig.socketPath ? connectionConfig.socketPath : `${connectionConfig.host || "localhost"}:${connectionConfig.port}`; - client.trackDependency( - { - target: dbName, - data: sqlString, - name: sqlString, - duration: event.data.duration, - success: success, - /* TODO: transmit result code from mysql */ - resultCode: success? "0": "1", - time: event.data.time, - dependencyTypeName: "mysql" - }); - }); -}; - -export function enable(enabled: boolean, client: TelemetryClient) { - if (enabled) { - let clientFound = clients.find(c => c == client); - if (clientFound) { - return; - } - if (clients.length === 0) { - channel.subscribe("mysql", subscriber, trueFilter, (module, version) => { - let statsbeat = client.getStatsbeat(); - if (statsbeat) { - statsbeat.addInstrumentation(StatsbeatInstrumentation.MYSQL); - } - }); - }; - clients.push(client); - } else { - clients = clients.filter((c) => c != client); - if (clients.length === 0) { - channel.unsubscribe("mysql", subscriber); - } - } -} diff --git a/AutoCollection/diagnostic-channel/postgres.sub.ts b/AutoCollection/diagnostic-channel/postgres.sub.ts deleted file mode 100644 index 031dcc232..000000000 --- a/AutoCollection/diagnostic-channel/postgres.sub.ts +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for details. -import TelemetryClient = require("../../Library/TelemetryClient"); -import { StatsbeatInstrumentation } from "../../Declarations/Constants"; -import { channel, IStandardEvent, trueFilter } from "diagnostic-channel"; - -import { pg } from "diagnostic-channel-publishers"; - -let clients: TelemetryClient[] = []; - -export const subscriber = (event: IStandardEvent) => { - clients.forEach((client) => { - const q = event.data.query; - const sql = (q.preparable && q.preparable.text) || q.plan || q.text || "unknown query"; - const success = !event.data.error; - const conn = `${event.data.database.host}:${event.data.database.port}`; - client.trackDependency({ - target: conn, - data: sql, - name: sql, - duration: event.data.duration, - success: success, - resultCode: success ? "0" : "1", - time: event.data.time, - dependencyTypeName: "postgres" - }); - }); -}; - -export function enable(enabled: boolean, client: TelemetryClient) { - if (enabled) { - let clientFound = clients.find(c => c == client); - if (clientFound) { - return; - } - if (clients.length === 0) { - channel.subscribe("postgres", subscriber, trueFilter, (module, version) => { - let statsbeat = client.getStatsbeat(); - if (statsbeat) { - statsbeat.addInstrumentation(StatsbeatInstrumentation.POSTGRES); - } - }); - }; - clients.push(client); - } else { - clients = clients.filter((c) => c != client); - if (clients.length === 0) { - channel.unsubscribe("postgres", subscriber); - } - } -} diff --git a/AutoCollection/diagnostic-channel/redis.sub.ts b/AutoCollection/diagnostic-channel/redis.sub.ts deleted file mode 100755 index 99b31d2ff..000000000 --- a/AutoCollection/diagnostic-channel/redis.sub.ts +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for details. -import TelemetryClient = require("../../Library/TelemetryClient"); -import { StatsbeatInstrumentation } from "../../Declarations/Constants"; -import { channel, IStandardEvent, trueFilter } from "diagnostic-channel"; - -import { redis } from "diagnostic-channel-publishers"; - -let clients: TelemetryClient[] = []; - -export const subscriber = (event: IStandardEvent) => { - clients.forEach((client) => { - if (event.data.commandObj.command === "info") { - // We don't want to report 'info', it's irrelevant - return; - } - client.trackDependency( - { - target: event.data.address, - name: event.data.commandObj.command, - data: event.data.commandObj.command, - duration: event.data.duration, - success: !event.data.err, - /* TODO: transmit result code from redis */ - resultCode: event.data.err ? "1" : "0", - time: event.data.time, - dependencyTypeName: "redis" - }); - - }); -}; - -export function enable(enabled: boolean, client: TelemetryClient) { - if (enabled) { - let clientFound = clients.find(c => c == client); - if (clientFound) { - return; - } - if (clients.length === 0) { - channel.subscribe("redis", subscriber, trueFilter, (module, version) => { - let statsbeat = client.getStatsbeat(); - if (statsbeat) { - statsbeat.addInstrumentation(StatsbeatInstrumentation.REDIS); - } - }); - }; - clients.push(client); - } else { - clients = clients.filter((c) => c != client); - if (clients.length === 0) { - channel.unsubscribe("redis", subscriber); - } - } -} diff --git a/AutoCollection/diagnostic-channel/winston.sub.ts b/AutoCollection/diagnostic-channel/winston.sub.ts deleted file mode 100644 index 5300e8e94..000000000 --- a/AutoCollection/diagnostic-channel/winston.sub.ts +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for details. -import TelemetryClient = require("../../Library/TelemetryClient"); -import { StatsbeatInstrumentation } from "../../Declarations/Constants"; -import { SeverityLevel } from "../../Declarations/Contracts"; - -import { channel, IStandardEvent, trueFilter } from "diagnostic-channel"; - -import { winston } from "diagnostic-channel-publishers"; - -let clients: TelemetryClient[] = []; - -const winstonToAILevelMap: { [key: string]: (og: string) => number } = { - syslog(og: string) { - const map: { [key: string]: number } = { - emerg: SeverityLevel.Critical, - alert: SeverityLevel.Critical, - crit: SeverityLevel.Critical, - error: SeverityLevel.Error, - warning: SeverityLevel.Warning, - notice: SeverityLevel.Information, - info: SeverityLevel.Information, - debug: SeverityLevel.Verbose - }; - - return map[og] === undefined ? SeverityLevel.Information : map[og]; - }, - npm(og: string) { - const map: { [key: string]: number } = { - error: SeverityLevel.Error, - warn: SeverityLevel.Warning, - info: SeverityLevel.Information, - verbose: SeverityLevel.Verbose, - debug: SeverityLevel.Verbose, - silly: SeverityLevel.Verbose - }; - - return map[og] === undefined ? SeverityLevel.Information : map[og]; - }, - unknown(og: string) { - return SeverityLevel.Information; - } -}; - -const subscriber = (event: IStandardEvent) => { - const message = event.data.message as Error | string; - clients.forEach((client) => { - if (message instanceof Error) { - client.trackException({ - exception: message, - properties: event.data.meta - }); - } else { - const AIlevel = winstonToAILevelMap[event.data.levelKind](event.data.level); - client.trackTrace({ - message: message, - severity: AIlevel, - properties: event.data.meta - }); - } - }); -}; - -export function enable(enabled: boolean, client: TelemetryClient) { - if (enabled) { - let clientFound = clients.find(c => c == client); - if (clientFound) { - return; - } - if (clients.length === 0) { - channel.subscribe("winston", subscriber, trueFilter, (module, version) => { - let statsbeat = client.getStatsbeat(); - if (statsbeat) { - statsbeat.addInstrumentation(StatsbeatInstrumentation.WINSTON); - } - }); - }; - clients.push(client); - } else { - clients = clients.filter((c) => c != client); - if (clients.length === 0) { - channel.unsubscribe("winston", subscriber); - } - } -} - -export function dispose() { - channel.unsubscribe("winston", subscriber); - clients = []; -} \ No newline at end of file diff --git a/Bootstrap/DataModel.ts b/Bootstrap/DataModel.ts deleted file mode 100644 index 532b7ae3b..000000000 --- a/Bootstrap/DataModel.ts +++ /dev/null @@ -1,43 +0,0 @@ -export interface AgentLogger { - log(message?: any, ...optional: any[]): void; - error(message?: any, ...optional: any[]): void; -} - -export const enum SeverityLevel { - ERROR = "ERROR", - WARN = "WARN", - INFO = "INFO" -}; - -export interface DiagnosticLog { - /** - * UTC - */ - time: string; - - /** - * Log severity, INFO, WARN, ERROR - */ - level: SeverityLevel; - - /** - * The logger writing this message. Usually the fully-qualified class or package name - */ - logger: string; - - /** - * The log message - */ - message: string; - - /** - * Exception (as string) - */ - exception?: string - - /** - * Any custom data related to the error/application/operation. Each field should have a string value - * Examples: operation, siteName, ikey, extensionVersion, sdkVersion, subscriptionId - */ - properties: { [key: string]: string }; -} diff --git a/Bootstrap/Default.ts b/Bootstrap/Default.ts deleted file mode 100644 index 8576c83bb..000000000 --- a/Bootstrap/Default.ts +++ /dev/null @@ -1,134 +0,0 @@ -import azureCore = require("@azure/core-http"); - -import * as types from "../applicationinsights"; -import * as Helpers from "./Helpers"; -import Constants = require("../Declarations/Constants"); -import { StatusLogger, StatusContract } from "./StatusLogger"; -import { DiagnosticLogger } from "./DiagnosticLogger"; -import { JsonConfig } from "../Library/JsonConfig"; - -// Private configuration vars -let _appInsights: typeof types | null; -let _prefix = "ad_"; // App Services, Default -let _logger: DiagnosticLogger = new DiagnosticLogger(console); -let _statusLogger: StatusLogger = new StatusLogger(console); - -// Env var local constants -const _setupString = JsonConfig.getInstance().connectionString || process.env.APPINSIGHTS_INSTRUMENTATIONKEY; -const forceStart = process.env.APPLICATIONINSIGHTS_FORCE_START === "true"; - -// Other local constants -const defaultStatus: StatusContract = { - ...StatusLogger.DEFAULT_STATUS, - Ikey: _setupString, -}; - -/** - * Sets the attach-time logger - * @param logger logger which implements the `AgentLogger` interface - */ -export function setLogger(logger: DiagnosticLogger) { - return _logger = logger; -} - -/** - * Sets the string which is prefixed to the existing sdkVersion, e.g. `ad_`, `alr_` - * @param prefix string prefix, including underscore. Defaults to `ad_` - */ -export function setUsagePrefix(prefix: string) { - _prefix = prefix; -} - -export function setStatusLogger(statusLogger: StatusLogger) { - _statusLogger = statusLogger; -} - -/** - * Try to setup and start this app insights instance if attach is enabled. - * @param setupString connection string or instrumentation key - */ -export function setupAndStart(setupString = _setupString, aadTokenCredential?: azureCore.TokenCredential): typeof types | null { - // If app already contains SDK, skip agent attach - if (!forceStart && Helpers.sdkAlreadyExists(_logger)) { - _statusLogger.logStatus({ - ...defaultStatus, - AgentInitializedSuccessfully: false, - SDKPresent: true, - Reason: "SDK already exists" - }) - return null; - } - - if (!setupString) { - const message = "Application Insights wanted to be started, but no Connection String or Instrumentation Key was provided"; - _logger.logError(message); - _statusLogger.logStatus({ - ...defaultStatus, - AgentInitializedSuccessfully: false, - Reason: message, - }); - return null; - } - - try { - _appInsights = require("../applicationinsights"); - if (_appInsights.defaultClient) { - // setupAndStart was already called, return the result - _logger.logError("Setup was attempted on the Application Insights Client multiple times. Aborting and returning the first client instance"); - return _appInsights; - } - - const prefixInternalSdkVersion = function (envelope: types.Contracts.Envelope, _contextObjects: Object) { - try { - var appInsightsSDKVersion = _appInsights.defaultClient.context.keys.internalSdkVersion; - envelope.tags[appInsightsSDKVersion] = _prefix + envelope.tags[appInsightsSDKVersion]; - } catch (e) { - _logger.logError("Error prefixing SDK version", e); - } - return true; - } - - const copyOverPrefixInternalSdkVersionToHeartBeatMetric = function (envelope: types.Contracts.Envelope, _contextObjects: Object) { - var appInsightsSDKVersion = _appInsights.defaultClient.context.keys.internalSdkVersion; - const sdkVersion = envelope.tags[appInsightsSDKVersion] || ""; - if (envelope.name === Constants.HeartBeatMetricName) { - ((envelope.data as any).baseData).properties = ((envelope.data as any).baseData).properties || {}; - ((envelope.data as any).baseData).properties["sdk"] = sdkVersion; - } - - return true; - } - - // Instrument the SDK - _appInsights.setup(setupString).setSendLiveMetrics(true); - _appInsights.defaultClient.setAutoPopulateAzureProperties(true); - _appInsights.defaultClient.addTelemetryProcessor(prefixInternalSdkVersion); - _appInsights.defaultClient.addTelemetryProcessor(copyOverPrefixInternalSdkVersionToHeartBeatMetric); - if (aadTokenCredential) { - _logger.logMessage("Using AAD Token Credential"); - _appInsights.defaultClient.config.aadTokenCredential = aadTokenCredential; - } - - _appInsights.start(); - // Add attach flag in Statsbeat - let statsbeat = _appInsights.defaultClient.getStatsbeat(); - if (statsbeat) { - statsbeat.setCodelessAttach(); - } - - // Agent successfully instrumented the SDK - _logger.logMessage("Application Insights was started with setupString: " + setupString); - _statusLogger.logStatus({ - ...defaultStatus, - AgentInitializedSuccessfully: true - }); - } catch (e) { - _logger.logError("Error setting up Application Insights", e); - _statusLogger.logStatus({ - ...defaultStatus, - AgentInitializedSuccessfully: false, - Reason: `Error setting up Application Insights: ${e && e.message}` - }) - } - return _appInsights; -} diff --git a/Bootstrap/DiagnosticLogger.ts b/Bootstrap/DiagnosticLogger.ts deleted file mode 100644 index 477e146e8..000000000 --- a/Bootstrap/DiagnosticLogger.ts +++ /dev/null @@ -1,67 +0,0 @@ -"use strict"; - -import * as path from "path"; -import * as DataModel from "./DataModel"; -import { FileWriter } from "./FileWriter"; -import { homedir } from "./Helpers/FileHelpers"; -import { APPLICATION_INSIGHTS_SDK_VERSION } from "../Declarations/Constants"; - -export class DiagnosticLogger { - public static readonly DEFAULT_FILE_NAME: string = "application-insights-extension.log"; - public static readonly DEFAULT_LOG_DIR: string = process.env.APPLICATIONINSIGHTS_LOGDIR || path.join(homedir, "LogFiles/ApplicationInsights"); - public static DefaultEnvelope: DataModel.DiagnosticLog = { - message: null, - level: null, - time: null, - logger: "applicationinsights.extension.diagnostics", - properties: { - language: "nodejs", - operation: "Startup", - siteName: process.env.WEBSITE_SITE_NAME, - ikey: process.env.APPINSIGHTS_INSTRUMENTATIONKEY, - extensionVersion: process.env.ApplicationInsightsAgent_EXTENSION_VERSION, - sdkVersion: APPLICATION_INSIGHTS_SDK_VERSION, - subscriptionId: process.env.WEBSITE_OWNER_NAME ? process.env.WEBSITE_OWNER_NAME.split("+")[0] : null, - } - } - - constructor(private _writer: DataModel.AgentLogger = console) { } - - logMessage(message: DataModel.DiagnosticLog | string, cb?: (err: Error) => void) { - if (typeof cb === "function" && this._writer instanceof FileWriter) { - this._writer.callback = cb; - } - if (typeof message === "string") { - const diagnosticMessage: DataModel.DiagnosticLog = { - ...DiagnosticLogger.DefaultEnvelope, - message, - level: DataModel.SeverityLevel.INFO, - time: new Date().toISOString(), - }; - this._writer.log(diagnosticMessage); - } else { - if (message.level === DataModel.SeverityLevel.ERROR) { - this._writer.error(message); - } else { - this._writer.log(message); - } - } - } - - logError(message: DataModel.DiagnosticLog | string, cb?: (err: Error) => void) { - if (typeof cb === "function" && this._writer instanceof FileWriter) { - this._writer.callback = cb; - } - if (typeof message === "string") { - const diagnosticMessage: DataModel.DiagnosticLog = { - ...DiagnosticLogger.DefaultEnvelope, - message, - level: DataModel.SeverityLevel.ERROR, - time: new Date().toUTCString() - }; - this._writer.error(diagnosticMessage); - } else { - this._writer.error(message); - } - } -} diff --git a/Bootstrap/Helpers.ts b/Bootstrap/Helpers.ts deleted file mode 100644 index 79a4e15d1..000000000 --- a/Bootstrap/Helpers.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { DiagnosticLogger } from "./DiagnosticLogger"; - -export function sdkAlreadyExists(_logger: DiagnosticLogger): boolean { - try { - // appInstance should either resolve to user SDK or crash. If it resolves to attach SDK, user probably modified their NODE_PATH - let appInstance: string; - try { - // Node 8.9+ - appInstance = (require.resolve as any)("applicationinsights", { paths: [process.cwd()] }); - } catch (e) { - // Node <8.9 - appInstance = require.resolve(process.cwd() + "/node_modules/applicationinsights"); - } - // If loaded instance is in Azure machine home path do not attach the SDK, this means customer already instrumented their app - if (appInstance.indexOf("home") > -1) { - _logger.logMessage( - "applicationinsights module is already installed in this application; not re-attaching. Installed SDK location: " + - appInstance - ); - return true; - } - else { - // ApplicationInsights could be loaded outside of customer application, attach in this case - return false; - } - } catch (e) { - // crashed while trying to resolve "applicationinsights", so SDK does not exist. Attach appinsights - return false; - } -} diff --git a/Bootstrap/NoopLogger.ts b/Bootstrap/NoopLogger.ts deleted file mode 100644 index b6a770d74..000000000 --- a/Bootstrap/NoopLogger.ts +++ /dev/null @@ -1,9 +0,0 @@ -import * as DataModel from "./DataModel"; -import { FileWriter } from "./FileWriter"; - -export class NoopLogger implements DataModel.AgentLogger { - log(message?: any, ...optional: any[]): void { - } - error(message?: any, ...optional: any[]): void { - } -} diff --git a/Bootstrap/Oryx.ts b/Bootstrap/Oryx.ts deleted file mode 100644 index 1b8797342..000000000 --- a/Bootstrap/Oryx.ts +++ /dev/null @@ -1,18 +0,0 @@ -import * as types from "../applicationinsights"; // needed but unused -import { StatusLogger } from "./StatusLogger"; -import { DiagnosticLogger } from "./DiagnosticLogger"; -import { NoopLogger } from "./NoopLogger"; -import appInsightsLoader = require("./Default"); - -appInsightsLoader.setUsagePrefix("alr_"); // App Services Linux Attach - -// Set Status.json logger -appInsightsLoader.setStatusLogger(new StatusLogger(new NoopLogger())); - -// Set Attach Diagnostic Logger -appInsightsLoader.setLogger(new DiagnosticLogger(new NoopLogger())); - -// Start the SDK -var appInsights = appInsightsLoader.setupAndStart(); - -export = appInsights; diff --git a/Bootstrap/StatusLogger.ts b/Bootstrap/StatusLogger.ts deleted file mode 100644 index aff0f4c17..000000000 --- a/Bootstrap/StatusLogger.ts +++ /dev/null @@ -1,42 +0,0 @@ -"use strict"; - -import * as os from "os"; -import * as path from "path"; -import * as fs from "fs"; -import * as DataModel from "./DataModel"; -import { FileWriter, homedir } from "./FileWriter"; -import { APPLICATION_INSIGHTS_SDK_VERSION } from "../Declarations/Constants"; - -export interface StatusContract { - AgentInitializedSuccessfully: boolean; - Reason?: string; - SDKPresent: boolean; - AppType: string; - MachineName: string; - PID: string; - SdkVersion: string; - Ikey: string; -} - -export class StatusLogger { - public static readonly DEFAULT_FILE_PATH: string = path.join(homedir, "status"); - public static readonly DEFAULT_FILE_NAME: string = `status_${os.hostname()}_${process.pid}.json`; - public static readonly DEFAULT_STATUS: StatusContract = { - AgentInitializedSuccessfully: false, - SDKPresent: false, - Ikey: "unknown", - AppType: "node.js", - SdkVersion: APPLICATION_INSIGHTS_SDK_VERSION, - MachineName: os.hostname(), - PID: String(process.pid) - } - - constructor(public _writer: DataModel.AgentLogger = console) {} - - public logStatus(data: StatusContract, cb?: (err: Error) => void) { - if (typeof cb === "function" && this._writer instanceof FileWriter) { - this._writer.callback = cb; - } - this._writer.log(data); - } -} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..6257f2e76 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,9 @@ +# Microsoft Open Source Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). + +Resources: + +- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) +- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) +- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ba26be353..4ea28c6ae 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,20 +14,27 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio # How to contribute to the Application Insights Node.js SDK -1. Install all dependencies with `npm install`. -2. Set an environment variable to your instrumentation key (optional). + +1. Fork this repo +2. Clone your fork locally (`git clone https://github.com//ApplicationInsights-node.js +3. Open a terminal and move into your local copy (`cd ApplicationInsights-node.js`) +4. Install all dependencies with `npm install`. +5. Build project ```bash - // windows - set APPINSIGHTS_INSTRUMENTATIONKEY= - // linux/macos - export APPINSIGHTS_INSTRUMENTATIONKEY= + npm run build ``` -3. Run tests +6. Run unit tests ```bash npm run test - npm run backcompattest + ``` +7. Run functional and back compatibility tests, start docker then run following commands: + ```bash npm run functionaltest ``` _Note: Functional tests require Docker_ - + +8. Run back compatibility tests to ckeck older version of Node.js runtime and Typescript. + ```bash + npm run backcompattest + ``` --- \ No newline at end of file diff --git a/Declarations/Constants.ts b/Declarations/Constants.ts deleted file mode 100644 index c32e41007..000000000 --- a/Declarations/Constants.ts +++ /dev/null @@ -1,201 +0,0 @@ -import Contracts = require("./Contracts") - -export const APPLICATION_INSIGHTS_SDK_VERSION = "2.2.1"; -export const DEFAULT_BREEZE_ENDPOINT = "https://dc.services.visualstudio.com"; -export const DEFAULT_LIVEMETRICS_ENDPOINT = "https://rt.services.visualstudio.com"; -export const DEFAULT_LIVEMETRICS_HOST = "rt.services.visualstudio.com"; - -export enum QuickPulseCounter { - // Memory - COMMITTED_BYTES = "\\Memory\\Committed Bytes", - - // CPU - PROCESSOR_TIME = "\\Processor(_Total)\\% Processor Time", - - // Request - REQUEST_RATE = "\\ApplicationInsights\\Requests\/Sec", - REQUEST_FAILURE_RATE = "\\ApplicationInsights\\Requests Failed\/Sec", - REQUEST_DURATION = "\\ApplicationInsights\\Request Duration", - - // Dependency - DEPENDENCY_RATE = "\\ApplicationInsights\\Dependency Calls\/Sec", - DEPENDENCY_FAILURE_RATE = "\\ApplicationInsights\\Dependency Calls Failed\/Sec", - DEPENDENCY_DURATION = "\\ApplicationInsights\\Dependency Call Duration", - - // Exception - EXCEPTION_RATE = "\\ApplicationInsights\\Exceptions\/Sec" -} - -export enum PerformanceCounter { - // Memory - PRIVATE_BYTES = "\\Process(??APP_WIN32_PROC??)\\Private Bytes", - AVAILABLE_BYTES = "\\Memory\\Available Bytes", - - // CPU - PROCESSOR_TIME = "\\Processor(_Total)\\% Processor Time", - PROCESS_TIME = "\\Process(??APP_WIN32_PROC??)\\% Processor Time", - - // Requests - REQUEST_RATE = "\\ASP.NET Applications(??APP_W3SVC_PROC??)\\Requests/Sec", - REQUEST_DURATION = "\\ASP.NET Applications(??APP_W3SVC_PROC??)\\Request Execution Time" -}; - -export enum MetricId { - REQUESTS_DURATION = "requests/duration", - DEPENDENCIES_DURATION = "dependencies/duration", - EXCEPTIONS_COUNT = "exceptions/count", - TRACES_COUNT = "traces/count", -}; - -/** - * Map a PerformanceCounter/QuickPulseCounter to a QuickPulseCounter. If no mapping exists, mapping is *undefined* - */ -export const PerformanceToQuickPulseCounter: { [key: string]: QuickPulseCounter } = { - [PerformanceCounter.PROCESSOR_TIME]: QuickPulseCounter.PROCESSOR_TIME, - [PerformanceCounter.REQUEST_RATE]: QuickPulseCounter.REQUEST_RATE, - [PerformanceCounter.REQUEST_DURATION]: QuickPulseCounter.REQUEST_DURATION, - - // Remap quick pulse only counters - [QuickPulseCounter.COMMITTED_BYTES]: QuickPulseCounter.COMMITTED_BYTES, - [QuickPulseCounter.REQUEST_FAILURE_RATE]: QuickPulseCounter.REQUEST_FAILURE_RATE, - [QuickPulseCounter.DEPENDENCY_RATE]: QuickPulseCounter.DEPENDENCY_RATE, - [QuickPulseCounter.DEPENDENCY_FAILURE_RATE]: QuickPulseCounter.DEPENDENCY_FAILURE_RATE, - [QuickPulseCounter.DEPENDENCY_DURATION]: QuickPulseCounter.DEPENDENCY_DURATION, - [QuickPulseCounter.EXCEPTION_RATE]: QuickPulseCounter.EXCEPTION_RATE -}; - -// Note: Explicitly define these types instead of using enum due to -// potential 'export enum' issues with typescript < 2.0. -export type QuickPulseDocumentType = "Event" | "Exception" | "Trace" | "Metric" | "Request" | "RemoteDependency" | "Availability" | "PageView"; -export type QuickPulseType = - | "EventTelemetryDocument" - | "ExceptionTelemetryDocument" - | "TraceTelemetryDocument" - | "MetricTelemetryDocument" - | "RequestTelemetryDocument" - | "DependencyTelemetryDocument" - | "AvailabilityTelemetryDocument" - | "PageViewTelemetryDocument"; - -export const QuickPulseDocumentType: { [key in Contracts.TelemetryTypeKeys]: QuickPulseDocumentType } = { - Event: "Event", - Exception: "Exception", - Trace: "Trace", - Metric: "Metric", - Request: "Request", - Dependency: "RemoteDependency", - Availability: "Availability", - PageView: "PageView", -}; - -export const QuickPulseType: { [key in Contracts.TelemetryTypeKeys]: QuickPulseType } = { - Event: "EventTelemetryDocument", - Exception: "ExceptionTelemetryDocument", - Trace: "TraceTelemetryDocument", - Metric: "MetricTelemetryDocument", - Request: "RequestTelemetryDocument", - Dependency: "DependencyTelemetryDocument", - Availability: "AvailabilityTelemetryDocument", - PageView: "PageViewTelemetryDocument", -}; - -export const TelemetryTypeStringToQuickPulseType: { [key in Contracts.TelemetryTypeValues]: QuickPulseType } = { - EventData: QuickPulseType.Event, - ExceptionData: QuickPulseType.Exception, - MessageData: QuickPulseType.Trace, - MetricData: QuickPulseType.Metric, - RequestData: QuickPulseType.Request, - RemoteDependencyData: QuickPulseType.Dependency, - AvailabilityData: QuickPulseType.Availability, - PageViewData: QuickPulseType.PageView -}; - -export const TelemetryTypeStringToQuickPulseDocumentType: { [key in Contracts.TelemetryTypeValues]: QuickPulseDocumentType } = { - EventData: QuickPulseDocumentType.Event, - ExceptionData: QuickPulseDocumentType.Exception, - MessageData: QuickPulseDocumentType.Trace, - MetricData: QuickPulseDocumentType.Metric, - RequestData: QuickPulseDocumentType.Request, - RemoteDependencyData: QuickPulseDocumentType.Dependency, - AvailabilityData: QuickPulseDocumentType.Availability, - PageViewData: QuickPulseDocumentType.PageView -}; - -export const DependencyTypeName = { - Grpc: "GRPC", - Http: "HTTP", - InProc: "InProc", - Sql: "SQL", - QueueMessage: "Queue Message", -} - -export const HeartBeatMetricName = "HeartBeat"; - -export const StatsbeatTelemetryName = "Statsbeat"; - -export const StatsbeatResourceProvider = { - appsvc: "appsvc", - functions: "functions", - vm: "vm", - unknown: "unknown", -} - -export const StatsbeatAttach = { - codeless: "codeless", - sdk: "sdk", -} - -export const StatsbeatCounter = { - REQUEST_SUCCESS: "Request Success Count", - REQUEST_FAILURE: "Request Failure Count", - REQUEST_DURATION: "Request Duration", - RETRY_COUNT: "Retry Count", - THROTTLE_COUNT: "Throttle Count", - EXCEPTION_COUNT: "Exception Count", - ATTACH: "Attach", - FEATURE: "Feature", -} - -export enum StatsbeatFeature { - NONE = 0, - DISK_RETRY = 1, - AAD_HANDLING = 2, -} - -export enum StatsbeatInstrumentation { - NONE = 0, - AZURE_CORE_TRACING = 1, - MONGODB = 2, - MYSQL = 4, - REDIS = 8, - POSTGRES = 16, - BUNYAN = 32, - WINSTON = 64, - CONSOLE = 128, -} - -export enum StatsbeatFeatureType { - Feature, - Instrumentation, -} - -export enum StatsbeatNetworkCategory { - Breeze, - Quickpulse, -} - -//Azure SDK Span Attributes -export const AzNamespace = "az.namespace"; -export const MicrosoftEventHub = "Microsoft.EventHub"; -export const MessageBusDestination = "message_bus.destination"; - -/** - * AI enqueued time attribute. - * @internal - */ - export const ENQUEUED_TIME = "enqueuedTime"; - /** - * AI time since enqueued attribute. - * @internal - */ - export const TIME_SINCE_ENQUEUED = "timeSinceEnqueued"; \ No newline at end of file diff --git a/Declarations/Contracts/Constants.ts b/Declarations/Contracts/Constants.ts deleted file mode 100644 index 997b919b2..000000000 --- a/Declarations/Contracts/Constants.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Domain, EventData, ExceptionData, MessageData, MetricData, PageViewData, RemoteDependencyData, RequestData } from "./Generated"; - -/** - * Breeze response definition. - */ -export interface BreezeResponse { - itemsReceived: number; - itemsAccepted: number; - errors: BreezeError[]; -} - -/** -* Breeze errors. -*/ -export interface BreezeError { - index: number; - statusCode: number; - message: string; -} - -export class RemoteDependencyDataConstants { - public static TYPE_HTTP: string = "Http"; - public static TYPE_AI: string = "Http (tracked component)"; -} - -export interface ISupportProperties extends Domain { - properties: any; -} - -export function domainSupportsProperties(domain: Domain): domain is ISupportProperties { - return "properties" in domain || // Do extra typechecks in case the type supports it but properties is null/undefined - domain instanceof EventData || - domain instanceof ExceptionData || - domain instanceof MessageData || - domain instanceof MetricData || - domain instanceof PageViewData || - domain instanceof RemoteDependencyData || - domain instanceof RequestData; -} - -/** - * Subset of Connection String fields which this SDK can parse. Lower-typecased to - * allow for case-insensitivity across field names - * @type ConnectionStringKey - */ -export interface ConnectionString { - instrumentationkey?: string; - ingestionendpoint?: string; - liveendpoint?: string; - location?: string; - endpointsuffix?: string; - - // Note: this is a node types backcompat equivalent to - // type ConnectionString = { [key in ConnectionStringKey]?: string } -} - -export type ConnectionStringKey = "instrumentationkey" | "ingestionendpoint" | "liveendpoint" | "location"| "endpointsuffix"; diff --git a/Declarations/Contracts/Generated/AvailabilityData.ts b/Declarations/Contracts/Generated/AvailabilityData.ts deleted file mode 100644 index 74ce01348..000000000 --- a/Declarations/Contracts/Generated/AvailabilityData.ts +++ /dev/null @@ -1,65 +0,0 @@ -// THIS FILE WAS AUTOGENERATED -import Domain = require('./Domain'); -"use strict"; - - /** - * Instances of AvailabilityData represent the result of executing an availability test. - */ - class AvailabilityData extends Domain - { - - /** - * Schema version - */ - public ver: number; - - /** - * Identifier of a test run. Use it to correlate steps of test run and telemetry generated by the service. - */ - public id: string; - - /** - * Name of the test that these availability results represent. - */ - public name: string; - - /** - * Duration in format: DD.HH:MM:SS.MMMMMM. Must be less than 1000 days. - */ - public duration: string; - - /** - * Success flag. - */ - public success: boolean; - - /** - * Name of the location where the test was run from. - */ - public runLocation: string; - - /** - * Diagnostic message for the result. - */ - public message: string; - - /** - * Collection of custom properties. - */ - public properties: any; - - /** - * Collection of custom measurements. - */ - public measurements: any; - - constructor() - { - super(); - - this.ver = 2; - this.properties = {}; - this.measurements = {}; - } - } -export = AvailabilityData; diff --git a/Declarations/Contracts/Generated/Base.ts b/Declarations/Contracts/Generated/Base.ts deleted file mode 100644 index 5e95abcee..000000000 --- a/Declarations/Contracts/Generated/Base.ts +++ /dev/null @@ -1,19 +0,0 @@ -// THIS FILE WAS AUTOGENERATED -"use strict"; - - /** - * Data struct to contain only C section with custom fields. - */ - class Base - { - - /** - * Name of item (B section) if any. If telemetry data is derived straight from this, this should be null. - */ - public baseType: string; - - constructor() - { - } - } -export = Base; diff --git a/Declarations/Contracts/Generated/Data.ts b/Declarations/Contracts/Generated/Data.ts deleted file mode 100644 index 4aa6a4dac..000000000 --- a/Declarations/Contracts/Generated/Data.ts +++ /dev/null @@ -1,27 +0,0 @@ -// THIS FILE WAS AUTOGENERATED -import Base = require('./Base'); -"use strict"; - - /** - * Data struct to contain both B and C sections. - */ - class Data extends Base - { - - /** - * Name of item (B section) if any. If telemetry data is derived straight from this, this should be null. - */ - public baseType: string; - - /** - * Container for data item (B section). - */ - public baseData: TDomain; - - constructor() - { - super(); - - } - } -export = Data; diff --git a/Declarations/Contracts/Generated/DataPoint.ts b/Declarations/Contracts/Generated/DataPoint.ts deleted file mode 100644 index 8326a5644..000000000 --- a/Declarations/Contracts/Generated/DataPoint.ts +++ /dev/null @@ -1,54 +0,0 @@ -// THIS FILE WAS AUTOGENERATED -import DataPointType = require('./DataPointType'); -"use strict"; - -/** - * Metric data single measurement. - */ -class DataPoint { - - /** - * Name of the metric. - */ - public name: string; - - /** - * Namespace of the metric. - */ - public ns: string; - - /** - * Metric type. Single measurement or the aggregated value. - */ - public kind: DataPointType; - - /** - * Single value for measurement. Sum of individual measurements for the aggregation. - */ - public value: number; - - /** - * Metric weight of the aggregated metric. Should not be set for a measurement. - */ - public count: number; - - /** - * Minimum value of the aggregated metric. Should not be set for a measurement. - */ - public min: number; - - /** - * Maximum value of the aggregated metric. Should not be set for a measurement. - */ - public max: number; - - /** - * Standard deviation of the aggregated metric. Should not be set for a measurement. - */ - public stdDev: number; - - constructor() { - this.kind = DataPointType.Measurement; - } -} -export = DataPoint; diff --git a/Declarations/Contracts/Generated/DataPointType.ts b/Declarations/Contracts/Generated/DataPointType.ts deleted file mode 100644 index 843d3d7fa..000000000 --- a/Declarations/Contracts/Generated/DataPointType.ts +++ /dev/null @@ -1,12 +0,0 @@ -// THIS FILE WAS AUTOGENERATED -"use strict"; - - /** - * Type of the metric data measurement. - */ - enum DataPointType - { - Measurement = 0, - Aggregation = 1, - } -export = DataPointType; diff --git a/Declarations/Contracts/Generated/Domain.ts b/Declarations/Contracts/Generated/Domain.ts deleted file mode 100644 index 163c203e3..000000000 --- a/Declarations/Contracts/Generated/Domain.ts +++ /dev/null @@ -1,14 +0,0 @@ -// THIS FILE WAS AUTOGENERATED -"use strict"; - - /** - * The abstract common base of all domains. - */ - class Domain - { - - constructor() - { - } - } -export = Domain; diff --git a/Declarations/Contracts/Generated/Envelope.ts b/Declarations/Contracts/Generated/Envelope.ts deleted file mode 100644 index c47a991c3..000000000 --- a/Declarations/Contracts/Generated/Envelope.ts +++ /dev/null @@ -1,58 +0,0 @@ -// THIS FILE WAS AUTOGENERATED -import Base = require('./Base'); -"use strict"; - - /** - * System variables for a telemetry item. - */ - class Envelope - { - - /** - * Envelope version. For internal use only. By assigning this the default, it will not be serialized within the payload unless changed to a value other than #1. - */ - public ver: number; - - /** - * Type name of telemetry data item. - */ - public name: string; - - /** - * Event date time when telemetry item was created. This is the wall clock time on the client when the event was generated. There is no guarantee that the client's time is accurate. This field must be formatted in UTC ISO 8601 format, with a trailing 'Z' character, as described publicly on https://en.wikipedia.org/wiki/ISO_8601#UTC. Note: the number of decimal seconds digits provided are variable (and unspecified). Consumers should handle this, i.e. managed code consumers should not use format 'O' for parsing as it specifies a fixed length. Example: 2009-06-15T13:45:30.0000000Z. - */ - public time: string; - - /** - * Sampling rate used in application. This telemetry item represents 1 / sampleRate actual telemetry items. - */ - public sampleRate: number; - - /** - * Sequence field used to track absolute order of uploaded events. - */ - public seq: string; - - /** - * The application's instrumentation key. The key is typically represented as a GUID, but there are cases when it is not a guid. No code should rely on iKey being a GUID. Instrumentation key is case insensitive. - */ - public iKey: string; - - /** - * Key/value collection of context properties. See ContextTagKeys for information on available properties. - */ - public tags: any; - - /** - * Telemetry data item. - */ - public data: Base; - - constructor() - { - this.ver = 1; - this.sampleRate = 100.0; - this.tags = {}; - } - } -export = Envelope; diff --git a/Declarations/Contracts/Generated/EventData.ts b/Declarations/Contracts/Generated/EventData.ts deleted file mode 100644 index d172f9c77..000000000 --- a/Declarations/Contracts/Generated/EventData.ts +++ /dev/null @@ -1,40 +0,0 @@ -// THIS FILE WAS AUTOGENERATED -import Domain = require('./Domain'); -"use strict"; - - /** - * Instances of Event represent structured event records that can be grouped and searched by their properties. Event data item also creates a metric of event count by name. - */ - class EventData extends Domain - { - - /** - * Schema version - */ - public ver: number; - - /** - * Event name. Keep it low cardinality to allow proper grouping and useful metrics. - */ - public name: string; - - /** - * Collection of custom properties. - */ - public properties: any; - - /** - * Collection of custom measurements. - */ - public measurements: any; - - constructor() - { - super(); - - this.ver = 2; - this.properties = {}; - this.measurements = {}; - } - } -export = EventData; diff --git a/Declarations/Contracts/Generated/ExceptionData.ts b/Declarations/Contracts/Generated/ExceptionData.ts deleted file mode 100644 index 233320002..000000000 --- a/Declarations/Contracts/Generated/ExceptionData.ts +++ /dev/null @@ -1,53 +0,0 @@ -// THIS FILE WAS AUTOGENERATED -import Domain = require('./Domain'); -import ExceptionDetails = require('./ExceptionDetails'); -import SeverityLevel = require('./SeverityLevel'); -"use strict"; - - /** - * An instance of Exception represents a handled or unhandled exception that occurred during execution of the monitored application. - */ - class ExceptionData extends Domain - { - - /** - * Schema version - */ - public ver: number; - - /** - * Exception chain - list of inner exceptions. - */ - public exceptions: ExceptionDetails[]; - - /** - * Severity level. Mostly used to indicate exception severity level when it is reported by logging library. - */ - public severityLevel: SeverityLevel; - - /** - * Identifier of where the exception was thrown in code. Used for exceptions grouping. Typically a combination of exception type and a function from the call stack. - */ - public problemId: string; - - /** - * Collection of custom properties. - */ - public properties: any; - - /** - * Collection of custom measurements. - */ - public measurements: any; - - constructor() - { - super(); - - this.ver = 2; - this.exceptions = []; - this.properties = {}; - this.measurements = {}; - } - } -export = ExceptionData; diff --git a/Declarations/Contracts/Generated/ExceptionDetails.ts b/Declarations/Contracts/Generated/ExceptionDetails.ts deleted file mode 100644 index 956b5e993..000000000 --- a/Declarations/Contracts/Generated/ExceptionDetails.ts +++ /dev/null @@ -1,52 +0,0 @@ -// THIS FILE WAS AUTOGENERATED -import StackFrame = require('./StackFrame'); -"use strict"; - - /** - * Exception details of the exception in a chain. - */ - class ExceptionDetails - { - - /** - * In case exception is nested (outer exception contains inner one), the id and outerId properties are used to represent the nesting. - */ - public id: number; - - /** - * The value of outerId is a reference to an element in ExceptionDetails that represents the outer exception - */ - public outerId: number; - - /** - * Exception type name. - */ - public typeName: string; - - /** - * Exception message. - */ - public message: string; - - /** - * Indicates if full exception stack is provided in the exception. The stack may be trimmed, such as in the case of a StackOverflow exception. - */ - public hasFullStack: boolean; - - /** - * Text describing the stack. Either stack or parsedStack should have a value. - */ - public stack: string; - - /** - * List of stack frames. Either stack or parsedStack should have a value. - */ - public parsedStack: StackFrame[]; - - constructor() - { - this.hasFullStack = true; - this.parsedStack = []; - } - } -export = ExceptionDetails; diff --git a/Declarations/Contracts/Generated/MessageData.ts b/Declarations/Contracts/Generated/MessageData.ts deleted file mode 100644 index d760fb0c3..000000000 --- a/Declarations/Contracts/Generated/MessageData.ts +++ /dev/null @@ -1,40 +0,0 @@ -// THIS FILE WAS AUTOGENERATED -import Domain = require('./Domain'); -import SeverityLevel = require('./SeverityLevel'); -"use strict"; - - /** - * Instances of Message represent printf-like trace statements that are text-searched. Log4Net, NLog and other text-based log file entries are translated into intances of this type. The message does not have measurements. - */ - class MessageData extends Domain - { - - /** - * Schema version - */ - public ver: number; - - /** - * Trace message - */ - public message: string; - - /** - * Trace severity level. - */ - public severityLevel: SeverityLevel; - - /** - * Collection of custom properties. - */ - public properties: any; - - constructor() - { - super(); - - this.ver = 2; - this.properties = {}; - } - } -export = MessageData; diff --git a/Declarations/Contracts/Generated/MetricData.ts b/Declarations/Contracts/Generated/MetricData.ts deleted file mode 100644 index d0bf6c1b0..000000000 --- a/Declarations/Contracts/Generated/MetricData.ts +++ /dev/null @@ -1,36 +0,0 @@ -// THIS FILE WAS AUTOGENERATED -import Domain = require('./Domain'); -import DataPoint = require('./DataPoint'); -"use strict"; - - /** - * An instance of the Metric item is a list of measurements (single data points) and/or aggregations. - */ - class MetricData extends Domain - { - - /** - * Schema version - */ - public ver: number; - - /** - * List of metrics. Only one metric in the list is currently supported by Application Insights storage. If multiple data points were sent only the first one will be used. - */ - public metrics: DataPoint[]; - - /** - * Collection of custom properties. - */ - public properties: any; - - constructor() - { - super(); - - this.ver = 2; - this.metrics = []; - this.properties = {}; - } - } -export = MetricData; diff --git a/Declarations/Contracts/Generated/PageViewData.ts b/Declarations/Contracts/Generated/PageViewData.ts deleted file mode 100644 index cb3fec74c..000000000 --- a/Declarations/Contracts/Generated/PageViewData.ts +++ /dev/null @@ -1,50 +0,0 @@ -// THIS FILE WAS AUTOGENERATED -import EventData = require('./EventData'); -"use strict"; - - /** - * An instance of PageView represents a generic action on a page like a button click. It is also the base type for PageView. - */ - class PageViewData extends EventData - { - - /** - * Schema version - */ - public ver: number; - - /** - * Request URL with all query string parameters - */ - public url: string; - - /** - * Event name. Keep it low cardinality to allow proper grouping and useful metrics. - */ - public name: string; - - /** - * Request duration in format: DD.HH:MM:SS.MMMMMM. For a page view (PageViewData), this is the duration. For a page view with performance information (PageViewPerfData), this is the page load time. Must be less than 1000 days. - */ - public duration: string; - - /** - * Collection of custom properties. - */ - public properties: any; - - /** - * Collection of custom measurements. - */ - public measurements: any; - - constructor() - { - super(); - - this.ver = 2; - this.properties = {}; - this.measurements = {}; - } - } -export = PageViewData; diff --git a/Declarations/Contracts/Generated/RemoteDependencyData.ts b/Declarations/Contracts/Generated/RemoteDependencyData.ts deleted file mode 100644 index f4838a43f..000000000 --- a/Declarations/Contracts/Generated/RemoteDependencyData.ts +++ /dev/null @@ -1,76 +0,0 @@ -// THIS FILE WAS AUTOGENERATED -import Domain = require('./Domain'); -"use strict"; - - /** - * An instance of Remote Dependency represents an interaction of the monitored component with a remote component/service like SQL or an HTTP endpoint. - */ - class RemoteDependencyData extends Domain - { - - /** - * Schema version - */ - public ver: number; - - /** - * Name of the command initiated with this dependency call. Low cardinality value. Examples are stored procedure name and URL path template. - */ - public name: string; - - /** - * Identifier of a dependency call instance. Used for correlation with the request telemetry item corresponding to this dependency call. - */ - public id: string; - - /** - * Result code of a dependency call. Examples are SQL error code and HTTP status code. - */ - public resultCode: string; - - /** - * Request duration in format: DD.HH:MM:SS.MMMMMM. Must be less than 1000 days. - */ - public duration: string; - - /** - * Indication of successfull or unsuccessfull call. - */ - public success: boolean; - - /** - * Command initiated by this dependency call. Examples are SQL statement and HTTP URL's with all query parameters. - */ - public data: string; - - /** - * Target site of a dependency call. Examples are server name, host address. - */ - public target: string; - - /** - * Dependency type name. Very low cardinality value for logical grouping of dependencies and interpretation of other fields like commandName and resultCode. Examples are SQL, Azure table, and HTTP. - */ - public type: string; - - /** - * Collection of custom properties. - */ - public properties: any; - - /** - * Collection of custom measurements. - */ - public measurements: any; - - constructor() - { - super(); - - this.ver = 2; - this.success = true; - this.properties = {}; - this.measurements = {}; - } - } -export = RemoteDependencyData; diff --git a/Declarations/Contracts/Generated/RequestData.ts b/Declarations/Contracts/Generated/RequestData.ts deleted file mode 100644 index 05ff833dd..000000000 --- a/Declarations/Contracts/Generated/RequestData.ts +++ /dev/null @@ -1,70 +0,0 @@ -// THIS FILE WAS AUTOGENERATED -import Domain = require('./Domain'); -"use strict"; - - /** - * An instance of Request represents completion of an external request to the application to do work and contains a summary of that request execution and the results. - */ - class RequestData extends Domain - { - - /** - * Schema version - */ - public ver: number; - - /** - * Identifier of a request call instance. Used for correlation between request and other telemetry items. - */ - public id: string; - - /** - * Source of the request. Examples are the instrumentation key of the caller or the ip address of the caller. - */ - public source: string; - - /** - * Name of the request. Represents code path taken to process request. Low cardinality value to allow better grouping of requests. For HTTP requests it represents the HTTP method and URL path template like 'GET /values/{id}'. - */ - public name: string; - - /** - * Request duration in format: DD.HH:MM:SS.MMMMMM. Must be less than 1000 days. - */ - public duration: string; - - /** - * Result of a request execution. HTTP status code for HTTP requests. - */ - public responseCode: string; - - /** - * Indication of successfull or unsuccessfull call. - */ - public success: boolean; - - /** - * Request URL with all query string parameters. - */ - public url: string; - - /** - * Collection of custom properties. - */ - public properties: any; - - /** - * Collection of custom measurements. - */ - public measurements: any; - - constructor() - { - super(); - - this.ver = 2; - this.properties = {}; - this.measurements = {}; - } - } -export = RequestData; diff --git a/Declarations/Contracts/Generated/SeverityLevel.ts b/Declarations/Contracts/Generated/SeverityLevel.ts deleted file mode 100644 index 94420c54d..000000000 --- a/Declarations/Contracts/Generated/SeverityLevel.ts +++ /dev/null @@ -1,15 +0,0 @@ -// THIS FILE WAS AUTOGENERATED -"use strict"; - - /** - * Defines the level of severity for the event. - */ - enum SeverityLevel - { - Verbose = 0, - Information = 1, - Warning = 2, - Error = 3, - Critical = 4, - } -export = SeverityLevel; diff --git a/Declarations/Contracts/Generated/StackFrame.ts b/Declarations/Contracts/Generated/StackFrame.ts deleted file mode 100644 index 225e5d35e..000000000 --- a/Declarations/Contracts/Generated/StackFrame.ts +++ /dev/null @@ -1,39 +0,0 @@ -// THIS FILE WAS AUTOGENERATED -"use strict"; - - /** - * Stack frame information. - */ - class StackFrame - { - - /** - * Level in the call stack. For the long stacks SDK may not report every function in a call stack. - */ - public level: number; - - /** - * Method name. - */ - public method: string; - - /** - * Name of the assembly (dll, jar, etc.) containing this function. - */ - public assembly: string; - - /** - * File name or URL of the method implementation. - */ - public fileName: string; - - /** - * Line number of the code implementation. - */ - public line: number; - - constructor() - { - } - } -export = StackFrame; diff --git a/Declarations/Contracts/Generated/index.ts b/Declarations/Contracts/Generated/index.ts deleted file mode 100644 index e7052fdc4..000000000 --- a/Declarations/Contracts/Generated/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -// THIS FILE WAS AUTOGENERATED -"use strict"; -export import AvailabilityData = require("./AvailabilityData"); -export import Base = require("./Base"); -export import ContextTagKeys = require("./ContextTagKeys"); -export import Data = require("./Data"); -export import DataPoint = require("./DataPoint"); -export import DataPointType = require("./DataPointType"); -export import Domain = require("./Domain"); -export import Envelope = require("./Envelope"); -export import EventData = require("./EventData"); -export import ExceptionData = require("./ExceptionData"); -export import ExceptionDetails = require("./ExceptionDetails"); -export import MessageData = require("./MessageData"); -export import MetricData = require("./MetricData"); -export import PageViewData = require("./PageViewData"); -export import RemoteDependencyData = require("./RemoteDependencyData"); -export import RequestData = require("./RequestData"); -export import SeverityLevel = require("./SeverityLevel"); -export import StackFrame = require("./StackFrame"); diff --git a/Declarations/Contracts/QuickPulseTypes/DependencyDocumentQuickPulse.ts b/Declarations/Contracts/QuickPulseTypes/DependencyDocumentQuickPulse.ts deleted file mode 100644 index 36409a253..000000000 --- a/Declarations/Contracts/QuickPulseTypes/DependencyDocumentQuickPulse.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { DocumentQuickPulse } from "./DocumentQuickPulse"; - -export interface DependencyDocumentQuickPulse extends DocumentQuickPulse { - Name: string; - Target: string; - Success?: boolean; - Duration: string; - ResultCode: string, - CommandName: string; - DependencyTypeName: string; - OperationName: string; -} diff --git a/Declarations/Contracts/QuickPulseTypes/DocumentQuickPulse.ts b/Declarations/Contracts/QuickPulseTypes/DocumentQuickPulse.ts deleted file mode 100644 index 2764553fb..000000000 --- a/Declarations/Contracts/QuickPulseTypes/DocumentQuickPulse.ts +++ /dev/null @@ -1,16 +0,0 @@ -export interface DocumentQuickPulse { - __type: string; - - DocumentType: string; - - Version: string; - - OperationId: string; - - Properties: IDocumentProperty[]; -} - -export interface IDocumentProperty { - key: string; - value: string; -}; \ No newline at end of file diff --git a/Declarations/Contracts/QuickPulseTypes/EnvelopeQuickPulse.ts b/Declarations/Contracts/QuickPulseTypes/EnvelopeQuickPulse.ts deleted file mode 100644 index 9cc278484..000000000 --- a/Declarations/Contracts/QuickPulseTypes/EnvelopeQuickPulse.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { DocumentQuickPulse } from "./DocumentQuickPulse"; -import { MetricQuickPulse } from "./MetricQuickPulse"; - -export interface EnvelopeQuickPulse { - Documents: DocumentQuickPulse[]; - - Instance: string; - - RoleName: string; - - InstrumentationKey: string; - - InvariantVersion: number; - - MachineName: string; - - Metrics: MetricQuickPulse[]; - - StreamId: string; - - Timestamp: string; - - Version: string; -} diff --git a/Declarations/Contracts/QuickPulseTypes/EventDocumentQuickPulse.ts b/Declarations/Contracts/QuickPulseTypes/EventDocumentQuickPulse.ts deleted file mode 100644 index 2a537344b..000000000 --- a/Declarations/Contracts/QuickPulseTypes/EventDocumentQuickPulse.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { DocumentQuickPulse } from "./DocumentQuickPulse"; - -export interface EventDocumentQuickPulse extends DocumentQuickPulse { - Name: string; -} diff --git a/Declarations/Contracts/QuickPulseTypes/ExceptionDocumentQuickPulse.ts b/Declarations/Contracts/QuickPulseTypes/ExceptionDocumentQuickPulse.ts deleted file mode 100644 index df44d812c..000000000 --- a/Declarations/Contracts/QuickPulseTypes/ExceptionDocumentQuickPulse.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { DocumentQuickPulse } from "./DocumentQuickPulse"; - -export interface ExceptionDocumentQuickPulse extends DocumentQuickPulse { - Exception: string; - ExceptionMessage: string; - ExceptionType: string; -} diff --git a/Declarations/Contracts/QuickPulseTypes/MessageDocumentQuickPulse.ts b/Declarations/Contracts/QuickPulseTypes/MessageDocumentQuickPulse.ts deleted file mode 100644 index 743647c18..000000000 --- a/Declarations/Contracts/QuickPulseTypes/MessageDocumentQuickPulse.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { DocumentQuickPulse } from "./DocumentQuickPulse"; - -export interface MessageDocumentQuickPulse extends DocumentQuickPulse { - Message: string; - SeverityLevel: string; -} diff --git a/Declarations/Contracts/QuickPulseTypes/MetricQuickPulse.ts b/Declarations/Contracts/QuickPulseTypes/MetricQuickPulse.ts deleted file mode 100644 index cb8e190d8..000000000 --- a/Declarations/Contracts/QuickPulseTypes/MetricQuickPulse.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface MetricQuickPulse { - Name: string; - - Value: number; - - Weight: number; -} diff --git a/Declarations/Contracts/QuickPulseTypes/RequestDocumentQuickPulse.ts b/Declarations/Contracts/QuickPulseTypes/RequestDocumentQuickPulse.ts deleted file mode 100644 index e8195b9b1..000000000 --- a/Declarations/Contracts/QuickPulseTypes/RequestDocumentQuickPulse.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { DocumentQuickPulse } from "./DocumentQuickPulse"; - -export interface RequestDocumentQuickPulse extends DocumentQuickPulse { - Name: string; - Success?: boolean; - Duration: string; - ResponseCode: string, - OperationName: string; -} diff --git a/Declarations/Contracts/QuickPulseTypes/index.ts b/Declarations/Contracts/QuickPulseTypes/index.ts deleted file mode 100644 index eb94d7620..000000000 --- a/Declarations/Contracts/QuickPulseTypes/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export * from "./MetricQuickPulse"; -export * from "./EnvelopeQuickPulse"; -export * from "./DocumentQuickPulse"; -export * from "./ExceptionDocumentQuickPulse"; -export * from "./MessageDocumentQuickPulse"; -export * from "./DependencyDocumentQuickPulse"; -export * from "./RequestDocumentQuickPulse"; -export * from "./EventDocumentQuickPulse"; diff --git a/Declarations/Contracts/TelemetryTypes/AvailabilityTelemetry.ts b/Declarations/Contracts/TelemetryTypes/AvailabilityTelemetry.ts deleted file mode 100644 index 187b45326..000000000 --- a/Declarations/Contracts/TelemetryTypes/AvailabilityTelemetry.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Telemetry } from "./Telemetry"; - -/** - * Telemetry type used for availability web test results. - */ -export interface AvailabilityTelemetry extends Telemetry { - - /** - * Identifier of a test run. Use it to correlate steps of test run and telemetry generated by the service. - */ - id: string; - - /** - * Name of the test that these availability results represent. - */ - name: string; - - /** - * Request duration in ms - */ - duration: number; - - /** - * Success flag. - */ - success: boolean; - - /** - * Name of the location where the test was run from. - */ - runLocation: string; - - /** - * Diagnostic message for the result. - */ - message: string; - - /** - * Metrics associated with this event, displayed in Metrics Explorer on the portal. - */ - measurements?: { [key: string]: number; }; -} \ No newline at end of file diff --git a/Declarations/Contracts/TelemetryTypes/DependencyTelemetry.ts b/Declarations/Contracts/TelemetryTypes/DependencyTelemetry.ts deleted file mode 100644 index 63ae96e17..000000000 --- a/Declarations/Contracts/TelemetryTypes/DependencyTelemetry.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Telemetry } from "./Telemetry"; - -/** - * Telemetry about the call to remote component - */ -export interface DependencyTelemetry extends Telemetry { - /** - * Type name of the telemetry, such as HTTP of SQL - */ - dependencyTypeName: string; - - /** - * Remote component general target information - * If left empty, this will be prepopulated with an extracted hostname from the data field, if it is a url. - * This prepopulation happens when calling `trackDependency`. Use `track` directly to avoid this behavior. - */ - target?: string; - - /** - * Remote call name - */ - name: string; - - /** - * Remote call data. This is the most detailed information about the call, such as full URL or SQL statement - */ - data: string; - - /** - * Remote call duration in ms - */ - duration: number; - - /** - * Result code returned form the remote component. This is domain specific and can be HTTP status code or SQL result code - */ - resultCode: string | number; - - /** - * True if remote call was successful, false otherwise - */ - success: boolean; -} \ No newline at end of file diff --git a/Declarations/Contracts/TelemetryTypes/EnvelopeTelemetry.ts b/Declarations/Contracts/TelemetryTypes/EnvelopeTelemetry.ts deleted file mode 100644 index 013c5bc43..000000000 --- a/Declarations/Contracts/TelemetryTypes/EnvelopeTelemetry.ts +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Telemetry Envelope - */ -export interface EnvelopeTelemetry { - /** - * Envelope version. For internal use only. By assigning this the default, it will not be serialized within the payload unless changed to a value other than #1. - */ - ver: number; - /** - * Type name of telemetry data item. - */ - name: string; - - /** - * Event date time when telemetry item was created. This is the wall clock time on the client when the event was generated. There is no guarantee that the client's time is accurate. This field must be formatted in UTC ISO 8601 format, with a trailing 'Z' character, as described publicly on https://en.wikipedia.org/wiki/ISO_8601#UTC. Note: the number of decimal seconds digits provided are variable (and unspecified). Consumers should handle this, i.e. managed code consumers should not use format 'O' for parsing as it specifies a fixed length. Example: 2009-06-15T13:45:30.0000000Z. - */ - time: string; - - /** - * Sampling rate used in application. This telemetry item represents 1 / sampleRate actual telemetry items. - */ - sampleRate: number; - - /** - * Sequence field used to track absolute order of uploaded events. - */ - seq: string; - - /** - * The application's instrumentation key. The key is typically represented as a GUID, but there are cases when it is not a guid. No code should rely on iKey being a GUID. Instrumentation key is case insensitive. - */ - iKey: string; - - /** - * Key/value collection of context properties. See ContextTagKeys for information on available properties. - */ - tags: Tags & Tags[]; - /** - * Part B Data - */ - data: DataTelemetry; - -} - -/** - * Envelope Data - */ -export interface DataTelemetry { - /** - * Telemetry type used for part B - */ - baseType: string; - /** - * Based on schema for part B - */ - baseData?: { - [key: string]: any; - }; -} - -/** - * Envelope Tags - */ -export interface Tags { - [key: string]: any; -} diff --git a/Declarations/Contracts/TelemetryTypes/PageViewTelemetry.ts b/Declarations/Contracts/TelemetryTypes/PageViewTelemetry.ts deleted file mode 100644 index af2126e42..000000000 --- a/Declarations/Contracts/TelemetryTypes/PageViewTelemetry.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Telemetry } from "./Telemetry"; - -/** - * Telemetry type used for availability web test results. - */ -export interface PageViewTelemetry extends Telemetry { - - /** - * Name of the test that these availability results represent. - */ - name?: string; - - /** - * URL of the page to track. - */ - url?: string; - - /** - * Request duration in ms - */ - duration?: number; - - /** - * Metrics associated with this event, displayed in Metrics Explorer on the portal. - */ - measurements?: { [key: string]: number; }; -} \ No newline at end of file diff --git a/Declarations/Contracts/TelemetryTypes/RequestTelemetry.ts b/Declarations/Contracts/TelemetryTypes/RequestTelemetry.ts deleted file mode 100644 index baac50764..000000000 --- a/Declarations/Contracts/TelemetryTypes/RequestTelemetry.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Telemetry } from "./Telemetry"; - -/** - * Telemetry about the incoming request processed by the application - */ -export interface RequestTelemetry extends Telemetry { - /** - * Request name - */ - name: string; - - /** - * Request url - */ - url: string; - - /** - * Request source. This encapsulates the information about the component that initiated the request - */ - source?: string; - - /** - * Request duration in ms - */ - duration: number; - - /** - * Result code reported by the application - */ - resultCode: string | number; - - /** - * Whether the request was successful - */ - success: boolean; - - /** - * Collection of custom measurements - */ - measurements?: { [key: string]: number; }; -} \ No newline at end of file diff --git a/Declarations/Contracts/TelemetryTypes/Telemetry.ts b/Declarations/Contracts/TelemetryTypes/Telemetry.ts deleted file mode 100644 index bdc39211a..000000000 --- a/Declarations/Contracts/TelemetryTypes/Telemetry.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Base telemetry interface encapsulating coming properties - */ -export interface Telemetry { - /** - * Telemetry time stamp. When it is not specified, current timestamp will be used. - */ - time?: Date; - /** - * Additional data used to filter events and metrics in the portal. Defaults to empty. - */ - properties?: { [key: string]: any; }; - /** - * An event-specific context that will be passed to telemetry processors handling this event before it is sent. For a context spanning your entire operation, consider appInsights.getCorrelationContext - */ - contextObjects?: { [name: string]: any; }; - /** - * The context tags to use for this telemetry which overwrite default context values - */ - tagOverrides?: { [key: string]: string; }; -} diff --git a/Declarations/Contracts/TelemetryTypes/TelemetryType.ts b/Declarations/Contracts/TelemetryTypes/TelemetryType.ts deleted file mode 100644 index a473c90e7..000000000 --- a/Declarations/Contracts/TelemetryTypes/TelemetryType.ts +++ /dev/null @@ -1,91 +0,0 @@ -export type TelemetryTypeKeys = "Event" | "Exception" | "Trace" | "Metric" | "Request" | "Dependency" | "Availability" | "PageView"; -export type TelemetryTypeValues = - | "EventData" - | "ExceptionData" - | "MessageData" - | "MetricData" - | "RequestData" - | "RemoteDependencyData" - | "AvailabilityData" - | "PageViewData"; - -/** - * Converts the user-friendly enumeration TelemetryType to the underlying schema baseType value - * @param type Type to convert to BaseData string - */ -export function telemetryTypeToBaseType(type: TelemetryType): TelemetryTypeValues { - switch(type) { - case TelemetryType.Event: - return "EventData"; - case TelemetryType.Exception: - return "ExceptionData"; - case TelemetryType.Trace: - return "MessageData"; - case TelemetryType.Metric: - return "MetricData"; - case TelemetryType.Request: - return "RequestData"; - case TelemetryType.Dependency: - return "RemoteDependencyData"; - case TelemetryType.Availability: - return "AvailabilityData"; - case TelemetryType.PageView: - return "PageViewData"; - } - return undefined; -} - -/** - * Converts the schema baseType value to the user-friendly enumeration TelemetryType - * @param baseType BaseData string to convert to TelemetryType - */ -export function baseTypeToTelemetryType(baseType: TelemetryTypeValues): TelemetryType { - switch(baseType) { - case "EventData": - return TelemetryType.Event; - case "ExceptionData": - return TelemetryType.Exception; - case "MessageData": - return TelemetryType.Trace; - case "MetricData": - return TelemetryType.Metric; - case "RequestData": - return TelemetryType.Request; - case "RemoteDependencyData": - return TelemetryType.Dependency; - case "AvailabilityData": - return TelemetryType.Availability; - case "PageViewData": - return TelemetryType.PageView; - } - return undefined; -} - -export const TelemetryTypeString: {[key: string]: TelemetryTypeValues} = { - Event: "EventData", - Exception: "ExceptionData", - Trace: "MessageData", - Metric: "MetricData", - Request: "RequestData", - Dependency: "RemoteDependencyData", - Availability: "AvailabilityData", - PageView: "PageViewData", -} - -/** - * Telemetry types supported by this SDK - */ -export enum TelemetryType { - Event, - Exception, - Trace, - Metric, - Request, - Dependency, - Availability, - PageView -} - -export interface Identified { - id?: string; -} diff --git a/Declarations/Contracts/TelemetryTypes/TraceTelemetry.ts b/Declarations/Contracts/TelemetryTypes/TraceTelemetry.ts deleted file mode 100644 index 1c2463517..000000000 --- a/Declarations/Contracts/TelemetryTypes/TraceTelemetry.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Telemetry } from "./Telemetry"; -import Contracts = require("../"); - -/** - * Trace telemetry reports technical, usually detailed information about the environment, - * usage of resources, performance, capacity etc - */ -export interface TraceTelemetry extends Telemetry { - /** - * Trace message - */ - message: string; - /** - * Trace severity level - */ - severity?: Contracts.SeverityLevel; -} \ No newline at end of file diff --git a/Declarations/Contracts/TelemetryTypes/index.ts b/Declarations/Contracts/TelemetryTypes/index.ts deleted file mode 100644 index c560a2406..000000000 --- a/Declarations/Contracts/TelemetryTypes/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -export * from "./DependencyTelemetry"; -export * from "./EventTelemetry"; -export * from "./ExceptionTelemetry"; -export * from "./MetricTelemetry"; -export * from "./RequestTelemetry"; -export * from "./TraceTelemetry"; -export * from "./Telemetry"; - -export * from "./NodeHttpDependencyTelemetry"; -export * from "./NodeHttpRequestTelemetry"; -export * from "./AvailabilityTelemetry"; -export * from "./PageViewTelemetry"; - -export * from "./EnvelopeTelemetry"; -export * from "./TelemetryType"; \ No newline at end of file diff --git a/Declarations/Contracts/index.ts b/Declarations/Contracts/index.ts deleted file mode 100644 index 09e292369..000000000 --- a/Declarations/Contracts/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from "./Constants"; -export * from "./Generated"; -export * from "./TelemetryTypes"; -export * from "./QuickPulseTypes"; diff --git a/Declarations/Metrics/AggregatedMetric.ts b/Declarations/Metrics/AggregatedMetric.ts deleted file mode 100644 index ed6fd341b..000000000 --- a/Declarations/Metrics/AggregatedMetric.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Constants = require("../Constants"); - -export class AggregatedMetric { - - public name: string; - - public metricType: Constants.MetricId; - - public dimensions: { [key: string]: any; }; - - public value: number; - - public count: number; - - public aggregationInterval: number; -} \ No newline at end of file diff --git a/Declarations/Metrics/AggregatedMetricCounters.ts b/Declarations/Metrics/AggregatedMetricCounters.ts deleted file mode 100644 index f969dee4d..000000000 --- a/Declarations/Metrics/AggregatedMetricCounters.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { MetricBaseDimensions } from "./AggregatedMetricDimensions"; - -export class AggregatedMetricCounter { - - public time: number; - - public lastTime: number; - - public totalCount: number; - - public lastTotalCount: number; - - public intervalExecutionTime: number; - - public lastIntervalExecutionTime: number; - - public dimensions: MetricBaseDimensions; - - constructor(dimensions: MetricBaseDimensions) { - this.dimensions = dimensions; - this.totalCount = 0; - this.lastTotalCount = 0; - this.intervalExecutionTime = 0; - this.lastTime = +new Date; - this.lastIntervalExecutionTime = 0; - } -} - diff --git a/Declarations/Metrics/AggregatedMetricDimensions.ts b/Declarations/Metrics/AggregatedMetricDimensions.ts deleted file mode 100644 index 9fea8ec30..000000000 --- a/Declarations/Metrics/AggregatedMetricDimensions.ts +++ /dev/null @@ -1,42 +0,0 @@ -export interface MetricBaseDimensions { - cloudRoleInstance?: string; - cloudRoleName?: string -} - -export interface MetricDependencyDimensions extends MetricBaseDimensions { - dependencyType?: string; - dependencyTarget?: string; - dependencySuccess?: boolean; - dependencyResultCode?: string; - operationSynthetic?: string; -} - -export interface MetricRequestDimensions extends MetricBaseDimensions { - requestSuccess?: boolean; - requestResultCode?: string; - operationSynthetic?: string; -} - -export interface MetricExceptionDimensions extends MetricBaseDimensions { -} - -export interface MetricTraceDimensions extends MetricBaseDimensions { - traceSeverityLevel?: string; -} - -export type MetricDimensionTypeKeys = "cloudRoleInstance" | "cloudRoleName" | "requestSuccess" | "requestResultCode" - | "dependencyType" | "dependencyTarget" | "dependencySuccess" | "dependencyResultCode" | "traceSeverityLevel" | "operationSynthetic"; - -// Names expected in Breeze side for dimensions -export const PreaggregatedMetricPropertyNames: { [key in MetricDimensionTypeKeys]: string } = { - cloudRoleInstance: "cloud/roleInstance", - cloudRoleName: "cloud/roleName", - operationSynthetic: "operation/synthetic", - requestSuccess: "Request.Success", - requestResultCode: "request/resultCode", - dependencyType: "Dependency.Type", - dependencyTarget: "dependency/target", - dependencySuccess: "Dependency.Success", - dependencyResultCode: "dependency/resultCode", - traceSeverityLevel: "trace/severityLevel", -}; diff --git a/Library/AuthorizationHandler.ts b/Library/AuthorizationHandler.ts deleted file mode 100644 index 9ebac93a9..000000000 --- a/Library/AuthorizationHandler.ts +++ /dev/null @@ -1,34 +0,0 @@ -import http = require("http"); -import https = require("https"); -import azureCore = require("@azure/core-http"); - -const applicationInsightsResource = "https://monitor.azure.com//.default"; - - -class AuthorizationHandler { - - private _azureTokenPolicy: azureCore.RequestPolicy; - - constructor(credential: azureCore.TokenCredential) { - let scopes: string[] = [applicationInsightsResource]; - let emptyPolicy: azureCore.RequestPolicy = { - sendRequest(httpRequest: azureCore.WebResourceLike): Promise { - return null; - } - }; - this._azureTokenPolicy = azureCore.bearerTokenAuthenticationPolicy(credential, scopes).create(emptyPolicy, new azureCore.RequestPolicyOptions()); - } - - /** - * Applies the Bearer token to the request through the Authorization header. - */ - public async addAuthorizationHeader(requestOptions: http.RequestOptions | https.RequestOptions): Promise { - let authHeaderName = azureCore.Constants.HeaderConstants.AUTHORIZATION; - let webResource = new azureCore.WebResource("https://"); - await this._azureTokenPolicy.sendRequest(webResource); - requestOptions.headers[authHeaderName] = webResource.headers.get(authHeaderName); - } - -} - -export = AuthorizationHandler; \ No newline at end of file diff --git a/Library/AzureVirtualMachine.ts b/Library/AzureVirtualMachine.ts deleted file mode 100644 index 3f65ad128..000000000 --- a/Library/AzureVirtualMachine.ts +++ /dev/null @@ -1,76 +0,0 @@ - -import Config = require("./Config"); -import Logging = require("./Logging"); -import Util = require("./Util"); -import AutoCollectHttpDependencies = require("../AutoCollection/HttpDependencies"); - -const AIMS_URI = "http://169.254.169.254/metadata/instance/compute"; -const AIMS_API_VERSION = "api-version=2017-12-01"; -const AIMS_FORMAT = "format=json"; -const ConnectionErrorMessage = "UNREACH"; // EHOSTUNREACH, ENETUNREACH - -export interface IVirtualMachineInfo { - isVM?: boolean; - id?: string; - subscriptionId?: string; - osType?: string; -} - -export class AzureVirtualMachine { - - private static TAG = "AzureVirtualMachine"; - - public static getAzureComputeMetadata(config: Config, callback: (vm: IVirtualMachineInfo) => void) { - let vmInfo: IVirtualMachineInfo = {}; - const metadataRequestUrl = `${AIMS_URI}?${AIMS_API_VERSION}&${AIMS_FORMAT}`; - const requestOptions = { - method: 'GET', - [AutoCollectHttpDependencies.disableCollectionRequestOption]: true, - headers: { - "Metadata": "True", - } - }; - - const req = Util.makeRequest(config, metadataRequestUrl, requestOptions, (res) => { - if (res.statusCode === 200) { - // Success; VM - vmInfo.isVM = true; - let virtualMachineData = ""; - res.on('data', (data: any) => { - virtualMachineData += data; - }); - res.on('end', () => { - try { - let data = JSON.parse(virtualMachineData); - vmInfo.id = data["vmId"] || ""; - vmInfo.subscriptionId = data["subscriptionId"] || ""; - vmInfo.osType = data["osType"] || ""; - } - catch (error) { - // Failed to parse JSON - Logging.info(AzureVirtualMachine.TAG, error); - } - callback(vmInfo); - }); - } else { - callback(vmInfo); - } - }, false, false); - if (req) { - req.on('error', (error: Error) => { - // Unable to contact endpoint. - // Do nothing for now. - if (error && error.message && error.message.indexOf(ConnectionErrorMessage) > -1) { - vmInfo.isVM = false; // confirm it's not in VM - } - else{ - // Only log when is not determined if VM or not to avoid noise outside of Azure VMs - Logging.info(AzureVirtualMachine.TAG, error); - } - callback(vmInfo); - }); - req.end(); - - } - } -} diff --git a/Library/Channel.ts b/Library/Channel.ts deleted file mode 100644 index 29e5837ad..000000000 --- a/Library/Channel.ts +++ /dev/null @@ -1,103 +0,0 @@ -import Contracts = require("../Declarations/Contracts"); -import Logging = require("./Logging"); -import Sender = require("./Sender"); -import Util = require("./Util"); - -class Channel { - protected _lastSend: number; - protected _timeoutHandle: any; - - protected _isDisabled: () => boolean; - protected _getBatchSize: () => number; - protected _getBatchIntervalMs: () => number; - - public _sender: Sender; - public _buffer: Contracts.EnvelopeTelemetry[]; - - constructor(isDisabled: () => boolean, getBatchSize: () => number, getBatchIntervalMs: () => number, sender: Sender) { - this._buffer = []; - this._lastSend = 0; - this._isDisabled = isDisabled; - this._getBatchSize = getBatchSize; - this._getBatchIntervalMs = getBatchIntervalMs; - this._sender = sender; - } - - /** - * Enable or disable disk-backed retry caching to cache events when client is offline (enabled by default) - * These cached events are stored in your system or user's temporary directory and access restricted to your user when possible. - * @param value if true events that occurred while client is offline will be cached on disk - * @param resendInterval The wait interval for resending cached events. - * @param maxBytesOnDisk The maximum size (in bytes) that the created temporary directory for cache events can grow to, before caching is disabled. - * @returns {Configuration} this class - */ - public setUseDiskRetryCaching(value: boolean, resendInterval?: number, maxBytesOnDisk?: number) { - this._sender.setDiskRetryMode(value, resendInterval, maxBytesOnDisk); - } - - /** - * Add a telemetry item to the send buffer - */ - public send(envelope: Contracts.EnvelopeTelemetry) { - - // if master off switch is set, don't send any data - if (this._isDisabled()) { - // Do not send/save data - return; - } - - // validate input - if (!envelope) { - Logging.warn("Cannot send null/undefined telemetry"); - return; - } - - // enqueue the payload - this._buffer.push(envelope); - - // flush if we would exceed the max-size limit by adding this item - if (this._buffer.length >= this._getBatchSize()) { - this.triggerSend(false); - return; - } - - // ensure an invocation timeout is set if anything is in the buffer - if (!this._timeoutHandle && this._buffer.length > 0) { - this._timeoutHandle = setTimeout(() => { - this._timeoutHandle = null; - this.triggerSend(false); - }, this._getBatchIntervalMs()); - } - } - - /** - * Immediately send buffered data - */ - public triggerSend(isNodeCrashing: boolean, callback?: (v: string) => void) { - let bufferIsEmpty = this._buffer.length < 1; - if (!bufferIsEmpty) { - // invoke send - if (isNodeCrashing || Util.isNodeExit) { - this._sender.saveOnCrash(this._buffer); - if (typeof callback === "function") { - callback("data saved on crash"); - } - } else { - this._sender.send(this._buffer, callback); - } - } - - // update lastSend time to enable throttling - this._lastSend = +new Date; - - // clear buffer - this._buffer = []; - clearTimeout(this._timeoutHandle); - this._timeoutHandle = null; - if (bufferIsEmpty && typeof callback === "function") { - callback("no data to send"); - } - } -} - -export = Channel; diff --git a/Library/Config.ts b/Library/Config.ts deleted file mode 100644 index ae42cb74b..000000000 --- a/Library/Config.ts +++ /dev/null @@ -1,199 +0,0 @@ -import azureCore = require("@azure/core-http"); - -import CorrelationIdManager = require('./CorrelationIdManager'); -import ConnectionStringParser = require('./ConnectionStringParser'); -import Logging = require('./Logging'); -import Constants = require('../Declarations/Constants'); -import http = require('http'); -import https = require('https'); -import url = require('url'); -import { JsonConfig } from "./JsonConfig"; -import { IConfig } from "../Declarations/Interfaces"; -import { DistributedTracingModes } from "../applicationinsights"; -import { IDisabledExtendedMetrics } from "../AutoCollection/NativePerformance"; - -class Config implements IConfig { - - public static ENV_azurePrefix = "APPSETTING_"; // Azure adds this prefix to all environment variables - public static ENV_iKey = "APPINSIGHTS_INSTRUMENTATIONKEY"; // This key is provided in the readme - public static legacy_ENV_iKey = "APPINSIGHTS_INSTRUMENTATION_KEY"; - public static ENV_profileQueryEndpoint = "APPINSIGHTS_PROFILE_QUERY_ENDPOINT"; - public static ENV_quickPulseHost = "APPINSIGHTS_QUICKPULSE_HOST"; - - // IConfig properties - public endpointUrl: string; - public maxBatchSize: number; - public maxBatchIntervalMs: number; - public disableAppInsights: boolean; - public samplingPercentage: number; - public correlationIdRetryIntervalMs: number; - public correlationHeaderExcludedDomains: string[]; - public proxyHttpUrl: string; - public proxyHttpsUrl: string; - public httpAgent: http.Agent; - public httpsAgent: https.Agent; - public ignoreLegacyHeaders: boolean; - public aadTokenCredential?: azureCore.TokenCredential; - public enableAutoCollectConsole: boolean; - public enableAutoCollectExceptions: boolean; - public enableAutoCollectPerformance: boolean; - public enableAutoCollectExternalLoggers: boolean; - public enableAutoCollectPreAggregatedMetrics: boolean; - public enableAutoCollectHeartbeat: boolean; - public enableAutoCollectRequests: boolean; - public enableAutoCollectDependencies: boolean; - public enableAutoDependencyCorrelation: boolean; - public enableSendLiveMetrics: boolean; - public enableUseDiskRetryCaching: boolean; - public enableUseAsyncHooks: boolean; - public distributedTracingMode: DistributedTracingModes; - public enableAutoCollectExtendedMetrics: boolean | IDisabledExtendedMetrics; - public enableResendInterval: number; - public enableMaxBytesOnDisk: number; - public enableInternalDebugLogging: boolean; - public enableInternalWarningLogging: boolean; - public disableAllExtendedMetrics: boolean; - public disableStatsbeat: boolean; - public extendedMetricDisablers: string; - public quickPulseHost: string; - - public correlationId: string; // TODO: Should be private - private _connectionString: string; - private _endpointBase: string = Constants.DEFAULT_BREEZE_ENDPOINT; - private _setCorrelationId: (v: string) => void; - private _profileQueryEndpoint: string; - private _instrumentationKey: string; - - - constructor(setupString?: string) { - // Load config values from env variables and JSON if available - this._mergeConfig(); - const connectionStringEnv: string | undefined = this._connectionString; - const csCode = ConnectionStringParser.parse(setupString); - const csEnv = ConnectionStringParser.parse(connectionStringEnv); - const iKeyCode = !csCode.instrumentationkey && Object.keys(csCode).length > 0 - ? null // CS was valid but instrumentation key was not provided, null and grab from env var - : setupString; // CS was invalid, so it must be an ikey - - this.instrumentationKey = csCode.instrumentationkey || iKeyCode /* === instrumentationKey */ || csEnv.instrumentationkey || Config._getInstrumentationKey(); - this.endpointUrl = `${this.endpointUrl || csCode.ingestionendpoint || csEnv.ingestionendpoint || this._endpointBase}/v2.1/track`; - this.maxBatchSize = this.maxBatchSize || 250; - this.maxBatchIntervalMs = this.maxBatchIntervalMs || 15000; - this.disableAppInsights = this.disableAppInsights || false; - this.samplingPercentage = this.samplingPercentage || 100; - this.correlationIdRetryIntervalMs = this.correlationIdRetryIntervalMs || 30 * 1000; - this.correlationHeaderExcludedDomains = - this.correlationHeaderExcludedDomains || - [ - "*.core.windows.net", - "*.core.chinacloudapi.cn", - "*.core.cloudapi.de", - "*.core.usgovcloudapi.net", - "*.core.microsoft.scloud", - "*.core.eaglex.ic.gov" - ]; - - this._setCorrelationId = (correlationId) => this.correlationId = correlationId; - this.ignoreLegacyHeaders = this.ignoreLegacyHeaders || false; - this.profileQueryEndpoint = csCode.ingestionendpoint || csEnv.ingestionendpoint || process.env[Config.ENV_profileQueryEndpoint] || this._endpointBase; - this.quickPulseHost = this.quickPulseHost || csCode.liveendpoint || csEnv.liveendpoint || process.env[Config.ENV_quickPulseHost] || Constants.DEFAULT_LIVEMETRICS_HOST; - // Parse quickPulseHost if it starts with http(s):// - if (this.quickPulseHost.match(/^https?:\/\//)) { - this.quickPulseHost = new url.URL(this.quickPulseHost).host; - } - } - - public set profileQueryEndpoint(endpoint: string) { - CorrelationIdManager.cancelCorrelationIdQuery(this, this._setCorrelationId); - this._profileQueryEndpoint = endpoint; - this.correlationId = CorrelationIdManager.correlationIdPrefix; // Reset the correlationId while we wait for the new query - CorrelationIdManager.queryCorrelationId(this, this._setCorrelationId); - } - - public get profileQueryEndpoint() { - return this._profileQueryEndpoint; - } - - public set instrumentationKey(iKey: string) { - if (!Config._validateInstrumentationKey(iKey)) { - Logging.warn("An invalid instrumentation key was provided. There may be resulting telemetry loss", this.instrumentationKey); - } - this._instrumentationKey = iKey; - } - - public get instrumentationKey(): string { - return this._instrumentationKey; - } - - private _mergeConfig() { - let jsonConfig = JsonConfig.getInstance(); - this._connectionString = jsonConfig.connectionString; - this.correlationHeaderExcludedDomains = jsonConfig.correlationHeaderExcludedDomains; - this.correlationIdRetryIntervalMs = jsonConfig.correlationIdRetryIntervalMs; - this.disableAllExtendedMetrics = jsonConfig.disableAllExtendedMetrics; - this.disableAppInsights = jsonConfig.disableAppInsights; - this.disableStatsbeat = jsonConfig.disableStatsbeat; - this.distributedTracingMode = jsonConfig.distributedTracingMode; - this.enableAutoCollectConsole = jsonConfig.enableAutoCollectConsole; - this.enableAutoCollectDependencies = jsonConfig.enableAutoCollectDependencies; - this.enableAutoCollectExceptions = jsonConfig.enableAutoCollectExceptions; - this.enableAutoCollectExtendedMetrics = jsonConfig.enableAutoCollectExtendedMetrics; - this.enableAutoCollectExternalLoggers = jsonConfig.enableAutoCollectExternalLoggers; - this.enableAutoCollectHeartbeat = jsonConfig.enableAutoCollectHeartbeat; - this.enableAutoCollectPerformance = jsonConfig.enableAutoCollectPerformance; - this.enableAutoCollectPreAggregatedMetrics = jsonConfig.enableAutoCollectPreAggregatedMetrics; - this.enableAutoCollectRequests = jsonConfig.enableAutoCollectRequests; - this.enableAutoDependencyCorrelation = jsonConfig.enableAutoDependencyCorrelation; - this.enableInternalDebugLogging = jsonConfig.enableInternalDebugLogging; - this.enableInternalWarningLogging = jsonConfig.enableInternalWarningLogging; - this.enableResendInterval = jsonConfig.enableResendInterval; - this.enableMaxBytesOnDisk = jsonConfig.enableMaxBytesOnDisk; - this.enableSendLiveMetrics = jsonConfig.enableSendLiveMetrics; - this.enableUseAsyncHooks = jsonConfig.enableUseAsyncHooks; - this.enableUseDiskRetryCaching = jsonConfig.enableUseDiskRetryCaching; - this.endpointUrl = jsonConfig.endpointUrl; - this.extendedMetricDisablers = jsonConfig.extendedMetricDisablers; - this.ignoreLegacyHeaders = jsonConfig.ignoreLegacyHeaders; - this.maxBatchIntervalMs = jsonConfig.maxBatchIntervalMs; - this.maxBatchSize = jsonConfig.maxBatchSize; - this.proxyHttpUrl = jsonConfig.proxyHttpUrl; - this.proxyHttpsUrl = jsonConfig.proxyHttpsUrl; - this.quickPulseHost = jsonConfig.quickPulseHost; - this.samplingPercentage = jsonConfig.samplingPercentage; - } - - private static _getInstrumentationKey(): string { - // check for both the documented env variable and the azure-prefixed variable - var iKey = process.env[Config.ENV_iKey] - || process.env[Config.ENV_azurePrefix + Config.ENV_iKey] - || process.env[Config.legacy_ENV_iKey] - || process.env[Config.ENV_azurePrefix + Config.legacy_ENV_iKey]; - if (!iKey || iKey == "") { - throw new Error("Instrumentation key not found, pass the key in the config to this method or set the key in the environment variable APPINSIGHTS_INSTRUMENTATIONKEY before starting the server"); - } - - return iKey; - } - - /** - * Validate UUID Format - * Specs taken from breeze repo - * The definition of a VALID instrumentation key is as follows: - * Not none - * Not empty - * Every character is a hex character [0-9a-f] - * 32 characters are separated into 5 sections via 4 dashes - * First section has 8 characters - * Second section has 4 characters - * Third section has 4 characters - * Fourth section has 4 characters - * Fifth section has 12 characters - */ - private static _validateInstrumentationKey(iKey: string): boolean { - const UUID_Regex = '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'; - const regexp = new RegExp(UUID_Regex); - return regexp.test(iKey); - } -} - -export = Config; diff --git a/Library/ConnectionStringParser.ts b/Library/ConnectionStringParser.ts deleted file mode 100644 index 90f4003e7..000000000 --- a/Library/ConnectionStringParser.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { ConnectionString, ConnectionStringKey } from "../Declarations/Contracts"; -import Constants = require("../Declarations/Constants"); - -class ConnectionStringParser { - private static _FIELDS_SEPARATOR = ";"; - private static _FIELD_KEY_VALUE_SEPARATOR = "="; - - public static parse(connectionString?: string): ConnectionString { - if (!connectionString) { - return {}; - } - - const kvPairs = connectionString.split(ConnectionStringParser._FIELDS_SEPARATOR); - - const result: ConnectionString = kvPairs.reduce((fields: ConnectionString, kv: string) => { - const kvParts = kv.split(ConnectionStringParser._FIELD_KEY_VALUE_SEPARATOR); - - if (kvParts.length === 2) { // only save fields with valid formats - const key = kvParts[0].toLowerCase() as ConnectionStringKey; - const value = kvParts[1]; - fields[key] = value as string; - } - return fields; - }, {}); - - if (Object.keys(result).length > 0) { - // this is a valid connection string, so parse the results - - if (result.endpointsuffix) { - // use endpoint suffix where overrides are not provided - const locationPrefix = result.location ? result.location + "." : ""; - result.ingestionendpoint = result.ingestionendpoint || ("https://" + locationPrefix + "dc." + result.endpointsuffix); - result.liveendpoint = result.liveendpoint || ("https://" + locationPrefix + "live." + result.endpointsuffix); - } - - // apply the default endpoints - result.ingestionendpoint = result.ingestionendpoint || Constants.DEFAULT_BREEZE_ENDPOINT; - result.liveendpoint = result.liveendpoint || Constants.DEFAULT_LIVEMETRICS_ENDPOINT; - } - - return result; - } -} - -export = ConnectionStringParser; diff --git a/Library/Context.ts b/Library/Context.ts deleted file mode 100644 index daf696147..000000000 --- a/Library/Context.ts +++ /dev/null @@ -1,62 +0,0 @@ -import os = require("os"); -import fs = require("fs"); -import path = require("path"); - -import Contracts = require("../Declarations/Contracts"); -import { APPLICATION_INSIGHTS_SDK_VERSION } from "../Declarations/Constants"; -import Logging = require("./Logging"); - -class Context { - - public keys: Contracts.ContextTagKeys; - public tags: { [key: string]: string }; - public static DefaultRoleName: string = "Web"; - public static appVersion: { [path: string]: string } = {}; - public static sdkVersion: string = null; - - constructor(packageJsonPath?: string) { - this.keys = new Contracts.ContextTagKeys(); - this.tags = <{ [key: string]: string }>{}; - - this._loadApplicationContext(packageJsonPath); - this._loadDeviceContext(); - this._loadInternalContext(); - } - - private _loadApplicationContext(packageJsonPath?: string) { - // note: this should return the host package.json - packageJsonPath = packageJsonPath || path.resolve(__dirname, "../../../../package.json"); - - if (!Context.appVersion[packageJsonPath]) { - Context.appVersion[packageJsonPath] = "unknown"; - try { - let packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); - if (packageJson && typeof packageJson.version === "string") { - Context.appVersion[packageJsonPath] = packageJson.version; - } - } catch (exception) { - Logging.info("unable to read app version: ", exception); - } - } - - this.tags[this.keys.applicationVersion] = Context.appVersion[packageJsonPath]; - } - - private _loadDeviceContext() { - this.tags[this.keys.deviceId] = ""; - this.tags[this.keys.cloudRoleInstance] = os && os.hostname(); - this.tags[this.keys.deviceOSVersion] = os && (os.type() + " " + os.release()); - this.tags[this.keys.cloudRole] = Context.DefaultRoleName; - - // not yet supported tags - this.tags["ai.device.osArchitecture"] = os && os.arch(); - this.tags["ai.device.osPlatform"] = os && os.platform(); - } - - private _loadInternalContext() { - Context.sdkVersion = APPLICATION_INSIGHTS_SDK_VERSION; - this.tags[this.keys.internalSdkVersion] = "node:" + Context.sdkVersion; - } -} - -export = Context; diff --git a/Library/CorrelationIdManager.ts b/Library/CorrelationIdManager.ts deleted file mode 100755 index c2344b6e7..000000000 --- a/Library/CorrelationIdManager.ts +++ /dev/null @@ -1,182 +0,0 @@ -import Util = require("./Util"); -import Config = require("./Config"); -import Logging = require("./Logging"); - -class CorrelationIdManager { - private static TAG = "CorrelationIdManager"; - private static _handle: NodeJS.Timer; - public static correlationIdPrefix = "cid-v1:"; - public static w3cEnabled = true; - - // To avoid extraneous HTTP requests, we maintain a queue of callbacks waiting on a particular appId lookup, - // as well as a cache of completed lookups so future requests can be resolved immediately. - private static pendingLookups: { [key: string]: Function[] } = {}; - private static completedLookups: { [key: string]: string } = {}; - - private static requestIdMaxLength = 1024; - private static currentRootId = Util.randomu32(); - - public static queryCorrelationId(config: Config, callback: (correlationId: string) => void) { - // GET request to `${this.endpointBase}/api/profiles/${this.instrumentationKey}/appId` - // If it 404s, the iKey is bad and we should give up - // If it fails otherwise, try again later - const appIdUrlString = `${config.profileQueryEndpoint}/api/profiles/${config.instrumentationKey}/appId`; - - if (CorrelationIdManager.completedLookups.hasOwnProperty(appIdUrlString)) { - callback(CorrelationIdManager.completedLookups[appIdUrlString]); - return; - } else if (CorrelationIdManager.pendingLookups[appIdUrlString]) { - CorrelationIdManager.pendingLookups[appIdUrlString].push(callback); - return; - } - - CorrelationIdManager.pendingLookups[appIdUrlString] = [callback]; - - const fetchAppId = () => { - if (!CorrelationIdManager.pendingLookups[appIdUrlString]) { - // This query has been cancelled. - return; - } - - const requestOptions = { - method: 'GET', - // Ensure this request is not captured by auto-collection. - // Note: we don't refer to the property in HttpDependencyParser because that would cause a cyclical dependency - disableAppInsightsAutoCollection: true - }; - - Logging.info(CorrelationIdManager.TAG, requestOptions); - const req = Util.makeRequest(config, appIdUrlString, requestOptions, (res) => { - if (res.statusCode === 200) { - // Success; extract the appId from the body - let appId = ""; - res.setEncoding("utf-8"); - res.on('data', (data: any) => { - appId += data; - }); - res.on('end', () => { - Logging.info(CorrelationIdManager.TAG, appId); - const result = CorrelationIdManager.correlationIdPrefix + appId; - CorrelationIdManager.completedLookups[appIdUrlString] = result; - if (CorrelationIdManager.pendingLookups[appIdUrlString]) { - CorrelationIdManager.pendingLookups[appIdUrlString].forEach((cb) => cb(result)); - } - delete CorrelationIdManager.pendingLookups[appIdUrlString]; - }); - } else if (res.statusCode >= 400 && res.statusCode < 500) { - // Not found, probably a bad key. Do not try again. - CorrelationIdManager.completedLookups[appIdUrlString] = undefined; - delete CorrelationIdManager.pendingLookups[appIdUrlString]; - } - else { - // Keep retrying - return; - } - // Do not retry - if (CorrelationIdManager._handle) { - clearTimeout(CorrelationIdManager._handle); - CorrelationIdManager._handle = undefined; - } - }, true, false); - if (req) { - req.on('error', (error: Error) => { - // Unable to contact endpoint. - // Do nothing for now. - Logging.warn(CorrelationIdManager.TAG, error); - if (this._handle) { - clearTimeout(CorrelationIdManager._handle); - CorrelationIdManager._handle = undefined; - } - }); - req.end(); - } - }; - if (!CorrelationIdManager._handle) { - CorrelationIdManager._handle = setTimeout(fetchAppId, config.correlationIdRetryIntervalMs); - CorrelationIdManager._handle.unref(); // Don't block apps from terminating - } - // Initial fetch - setImmediate(fetchAppId); - } - - public static cancelCorrelationIdQuery(config: Config, callback: (correlationId: string) => void) { - const appIdUrlString = `${config.profileQueryEndpoint}/api/profiles/${config.instrumentationKey}/appId`; - const pendingLookups = CorrelationIdManager.pendingLookups[appIdUrlString]; - if (pendingLookups) { - CorrelationIdManager.pendingLookups[appIdUrlString] = pendingLookups.filter((cb) => cb != callback); - if (CorrelationIdManager.pendingLookups[appIdUrlString].length == 0) { - delete CorrelationIdManager.pendingLookups[appIdUrlString]; - } - } - } - - /** - * Generate a request Id according to https://github.com/lmolkova/correlation/blob/master/hierarchical_request_id.md - * @param parentId - */ - public static generateRequestId(parentId: string): string { - if (parentId) { - parentId = parentId[0] == '|' ? parentId : '|' + parentId; - if (parentId[parentId.length - 1] !== '.') { - parentId += '.'; - } - - const suffix = (CorrelationIdManager.currentRootId++).toString(16); - - return CorrelationIdManager.appendSuffix(parentId, suffix, '_') - } else { - return CorrelationIdManager.generateRootId(); - } - } - - /** - * Given a hierarchical identifier of the form |X.* - * return the root identifier X - * @param id - */ - public static getRootId(id: string): string { - let endIndex = id.indexOf('.'); - if (endIndex < 0) { - endIndex = id.length; - } - - const startIndex = id[0] === '|' ? 1 : 0; - return id.substring(startIndex, endIndex); - } - - private static generateRootId(): string { - return '|' + Util.w3cTraceId() + '.'; - } - - private static appendSuffix(parentId: string, suffix: string, delimiter: string): string { - if (parentId.length + suffix.length < CorrelationIdManager.requestIdMaxLength) { - return parentId + suffix + delimiter; - } - - // Combined identifier would be too long, so we must truncate it. - // We need 9 characters of space: 8 for the overflow ID, 1 for the - // overflow delimiter '#' - let trimPosition = CorrelationIdManager.requestIdMaxLength - 9; - if (parentId.length > trimPosition) { - for (; trimPosition > 1; --trimPosition) { - const c = parentId[trimPosition - 1]; - if (c === '.' || c === '_') { - break; - } - } - } - - if (trimPosition <= 1) { - // parentId is not a valid ID - return CorrelationIdManager.generateRootId(); - } - - suffix = Util.randomu32().toString(16); - while (suffix.length < 8) { - suffix = '0' + suffix; - } - return parentId.substring(0, trimPosition) + suffix + '#'; - } -} - -export = CorrelationIdManager; diff --git a/Library/EnvelopeFactory.ts b/Library/EnvelopeFactory.ts deleted file mode 100644 index 1fae95045..000000000 --- a/Library/EnvelopeFactory.ts +++ /dev/null @@ -1,395 +0,0 @@ -import Contracts = require("../Declarations/Contracts") -import Util = require("./Util") -import Config = require("./Config"); -import Context = require("./Context"); -import { CorrelationContextManager } from "../AutoCollection/CorrelationContextManager"; - - -/** - * Manages the logic of creating envelopes from Telemetry objects - */ -class EnvelopeFactory { - - - /** - * Creates envelope ready to be sent by Channel - * @param telemetry Telemetry data - * @param telemetryType Type of telemetry - * @param commonProperties Bag of custom common properties to be added to the envelope - * @param context Client context - * @param config Client configuration - */ - public static createEnvelope( - telemetry: Contracts.Telemetry, - telemetryType: Contracts.TelemetryType, - commonProperties?: { [key: string]: string; }, - context?: Context, - config?: Config): Contracts.Envelope { - - var data = null; - - - switch (telemetryType) { - case Contracts.TelemetryType.Trace: - data = EnvelopeFactory.createTraceData(telemetry); - break; - case Contracts.TelemetryType.Dependency: - data = EnvelopeFactory.createDependencyData(telemetry); - break; - case Contracts.TelemetryType.Event: - data = EnvelopeFactory.createEventData(telemetry); - break; - case Contracts.TelemetryType.Exception: - data = EnvelopeFactory.createExceptionData(telemetry); - break; - case Contracts.TelemetryType.Request: - data = EnvelopeFactory.createRequestData(telemetry); - break; - case Contracts.TelemetryType.Metric: - data = EnvelopeFactory.createMetricData(telemetry); - break; - case Contracts.TelemetryType.Availability: - data = EnvelopeFactory.createAvailabilityData(telemetry); - break; - case Contracts.TelemetryType.PageView: - data = EnvelopeFactory.createPageViewData(telemetry); - break; - } - - if (data && data.baseData) { - if (Contracts.domainSupportsProperties(data.baseData)) { // Do instanceof check. TS will automatically cast and allow the properties property - if (commonProperties) { - // if no properties are specified just add the common ones - if (!data.baseData.properties) { - data.baseData.properties = commonProperties; - } else { - // otherwise, check each of the common ones - for (var name in commonProperties) { - // only override if the property `name` has not been set on this item - if (!data.baseData.properties[name]) { - data.baseData.properties[name] = commonProperties[name]; - } - } - } - } - if (data.baseData.properties) { - // sanitize properties - data.baseData.properties = Util.validateStringMap(data.baseData.properties); - } - } - } - - var iKey = config ? config.instrumentationKey || "" : ""; - var envelope = new Contracts.Envelope(); - envelope.data = data; - envelope.iKey = iKey; - - // this is kind of a hack, but the envelope name is always the same as the data name sans the chars "data" - envelope.name = - "Microsoft.ApplicationInsights." + - iKey.replace(/-/g, "") + - "." + - data.baseType.substr(0, data.baseType.length - 4); - envelope.tags = this.getTags(context, telemetry.tagOverrides); - envelope.time = (new Date()).toISOString(); - envelope.ver = 1; - envelope.sampleRate = config ? config.samplingPercentage : 100; - - // Exclude metrics from sampling by default - if (telemetryType === Contracts.TelemetryType.Metric) { - envelope.sampleRate = 100; - } - - return envelope; - } - - private static createTraceData(telemetry: Contracts.TraceTelemetry): Contracts.Data { - var trace = new Contracts.MessageData(); - trace.message = telemetry.message; - trace.properties = telemetry.properties; - if (!isNaN(telemetry.severity)) { - trace.severityLevel = telemetry.severity; - } else { - trace.severityLevel = Contracts.SeverityLevel.Information; - } - - var data = new Contracts.Data(); - data.baseType = Contracts.telemetryTypeToBaseType(Contracts.TelemetryType.Trace); - data.baseData = trace; - return data; - } - - private static createDependencyData(telemetry: Contracts.DependencyTelemetry & Contracts.Identified): Contracts.Data { - var remoteDependency = new Contracts.RemoteDependencyData(); - if (typeof telemetry.name === "string") { - remoteDependency.name = telemetry.name.length > 1024 ? telemetry.name.slice(0, 1021) + '...' : telemetry.name; - } - remoteDependency.data = telemetry.data; - remoteDependency.target = telemetry.target; - remoteDependency.duration = Util.msToTimeSpan(telemetry.duration); - remoteDependency.success = telemetry.success; - remoteDependency.type = telemetry.dependencyTypeName; - remoteDependency.properties = telemetry.properties; - remoteDependency.resultCode = (telemetry.resultCode ? telemetry.resultCode + '' : ''); - - if (telemetry.id) { - remoteDependency.id = telemetry.id; - } - else { - remoteDependency.id = Util.w3cTraceId(); - } - - var data = new Contracts.Data(); - data.baseType = Contracts.telemetryTypeToBaseType(Contracts.TelemetryType.Dependency); - data.baseData = remoteDependency; - return data; - } - - private static createEventData(telemetry: Contracts.EventTelemetry): Contracts.Data { - var event = new Contracts.EventData(); - event.name = telemetry.name; - event.properties = telemetry.properties; - event.measurements = telemetry.measurements; - - var data = new Contracts.Data(); - data.baseType = Contracts.telemetryTypeToBaseType(Contracts.TelemetryType.Event); - data.baseData = event; - return data; - } - - private static createExceptionData(telemetry: Contracts.ExceptionTelemetry): Contracts.Data { - var exception = new Contracts.ExceptionData(); - exception.properties = telemetry.properties; - if (!isNaN(telemetry.severity)) { - exception.severityLevel = telemetry.severity; - } else { - exception.severityLevel = Contracts.SeverityLevel.Error; - } - exception.measurements = telemetry.measurements; - exception.exceptions = []; - - var stack = telemetry.exception["stack"]; - var exceptionDetails = new Contracts.ExceptionDetails(); - exceptionDetails.message = telemetry.exception.message; - exceptionDetails.typeName = telemetry.exception.name; - exceptionDetails.parsedStack = this.parseStack(stack); - exceptionDetails.hasFullStack = Util.isArray(exceptionDetails.parsedStack) && exceptionDetails.parsedStack.length > 0; - exception.exceptions.push(exceptionDetails); - - var data = new Contracts.Data(); - data.baseType = Contracts.telemetryTypeToBaseType(Contracts.TelemetryType.Exception); - data.baseData = exception; - return data; - } - - private static createRequestData(telemetry: Contracts.RequestTelemetry & Contracts.Identified): Contracts.Data { - var requestData = new Contracts.RequestData(); - if (telemetry.id) { - requestData.id = telemetry.id; - } - else { - requestData.id = Util.w3cTraceId(); - } - requestData.name = telemetry.name; - requestData.url = telemetry.url; - requestData.source = telemetry.source; - requestData.duration = Util.msToTimeSpan(telemetry.duration); - requestData.responseCode = (telemetry.resultCode ? telemetry.resultCode + '' : ''); - requestData.success = telemetry.success - requestData.properties = telemetry.properties; - requestData.measurements = telemetry.measurements; - - var data = new Contracts.Data(); - data.baseType = Contracts.telemetryTypeToBaseType(Contracts.TelemetryType.Request); - data.baseData = requestData; - return data; - } - - private static createMetricData(telemetry: Contracts.MetricTelemetry): Contracts.Data { - var metrics = new Contracts.MetricData(); // todo: enable client-batching of these - metrics.metrics = []; - - var metric = new Contracts.DataPoint(); - metric.count = !isNaN(telemetry.count) ? telemetry.count : 1; - metric.kind = Contracts.DataPointType.Aggregation; - metric.max = !isNaN(telemetry.max) ? telemetry.max : telemetry.value; - metric.min = !isNaN(telemetry.min) ? telemetry.min : telemetry.value; - metric.name = telemetry.name; - metric.stdDev = !isNaN(telemetry.stdDev) ? telemetry.stdDev : 0; - metric.value = telemetry.value; - metric.ns = telemetry.namespace; - - metrics.metrics.push(metric); - - metrics.properties = telemetry.properties; - - var data = new Contracts.Data(); - data.baseType = Contracts.telemetryTypeToBaseType(Contracts.TelemetryType.Metric); - data.baseData = metrics; - return data; - } - - private static createAvailabilityData( - telemetry: Contracts.AvailabilityTelemetry & Contracts.Identified, - ): Contracts.Data { - let availabilityData = new Contracts.AvailabilityData(); - - if (telemetry.id) { - availabilityData.id = telemetry.id; - } else { - availabilityData.id = Util.w3cTraceId(); - } - availabilityData.name = telemetry.name; - availabilityData.duration = Util.msToTimeSpan(telemetry.duration); - availabilityData.success = telemetry.success; - availabilityData.runLocation = telemetry.runLocation; - availabilityData.message = telemetry.message; - availabilityData.measurements = telemetry.measurements; - availabilityData.properties = telemetry.properties; - - let data = new Contracts.Data(); - data.baseType = Contracts.telemetryTypeToBaseType(Contracts.TelemetryType.Availability); - data.baseData = availabilityData; - - return data; - } - - private static createPageViewData( - telemetry: Contracts.PageViewTelemetry & Contracts.Identified, - ): Contracts.Data { - let pageViewData = new Contracts.PageViewData(); - - pageViewData.name = telemetry.name; - pageViewData.duration = Util.msToTimeSpan(telemetry.duration); - pageViewData.url = telemetry.url; - pageViewData.measurements = telemetry.measurements; - pageViewData.properties = telemetry.properties; - - let data = new Contracts.Data(); - data.baseType = Contracts.telemetryTypeToBaseType(Contracts.TelemetryType.PageView); - data.baseData = pageViewData; - - return data; - } - - private static getTags(context: Context, tagOverrides?: { [key: string]: string; }) { - var correlationContext = CorrelationContextManager.getCurrentContext(); - - // Make a copy of context tags so we don't alter the actual object - // Also perform tag overriding - var newTags = <{ [key: string]: string }>{}; - - if (context && context.tags) { - for (var key in context.tags) { - newTags[key] = context.tags[key]; - } - } - if (tagOverrides) { - for (var key in tagOverrides) { - newTags[key] = tagOverrides[key]; - } - } - - // Fill in internally-populated values if not already set - if (correlationContext) { - newTags[context.keys.operationId] = newTags[context.keys.operationId] || correlationContext.operation.id; - newTags[context.keys.operationName] = newTags[context.keys.operationName] || correlationContext.operation.name; - newTags[context.keys.operationParentId] = newTags[context.keys.operationParentId] || correlationContext.operation.parentId; - } - - return newTags; - } - - - private static parseStack(stack: any): _StackFrame[] { - var parsedStack: _StackFrame[] = undefined; - if (typeof stack === "string") { - var frames = stack.split("\n"); - parsedStack = []; - var level = 0; - - var totalSizeInBytes = 0; - for (var i = 0; i <= frames.length; i++) { - var frame = frames[i]; - if (_StackFrame.regex.test(frame)) { - var parsedFrame = new _StackFrame(frames[i], level++); - totalSizeInBytes += parsedFrame.sizeInBytes; - parsedStack.push(parsedFrame); - } - } - - // DP Constraint - exception parsed stack must be < 32KB - // remove frames from the middle to meet the threshold - var exceptionParsedStackThreshold = 32 * 1024; - if (totalSizeInBytes > exceptionParsedStackThreshold) { - var left = 0; - var right = parsedStack.length - 1; - var size = 0; - var acceptedLeft = left; - var acceptedRight = right; - - while (left < right) { - // check size - var lSize = parsedStack[left].sizeInBytes; - var rSize = parsedStack[right].sizeInBytes; - size += lSize + rSize; - - if (size > exceptionParsedStackThreshold) { - - // remove extra frames from the middle - var howMany = acceptedRight - acceptedLeft + 1; - parsedStack.splice(acceptedLeft, howMany); - break; - } - - // update pointers - acceptedLeft = left; - acceptedRight = right; - - left++; - right--; - } - } - } - - return parsedStack; - } - -} - -class _StackFrame { - - // regex to match stack frames from ie/chrome/ff - // methodName=$2, fileName=$4, lineNo=$5, column=$6 - public static regex = /^(\s+at)?(.*?)(\@|\s\(|\s)([^\(\n]+):(\d+):(\d+)(\)?)$/; - public static baseSize = 58; //'{"method":"","level":,"assembly":"","fileName":"","line":}'.length - public sizeInBytes = 0; - public level: number; - public method: string; - public assembly: string; - public fileName: string; - public line: number; - - constructor(frame: string, level: number) { - this.level = level; - this.method = ""; - this.assembly = Util.trim(frame); - var matches = frame.match(_StackFrame.regex); - if (matches && matches.length >= 5) { - this.method = Util.trim(matches[2]) || this.method; - this.fileName = Util.trim(matches[4]) || ""; - this.line = parseInt(matches[5]) || 0; - } - - this.sizeInBytes += this.method.length; - this.sizeInBytes += this.fileName.length; - this.sizeInBytes += this.assembly.length; - - // todo: these might need to be removed depending on how the back-end settles on their size calculation - this.sizeInBytes += _StackFrame.baseSize; - this.sizeInBytes += this.level.toString().length; - this.sizeInBytes += this.line.toString().length; - } -} - -export = EnvelopeFactory; diff --git a/Library/FileAccessControl.ts b/Library/FileAccessControl.ts deleted file mode 100644 index 038ffb1f0..000000000 --- a/Library/FileAccessControl.ts +++ /dev/null @@ -1,164 +0,0 @@ -import fs = require("fs"); -import os = require("os"); -import child_process = require("child_process"); - -import Logging = require("./Logging"); - - -export class FileAccessControl { - private static TAG = "FileAccessControl"; - - private static ICACLS_PATH = `${process.env.systemdrive}/windows/system32/icacls.exe`; - private static POWERSHELL_PATH = `${process.env.systemdrive}/windows/system32/windowspowershell/v1.0/powershell.exe`; - private static ACLED_DIRECTORIES: { [id: string]: boolean } = {}; - private static ACL_IDENTITY: string = null; - private static OS_FILE_PROTECTION_CHECKED = false; - public static OS_PROVIDES_FILE_PROTECTION = false; - public static USE_ICACLS = os.type() === "Windows_NT"; - - - // Check if file access control could be enabled - public static checkFileProtection() { - if (!FileAccessControl.OS_PROVIDES_FILE_PROTECTION && !FileAccessControl.OS_FILE_PROTECTION_CHECKED) { - FileAccessControl.OS_FILE_PROTECTION_CHECKED = true; - // Node's chmod levels do not appropriately restrict file access on Windows - // Use the built-in command line tool ICACLS on Windows to properly restrict - // access to the temporary directory used for disk retry mode. - if (FileAccessControl.USE_ICACLS) { - // This should be async - but it's currently safer to have this synchronous - // This guarantees we can immediately fail setDiskRetryMode if we need to - try { - FileAccessControl.OS_PROVIDES_FILE_PROTECTION = fs.existsSync(FileAccessControl.ICACLS_PATH); - } catch (e) { } - if (!FileAccessControl.OS_PROVIDES_FILE_PROTECTION) { - Logging.warn(FileAccessControl.TAG, "Could not find ICACLS in expected location! This is necessary to use disk retry mode on Windows.") - } - } else { - // chmod works everywhere else - FileAccessControl.OS_PROVIDES_FILE_PROTECTION = true; - } - } - } - - public static async applyACLRules(directory: string): Promise { - if (FileAccessControl.USE_ICACLS) { - if (FileAccessControl.ACLED_DIRECTORIES[directory] === undefined) { - // Avoid multiple calls race condition by setting ACLED_DIRECTORIES to false for this directory immediately - // If batches are being failed faster than the processes spawned below return, some data won't be stored to disk - // This is better than the alternative of potentially infinitely spawned processes - FileAccessControl.ACLED_DIRECTORIES[directory] = false; - try { - // Restrict this directory to only current user and administrator access - let identity = await this._getACLIdentity(); - await this._runICACLS(this._getACLArguments(directory, identity)); - FileAccessControl.ACLED_DIRECTORIES[directory] = true; - } - catch (ex) { - FileAccessControl.ACLED_DIRECTORIES[directory] = false; // false is used to cache failed (vs undefined which is "not yet tried") - throw ex; - } - } else { - if (!FileAccessControl.ACLED_DIRECTORIES[directory]) { - throw new Error("Setting ACL restrictions did not succeed (cached result)"); - } - } - } - } - - public static applyACLRulesSync(directory: string) { - if (FileAccessControl.USE_ICACLS) { - // For performance, only run ACL rules if we haven't already during this session - if (FileAccessControl.ACLED_DIRECTORIES[directory] === undefined) { - this._runICACLSSync(this._getACLArguments(directory, this._getACLIdentitySync())); - FileAccessControl.ACLED_DIRECTORIES[directory] = true; // If we get here, it succeeded. _runIACLSSync will throw on failures - return; - } else if (!FileAccessControl.ACLED_DIRECTORIES[directory]) { // falsy but not undefined - throw new Error("Setting ACL restrictions did not succeed (cached result)"); - } - } - } - - private static _runICACLS(args: string[]): Promise { - return new Promise((resolve, reject) => { - var aclProc = child_process.spawn(FileAccessControl.ICACLS_PATH, args, { windowsHide: true }); - aclProc.on("error", (e: Error) => reject(e)); - aclProc.on("close", (code: number, signal: string) => { - if (code === 0) { - resolve(); - } - else { - reject(new Error(`Setting ACL restrictions did not succeed (ICACLS returned code ${code})`)); - } - }); - }); - } - - private static _runICACLSSync(args: string[]) { - // Some very old versions of Node (< 0.11) don't have this - if (child_process.spawnSync) { - var aclProc = child_process.spawnSync(FileAccessControl.ICACLS_PATH, args, { windowsHide: true }); - if (aclProc.error) { - throw aclProc.error; - } else if (aclProc.status !== 0) { - throw new Error(`Setting ACL restrictions did not succeed (ICACLS returned code ${aclProc.status})`); - } - } else { - throw new Error("Could not synchronously call ICACLS under current version of Node.js"); - } - } - - private static _getACLIdentity(): Promise { - return new Promise((resolve, reject) => { - if (FileAccessControl.ACL_IDENTITY) { - resolve(FileAccessControl.ACL_IDENTITY); - } - var psProc = child_process.spawn(FileAccessControl.POWERSHELL_PATH, - ["-Command", "[System.Security.Principal.WindowsIdentity]::GetCurrent().Name"], { - windowsHide: true, - stdio: ['ignore', 'pipe', 'pipe'] // Needed to prevent hanging on Win 7 - }); - let data = ""; - psProc.stdout.on("data", (d: string) => data += d); - psProc.on("error", (e: Error) => reject(e)); - psProc.on("close", (code: number, signal: string) => { - FileAccessControl.ACL_IDENTITY = data && data.trim(); - if (code === 0) { - resolve(FileAccessControl.ACL_IDENTITY); - } - else { - reject(new Error(`Getting ACL identity did not succeed (PS returned code ${code})`)); - } - }); - }); - } - - private static _getACLIdentitySync() { - if (FileAccessControl.ACL_IDENTITY) { - return FileAccessControl.ACL_IDENTITY; - } - // Some very old versions of Node (< 0.11) don't have this - if (child_process.spawnSync) { - var psProc = child_process.spawnSync(FileAccessControl.POWERSHELL_PATH, - ["-Command", "[System.Security.Principal.WindowsIdentity]::GetCurrent().Name"], { - windowsHide: true, - stdio: ['ignore', 'pipe', 'pipe'] // Needed to prevent hanging on Win 7 - }); - if (psProc.error) { - throw psProc.error; - } else if (psProc.status !== 0) { - throw new Error(`Getting ACL identity did not succeed (PS returned code ${psProc.status})`); - } - FileAccessControl.ACL_IDENTITY = psProc.stdout && psProc.stdout.toString().trim(); - return FileAccessControl.ACL_IDENTITY; - } else { - throw new Error("Could not synchronously get ACL identity under current version of Node.js"); - } - } - - private static _getACLArguments(directory: string, identity: string) { - return [directory, - "/grant", "*S-1-5-32-544:(OI)(CI)F", // Full permission for Administrators - "/grant", `${identity}:(OI)(CI)F`, // Full permission for current user - "/inheritance:r"]; // Remove all inherited permissions - } -} diff --git a/Library/FlushOptions.ts b/Library/FlushOptions.ts deleted file mode 100644 index a6d3f2ce8..000000000 --- a/Library/FlushOptions.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Encapsulates options passed into client.flush() function - */ -interface FlushOptions -{ - /** - * Flag indicating whether application is crashing. When this flag is set to true - * and storing data locally is enabled, Node.JS SDK will attempt to store data on disk - */ - isAppCrashing?:boolean - - /** - * Callback that will be invoked with the response from server, in case of isAppCrashing set to true, - * with immediate notification that data was stored - */ - callback?: (v: string) => void; -} -export = FlushOptions; \ No newline at end of file diff --git a/Library/Functions.ts b/Library/Functions.ts deleted file mode 100644 index 875712ee1..000000000 --- a/Library/Functions.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * The context object can be used for writing logs, reading data from bindings, setting outputs and using - * the context.done callback when your exported function is synchronous. A context object is passed - * to your function from the Azure Functions runtime on function invocation. - */ -export interface Context { - traceContext: TraceContext -} - -/** - * HTTP request object. Provided to your function when using HTTP Bindings. - */ -export interface HttpRequest { - method: string | null; - url: string; - headers: { - [key: string]: string; - }; -} - -/** - * TraceContext information to enable distributed tracing scenarios. - */ -export interface TraceContext { - /** Describes the position of the incoming request in its trace graph in a portable, fixed-length format. */ - traceparent: string | null | undefined; - /** Extends traceparent with vendor-specific data. */ - tracestate: string | null | undefined; - /** Holds additional properties being sent as part of request telemetry. */ - attributes: { - [k: string]: string; - } | null | undefined; -} diff --git a/Library/InternalAzureLogger.ts b/Library/InternalAzureLogger.ts deleted file mode 100644 index b68fc809a..000000000 --- a/Library/InternalAzureLogger.ts +++ /dev/null @@ -1,167 +0,0 @@ -import fs = require("fs"); -import os = require("os"); -import path = require("path"); -import FileSystemHelper = require("./FileSystemHelper"); - - -class InternalAzureLogger { - - private static _instance: InternalAzureLogger; - public maxHistory: number; - public maxSizeBytes: number; - - private TAG = "Logger"; - private _cleanupTimeOut = 60 * 30 * 1000; // 30 minutes; - private static _fileCleanupTimer: NodeJS.Timer = null; - private _tempDir: string; - public _logFileName: string; - private _fileFullPath: string; - private _backUpNameFormat: string; - private _logToFile = false; - private _logToConsole = true; - - - constructor() { - let logDestination = process.env.APPLICATIONINSIGHTS_LOG_DESTINATION; // destination can be one of file, console or file+console - if (logDestination == "file+console") { - this._logToFile = true; - } - if (logDestination == "file") { - this._logToFile = true; - this._logToConsole = false; - } - this.maxSizeBytes = 50000; - this.maxHistory = 1; - this._logFileName = "applicationinsights.log"; - - // If custom path not provided use temp folder, /tmp for *nix and USERDIR/AppData/Local/Temp for Windows - let logFilePath = process.env.APPLICATIONINSIGHTS_LOGDIR; - if (!logFilePath) { - this._tempDir = path.join(os.tmpdir(), "appInsights-node"); - } - else { - if (path.isAbsolute(logFilePath)) { - this._tempDir = logFilePath; - } - else { - this._tempDir = path.join(process.cwd(), logFilePath); - } - } - this._fileFullPath = path.join(this._tempDir, this._logFileName); - this._backUpNameFormat = "." + this._logFileName; // {currentime}.applicationinsights.log - - if (this._logToFile) { - if (!InternalAzureLogger._fileCleanupTimer) { - InternalAzureLogger._fileCleanupTimer = setInterval(() => { this._fileCleanupTask(); }, this._cleanupTimeOut); - InternalAzureLogger._fileCleanupTimer.unref(); - } - } - } - - public info(message?: any, ...optionalParams: any[]) { - let args = message ? [message, ...optionalParams] : optionalParams; - if (this._logToFile) { - this._storeToDisk(args); - } - if (this._logToConsole) { - console.info(...args); - } - } - - public warning(message?: any, ...optionalParams: any[]) { - let args = message ? [message, ...optionalParams] : optionalParams; - if (this._logToFile) { - this._storeToDisk(args); - } - if (this._logToConsole) { - console.warn(...args); - } - } - - static getInstance() { - if (!InternalAzureLogger._instance) { - InternalAzureLogger._instance = new InternalAzureLogger(); - } - return InternalAzureLogger._instance; - } - - private async _storeToDisk(args: any): Promise { - let data = args + "\r\n"; - - try { - await FileSystemHelper.confirmDirExists(this._tempDir); - } - catch (err) { - console.log(this.TAG, "Failed to create directory for log file: " + (err && err.message)); - return; - } - try { - await FileSystemHelper.accessAsync(this._fileFullPath, fs.constants.F_OK); - } - catch (err) { - // No file create one - await FileSystemHelper.appendFileAsync(this._fileFullPath, data).catch((appendError) => { - console.log(this.TAG, "Failed to put log into file: " + (appendError && appendError.message)); - }); - return; - } - try { - // Check size - let size = await FileSystemHelper.getShallowFileSize(this._fileFullPath); - if (size > this.maxSizeBytes) { - await this._createBackupFile(data); - } - else { - await FileSystemHelper.appendFileAsync(this._fileFullPath, data); - } - } - catch (err) { - console.log(this.TAG, "Failed to create backup file: " + (err && err.message)); - } - } - - private async _createBackupFile(data: string): Promise { - try { - let buffer = await FileSystemHelper.readFileAsync(this._fileFullPath); - let backupPath = path.join(this._tempDir, new Date().getTime() + "." + this._logFileName); - await FileSystemHelper.writeFileAsync(backupPath, buffer); - } - catch (err) { - console.log(`Failed to generate backup log file`, err); - } - finally { - // Store logs - FileSystemHelper.writeFileAsync(this._fileFullPath, data); - } - } - - private async _fileCleanupTask(): Promise { - try { - let files = await FileSystemHelper.readdirAsync(this._tempDir); - // Filter only backup files - files = files.filter(f => path.basename(f).indexOf(this._backUpNameFormat) > -1); - // Sort by creation date - files.sort((a: string, b: String) => { - // Check expiration - let aCreationDate: Date = new Date(parseInt(a.split(this._backUpNameFormat)[0])); - let bCreationDate: Date = new Date(parseInt(b.split(this._backUpNameFormat)[0])); - if (aCreationDate < bCreationDate) { - return -1; - } - if (aCreationDate >= bCreationDate) { - return 1; - } - }); - let totalFiles = files.length; - for (let i = 0; i < totalFiles - this.maxHistory; i++) { - let pathToDelete = path.join(this._tempDir, files[i]); - await FileSystemHelper.unlinkAsync(pathToDelete); - } - } - catch (err) { - console.log(this.TAG, "Failed to cleanup log files: " + (err && err.message)); - } - } -} - -export = InternalAzureLogger; \ No newline at end of file diff --git a/Library/Logging.ts b/Library/Logging.ts deleted file mode 100644 index 4cc5d9e4b..000000000 --- a/Library/Logging.ts +++ /dev/null @@ -1,22 +0,0 @@ -import InternalAzureLogger = require("./InternalAzureLogger"); - -class Logging { - public static enableDebug = false; - public static disableWarnings = false; - - private static TAG = "ApplicationInsights:"; - - public static info(message?: any, ...optionalParams: any[]) { - if (this.enableDebug) { - InternalAzureLogger.getInstance().info(this.TAG + message, optionalParams); - } - } - - public static warn(message?: any, ...optionalParams: any[]) { - if (!this.disableWarnings) { - InternalAzureLogger.getInstance().warning(this.TAG + message, optionalParams); - } - } -} - -export = Logging; diff --git a/Library/NodeClient.ts b/Library/NodeClient.ts deleted file mode 100644 index dff48f037..000000000 --- a/Library/NodeClient.ts +++ /dev/null @@ -1,62 +0,0 @@ -import http = require("http"); -import https = require("https"); -import TelemetryClient = require("./TelemetryClient"); -import ServerRequestTracking = require("../AutoCollection/HttpRequests"); -import ClientRequestTracking = require("../AutoCollection/HttpDependencies"); -import Logging = require("./Logging"); -import Contracts = require("../Declarations/Contracts"); - -/** - * Application Insights Telemetry Client for Node.JS. Provides the Application Insights TelemetryClient API - * in addition to Node-specific helper functions. - * Construct a new TelemetryClient to have an instance with a different configuration than the default client. - * In most cases, `appInsights.defaultClient` should be used instead. - */ -class NodeClient extends TelemetryClient { - - /** - * Log RequestTelemetry from HTTP request and response. This method will log immediately without waiting for request completion - * and it requires duration parameter to be specified on NodeHttpRequestTelemetry object. - * Use trackNodeHttpRequest function to log the telemetry after request completion - * @param telemetry Object encapsulating incoming request, response and duration information - */ - public trackNodeHttpRequestSync(telemetry: Contracts.NodeHttpRequestTelemetry) { - if (telemetry && telemetry.request && telemetry.response && telemetry.duration) { - ServerRequestTracking.trackRequestSync(this, telemetry); - } else { - Logging.warn("trackNodeHttpRequestSync requires NodeHttpRequestTelemetry object with request, response and duration specified."); - } - } - - /** - * Log RequestTelemetry from HTTP request and response. This method will `follow` the request to completion. - * Use trackNodeHttpRequestSync function to log telemetry immediately without waiting for request completion - * @param telemetry Object encapsulating incoming request and response information - */ - public trackNodeHttpRequest(telemetry: Contracts.NodeHttpRequestTelemetry) { - if (telemetry.duration || telemetry.error) { - Logging.warn("trackNodeHttpRequest will ignore supplied duration and error parameters. These values are collected from the request and response objects."); - } - if (telemetry && telemetry.request && telemetry.response) { - ServerRequestTracking.trackRequest(this, telemetry); - } else { - Logging.warn("trackNodeHttpRequest requires NodeHttpRequestTelemetry object with request and response specified."); - } - } - - /** - * Log DependencyTelemetry from outgoing HTTP request. This method will instrument the outgoing request and append - * the specified headers and will log the telemetry when outgoing request is complete - * @param telemetry Object encapsulating outgoing request information - */ - public trackNodeHttpDependency(telemetry: Contracts.NodeHttpDependencyTelemetry) { - if (telemetry && telemetry.request) { - ClientRequestTracking.trackRequest(this, telemetry); - } - else { - Logging.warn("trackNodeHttpDependency requires NodeHttpDependencyTelemetry object with request specified."); - } - } -} - -export = NodeClient \ No newline at end of file diff --git a/Library/QuickPulseEnvelopeFactory.ts b/Library/QuickPulseEnvelopeFactory.ts deleted file mode 100644 index 298386491..000000000 --- a/Library/QuickPulseEnvelopeFactory.ts +++ /dev/null @@ -1,215 +0,0 @@ -import os = require("os"); -import Contracts = require("../Declarations/Contracts") -import Constants = require("../Declarations/Constants"); -import Util = require("./Util") -import Config = require("./Config"); -import Context = require("./Context"); -import Logging = require("./Logging"); - -var StreamId = Util.w3cTraceId(); // Create a guid - -class QuickPulseEnvelopeFactory { - private static keys = new Contracts.ContextTagKeys(); - - public static createQuickPulseEnvelope(metrics: Contracts.MetricQuickPulse[], documents: Contracts.DocumentQuickPulse[], config: Config, context: Context): Contracts.EnvelopeQuickPulse { - const machineName = (os && typeof os.hostname === "function" - && os.hostname()) || "Unknown"; // Note: os.hostname() was added in node v0.3.3 - const instance = (context.tags - && context.keys - && context.keys.cloudRoleInstance - && context.tags[context.keys.cloudRoleInstance]) || machineName; - - const roleName = (context.tags - && context.keys - && context.keys.cloudRole - && context.tags[context.keys.cloudRole]) || null; - - var envelope: Contracts.EnvelopeQuickPulse = { - Documents: documents.length > 0 ? documents : null, - InstrumentationKey: config.instrumentationKey || "", - Metrics: metrics.length > 0 ? metrics : null, - InvariantVersion: 1, // 1 -> v1 QPS protocol - Timestamp: `\/Date(${Date.now()})\/`, - Version: context.tags[context.keys.internalSdkVersion], - StreamId: StreamId, - MachineName: machineName, - Instance: instance, - RoleName: roleName - } - - return envelope; - } - - public static createQuickPulseMetric( - telemetry: Contracts.MetricTelemetry - ): Contracts.MetricQuickPulse { - var data: Contracts.MetricQuickPulse; - data = { - Name: telemetry.name, // TODO: map from MetricTelemetry name to QuickPulse name - Value: telemetry.value, - Weight: telemetry.count || 1 - }; - return data; - } - - public static telemetryEnvelopeToQuickPulseDocument(envelope: Contracts.Envelope): Contracts.DocumentQuickPulse { - switch (envelope.data.baseType) { - case Contracts.TelemetryTypeString.Event: - return QuickPulseEnvelopeFactory.createQuickPulseEventDocument(envelope); - case Contracts.TelemetryTypeString.Exception: - return QuickPulseEnvelopeFactory.createQuickPulseExceptionDocument(envelope); - case Contracts.TelemetryTypeString.Trace: - return QuickPulseEnvelopeFactory.createQuickPulseTraceDocument(envelope); - case Contracts.TelemetryTypeString.Dependency: - return QuickPulseEnvelopeFactory.createQuickPulseDependencyDocument(envelope); - case Contracts.TelemetryTypeString.Request: - return QuickPulseEnvelopeFactory.createQuickPulseRequestDocument(envelope); - } - return null; - } - - private static createQuickPulseEventDocument(envelope: Contracts.Envelope): Contracts.EventDocumentQuickPulse { - const document = QuickPulseEnvelopeFactory.createQuickPulseDocument(envelope); - const name = ((envelope.data as any).baseData as Contracts.EventData).name; - const eventDocument: Contracts.EventDocumentQuickPulse = { - ...document, - Name: name - }; - - return eventDocument; - } - - private static createQuickPulseTraceDocument(envelope: Contracts.Envelope): Contracts.MessageDocumentQuickPulse { - const document = QuickPulseEnvelopeFactory.createQuickPulseDocument(envelope); - const severityLevel = ((envelope.data as any).baseData as Contracts.MessageData).severityLevel || 0; - var traceDocument: Contracts.MessageDocumentQuickPulse = { - ...document, - Message: ((envelope.data as any).baseData as Contracts.MessageData).message, - SeverityLevel: Contracts.SeverityLevel[severityLevel] - } - - return traceDocument; - } - - private static createQuickPulseExceptionDocument(envelope: Contracts.Envelope): Contracts.ExceptionDocumentQuickPulse { - const document = QuickPulseEnvelopeFactory.createQuickPulseDocument(envelope); - const exceptionDetails = ((envelope.data as any).baseData as Contracts.ExceptionData).exceptions; - - let exception = ''; - let exceptionMessage = ''; - let exceptionType = ''; - - // Try to fill exception information from first error only - if (exceptionDetails && exceptionDetails.length > 0) { - // Try to grab the stack from parsedStack or stack - if (exceptionDetails[0].parsedStack && exceptionDetails[0].parsedStack.length > 0) { - exceptionDetails[0].parsedStack.forEach(err => { - exception += err.assembly + "\n"; - }); - } else if (exceptionDetails[0].stack && exceptionDetails[0].stack.length > 0) { - exception = exceptionDetails[0].stack; - } - - exceptionMessage = exceptionDetails[0].message; - exceptionType = exceptionDetails[0].typeName; - } - - var exceptionDocument = { - ...document, - Exception: exception, - ExceptionMessage: exceptionMessage, - ExceptionType: exceptionType - }; - return exceptionDocument; - } - - private static createQuickPulseRequestDocument(envelope: Contracts.Envelope): Contracts.RequestDocumentQuickPulse { - const document = QuickPulseEnvelopeFactory.createQuickPulseDocument(envelope); - const baseData = (envelope.data as Contracts.Data).baseData; - const requestDocument: Contracts.RequestDocumentQuickPulse = { - ...document, - Name: baseData.name, - Success: baseData.success, - Duration: baseData.duration, - ResponseCode: baseData.responseCode, - OperationName: baseData.name // TODO: is this correct? - }; - - return requestDocument; - } - - private static createQuickPulseDependencyDocument(envelope: Contracts.Envelope): Contracts.DependencyDocumentQuickPulse { - const document = QuickPulseEnvelopeFactory.createQuickPulseDocument(envelope); - const baseData = (envelope.data as Contracts.Data).baseData; - - const dependencyDocument: Contracts.DependencyDocumentQuickPulse = { - ...document, - Name: baseData.name, - Target: baseData.target, - Success: baseData.success, - Duration: baseData.duration, - ResultCode: baseData.resultCode, - CommandName: baseData.data, - OperationName: document.OperationId, - DependencyTypeName: baseData.type, - } - return dependencyDocument; - } - - private static createQuickPulseDocument(envelope: Contracts.Envelope): Contracts.DocumentQuickPulse { - let documentType: Constants.QuickPulseDocumentType; - let __type: Constants.QuickPulseType; - let operationId, properties; - - - if (envelope.data.baseType) { - __type = Constants.TelemetryTypeStringToQuickPulseType[envelope.data.baseType as Contracts.TelemetryTypeValues]; - documentType = Constants.TelemetryTypeStringToQuickPulseDocumentType[envelope.data.baseType as Contracts.TelemetryTypeValues]; - } else { - // Remark: This should never be hit because createQuickPulseDocument is only called within - // valid baseType values - Logging.warn("Document type invalid; not sending live metric document", envelope.data.baseType); - } - - operationId = envelope.tags[QuickPulseEnvelopeFactory.keys.operationId]; - properties = QuickPulseEnvelopeFactory.aggregateProperties(envelope); - - var document: Contracts.DocumentQuickPulse = { - DocumentType: documentType, - __type: __type, - OperationId: operationId, - Version: "1.0", - Properties: properties - }; - - return document; - } - - private static aggregateProperties(envelope: Contracts.Envelope): Contracts.IDocumentProperty[] { - const properties: Contracts.IDocumentProperty[] = []; - - // Collect measurements - const meas = ((envelope.data as any).baseData).measurements || {}; - for (let key in meas) { - if (meas.hasOwnProperty(key)) { - const value = meas[key]; - const property: Contracts.IDocumentProperty = {key, value}; - properties.push(property); - } - } - - // Collect properties - const props = ((envelope.data as any).baseData).properties || {}; - for (let key in props) { - if (props.hasOwnProperty(key)) { - const value = props[key]; - const property: Contracts.IDocumentProperty = {key, value}; - properties.push(property); - } - } - - return properties; - } -} - -export = QuickPulseEnvelopeFactory; diff --git a/Library/QuickPulseSender.ts b/Library/QuickPulseSender.ts deleted file mode 100644 index 1412b4be4..000000000 --- a/Library/QuickPulseSender.ts +++ /dev/null @@ -1,153 +0,0 @@ -import https = require("https"); - -import AuthorizationHandler = require("./AuthorizationHandler"); -import Config = require("./Config"); -import AutoCollectHttpDependencies = require("../AutoCollection/HttpDependencies"); -import Logging = require("./Logging"); -import QuickPulseUtil = require("./QuickPulseUtil"); -import Util = require("./Util"); - -// Types -import * as http from "http"; -import * as Contracts from "../Declarations/Contracts"; - -const QuickPulseConfig = { - method: "POST", - time: "x-ms-qps-transmission-time", - pollingIntervalHint: "x-ms-qps-service-polling-interval-hint", - endpointRedirect: "x-ms-qps-service-endpoint-redirect", - instanceName: "x-ms-qps-instance-name", - streamId: "x-ms-qps-stream-id", - machineName: "x-ms-qps-machine-name", - roleName: "x-ms-qps-role-name", - streamid: "x-ms-qps-stream-id", - invariantVersion: "x-ms-qps-invariant-version", - subscribed: "x-ms-qps-subscribed" -}; - -class QuickPulseSender { - private static TAG = "QuickPulseSender"; - private static MAX_QPS_FAILURES_BEFORE_WARN = 25; - - private _config: Config; - private _consecutiveErrors: number; - private _getAuthorizationHandler: (config: Config) => AuthorizationHandler; - - constructor(config: Config, getAuthorizationHandler?: (config: Config) => AuthorizationHandler) { - this._config = config; - this._consecutiveErrors = 0; - this._getAuthorizationHandler = getAuthorizationHandler; - } - - public ping(envelope: Contracts.EnvelopeQuickPulse, - redirectedHostEndpoint: string, - done: (shouldPOST?: boolean, res?: http.IncomingMessage, redirectedHost?: string, pollingIntervalHint?: number) => void, - ): void { - - let pingHeaders: { name: string, value: string }[] = [ - { name: QuickPulseConfig.streamId, value: envelope.StreamId }, - { name: QuickPulseConfig.machineName, value: envelope.MachineName }, - { name: QuickPulseConfig.roleName, value: envelope.RoleName }, - { name: QuickPulseConfig.instanceName, value: envelope.Instance }, - { name: QuickPulseConfig.invariantVersion, value: envelope.InvariantVersion.toString() } - ]; - this._submitData(envelope, redirectedHostEndpoint, done, "ping", pingHeaders); - } - - public async post(envelope: Contracts.EnvelopeQuickPulse, - redirectedHostEndpoint: string, - done: (shouldPOST?: boolean, res?: http.IncomingMessage, redirectedHost?: string, pollingIntervalHint?: number) => void, - ): Promise { - - // Important: When POSTing data, envelope must be an array - await this._submitData([envelope], redirectedHostEndpoint, done, "post"); - } - - private async _submitData(envelope: Contracts.EnvelopeQuickPulse | Contracts.EnvelopeQuickPulse[], - redirectedHostEndpoint: string, - done: (shouldPOST?: boolean, res?: http.IncomingMessage, redirectedHost?: string, pollingIntervalHint?: number) => void, - postOrPing: "post" | "ping", - additionalHeaders?: { name: string, value: string }[] - ): Promise { - - const payload = Util.stringify(envelope); - var options = { - [AutoCollectHttpDependencies.disableCollectionRequestOption]: true, - host: (redirectedHostEndpoint && redirectedHostEndpoint.length > 0) ? redirectedHostEndpoint : this._config.quickPulseHost, - method: QuickPulseConfig.method, - path: `/QuickPulseService.svc/${postOrPing}?ikey=${this._config.instrumentationKey}`, - headers: { - 'Expect': '100-continue', - [QuickPulseConfig.time]: QuickPulseUtil.getTransmissionTime(), // unit = 100s of nanoseconds - 'Content-Type': 'application\/json', - 'Content-Length': Buffer.byteLength(payload) - } - }; - - if (additionalHeaders && additionalHeaders.length > 0) { - additionalHeaders.forEach(header => options.headers[header.name] = header.value); - } - - if (postOrPing === "post") { - let authHandler = this._getAuthorizationHandler ? this._getAuthorizationHandler(this._config) : null; - if (authHandler) { - try { - // Add bearer token - await authHandler.addAuthorizationHeader(options); - } - catch (authError) { - let notice = `Failed to get AAD bearer token for the Application. Error:`; - Logging.info(QuickPulseSender.TAG, notice, authError); - // Do not send request to Quickpulse if auth fails, data will be dropped - return; - } - } - } - - // HTTPS only - if (this._config.httpsAgent) { - (options).agent = this._config.httpsAgent; - } else { - (options).agent = Util.tlsRestrictedAgent; - } - - const req = https.request(options, (res: http.IncomingMessage) => { - if (res.statusCode == 200) { - const shouldPOSTData = res.headers[QuickPulseConfig.subscribed] === "true"; - const redirectHeader = res.headers[QuickPulseConfig.endpointRedirect] ? res.headers[QuickPulseConfig.endpointRedirect].toString() : null; - const pollingIntervalHint = res.headers[QuickPulseConfig.pollingIntervalHint] ? parseInt(res.headers[QuickPulseConfig.pollingIntervalHint].toString()) : null; - this._consecutiveErrors = 0; - done(shouldPOSTData, res, redirectHeader, pollingIntervalHint); - } - else { - this._onError("StatusCode:" + res.statusCode + " StatusMessage:" + res.statusMessage); - done(); - } - }); - - req.on("error", (error: Error) => { - this._onError(error); - done(); - }); - - req.write(payload); - req.end(); - } - - private _onError(error: Error | string) { - // Unable to contact qps endpoint. - // Do nothing for now. - this._consecutiveErrors++; - // LOG every error, but WARN instead when X number of consecutive errors occur - let notice = `Transient error connecting to the Live Metrics endpoint. This packet will not appear in your Live Metrics Stream. Error:`; - if (this._consecutiveErrors % QuickPulseSender.MAX_QPS_FAILURES_BEFORE_WARN === 0) { - notice = `Live Metrics endpoint could not be reached ${this._consecutiveErrors} consecutive times. Most recent error:`; - Logging.warn(QuickPulseSender.TAG, notice, error); - } else { - // Potentially transient error, do not change the ping/post state yet. - Logging.info(QuickPulseSender.TAG, notice, error); - } - } -} - -export = QuickPulseSender; \ No newline at end of file diff --git a/Library/QuickPulseStateManager.ts b/Library/QuickPulseStateManager.ts deleted file mode 100644 index 23a5b9fed..000000000 --- a/Library/QuickPulseStateManager.ts +++ /dev/null @@ -1,197 +0,0 @@ -import AuthorizationHandler = require("./AuthorizationHandler"); -import Logging = require("./Logging"); -import Config = require("./Config"); -import QuickPulseEnvelopeFactory = require("./QuickPulseEnvelopeFactory"); -import QuickPulseSender = require("./QuickPulseSender"); -import Constants = require("../Declarations/Constants"); -import Context = require("./Context"); - -import * as http from "http"; -import * as Contracts from "../Declarations/Contracts"; - - -/** State Container for sending to the QuickPulse Service */ -class QuickPulseStateManager { - public config: Config; - public context: Context; - public authorizationHandler: AuthorizationHandler; - - private static MAX_POST_WAIT_TIME = 20000; - private static MAX_PING_WAIT_TIME = 60000; - private static FALLBACK_INTERVAL = 60000; - private static PING_INTERVAL = 5000; - private static POST_INTERVAL = 1000; - - private _isCollectingData: boolean = false; - private _sender: QuickPulseSender; - private _isEnabled: boolean; - private _lastSuccessTime: number = Date.now(); - private _lastSendSucceeded: boolean = true; - private _handle: NodeJS.Timer; - private _metrics: { [name: string]: Contracts.MetricQuickPulse } = {}; - private _documents: Contracts.DocumentQuickPulse[] = []; - private _collectors: { enable: (enable: boolean) => void }[] = []; - private _redirectedHost: string = null; - private _pollingIntervalHint: number = -1; - - constructor(config: Config, context?: Context, getAuthorizationHandler?: (config: Config) => AuthorizationHandler) { - this.config = config; - this.context = context || new Context(); - this._sender = new QuickPulseSender(this.config, getAuthorizationHandler); - this._isEnabled = false; - } - - /** - * - * @param collector - */ - public addCollector(collector: any): void { - this._collectors.push(collector); - } - - /** - * Override of TelemetryClient.trackMetric - */ - public trackMetric(telemetry: Contracts.MetricTelemetry): void { - this._addMetric(telemetry); - } - - /** - * Add a document to the current buffer - * @param envelope - */ - public addDocument(envelope: Contracts.Envelope): void { - // Only add documents in buffer when Live Metrics is collecting data - if (this._isCollectingData) { - const document = QuickPulseEnvelopeFactory.telemetryEnvelopeToQuickPulseDocument(envelope); - if (document) { - this._documents.push(document); - } - } - } - - /** - * Enable or disable communication with QuickPulseService - * @param isEnabled - */ - public enable(isEnabled: boolean): void { - if (isEnabled && !this._isEnabled) { - this._isEnabled = true; - this._goQuickPulse(); - } else if (!isEnabled && this._isEnabled) { - this._isEnabled = false; - clearTimeout(this._handle); - this._handle = undefined; - } - } - - /** - * Enable or disable all collectors in this instance - * @param enable - */ - private enableCollectors(enable: boolean): void { - this._collectors.forEach(collector => { - collector.enable(enable) - }); - } - - /** - * Add the metric to this buffer. If same metric already exists in this buffer, add weight to it - * @param telemetry - */ - private _addMetric(telemetry: Contracts.MetricTelemetry) { - const { value } = telemetry; - const count = telemetry.count || 1; - - let name = Constants.PerformanceToQuickPulseCounter[telemetry.name]; - if (name) { - if (this._metrics[name]) { - this._metrics[name].Value = (this._metrics[name].Value * this._metrics[name].Weight + value * count) / (this._metrics[name].Weight + count); - this._metrics[name].Weight += count; - } else { - this._metrics[name] = QuickPulseEnvelopeFactory.createQuickPulseMetric(telemetry); - this._metrics[name].Name = name; - this._metrics[name].Weight = 1; - } - } - } - - private _resetQuickPulseBuffer(): void { - delete this._metrics; - this._metrics = {}; - this._documents.length = 0; - } - - private async _goQuickPulse(): Promise { - // Create envelope from Documents and Metrics - const metrics = Object.keys(this._metrics).map(k => this._metrics[k]); - const envelope = QuickPulseEnvelopeFactory.createQuickPulseEnvelope(metrics, this._documents.slice(), this.config, this.context); - - // Clear this document, metric buffer - this._resetQuickPulseBuffer(); - - // Send it to QuickPulseService, if collecting - if (this._isCollectingData) { - await this._post(envelope); - } else { - this._ping(envelope); - } - - let pingInterval = this._pollingIntervalHint > 0 ? this._pollingIntervalHint : QuickPulseStateManager.PING_INTERVAL; - let currentTimeout = this._isCollectingData ? QuickPulseStateManager.POST_INTERVAL : pingInterval; - if (this._isCollectingData && Date.now() - this._lastSuccessTime >= QuickPulseStateManager.MAX_POST_WAIT_TIME && !this._lastSendSucceeded) { - // Haven't posted successfully in 20 seconds, so wait 60 seconds and ping - this._isCollectingData = false; - currentTimeout = QuickPulseStateManager.FALLBACK_INTERVAL; - } else if (!this._isCollectingData && Date.now() - this._lastSuccessTime >= QuickPulseStateManager.MAX_PING_WAIT_TIME && !this._lastSendSucceeded) { - // Haven't pinged successfully in 60 seconds, so wait another 60 seconds - currentTimeout = QuickPulseStateManager.FALLBACK_INTERVAL; - } - this._lastSendSucceeded = null; - this._handle = setTimeout(this._goQuickPulse.bind(this), currentTimeout); - this._handle.unref(); // Don't block apps from terminating - } - - private _ping(envelope: Contracts.EnvelopeQuickPulse): void { - this._sender.ping(envelope, this._redirectedHost, this._quickPulseDone.bind(this)); - } - - private async _post(envelope: Contracts.EnvelopeQuickPulse): Promise { - await this._sender.post(envelope, this._redirectedHost, this._quickPulseDone.bind(this)); - } - - /** - * Change the current QPS send state. (shouldPOST == undefined) --> error, but do not change the state yet. - */ - private _quickPulseDone(shouldPOST?: boolean, res?: http.IncomingMessage, - redirectedHost?: string, pollingIntervalHint?: number): void { - if (shouldPOST != undefined) { - if (this._isCollectingData !== shouldPOST) { - Logging.info("Live Metrics sending data", shouldPOST); - this.enableCollectors(shouldPOST); - } - this._isCollectingData = shouldPOST; - - if (redirectedHost && redirectedHost.length > 0) { - this._redirectedHost = redirectedHost; - Logging.info("Redirecting endpoint to: ", redirectedHost); - } - - if (pollingIntervalHint && pollingIntervalHint > 0) { - this._pollingIntervalHint = pollingIntervalHint; - } - - if (res && res.statusCode < 300 && res.statusCode >= 200) { - this._lastSuccessTime = Date.now(); - this._lastSendSucceeded = true; - } else { - this._lastSendSucceeded = false; - } - } else { - // Received an error, keep the state as is - this._lastSendSucceeded = false; - } - } -} - -export = QuickPulseStateManager; \ No newline at end of file diff --git a/Library/QuickPulseUtil.ts b/Library/QuickPulseUtil.ts deleted file mode 100644 index 09d049296..000000000 --- a/Library/QuickPulseUtil.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @description UTC time the request was made. Expressed as the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight on January 1, 0001. This is used for clock skew calculations, so the value can never be stale (cached). - * - * @example - * 8/5/2020 10:15:00 PM UTC => 637322625000000000 - * 8/5/2020 10:15:01 PM UTC => 637322625010000000 - * - * @returns {number} - */ -const getTransmissionTime = (): number => { - return (Date.now() + 62135596800000) * 10000; -} - -export = {getTransmissionTime}; diff --git a/Library/RequestResponseHeaders.ts b/Library/RequestResponseHeaders.ts deleted file mode 100644 index 7c0046fde..000000000 --- a/Library/RequestResponseHeaders.ts +++ /dev/null @@ -1,52 +0,0 @@ -export = { - - /** - * Request-Context header - */ - requestContextHeader: "request-context", - - /** - * Source instrumentation header that is added by an application while making http - * requests and retrieved by the other application when processing incoming requests. - */ - requestContextSourceKey: "appId", - - /** - * Target instrumentation header that is added to the response and retrieved by the - * calling application when processing incoming responses. - */ - requestContextTargetKey: "appId", - - /** - * Request-Id header - */ - requestIdHeader: "request-id", - - /** - * Legacy Header containing the id of the immediate caller - */ - parentIdHeader: "x-ms-request-id", - - /** - * Legacy Header containing the correlation id that kept the same for every telemetry item - * across transactions - */ - rootIdHeader: "x-ms-request-root-id", - - /** - * Correlation-Context header - * - * Not currently actively used, but the contents should be passed from incoming to outgoing requests - */ - correlationContextHeader: "correlation-context", - - /** - * W3C distributed tracing protocol header - */ - traceparentHeader: "traceparent", - - /** - * W3C distributed tracing protocol state header - */ - traceStateHeader: "tracestate" -} diff --git a/Library/Sender.ts b/Library/Sender.ts deleted file mode 100644 index 9f8435fab..000000000 --- a/Library/Sender.ts +++ /dev/null @@ -1,462 +0,0 @@ -import fs = require("fs"); -import http = require("http"); -import os = require("os"); -import path = require("path"); -import zlib = require("zlib"); - -import AuthorizationHandler = require("./AuthorizationHandler"); -import Config = require("./Config") -import Contracts = require("../Declarations/Contracts"); -import Constants = require("../Declarations/Constants"); -import AutoCollectHttpDependencies = require("../AutoCollection/HttpDependencies"); -import Statsbeat = require("../AutoCollection/Statsbeat"); -import FileSystemHelper = require("./FileSystemHelper"); -import Util = require("./Util"); -import { URL } from "url"; -import Logging = require("./Logging"); -import { FileAccessControl } from "./FileAccessControl"; -import { Envelope } from "../Declarations/Contracts"; - - -class Sender { - private static TAG = "Sender"; - // the amount of time the SDK will wait between resending cached data, this buffer is to avoid any throttling from the service side - public static WAIT_BETWEEN_RESEND = 60 * 1000; // 1 minute - public static MAX_BYTES_ON_DISK = 50 * 1024 * 1024; // 50 mb - public static MAX_CONNECTION_FAILURES_BEFORE_WARN = 5; - public static CLEANUP_TIMEOUT = 60 * 60 * 1000; // 1 hour - public static FILE_RETEMPTION_PERIOD = 7 * 24 * 60 * 60 * 1000; // 7 days - public static TEMPDIR_PREFIX: string = "appInsights-node"; - - private _config: Config; - private _statsbeat: Statsbeat; - private _onSuccess: (response: string) => void; - private _onError: (error: Error) => void; - private _getAuthorizationHandler: (config: Config) => AuthorizationHandler; - private _enableDiskRetryMode: boolean; - private _numConsecutiveFailures: number; - private _numConsecutiveRedirects: number; - private _resendTimer: NodeJS.Timer | null; - private _fileCleanupTimer: NodeJS.Timer; - private _redirectedHost: string = null; - private _tempDir: string; - protected _resendInterval: number; - protected _maxBytesOnDisk: number; - - constructor(config: Config, getAuthorizationHandler?: (config: Config) => AuthorizationHandler, onSuccess?: (response: string) => void, onError?: (error: Error) => void, statsbeat?: Statsbeat) { - this._config = config; - this._onSuccess = onSuccess; - this._onError = onError; - this._statsbeat = statsbeat; - this._enableDiskRetryMode = false; - this._resendInterval = Sender.WAIT_BETWEEN_RESEND; - this._maxBytesOnDisk = Sender.MAX_BYTES_ON_DISK; - this._numConsecutiveFailures = 0; - this._numConsecutiveRedirects = 0; - this._resendTimer = null; - this._getAuthorizationHandler = getAuthorizationHandler; - this._fileCleanupTimer = null; - // tmpdir is /tmp for *nix and USERDIR/AppData/Local/Temp for Windows - this._tempDir = path.join(os.tmpdir(), Sender.TEMPDIR_PREFIX + this._config.instrumentationKey); - } - - /** - * Enable or disable offline mode - */ - public setDiskRetryMode(value: boolean, resendInterval?: number, maxBytesOnDisk?: number) { - if (value) { - FileAccessControl.checkFileProtection(); // Only check file protection when disk retry is enabled - } - this._enableDiskRetryMode = FileAccessControl.OS_PROVIDES_FILE_PROTECTION && value; - if (typeof resendInterval === 'number' && resendInterval >= 0) { - this._resendInterval = Math.floor(resendInterval); - } - if (typeof maxBytesOnDisk === 'number' && maxBytesOnDisk >= 0) { - this._maxBytesOnDisk = Math.floor(maxBytesOnDisk); - } - - if (value && !FileAccessControl.OS_PROVIDES_FILE_PROTECTION) { - this._enableDiskRetryMode = false; - Logging.warn(Sender.TAG, "Ignoring request to enable disk retry mode. Sufficient file protection capabilities were not detected.") - } - if (this._enableDiskRetryMode) { - if (this._statsbeat) { - this._statsbeat.addFeature(Constants.StatsbeatFeature.DISK_RETRY); - } - // Starts file cleanup task - if (!this._fileCleanupTimer) { - this._fileCleanupTimer = setTimeout(() => { this._fileCleanupTask(); }, Sender.CLEANUP_TIMEOUT); - this._fileCleanupTimer.unref(); - } - } - else { - if (this._statsbeat) { - this._statsbeat.removeFeature(Constants.StatsbeatFeature.DISK_RETRY); - } - if (this._fileCleanupTimer) { - clearTimeout(this._fileCleanupTimer); - } - } - } - - public async send(envelopes: Contracts.EnvelopeTelemetry[], callback?: (v: string) => void) { - if (envelopes) { - var endpointUrl = this._redirectedHost || this._config.endpointUrl; - - var endpointHost = new URL(endpointUrl).hostname; - - // todo: investigate specifying an agent here: https://nodejs.org/api/http.html#http_class_http_agent - var options = { - method: "POST", - withCredentials: false, - headers: <{ [key: string]: string }>{ - "Content-Type": "application/x-json-stream" - } - }; - - let authHandler = this._getAuthorizationHandler ? this._getAuthorizationHandler(this._config) : null; - if (authHandler) { - if (this._statsbeat) { - this._statsbeat.addFeature(Constants.StatsbeatFeature.AAD_HANDLING); - } - try { - // Add bearer token - await authHandler.addAuthorizationHeader(options); - } - catch (authError) { - let errorMsg = "Failed to get AAD bearer token for the Application."; - if (this._enableDiskRetryMode) { - errorMsg += "This batch of telemetry items will be retried. "; - this._storeToDisk(envelopes); - } - errorMsg += "Error:" + authError.toString(); - Logging.warn(Sender.TAG, errorMsg); - - if (typeof callback === "function") { - callback(errorMsg); - } - return; // If AAD auth fails do not send to Breeze - } - } - - let batch: string = ""; - envelopes.forEach(envelope => { - var payload: string = Util.stringify(envelope); - if (typeof payload !== "string") { - return; - } - batch += payload + "\n"; - }); - // Remove last \n - if (batch.length > 0) { - batch = batch.substring(0, batch.length - 1); - } - - let payload: Buffer = Buffer.from ? Buffer.from(batch) : new Buffer(batch); - - zlib.gzip(payload, (err, buffer) => { - var dataToSend = buffer; - if (err) { - Logging.warn(Sender.TAG, err); - dataToSend = payload; // something went wrong so send without gzip - options.headers["Content-Length"] = payload.length.toString(); - } else { - options.headers["Content-Encoding"] = "gzip"; - options.headers["Content-Length"] = buffer.length.toString(); - } - - Logging.info(Sender.TAG, options); - - // Ensure this request is not captured by auto-collection. - (options)[AutoCollectHttpDependencies.disableCollectionRequestOption] = true; - - let startTime = +new Date(); - - var requestCallback = (res: http.ClientResponse) => { - res.setEncoding("utf-8"); - - //returns empty if the data is accepted - var responseString = ""; - res.on("data", (data: string) => { - responseString += data; - }); - - res.on("end", () => { - let endTime = +new Date(); - let duration = endTime - startTime; - this._numConsecutiveFailures = 0; - if (this._enableDiskRetryMode) { - // try to send any cached events if the user is back online - if (res.statusCode === 200) { - if (!this._resendTimer) { - this._resendTimer = setTimeout(() => { - this._resendTimer = null; - this._sendFirstFileOnDisk() - }, this._resendInterval); - this._resendTimer.unref(); - } - } else if (this._isRetriable(res.statusCode)) { - try { - if (this._statsbeat) { - this._statsbeat.countRetry(Constants.StatsbeatNetworkCategory.Breeze, endpointHost); - if (res.statusCode === 429) { - this._statsbeat.countThrottle(Constants.StatsbeatNetworkCategory.Breeze, endpointHost); - } - } - const breezeResponse = JSON.parse(responseString) as Contracts.BreezeResponse; - let filteredEnvelopes: Contracts.EnvelopeTelemetry[] = []; - if (breezeResponse.errors) { - breezeResponse.errors.forEach(error => { - if (this._isRetriable(error.statusCode)) { - filteredEnvelopes.push(envelopes[error.index]); - } - }); - if (filteredEnvelopes.length > 0) { - this._storeToDisk(filteredEnvelopes); - } - } - - } - catch (ex) { - this._storeToDisk(envelopes); // Retriable status code with not valid Breeze response - } - } - } - // Redirect handling - if (res.statusCode === 307 || // Temporary Redirect - res.statusCode === 308) { // Permanent Redirect - this._numConsecutiveRedirects++; - // To prevent circular redirects - if (this._numConsecutiveRedirects < 10) { - // Try to get redirect header - const locationHeader = res.headers["location"] ? res.headers["location"].toString() : null; - if (locationHeader) { - this._redirectedHost = locationHeader; - // Send to redirect endpoint as HTTPs library doesn't handle redirect automatically - this.send(envelopes, callback); - } - } - else { - if (this._statsbeat) { - this._statsbeat.countException(Constants.StatsbeatNetworkCategory.Breeze, endpointHost); - } - if (typeof callback === "function") { - callback("Error sending telemetry because of circular redirects."); - } - } - - } - else { - if (this._statsbeat) { - this._statsbeat.countRequest(Constants.StatsbeatNetworkCategory.Breeze, endpointHost, duration, res.statusCode === 200); - } - this._numConsecutiveRedirects = 0; - if (typeof callback === "function") { - callback(responseString); - } - Logging.info(Sender.TAG, responseString); - if (typeof this._onSuccess === "function") { - this._onSuccess(responseString); - } - } - }); - }; - - var req = Util.makeRequest(this._config, endpointUrl, options, requestCallback); - - req.on("error", (error: Error) => { - // todo: handle error codes better (group to recoverable/non-recoverable and persist) - this._numConsecutiveFailures++; - if (this._statsbeat) { - this._statsbeat.countException(Constants.StatsbeatNetworkCategory.Breeze, endpointHost); - } - - // Only use warn level if retries are disabled or we've had some number of consecutive failures sending data - // This is because warn level is printed in the console by default, and we don't want to be noisy for transient and self-recovering errors - // Continue informing on each failure if verbose logging is being used - if (!this._enableDiskRetryMode || this._numConsecutiveFailures > 0 && this._numConsecutiveFailures % Sender.MAX_CONNECTION_FAILURES_BEFORE_WARN === 0) { - let notice = "Ingestion endpoint could not be reached. This batch of telemetry items has been lost. Use Disk Retry Caching to enable resending of failed telemetry. Error:"; - if (this._enableDiskRetryMode) { - notice = `Ingestion endpoint could not be reached ${this._numConsecutiveFailures} consecutive times. There may be resulting telemetry loss. Most recent error:`; - } - Logging.warn(Sender.TAG, notice, Util.dumpObj(error)); - } else { - let notice = "Transient failure to reach ingestion endpoint. This batch of telemetry items will be retried. Error:"; - Logging.info(Sender.TAG, notice, Util.dumpObj(error)); - } - this._onErrorHelper(error); - - if (typeof callback === "function") { - if (error) { - callback(Util.dumpObj(error)); - } - else { - callback("Error sending telemetry"); - } - } - - if (this._enableDiskRetryMode) { - this._storeToDisk(envelopes); - } - }); - - req.write(dataToSend); - req.end(); - }); - } - } - - public saveOnCrash(envelopes: Contracts.EnvelopeTelemetry[]) { - if (this._enableDiskRetryMode) { - this._storeToDiskSync(Util.stringify(envelopes)); - } - } - - private _isRetriable(statusCode: number) { - return ( - statusCode === 206 || // Retriable - statusCode === 401 || // Unauthorized - statusCode === 403 || // Forbidden - statusCode === 408 || // Timeout - statusCode === 429 || // Throttle - statusCode === 439 || // Quota - statusCode === 500 || // Server Error - statusCode === 503 // Server Unavilable - ); - } - - /** - * Stores the payload as a json file on disk in the temp directory - */ - private async _storeToDisk(envelopes: Contracts.EnvelopeTelemetry[]): Promise { - try { - Logging.info(Sender.TAG, "Checking existence of data storage directory: " + this._tempDir); - await FileSystemHelper.confirmDirExists(this._tempDir); - } - catch (ex) { - Logging.warn(Sender.TAG, "Failed to create folder to put telemetry: " + (ex && ex.message)); - this._onErrorHelper(ex); - return; - } - try { - await FileAccessControl.applyACLRules(this._tempDir); - } - catch (ex) { - Logging.warn(Sender.TAG, "Failed to apply file access control to folder: " + (ex && ex.message)); - this._onErrorHelper(ex); - return; - } - try { - let size = await FileSystemHelper.getShallowDirectorySize(this._tempDir); - if (size > this._maxBytesOnDisk) { - Logging.warn(Sender.TAG, "Not saving data due to max size limit being met. Directory size in bytes is: " + size); - return; - } - //create file - file name for now is the timestamp, a better approach would be a UUID but that - //would require an external dependency - var fileName = new Date().getTime() + ".ai.json"; - var fileFullPath = path.join(this._tempDir, fileName); - - // Mode 600 is w/r for creator and no read access for others (only applies on *nix) - // For Windows, ACL rules are applied to the entire directory (see logic in _confirmDirExists and _applyACLRules) - Logging.info(Sender.TAG, "saving data to disk at: " + fileFullPath); - FileSystemHelper.writeFileAsync(fileFullPath, Util.stringify(envelopes), { mode: 0o600 }); - } - catch (ex) { - Logging.warn(Sender.TAG, "Failed to persist telemetry to disk: " + (ex && ex.message)); - this._onErrorHelper(ex); - return; - } - } - - /** - * Stores the payload as a json file on disk using sync file operations - * this is used when storing data before crashes - */ - private _storeToDiskSync(payload: any) { - try { - Logging.info(Sender.TAG, "Checking existence of data storage directory: " + this._tempDir); - if (!fs.existsSync(this._tempDir)) { - fs.mkdirSync(this._tempDir); - } - - // Make sure permissions are valid - FileAccessControl.applyACLRulesSync(this._tempDir); - - let dirSize = FileSystemHelper.getShallowDirectorySizeSync(this._tempDir); - if (dirSize > this._maxBytesOnDisk) { - Logging.info(Sender.TAG, "Not saving data due to max size limit being met. Directory size in bytes is: " + dirSize); - return; - } - - //create file - file name for now is the timestamp, a better approach would be a UUID but that - //would require an external dependency - var fileName = new Date().getTime() + ".ai.json"; - var fileFullPath = path.join(this._tempDir, fileName); - - // Mode 600 is w/r for creator and no access for anyone else (only applies on *nix) - Logging.info(Sender.TAG, "saving data before crash to disk at: " + fileFullPath); - fs.writeFileSync(fileFullPath, payload, { mode: 0o600 }); - - } catch (error) { - Logging.warn(Sender.TAG, "Error while saving data to disk: " + (error && error.message)); - this._onErrorHelper(error); - } - } - - /** - * Check for temp telemetry files - * reads the first file if exist, deletes it and tries to send its load - */ - private async _sendFirstFileOnDisk(): Promise { - try { - let files = await FileSystemHelper.readdirAsync(this._tempDir); - files = files.filter(f => path.basename(f).indexOf(".ai.json") > -1); - if (files.length > 0) { - var firstFile = files[0]; - var filePath = path.join(this._tempDir, firstFile); - let buffer = await FileSystemHelper.readFileAsync(filePath); - // delete the file first to prevent double sending - await FileSystemHelper.unlinkAsync(filePath); - let envelopes: Contracts.EnvelopeTelemetry[] = JSON.parse(buffer.toString()); - await this.send(envelopes); - } - } - catch (err) { - this._onErrorHelper(err); - } - } - - private _onErrorHelper(error: Error): void { - if (typeof this._onError === "function") { - this._onError(error); - } - } - - private async _fileCleanupTask(): Promise { - try { - let files = await FileSystemHelper.readdirAsync(this._tempDir); - files = files.filter(f => path.basename(f).indexOf(".ai.json") > -1); - if (files.length > 0) { - for (let i = 0; i < files.length; i++) { - // Check expiration - let fileCreationDate: Date = new Date(parseInt(files[i].split(".ai.json")[0])); - let expired = new Date(+(new Date()) - Sender.FILE_RETEMPTION_PERIOD) > fileCreationDate; - if (expired) { - var filePath = path.join(this._tempDir, files[i]); - await FileSystemHelper.unlinkAsync(filePath).catch((err) => { - this._onErrorHelper(err); - }); - } - } - } - } - catch (err) { - if (err.code != "ENOENT") { - this._onErrorHelper(err); - } - } - } -} - -export = Sender; \ No newline at end of file diff --git a/Library/TelemetryClient.ts b/Library/TelemetryClient.ts deleted file mode 100644 index 292722d73..000000000 --- a/Library/TelemetryClient.ts +++ /dev/null @@ -1,273 +0,0 @@ -import url = require("url"); -import os = require("os"); -import azureCore = require("@azure/core-http"); - -import Config = require("./Config"); -import AuthorizationHandler = require("./AuthorizationHandler"); -import Context = require("./Context"); -import Contracts = require("../Declarations/Contracts"); -import Channel = require("./Channel"); -import TelemetryProcessors = require("../TelemetryProcessors"); -import { CorrelationContextManager } from "../AutoCollection/CorrelationContextManager"; -import Statsbeat = require("../AutoCollection/Statsbeat"); -import Sender = require("./Sender"); -import Util = require("./Util"); -import Logging = require("./Logging"); -import FlushOptions = require("./FlushOptions"); -import EnvelopeFactory = require("./EnvelopeFactory"); -import QuickPulseStateManager = require("./QuickPulseStateManager"); -import { Tags } from "../Declarations/Contracts"; - -/** - * Application Insights telemetry client provides interface to track telemetry items, register telemetry initializers and - * and manually trigger immediate sending (flushing) - */ -class TelemetryClient { - private static TAG = "TelemetryClient"; - private _telemetryProcessors: { (envelope: Contracts.EnvelopeTelemetry, contextObjects: { [name: string]: any; }): boolean; }[] = []; - private _enableAzureProperties: boolean = false; - private _statsbeat: Statsbeat; - - public config: Config; - public context: Context; - public commonProperties: { [key: string]: string; }; - public channel: Channel; - public quickPulseClient: QuickPulseStateManager; - public authorizationHandler: AuthorizationHandler; - - /** - * Constructs a new client of the client - * @param setupString the Connection String or Instrumentation Key to use (read from environment variable if not specified) - */ - constructor(setupString?: string) { - var config = new Config(setupString); - this.config = config; - this.context = new Context(); - this.commonProperties = {}; - this.authorizationHandler = null; - if (!this.config.disableStatsbeat) { - this._statsbeat = new Statsbeat(this.config, this.context); - this._statsbeat.enable(true); - } - var sender = new Sender(this.config, this.getAuthorizationHandler, null, null, this._statsbeat); - this.channel = new Channel(() => config.disableAppInsights, () => config.maxBatchSize, () => config.maxBatchIntervalMs, sender); - } - - /** - * Log information about availability of an application - * @param telemetry Object encapsulating tracking options - */ - public trackAvailability(telemetry: Contracts.AvailabilityTelemetry): void { - this.track(telemetry, Contracts.TelemetryType.Availability); - } - - /** - * Log a page view - * @param telemetry Object encapsulating tracking options - */ - public trackPageView(telemetry: Contracts.PageViewTelemetry): void { - this.track(telemetry, Contracts.TelemetryType.PageView); - } - - /** - * Log a trace message - * @param telemetry Object encapsulating tracking options - */ - public trackTrace(telemetry: Contracts.TraceTelemetry): void { - this.track(telemetry, Contracts.TelemetryType.Trace); - } - - /** - * Log a numeric value that is not associated with a specific event. Typically used to send regular reports of performance indicators. - * To send a single measurement, use just the first two parameters. If you take measurements very frequently, you can reduce the - * telemetry bandwidth by aggregating multiple measurements and sending the resulting average at intervals. - * @param telemetry Object encapsulating tracking options - */ - public trackMetric(telemetry: Contracts.MetricTelemetry): void { - this.track(telemetry, Contracts.TelemetryType.Metric); - } - - /** - * Log an exception - * @param telemetry Object encapsulating tracking options - */ - public trackException(telemetry: Contracts.ExceptionTelemetry): void { - if (telemetry && telemetry.exception && !Util.isError(telemetry.exception)) { - telemetry.exception = new Error(telemetry.exception.toString()); - } - this.track(telemetry, Contracts.TelemetryType.Exception); - } - - /** - * Log a user action or other occurrence. - * @param telemetry Object encapsulating tracking options - */ - public trackEvent(telemetry: Contracts.EventTelemetry): void { - this.track(telemetry, Contracts.TelemetryType.Event); - } - - /** - * Log a request. Note that the default client will attempt to collect HTTP requests automatically so only use this for requests - * that aren't automatically captured or if you've disabled automatic request collection. - * - * @param telemetry Object encapsulating tracking options - */ - public trackRequest(telemetry: Contracts.RequestTelemetry & Contracts.Identified): void { - this.track(telemetry, Contracts.TelemetryType.Request); - } - - /** - * Log a dependency. Note that the default client will attempt to collect dependencies automatically so only use this for dependencies - * that aren't automatically captured or if you've disabled automatic dependency collection. - * - * @param telemetry Object encapsulating tracking option - * */ - public trackDependency(telemetry: Contracts.DependencyTelemetry & Contracts.Identified) { - - if (telemetry && !telemetry.target && telemetry.data) { - // url.parse().host returns null for non-urls, - // making this essentially a no-op in those cases - // If this logic is moved, update jsdoc in DependencyTelemetry.target - // url.parse() is deprecated, update to use WHATWG URL API instead - try { - telemetry.target = new url.URL(telemetry.data).host; - } catch (error) { - // set target as null to be compliant with previous behavior - telemetry.target = null; - Logging.warn(TelemetryClient.TAG, "The URL object is failed to create.", error); - } - } - this.track(telemetry, Contracts.TelemetryType.Dependency); - } - - /** - * Immediately send all queued telemetry. - * @param options Flush options, including indicator whether app is crashing and callback - */ - public flush(options?: FlushOptions) { - this.channel.triggerSend( - options ? !!options.isAppCrashing : false, - options ? options.callback : undefined); - } - - /** - * Generic track method for all telemetry types - * @param data the telemetry to send - * @param telemetryType specify the type of telemetry you are tracking from the list of Contracts.DataTypes - */ - public track(telemetry: Contracts.Telemetry, telemetryType: Contracts.TelemetryType) { - if (telemetry && Contracts.telemetryTypeToBaseType(telemetryType)) { - var envelope = EnvelopeFactory.createEnvelope(telemetry, telemetryType, this.commonProperties, this.context, this.config); - - // Set time on the envelope if it was set on the telemetry item - if (telemetry.time) { - envelope.time = telemetry.time.toISOString(); - } - if (this._enableAzureProperties) { - TelemetryProcessors.azureRoleEnvironmentTelemetryProcessor(envelope, this.context); - } - var accepted = this.runTelemetryProcessors(envelope, telemetry.contextObjects); - - // Ideally we would have a central place for "internal" telemetry processors and users can configure which ones are in use. - // This will do for now. Otherwise clearTelemetryProcessors() would be problematic. - accepted = accepted && TelemetryProcessors.samplingTelemetryProcessor(envelope, { correlationContext: CorrelationContextManager.getCurrentContext() }); - TelemetryProcessors.preAggregatedMetricsTelemetryProcessor(envelope, this.context); - if (accepted) { - TelemetryProcessors.performanceMetricsTelemetryProcessor(envelope, this.quickPulseClient); - this.channel.send(envelope); - } - } - else { - Logging.warn(TelemetryClient.TAG, "track() requires telemetry object and telemetryType to be specified.") - } - } - - /** - * Automatically populate telemetry properties like RoleName when running in Azure - * - * @param value if true properties will be populated - */ - public setAutoPopulateAzureProperties(value: boolean) { - this._enableAzureProperties = value; - } - - /** - * Get Authorization handler - */ - public getAuthorizationHandler(config: Config): AuthorizationHandler { - if (config && config.aadTokenCredential) { - if (!this.authorizationHandler) { - Logging.info(TelemetryClient.TAG, "Adding authorization handler"); - this.authorizationHandler = new AuthorizationHandler(config.aadTokenCredential) - } - return this.authorizationHandler; - } - return null; - } - - /** - * Adds telemetry processor to the collection. Telemetry processors will be called one by one - * before telemetry item is pushed for sending and in the order they were added. - * - * @param telemetryProcessor function, takes Envelope, and optional context object and returns boolean - */ - public addTelemetryProcessor(telemetryProcessor: (envelope: Contracts.EnvelopeTelemetry, contextObjects?: { [name: string]: any; }) => boolean) { - this._telemetryProcessors.push(telemetryProcessor); - } - - /* - * Removes all telemetry processors - */ - public clearTelemetryProcessors() { - this._telemetryProcessors = []; - } - - private runTelemetryProcessors(envelope: Contracts.EnvelopeTelemetry, contextObjects: { [name: string]: any; }): boolean { - var accepted = true; - var telemetryProcessorsCount = this._telemetryProcessors.length; - - if (telemetryProcessorsCount === 0) { - return accepted; - } - - contextObjects = contextObjects || {}; - contextObjects['correlationContext'] = CorrelationContextManager.getCurrentContext(); - - for (var i = 0; i < telemetryProcessorsCount; ++i) { - try { - var processor = this._telemetryProcessors[i]; - if (processor) { - if (processor.apply(null, [envelope, contextObjects]) === false) { - accepted = false; - break; - } - } - - } catch (error) { - accepted = true; - Logging.warn(TelemetryClient.TAG, "One of telemetry processors failed, telemetry item will be sent.", error, envelope); - } - } - - // Sanitize tags and properties after running telemetry processors - if (accepted) { - if (envelope && envelope.tags) { - envelope.tags = Util.validateStringMap(envelope.tags) as Tags & Tags[]; - } - if (envelope && envelope.data && envelope.data.baseData && envelope.data.baseData.properties) { - envelope.data.baseData.properties = Util.validateStringMap(envelope.data.baseData.properties); - } - } - - return accepted; - } - - /* - * Get Statsbeat instance - */ - public getStatsbeat() { - return this._statsbeat; - } -} - -export = TelemetryClient; \ No newline at end of file diff --git a/Library/Traceparent.ts b/Library/Traceparent.ts deleted file mode 100644 index 18535e8dc..000000000 --- a/Library/Traceparent.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { TraceFlags } from "@opentelemetry/api"; -import Util = require("./Util"); -import CorrelationIdManager = require("./CorrelationIdManager"); - -/** - * Helper class to manage parsing and validation of traceparent header. Also handles hierarchical - * back-compatibility headers generated from traceparent. W3C traceparent spec is documented at - * https://www.w3.org/TR/trace-context/#traceparent-field - */ -class Traceparent { - public static DEFAULT_TRACE_FLAG = "01"; - public static DEFAULT_VERSION = "00"; - - public legacyRootId: string; - public parentId: string; - public spanId: string; - public traceFlag: string = Traceparent.DEFAULT_TRACE_FLAG; - public traceId: string; - public version: string = Traceparent.DEFAULT_VERSION; - - constructor(traceparent?: string, parentId?: string) { - if (traceparent && typeof traceparent === "string") { // traceparent constructor - // If incoming request contains traceparent: parse it, set operation, parent and telemetry id accordingly. traceparent should be injected into outgoing requests. request-id should be injected in back-compat format |traceId.spanId. so that older SDKs could understand it. - if (traceparent.split(",").length > 1) { // If more than 1 traceparent is present, discard both - this.traceId = Util.w3cTraceId(); - this.spanId = Util.w3cTraceId().substr(0, 16); - } else { - const traceparentArr = traceparent.trim().split("-"); - const len = traceparentArr.length; - if (len >= 4) { // traceparent must contain at least 4 fields - this.version = traceparentArr[0]; - this.traceId = traceparentArr[1]; - this.spanId = traceparentArr[2]; - this.traceFlag = traceparentArr[3]; - } else { // Discard traceparent if a field is missing - this.traceId = Util.w3cTraceId(); - this.spanId = Util.w3cTraceId().substr(0, 16); - } - - // Version validation - if (!this.version.match(/^[0-9a-f]{2}$/g)) { - this.version = Traceparent.DEFAULT_VERSION; - this.traceId = Util.w3cTraceId(); - } - if (this.version === "00" && len !== 4) { // 0x00 (and perhaps future versions) require exactly 4 fields. This strict check will need to be updated on each spec release - this.traceId = Util.w3cTraceId(); - this.spanId = Util.w3cTraceId().substr(0, 16); - } - if (this.version === "ff") { // 0xff is forbidden, generate new traceparent - this.version = Traceparent.DEFAULT_VERSION; - this.traceId = Util.w3cTraceId(); - this.spanId = Util.w3cTraceId().substr(0, 16); - } - if (!this.version.match(/^0[0-9a-f]$/g)) { - this.version = Traceparent.DEFAULT_VERSION; - } - - // TraceFlag validation - if (!this.traceFlag.match(/^[0-9a-f]{2}$/g)) { - this.traceFlag = Traceparent.DEFAULT_TRACE_FLAG; - this.traceId = Util.w3cTraceId(); - } - - // Validate TraceId, regenerate new traceid if invalid - if (!Traceparent.isValidTraceId(this.traceId)) { - this.traceId = Util.w3cTraceId(); - } - - // Validate Span Id, discard entire traceparent if invalid - if (!Traceparent.isValidSpanId(this.spanId)) { - this.spanId = Util.w3cTraceId().substr(0, 16); - this.traceId = Util.w3cTraceId(); - } - - // Save backCompat parentId - this.parentId = this.getBackCompatRequestId(); - } - } else if (parentId) { // backcompat constructor - // If incoming request contains only request-id, new traceid and spanid should be started, request-id value should be used as a parent. Root part of request-id should be stored in custom dimension on the request telemetry if root part is different from traceid. On the outgoing request side, request-id should be emitted in the |traceId.spanId. format. - this.parentId = parentId.slice(); // copy - let operationId = CorrelationIdManager.getRootId(parentId); - if (!Traceparent.isValidTraceId(operationId)) { - this.legacyRootId = operationId; - operationId = Util.w3cTraceId(); - } - if (parentId.indexOf("|") !== -1) { - parentId = parentId.substring(1 + parentId.substring(0, parentId.length - 1).lastIndexOf("."), parentId.length - 1); - } - this.traceId = operationId; - this.spanId = parentId; - } else { - // Fallback default constructor - // if request does not contain any correlation headers, see case p2 - this.traceId = Util.w3cTraceId(); - this.spanId = Util.w3cTraceId().substr(0, 16); - } - - } - - public static isValidTraceId(id: string): boolean { - return id.match(/^[0-9a-f]{32}$/) && id !== "00000000000000000000000000000000"; - } - - public static isValidSpanId(id: string): boolean { - return id.match(/^[0-9a-f]{16}$/) && id !== "0000000000000000"; - } - - public static formatOpenTelemetryTraceFlags(traceFlags : number){ - let formattedFlags = ("0" + traceFlags.toString(16)); - return formattedFlags.substring(formattedFlags.length -2); - } - - public getBackCompatRequestId(): string { - return `|${this.traceId}.${this.spanId}.`; - } - - public toString(): string { - return `${this.version}-${this.traceId}-${this.spanId}-${this.traceFlag}`; - } - - public updateSpanId(): void { - this.spanId = Util.w3cTraceId().substr(0, 16); - } -} - -export = Traceparent; diff --git a/Library/Tracestate.ts b/Library/Tracestate.ts deleted file mode 100644 index 368a3d4cc..000000000 --- a/Library/Tracestate.ts +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Helper class to manage parsing and strict-validation of tracestate header. W3C tracestate spec - * is documented at https://www.w3.org/TR/trace-context/#header-value - * @class Tracestate - */ -class Tracestate { - public static strict = true; - - public fieldmap: string[] = []; - - // if true, performs strict tracestate header checking, else just passes it along - constructor(id?: string) { - if (!id) { - return; - } - this.fieldmap = this.parseHeader(id); - } - - public toString(): string { - const fieldarr = this.fieldmap; - - if (!fieldarr || fieldarr.length == 0) { - return null; - } - - return fieldarr.join(", "); - } - - private static validateKeyChars(key: string): boolean { - const keyParts = key.split("@"); - if (keyParts.length == 2) { - // Parse for tenant@vendor format - const tenant = keyParts[0].trim(); - const vendor = keyParts[1].trim(); - const tenantValid = Boolean(tenant.match(/^[\ ]?[a-z0-9\*\-\_/]{1,241}$/)); - const vendorValid = Boolean(vendor.match(/^[\ ]?[a-z0-9\*\-\_/]{1,14}$/)); - return tenantValid && vendorValid; - } else if (keyParts.length == 1) { - // Parse for standard key format - return Boolean(key.match(/^[\ ]?[a-z0-9\*\-\_/]{1,256}$/)); - } - - return false; - } - - private parseHeader(id: string): string[] { - const res: string[] = []; - const keydeduper: {[key: string]: boolean} = {}; - const parts = id.split(","); - if (parts.length > 32) return null; - for (let rawPart of parts) { - const part = rawPart.trim(); // trim out whitespace - if (part.length === 0) { - continue; // Discard empty pairs, but keep the rest of this tracestate - } - - const pair = part.split("="); - // pair should contain exactly one "=" - if (pair.length !== 2) { - return null; // invalid pair: discard entire tracestate - } - - // Validate length and charset of this key - if (!Tracestate.validateKeyChars(pair[0])) { - return null; - } - - // Assert uniqueness of this key - if (keydeduper[pair[0]]) { - return null; // duplicate key: discard entire tracestate - } else { - keydeduper[pair[0]] = true - } - - // All checks passed -- add this part - res.push(part); - } - - return res; - } -} - -export = Tracestate; diff --git a/Library/Util.ts b/Library/Util.ts deleted file mode 100644 index 7fc99851e..000000000 --- a/Library/Util.ts +++ /dev/null @@ -1,446 +0,0 @@ -import http = require("http"); -import https = require("https"); -import url = require("url"); -import constants = require("constants"); - -import Logging = require("./Logging"); -import Config = require("./Config"); -import TelemetryClient = require("../Library/TelemetryClient"); -import RequestResponseHeaders = require("./RequestResponseHeaders"); -import { HttpRequest } from "../Library/Functions"; -import { JsonConfig } from "./JsonConfig"; - - -class Util { - private static _useKeepAlive = !JsonConfig.getInstance().noHttpAgentKeepAlive; - private static _listenerAttached = false; - - public static MAX_PROPERTY_LENGTH = 8192; - public static keepAliveAgent: http.Agent = new https.Agent({ - keepAlive: true, - maxSockets: 25, - secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | - constants.SSL_OP_NO_TLSv1 | constants.SSL_OP_NO_TLSv1_1 - }); - public static tlsRestrictedAgent: http.Agent = new https.Agent({ - secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | - constants.SSL_OP_NO_TLSv1 | constants.SSL_OP_NO_TLSv1_1 - }); - public static isNodeExit = false; - - public constructor() { - Util._addCloseHandler(); - } - - /** - * helper method to access userId and sessionId cookie - */ - public static getCookie(name: string, cookie: string) { - var value = ""; - if (name && name.length && typeof cookie === "string") { - var cookieName = name + "="; - var cookies = cookie.split(";"); - for (var i = 0; i < cookies.length; i++) { - var cookie = cookies[i]; - cookie = Util.trim(cookie); - if (cookie && cookie.indexOf(cookieName) === 0) { - value = cookie.substring(cookieName.length, cookies[i].length); - break; - } - } - } - - return value; - } - - /** - * helper method to trim strings (IE8 does not implement String.prototype.trim) - */ - public static trim(str: string): string { - if (typeof str === "string") { - return str.replace(/^\s+|\s+$/g, ""); - } else { - return ""; - } - } - - /** - * Convert an array of int32 to Base64 (no '==' at the end). - * MSB first. - */ - public static int32ArrayToBase64(array: number[]) { - let toChar = (v: number, i: number) => - String.fromCharCode((v >> i) & 0xFF); - let int32AsString = (v: number) => - toChar(v, 24) + toChar(v, 16) + toChar(v, 8) + toChar(v, 0); - let x = array.map(int32AsString).join(""); - const b = Buffer.from ? Buffer.from(x, "binary") : new Buffer(x, "binary"); - let s = b.toString("base64"); - return s.substr(0, s.indexOf("=")); - } - - /** - * generate a random 32bit number (-0x80000000..0x7FFFFFFF). - */ - public static random32() { - return (0x100000000 * Math.random()) | 0; - } - - /** - * generate a random 32bit number (0x00000000..0xFFFFFFFF). - */ - public static randomu32() { - return Util.random32() + 0x80000000; - } - - /** - * generate W3C-compatible trace id - * https://github.com/w3c/distributed-tracing/blob/master/trace_context/HTTP_HEADER_FORMAT.md#trace-id - */ - public static w3cTraceId() { - var hexValues = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"]; - - // rfc4122 version 4 UUID without dashes and with lowercase letters - var oct = "", tmp; - for (var a = 0; a < 4; a++) { - tmp = Util.random32(); - oct += - hexValues[tmp & 0xF] + - hexValues[tmp >> 4 & 0xF] + - hexValues[tmp >> 8 & 0xF] + - hexValues[tmp >> 12 & 0xF] + - hexValues[tmp >> 16 & 0xF] + - hexValues[tmp >> 20 & 0xF] + - hexValues[tmp >> 24 & 0xF] + - hexValues[tmp >> 28 & 0xF]; - } - - // "Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively" - var clockSequenceHi = hexValues[8 + (Math.random() * 4) | 0]; - return oct.substr(0, 8) + oct.substr(9, 4) + "4" + oct.substr(13, 3) + clockSequenceHi + oct.substr(16, 3) + oct.substr(19, 12); - } - - public static w3cSpanId() { - return Util.w3cTraceId().substring(16); - } - - public static isValidW3CId(id: string): boolean { - return id.length === 32 && id !== "00000000000000000000000000000000"; - } - - /** - * Check if an object is of type Array - */ - public static isArray(obj: any): boolean { - return Object.prototype.toString.call(obj) === "[object Array]"; - } - - /** - * Check if an object is of type Error - */ - public static isError(obj: any): boolean { - return obj instanceof Error; - } - - public static isPrimitive(input: any): boolean { - const propType = typeof input; - return propType === "string" || propType === "number" || propType === "boolean"; - } - - /** - * Check if an object is of type Date - */ - public static isDate(obj: any): boolean { - return Object.prototype.toString.call(obj) === "[object Date]"; - } - - /** - * Convert ms to c# time span format - */ - public static msToTimeSpan(totalms: number): string { - if (isNaN(totalms) || totalms < 0) { - totalms = 0; - } - - var sec = ((totalms / 1000) % 60).toFixed(7).replace(/0{0,4}$/, ""); - var min = "" + Math.floor(totalms / (1000 * 60)) % 60; - var hour = "" + Math.floor(totalms / (1000 * 60 * 60)) % 24; - var days = Math.floor(totalms / (1000 * 60 * 60 * 24)); - - sec = sec.indexOf(".") < 2 ? "0" + sec : sec; - min = min.length < 2 ? "0" + min : min; - hour = hour.length < 2 ? "0" + hour : hour; - var daysText = days > 0 ? days + "." : ""; - - return daysText + hour + ":" + min + ":" + sec; - } - - /** - * Using JSON.stringify, by default Errors do not serialize to something useful: - * Simplify a generic Node Error into a simpler map for customDimensions - * Custom errors can still implement toJSON to override this functionality - */ - protected static extractError(err: Error): { message: string, code: string } { - // Error is often subclassed so may have code OR id properties: - // https://nodejs.org/api/errors.html#errors_error_code - const looseError = err as any; - return { - message: err.message, - code: looseError.code || looseError.id || "", - } - } - - /** - * Manually call toJSON if available to pre-convert the value. - * If a primitive is returned, then the consumer of this function can skip JSON.stringify. - * This avoids double escaping of quotes for Date objects, for example. - */ - protected static extractObject(origProperty: any): any { - if (origProperty instanceof Error) { - return Util.extractError(origProperty); - } - if (typeof origProperty.toJSON === "function") { - return origProperty.toJSON(); - } - return origProperty; - } - - /** - * Validate that an object is of type { [key: string]: string } - */ - public static validateStringMap(obj: any): { [key: string]: string } { - if (typeof obj !== "object") { - Logging.info("Invalid properties dropped from payload"); - return; - } - const map: { [key: string]: string } = {}; - for (let field in obj) { - let property: string = ''; - const origProperty: any = obj[field]; - const propType = typeof origProperty; - - if (Util.isPrimitive(origProperty)) { - property = origProperty.toString(); - } else if (origProperty === null || propType === "undefined") { - property = ""; - } else if (propType === "function") { - Logging.info("key: " + field + " was function; will not serialize"); - continue; - } else { - const stringTarget = Util.isArray(origProperty) ? origProperty : Util.extractObject(origProperty); - try { - if (Util.isPrimitive(stringTarget)) { - property = stringTarget; - } else { - property = JSON.stringify(stringTarget); - } - } catch (e) { - property = origProperty.constructor.name.toString() + " (Error: " + e.message + ")"; - Logging.info("key: " + field + ", could not be serialized"); - } - } - - map[field] = property.substring(0, Util.MAX_PROPERTY_LENGTH); - } - return map; - } - - - /** - * Checks if a request url is not on a excluded domain list - * and if it is safe to add correlation headers - */ - public static canIncludeCorrelationHeader(client: TelemetryClient, requestUrl: string) { - let excludedDomains = client && client.config && client.config.correlationHeaderExcludedDomains; - if (!excludedDomains || excludedDomains.length == 0 || !requestUrl) { - return true; - } - - for (let i = 0; i < excludedDomains.length; i++) { - let regex = new RegExp(excludedDomains[i].replace(/\./g, "\.").replace(/\*/g, ".*")); - try { - if (regex.test(new url.URL(requestUrl).hostname)) { - return false; - } - } - catch (ex) { } - } - - return true; - } - - public static getCorrelationContextTarget(response: http.ClientResponse | http.ServerRequest | HttpRequest, key: string) { - const contextHeaders = response.headers && response.headers[RequestResponseHeaders.requestContextHeader]; - if (contextHeaders) { - const keyValues = (contextHeaders).split(","); - for (let i = 0; i < keyValues.length; ++i) { - const keyValue = keyValues[i].split("="); - if (keyValue.length == 2 && keyValue[0] == key) { - return keyValue[1]; - } - } - } - } - - - /** - * Generate request - * - * Proxify the request creation to handle proxy http - * - * @param {string} requestUrl url endpoint - * @param {Object} requestOptions Request option - * @param {Function} requestCallback callback on request - * @param {boolean} useProxy Use proxy URL from config - * @param {boolean} useAgent Set Http Agent in request - * @returns {http.ClientRequest} request object - */ - public static makeRequest( - config: Config, - requestUrl: string, - requestOptions: http.RequestOptions | https.RequestOptions, - requestCallback: (res: http.IncomingMessage) => void, - useProxy = true, - useAgent = true): http.ClientRequest { - - if (requestUrl && requestUrl.indexOf('//') === 0) { - requestUrl = 'https:' + requestUrl; - } - - var requestUrlParsed = new url.URL(requestUrl); - var options = { - ...requestOptions, - host: requestUrlParsed.hostname, - port: requestUrlParsed.port, - path: requestUrlParsed.pathname, - }; - - var proxyUrl: string = undefined; - if (useProxy) { - if (requestUrlParsed.protocol === 'https:') { - proxyUrl = config.proxyHttpsUrl || undefined; - } - if (requestUrlParsed.protocol === 'http:') { - proxyUrl = config.proxyHttpUrl || undefined; - } - if (proxyUrl) { - if (proxyUrl.indexOf('//') === 0) { - proxyUrl = 'http:' + proxyUrl; - } - try { - var proxyUrlParsed = new url.URL(proxyUrl); - // https is not supported at the moment - if (proxyUrlParsed.protocol === 'https:') { - Logging.info("Proxies that use HTTPS are not supported"); - proxyUrl = undefined; - } else { - options = { - ...options, - host: proxyUrlParsed.hostname, - port: proxyUrlParsed.port || "80", - path: requestUrl, - headers: { - ...options.headers, - Host: requestUrlParsed.hostname, - }, - }; - } - } - catch (err) { - Logging.warn("Wrong proxy URL provided"); - } - } - } - - var isHttps = requestUrlParsed.protocol === 'https:' && !proxyUrl; - if (useAgent) { - if (isHttps && config.httpsAgent !== undefined) { - options.agent = config.httpsAgent; - } else if (!isHttps && config.httpAgent !== undefined) { - options.agent = config.httpAgent; - } else if (isHttps) { - // HTTPS without a passed in agent. Use one that enforces our TLS rules - options.agent = Util._useKeepAlive ? Util.keepAliveAgent : Util.tlsRestrictedAgent; - } - } - if (isHttps) { - return https.request(options, requestCallback); - } else { - return http.request(options, requestCallback); - } - - }; - - /** - * Parse standard request-context header - */ - public static safeIncludeCorrelationHeader(client: TelemetryClient, request: http.ClientRequest | http.ServerResponse, correlationHeader: any) { - let header: string; // attempt to cast correlationHeader to string - if (typeof correlationHeader === "string") { - header = correlationHeader; - } else if (correlationHeader instanceof Array) { // string[] - header = correlationHeader.join(","); - } else if (correlationHeader && typeof (correlationHeader as any).toString === "function") { - // best effort attempt: requires well-defined toString - try { - header = (correlationHeader as any).toString(); - } catch (err) { - Logging.warn("Outgoing request-context header could not be read. Correlation of requests may be lost.", err, correlationHeader); - } - } - - if (header) { - Util.addCorrelationIdHeaderFromString(client, request, header); - } else { - request.setHeader( - RequestResponseHeaders.requestContextHeader, - `${RequestResponseHeaders.requestContextSourceKey}=${client.config.correlationId}`); - } - } - - /** - * Returns string representation of an object suitable for diagnostics logging. - */ - public static dumpObj(object: any): string { - const objectTypeDump: string = Object["prototype"].toString.call(object); - let propertyValueDump: string = ""; - if (objectTypeDump === "[object Error]") { - propertyValueDump = "{ stack: '" + object.stack + "', message: '" + object.message + "', name: '" + object.name + "'"; - } else { - propertyValueDump = JSON.stringify(object); - } - - return objectTypeDump + propertyValueDump; - } - - public static stringify(payload: any) { - try { - return JSON.stringify(payload); - } catch (error) { - Logging.warn("Failed to serialize payload", error, payload); - } - } - - private static addCorrelationIdHeaderFromString(client: TelemetryClient, response: http.ClientRequest | http.ServerResponse, correlationHeader: string) { - const components = correlationHeader.split(","); - const key = `${RequestResponseHeaders.requestContextSourceKey}=`; - const found = components.some(value => value.substring(0, key.length) === key); - - if (!found) { - response.setHeader( - RequestResponseHeaders.requestContextHeader, - `${correlationHeader},${RequestResponseHeaders.requestContextSourceKey}=${client.config.correlationId}`); - } - } - - private static _addCloseHandler() { - if (!Util._listenerAttached) { - process.on("exit", () => { - Util.isNodeExit = true; - Util._useKeepAlive = false; - }); - Util._listenerAttached = true; - } - } -} -export = Util; diff --git a/NOTICE b/NOTICE new file mode 100644 index 000000000..5280066e3 --- /dev/null +++ b/NOTICE @@ -0,0 +1,17 @@ +NOTICES AND INFORMATION +Do Not Translate or Localize + +This software incorporates material from third parties. Microsoft makes certain +open source code available at https://3rdpartysource.microsoft.com, or you may +send a check or money order for US $5.00, including the product name, the open +source component name, and version number, to: + +Source Code Compliance Team +Microsoft Corporation +One Microsoft Way +Redmond, WA 98052 +USA + +Notwithstanding any other terms, you may reverse engineer this software to the +extent required to debug changes to any libraries licensed under the GNU Lesser +General Public License. \ No newline at end of file diff --git a/PRIVACY b/PRIVACY new file mode 100644 index 000000000..69f930403 --- /dev/null +++ b/PRIVACY @@ -0,0 +1,3 @@ +# Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at https://go.microsoft.com/fwlink/?LinkID=824704. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. \ No newline at end of file diff --git a/README.md b/README.md index aed5aa051..3cf6feb3c 100644 --- a/README.md +++ b/README.md @@ -1,600 +1,277 @@ -# Application Insights for Node.js +# Application Insights for Node.js (Beta) -[![npm version](https://badge.fury.io/js/applicationinsights.svg)](http://badge.fury.io/js/applicationinsights) -[![Build Status](https://travis-ci.org/Microsoft/ApplicationInsights-node.js.svg?branch=master)](https://travis-ci.org/Microsoft/ApplicationInsights-node.js) -![Integration Tests CI](https://github.com/microsoft/ApplicationInsights-node.js/workflows/Integration%20Tests%20CI/badge.svg) -![Node.js CI](https://github.com/microsoft/ApplicationInsights-node.js/workflows/Node.js%20CI/badge.svg) -![Back Compatability CI](https://github.com/microsoft/ApplicationInsights-node.js/workflows/Back%20Compatability%20CI/badge.svg) - -[Azure Application Insights][] monitors your backend services and components after -you deploy them to help you [discover and rapidly diagnose performance and other -issues][]. Add this SDK to your Node.js services to include deep info about Node.js +Application Insights SDK monitors your backend services and components after +you deploy them to help you discover and rapidly diagnose performance and other +issues. Add this SDK to your Node.js services to include deep info about Node.js processes and their external dependencies such as database and cache services. You can use this SDK for your Node.js services hosted anywhere: your datacenter, -Azure VMs and Web Apps, and even other public clouds. +Azure VMs and Web Apps, and even other public clouds. This solution is based on OpenTelemetry, to learn more about OpenTelemetry concepts, see the [OpenTelemetry overview](opentelemetry-overview.md) or [OpenTelemetry FAQ](/azure/azure-monitor/faq#opentelemetry). + +> *Important:* The Azure Monitor OpenTelemetry-based Offerings for Node.js applications are currently in preview. +> See the [Supplemental Terms of Use for Microsoft Azure Previews](https://azure.microsoft.com/support/legal/preview-supplemental-terms/) for legal terms that apply to Azure features that are in beta, preview, or otherwise not yet released into general availability. [Azure Application Insights]: https://azure.microsoft.com/documentation/articles/app-insights-overview/ [discover and rapidly diagnose performance and other issues]: https://docs.microsoft.com/azure/application-insights/app-insights-detect-triage-diagnose -This library tracks the following out-of-the-box: -- Incoming and outgoing HTTP requests -- Important system metrics such as CPU usage -- Unhandled exceptions -- Events from many popular third-party libraries ([see Automatic third-party instrumentation](#automatic-third-party-instrumentation)) - -You can manually track more aspects of your app and system using the API described in the -[Track custom telemetry](#track-custom-telemetry) section. - -## Supported Node.JS versions - -| Platform Version | Supported | -|------------------|-------------------------------------------------| -| Node.JS `v15` | ✅ | -| Node.JS `v14` | ✅ | -| Node.JS `v12` | ✅ | -| Node.JS `v10` | ✅ | -| Node.JS `v8` | ✅ | - - -## Getting Started - -1. Create an Application Insights resource in Azure by following [these instructions][]. -2. Grab the _Connection String_ from the resource you created in - step 1. Later, you'll either add it to your app's environment variables or - use it directly in your scripts. -3. Add the Application Insights Node.js SDK to your app's dependencies and - package.json: - ```bash - npm install --save applicationinsights - ``` - > *Note:* If you're using TypeScript, please install @types/node package to prevent build issues, this npm package contains built-in typings. -4. As early as possible in your app's code, load the Application Insights - package: - ```javascript - let appInsights = require('applicationinsights'); - ``` -5. Configure the local SDK by calling `appInsights.setup('YOUR_CONNECTION_STRING');`, using - the connection string you grabbed in step 2. Or put it in the - `APPLICATIONINSIGHTS_CONNECTION_STRING` environment variable and call - `appInsights.setup()` without parameters. - > For more configuration options see below. -6. Finally, start automatically collecting and sending data by calling - `appInsights.start();`. - -[these instructions]: https://docs.microsoft.com/azure/application-insights/app-insights-nodejs - - -## Basic Usage - -> *Important:* `applicationinsights` must be setup *and* started *before* you import anything else. There may be resulting telemetry loss if other libraries are imported first. - -For out-of-the-box collection of HTTP requests, popular third-party library events, -unhandled exceptions, and system metrics: - -```javascript -let appInsights = require("applicationinsights"); -appInsights.setup("YOUR_CONNECTION_STRING").start(); -``` - -* If the instrumentation key is set in the environment variable - APPLICATIONINSIGHTS\_CONNECTION\_STRING, `.setup()` can be called with no - arguments. This makes it easy to use different connection strings for different - environments. - -Load the Application Insights library (i.e. `require("applicationinsights")`) as -early as possible in your scripts, before loading other packages. This is needed -so that the Application Insights library can prepare later packages for tracking. -If you encounter conflicts with other libraries doing similar preparation, try -loading the Application Insights library after those. - -Because of the way JavaScript handles callbacks, additional work is necessary to -track a request across external dependencies and later callbacks. By default -this additional tracking is enabled; disable it by calling -`setAutoDependencyCorrelation(false)` as described in the -Configuration section below. - -## Azure Functions - -Due to how Azure Functions (and other FaaS services) handle incoming requests, they are not seen as `http` requests to the Node.js runtime. For this reason, Request -> Dependency correlelation will **not** work out of the box. -To enable tracking here, you simply need to grab the context from your Function request handler, and wrap your Function with that context. - -### Setting up Auto-Correlation for Azure Functions - -You do not need to make any changes to your existing Function logic. -Instead, you can update the `default` export of your `httpTrigger` to be wrapped with some Application Insights logic: - -```js -... - -// Default export wrapped with Application Insights FaaS context propagation -export default async function contextPropagatingHttpTrigger(context, req) { - // Start an AI Correlation Context using the provided Function context - const correlationContext = appInsights.startOperation(context, req); - - // Wrap the Function runtime with correlationContext - return appInsights.wrapWithCorrelationContext(async () => { - const startTime = Date.now(); // Start trackRequest timer - - // Run the Function - await httpTrigger(context, req); - - // Track Request on completion - appInsights.defaultClient.trackRequest({ - name: context.req.method + " " + context.req.url, - resultCode: context.res.status, - success: true, - url: req.url, - duration: Date.now() - startTime, - id: correlationContext.operation.parentId, - }); - appInsights.defaultClient.flush(); - }, correlationContext)(); -}; -``` +## Supported Node.js versions -### Azure Functions Example +We support the versions of Node.js that are [supported by OpenTelemetry](https://github.com/open-telemetry/opentelemetry-js#supported-runtimes). -An example of making an `axios` call to and returning the reponse. +## Limitations of current preview release -```js -const appInsights = require("applicationinsights"); -appInsights.setup("") - .setAutoCollectPerformance(false) - .start(); +Consider whether this preview is right for you. It *enables distributed tracing, metrics, logs* and _excludes_: -const axios = require("axios"); + - Autopopulation of Cloud Role Name and Cloud Role Instance in Azure environments + - Autopopulation of User ID and Authenticated User ID when you use the Application Insights JavaScript SDK + - Autopopulation of User IP (to determine location attributes) + - Ability to override Operation Name + - Ability to manually set User ID or Authenticated User ID + - Propagating Operation Name to Dependency Telemetry -/** - * No changes required to your existing Function logic - */ -const httpTrigger = async function (context, req) { - const response = await axios.get("https://httpbin.org/status/200"); +> *Warning:* This SDK only works for Node.js environments. Use the [Application Insights JavaScript SDK](https://github.com/microsoft/ApplicationInsights-JS) for web and browser scenarios. - context.res = { - status: response.status, - body: response.statusText, - }; -}; - -// Default export wrapped with Application Insights FaaS context propagation -export default async function contextPropagatingHttpTrigger(context, req) { - // Start an AI Correlation Context using the provided Function context - const correlationContext = appInsights.startOperation(context, req); - - // Wrap the Function runtime with correlationContext - return appInsights.wrapWithCorrelationContext(async () => { - const startTime = Date.now(); // Start trackRequest timer - - // Run the Function - await httpTrigger(context, req); - - // Track Request on completion - appInsights.defaultClient.trackRequest({ - name: context.req.method + " " + context.req.url, - resultCode: context.res.status, - success: true, - url: req.url, - duration: Date.now() - startTime, - id: correlationContext.operation.parentId, - }); - appInsights.defaultClient.flush(); - }, correlationContext)(); -}; -``` -## Configuration +## Get started +Application Insights SDK internally consumes [Azure Monitor OpenTelemetry for JavaScript](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/monitor/monitor-opentelemetry), all relevant documentation is available in that package repository, it also exposes previous functionalities and APIs that were previously available to have a smother transtion for customers using previous versions of the package, including manual track APIs, for new customers we recommend to use @azure/monitor-opentelemetry instead. -The appInsights object provides a number of methods to setup SDK behavior. They are -listed in the following snippet with their default values. +### Prerequisites -```javascript -let appInsights = require("applicationinsights"); -appInsights.setup("") - .setAutoDependencyCorrelation(true) - .setAutoCollectRequests(true) - .setAutoCollectPerformance(true, true) - .setAutoCollectExceptions(true) - .setAutoCollectDependencies(true) - .setAutoCollectConsole(true, false) - .setUseDiskRetryCaching(true) - .setAutoCollectPreAggregatedMetrics(true) - .setSendLiveMetrics(false) - .setAutoCollectHeartbeat(false) - .setInternalLogging(false, true) - .setDistributedTracingMode(appInsights.DistributedTracingModes.AI_AND_W3C) - .start(); -``` +- Azure subscription: [Create an Azure subscription for free](https://azure.microsoft.com/free/) +- Application Insights resource: [Create an Application Insights resource](create-workspace-resource.md#create-a-workspace-based-resource) -Please review their descriptions in your IDE's built-in type hinting, or [applicationinsights.ts](https://github.com/microsoft/ApplicationInsights-node.js/tree/develop/applicationinsights.ts) for -detailed information on what these control, and optional secondary arguments. +- Application using an officially [supported version](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/monitor/monitor-opentelemetry-exporter#currently-supported-environments) of Node.js runtime: + - [OpenTelemetry supported runtimes](https://github.com/open-telemetry/opentelemetry-js#supported-runtimes) + - [Azure Monitor OpenTelemetry Exporter supported runtimes](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/monitor/monitor-opentelemetry-exporter#currently-supported-environments) -Note that by default `setAutoCollectConsole` is configured to *exclude* calls to `console.log` -(and other `console` methods). By default, only calls to supported third-party loggers -(e.g. `winston`, `bunyan`) will be collected. You can change this behavior to *include* calls -to `console` methods by using `setAutoCollectConsole(true, true)`. +### Install the library -The TelemetryClient object contains a `config` property with many optional settings. These can be set as follows: +```sh +npm install applicationinsights@beta ``` -client.config.PROPERTYNAME = VALUE; -``` -These properties are client specific, so you can configure `appInsights.defaultClient` -separately from clients created with `new appInsights.TelemetryClient()`. - -| Property | Description | -| ------------------------------- |------------------------------------------------------------------------------------------------------------| -| instrumentationKey | Application Insights Instrumentation Key | -| endpointUrl | The ingestion endpoint to send telemetry payloads to | -| proxyHttpUrl | A proxy server for SDK HTTP traffic (Optional, Default pulled from `http_proxy` environment variable) | -| proxyHttpsUrl | A proxy server for SDK HTTPS traffic (Optional, Default pulled from `https_proxy` environment variable) | -| maxBatchSize | The maximum number of telemetry items to include in a payload to the ingestion endpoint (Default `250`) | -| maxBatchIntervalMs | The maximum amount of time to wait to for a payload to reach maxBatchSize (Default `15000`) | -| disableAppInsights | A flag indicating if telemetry transmission is disabled (Default `false`) | -| samplingPercentage | The percentage of telemetry items tracked that should be transmitted (Default `100`) | -| correlationIdRetryIntervalMs | The time to wait before retrying to retrieve the id for cross-component correlation (Default `30000`) | -| correlationHeaderExcludedDomains| A list of domains to exclude from cross-component correlation header injection (Default See [Config.ts][]) | -| ignoreLegacyHeaders | Disable including legacy headers in outgoing requests, x-ms-request-id | -| distributedTracingMode | Sets the distributed tracing modes (Default=AI) | -| enableAutoCollectExternalLoggers| Sets the state of console. If true logger activity will be sent to Application Insights | -| enableAutoCollectConsole | Sets the state of logger tracking (enabled by default for third-party loggers only). If true, logger auto collection will include console.log calls (default false) | -| enableAutoCollectExceptions | Sets the state of exception tracking (enabled by default). If true uncaught exceptions will be sent to Application Insights | -| enableAutoCollectPerformance | Sets the state of performance tracking (enabled by default). If true performance counters will be collected every second and sent to Application Insights | -| enableAutoCollectExtendedMetrics| Sets the state of performance tracking (enabled by default). If true, extended metrics counters will be collected every minute and sent to Application Insights | -| enableAutoCollectPreAggregatedMetrics | Sets the state of pre aggregated metrics tracking (enabled by default). If true pre aggregated metrics will be collected every minute and sent to Application Insights | -| enableAutoCollectHeartbeat | Sets the state of request tracking (enabled by default). If true HeartBeat metric data will be collected every 15 minutes and sent to Application Insights | -| enableAutoCollectRequests | Sets the state of request tracking (enabled by default). If true requests will be sent to Application Insights | -| enableAutoCollectDependencies | Sets the state of dependency tracking (enabled by default). If true dependencies will be sent to Application Insights | -| enableAutoDependencyCorrelation| Sets the state of automatic dependency correlation (enabled by default). If true dependencies will be correlated with requests | -| enableUseAsyncHooks | Sets the state of automatic dependency correlation (enabled by default). If true, forces use of experimental async_hooks module to provide correlation. If false, instead uses only patching-based techniques. If left blank, the best option is chosen for you based on your version of Node.js. | -| enableUseDiskRetryCaching | If true events that occurred while client is offline will be cached on disk | -| enableResendInterval | The wait interval for resending cached events. | -| enableMaxBytesOnDisk | The maximum size (in bytes) that the created temporary directory for cache events can grow to, before caching is disabled. | -| enableInternalDebugLogging | Enables debug and warning logging for AppInsights itself. If true, enables debug logging | -| enableInternalWarningLogging | Enables debug and warning logging for AppInsights itself. If true, enables warning logging | -| enableSendLiveMetrics | Enables communication with Application Insights Live Metrics. If true, enables communication with the live metrics service | -| disableAllExtendedMetrics | Disable all environment variables set | -| extendedMetricDisablers | Disable individual environment variables set. `"extendedMetricDisablers": "..."` | -| noDiagnosticChannel | In order to track context across asynchronous calls, some changes are required in third party libraries such as mongodb and redis. By default ApplicationInsights will use diagnostic-channel-publishers to monkey-patch some of these libraries. This property is to disable the feature. Note that by setting this flag, events may no longer be correctly associated with the right operation. | -| noPatchModules | Disable individual monkey-patches. Set `noPatchModules` to a comma separated list of packages to disable. e.g. `"noPatchModules": "console,redis"` to avoid patching the console and redis packages. The following modules are available: `azuresdk, bunyan, console, mongodb, mongodb-core, mysql, redis, winston, pg`, and `pg-pool`. Visit the [diagnostic-channel-publishers' README](https://github.com/microsoft/node-diagnostic-channel/blob/master/src/diagnostic-channel-publishers/README.md) for information about exactly which versions of these packages are patched. | -| noHttpAgentKeepAlive | HTTPS without a passed in agent | -| httpAgent | An http.Agent to use for SDK HTTP traffic (Optional, Default undefined) | -| httpsAgent | An https.Agent to use for SDK HTTPS traffic (Optional, Default undefined) -| aadTokenCredential| Azure Credential instance to be used to authenticate the App. [AAD Identity Credential Classes](https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/identity/identity#credential-classes) | | - -[Config.ts]: https://github.com/microsoft/ApplicationInsights-node.js/blob/develop/Library/Config.ts - -All these properties except httpAgent, httpsAgent and aadTokenCredential could be configured using configuration file `applicationinsights.json` located under root folder of applicationinsights package installation folder, Ex: `node_modules/applicationinsights`. These configuration values will be applied to all TelemetryClients created in the SDK. +### Enable Application Insights -```javascript -{ - "samplingPercentage": 80, - "enableAutoCollectExternalLoggers": true, - "enableAutoCollectExceptions": true, - "enableAutoCollectHeartbeat": true, - "enableSendLiveMetrics": true, - ... -} - -``` +> *Important:* `useAzureMonitor` must be called *before* you import anything else. There may be resulting telemetry loss if other libraries are imported first. -Custom JSON file could be provided using `APPLICATIONINSIGHTS_CONFIGURATION_FILE` environment variable. -```javascript -process.env.APPLICATIONINSIGHTS_CONFIGURATION_FILE = "C:/applicationinsights/config/customConfig.json" - -// Application Insights SDK setup.... -``` - -### Sampling - -By default, the SDK will send all collected data to the Application Insights service. If you collect a lot of data, you might want to enable sampling to reduce the amount of data sent. Set the `samplingPercentage` field on the Config object of a Client to accomplish this. Setting `samplingPercentage` to 100 (the default) means all data will be sent, and 0 means nothing will be sent. - -If you are using automatic correlation, all data associated with a single request will be included or excluded as a unit. - -Add code such as the following to enable sampling: - -```javascript -const appInsights = require("applicationinsights"); -appInsights.setup(""); -appInsights.defaultClient.config.samplingPercentage = 33; // 33% of all telemetry will be sent to Application Insights -appInsights.start(); -``` - -### Multiple roles for multi-component applications - -If your application consists of multiple components that you wish to instrument all with the same Instrumentation Key and still see these components as separate units in the Portal as if they were using separate Instrumentation Keys (for example, as separate nodes on the Application Map) you may need to manually configure the RoleName field to distinguish one component's telemetry from other components sending data to your Application Insights resource. (See [Monitor multi-component applications with Application Insights (preview)](https://docs.microsoft.com/azure/application-insights/app-insights-monitor-multi-role-apps)) - -Use the following to set the RoleName field: - -```javascript -const appInsights = require("applicationinsights"); -appInsights.setup(""); -appInsights.defaultClient.context.tags[appInsights.defaultClient.context.keys.cloudRole] = "MyRoleName"; -appInsights.start(); -``` +```typescript +const { useAzureMonitor, AzureMonitorOpenTelemetryOptions, InstrumentationOptions } = require("applicationinsights"); -If running in Azure App service or Azure functions the SDK will automatically populate the cloud role when following code is added: -```javascript -const appInsights = require("applicationinsights"); -appInsights.setup(""); -appInsights.defaultClient.setAutoPopulateAzureProperties(true); -appInsights.start(); +const config : AzureMonitorOpenTelemetryOptions = { + azureMonitorExporterOptions: { + connectionString: process.env["APPLICATIONINSIGHTS_CONNECTION_STRING"] || "" + } +}; +useAzureMonitor(config); ``` +* If the Connection String is set in the environment variable + APPLICATIONINSIGHTS\_CONNECTION\_STRING, `TelemetryClient` constructor can be called with no + arguments. This makes it easy to use different connection strings for different + environments. -### Automatic third-party instrumentation - -In order to track context across asynchronous calls, some changes are required in third party libraries such as mongodb and redis. -By default ApplicationInsights will use [`diagnostic-channel-publishers`](https://github.com/microsoft/node-diagnostic-channel/tree/master/src/diagnostic-channel-publishers) -to monkey-patch some of these libraries. -This can be disabled by setting the `APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL` environment variable. Note that by setting that -environment variable, events may no longer be correctly associated with the right operation. Individual monkey-patches can be -disabled by setting the `APPLICATION_INSIGHTS_NO_PATCH_MODULES` environment variable to a comma separated list of packages to -disable, e.g. `APPLICATION_INSIGHTS_NO_PATCH_MODULES=console,redis` to avoid patching the `console` and `redis` packages. - -The following modules are available: `azuresdk`, `bunyan`, `console`, `mongodb`, `mongodb-core`, `mysql`, `redis`, `winston`, -`pg`, and `pg-pool`. Visit the [diagnostic-channel-publishers' README](https://github.com/microsoft/node-diagnostic-channel/blob/master/src/diagnostic-channel-publishers/README.md) -for information about exactly which versions of these packages are patched. - -Automatic instrumentation for several Azure SDKs is also enabled, currently Cognitive Search, Communication Common and Cosmos DB SDKs are not supported. -[Javascript Azure SDKs](https://azure.github.io/azure-sdk/releases/latest/index.html#javascript) - -The `bunyan`, `winston`, and `console` patches will generate Application Insights Trace events based on whether `setAutoCollectConsole` is enabled. -The rest will generate Application Insights Dependency events based on whether `setAutoCollectDependencies` is enabled. Make sure that `applicationinsights` is imported **before** any 3rd-party packages for them to be instrumented successfully. +## Configuration -### Live Metrics -To enable sending live metrics of your app to Azure, use `setSendLiveMetrics(true)`. Filtering of live metrics in the Portal is currently not supported. +The ApplicationInsightsConfig interface provides a number of options to setup SDK behavior. -### Extended Metrics ->***Note:*** The ability to send extended native metrics was added in version `1.4.0` +```typescript +const config : AzureMonitorOpenTelemetryOptions = { + azureMonitorExporterOptions: { + // Offline storage + storageDirectory: "c://azureMonitor", + // Automatic retries + disableOfflineStorage: false, + // Application Insights Connection String + connectionString: process.env["APPLICATIONINSIGHTS_CONNECTION_STRING"] || "", + }, + otlpTraceExporterConfig: {}, + otlpMetricExporterConfig: {}, + otlpLogExporterConfig: {}, + samplingRatio: 1, + enableAutoCollectExceptions: true, + enableAutoCollectStandardMetrics: true, + enableAutoCollectPerformance: true, + enableLiveMetrics: false, + enableWebInstrumentation: false, + webInstrumentationConnectionString: "", + instrumentationOptions: { + azureSdk: { enabled: true }, + http: { enabled: true }, + mongoDb: { enabled: true }, + mySql: { enabled: true }, + postgreSql: { enabled: true }, + redis: { enabled: true }, + redis4: { enabled: true }, + console: { enabled: true}, + bunyan: { enabled: true}, + winston: { enabled: true}, + } as InstrumentationOptions, + resource: resource +}; +useAzureMonitor(config); -To enable sending extended native metrics of your app to Azure, simply install the separate native metrics package. The SDK will automatically load it when it is installed and start collecting Node.js native metrics. -```zsh -npm install applicationinsights-native-metrics -``` -Currently, the native metrics package performs autocollection of Garbage Collection CPU time, Event Loop ticks, and heap usage: -- **Garbage Collection:** The amount of CPU time spent on each type of garbage collection, and how many occurrences of each type. -- **Event Loop:** How many ticks occurred and how much CPU time was spent in total. -- **Heap vs Non-Heap:** How much of your app's memory usage is in the heap or non-heap. - -### Distributed Tracing Modes -By default, this SDK will send headers understood by other applications/services instrumented with an Application Insights SDK. You can optionally enable sending/receiving of [W3C Trace Context](https://github.com/w3c/trace-context) headers in addition to the existing AI headers, so you will not break correlation with any of your existing legacy services. Enabling W3C headers will allow your app to correlate with other services not instrumented with Application Insights, but do adopt this W3C standard. - -```js -const appInsights = require("applicationinsights"); -appInsights - .setup("") - .setDistributedTracingMode(appInsights.DistributedTracingModes.AI_AND_W3C) - .start() ``` -## Track custom telemetry -You can track any request, event, metric or exception using the Application -Insights client. Examples follow: -```javascript -let appInsights = require("applicationinsights"); -appInsights.setup().start(); // assuming connection string is in environment variables. start() can be omitted to disable any non-custom data -let client = appInsights.defaultClient; -client.trackEvent({name: "my custom event", properties: {customProperty: "custom property value"}}); -client.trackException({exception: new Error("handled exceptions can be logged with this method")}); -client.trackMetric({name: "custom metric", value: 3}); -client.trackTrace({message: "trace message"}); -client.trackDependency({target:"http://dbname", name:"select customers proc", data:"SELECT * FROM Customers", duration:231, resultCode:0, success: true, dependencyTypeName: "ZSQL"}); -client.trackRequest({name:"GET /customers", url:"http://myserver/customers", duration:309, resultCode:200, success:true}); - -let http = require("http"); -http.createServer( (req, res) => { - client.trackNodeHttpRequest({request: req, response: res}); // Place at the beginning of your request handler -}); -``` +| Property | Description | Default | +| --------------------------------|------------------------------------------------------------------------------------------------------------|-------| +| ... | Azure Monitor OpenTelemetry Configuration [More info here](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/monitor/monitor-opentelemetry#configuration) | | +| azureMonitorExporterOptions | Azure Monitor OpenTelemetry Exporter Configuration [More info here](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/monitor/monitor-opentelemetry-exporter) | | +| otlpTraceExporterConfig | OTLP Trace Exporter Configurationon [More info here](https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/exporter-trace-otlp-http) | | +| otlpMetricExporterConfig | OTLP Metric Exporter Configuration [More info here](https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-exporter-metrics-otlp-http) | | +| otlpLogExporterConfig | OTLP Log Exporter Configuration [More info here](https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/exporter-logs-otlp-http) | | +| samplingRatio | Sampling ratio must take a value in the range [0,1], 1 meaning all data will sampled and 0 all Tracing data will be sampled out. | 1 | +| enableAutoCollectExceptions | Sets the state of exception tracking. If true uncaught exceptions will be sent to Application Insights | true | +| enableAutoCollectStandardMetrics | Sets the state of standard metrics tracking. If true standard metrics will be collected and sent to Application Insights | true | +| enableAutoCollectPerformance | Sets the state of Performance Counters. If true Performance Counters will be sent to Application Insights | true | +| enableLiveMetrics | Enables communication with Application Insights Live Metrics. If true, enables communication with the live metrics service | false | +| enableWebInstrumentation | Sets the state of automatic web Instrumentation (Optional, disabled by default). If true, web instrumentation will be enabled on valid node.js server http response with the connection string used for SDK initialization | false | +| webInstrumentationConnectionString | Sets connection string used for web Instrumentation (Browser SDK Loader) (Optional, Default undefined) | | +| instrumentationOptions | instrumentation options | { azureSdk: { enabled: true }, http: { enabled: true }, mongoDb: { enabled: true }, mySql: { enabled: true }, postgreSql: { enabled: true }, redis: { enabled: true }, redis4: { enabled: true }, console: { enabled: true}, bunyan: { enabled: true}, winston: { enabled: true} } | +| resource | Opentelemetry Resource. [More info here](https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-resources) | | -Note that custom properties are converted to their string representation before being sent, see [Using properties](https://docs.microsoft.com/azure/azure-monitor/app/api-custom-events-metrics#properties) for more information. +Configuration could be set using configuration file `applicationinsights.json` located under root folder of applicationinsights package installation folder, Ex: `node_modules/applicationinsights`. -An example utility using `trackMetric` to measure how long event loop scheduling takes: -```javascript -function startMeasuringEventLoop() { - var startTime = process.hrtime(); - var sampleSum = 0; - var sampleCount = 0; - - // Measure event loop scheduling delay - setInterval(() => { - var elapsed = process.hrtime(startTime); - startTime = process.hrtime(); - sampleSum += elapsed[0] * 1e9 + elapsed[1]; - sampleCount++; - }, 0); - - // Report custom metric every second - setInterval(() => { - var samples = sampleSum; - var count = sampleCount; - sampleSum = 0; - sampleCount = 0; - - if (count > 0) { - var avgNs = samples / count; - var avgMs = Math.round(avgNs / 1e6); - client.trackMetric({name: "Event Loop Delay", value: avgMs}); +```json +{ + "azureMonitorExporterOptions": {"connectionString":""}, + "samplingRatio": 0.8, + "enableAutoCollectExceptions": true, + "instrumentationOptions":{ + "azureSdk": { + "enabled": false + }, + "console": { + "enabled": true + } } - }, 1000); + ... } + ``` -## Preprocess data with Telemetry Processors +Custom JSON file could be provided using `APPLICATIONINSIGHTS_CONFIGURATION_FILE` environment variable. ```javascript -public addTelemetryProcessor(telemetryProcessor: (envelope: Contracts.Envelope, context: { http.RequestOptions, http.ClientRequest, http.ClientResponse, Error, correlationContext }) => boolean) -``` - -You can process and filter collected data before it is sent for retention using -_Telemetry Processors_. Telemetry processors are called one by one in the -order they were added before the telemetry item is sent to the cloud. - -If a telemetry processor returns false that telemetry item will not be sent. - -All telemetry processors receive the telemetry data and its envelope to inspect and -modify. They also receive a context object. The contents of this object is defined by -the `contextObjects` parameter when calling a track method for manually tracked telemetry. -For automatically collected telemetry, this object is filled with available request information -and the persistent request context as provided by `appInsights.getCorrelationContext()` (if -automatic dependency correlation is enabled). - -The TypeScript type for a telemetry processor is: +process.env.APPLICATIONINSIGHTS_CONFIGURATION_FILE = "C:/applicationinsights/config/customConfig.json" -```typescript -telemetryProcessor: (envelope: ContractsModule.Contracts.Envelope, context: { http.RequestOptions, http.ClientRequest, http.ClientResponse, Error, correlationContext }) => boolean; +// Application Insights SDK setup.... ``` -For example, a processor that removes stack trace data from exceptions might be -written and added as follows: +## ApplicationInsights Shim Unsupported Properties +The ApplicationInsights shim will provide support path for customers who only require basic instrumentation as opposed to migrating to Azure Monitor OpenTelemetry. If unsupported methods are called, they are not breaking and your application will still run. Calling these unsupported methods will throw a warning that the method is not supported by the ApplicationInsights shim. -```javascript -function removeStackTraces ( envelope, context ) { - if (envelope.data.baseType === "ExceptionData") { - var data = envelope.data.baseData; - if (data.exceptions && data.exceptions.length > 0) { - for (var i = 0; i < data.exceptions.length; i++) { - var exception = data.exceptions[i]; - exception.parsedStack = null; - exception.hasFullStack = false; - } - } - // Add extra properties - var originalError = context["Error"]; - if(originalError && originalError.prop){ - data.properties = data.properties || {}; - data.properties.customProperty = originalError.prop; - } - } - return true; -} +The following methods are called after using the below method. -appInsights.defaultClient.addTelemetryProcessor(removeStackTraces); +```javascript +let applicationinsights = require("applicationinsights"); +appinsights.setup("").start(); ``` -More info on the telemetry API is available in [the docs][]. - -[the docs]: https://azure.microsoft.com/documentation/articles/app-insights-api-custom-events-metrics/ +And invoked via `appInsights.` +|Property |Support Status | +| ----------------------------|------------------------------------------------------------------------------------------------------------| +| setDistributedTracingMode | AI only tracing mode is no longer supported. Migrate to using W3C_AND_AI tracing mode. | +| setAutoCollectHeartbeat | Heartbeat is not supported in either Azure Monitor OpenTelemetry or the shim.| +| setAutoDependencyCorrelation| Turning off autoDependencyCorrelation is not supported by either Azure Monitor OpenTelemetry or the shim. | +| setUseDiskRetryCaching | While enabling/disabling offline storage is supported, setting the resend interval or the maxBytesOnDisk values are not supported in the shim or Azure Monitor OpenTelemetry. | +| setAutoCollectIncomingRequestAzureFunctions | Auto collection of Azure Functions is not supported by the shim or Azure Monitor OpenTelemetry. | + +The following configurations are set using either environment variables, setting them in the `applicationinsights.json` file or by calling `appInsights.defaultClient.config.;`. + +|Property |Support Status | +|-----------------------|-----------------------------------------------------------------------| +| instrumentationKey & endpointUrl | Not supported by the shim or Azure Monitor OpenTelemetry. Please migrate to using the connectionString. | +| maxBatchSize | Not supported by the shim but can be configured by using OpenTelemetry SpanProcessors in Azure Monitor OpenTelemetry. | +| disableAppInsights | Not supported by the shim. Disabling telemetry export is possible via Azure Monitor OpenTelemetry using OpenTelemetry.| +| correlationIdRetryIntervalMs | Not supported by either the shim or Azure Monitor OpenTelemetry as correlation ID is deprecated.| +| ignoreLegacyHeaders | Legacy headers in outgoing requests are not supported in the shim or Azure Monitor OpenTelemetry. Therefore they will always be disabled. | +| distributedTracingMode | Distributed tracing mode is always set to AI_AND_W3C. AI only tracing mode is not supported in the shim or Azure Monitor OpenTelemetry.| +| enableLoggerErrorToTrace | Not supported in the shim or Azure Monitor OpenTelemetry as all errors will be logged as exceptions in both. | +| enableAutoCollectHeartbeat | Heartbeat is not supported in the shim or Azure Monitor OpenTelemetry as it is deprecated. | +| enableAutoDependencyCorrelation | Cannot disable dependency correlation in either the shim or Azure Monitor OpenTelemetry. Dependency correlation will always be enabled and therefore this setting is always true. | +| enableAutoCollectIncomingRequestAzureFunctions | Auto collection of Azure Functions is not supported by the shim or Azure Monitor OpenTelemetry. | +| enableUseAsyncHooks | Using async hooks is not supported by the shim or Azure Monitor OpenTelemetry as it is not supported by OpenTelemetry. | +| enableResendInterval | Not supported by the shim. It is possible to configure the interval between exports via OpenTelemetry span processors, but not specifically cached events. The @azure/monitor-opentelemetry-exporter uses a resend interval of one minute. | +| enableMaxBytesOnDisk | Not supported by the shim. And not supported to be changed in Azure Monitor OpenTelemetry. The @azure/monitor-opentelemetry-exporter sets this value at 50MB. | +| noHttpAgentKeepAlive | Not supported in the shim or Azure Monitor OpenTelemetry. | +| httpAgent/httpsAgent | Not supported in the shim or Azure Monitor OpenTelemetry. | +| webInstrumentationConfig | Not currently supported by the shim or Azure Monitor OpenTelemetry. | +| quickPulseHost | Not supported in the shim or Azure Monitor OpenTelemetry. | +| enableAutoCollectExtendedMetrics | Extended/native metrics are not supported in the shim or Azure Monitor OpenTelemetry. | +| disableAllExtendedMetrics | Will not have any effect as extended/native metrics are not supported in the shim or Azure Monitor OpenTelemetry. | +| extendedMetricDisablers | Will not have any effect as extended/native metrics are not supported in the shim or Azure Monitor OpenTelemetry. | + +The following methods are part of the `TelemetryClient` class. They can be called using `applicationinsights.defaultClient.()`. + +|Property | Support Status | +|---------------|-------------------------------------------------| +| trackPageView | PageViewTelemetry requires an id and a name field now instead of only an optional name field. | +| track | Tracking generic telemetry is not longer supported. Please use one of the other manual track methods to track a specific telemetry type. | +| getAuthorizationHandler | Not supported in the shim. | +| addTelemetryProcessor | TelemetryProcessors are not supported in the shim. Please migrate to Azure Monitor OpenTelemetry and use OpenTelemetry Span Processors. | +| clearTelemetryProcessors | TelemetryProcessors are not supported in the shim. Please migrate to Azure Monitor OpenTelemetry and use OpenTelemetry Span Processors. | +| runTelemetryProcessors | TelemetryProcessors are not supported in the shim. Please migrate to Azure Monitor OpenTelemetry and use OpenTelemetry Span Processors. | +| trackNodeHttpRequestSync | Not supported. Please use the trackRequest method instead. | +| trackNodeHttpRequest | Not supported. Please use the trackRequest method instead. | +| trackNodeHttpDependency | Not supported. Please use the trackDependency method instead. | + + +## Troubleshooting + +### Self-diagnostics + + +`APPLICATIONINSIGHTS_INSTRUMENTATION_LOGGING_LEVEL` environment varialbe could be used to set desired log level, supporting the following values: `NONE`, `ERROR`, `WARN`, `INFO`, `DEBUG`, `VERBOSE` and `ALL`. -## Use multiple Application Insights resources -You can create multiple Azure Application Insights resources and send different -data to each by using their respective connection string. For -example: +Logs could be put into local file using `APPLICATIONINSIGHTS_LOG_DESTINATION` environment variable, supported values are `file` and `file+console`, a file named `applicationinsights.log` will be generated on tmp folder by default, including all logs, `/tmp` for *nix and `USERDIR/AppData/Local/Temp` for Windows. Log directory could be configured using `APPLICATIONINSIGHTS_LOGDIR` environment variable. ```javascript -let appInsights = require("applicationinsights"); - -// configure auto-collection under one Connection String -appInsights.setup("").start(); +process.env.APPLICATIONINSIGHTS_INSTRUMENTATION_LOGGING_LEVEL = "VERBOSE"; +process.env.APPLICATIONINSIGHTS_LOG_DESTINATION = "file"; +process.env.APPLICATIONINSIGHTS_LOGDIR = "C:/applicationinsights/logs"; -// track some events manually under another connection string -let otherClient = new appInsights.TelemetryClient(""); -otherClient.trackEvent({name: "my custom event"}); +// Application Insights SDK setup.... ``` -## Examples - -* Track dependencies - - ```javascript - let appInsights = require("applicationinsights"); - let client = new appInsights.TelemetryClient(); - - var success = false; - let startTime = Date.now(); - // execute dependency call here.... - let duration = Date.now() - startTime; - success = true; - - client.trackDependency({target:"http://dbname", name:"select customers proc", data:"SELECT * FROM Customers", duration:duration, resultCode:0, success: true, dependencyTypeName: "ZSQL"}); - ``` - -* Assign custom properties to be included with all events - - ```javascript - appInsights.defaultClient.commonProperties = { - environment: process.env.SOME_ENV_VARIABLE - }; - ``` - -* Manually track all HTTP GET requests - - Note that all requests are tracked by default. To disable automatic - collection, call `.setAutoCollectRequests(false)` before calling `start()`. - - ```javascript - appInsights.defaultClient.trackRequest({name:"GET /customers", url:"http://myserver/customers", duration:309, resultCode:200, success:true}); - ``` - Alternatively you can track requests using ```trackNodeHttpRequest``` method: - - ```javascript - var server = http.createServer((req, res) => { - if ( req.method === "GET" ) { - appInsights.defaultClient.trackNodeHttpRequest({request:req, response:res}); - } - // other work here.... - res.end(); - }); - ``` - -* Track server startup time +## Support - ```javascript - let start = Date.now(); - server.on("listening", () => { - let duration = Date.now() - start; - appInsights.defaultClient.trackMetric({name: "server startup time", value: duration}); - }); - ``` +For help and questions about using this project, please create a Support request issue on +https://github.com/microsoft/ApplicationInsights-node.js/issues. -## Self-diagnostics +For OpenTelemetry issues, contact the [OpenTelemetry JavaScript community](https://github.com/open-telemetry/opentelemetry-js) directly. [Support Policy](SUPPORT) -"Self-diagnostics" refers to internal logging from Application Insights Node.js SDK. -This functionality can be helpful for spotting and diagnosing issues with Application Insights itself. -By default, Application Insights Node.js SDK logs at warning level to console, following code demonstrate how to enable debug logging as well and generate telemetry for internal logs: - -```javascript -let appInsights = require("applicationinsights"); -appInsights.setup("") - .setInternalLogging(true, true) // Enable both debug and warning logging - .setAutoCollectConsole(true, true) // Generate Trace telemetry for winston/bunyan and console logs - .start(); -``` - -Logs could be put into local file using `APPLICATIONINSIGHTS_LOG_DESTINATION` environment variable, supported values are `file` and `file+console`, a file named `applicationinsights.log` will be generated on tmp folder by default, including all logs, `/tmp` for *nix and `USERDIR/AppData/Local/Temp` for Windows. Log directory could be configured using `APPLICATIONINSIGHTS_LOGDIR` environment variable. +## Contributing -```javascript -process.env.APPLICATIONINSIGHTS_LOG_DESTINATION = "file"; -process.env.APPLICATIONINSIGHTS_LOGDIR = "C:/applicationinsights/logs" +This project welcomes contributions and suggestions. Most contributions require you to +agree to a Contributor License Agreement (CLA) declaring that you have the right to, +and actually do, grant us the rights to use your contribution. For details, visit +https://cla.microsoft.com. -// Application Insights SDK setup.... -``` +When you submit a pull request, a CLA-bot will automatically determine whether you need +to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the +instructions provided by the bot. You will only need to do this once across all repositories using our CLA. -## Branches +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) +or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. -- Ongoing development takes place on the [develop][] branch. **Please submit - pull requests to this branch.** -- Releases are merged to the [master][] branch and published to [npm][]. +## Data Collection -[master]: https://github.com/microsoft/ApplicationInsights-node.js/tree/master -[develop]: https://github.com/microsoft/ApplicationInsights-node.js/tree/develop -[npm]: https://www.npmjs.com/package/applicationinsights +As this SDK is designed to enable applications to perform data collection which is sent to the Microsoft collection endpoints the following is required to identify our privacy statement. -## Contributing -For details on contributing to this repository, see the [contributing guide](https://github.com/microsoft/ApplicationInsights-node.js/master/CONTRIBUTING.md). +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at https://go.microsoft.com/fwlink/?LinkID=824704. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. -This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit -https://cla.microsoft.com. +## Trademarks -When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repositories using our CLA. +This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow [Microsoft’s Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party’s policies. -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. +## License +[MIT](LICENSE) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..f7b89984f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). + + \ No newline at end of file diff --git a/SUPPORT.md b/SUPPORT.md new file mode 100644 index 000000000..bfd18b1a3 --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,16 @@ +# Support + +## How to file issues and get help + +This project uses GitHub Issues to track bugs and feature requests. Please search the existing +issues before filing new issues to avoid duplicates. For new issues, file your bug or +feature request as a new Issue. + +For help and questions about using this project, please create a Support request issue on +https://github.com/microsoft/ApplicationInsights-node.js/issues. + +For OpenTelemetry issues, contact the [OpenTelemetry JavaScript community](https://github.com/open-telemetry/opentelemetry-js) directly. + +## Microsoft Support Policy + +Support for this **PROJECT or PRODUCT** is limited to the resources listed above. \ No newline at end of file diff --git a/Schema/PublicSchema/AvailabilityData.bond b/Schema/PublicSchema/AvailabilityData.bond deleted file mode 100644 index ab6d08348..000000000 --- a/Schema/PublicSchema/AvailabilityData.bond +++ /dev/null @@ -1,47 +0,0 @@ -import "Domain.bond" - -namespace AI - -[Description("Instances of AvailabilityData represent the result of executing an availability test.")] -struct AvailabilityData - : Domain -{ - [Description("Schema version")] - 10: required int32 ver = 2; - - [MaxStringLength("64")] - [Description("Identifier of a test run. Use it to correlate steps of test run and telemetry generated by the service.")] - [ActAsRequired("Renaming testRunId to id.")] - 21: required string id; - - [MaxStringLength("1024")] - [Description("Name of the test that these availability results represent.")] - [ActAsRequired("Renaming testName to name.")] - 41: required string name; - - [Description("Duration in format: DD.HH:MM:SS.MMMMMM. Must be less than 1000 days.")] - [CSType("TimeSpan")] - 50: required string duration; - - [ActAsRequired("Renaming result to success.")] - [Description("Success flag.")] - 61: required bool success; - - [MaxStringLength("1024")] - [Description("Name of the location where the test was run from.")] - 70: string runLocation; - - [MaxStringLength("8192")] - [Description("Diagnostic message for the result.")] - 80: string message; - - [Description("Collection of custom properties.")] - [MaxKeyLength("150")] - [MaxValueLength("8192")] - 100: map properties; - - [Description("Collection of custom measurements.")] - [MaxKeyLength("150")] - 200: map measurements; - -} diff --git a/Schema/PublicSchema/Base.bond b/Schema/PublicSchema/Base.bond deleted file mode 100644 index 0555316c6..000000000 --- a/Schema/PublicSchema/Base.bond +++ /dev/null @@ -1,11 +0,0 @@ - -namespace AI - -[Description("Data struct to contain only C section with custom fields.")] -struct Base -{ - [Name("ItemTypeName")] - [Description("Name of item (B section) if any. If telemetry data is derived straight from this, this should be null.")] - 10: string baseType; - -} diff --git a/Schema/PublicSchema/ContextTagKeys.bond b/Schema/PublicSchema/ContextTagKeys.bond deleted file mode 100644 index f48e62e2c..000000000 --- a/Schema/PublicSchema/ContextTagKeys.bond +++ /dev/null @@ -1,101 +0,0 @@ - -namespace AI - -[ContextContract("Emit")] -[PseudoType("JSMap")] -struct ContextTagKeys -{ - [Description("Application version. Information in the application context fields is always about the application that is sending the telemetry.")] - [MaxStringLength("1024")] - 10: string ApplicationVersion = "ai.application.ver"; - - [Description("Unique client device id. Computer name in most cases.")] - [MaxStringLength("1024")] - 100: string DeviceId = "ai.device.id"; - - [Description("Device locale using - pattern, following RFC 5646. Example 'en-US'.")] - [MaxStringLength("64")] - 115: string DeviceLocale = "ai.device.locale"; - - [Description("Model of the device the end user of the application is using. Used for client scenarios. If this field is empty then it is derived from the user agent.")] - [MaxStringLength("256")] - 120: string DeviceModel = "ai.device.model"; - - [Description("Client device OEM name taken from the browser.")] - [MaxStringLength("256")] - 130: string DeviceOEMName = "ai.device.oemName"; - - [Description("Operating system name and version of the device the end user of the application is using. If this field is empty then it is derived from the user agent. Example 'Windows 10 Pro 10.0.10586.0'")] - [MaxStringLength("256")] - 140: string DeviceOSVersion = "ai.device.osVersion"; - - [Description("The type of the device the end user of the application is using. Used primarily to distinguish JavaScript telemetry from server side telemetry. Examples: 'PC', 'Phone', 'Browser'. 'PC' is the default value.")] - [MaxStringLength("64")] - 160: string DeviceType = "ai.device.type"; - - [Description("The IP address of the client device. IPv4 and IPv6 are supported. Information in the location context fields is always about the end user. When telemetry is sent from a service, the location context is about the user that initiated the operation in the service.")] - [MaxStringLength("46")] - 200: string LocationIp = "ai.location.ip"; - - [Description("A unique identifier for the operation instance. The operation.id is created by either a request or a page view. All other telemetry sets this to the value for the containing request or page view. Operation.id is used for finding all the telemetry items for a specific operation instance.")] - [MaxStringLength("128")] - 300: string OperationId = "ai.operation.id"; - - [Description("The name (group) of the operation. The operation.name is created by either a request or a page view. All other telemetry items set this to the value for the containing request or page view. Operation.name is used for finding all the telemetry items for a group of operations (i.e. 'GET Home/Index').")] - [MaxStringLength("1024")] - 305: string OperationName = "ai.operation.name"; - - [Description("The unique identifier of the telemetry item's immediate parent.")] - [MaxStringLength("128")] - 310: string OperationParentId = "ai.operation.parentId"; - - [Description("Name of synthetic source. Some telemetry from the application may represent a synthetic traffic. It may be web crawler indexing the web site, site availability tests or traces from diagnostic libraries like Application Insights SDK itself.")] - [MaxStringLength("1024")] - 320: string OperationSyntheticSource = "ai.operation.syntheticSource"; - - [Description("The correlation vector is a light weight vector clock which can be used to identify and order related events across clients and services.")] - [MaxStringLength("64")] - 330: string OperationCorrelationVector = "ai.operation.correlationVector"; - - [Description("Session ID - the instance of the user's interaction with the app. Information in the session context fields is always about the end user. When telemetry is sent from a service, the session context is about the user that initiated the operation in the service.")] - [MaxStringLength("64")] - 400: string SessionId = "ai.session.id"; - - [Description("Boolean value indicating whether the session identified by ai.session.id is first for the user or not.")] - [MaxStringLength("5")] - [Question("Should it be marked as JSType-bool for breeze?")] - 405: string SessionIsFirst = "ai.session.isFirst"; - - [Description("In multi-tenant applications this is the account ID or name which the user is acting with. Examples may be subscription ID for Azure portal or blog name blogging platform.")] - [MaxStringLength("1024")] - 505: string UserAccountId = "ai.user.accountId"; - - [Description("Anonymous user id. Represents the end user of the application. When telemetry is sent from a service, the user context is about the user that initiated the operation in the service.")] - [MaxStringLength("128")] - 515: string UserId = "ai.user.id"; - - [Description("Authenticated user id. The opposite of ai.user.id, this represents the user with a friendly name. Since it's PII information it is not collected by default by most SDKs.")] - [MaxStringLength("1024")] - 525: string UserAuthUserId = "ai.user.authUserId"; - - [Description("Name of the role the application is a part of. Maps directly to the role name in azure.")] - [MaxStringLength("256")] - 705: string CloudRole = "ai.cloud.role"; - - [Description("Name of the instance where the application is running. Computer name for on-premises, instance name for Azure.")] - [MaxStringLength("256")] - 715: string CloudRoleInstance = "ai.cloud.roleInstance"; - - [Description("SDK version. See https://github.com/microsoft/ApplicationInsights-Home/blob/master/SDK-AUTHORING.md#sdk-version-specification for information.")] - [MaxStringLength("64")] - 1000: string InternalSdkVersion = "ai.internal.sdkVersion"; - - [Description("Agent version. Used to indicate the version of StatusMonitor installed on the computer if it is used for data collection.")] - [MaxStringLength("64")] - 1001: string InternalAgentVersion = "ai.internal.agentVersion"; - - [Description("This is the node name used for billing purposes. Use it to override the standard detection of nodes.")] - [MaxStringLength("256")] - 1002: string InternalNodeName = "ai.internal.nodeName"; - -} diff --git a/Schema/PublicSchema/Data.bond b/Schema/PublicSchema/Data.bond deleted file mode 100644 index def8b38d4..000000000 --- a/Schema/PublicSchema/Data.bond +++ /dev/null @@ -1,13 +0,0 @@ -import "Base.bond" - -namespace AI - -[Description("Data struct to contain both B and C sections.")] -struct Data - : Base -{ - [Name("Item")] - [Description("Container for data item (B section).")] - 20: required TDomain baseData; - -} diff --git a/Schema/PublicSchema/DataPoint.bond b/Schema/PublicSchema/DataPoint.bond deleted file mode 100644 index 03ef91fc0..000000000 --- a/Schema/PublicSchema/DataPoint.bond +++ /dev/null @@ -1,30 +0,0 @@ -import "DataPointType.bond" - -namespace AI - -[Description("Metric data single measurement.")] -struct DataPoint -{ - [Description("Name of the metric.")] - [MaxStringLength("1024")] - 10: required string name; - - [Description("Metric type. Single measurement or the aggregated value.")] - 20: AI.DataPointType kind = Measurement; - - [Description("Single value for measurement. Sum of individual measurements for the aggregation.")] - 30: required double value; - - [Description("Metric weight of the aggregated metric. Should not be set for a measurement.")] - 40: nullable count; - - [Description("Minimum value of the aggregated metric. Should not be set for a measurement.")] - 50: nullable min; - - [Description("Maximum value of the aggregated metric. Should not be set for a measurement.")] - 60: nullable max; - - [Description("Standard deviation of the aggregated metric. Should not be set for a measurement.")] - 70: nullable stdDev; - -} diff --git a/Schema/PublicSchema/DataPointType.bond b/Schema/PublicSchema/DataPointType.bond deleted file mode 100644 index a65c6fc62..000000000 --- a/Schema/PublicSchema/DataPointType.bond +++ /dev/null @@ -1,8 +0,0 @@ -namespace AI - -[Description("Type of the metric data measurement.")] -enum DataPointType -{ - Measurement, - Aggregation, -} diff --git a/Schema/PublicSchema/Domain.bond b/Schema/PublicSchema/Domain.bond deleted file mode 100644 index 4ad84d3b7..000000000 --- a/Schema/PublicSchema/Domain.bond +++ /dev/null @@ -1,7 +0,0 @@ - -namespace AI - -[Description("The abstract common base of all domains.")] -struct Domain -{ -} diff --git a/Schema/PublicSchema/Envelope.bond b/Schema/PublicSchema/Envelope.bond deleted file mode 100644 index 3dba5e2bf..000000000 --- a/Schema/PublicSchema/Envelope.bond +++ /dev/null @@ -1,50 +0,0 @@ -import "Base.bond" - -namespace AI - -[Description("System variables for a telemetry item.")] -struct Envelope -{ - [Description("Envelope version. For internal use only. By assigning this the default, it will not be serialized within the payload unless changed to a value other than #1.")] - [Name("SchemaVersion")] - 10: int32 ver = 1; - - [Description("Type name of telemetry data item.")] - [Name("DataTypeName")] - [MaxStringLength("1024")] - 20: required string name; - - [Description("Event date time when telemetry item was created. This is the wall clock time on the client when the event was generated. There is no guarantee that the client's time is accurate. This field must be formatted in UTC ISO 8601 format, with a trailing 'Z' character, as described publicly on https://en.wikipedia.org/wiki/ISO_8601#UTC. Note: the number of decimal seconds digits provided are variable (and unspecified). Consumers should handle this, i.e. managed code consumers should not use format 'O' for parsing as it specifies a fixed length. Example: 2009-06-15T13:45:30.0000000Z.")] - [Name("DateTime")] - [CSType("DateTimeOffset")] - [JSType("Date")] - [HockeyAppMinDateOffsetFromNow("2592000000")] - [MinDateOffsetFromNow("172800000")] - [MaxDateOffsetFromNow("7200000")] - [MaxStringLength("64")] - 30: required string time; - - [Name("SamplingRate")] - [Description("Sampling rate used in application. This telemetry item represents 1 / sampleRate actual telemetry items.")] - 40: double sampleRate = 100.0; - - [Description("Sequence field used to track absolute order of uploaded events.")] - [Name("SequenceNumber")] - [MaxStringLength("64")] - 50: string seq; - - [Description("The application's instrumentation key. The key is typically represented as a GUID, but there are cases when it is not a guid. No code should rely on iKey being a GUID. Instrumentation key is case insensitive.")] - [Name("InstrumentationKey")] - [MaxStringLength("40")] - 60: string iKey; - - [Name("Tags")] - [TypeAlias("ContextTagKeys")] - [Description("Key/value collection of context properties. See ContextTagKeys for information on available properties.")] - 500: map tags; - - [Name("TelemetryData")] - [Description("Telemetry data item.")] - 999: Base data; - -} diff --git a/Schema/PublicSchema/EventData.bond b/Schema/PublicSchema/EventData.bond deleted file mode 100644 index 77a538cb8..000000000 --- a/Schema/PublicSchema/EventData.bond +++ /dev/null @@ -1,26 +0,0 @@ -import "Domain.bond" - -namespace AI - -[Description("Instances of Event represent structured event records that can be grouped and searched by their properties. Event data item also creates a metric of event count by name.")] -struct EventData - : Domain -{ - [Description("Schema version")] - 10: required int32 ver = 2; - - [MaxStringLength("512")] - [Description("Event name. Keep it low cardinality to allow proper grouping and useful metrics.")] - [Question("Why Custom Event name is shorter than Request name or dependency name?")] - 20: required string name; - - [Description("Collection of custom properties.")] - [MaxKeyLength("150")] - [MaxValueLength("8192")] - 100: map properties; - - [Description("Collection of custom measurements.")] - [MaxKeyLength("150")] - 200: map measurements; - -} diff --git a/Schema/PublicSchema/ExceptionData.bond b/Schema/PublicSchema/ExceptionData.bond deleted file mode 100644 index 75685fa3e..000000000 --- a/Schema/PublicSchema/ExceptionData.bond +++ /dev/null @@ -1,33 +0,0 @@ -import "Domain.bond" -import "ExceptionDetails.bond" -import "SeverityLevel.bond" - -namespace AI - -[Description("An instance of Exception represents a handled or unhandled exception that occurred during execution of the monitored application.")] -struct ExceptionData - : Domain -{ - [Description("Schema version")] - 10: required int32 ver = 2; - - [Description("Exception chain - list of inner exceptions.")] - 50: required vector exceptions; - - [Description("Severity level. Mostly used to indicate exception severity level when it is reported by logging library.")] - 60: nullable severityLevel; - - [Description("Identifier of where the exception was thrown in code. Used for exceptions grouping. Typically a combination of exception type and a function from the call stack.")] - [MaxStringLength("1024")] - 80: string problemId; - - [Description("Collection of custom properties.")] - [MaxKeyLength("150")] - [MaxValueLength("8192")] - 100: map properties; - - [Description("Collection of custom measurements.")] - [MaxKeyLength("150")] - 200: map measurements; - -} diff --git a/Schema/PublicSchema/ExceptionDetails.bond b/Schema/PublicSchema/ExceptionDetails.bond deleted file mode 100644 index e33270863..000000000 --- a/Schema/PublicSchema/ExceptionDetails.bond +++ /dev/null @@ -1,32 +0,0 @@ -import "StackFrame.bond" - -namespace AI - -[Description("Exception details of the exception in a chain.")] -struct ExceptionDetails -{ - [Description("In case exception is nested (outer exception contains inner one), the id and outerId properties are used to represent the nesting.")] - 10: int32 id; - - [Description("The value of outerId is a reference to an element in ExceptionDetails that represents the outer exception")] - 20: int32 outerId; - - [Description("Exception type name.")] - [MaxStringLength("1024")] - 30: required string typeName; - - [Description("Exception message.")] - [MaxStringLength("32768")] - 40: required string message; - - [Description("Indicates if full exception stack is provided in the exception. The stack may be trimmed, such as in the case of a StackOverflow exception.")] - 50: bool hasFullStack = true; - - [Description("Text describing the stack. Either stack or parsedStack should have a value.")] - [MaxStringLength("32768")] - 60: string stack; - - [Description("List of stack frames. Either stack or parsedStack should have a value.")] - 70: vector parsedStack; - -} diff --git a/Schema/PublicSchema/MessageData.bond b/Schema/PublicSchema/MessageData.bond deleted file mode 100644 index c6022ac05..000000000 --- a/Schema/PublicSchema/MessageData.bond +++ /dev/null @@ -1,25 +0,0 @@ -import "Domain.bond" -import "SeverityLevel.bond" - -namespace AI - -[Description("Instances of Message represent printf-like trace statements that are text-searched. Log4Net, NLog and other text-based log file entries are translated into intances of this type. The message does not have measurements.")] -struct MessageData - : Domain -{ - [Description("Schema version")] - 10: required int32 ver = 2; - - [MaxStringLength("32768")] - [Description("Trace message")] - 20: required string message; - - [Description("Trace severity level.")] - 30: nullable severityLevel; - - [Description("Collection of custom properties.")] - [MaxKeyLength("150")] - [MaxValueLength("8192")] - 100: map properties; - -} diff --git a/Schema/PublicSchema/MetricData.bond b/Schema/PublicSchema/MetricData.bond deleted file mode 100644 index a49f8e9f3..000000000 --- a/Schema/PublicSchema/MetricData.bond +++ /dev/null @@ -1,21 +0,0 @@ -import "Domain.bond" -import "DataPoint.bond" - -namespace AI - -[Description("An instance of the Metric item is a list of measurements (single data points) and/or aggregations.")] -struct MetricData - : Domain -{ - [Description("Schema version")] - 10: required int32 ver = 2; - - [Description("List of metrics. Only one metric in the list is currently supported by Application Insights storage. If multiple data points were sent only the first one will be used.")] - 20: required vector metrics; - - [Description("Collection of custom properties.")] - [MaxKeyLength("150")] - [MaxValueLength("8192")] - 100: map properties; - -} diff --git a/Schema/PublicSchema/PageViewData.bond b/Schema/PublicSchema/PageViewData.bond deleted file mode 100644 index 3b1dbd161..000000000 --- a/Schema/PublicSchema/PageViewData.bond +++ /dev/null @@ -1,18 +0,0 @@ -import "EventData.bond" - -namespace AI - -[Description("An instance of PageView represents a generic action on a page like a button click. It is also the base type for PageView.")] -[Alias("PageviewData;PageEventData")] -struct PageViewData - : EventData -{ - [MaxStringLength("2048")] - [Description("Request URL with all query string parameters")] - 10: string url; - - [CSType("TimeSpan")] - [Description("Request duration in format: DD.HH:MM:SS.MMMMMM. For a page view (PageViewData), this is the duration. For a page view with performance information (PageViewPerfData), this is the page load time. Must be less than 1000 days.")] - 20: string duration; - -} diff --git a/Schema/PublicSchema/RemoteDependencyData.bond b/Schema/PublicSchema/RemoteDependencyData.bond deleted file mode 100644 index ba21cafc8..000000000 --- a/Schema/PublicSchema/RemoteDependencyData.bond +++ /dev/null @@ -1,53 +0,0 @@ -import "Domain.bond" - -namespace AI - -[Description("An instance of Remote Dependency represents an interaction of the monitored component with a remote component/service like SQL or an HTTP endpoint.")] -struct RemoteDependencyData - : Domain -{ - [Description("Schema version")] - 10: required int32 ver = 2; - - [MaxStringLength("1024")] - [Description("Name of the command initiated with this dependency call. Low cardinality value. Examples are stored procedure name and URL path template.")] - 20: required string name; - - [MaxStringLength("128")] - [Description("Identifier of a dependency call instance. Used for correlation with the request telemetry item corresponding to this dependency call.")] - 30: string id; - - [MaxStringLength("1024")] - [Description("Result code of a dependency call. Examples are SQL error code and HTTP status code.")] - 40: string resultCode; - - [CSType("TimeSpan")] - [Description("Request duration in format: DD.HH:MM:SS.MMMMMM. Must be less than 1000 days.")] - [ActAsRequired("Renaming value to duration.")] - 61: required string duration; - - [Description("Indication of successful or unsuccessful call.")] - 120: nullable success = true; - - [MaxStringLength("8192")] - [Description("Command initiated by this dependency call. Examples are SQL statement and HTTP URL's with all query parameters.")] - 151: string data; - - [MaxStringLength("1024")] - [Description("Dependency type name. Very low cardinality value for logical grouping of dependencies and interpretation of other fields like commandName and resultCode. Examples are SQL, Azure table, and HTTP.")] - 162: string type; - - [MaxStringLength("1024")] - [Description("Target site of a dependency call. Examples are server name, host address.")] - 161: string target; - - [Description("Collection of custom properties.")] - [MaxKeyLength("150")] - [MaxValueLength("8192")] - 200: map properties; - - [Description("Collection of custom measurements.")] - [MaxKeyLength("150")] - 300: map measurements; - -} diff --git a/Schema/PublicSchema/RequestData.bond b/Schema/PublicSchema/RequestData.bond deleted file mode 100644 index 699a86cf5..000000000 --- a/Schema/PublicSchema/RequestData.bond +++ /dev/null @@ -1,48 +0,0 @@ -import "Domain.bond" - -namespace AI - -[Description("An instance of Request represents completion of an external request to the application to do work and contains a summary of that request execution and the results.")] -struct RequestData - : Domain -{ - [Description("Schema version")] - 10: required int32 ver = 2; - - [MaxStringLength("128")] - [Description("Identifier of a request call instance. Used for correlation between request and other telemetry items.")] - 20: required string id; - - [CSType("TimeSpan")] - [Description("Request duration in format: DD.HH:MM:SS.MMMMMM. Must be less than 1000 days.")] - 50: required string duration; - - [MaxStringLength("1024")] - [Description("Result of a request execution. HTTP status code for HTTP requests.")] - 60: required string responseCode; - - [Description("Indication of successful or unsuccessful call.")] - 70: required bool success; - - [MaxStringLength("1024")] - [Description("Source of the request. Examples are the instrumentation key of the caller or the ip address of the caller.")] - 29: string source; - - [MaxStringLength("1024")] - [Description("Name of the request. Represents code path taken to process request. Low cardinality value to allow better grouping of requests. For HTTP requests it represents the HTTP method and URL path template like 'GET /values/{id}'.")] - 30: string name; - - [MaxStringLength("2048")] - [Description("Request URL with all query string parameters.")] - 90: string url; - - [Description("Collection of custom properties.")] - [MaxKeyLength("150")] - [MaxValueLength("8192")] - 100: map properties; - - [Description("Collection of custom measurements.")] - [MaxKeyLength("150")] - 200: map measurements; - -} diff --git a/Schema/PublicSchema/SeverityLevel.bond b/Schema/PublicSchema/SeverityLevel.bond deleted file mode 100644 index f336b1228..000000000 --- a/Schema/PublicSchema/SeverityLevel.bond +++ /dev/null @@ -1,11 +0,0 @@ -namespace AI - -[Description("Defines the level of severity for the event.")] -enum SeverityLevel -{ - Verbose, - Information, - Warning, - Error, - Critical, -} diff --git a/Schema/PublicSchema/StackFrame.bond b/Schema/PublicSchema/StackFrame.bond deleted file mode 100644 index fc8be5c72..000000000 --- a/Schema/PublicSchema/StackFrame.bond +++ /dev/null @@ -1,25 +0,0 @@ - -namespace AI - -[Description("Stack frame information.")] -struct StackFrame -{ - [Description("Level in the call stack. For the long stacks SDK may not report every function in a call stack.")] - 10: required int32 level; - - [Description("Method name.")] - [MaxStringLength("1024")] - 20: required string method; - - [Description("Name of the assembly (dll, jar, etc.) containing this function.")] - [MaxStringLength("1024")] - 30: string assembly; - - [Description("File name or URL of the method implementation.")] - [MaxStringLength("1024")] - 50: string fileName; - - [Description("Line number of the code implementation.")] - 60: int32 line; - -} diff --git a/Schema/generateSchema.ps1 b/Schema/generateSchema.ps1 deleted file mode 100644 index 3a5cc8c01..000000000 --- a/Schema/generateSchema.ps1 +++ /dev/null @@ -1,65 +0,0 @@ -$generatorPath = "C:\src\mseng\AppInsights-Common" -$publicSchemaLocation = "https://raw.githubusercontent.com/Microsoft/ApplicationInsights-Home/master/EndpointSpecs/Schemas/Bond" - - -$currentDir = $scriptPath = split-path -parent $MyInvocation.MyCommand.Definition -#fix path -$generatorPath = "$generatorPath\..\bin\Debug\BondSchemaGenerator\BondSchemaGenerator" - - -##################################################################### -## PUBLIC SCHEMA -##################################################################### - -mkdir -Force $currentDir\PublicSchema - -del "$currentDir\PublicSchema\*.bond" - -$argumentList = "" - -# Download public schema from the github -@( -"AvailabilityData.bond", -"Base.bond", -"ContextTagKeys.bond", -"Data.bond", -"DataPoint.bond", -"DataPointType.bond", -"Domain.bond", -"Envelope.bond", -"EventData.bond", -"ExceptionData.bond", -"ExceptionDetails.bond", -"MessageData.bond", -"MetricData.bond", -"PageViewData.bond", -"RemoteDependencyData.bond", -"RequestData.bond", -"SeverityLevel.bond", -"StackFrame.bond" -) | ForEach-Object { - $fileName = $_ - $argumentList = "$argumentList -i $currentDir\PublicSchema\$fileName" - & Invoke-WebRequest -o "$currentDir\PublicSchema\$fileName" "$publicSchemaLocation/$fileName" -} - -$argumentList = "-v $argumentList -o $currentDir\PublicSchema\ -e TypeScriptLanguage -t TypeScriptLayout -n _IMPORTS_ --flatten true" - -# Generate public schema using bond generator -# Also build an index.ts at the same time -$p1 = Start-Process "$generatorPath\BondSchemaGenerator.exe" -ArgumentList $argumentList -wait -NoNewWindow -PassThru -$p1.HasExited -$p1.ExitCode - -del "$currentDir\..\Declarations\Contracts\Generated\*.ts" - -Add-Content "$currentDir\..\Declarations\Contracts\Generated\index.ts" "// THIS FILE WAS AUTOGENERATED" -Add-Content "$currentDir\..\Declarations\Contracts\Generated\index.ts" "`"use strict`";" -dir "$currentDir\PublicSchema\Contracts\Generated\*.ts" | ForEach-Object { - $fileName = $_ - $baseName = $fileName.BaseName - copy $fileName "$currentDir\..\Declarations\Contracts\Generated\" - Add-Content "$currentDir\..\Declarations\Contracts\Generated\index.ts" "export import $baseName = require(`"./$baseName`");" -} - -del "$currentDir\PublicSchema\Contracts\Generated\*.ts" \ No newline at end of file diff --git a/TelemetryProcessors/AzureRoleEnvironmentTelemetryInitializer.ts b/TelemetryProcessors/AzureRoleEnvironmentTelemetryInitializer.ts deleted file mode 100644 index 2404b8141..000000000 --- a/TelemetryProcessors/AzureRoleEnvironmentTelemetryInitializer.ts +++ /dev/null @@ -1,14 +0,0 @@ -import Contracts = require("../Declarations/Contracts"); -import Context = require("../Library/Context"); - -/** - * A telemetry processor that handles Azure specific variables. - */ -export function azureRoleEnvironmentTelemetryProcessor(envelope: Contracts.EnvelopeTelemetry, context: Context): void { - if (process.env.WEBSITE_SITE_NAME) { // Azure Web apps and Functions - envelope.tags[context.keys.cloudRole] = process.env.WEBSITE_SITE_NAME; - } - if (process.env.WEBSITE_INSTANCE_ID) { - envelope.tags[context.keys.cloudRoleInstance] = process.env.WEBSITE_INSTANCE_ID; - } -} diff --git a/TelemetryProcessors/PerformanceMetricsTelemetryProcessor.ts b/TelemetryProcessors/PerformanceMetricsTelemetryProcessor.ts deleted file mode 100644 index 726587c06..000000000 --- a/TelemetryProcessors/PerformanceMetricsTelemetryProcessor.ts +++ /dev/null @@ -1,27 +0,0 @@ -import Contracts = require("../Declarations/Contracts"); -import QuickPulseStateManager = require("../Library/QuickPulseStateManager") -import AutoCollectPerformance = require("../AutoCollection/Performance"); -import * as TelemetryType from "../Declarations/Contracts"; - -export function performanceMetricsTelemetryProcessor(envelope: Contracts.EnvelopeTelemetry, client?: QuickPulseStateManager): boolean { - // If live metrics is enabled, forward all telemetry there - if (client) { - client.addDocument(envelope); - } - - // Increment rate counters (for standard metrics and live metrics) - switch (envelope.data.baseType) { - case TelemetryType.TelemetryTypeString.Exception: - AutoCollectPerformance.countException(); - break; - case TelemetryType.TelemetryTypeString.Request: - const requestData: Contracts.RequestData = (envelope.data as any).baseData; - AutoCollectPerformance.countRequest(requestData.duration, requestData.success); - break; - case TelemetryType.TelemetryTypeString.Dependency: - const remoteDependencyData: Contracts.RemoteDependencyData = (envelope.data as any).baseData; - AutoCollectPerformance.countDependency(remoteDependencyData.duration, remoteDependencyData.success); - break; - } - return true; -} diff --git a/TelemetryProcessors/PreAggregatedMetricsTelemetryProcessor.ts b/TelemetryProcessors/PreAggregatedMetricsTelemetryProcessor.ts deleted file mode 100644 index 94040d606..000000000 --- a/TelemetryProcessors/PreAggregatedMetricsTelemetryProcessor.ts +++ /dev/null @@ -1,76 +0,0 @@ -import Contracts = require("../Declarations/Contracts"); -import AutoCollecPreAggregatedMetrics = require("../AutoCollection/PreAggregatedMetrics"); -import * as TelemetryType from "../Declarations/Contracts"; -import { - MetricDependencyDimensions, - MetricExceptionDimensions, - MetricRequestDimensions, - MetricTraceDimensions -} from "../Declarations/Metrics/AggregatedMetricDimensions"; -import Context = require("../Library/Context"); - -export function preAggregatedMetricsTelemetryProcessor(envelope: Contracts.EnvelopeTelemetry, context: Context): boolean { - if (AutoCollecPreAggregatedMetrics.isEnabled()) { - // Increment rate counters - switch (envelope.data.baseType) { - case TelemetryType.TelemetryTypeString.Exception: - const exceptionData: Contracts.ExceptionData = (envelope.data as any).baseData; - exceptionData.properties = { - ...exceptionData.properties, - "_MS.ProcessedByMetricExtractors": "(Name:'Exceptions', Ver:'1.1')" - } - let exceptionDimensions: MetricExceptionDimensions = { - cloudRoleInstance: envelope.tags[context.keys.cloudRoleInstance], - cloudRoleName: envelope.tags[context.keys.cloudRole], - }; - AutoCollecPreAggregatedMetrics.countException(exceptionDimensions); - break; - case TelemetryType.TelemetryTypeString.Trace: - const traceData: Contracts.TraceTelemetry = (envelope.data as any).baseData; - traceData.properties = { - ...traceData.properties, - "_MS.ProcessedByMetricExtractors": "(Name:'Traces', Ver:'1.1')" - } - let traceDimensions: MetricTraceDimensions = { - cloudRoleInstance: envelope.tags[context.keys.cloudRoleInstance], - cloudRoleName: envelope.tags[context.keys.cloudRole], - traceSeverityLevel: Contracts.SeverityLevel[traceData.severity], - }; - AutoCollecPreAggregatedMetrics.countTrace(traceDimensions); - break; - case TelemetryType.TelemetryTypeString.Request: - const requestData: Contracts.RequestData = (envelope.data as any).baseData; - requestData.properties = { - ...requestData.properties, - "_MS.ProcessedByMetricExtractors": "(Name:'Requests', Ver:'1.1')" - } - let requestDimensions: MetricRequestDimensions = { - cloudRoleInstance: envelope.tags[context.keys.cloudRoleInstance], - cloudRoleName: envelope.tags[context.keys.cloudRole], - operationSynthetic: envelope.tags[context.keys.operationSyntheticSource], - requestSuccess: requestData.success, - requestResultCode: requestData.responseCode, - }; - AutoCollecPreAggregatedMetrics.countRequest(requestData.duration, requestDimensions); - break; - case TelemetryType.TelemetryTypeString.Dependency: - const remoteDependencyData: Contracts.RemoteDependencyData = (envelope.data as any).baseData; - remoteDependencyData.properties = { - ...remoteDependencyData.properties, - "_MS.ProcessedByMetricExtractors": "(Name:'Dependencies', Ver:'1.1')" - } - let dependencyDimensions: MetricDependencyDimensions = { - cloudRoleInstance: envelope.tags[context.keys.cloudRoleInstance], - cloudRoleName: envelope.tags[context.keys.cloudRole], - operationSynthetic: envelope.tags[context.keys.operationSyntheticSource], - dependencySuccess: remoteDependencyData.success, - dependencyType: remoteDependencyData.type, - dependencyTarget: remoteDependencyData.target, - dependencyResultCode: remoteDependencyData.resultCode, - }; - AutoCollecPreAggregatedMetrics.countDependency(remoteDependencyData.duration, dependencyDimensions); - break; - } - } - return true; -} diff --git a/TelemetryProcessors/SamplingTelemetryProcessor.ts b/TelemetryProcessors/SamplingTelemetryProcessor.ts deleted file mode 100644 index 6acb02801..000000000 --- a/TelemetryProcessors/SamplingTelemetryProcessor.ts +++ /dev/null @@ -1,48 +0,0 @@ -import Contracts = require("../Declarations/Contracts"); -import { CorrelationContext } from "../AutoCollection/CorrelationContextManager"; - -/** - * A telemetry processor that handles sampling. - */ -export function samplingTelemetryProcessor(envelope: Contracts.EnvelopeTelemetry, contextObjects: { correlationContext: CorrelationContext }): boolean { - var samplingPercentage = envelope.sampleRate; // Set for us in Client.getEnvelope - var isSampledIn = false; - - if (samplingPercentage === null || samplingPercentage === undefined || samplingPercentage >= 100) { - return true; - } else if (envelope.data && Contracts.TelemetryType.Metric === Contracts.baseTypeToTelemetryType(envelope.data.baseType as Contracts.TelemetryTypeValues)) { - // Exclude MetricData telemetry from sampling - return true; - } else if (contextObjects.correlationContext && contextObjects.correlationContext.operation) { - // If we're using dependency correlation, sampling should retain all telemetry from a given request - isSampledIn = getSamplingHashCode(contextObjects.correlationContext.operation.id) < samplingPercentage; - } else { - // If we're not using dependency correlation, sampling should use a random distribution on each item - isSampledIn = (Math.random() * 100) < samplingPercentage; - } - - return isSampledIn; -} - -/** Ported from AI .NET SDK */ -export function getSamplingHashCode(input: string): number { - var csharpMin = -2147483648; - var csharpMax = 2147483647; - var hash = 5381; - - if (!input) { - return 0; - } - - while (input.length < 8) { - input = input + input; - } - - for (var i = 0; i < input.length; i++) { - // JS doesn't respond to integer overflow by wrapping around. Simulate it with bitwise operators ( | 0) - hash = ((((hash << 5) + hash) | 0) + input.charCodeAt(i) | 0); - } - - hash = hash <= csharpMin ? csharpMax : Math.abs(hash); - return (hash / csharpMax) * 100; -} \ No newline at end of file diff --git a/TelemetryProcessors/index.ts b/TelemetryProcessors/index.ts deleted file mode 100644 index ecce5c99a..000000000 --- a/TelemetryProcessors/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from "./AzureRoleEnvironmentTelemetryInitializer"; -export * from "./SamplingTelemetryProcessor"; -export * from "./PerformanceMetricsTelemetryProcessor"; -export * from "./PreAggregatedMetricsTelemetryProcessor"; diff --git a/Tests/AutoCollection/Console.tests.ts b/Tests/AutoCollection/Console.tests.ts deleted file mode 100644 index 493357693..000000000 --- a/Tests/AutoCollection/Console.tests.ts +++ /dev/null @@ -1,67 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); -import Console = require("../../AutoCollection/Console") - -import AppInsights = require("../../applicationinsights"); - -import { channel, IStandardEvent } from "diagnostic-channel"; -import { enable, dispose as disable } from "../../AutoCollection/diagnostic-channel/console.sub"; -import { console } from "diagnostic-channel-publishers"; - -describe("AutoCollection/Console", () => { - afterEach(() => { - AppInsights.dispose(); - }); - describe("#init and #dispose()", () => { - it("init should enable and dispose should stop console autocollection", () => { - - var appInsights = AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333").setAutoCollectConsole(true); - var enableConsoleRequestsSpy = sinon.spy(Console.INSTANCE, "enable"); - appInsights.start(); - - assert.equal(enableConsoleRequestsSpy.callCount, 1, "enable should be called once as part of console autocollection initialization"); - assert.equal(enableConsoleRequestsSpy.getCall(0).args[0], true); - AppInsights.dispose(); - assert.equal(enableConsoleRequestsSpy.callCount, 2, "enable(false) should be called once as part of console autocollection shutdown"); - assert.equal(enableConsoleRequestsSpy.getCall(1).args[0], false); - }); - }); - - describe("#log and #error()", () => { - it("should call trackException for errors and trackTrace for logs", () => { - var appInsights = AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - appInsights.start(); - - const trackExceptionStub = sinon.stub(AppInsights.defaultClient, "trackException"); - const trackTraceStub = sinon.stub(AppInsights.defaultClient, "trackTrace"); - - disable(); - enable(true, AppInsights.defaultClient); - const logEvent: console.IConsoleData = { - message: "test log", - stderr: true // should log as MessageData regardless of this setting - }; - const dummyError = new Error("test error"); - const errorEvent: console.IConsoleData = { - message: dummyError as any, - stderr: false, // log() should still log as ExceptionData - }; - - channel.publish("console", logEvent); - assert.ok(trackExceptionStub.notCalled); - assert.ok(trackTraceStub.calledOnce); - assert.deepEqual(trackTraceStub.args[0][0].message, "test log"); - trackExceptionStub.reset(); - trackTraceStub.reset(); - - channel.publish("console", errorEvent); - assert.ok(trackExceptionStub.calledOnce); - assert.ok(trackTraceStub.notCalled); - assert.deepEqual(trackExceptionStub.args[0][0].exception, dummyError); - - disable(); - trackExceptionStub.restore(); - trackTraceStub.restore(); - }); - }); -}); diff --git a/Tests/AutoCollection/CorrelationContextManager.tests.ts b/Tests/AutoCollection/CorrelationContextManager.tests.ts deleted file mode 100644 index a9b9d2cc2..000000000 --- a/Tests/AutoCollection/CorrelationContextManager.tests.ts +++ /dev/null @@ -1,422 +0,0 @@ -import { CorrelationContextManager, CorrelationContext } from "../../AutoCollection/CorrelationContextManager"; -import * as azureFunctionTypes from "../../Library/Functions"; - -import assert = require("assert"); -import sinon = require("sinon"); -import Traceparent = require("../../Library/Traceparent"); -import { SpanContext } from "@opentelemetry/api"; - -const customProperties = { - getProperty(prop: string) { return "" }, - setProperty(prop: string, val: string) { }, -} - -if (CorrelationContextManager.isNodeVersionCompatible()) { - describe("AutoCollection/CorrelationContextManager", () => { - var testContext: CorrelationContext = { - operation: { - id: "test", - name: "test", - parentId: "test" - }, - customProperties - }; - var testContext2: CorrelationContext = { - operation: { - id: "test2", - name: "test2", - parentId: "test2" - }, - customProperties - }; - describe("#enable", () => { - beforeEach(() => { - (CorrelationContextManager as any).hasEverEnabled = false; - (CorrelationContextManager as any).cls = undefined; - CorrelationContextManager.disable(); - }); - afterEach(() => { - (CorrelationContextManager as any).hasEverEnabled = false; - (CorrelationContextManager as any).cls = undefined; - CorrelationContextManager.disable(); - }); - - it("should use cls-hooked if force flag is set to true", () => { - if (CorrelationContextManager.canUseClsHooked()) { - CorrelationContextManager.enable(true); - assert.deepEqual((CorrelationContextManager as any).cls, require('cls-hooked'), 'cls-hooked is loaded'); - assert.notDeepEqual((CorrelationContextManager as any).cls, require('continuation-local-storage')); - } - }); - it("should use continuation-local-storage if force flag is set to false", () => { - CorrelationContextManager.enable(false); - assert.deepEqual((CorrelationContextManager as any).cls, require('continuation-local-storage'), 'cls is loaded'); - if (CorrelationContextManager.canUseClsHooked()) { - assert.notDeepEqual((CorrelationContextManager as any).cls, require('cls-hooked')); - } - }); - it("should pick correct version of cls based on node version", () => { - CorrelationContextManager.enable(); - if (CorrelationContextManager.shouldUseClsHooked()) { - assert.deepEqual((CorrelationContextManager as any).cls, require('cls-hooked'), 'cls-hooked is loaded'); - assert.notDeepEqual((CorrelationContextManager as any).cls, require('continuation-local-storage')); - } else { - assert.deepEqual((CorrelationContextManager as any).cls, require('continuation-local-storage'), 'cls is loaded'); - if (CorrelationContextManager.canUseClsHooked()) { - assert.notDeepEqual((CorrelationContextManager as any).cls, require('cls-hooked')); - } - } - }); - }); - - describe("#getCurrentContext()", () => { - afterEach(() => { - // Mocha's async "done" methods cause future tests to be in the same context chain - // Reset the context each time - CorrelationContextManager.reset(); - assert.equal(null, CorrelationContextManager.getCurrentContext()); - }); - it("should return null if not in a context", () => { - CorrelationContextManager.enable(); - - assert.equal(CorrelationContextManager.getCurrentContext(), null); - }); - it("should return null if the ContextManager is disabled (outside context)", () => { - CorrelationContextManager.disable(); - - assert.equal(CorrelationContextManager.getCurrentContext(), null); - }); - it("should return null if the ContextManager is disabled (inside context)", (done) => { - CorrelationContextManager.enable(); - - CorrelationContextManager.runWithContext(testContext, () => { - CorrelationContextManager.disable(); - assert.equal(CorrelationContextManager.getCurrentContext(), null); - done(); - }); - }); - it("should return the context if in a context", (done) => { - CorrelationContextManager.enable(); - - CorrelationContextManager.runWithContext(testContext, () => { - assert.equal(CorrelationContextManager.getCurrentContext(), testContext); - done(); - }); - }); - it("should return the context if called by an asynchronous callback in a context", (done) => { - CorrelationContextManager.enable(); - - CorrelationContextManager.runWithContext(testContext2, () => { - process.nextTick(() => { - assert.equal(CorrelationContextManager.getCurrentContext(), testContext2); - done(); - }); - }); - }); - it("should return the correct context to asynchronous callbacks occuring in parallel", (done) => { - CorrelationContextManager.enable(); - - CorrelationContextManager.runWithContext(testContext, () => { - process.nextTick(() => { - assert.equal(CorrelationContextManager.getCurrentContext(), testContext); - }); - }); - - CorrelationContextManager.runWithContext(testContext2, () => { - process.nextTick(() => { - assert.equal(CorrelationContextManager.getCurrentContext(), testContext2); - }); - }); - - setTimeout(() => done(), 10); - }); - }); - - describe("#AppInsightsAsyncCorrelatedErrorWrapper", () => { - it("should not crash if prepareStackTrace is used", () => { - CorrelationContextManager.enable(); - - try { - var stackTrace = (Error)['prepareStackTrace']; - (Error)['prepareStackTrace'] = function (_: any, stack: any) { - (Error)['prepareStackTrace'] = stackTrace; - return stack; - }; - - var error = new Error(); - assert(error.stack instanceof Array); - } catch (e) { - assert(false); - } - }); - it("should remove extra AI+Zone methods if prepareStackTrace is used", () => { - CorrelationContextManager.enable(); - - var stackTrace = (Error)['prepareStackTrace']; - (Error)['prepareStackTrace'] = function (_: any, stack: any) { - (Error)['prepareStackTrace'] = stackTrace; - return stack; - }; - - var error = new Error(); - var topOfStack = (error.stack)[0].getFileName(); - assert(topOfStack.indexOf("CorrelationContextManager.tests.js") !== -1, "Top of stack not expected to be " + topOfStack); - }); - it("should not crash on missing filename", () => { - CorrelationContextManager.enable(); - - var stackTrace = (Error)['prepareStackTrace']; - (Error)['prepareStackTrace'] = function (_: any, stack: any): any[] { - return stack; - }; - - var error = new Error(); - try { - (Error)['prepareStackTrace'](null, [{ getFunctionName: () => '', getFileName: (): any => null }]); - (Error)['prepareStackTrace'] = stackTrace; - } catch (e) { - (Error)['prepareStackTrace'] = stackTrace; - assert(false, "prepareStackTrace should not throw. Threw: " + e); - } - }); - }); - - describe("#runWithContext()", () => { - it("should run the supplied function", () => { - CorrelationContextManager.enable(); - var fn = sinon.spy(); - - CorrelationContextManager.runWithContext(testContext, fn); - - assert(fn.calledOnce); - }); - }); - - describe("#wrapCallback()", () => { - it("should return the supplied function if disabled", () => { - CorrelationContextManager.disable(); - var fn = sinon.spy(); - - var wrapped = CorrelationContextManager.wrapCallback(fn); - - assert.equal(wrapped, fn); - }); - it("should return a function that calls the supplied function if enabled", () => { - CorrelationContextManager.enable(); - var fn = sinon.spy(); - - var wrapped = CorrelationContextManager.wrapCallback(fn); - wrapped(); - - assert.notEqual(wrapped, fn); - assert(fn.calledOnce); - }); - it("should return a function that restores the context available at call-time into the supplied function if enabled", (done) => { - CorrelationContextManager.enable(); - - var sharedFn = () => { - assert.equal(CorrelationContextManager.getCurrentContext(), testContext); - }; - - CorrelationContextManager.runWithContext(testContext, () => { - sharedFn = CorrelationContextManager.wrapCallback(sharedFn); - }); - - CorrelationContextManager.runWithContext(testContext2, () => { - setTimeout(() => { - sharedFn(); - }, 8); - }); - - setTimeout(() => done(), 10); - }); - }); - - describe("#startOperation()", () => { - const spanContext: SpanContext = { - traceId: '5e84aff3af474588a42dcbf3bd1db95f', - spanId: '1fc066fb77fa43a3', - traceFlags: 0 - }; - const functionContext: azureFunctionTypes.TraceContext = { - traceparent: "00-5e84aff3af474588a42dcbf3bd1db95f-1fc066fb77fa43a3-00", - tracestate: "", - attributes: {}, - }; - - const request = { - method: "GET", - url: "/search", - connection: { - encrypted: false - }, - headers: { - host: "bing.com", - traceparent: functionContext.traceparent, - }, - query: { q: 'test' }, - params: {} - }; - - describe("#Azure Functions", () => { - it("should start a new context with 2nd arg http request", () => { - const context = CorrelationContextManager.startOperation({ traceContext: functionContext } as any, request) - const traceparent = new Traceparent(functionContext.traceparent); - - assert.ok(context.operation); - assert.deepEqual(context.operation.id, traceparent.traceId); - assert.deepEqual(context.operation.parentId, `|${traceparent.traceId}.${traceparent.spanId}.`); - assert.deepEqual(context.operation.traceparent, traceparent); - assert.deepEqual(context.operation.name, "GET /search"); - }); - - it("should start a new context with 2nd arg string", () => { - const context = CorrelationContextManager.startOperation({ traceContext: functionContext } as any, "GET /foo") - const traceparent = new Traceparent(functionContext.traceparent); - - assert.ok(context.operation); - assert.deepEqual(context.operation.id, traceparent.traceId); - assert.deepEqual(context.operation.parentId, `|${traceparent.traceId}.${traceparent.spanId}.`); - assert.deepEqual(context.operation.traceparent, traceparent); - assert.deepEqual(context.operation.name, "GET /foo"); - }); - }); - - describe("#SpanContext", () => { - it("should start a new context", () => { - const context = CorrelationContextManager.startOperation(spanContext, "GET /example"); - - assert.ok(context.operation); - assert.deepEqual(context.operation.id, spanContext.traceId); - assert.deepEqual(context.operation.parentId, context.operation.parentId); - assert.deepEqual(context.operation.name, "GET /example"); - }); - }); - }); - }); -} else { - describe("AutoCollection/CorrelationContextManager[IncompatibleVersion!]", () => { - var testContext: CorrelationContext = { - operation: { - id: "test", - name: "test", - parentId: "test" - }, - customProperties - }; - var testContext2: CorrelationContext = { - operation: { - id: "test2", - name: "test2", - parentId: "test2" - }, - customProperties - }; - - describe("#getCurrentContext()", () => { - it("should return null if not in a context", () => { - CorrelationContextManager.enable(); - - assert.equal(CorrelationContextManager.getCurrentContext(), null); - }); - it("should return null if the ContextManager is disabled (outside context)", () => { - CorrelationContextManager.disable(); - - assert.equal(CorrelationContextManager.getCurrentContext(), null); - }); - it("should return null if the ContextManager is disabled (inside context)", (done) => { - CorrelationContextManager.enable(); - - CorrelationContextManager.runWithContext(testContext, ()=>{ - CorrelationContextManager.disable(); - assert.equal(CorrelationContextManager.getCurrentContext(), null); - done(); - }); - }); - it("should return null if in a context", (done) => { - CorrelationContextManager.enable(); - - CorrelationContextManager.runWithContext(testContext, ()=>{ - assert.equal(CorrelationContextManager.getCurrentContext(), null); - done(); - }); - }); - it("should return null if called by an asynchronous callback in a context", (done) => { - CorrelationContextManager.enable(); - - CorrelationContextManager.runWithContext(testContext, ()=>{ - process.nextTick(()=>{ - assert.equal(CorrelationContextManager.getCurrentContext(), null); - done(); - }); - }); - }); - it("should return null to asynchronous callbacks occuring in parallel", (done) => { - CorrelationContextManager.enable(); - - CorrelationContextManager.runWithContext(testContext, ()=>{ - process.nextTick(()=>{ - assert.equal(CorrelationContextManager.getCurrentContext(), null); - }); - }); - - CorrelationContextManager.runWithContext(testContext2, ()=>{ - process.nextTick(()=>{ - assert.equal(CorrelationContextManager.getCurrentContext(), null); - }); - }); - - setTimeout(()=>done(), 10); - }); - }); - - describe("#runWithContext()", () => { - it("should run the supplied function", () => { - CorrelationContextManager.enable(); - var fn = sinon.spy(); - - CorrelationContextManager.runWithContext(testContext, fn); - - assert(fn.calledOnce); - }); - }); - - describe("#wrapCallback()", () => { - it("should return the supplied function if disabled", () => { - CorrelationContextManager.disable(); - var fn = sinon.spy(); - - var wrapped = CorrelationContextManager.wrapCallback(fn); - - assert.equal(wrapped, fn); - }); - it("should return the supplied function if enabled", () => { - CorrelationContextManager.enable(); - var fn = sinon.spy(); - - var wrapped = CorrelationContextManager.wrapCallback(fn); - - assert.equal(wrapped, fn); - }); - it("should not return a function that restores a null context at call-time into the supplied function if enabled", (done) => { - CorrelationContextManager.enable(); - - var sharedFn = ()=> { - assert.equal(CorrelationContextManager.getCurrentContext(), null); - }; - - CorrelationContextManager.runWithContext(testContext, ()=>{ - sharedFn = CorrelationContextManager.wrapCallback(sharedFn); - }); - - CorrelationContextManager.runWithContext(testContext2, ()=>{ - setTimeout(()=>{ - sharedFn(); - }, 8); - }); - - setTimeout(()=>done(), 10); - }); - }); - }); -} diff --git a/Tests/AutoCollection/Exceptions.tests.ts b/Tests/AutoCollection/Exceptions.tests.ts deleted file mode 100644 index cc582c9a5..000000000 --- a/Tests/AutoCollection/Exceptions.tests.ts +++ /dev/null @@ -1,39 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); - -import AutoCollectionExceptions = require("../../AutoCollection/Exceptions"); -import Client = require("../../Library/TelemetryClient"); -import AppInsights = require("../../applicationinsights"); - -describe("AutoCollection/Exceptions", () => { - describe("#init and dispose()", () => { - afterEach(() => { - AppInsights.dispose(); - }); - - it("should use uncaughtExceptionMonitor for node 13.7.0+", () => { - var nodeVer = process.versions.node.split("."); - var expectation = parseInt(nodeVer[0]) > 13 || (parseInt(nodeVer[0]) === 13 && parseInt(nodeVer[1]) >= 7); - var exceptions = new AutoCollectionExceptions(null); - assert.equal(AutoCollectionExceptions["_canUseUncaughtExceptionMonitor"], expectation); - }); - - it("disables autocollection", () => { - var processOnSpy = sinon.spy(global.process, "on"); - var processRemoveListenerSpy = sinon.spy(global.process, "removeListener"); - - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333").setAutoCollectExceptions(true).start(); - - if (AutoCollectionExceptions["_canUseUncaughtExceptionMonitor"]) { - assert.equal(processOnSpy.callCount, 1, "After enabling exception autocollection, there should be 1 call to processOnSpy"); - assert.equal(processOnSpy.getCall(0).args[0], AutoCollectionExceptions.UNCAUGHT_EXCEPTION_MONITOR_HANDLER_NAME); - } else { - assert.equal(processOnSpy.callCount, 2, "After enabling exception autocollection, there should be 2 calls to processOnSpy"); - assert.equal(processOnSpy.getCall(0).args[0], AutoCollectionExceptions.UNCAUGHT_EXCEPTION_HANDLER_NAME); - assert.equal(processOnSpy.getCall(1).args[0], AutoCollectionExceptions.UNHANDLED_REJECTION_HANDLER_NAME); - } - processOnSpy.restore(); - processRemoveListenerSpy.restore(); - }); - }); -}); diff --git a/Tests/AutoCollection/Heartbeat.tests.ts b/Tests/AutoCollection/Heartbeat.tests.ts deleted file mode 100644 index 007c0ed39..000000000 --- a/Tests/AutoCollection/Heartbeat.tests.ts +++ /dev/null @@ -1,109 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); -import os = require("os"); - -import AppInsights = require("../../applicationinsights"); -import HeartBeat = require("../../AutoCollection/HeartBeat"); -import TelemetryClient = require("../../Library/TelemetryClient"); -import Context = require("../../Library/Context"); -import { JsonConfig } from "../../Library/JsonConfig"; - -describe("AutoCollection/HeartBeat", () => { - var sandbox: sinon.SinonSandbox; - let originalEnv: NodeJS.ProcessEnv; - const client = new TelemetryClient("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - client.config.correlationId = "testicd"; - - beforeEach(() => { - originalEnv = process.env; - sandbox = sinon.sandbox.create(); - }); - - afterEach(() => { - process.env = originalEnv; - AppInsights.dispose(); - sandbox.restore(); - }); - - describe("#init and #dispose()", () => { - it("init should enable and dispose should stop autocollection interval", () => { - JsonConfig["_instance"] = undefined; - var env = <{ [id: string]: string }>{}; - env["APPLICATION_INSIGHTS_NO_STATSBEAT"] = "true"; - process.env = env; - var setIntervalSpy = sandbox.spy(global, "setInterval"); - var clearIntervalSpy = sandbox.spy(global, "clearInterval"); - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333") - .setAutoCollectPreAggregatedMetrics(false) - .setAutoCollectPerformance(false, false) - .setAutoCollectHeartbeat(true) - .start(); - assert.equal(setIntervalSpy.callCount, 1, "setInterval should be called as part of heartbeat initialization"); - AppInsights.dispose(); - assert.equal(clearIntervalSpy.callCount, 1, "clearInterval should be called once as part of heartbeat shutdown"); - }); - }); - - describe("#trackHeartBeat()", () => { - it("should read correct web app values from envrionment variable", (done) => { - const heartbeat1: HeartBeat = new HeartBeat(client); - heartbeat1.enable(true); - HeartBeat.INSTANCE.enable(true); - const stub1 = sandbox.stub(heartbeat1["_client"], "trackMetric"); - - var env1 = <{ [id: string]: string }>{}; - - env1["WEBSITE_SITE_NAME"] = "site_name"; - env1["WEBSITE_HOME_STAMPNAME"] = "stamp_name"; - env1["WEBSITE_HOSTNAME"] = "host_name"; - process.env = env1; - - heartbeat1["trackHeartBeat"](client.config, () => { - assert.equal(stub1.callCount, 1, "should call trackMetric for the appSrv heartbeat metric"); - assert.equal(stub1.args[0][0].name, "HeartBeat", "should use correct name for heartbeat metric"); - assert.equal(stub1.args[0][0].value, 0, "value should be 0"); - const keys1 = Object.keys(stub1.args[0][0].properties); - assert.equal(keys1.length, 5, "should have 5 kv pairs added when resource type is appSrv"); - assert.equal(keys1[0], "sdk", "sdk should be added as a key"); - assert.equal(keys1[1], "osType", "osType should be added as a key"); - assert.equal(keys1[2], "appSrv_SiteName", "appSrv_SiteName should be added as a key"); - assert.equal(keys1[3], "appSrv_wsStamp", "appSrv_wsStamp should be added as a key"); - assert.equal(keys1[4], "appSrv_wsHost", "appSrv_wsHost should be added as a key"); - const properties1 = stub1.args[0][0].properties; - assert.equal(properties1["sdk"], Context.sdkVersion, "sdk version should be read from Context"); - assert.equal(properties1["osType"], os.type(), "osType should be read from os library"); - assert.equal(properties1["appSrv_SiteName"], "site_name", "appSrv_SiteName should be read from envrionment variable"); - assert.equal(properties1["appSrv_wsStamp"], "stamp_name", "appSrv_wsStamp should be read from envrionment variable"); - assert.equal(properties1["appSrv_wsHost"], "host_name", "appSrv_wsHost should be read from envrionment variable"); - done(); - }); - }); - - it("should read correct function app values from envrionment variable", (done) => { - const heartbeat2: HeartBeat = new HeartBeat(client); - heartbeat2.enable(true); - HeartBeat.INSTANCE.enable(true); - const stub2 = sandbox.stub(heartbeat2["_client"], "trackMetric"); - var env2 = <{ [id: string]: string }>{}; - env2["FUNCTIONS_WORKER_RUNTIME"] = "nodejs"; - env2["WEBSITE_HOSTNAME"] = "host_name"; - process.env = env2; - - heartbeat2["trackHeartBeat"](client.config, () => { - assert.equal(stub2.callCount, 1, "should call trackMetric for the VM heartbeat metric"); - assert.equal(stub2.args[0][0].name, "HeartBeat", "should use correct name for heartbeat metric"); - assert.equal(stub2.args[0][0].value, 0, "value should be 0"); - const keys2 = Object.keys(stub2.args[0][0].properties); - assert.equal(keys2.length, 3, "should have 3 kv pairs added when resource type is functiona app"); - assert.equal(keys2[0], "sdk", "sdk should be added as a key"); - assert.equal(keys2[1], "osType", "osType should be added as a key"); - assert.equal(keys2[2], "azfunction_appId", "azfunction_appId should be added as a key"); - const properties2 = stub2.args[0][0].properties; - assert.equal(properties2["sdk"], Context.sdkVersion, "sdk version should be read from Context"); - assert.equal(properties2["osType"], os.type(), "osType should be read from os library"); - assert.equal(properties2["azfunction_appId"], "host_name", "azfunction_appId should be read from envrionment variable"); - done(); - }); - }); - }); -}); \ No newline at end of file diff --git a/Tests/AutoCollection/HttpDependencies.tests.ts b/Tests/AutoCollection/HttpDependencies.tests.ts deleted file mode 100644 index fe7cfe092..000000000 --- a/Tests/AutoCollection/HttpDependencies.tests.ts +++ /dev/null @@ -1,122 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); -import HttpDependencies = require("../../AutoCollection/HttpDependencies") -import Traceparent = require("../../Library/Traceparent"); -import Tracestate = require("../../Library/Tracestate"); - -import AppInsights = require("../../applicationinsights"); -import { CorrelationContextManager } from "../../AutoCollection/CorrelationContextManager"; - -describe("AutoCollection/HttpDependencies", () => { - afterEach(() => { - AppInsights.dispose(); - }); - describe("#init and #dispose()", () => { - it("init should enable and dispose should stop dependencies autocollection", () => { - - var appInsights = AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333").setAutoCollectDependencies(true); - var enableHttpDependenciesSpy = sinon.spy(HttpDependencies.INSTANCE, "enable"); - appInsights.start(); - - assert.equal(enableHttpDependenciesSpy.callCount, 1, "enable should be called once as part of dependencies autocollection initialization"); - assert.equal(enableHttpDependenciesSpy.getCall(0).args[0], true); - AppInsights.dispose(); - assert.equal(enableHttpDependenciesSpy.callCount, 2, "enable(false) should be called once as part of dependencies autocollection shutdown"); - assert.equal(enableHttpDependenciesSpy.getCall(1).args[0], false); - }); - }); - describe("#trackRequest", () => { - var telemetry = { - options: {}, - request: { - headers: <{ [key: string]: any }>{}, - getHeader: function (name: string) { return this.headers[name] }, - setHeader: function (name: string, value: any) { this.headers[name] = value }, - clearHeaders: function () { this.headers = {} } - } - } - - afterEach(() => { - AppInsights.dispose(); - telemetry.request.clearHeaders(); - }); - it("should accept string request-context", () => { - var appInsights = AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333").setAutoCollectDependencies(true); - AppInsights.defaultClient.config.correlationId = "abcdefg"; - appInsights.start(); - - telemetry.request.setHeader("request-context", "appId=cid-v1:aaaaed48-297a-4ea2-af46-0a5a5d26aaaa"); - assert.doesNotThrow(() => HttpDependencies.trackRequest(AppInsights.defaultClient, telemetry as any)); - }); - - it("should accept nonstring request-context", () => { - var appInsights = AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333").setAutoCollectDependencies(true); - AppInsights.defaultClient.config.correlationId = "abcdefg"; - appInsights.start(); - - telemetry.request.setHeader("request-context", ["appId=cid-v1:aaaaed48-297a-4ea2-af46-0a5a5d26aaaa"]); - assert.doesNotThrow(() => HttpDependencies.trackRequest(AppInsights.defaultClient, telemetry as any)); - assert.deepEqual(telemetry.request.getHeader("request-context"), ["appId=cid-v1:aaaaed48-297a-4ea2-af46-0a5a5d26aaaa"], "does not modify valid appId header") - - const myCustomObject = { foo: { bar: "appId=cid-v1:aaaaed48-297a-4ea2-af46-0a5a5d26aaaa" } }; - myCustomObject.toString = () => myCustomObject.foo.bar; - telemetry.request.setHeader("request-context", myCustomObject); - assert.doesNotThrow(() => HttpDependencies.trackRequest(AppInsights.defaultClient, telemetry as any)); - assert.equal(telemetry.request.getHeader("request-context"), myCustomObject.toString(), "does not modify valid appId header"); - - telemetry.request.setHeader("request-context", 123); - assert.doesNotThrow(() => HttpDependencies.trackRequest(AppInsights.defaultClient, telemetry as any)); - assert.ok(telemetry.request.getHeader("request-context").indexOf("abcdefg") !== -1) - - telemetry.request.setHeader("request-context", { foo: 'bar' }); - assert.doesNotThrow(() => HttpDependencies.trackRequest(AppInsights.defaultClient, telemetry as any)); - assert.ok(telemetry.request.getHeader("request-context").indexOf("abcdefg") !== -1) - }); - - it("should add AI correlation headers to request", () => { - var appInsights = AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333") - .setAutoCollectDependencies(true) - .setAutoDependencyCorrelation(true) - .setDistributedTracingMode(AppInsights.DistributedTracingModes.AI); - AppInsights.defaultClient.config.correlationId = "abcdefg"; - appInsights.start(); - let testContext = CorrelationContextManager.generateContextObject("testOperationId", "testParentId", "testOperationName"); - CorrelationContextManager.runWithContext(testContext, () => { - assert.doesNotThrow(() => HttpDependencies.trackRequest(AppInsights.defaultClient, telemetry as any)); - assert.deepEqual(telemetry.request.getHeader("request-id"), "testParentId1."); - // Legacy headers - assert.deepEqual(telemetry.request.getHeader("x-ms-request-id"), "testOperationId"); - assert.deepEqual(telemetry.request.getHeader("x-ms-request-root-id"), "testParentId1."); - // W3C headers not present - assert.deepEqual(telemetry.request.getHeader("traceparent"), undefined); - assert.deepEqual(telemetry.request.getHeader("tracestate"), undefined); - }); - }); - - it("should add W3C correlation headers to request", () => { - var appInsights = AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333") - .setAutoCollectDependencies(true) - .setAutoDependencyCorrelation(true) - .setDistributedTracingMode(AppInsights.DistributedTracingModes.AI_AND_W3C); - AppInsights.defaultClient.config.correlationId = "abcdefg"; - AppInsights.defaultClient.config.ignoreLegacyHeaders = true; - appInsights.start(); - - const traceparent = new Traceparent("00-5e84aff3af474588a42dcbf3bd1db95f-1fc066fb77fa43a3-00"); - const tracestate = new Tracestate("test=testvalue"); - let testContext = CorrelationContextManager.generateContextObject("testOperationId", "testParentId", "testOperationName", null, traceparent, tracestate); - CorrelationContextManager.runWithContext(testContext, () => { - assert.doesNotThrow(() => HttpDependencies.trackRequest(AppInsights.defaultClient, telemetry as any)); - // AI header - assert.ok(telemetry.request.getHeader("request-id").match(/^\|[0-z]{32}\.[0-z]{16}\./g)); - // W3C headers - assert.ok(telemetry.request.getHeader("traceparent").match(/^00-5e84aff3af474588a42dcbf3bd1db95f-[0-z]{16}-00$/)); - assert.notEqual(telemetry.request.getHeader("traceparent"), "00-5e84aff3af474588a42dcbf3bd1db95f-1fc066fb77fa43a3-00"); - assert.equal(telemetry.request.getHeader("tracestate"), "test=testvalue"); - // Legacy headers not present - assert.deepEqual(telemetry.request.getHeader("x-ms-request-id"), undefined); - assert.deepEqual(telemetry.request.getHeader("x-ms-request-root-id"), undefined); - }); - }); - }); -}); \ No newline at end of file diff --git a/Tests/AutoCollection/HttpDependencyParser.tests.ts b/Tests/AutoCollection/HttpDependencyParser.tests.ts deleted file mode 100644 index cbd64b7af..000000000 --- a/Tests/AutoCollection/HttpDependencyParser.tests.ts +++ /dev/null @@ -1,243 +0,0 @@ -import http = require("http"); -import assert = require("assert"); -import sinon = require("sinon"); - -import HttpDependencyParser = require("../../AutoCollection/HttpDependencyParser"); -import Contracts = require("../../Declarations/Contracts"); - -describe("AutoCollection/HttpDependencyParser", () => { - - describe("#getDependencyData()", () => { - let request: http.ClientRequest = { - agent: { protocol: "http" }, - }; - let response: http.ClientResponse = { - }; - - it("should return correct data for a URL string", () => { - (request)["method"] = "GET"; - let parser = new HttpDependencyParser("http://bing.com/search", request); - - response.statusCode = 200; - parser.onResponse(response); - - let dependencyTelemetry = parser.getDependencyTelemetry(); - assert.equal(dependencyTelemetry.dependencyTypeName, Contracts.RemoteDependencyDataConstants.TYPE_HTTP); - assert.equal(dependencyTelemetry.success, true); - assert.equal(dependencyTelemetry.name, "GET /search"); - assert.equal(dependencyTelemetry.data, "http://bing.com/search"); - assert.equal(dependencyTelemetry.target, "bing.com"); - }); - - it("should return correct data for a URL string with correlationId", () => { - (request)["method"] = "GET"; - let parser = new HttpDependencyParser("http://bing.com:123/search", request); - - response.statusCode = 200; - parser.onResponse(response); - - parser["correlationId"] = "abcdefg"; - let dependencyTelemetry = parser.getDependencyTelemetry(); - assert.equal(dependencyTelemetry.dependencyTypeName, Contracts.RemoteDependencyDataConstants.TYPE_AI); - assert.equal(dependencyTelemetry.success, true); - assert.equal(dependencyTelemetry.name, "GET /search"); - assert.equal(dependencyTelemetry.data, "http://bing.com:123/search"); - assert.equal(dependencyTelemetry.target, "bing.com:123 | abcdefg"); - }); - - it("should return correct data for a URL without a protocol (https)", () => { - (request)["method"] = "GET"; - let parser = new HttpDependencyParser("a.bing.com:443/search", request); - - response.statusCode = 200; - parser.onResponse(response); - - let dependencyTelemetry = parser.getDependencyTelemetry(); - assert.equal(dependencyTelemetry.dependencyTypeName, Contracts.RemoteDependencyDataConstants.TYPE_HTTP); - assert.equal(dependencyTelemetry.success, true); - assert.equal(dependencyTelemetry.name, "GET /search"); - assert.equal(dependencyTelemetry.data, "https://a.bing.com/search"); - assert.equal(dependencyTelemetry.target, "a.bing.com"); - }); - - it("should return correct data for a URL without a protocol (http)", () => { - (request)["method"] = "GET"; - let parser = new HttpDependencyParser("a.bing.com:123/search", request); - - response.statusCode = 200; - parser.onResponse(response); - - let dependencyTelemetry = parser.getDependencyTelemetry(); - assert.equal(dependencyTelemetry.dependencyTypeName, Contracts.RemoteDependencyDataConstants.TYPE_HTTP); - assert.equal(dependencyTelemetry.success, true); - assert.equal(dependencyTelemetry.name, "GET /search"); - assert.equal(dependencyTelemetry.data, "http://a.bing.com:123/search"); - assert.equal(dependencyTelemetry.target, "a.bing.com:123"); - }); - - if (parseInt(process.versions.node.split(".")[0]) >= 10) { - it("should return correct data for a URL instance", () => { - (request)["method"] = "GET"; - let parser = new HttpDependencyParser(new URL("http://bing.com/search"), request); - - response.statusCode = 200; - parser.onResponse(response); - - let dependencyTelemetry = parser.getDependencyTelemetry(); - assert.equal(dependencyTelemetry.dependencyTypeName, Contracts.RemoteDependencyDataConstants.TYPE_HTTP); - assert.equal(dependencyTelemetry.success, true); - assert.equal(dependencyTelemetry.name, "GET /search"); - assert.equal(dependencyTelemetry.data, "http://bing.com/search"); - assert.equal(dependencyTelemetry.target, "bing.com"); - }); - } - - it("should propagate a custom timestamp", () => { - (request)["method"] = "GET"; - let parser = new HttpDependencyParser("http://bing.com/search", request); - - response.statusCode = 200; - parser.onResponse(response); - - const dependencyTelemetry1 = parser.getDependencyTelemetry({ time: new Date(111111) }); - const dependencyTelemetry2 = parser.getDependencyTelemetry({ time: new Date(222222) }); - assert.deepEqual(dependencyTelemetry1.time, new Date(111111)); - assert.deepEqual(dependencyTelemetry2.time, new Date(222222)); - assert.notDeepEqual(dependencyTelemetry1, dependencyTelemetry2); - }); - - it("should return correct data for a posted URL with query string", () => { - (request)["method"] = "POST"; - let parser = new HttpDependencyParser("http://bing.com/search?q=test", request); - - response.statusCode = 200; - parser.onResponse(response); - - let dependencyTelemetry = parser.getDependencyTelemetry(); - assert.equal(dependencyTelemetry.dependencyTypeName, Contracts.RemoteDependencyDataConstants.TYPE_HTTP); - assert.equal(dependencyTelemetry.success, true); - assert.equal(dependencyTelemetry.name, "POST /search"); - assert.equal(dependencyTelemetry.data, "http://bing.com/search?q=test"); - assert.equal(dependencyTelemetry.target, "bing.com"); - }); - - it("should return correct data for a request options object", () => { - let requestOptions = { - host: "bing.com", - port: 8000, - path: "/search?q=test", - }; - (request)["method"] = "POST"; - let parser = new HttpDependencyParser(requestOptions, request); - - response.statusCode = 200; - parser.onResponse(response); - - let dependencyTelemetry = parser.getDependencyTelemetry(); - assert.equal(dependencyTelemetry.dependencyTypeName, Contracts.RemoteDependencyDataConstants.TYPE_HTTP); - assert.equal(dependencyTelemetry.success, true); - assert.equal(dependencyTelemetry.name, "POST /search"); - assert.equal(dependencyTelemetry.data, "http://bing.com:8000/search?q=test"); - assert.equal(dependencyTelemetry.target, "bing.com:8000"); - }); - - it("should return correct data for a request options object with a hostname but no host", () => { - let requestOptions = { - hostname: "bing.com", - port: 8000, - path: "/search?q=test", - }; - (request)["method"] = "POST"; - let parser = new HttpDependencyParser(requestOptions, request); - - response.statusCode = 200; - parser.onResponse(response); - - let dependencyTelemetry = parser.getDependencyTelemetry(); - assert.equal(dependencyTelemetry.dependencyTypeName, Contracts.RemoteDependencyDataConstants.TYPE_HTTP); - assert.equal(dependencyTelemetry.success, true); - assert.equal(dependencyTelemetry.name, "POST /search"); - assert.equal(dependencyTelemetry.data, "http://bing.com:8000/search?q=test"); - assert.equal(dependencyTelemetry.target, "bing.com:8000"); - }); - - it("should return correct data for URL with protocol in request", () => { - let testRequest: http.ClientRequest = { - agent: { protocol: undefined }, - method: "GET", - protocol: "https:" - }; - let requestOptions = { - host: "bing.com", - port: 8000, - path: "/search?q=test", - }; - - let parser = new HttpDependencyParser(requestOptions, testRequest); - response.statusCode = 200; - parser.onResponse(response); - let dependencyTelemetry = parser.getDependencyTelemetry(); - assert.equal(dependencyTelemetry.dependencyTypeName, Contracts.RemoteDependencyDataConstants.TYPE_HTTP); - assert.equal(dependencyTelemetry.success, true); - assert.equal(dependencyTelemetry.name, "GET /search"); - assert.equal(dependencyTelemetry.data, "https://bing.com:8000/search?q=test"); - assert.equal(dependencyTelemetry.target, "bing.com:8000"); - }); - - it("should return correct data for a request options object", () => { - var path = "/finance/info?client=ig&q="; - - let requestOptions = { - host: "finance.google.com", - path: path + "msft" - }; - (request)["method"] = "GET"; - let parser = new HttpDependencyParser(requestOptions, request); - - response.statusCode = 200; - parser.onResponse(response); - - let dependencyTelemetry = parser.getDependencyTelemetry(); - assert.equal(dependencyTelemetry.dependencyTypeName, Contracts.RemoteDependencyDataConstants.TYPE_HTTP); - assert.equal(dependencyTelemetry.success, true); - assert.equal(dependencyTelemetry.name, "GET /finance/info"); - assert.equal(dependencyTelemetry.data, "http://finance.google.com/finance/info?client=ig&q=msft"); - assert.equal(dependencyTelemetry.target, "finance.google.com"); - }); - - it("should return non-success for a request error", () => { - (request)["method"] = "GET"; - let parser = new HttpDependencyParser("http://bing.com/search", request); - parser.onError(new Error("test error message")); - - let dependencyTelemetry = parser.getDependencyTelemetry(); - assert.equal(dependencyTelemetry.dependencyTypeName, Contracts.RemoteDependencyDataConstants.TYPE_HTTP); - assert.equal(dependencyTelemetry.success, false); - assert.ok(dependencyTelemetry.properties); - assert.equal(dependencyTelemetry.properties.error, "test error message"); - }); - - it("should return non-success for a response error status", () => { - (request)["method"] = "GET"; - let parser = new HttpDependencyParser("http://bing.com/search", request); - - response.statusCode = 400; - parser.onResponse(response); - - let dependencyTelemetry = parser.getDependencyTelemetry(); - assert.equal(dependencyTelemetry.dependencyTypeName, Contracts.RemoteDependencyDataConstants.TYPE_HTTP); - assert.equal(dependencyTelemetry.success, false); - }); - - it("should return non-success for a request abort", () => { - (request)["method"] = "GET"; - let parser = new HttpDependencyParser("http://bing.com/search", request); - parser.onError(new Error()); - - let dependencyTelemetry = parser.getDependencyTelemetry(); - assert.equal(dependencyTelemetry.dependencyTypeName, Contracts.RemoteDependencyDataConstants.TYPE_HTTP); - assert.equal(dependencyTelemetry.success, false); - assert.ok(dependencyTelemetry.properties); - }); - }); -}); diff --git a/Tests/AutoCollection/HttpRequestParser.tests.ts b/Tests/AutoCollection/HttpRequestParser.tests.ts deleted file mode 100644 index a10ad9c1d..000000000 --- a/Tests/AutoCollection/HttpRequestParser.tests.ts +++ /dev/null @@ -1,249 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); - -import HttpRequestParser = require("../../AutoCollection/HttpRequestParser"); -import CorrelationIdManager = require("../../Library/CorrelationIdManager"); -import Util = require("../../Library/Util"); -import Traceparent = require("../../Library/Traceparent"); - -describe("AutoCollection/HttpRequestParser", () => { - describe("#parseId()", () => { - it("should extract guid out of cookie", () => { - var cookieValue = "id|1234|1234"; - var actual = HttpRequestParser.parseId(cookieValue); - assert.equal("id", actual, "id in cookie is parsed correctly"); - }); - }); - - describe("#w3c", () => { - var backCompatFormat = /^\|[0-z]{32}\.[0-z]{16}\./g; // |traceId.spanId. - var request = { - method: "GET", - url: "/search?q=test", - connection: { - encrypted: false - }, - headers: { - host: "bing.com" - } - }; - - var w3cRequest = {...request, headers: {...request.headers, traceparent: "00-26130040769d49c4826831c978e85131-96665a1c28c5482e-00"}}; - var legacyRequest = {...request, headers: {...request.headers, "request-id": "|abc.def."}}; - var legacyRequestW3C = {...request, headers: {...request.headers, "request-id": "|26130040769d49c4826831c978e85131.96665a1c28c5482e."}}; - var legacyRequestUnique = {...request, headers: {...request.headers, "request-id": "abc"}}; - var legacyRequestUniqueW3C = {...request, headers: {...request.headers, "request-id": "26130040769d49c4826831c978e85131"}}; - - before(() => { - CorrelationIdManager.w3cEnabled = true; - }); - - after(() => { - CorrelationIdManager.w3cEnabled = false; - }); - - it("should parse traceparent if it is available and w3c tracing is enabled", () => { - var helper = new HttpRequestParser(w3cRequest); - var requestTags = helper.getRequestTags({}); - assert.equal(requestTags[(HttpRequestParser).keys.operationId], "26130040769d49c4826831c978e85131"); - assert.equal(requestTags[(HttpRequestParser).keys.operationParentId], "|26130040769d49c4826831c978e85131.96665a1c28c5482e."); - assert.ok(Traceparent.isValidSpanId(helper["traceparent"].spanId)); - }); - - it("if w3c tracing is enabled and !traceparent && request-id ~ |X.Y., generate traceparent", () => { - var helper = new HttpRequestParser(legacyRequest); - var requestTags = helper.getRequestTags({}); - assert.equal(helper["legacyRootId"], "abc"); - assert.equal(helper["parentId"], legacyRequest.headers["request-id"]); - assert.ok(helper["requestId"].match(backCompatFormat)); - assert.ok(Util.isValidW3CId(requestTags[(HttpRequestParser).keys.operationId])); - assert.ok(Util.isValidW3CId(helper["requestId"].substr(1, 32))); - const traceparent = helper["traceparent"]; - assert.equal(traceparent.version, Traceparent["DEFAULT_VERSION"]); - assert.ok(Util.isValidW3CId(traceparent.traceId)); - assert.ok(Traceparent.isValidSpanId(traceparent.spanId)); - assert.notEqual(traceparent.traceId, traceparent.spanId); - assert.equal(traceparent.traceFlag, Traceparent["DEFAULT_TRACE_FLAG"]); - }); - - it("if w3c tracing is enabled and request-id in format of X", () => { - var helper = new HttpRequestParser(legacyRequestUnique); - var requestTags = helper.getRequestTags({}); - assert.equal(helper["parentId"], legacyRequestUnique.headers["request-id"], "parentId is same as request-id"); - assert.ok(helper["requestId"].match(backCompatFormat)); - assert.equal(helper["legacyRootId"], "abc"); - assert.ok(Util.isValidW3CId(requestTags[(HttpRequestParser).keys.operationId])); - const traceparent = helper["traceparent"]; - assert.equal(traceparent.version, Traceparent["DEFAULT_VERSION"]); - assert.ok(Util.isValidW3CId(traceparent.traceId)); - assert.ok(Traceparent.isValidSpanId(traceparent.spanId)); - assert.notEqual(traceparent.traceId, traceparent.spanId); - assert.equal(traceparent.traceFlag, Traceparent["DEFAULT_TRACE_FLAG"]); - }); - - it("should generate a traceparent if both tracing headers are not present (p4)", () => { - var helper = new HttpRequestParser(request); - var requestTags = helper.getRequestTags({}); - assert.ok(!helper["parentId"]); - assert.ok(helper["requestId"]); - assert.ok(helper["requestId"].match(backCompatFormat)); - assert.ok(Util.isValidW3CId(requestTags[(HttpRequestParser).keys.operationId])); - const traceparent = helper["traceparent"]; - assert.equal(traceparent.version, Traceparent["DEFAULT_VERSION"]); - assert.ok(Util.isValidW3CId(traceparent.traceId)); - assert.ok(Traceparent.isValidSpanId(traceparent.spanId)); - assert.notEqual(traceparent.traceId, traceparent.spanId); - assert.equal(traceparent.traceFlag, Traceparent["DEFAULT_TRACE_FLAG"]); - - assert.equal(traceparent.traceId, helper["operationId"]); - assert.notEqual(traceparent.spanId, helper["operationId"]); - }) - }); - - describe("#getRequestData()", () => { - var request = { - method: "GET", - url: "/search?q=test", - connection: { - encrypted: false - }, - headers: { - host: "bing.com" - } - } - - it("should propagate a custom timestamp", () => { - var helper = new HttpRequestParser(request); - helper["startTime"] = 321; - var requestData1 = helper.getRequestTelemetry({time: new Date(123)}); - var requestData2 = helper.getRequestTelemetry({time: new Date(456)}); - var requestData3 = helper.getRequestTelemetry(); - - assert.deepEqual(requestData1.time, new Date(123)); - assert.deepEqual(requestData2.time, new Date(456)); - assert.deepEqual(requestData3.time, new Date(321)); - assert.notDeepEqual(requestData1, requestData2); - }); - - it("should return an absolute url", () => { - var helper = new HttpRequestParser(request); - var requestData = helper.getRequestTelemetry(); - assert.equal(requestData.url, "http://bing.com/search?q=test"); - }); - - it("should return an absolute url for encrypted traffic", () => { - request.connection.encrypted = true; - - var helper = new HttpRequestParser(request); - var requestData = helper.getRequestTelemetry(); - assert.equal(requestData.url, "https://bing.com/search?q=test"); - }); - - var requestComplex = { - method: "GET", - url: "/a/b/c/?q=test&test2", - connection: { - encrypted: false - }, - headers: { - host: "bing.com" - } - } - - it("should return an absolute url for complex urls", () => { - var helper = new HttpRequestParser(requestComplex); - var requestData = helper.getRequestTelemetry(); - assert.equal(requestData.url, "http://bing.com/a/b/c/?q=test&test2"); - }); - - var requestNoSearchParam = { - method: "method", - url: "/a/", - connection: { - encrypted: false - }, - headers: { - host: "bing.com" - } - } - - it("should return an absolute url when url does not have search part", () => { - var helper = new HttpRequestParser(requestNoSearchParam); - var requestData = helper.getRequestTelemetry(); - assert.equal(requestData.url, "http://bing.com/a/"); - }); - - var requestNoPathName = { - method: "method", - url: "/", - connection: { - encrypted: false - }, - headers: { - host: "bing.com" - } - } - - it("should return an absolute url when url does not have path name", () => { - var helper = new HttpRequestParser(requestNoPathName); - var requestData = helper.getRequestTelemetry(); - assert.equal(requestData.url, "http://bing.com/"); - }); - }); - - describe("#getRequestTags()", () => { - - var request = { - method: "GET", - url: "/search?q=test", - connection: { - encrypted: false - }, - headers: { - host: "bing.com", - "x-forwarded-for": "123.123.123.123", - "cookie": "ai_user=cookieUser|time;ai_session=cookieSession|time;ai_authUser=cookieAuthUser|time", - "x-ms-request-id": "parentRequestId", - "x-ms-request-root-id": "operationId", - } - } - - it("should not override context tags if they are already set", () => { - var helper = new HttpRequestParser(request); - - var originalTags: {[key: string]:string} = { - [(HttpRequestParser).keys.locationIp]: 'originalIp', - [(HttpRequestParser).keys.userId]: 'originalUserId', - [(HttpRequestParser).keys.userAuthUserId]: 'originalAuthUserId', - [(HttpRequestParser).keys.userAgent]: 'originalUserAgent', - [(HttpRequestParser).keys.operationName]: 'originalOperationName', - [(HttpRequestParser).keys.operationId]: 'originalOperationId', - [(HttpRequestParser).keys.operationParentId]: 'originalOperationParentId' - }; - var newTags = helper.getRequestTags(originalTags); - assert.equal(newTags[(HttpRequestParser).keys.locationIp], 'originalIp'); - assert.equal(newTags[(HttpRequestParser).keys.userId], 'originalUserId'); - assert.equal(newTags[(HttpRequestParser).keys.userAuthUserId], 'originalAuthUserId'); - assert.equal(newTags[(HttpRequestParser).keys.userAgent], 'originalUserAgent'); - assert.equal(newTags[(HttpRequestParser).keys.operationName], 'originalOperationName'); - assert.equal(newTags[(HttpRequestParser).keys.operationId], 'originalOperationId'); - assert.equal(newTags[(HttpRequestParser).keys.operationParentId], 'originalOperationParentId'); - }); - - it("should read tags from headers", () => { - var helper = new HttpRequestParser(request); - - var originalTags: {[key: string]:string} = { - }; - - var newTags = helper.getRequestTags(originalTags); - assert.equal(newTags[(HttpRequestParser).keys.locationIp], '123.123.123.123'); - assert.equal(newTags[(HttpRequestParser).keys.userId], 'cookieUser'); - assert.equal(newTags[(HttpRequestParser).keys.userAuthUserId], 'cookieAuthUser'); - assert.equal(newTags[(HttpRequestParser).keys.userAgent], undefined); - assert.equal(newTags[(HttpRequestParser).keys.operationName], 'GET /search'); - assert.equal(newTags[(HttpRequestParser).keys.operationId], 'operationId'); - assert.equal(newTags[(HttpRequestParser).keys.operationParentId], 'parentRequestId'); - }); - }); -}); diff --git a/Tests/AutoCollection/HttpRequests.tests.ts b/Tests/AutoCollection/HttpRequests.tests.ts deleted file mode 100644 index 70239a192..000000000 --- a/Tests/AutoCollection/HttpRequests.tests.ts +++ /dev/null @@ -1,65 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); -import HttpRequests = require("../../AutoCollection/HttpRequests") -import AppInsights = require("../../applicationinsights"); -import { CorrelationContextManager, CorrelationContext } from "../../AutoCollection/CorrelationContextManager"; - -describe("AutoCollection/HttpRequests", () => { - afterEach(() => { - AppInsights.dispose(); - }); - describe("#init and #dispose()", () => { - it("init should enable and dispose should stop server requests autocollection", () => { - var appInsights = AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333").setAutoCollectRequests(true); - var enableHttpRequestsSpy = sinon.spy(HttpRequests.INSTANCE, "enable"); - appInsights.start(); - - assert.equal(enableHttpRequestsSpy.callCount, 1, "enable should be called once as part of requests autocollection initialization"); - assert.equal(enableHttpRequestsSpy.getCall(0).args[0], true); - AppInsights.dispose(); - assert.equal(enableHttpRequestsSpy.callCount, 2, "enable(false) should be called once as part of requests autocollection shutdown"); - assert.equal(enableHttpRequestsSpy.getCall(1).args[0], false); - }); - }); - - describe("#addResponseCorrelationIdHeader", () => { - var response = { - headers: <{[key: string]: any} >{}, - getHeader: function (name: string) { return this.headers[name] }, - setHeader: function (name: string, value: any) { this.headers[name] = value }, - clearHeaders: function() { this.headers = {} } - } - - afterEach(() => { - AppInsights.dispose(); - response.clearHeaders(); - }); - - it("should accept string request-context", () => { - var appInsights = AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333").setAutoCollectRequests(true); - AppInsights.defaultClient.config.correlationId = "abcdefg"; - appInsights.start(); - - response.setHeader("request-context", "appId=cid-v1:aaaaed48-297a-4ea2-af46-0a5a5d26aaaa"); - assert.doesNotThrow(() => HttpRequests["addResponseCorrelationIdHeader"](AppInsights.defaultClient, response as any)) - }); - - it("should accept nonstring request-context", () => { - var appInsights = AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333").setAutoCollectDependencies(true); - AppInsights.defaultClient.config.correlationId = "abcdefg"; - appInsights.start(); - - response.setHeader("request-context", ["appId=cid-v1:aaaaed48-297a-4ea2-af46-0a5a5d26aaaa"]); - assert.doesNotThrow(() => HttpRequests["addResponseCorrelationIdHeader"](AppInsights.defaultClient, response as any)); - assert.deepEqual(response.getHeader("request-context"), ["appId=cid-v1:aaaaed48-297a-4ea2-af46-0a5a5d26aaaa"], "does not modify valid appId") - - response.setHeader("request-context", 123); - assert.doesNotThrow(() => HttpRequests["addResponseCorrelationIdHeader"](AppInsights.defaultClient, response as any)); - assert.ok(response.getHeader("request-context").indexOf("abcdefg") !== -1) - - response.setHeader("request-context", {foo: 'bar'}); - assert.doesNotThrow(() => HttpRequests["addResponseCorrelationIdHeader"](AppInsights.defaultClient, response as any)); - assert.ok(response.getHeader("request-context").indexOf("abcdefg") !== -1) - }) - }); -}); diff --git a/Tests/AutoCollection/NativePerformance.tests.ts b/Tests/AutoCollection/NativePerformance.tests.ts deleted file mode 100644 index 08f637b23..000000000 --- a/Tests/AutoCollection/NativePerformance.tests.ts +++ /dev/null @@ -1,139 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); - -import AppInsights = require("../../applicationinsights"); -import TelemetryClient = require("../../Library/TelemetryClient"); -import { AutoCollectNativePerformance } from "../../AutoCollection/NativePerformance"; -import { JsonConfig } from "../../Library/JsonConfig"; - -const ENV_nativeMetricsDisablers = "APPLICATION_INSIGHTS_DISABLE_EXTENDED_METRIC"; -const ENV_nativeMetricsDisableAll = "APPLICATION_INSIGHTS_DISABLE_ALL_EXTENDED_METRICS"; -describe("AutoCollection/NativePerformance", () => { - var sandbox: sinon.SinonSandbox; - - beforeEach(() => { - sandbox = sinon.sandbox.create(); - JsonConfig["_instance"] = undefined; - }); - - afterEach(() => { - AppInsights.dispose(); - sandbox.restore(); - }); - - if (AutoCollectNativePerformance.isNodeVersionCompatible()) { - describe("#init and #dispose()", () => { - it("init should enable and dispose should stop autocollection interval", () => { - var setIntervalSpy = sandbox.spy(global, "setInterval"); - var clearIntervalSpy = sandbox.spy(global, "clearInterval"); - - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333") - .setAutoCollectHeartbeat(false) - .setAutoCollectPerformance(false, true) - .setAutoCollectPreAggregatedMetrics(false) - .start(); - if (AutoCollectNativePerformance["_metricsAvailable"]) { - assert.equal(setIntervalSpy.callCount, 3, "setInteval should be called three times as part of NativePerformance initialization as well as Statsbeat"); - AppInsights.dispose(); - assert.equal(clearIntervalSpy.callCount, 1, "clearInterval should be called once as part of NativePerformance shutdown"); - } else { - assert.equal(setIntervalSpy.callCount, 2, "setInterval should not be called if NativePerformance package is not available, Statsbeat will be called"); - AppInsights.dispose(); - assert.equal(clearIntervalSpy.callCount, 0, "clearInterval should not be called if NativePerformance package is not available"); - } - }); - - it("constructor should be safe to call multiple times", () => { - var client = new TelemetryClient("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - var native = new AutoCollectNativePerformance(client); - var sinonSpy = sandbox.spy(AutoCollectNativePerformance.INSTANCE, "dispose"); - - assert.ok(native); - assert.ok(sinonSpy.notCalled); - - assert.doesNotThrow(() => { native = new AutoCollectNativePerformance(client) }, "NativePerformance can be constructed more than once"); - assert.ok(sinonSpy.calledOnce, "dispose is called when second instance is constructed"); - }); - - it("Calling enable multiple times shoud not create multiple timers", () => { - var client = new TelemetryClient("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - var sinonSpy = sandbox.spy(global, "setInterval"); - var native = new AutoCollectNativePerformance(client); - - assert.ok(native); - assert.doesNotThrow(() => native.enable(true), "Does not throw when trying to enable"); - assert.doesNotThrow(() => native.enable(true), "Does not throw when trying to enable"); - assert.equal(sinonSpy.callCount, 1, "setInterval should be singleton"); - }); - - it("Calling enable when metrics are not available should fail gracefully", () => { - var client = new TelemetryClient("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - var native = new AutoCollectNativePerformance(client); - - AutoCollectNativePerformance["_metricsAvailable"] = false; - assert.ok(!(native)["_emitter"]); - - assert.doesNotThrow(() => native.enable(true), "Does not throw when native metrics are not available and trying to enable"); - assert.doesNotThrow(() => native.enable(false), "Does not throw when native metrics are not available and trying to disable"); - }); - }); - - describe("#_parseEnabled", () => { - it("should return equal input arg if no env vars are set", () => { - const _customConfig = JsonConfig.getInstance(); - assert.deepEqual(AutoCollectNativePerformance.parseEnabled(true, _customConfig), { isEnabled: true, disabledMetrics: {} }); - assert.deepEqual(AutoCollectNativePerformance.parseEnabled(false, _customConfig), { isEnabled: false, disabledMetrics: {} }); - - const config = { gc: true, heap: true }; - assert.deepEqual(AutoCollectNativePerformance.parseEnabled(config, _customConfig), { isEnabled: true, disabledMetrics: config }); - }); - - it("should overwrite input arg if disable all extended metrics env var is set", () => { - const env = <{ [id: string]: string }>{}; - const originalEnv = process.env; - - env[ENV_nativeMetricsDisableAll] = "set"; - process.env = env; - - const _customConfig = JsonConfig.getInstance(); - - assert.deepEqual(AutoCollectNativePerformance.parseEnabled(true, _customConfig), { isEnabled: false, disabledMetrics: {} }); - assert.deepEqual(AutoCollectNativePerformance.parseEnabled({}, _customConfig), { isEnabled: false, disabledMetrics: {} }); - assert.deepEqual(AutoCollectNativePerformance.parseEnabled({ gc: true }, _customConfig), { isEnabled: false, disabledMetrics: {} }); - - process.env = originalEnv; - }); - - it("should overwrite input arg if individual env vars are set", () => { - const expectation = { gc: true, heap: true }; - const env = <{ [id: string]: string }>{}; - const originalEnv = process.env; - - env[ENV_nativeMetricsDisablers] = "gc,heap"; - process.env = env; - - const _customConfig = JsonConfig.getInstance(); - - let inConfig; - - inConfig = false; - assert.deepEqual(AutoCollectNativePerformance.parseEnabled(inConfig, _customConfig), { isEnabled: false, disabledMetrics: expectation }); - - inConfig = true; - assert.deepEqual(AutoCollectNativePerformance.parseEnabled(inConfig, _customConfig), { isEnabled: true, disabledMetrics: expectation }); - - inConfig = {}; - assert.deepEqual(AutoCollectNativePerformance.parseEnabled(inConfig, _customConfig), { isEnabled: true, disabledMetrics: expectation }); - inConfig = { gc: true }; - assert.deepEqual(AutoCollectNativePerformance.parseEnabled(inConfig, _customConfig), { isEnabled: true, disabledMetrics: expectation }); - inConfig = { loop: true }; - - assert.deepEqual(AutoCollectNativePerformance.parseEnabled(inConfig, _customConfig), { isEnabled: true, disabledMetrics: { ...inConfig, ...expectation } }); - inConfig = { gc: false, loop: true, heap: 'abc', something: 'else' }; - assert.deepEqual(AutoCollectNativePerformance.parseEnabled(inConfig, _customConfig), { isEnabled: true, disabledMetrics: { ...inConfig, ...expectation } }); - - process.env = originalEnv; - }); - }); - } -}); diff --git a/Tests/AutoCollection/Performance.tests.ts b/Tests/AutoCollection/Performance.tests.ts deleted file mode 100644 index 7b8df3e06..000000000 --- a/Tests/AutoCollection/Performance.tests.ts +++ /dev/null @@ -1,89 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); - -import AppInsights = require("../../applicationinsights"); -import Performance = require("../../AutoCollection/Performance"); -import TelemetryClient = require("../../Library/TelemetryClient"); - -describe("AutoCollection/Performance", () => { - var sandbox: sinon.SinonSandbox; - - beforeEach(() => { - sandbox = sinon.sandbox.create(); - }); - - afterEach(() => { - AppInsights.dispose(); - sandbox.restore(); - }); - - describe("#init and #dispose()", () => { - it("init should enable and dispose should stop autocollection interval", () => { - var setIntervalSpy = sandbox.spy(global, "setInterval"); - var clearIntervalSpy = sandbox.spy(global, "clearInterval"); - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333") - .setAutoCollectHeartbeat(false) - .setAutoCollectPerformance(true, false) - .setAutoCollectPreAggregatedMetrics(false) - .start(); - assert.equal(setIntervalSpy.callCount, 3, "setInteval should be called three times as part of performance initialization and also as part of Statsbeat"); - AppInsights.dispose(); - assert.equal(clearIntervalSpy.callCount, 1, "clearInterval should be called once as part of performance shutdown"); - - - }); - }); - - describe("#trackNetwork()", () => { - it("should not produce incorrect metrics because of multiple instances of Performance class", (done) => { - const setIntervalStub = sandbox.stub(global, "setInterval", () => ({ unref: () => { } })); - const clearIntervalSpy = sandbox.spy(global, "clearInterval"); - const appInsights = AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333").setAutoCollectPerformance(false).start(); - const performance1 = new Performance(new TelemetryClient("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"), 1234, false); - const performance2 = new Performance(new TelemetryClient("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"), 4321, true); - performance1.enable(true); - performance2.enable(true); - Performance.INSTANCE.enable(true); - const stub1 = sandbox.stub(performance1["_client"], "trackMetric"); - const stub2 = sandbox.stub(performance2["_client"], "trackMetric"); - - Performance.countRequest(1000, true); - Performance.countRequest(2000, true); - performance1["_trackNetwork"](); - performance2["_trackNetwork"](); - Performance.countRequest(5000, true); - const prev1 = performance1["_lastIntervalRequestExecutionTime"]; - const prev2 = performance2["_lastIntervalRequestExecutionTime"]; - assert.deepEqual(prev1, prev2); - assert.deepEqual(prev1, 1000 + 2000); - - // Add to end of event loop - setTimeout(() => { - assert.equal(Performance["_intervalRequestExecutionTime"], 1000 + 2000 + 5000); - assert.equal(stub1.callCount, 2, "calls trackMetric for the 2 standard metrics"); - assert.equal(stub2.callCount, 3, "calls trackMetric for the 3 live metric counters"); - assert.equal(stub2.args[1][0].value, stub1.args[1][0].value); - assert.equal(stub1.args[1][0].value, (1000 + 2000) / 2, "request duration average should be 1500"); - - stub1.reset(); - stub2.reset(); - - setTimeout(() => { - // need to wait at least 1 ms so trackNetwork has valid elapsedMs value - performance1["_trackNetwork"](); - performance2["_trackNetwork"](); - assert.equal(stub1.callCount, 2, "calls trackMetric for the 2 standard metrics"); - assert.equal(stub2.callCount, 3, "calls trackMetric for the 3 live metric counters"); - assert.equal(stub2.args[1][0].value, stub1.args[1][0].value); - assert.equal(stub1.args[1][0].value, (5000) / 1, "request duration average should be 5000"); - - appInsights.setAutoCollectPerformance(true); // set back to default of true so tests expecting the default can pass - Performance.INSTANCE.dispose(); - performance1.dispose(); - performance2.dispose(); - done(); - }, 100); - }, 100); - }); - }); -}); diff --git a/Tests/AutoCollection/PreAggregatedMetrics.tests.ts b/Tests/AutoCollection/PreAggregatedMetrics.tests.ts deleted file mode 100644 index 4a8886079..000000000 --- a/Tests/AutoCollection/PreAggregatedMetrics.tests.ts +++ /dev/null @@ -1,83 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); - -import AppInsights = require("../../applicationinsights"); -import AutoCollectPreAggregatedMetrics = require("../../AutoCollection/PreAggregatedMetrics"); -import TelemetryClient = require("../../Library/TelemetryClient"); - -describe("AutoCollection/PreAggregatedMetrics", () => { - var sandbox: sinon.SinonSandbox; - - beforeEach(() => { - sandbox = sinon.sandbox.create(); - }); - - afterEach(() => { - AppInsights.dispose(); - sandbox.restore(); - }); - - describe("#init and #dispose()", () => { - it("init should enable and dispose should stop autocollection interval", () => { - var setIntervalSpy = sandbox.spy(global, "setInterval"); - var clearIntervalSpy = sandbox.spy(global, "clearInterval"); - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333") - .setAutoCollectHeartbeat(false) - .setAutoCollectPerformance(false, false) - .setAutoCollectPreAggregatedMetrics(true) - .start(); - assert.equal(setIntervalSpy.callCount, 3, "setInteval should be called three times as part of PreAggregatedMetrics initialization and other one for Statsbeat"); - AppInsights.dispose(); - assert.equal(clearIntervalSpy.callCount, 1, "clearInterval should be called once as part of PreAggregatedMetrics shutdown"); - - setIntervalSpy.restore(); - clearIntervalSpy.restore(); - }); - }); - - describe("#trackRequestMetrics()", () => { - it("should not produce incorrect metrics because of multiple instances of AutoCollectPreAggregatedMetrics class", (done) => { - const setIntervalStub = sandbox.stub(global, "setInterval", () => ({ unref: () => { } })); - const clearIntervalSpy = sandbox.spy(global, "clearInterval"); - const appInsights = AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333").setAutoCollectPerformance(false).start(); - const autoCollect1 = new AutoCollectPreAggregatedMetrics(new TelemetryClient("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"), 1234); - const autoCollect2 = new AutoCollectPreAggregatedMetrics(new TelemetryClient("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"), 4321); - autoCollect1.enable(true); - autoCollect2.enable(true); - AutoCollectPreAggregatedMetrics.INSTANCE.enable(true); - const stub1 = sandbox.stub(autoCollect1["_client"], "trackMetric"); - const stub2 = sandbox.stub(autoCollect2["_client"], "trackMetric"); - - AutoCollectPreAggregatedMetrics.countRequest(1000, {}); - AutoCollectPreAggregatedMetrics.countRequest(2000, {}); - setTimeout(() => { - autoCollect1["_trackRequestMetrics"](); - autoCollect2["_trackRequestMetrics"](); - AutoCollectPreAggregatedMetrics.countRequest(5000, {}); - - assert.deepEqual(AutoCollectPreAggregatedMetrics["_requestCountersCollection"][0]["lastIntervalExecutionTime"], 1000 + 2000); - - // Add to end of event loop - setTimeout(() => { - // need to wait at least 1 ms so _trackRequestMetrics has valid elapsedMs value - const counter = AutoCollectPreAggregatedMetrics["_getAggregatedCounter"]({}, AutoCollectPreAggregatedMetrics["_requestCountersCollection"]); - assert.equal(counter.intervalExecutionTime, 1000 + 2000 + 5000); - assert.equal(stub1.callCount, 1, "calls trackMetric"); - assert.equal(stub2.callCount, 0, "No calls to trackMetric"); - assert.equal(stub1.args[0][0].value, (1000 + 2000) / 2, "request duration average should be 1500"); - - stub1.reset(); - - appInsights.setAutoCollectPerformance(true); // set back to default of true so tests expecting the default can pass - AutoCollectPreAggregatedMetrics.INSTANCE.dispose(); - autoCollect1.dispose(); - autoCollect2.dispose(); - stub1.restore(); - setIntervalStub.restore(); - clearIntervalSpy.restore(); - done(); - }, 100); - }, 100); - }); - }); -}); diff --git a/Tests/AutoCollection/SpanParser.tests.ts b/Tests/AutoCollection/SpanParser.tests.ts deleted file mode 100644 index fbd32c860..000000000 --- a/Tests/AutoCollection/SpanParser.tests.ts +++ /dev/null @@ -1,399 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); -import { Span, BasicTracerProvider } from "@opentelemetry/sdk-trace-base"; -import { Link, SpanKind, SpanContext, ROOT_CONTEXT, SpanStatus, SpanStatusCode } from "@opentelemetry/api"; -import { - SemanticAttributes, - SemanticResourceAttributes -} from "@opentelemetry/semantic-conventions"; - -import AppInsights = require("../../applicationinsights"); -import { spanToTelemetryContract } from "../../AutoCollection/diagnostic-channel/SpanParser"; -import { DependencyTelemetry, Identified, RequestTelemetry } from "../../Declarations/Contracts"; - -describe("diagnostic-channel/azure-coretracing", () => { - var sandbox: sinon.SinonSandbox; - - const tracer = new BasicTracerProvider().getTracer("default"); - - before(() => { - sandbox = sinon.sandbox.create(); - }); - - afterEach(() => { - AppInsights.dispose(); - sandbox.restore(); - }); - - it("should populate HTTP dependency", () => { - const span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.CLIENT, - "parentSpanId" - ); - span.attributes[SemanticAttributes.HTTP_METHOD] = "POST"; - span.attributes[SemanticAttributes.HTTP_STATUS_CODE] = "404"; - span.attributes[SemanticAttributes.HTTP_URL] = "http://test.com/path/"; - span.attributes[SemanticAttributes.PEER_SERVICE] = "http://testpeer.com/"; - span.attributes["TestAttribute"] = "test"; - let status: SpanStatus = { code: SpanStatusCode.ERROR, message: "test error" }; - span.setStatus(status); - let context: SpanContext = { - traceId: "linkTraceId", - spanId: "linkSpanId", - traceFlags: 0 - }; - let link: Link = { - context: context, - }; - span.links.push(link); - let dependency: DependencyTelemetry & Identified = spanToTelemetryContract(span); - assert.equal(dependency.name, "POST /path/"); - assert.equal(dependency.id, "spanId"); - assert.equal(dependency.success, false); - assert.equal(dependency.resultCode, "404"); - assert.equal(dependency.dependencyTypeName, "HTTP"); - assert.equal(dependency.data, "http://test.com/path/"); - assert.equal(dependency.target, "http://testpeer.com/"); - assert.ok(dependency.duration); - assert.equal(dependency.properties["TestAttribute"], "test"); - assert.equal(dependency.properties["_MS.links"], '[{"operation_Id":"linkTraceId","id":"linkSpanId"}]'); - }); - - it("should populate DB dependency", () => { - const span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.INTERNAL, - "parentSpanId" - ); - span.attributes[SemanticAttributes.DB_SYSTEM] = "mysql"; - span.attributes[SemanticAttributes.DB_STATEMENT] = "SELECT * FROM Test"; - span.attributes[SemanticAttributes.DB_NAME] = "TestDB"; - span.attributes[SemanticAttributes.NET_PEER_NAME] = "Test Net peer name"; - span.attributes["TestAttribute"] = "test"; - let status: SpanStatus = { code: SpanStatusCode.UNSET, message: "test" }; - span.setStatus(status); - let dependency: DependencyTelemetry & Identified = spanToTelemetryContract(span); - assert.equal(dependency.name, "test span"); - assert.equal(dependency.id, "spanId"); - assert.equal(dependency.success, true); - assert.equal(dependency.resultCode, "0"); - assert.equal(dependency.dependencyTypeName, "mysql"); - assert.equal(dependency.target, "Test Net peer name|TestDB"); - assert.equal(dependency.data, "SELECT * FROM Test"); - assert.ok(dependency.duration); - assert.equal(dependency.properties["TestAttribute"], "test"); - }); - - it("should populate RPC dependency", () => { - const span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.INTERNAL, - "parentSpanId" - ); - span.attributes[SemanticAttributes.RPC_METHOD] = "TestRpcMethod"; - span.attributes[SemanticAttributes.RPC_GRPC_STATUS_CODE] = "202"; - span.attributes[SemanticAttributes.RPC_SYSTEM] = "TestRpcSystem"; - span.attributes["TestAttribute"] = "test"; - let status: SpanStatus = { code: SpanStatusCode.OK, message: "test" }; - span.setStatus(status); - let dependency: DependencyTelemetry & Identified = spanToTelemetryContract(span); - assert.equal(dependency.name, "test span"); - assert.equal(dependency.id, "spanId"); - assert.equal(dependency.success, true); - assert.equal(dependency.resultCode, "202"); - assert.equal(dependency.dependencyTypeName, "GRPC"); - assert.equal(dependency.target, "TestRpcSystem"); - assert.equal(dependency.data, ""); - assert.ok(dependency.duration); - assert.equal(dependency.properties["TestAttribute"], "test"); - }); - - it("should populate HTTP request", () => { - const span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.SERVER, - "parentSpanId" - ); - span.attributes[SemanticAttributes.HTTP_METHOD] = "POST"; - span.attributes[SemanticAttributes.HTTP_STATUS_CODE] = "500"; - span.attributes[SemanticAttributes.HTTP_URL] = "http://test.com/"; - span.attributes["TestAttribute"] = "test"; - let status: SpanStatus = { code: SpanStatusCode.ERROR, message: "test error" }; - span.setStatus(status); - let context: SpanContext = { - traceId: "linkTraceId", - spanId: "linkSpanId", - traceFlags: 0 - }; - let link: Link = { - context: context, - }; - span.links.push(link); - let request: RequestTelemetry & Identified = spanToTelemetryContract(span); - assert.equal(request.name, "POST /"); - assert.equal(request.id, "spanId"); - assert.equal(request.success, false); - assert.equal(request.resultCode, "500"); - assert.equal(request.url, "http://test.com/"); - assert.equal(request.source, undefined); - assert.ok(request.duration); - assert.equal(request.properties["TestAttribute"], "test"); - assert.equal(request.properties["_MS.links"], '[{"operation_Id":"linkTraceId","id":"linkSpanId"}]'); - }); - - it("should populate GRPC request", () => { - const span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.CONSUMER, - "parentSpanId" - ); - span.attributes[SemanticAttributes.RPC_GRPC_STATUS_CODE] = "505"; - span.attributes["TestAttribute"] = "test"; - let status: SpanStatus = { code: SpanStatusCode.ERROR, message: "test error" }; - span.setStatus(status); - let request: RequestTelemetry & Identified = spanToTelemetryContract(span); - assert.equal(request.name, "test span"); - assert.equal(request.id, "spanId"); - assert.equal(request.success, false); - assert.equal(request.resultCode, "505"); - assert.equal(request.url, ""); - assert.equal(request.source, undefined); - assert.ok(request.duration); - assert.equal(request.properties["TestAttribute"], "test"); - }); - - it("HTTP Dependency Data(URL)", () => { - const span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.CLIENT, - "parentSpanId" - ); - span.attributes[SemanticAttributes.HTTP_METHOD] = "GET"; - span.attributes[SemanticAttributes.HTTP_SCHEME] = "https"; - span.attributes[SemanticAttributes.HTTP_TARGET] = "/path/12314/?q=ddds#123"; - span.attributes[SemanticAttributes.NET_PEER_PORT] = "443"; - span.attributes[SemanticAttributes.NET_PEER_IP] = "127.0.0.1"; - let dependency: DependencyTelemetry & Identified = spanToTelemetryContract(span); - assert.equal(dependency.data, "https://127.0.0.1:443/path/12314/?q=ddds#123"); - span.attributes[SemanticAttributes.NET_PEER_NAME] = "example.com"; - dependency = spanToTelemetryContract(span); - assert.equal(dependency.data, "https://example.com:443/path/12314/?q=ddds#123"); - span.attributes[SemanticAttributes.HTTP_HOST] = "www.example.org"; - dependency = spanToTelemetryContract(span); - assert.equal(dependency.data, "https://www.example.org/path/12314/?q=ddds#123"); - }); - - it("HTTP Request Url", () => { - const span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.SERVER, - "parentSpanId" - ); - span.attributes[SemanticAttributes.HTTP_METHOD] = "GET"; - span.attributes[SemanticAttributes.HTTP_SCHEME] = "https"; - span.attributes[SemanticAttributes.HTTP_TARGET] = "/path/12314/?q=ddds#123"; - span.attributes[SemanticAttributes.NET_PEER_PORT] = "443"; - span.attributes[SemanticAttributes.NET_PEER_IP] = "127.0.0.1"; - let request: RequestTelemetry & Identified = spanToTelemetryContract(span); - assert.equal(request.url, "https://127.0.0.1:443/path/12314/?q=ddds#123"); - span.attributes[SemanticAttributes.NET_PEER_NAME] = "example.com"; - request = spanToTelemetryContract(span); - assert.equal(request.url, "https://example.com:443/path/12314/?q=ddds#123"); - span.attributes[SemanticAttributes.HTTP_HOST] = "www.example.org"; - request = spanToTelemetryContract(span); - assert.equal(request.url, "https://www.example.org/path/12314/?q=ddds#123"); - }); - - it("HTTP Dependency Target", () => { - const span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.CLIENT, - "parentSpanId" - ); - span.attributes[SemanticAttributes.HTTP_METHOD] = "GET"; - span.attributes[SemanticAttributes.NET_PEER_IP] = "https://127.0.0.0:443"; // Default ports - let dependency: DependencyTelemetry & Identified = spanToTelemetryContract(span); - assert.equal(dependency.target, "https://127.0.0.0"); - span.attributes[SemanticAttributes.NET_PEER_NAME] = "https://test.com:80"; // Wrong Default port - dependency = spanToTelemetryContract(span); - assert.equal(dependency.target, "https://test.com:80"); - span.attributes[SemanticAttributes.HTTP_URL] = "http://test.com:22"; // Non default ports - dependency = spanToTelemetryContract(span); - assert.equal(dependency.target, "http://test.com:22"); - span.attributes[SemanticAttributes.HTTP_HOST] = "www.test.com"; // Wrong URL - dependency = spanToTelemetryContract(span); - assert.equal(dependency.target, "www.test.com"); - span.attributes[SemanticAttributes.PEER_SERVICE] = "http://test.com/"; // No port - dependency = spanToTelemetryContract(span); - assert.equal(dependency.target, "http://test.com/"); - }); - - it("DB Dependency Target", () => { - const span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.CLIENT, - "parentSpanId" - ); - span.attributes[SemanticAttributes.DB_SYSTEM] = "MongoDB"; - let dependency: DependencyTelemetry & Identified = spanToTelemetryContract(span); - assert.equal(dependency.target, "MongoDB"); - span.attributes[SemanticAttributes.DB_NAME] = "TestDB"; - dependency = spanToTelemetryContract(span); - assert.equal(dependency.target, "TestDB"); - span.attributes[SemanticAttributes.PEER_SERVICE] = "testPeerService"; - dependency = spanToTelemetryContract(span); - assert.equal(dependency.target, "testPeerService|TestDB"); - }); - - it("RPC Dependency Target", () => { - const span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.CLIENT, - "parentSpanId" - ); - span.attributes[SemanticAttributes.RPC_SYSTEM] = "TestSystem"; - let dependency: DependencyTelemetry & Identified = spanToTelemetryContract(span); - assert.equal(dependency.target, "TestSystem"); - span.attributes[SemanticAttributes.PEER_SERVICE] = "testPeerService"; - dependency = spanToTelemetryContract(span); - assert.equal(dependency.target, "testPeerService"); - }); - - it("QueueMessage Dependency Type", () => { - const span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.PRODUCER, - "parentSpanId" - ); - let dependency: DependencyTelemetry & Identified = spanToTelemetryContract(span); - assert.equal(dependency.dependencyTypeName, "Queue Message"); - }); - - it("InProc Dependency Type", () => { - const span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.INTERNAL, - "parentSpanId" - ); - let dependency: DependencyTelemetry & Identified = spanToTelemetryContract(span); - assert.equal(dependency.dependencyTypeName, "InProc"); - }); - - it("az.namespace Dependency", () => { - let span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.INTERNAL, - ); - span.attributes["az.namespace"] = "Microsoft.AzureSomething"; - let dependency = spanToTelemetryContract(span); - assert.equal(dependency.dependencyTypeName, "InProc | Microsoft.AzureSomething"); - }); - - it("EventHub Dependency", () => { - // CLIENT Span - let span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.CLIENT, - ); - span.attributes["az.namespace"] = "Microsoft.EventHub"; - span.attributes["peer.address"] = "testPeerAddress"; - span.attributes["message_bus.destination"] = "messageBusDestination"; - let dependency: DependencyTelemetry & Identified = spanToTelemetryContract(span); - assert.equal(dependency.dependencyTypeName, "Microsoft.EventHub"); - assert.equal(dependency.target, "testPeerAddress/messageBusDestination"); - // PRODUCER Span - span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.PRODUCER, - ); - span.attributes["az.namespace"] = "Microsoft.EventHub"; - span.attributes["peer.address"] = "testPeerAddress"; - span.attributes["message_bus.destination"] = "messageBusDestination"; - dependency = spanToTelemetryContract(span); - assert.equal(dependency.dependencyTypeName, "Queue Message | Microsoft.EventHub"); - assert.equal(dependency.target, "testPeerAddress/messageBusDestination"); - }); - - it("Eventhub Request", () => { - const startTime = Date.now(); - let context: SpanContext = { - traceId: "linkTraceId", - spanId: "linkSpanId", - traceFlags: 0 - }; - let span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.CONSUMER, - undefined, - [ - { - context: context, - attributes: { ["enqueuedTime"]: startTime - 111 } - }, - { - context: context, - attributes: { ["enqueuedTime"]: startTime - 222 } - }, - { - context: context, - attributes: { ["enqueuedTime"]: startTime - 111 } - } - ] - ); - span.attributes["az.namespace"] = "Microsoft.EventHub"; - span.attributes["peer.address"] = "testPeerAddress"; - span.attributes["message_bus.destination"] = "messageBusDestination"; - let request: RequestTelemetry & Identified = spanToTelemetryContract(span); - assert.equal(request.source, "testPeerAddress/messageBusDestination"); - assert.ok(!isNaN(request.measurements["timeSinceEnqueued"])); - }); -}); diff --git a/Tests/AutoCollection/Statsbeat.tests.ts b/Tests/AutoCollection/Statsbeat.tests.ts deleted file mode 100644 index e3265a36a..000000000 --- a/Tests/AutoCollection/Statsbeat.tests.ts +++ /dev/null @@ -1,302 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); -import nock = require("nock"); - -import AppInsights = require("../../applicationinsights"); -import Statsbeat = require("../../AutoCollection/Statsbeat"); -import Constants = require("../../Declarations/Constants"); -import TelemetryClient = require("../../Library/TelemetryClient"); -import Config = require("../../Library/Config"); - -describe("AutoCollection/Statsbeat", () => { - var sandbox: sinon.SinonSandbox; - const config = new Config("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - Statsbeat.CONNECTION_STRING = "InstrumentationKey=2aa22222-bbbb-1ccc-8ddd-eeeeffff3333;" - let statsBeat: Statsbeat = null; - - beforeEach(() => { - sandbox = sinon.sandbox.create(); - statsBeat = new Statsbeat(config); - }); - - afterEach(() => { - sandbox.restore(); - AppInsights.dispose(); - statsBeat.enable(false); - statsBeat = null; - }); - - after(() => { - nock.cleanAll(); - }); - - describe("#init and #disable()", () => { - it("init should enable and dispose should stop autocollection interval", () => { - var setIntervalSpy = sandbox.spy(global, "setInterval"); - var clearIntervalSpy = sandbox.spy(global, "clearInterval"); - let client = new TelemetryClient("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.equal(setIntervalSpy.callCount, 2, "setInterval should be called twice as part of Statsbeat initialization"); - client.getStatsbeat().enable(false); - assert.equal(clearIntervalSpy.callCount, 2, "clearInterval should be called twice as part of Statsbeat disable"); - }); - }); - - describe("#Resource provider property", () => { - it("unknown resource provider", (done) => { - statsBeat["_getResourceProvider"]().then(() => { - assert.equal(statsBeat["_resourceProvider"], "unknown"); - assert.equal(statsBeat["_resourceIdentifier"], "unknown"); - done(); - }).catch((error) => { done(error); }); - }); - - it("app service", (done) => { - var newEnv = <{ [id: string]: string }>{}; - newEnv["WEBSITE_SITE_NAME"] = "Test Website"; - newEnv["WEBSITE_HOME_STAMPNAME"] = "test_home"; - var originalEnv = process.env; - process.env = newEnv; - statsBeat["_getResourceProvider"]().then(() => { - process.env = originalEnv; - assert.equal(statsBeat["_resourceProvider"], "appsvc"); - assert.equal(statsBeat["_resourceIdentifier"], "Test Website/test_home"); - done(); - }).catch((error) => { done(error); }); - }); - - it("Azure Function", (done) => { - var newEnv = <{ [id: string]: string }>{}; - newEnv["FUNCTIONS_WORKER_RUNTIME"] = "test"; - newEnv["WEBSITE_HOSTNAME"] = "test_host"; - var originalEnv = process.env; - process.env = newEnv; - statsBeat["_getResourceProvider"]().then(() => { - process.env = originalEnv; - assert.equal(statsBeat["_resourceProvider"], "functions"); - assert.equal(statsBeat["_resourceIdentifier"], "test_host"); - done(); - }).catch((error) => { done(error); }); - - }); - - it("Azure VM", (done) => { - var newEnv = <{ [id: string]: string }>{}; - var originalEnv = process.env; - process.env = newEnv; - let interceptor = nock("http://169.254.169.254") - .get("/metadata/instance/compute", (body: string) => { - return true; - }); - interceptor.reply(200, { - "vmId": "testId", - "subscriptionId": "testsubscriptionId", - "osType": "testOsType" - }); - statsBeat["_getResourceProvider"]().then(() => { - process.env = originalEnv; - assert.equal(statsBeat["_resourceProvider"], "vm"); - assert.equal(statsBeat["_resourceIdentifier"], "testId/testsubscriptionId"); - assert.equal(statsBeat["_os"], "testOsType"); - done(); - }).catch((error) => { done(error); }); - }); - }); - - describe("#trackStatbeats", () => { - beforeEach(() => { - // Prevent handles to be initialized - statsBeat["_longHandle"] = setInterval(() => { }, 0); - statsBeat["_handle"] = setInterval(() => { }, 0); - }); - - it("It adds correct network properties to custom metric", (done) => { - statsBeat.enable(true); - const sendStub = sandbox.stub(statsBeat, "_sendStatsbeats"); - statsBeat.countRequest(1, "testEndpointHost", 123, true); - statsBeat.setCodelessAttach(); - statsBeat.trackShortIntervalStatsbeats().then(() => { - assert.ok(sendStub.called, "should call _sendStatsbeats"); - let metric = statsBeat["_statbeatMetrics"].filter(f => f.name === "Request Duration")[0]; - assert.ok(metric, "Statsbeat Request not found"); - assert.equal(metric.value, 123); - assert.equal(((metric.properties))["attach"], "codeless"); - assert.equal(((metric.properties))["cikey"], "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.equal(((metric.properties))["language"], "node"); - assert.equal(((metric.properties))["rp"], "unknown"); - assert.equal(((metric.properties))["endpoint"], 1); - assert.equal(((metric.properties))["host"], "testEndpointHost"); - assert.ok(((metric.properties))["os"]); - assert.ok(((metric.properties))["runtimeVersion"]); - assert.ok(((metric.properties))["version"]); - - done(); - }).catch((error) => { done(error); }); - }); - - it("Track duration", (done) => { - statsBeat.enable(true); - const sendStub = sandbox.stub(statsBeat, "_sendStatsbeats"); - statsBeat.countRequest(0, "test", 1000, true); - statsBeat.countRequest(0, "test", 500, false); - statsBeat.trackShortIntervalStatsbeats().then((error) => { - assert.ok(sendStub.called, "should call _sendStatsbeats"); - assert.equal(statsBeat["_statbeatMetrics"].length, 3); - let metric = statsBeat["_statbeatMetrics"].filter(f => f.name === "Request Duration")[0]; - assert.ok(metric, "Request Duration metric not found"); - assert.equal(metric.value, 750); - done(); - }).catch((error) => { done(error); }); - }); - - it("Track counts", (done) => { - statsBeat.enable(true); - const sendStub = sandbox.stub(statsBeat, "_sendStatsbeats"); - statsBeat.countRequest(0, "test", 1, true); - statsBeat.countRequest(0, "test", 1, true); - statsBeat.countRequest(0, "test", 1, true); - statsBeat.countRequest(0, "test", 1, true); - statsBeat.countRequest(0, "test", 1, false); - statsBeat.countRequest(0, "test", 1, false); - statsBeat.countRequest(0, "test", 1, false); - statsBeat.countRetry(0, "test"); - statsBeat.countRetry(0, "test"); - statsBeat.countThrottle(0, "test"); - statsBeat.countException(0, "test"); - statsBeat.trackShortIntervalStatsbeats().then(() => { - assert.ok(sendStub.called, "should call _sendStatsbeats"); - assert.equal(statsBeat["_statbeatMetrics"].length, 6); - let metric = statsBeat["_statbeatMetrics"].filter(f => f.name === "Request Success Count")[0]; - assert.ok(metric, "Request Success Count metric not found"); - assert.equal(metric.value, 4); - metric = statsBeat["_statbeatMetrics"].filter(f => f.name === "Request Failure Count")[0]; - assert.ok(metric, "Request Failure Count metric not found"); - assert.equal(metric.value, 3); - metric = statsBeat["_statbeatMetrics"].filter(f => f.name === "Retry Count")[0]; - assert.ok(metric, "Retry Count metric not found"); - assert.equal(metric.value, 2); - metric = statsBeat["_statbeatMetrics"].filter(f => f.name === "Throttle Count")[0]; - assert.ok(metric, "Throttle Count metric not found"); - assert.equal(metric.value, 1); - metric = statsBeat["_statbeatMetrics"].filter(f => f.name === "Exception Count")[0]; - assert.ok(metric, "Exception Count metric not found"); - assert.equal(metric.value, 1); - done(); - }).catch((error) => { done(error); }); - }); - - it("Track attach Statbeat", (done) => { - statsBeat.enable(true); - const sendStub = sandbox.stub(statsBeat, "_sendStatsbeats"); - statsBeat.trackLongIntervalStatsbeats().then(() => { - assert.ok(sendStub.called, "should call _sendStatsbeats"); - let metric = statsBeat["_statbeatMetrics"].filter(f => f.name === "Attach")[0]; - assert.ok(metric, "attach metric not found"); - assert.equal(metric.value, 1); - assert.equal(((metric.properties))["cikey"], "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.equal(((metric.properties))["language"], "node"); - assert.equal(((metric.properties))["rp"], "unknown"); - assert.equal(((metric.properties))["rpId"], "unknown"); - assert.equal(((metric.properties))["attach"], "sdk"); - assert.ok(((metric.properties))["os"]); - assert.ok(((metric.properties))["runtimeVersion"]); - assert.ok(((metric.properties))["version"]); - done(); - }).catch((error) => { done(error); }); - }); - - it("Track feature Statbeat", (done) => { - statsBeat.enable(true); - statsBeat.addFeature(Constants.StatsbeatFeature.DISK_RETRY); - const sendStub = sandbox.stub(statsBeat, "_sendStatsbeats"); - statsBeat.trackLongIntervalStatsbeats().then(() => { - assert.ok(sendStub.called, "should call _sendStatsbeats"); - let metric = statsBeat["_statbeatMetrics"].filter(f => f.name === "Feature")[0]; - assert.ok(metric, "feature metric not found"); - assert.equal(metric.name, "Feature"); - assert.equal(metric.value, 1); - assert.equal(((metric.properties))["type"], 0); - assert.equal(((metric.properties))["cikey"], "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.equal(((metric.properties))["language"], "node"); - assert.equal(((metric.properties))["rp"], "unknown"); - assert.equal(((metric.properties))["attach"], "sdk"); - assert.equal(((metric.properties))["feature"], 1); - assert.ok(((metric.properties))["os"]); - assert.ok(((metric.properties))["runtimeVersion"]); - assert.ok(((metric.properties))["version"]); - done(); - }).catch((error) => { done(error); }); - }); - - it("Track instrumentation Statbeat", (done) => { - statsBeat.enable(true); - statsBeat.addInstrumentation(Constants.StatsbeatInstrumentation.AZURE_CORE_TRACING); - const sendStub = sandbox.stub(statsBeat, "_sendStatsbeats"); - statsBeat.trackLongIntervalStatsbeats().then(() => { - assert.ok(sendStub.called, "should call _sendStatsbeats"); - let metric = statsBeat["_statbeatMetrics"].filter(f => f.name === "Feature")[0]; - assert.ok(metric, "instrumentation metric not found"); - assert.equal(metric.name, "Feature"); - assert.equal(metric.value, 1); - assert.equal(((metric.properties))["type"], 1); - assert.equal(((metric.properties))["cikey"], "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.equal(((metric.properties))["language"], "node"); - assert.equal(((metric.properties))["rp"], "unknown"); - assert.equal(((metric.properties))["attach"], "sdk"); - assert.equal(((metric.properties))["feature"], 1); - assert.ok(((metric.properties))["os"]); - assert.ok(((metric.properties))["runtimeVersion"]); - assert.ok(((metric.properties))["version"]); - done(); - }).catch((error) => { done(error); }); - }); - - it("Instrumentations", () => { - statsBeat.addInstrumentation(Constants.StatsbeatInstrumentation.AZURE_CORE_TRACING); - assert.equal(statsBeat["_instrumentation"], 1); - statsBeat.addInstrumentation(Constants.StatsbeatInstrumentation.MONGODB); - assert.equal(statsBeat["_instrumentation"], 3); - statsBeat.addInstrumentation(Constants.StatsbeatInstrumentation.MYSQL); - assert.equal(statsBeat["_instrumentation"], 7); - statsBeat.removeInstrumentation(Constants.StatsbeatInstrumentation.AZURE_CORE_TRACING); - assert.equal(statsBeat["_instrumentation"], 6); - statsBeat.removeInstrumentation(Constants.StatsbeatInstrumentation.MYSQL); - assert.equal(statsBeat["_instrumentation"], 2); - }); - - it("Features", () => { - statsBeat.addFeature(Constants.StatsbeatFeature.DISK_RETRY); - assert.equal(statsBeat["_feature"], 1); - statsBeat.addFeature(Constants.StatsbeatFeature.AAD_HANDLING); - assert.equal(statsBeat["_feature"], 3); - statsBeat.removeFeature(Constants.StatsbeatFeature.DISK_RETRY); - assert.equal(statsBeat["_feature"], 2); - }); - - it("Multiple network categories and endpoints", (done) => { - statsBeat.enable(true); - const sendStub = sandbox.stub(statsBeat, "_sendStatsbeats"); - statsBeat.countRequest(0, "breezeFirstEndpoint", 100, true); - statsBeat.countRequest(1, "quickpulseEndpoint", 200, true); - statsBeat.countRequest(0, "breezeSecondEndpoint", 400, true); - statsBeat.trackShortIntervalStatsbeats().then(() => { - assert.ok(sendStub.called, "should call _sendStatsbeats"); - let metric: any = statsBeat["_statbeatMetrics"].find(f => f.name === "Request Duration" - && f.value === 100); - assert.ok(metric, "breezeFirstEndpoint metric not found"); - assert.equal(((metric.properties))["endpoint"], 0); - assert.equal(((metric.properties))["host"], "breezeFirstEndpoint"); - metric = statsBeat["_statbeatMetrics"].find(f => f.name === "Request Duration" - && f.value === 200); - assert.ok(metric, "quickpulseEndpoint metric not found"); - assert.equal(((metric.properties))["endpoint"], 1); - assert.equal(((metric.properties))["host"], "quickpulseEndpoint"); - metric = statsBeat["_statbeatMetrics"].find(f => f.name === "Request Duration" - && f.value === 400); - assert.ok(metric, "breezeSecondEndpoint metric not found"); - assert.equal(((metric.properties))["endpoint"], 0); - assert.equal(((metric.properties))["host"], "breezeSecondEndpoint"); - done(); - }).catch((error) => { done(error); }); - }); - }); -}); \ No newline at end of file diff --git a/Tests/AutoCollection/azure-coretracing.tests.ts b/Tests/AutoCollection/azure-coretracing.tests.ts deleted file mode 100644 index 09dc3d2ab..000000000 --- a/Tests/AutoCollection/azure-coretracing.tests.ts +++ /dev/null @@ -1,129 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); -import { Span, BasicTracerProvider, TracerConfig } from "@opentelemetry/sdk-trace-base"; -import { SpanKind, ROOT_CONTEXT } from "@opentelemetry/api"; - -import AppInsights = require("../../applicationinsights"); -import { channel } from "diagnostic-channel"; -import { enable } from "../../AutoCollection/diagnostic-channel/azure-coretracing.sub"; -import { DependencyTelemetry } from "../../Declarations/Contracts"; - -describe("diagnostic-channel/azure-coretracing", () => { - var sandbox: sinon.SinonSandbox; - - const tracer = new BasicTracerProvider().getTracer("default"); - - before(() => { - sandbox = sinon.sandbox.create(); - }); - - afterEach(() => { - AppInsights.dispose(); - sandbox.restore(); - }); - - it("should call trackDependency for client spans", () => { - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - AppInsights.start(); - const trackRequestStub = sandbox.stub(AppInsights.defaultClient, "trackRequest"); - const trackDependencyStub = sandbox.stub(AppInsights.defaultClient, "trackDependency"); - enable(true, AppInsights.defaultClient); - const span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.CLIENT, - "parentSpanId" - ); - channel.publish("azure-coretracing", span); - assert.ok(trackRequestStub.notCalled); - assert.ok(trackDependencyStub.called); - var dependency = trackDependencyStub.args[0][0] - assert.deepEqual(dependency.name, "test span"); - }); - - it("should call trackDependency for internal spans", () => { - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - AppInsights.start(); - const trackRequestStub = sandbox.stub(AppInsights.defaultClient, "trackRequest"); - const trackDependencyStub = sandbox.stub(AppInsights.defaultClient, "trackDependency"); - enable(true, AppInsights.defaultClient); - const span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.INTERNAL, - "parentSpanId" - ); - channel.publish("azure-coretracing", span); - assert.ok(trackRequestStub.notCalled); - assert.ok(trackDependencyStub.called); - var dependency = trackDependencyStub.args[0][0] - assert.deepEqual(dependency.name, "test span"); - }); - - it("should call trackDependency for producer spans", () => { - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - AppInsights.start(); - const trackRequestStub = sandbox.stub(AppInsights.defaultClient, "trackRequest"); - const trackDependencyStub = sandbox.stub(AppInsights.defaultClient, "trackDependency"); - enable(true, AppInsights.defaultClient); - const span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.PRODUCER, - "parentSpanId" - ); - channel.publish("azure-coretracing", span); - assert.ok(trackRequestStub.notCalled); - assert.ok(trackDependencyStub.called); - var dependency = trackDependencyStub.args[0][0] - assert.deepEqual(dependency.name, "test span"); - }); - - it("should call trackRequest for server span", () => { - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - AppInsights.start(); - const trackRequestStub = sandbox.stub(AppInsights.defaultClient, "trackRequest"); - const trackDependencyStub = sandbox.stub(AppInsights.defaultClient, "trackDependency"); - enable(true, AppInsights.defaultClient); - const span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.SERVER, - "parentSpanId" - ); - channel.publish("azure-coretracing", span); - assert.ok(trackRequestStub.called); - assert.ok(trackDependencyStub.notCalled); - var request = trackRequestStub.args[0][0] - assert.deepEqual(request.name, "test span"); - }); - - it("should call trackRequest for consumer span", () => { - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - AppInsights.start(); - const trackRequestStub = sandbox.stub(AppInsights.defaultClient, "trackRequest"); - const trackDependencyStub = sandbox.stub(AppInsights.defaultClient, "trackDependency"); - enable(true, AppInsights.defaultClient); - const span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.CONSUMER, - "parentSpanId" - ); - channel.publish("azure-coretracing", span); - assert.ok(trackRequestStub.called); - assert.ok(trackDependencyStub.notCalled); - var request = trackRequestStub.args[0][0] - assert.deepEqual(request.name, "test span"); - }); -}); diff --git a/Tests/AutoCollection/bunyan.tests.ts b/Tests/AutoCollection/bunyan.tests.ts deleted file mode 100644 index 7232c4a44..000000000 --- a/Tests/AutoCollection/bunyan.tests.ts +++ /dev/null @@ -1,50 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); -import AppInsights = require("../../applicationinsights"); -import { channel } from "diagnostic-channel"; -import { enable, dispose as disable } from "../../AutoCollection/diagnostic-channel/bunyan.sub"; -import { bunyan } from "diagnostic-channel-publishers"; -import Util = require("../../Library/Util"); - -describe("diagnostic-channel/bunyan", () => { - afterEach(() => { - AppInsights.dispose(); - disable(); - }); - it("should call trackException for errors, trackTrace for logs", () => { - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - AppInsights.start(); - - const trackExceptionStub = sinon.stub(AppInsights.defaultClient, "trackException"); - const trackTraceStub = sinon.stub(AppInsights.defaultClient, "trackTrace"); - - disable(); - enable(true, AppInsights.defaultClient); - const logEvent: bunyan.IBunyanData = { - result: "test log", - level: 50 // Error should still log as MessageData - }; - - const dummyError = { stack: "Test error" }; - const bunyanJson = Util.stringify({ err: dummyError }); - const errorEvent: bunyan.IBunyanData = { - result: bunyanJson, - level: 10, // Verbose should still log as ExceptionData - }; - - channel.publish("bunyan", logEvent); - assert.ok(trackExceptionStub.notCalled); - assert.ok(trackTraceStub.calledOnce); - assert.deepEqual(trackTraceStub.args[0][0].message, "test log"); - trackExceptionStub.reset(); - trackTraceStub.reset(); - - channel.publish("bunyan", errorEvent); - assert.ok(trackExceptionStub.calledOnce); - assert.ok(trackTraceStub.notCalled); - assert.deepEqual(trackExceptionStub.args[0][0].exception, dummyError); - - trackExceptionStub.restore(); - trackTraceStub.restore(); - }); -}); diff --git a/Tests/AutoCollection/winston.tests.ts b/Tests/AutoCollection/winston.tests.ts deleted file mode 100644 index f63e12a12..000000000 --- a/Tests/AutoCollection/winston.tests.ts +++ /dev/null @@ -1,51 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); -import AppInsights = require("../../applicationinsights"); -import { channel, IStandardEvent } from "diagnostic-channel"; -import { enable, dispose as disable } from "../../AutoCollection/diagnostic-channel/winston.sub"; -import { winston } from "diagnostic-channel-publishers"; - -describe("diagnostic-channel/winston", () => { - afterEach(() => { - AppInsights.dispose(); - disable(); - }); - it("should call trackException for errors, trackTrace for logs", () => { - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - AppInsights.start(); - - const trackExceptionStub = sinon.stub(AppInsights.defaultClient, "trackException"); - const trackTraceStub = sinon.stub(AppInsights.defaultClient, "trackTrace"); - - disable(); - enable(true, AppInsights.defaultClient); - const logEvent: winston.IWinstonData = { - message: "test log", - meta: {}, - level: "foo", - levelKind: "npm" - }; - const dummyError = new Error("test error"); - const errorEvent: winston.IWinstonData = { - message: dummyError as any, - meta: {}, - level: "foo", - levelKind: "npm" - }; - - channel.publish("winston", logEvent); - assert.ok(trackExceptionStub.notCalled); - assert.ok(trackTraceStub.calledOnce); - assert.deepEqual(trackTraceStub.args[0][0].message, "test log"); - trackExceptionStub.reset(); - trackTraceStub.reset(); - - channel.publish("winston", errorEvent); - assert.ok(trackExceptionStub.calledOnce); - assert.ok(trackTraceStub.notCalled); - assert.deepEqual(trackExceptionStub.args[0][0].exception, dummyError); - - trackExceptionStub.restore(); - trackTraceStub.restore(); - }); -}); diff --git a/Tests/BackCompatibility/Node10Types/main.ts b/Tests/BackCompatibility/Node10Types/main.ts deleted file mode 100644 index c22db64e0..000000000 --- a/Tests/BackCompatibility/Node10Types/main.ts +++ /dev/null @@ -1,6 +0,0 @@ -import appInsights = require("applicationinsights"); -import http = require("http"); - -appInsights.setup().start(); -appInsights.defaultClient.trackEvent({name: "Test event"}); -appInsights.defaultClient.trackNodeHttpDependency({options: {}, request: http.request({})}); \ No newline at end of file diff --git a/Tests/BackCompatibility/OldTSC/main.ts b/Tests/BackCompatibility/OldTSC/main.ts deleted file mode 100644 index c22db64e0..000000000 --- a/Tests/BackCompatibility/OldTSC/main.ts +++ /dev/null @@ -1,6 +0,0 @@ -import appInsights = require("applicationinsights"); -import http = require("http"); - -appInsights.setup().start(); -appInsights.defaultClient.trackEvent({name: "Test event"}); -appInsights.defaultClient.trackNodeHttpDependency({options: {}, request: http.request({})}); \ No newline at end of file diff --git a/Tests/BackCompatibility/OldTSC/package-lock.json b/Tests/BackCompatibility/OldTSC/package-lock.json deleted file mode 100644 index 549e8f469..000000000 --- a/Tests/BackCompatibility/OldTSC/package-lock.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "name": "oldtsc", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "oldtsc", - "version": "1.0.0", - "license": "UNLICENSED", - "devDependencies": { - "@types/node": "^8.0.0", - "typescript": "4.1.2" - } - }, - "node_modules/@types/node": { - "version": "8.10.66", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", - "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==", - "dev": true - }, - "node_modules/typescript": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz", - "integrity": "sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - } - }, - "dependencies": { - "@types/node": { - "version": "8.10.66", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", - "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==", - "dev": true - }, - "typescript": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz", - "integrity": "sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==", - "dev": true - } - } -} diff --git a/Tests/Bootstrap/Default.spec.ts b/Tests/Bootstrap/Default.spec.ts deleted file mode 100644 index cd865a06c..000000000 --- a/Tests/Bootstrap/Default.spec.ts +++ /dev/null @@ -1,118 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); -import { DiagnosticLogger } from "../../Bootstrap/DiagnosticLogger"; -import * as DataModel from "../../Bootstrap/DataModel"; -import * as Helpers from "../../Bootstrap/Helpers"; -import * as DefaultTypes from "../../Bootstrap/Default"; - -const appInsights = require("../../applicationinsights"); - -class LoggerSpy implements DataModel.AgentLogger { - public logCount = 0 - public errorCount = 0; - - public log() { - this.logCount++; - } - public error() { - this.errorCount++; - } -} - -describe("#setupAndStart()", () => { - const startSpy = sinon.spy(appInsights, "start"); - - before(() => { - startSpy.reset(); - }); - - afterEach(() => { - startSpy.reset(); - delete require.cache[require.resolve("../../Bootstrap/Default")]; - }); - - after(() => { - startSpy.restore(); - }); - - it("should return the client if started multiple times", () => { - const logger = new LoggerSpy(); - const origEnv = process.env.ApplicationInsightsAgent_EXTENSION_VERSION; - process.env.ApplicationInsightsAgent_EXTENSION_VERSION = "~2"; - const alreadyExistsStub = sinon.stub(Helpers, "sdkAlreadyExists", () => false); - - // Test - const Default = require("../../Bootstrap/Default") as typeof DefaultTypes; - Default.setLogger(new DiagnosticLogger(logger)); - const instance1 = Default.setupAndStart("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.ok(instance1.defaultClient); - const instance2 = Default.setupAndStart("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.deepEqual(instance1.defaultClient, instance2.defaultClient); - assert.deepEqual(instance1.defaultClient["_telemetryProcessors"].length, 2) - assert.deepEqual(instance2.defaultClient["_telemetryProcessors"].length, 2) - - // Cleanup - alreadyExistsStub.restore(); - instance1.dispose(); - instance2.dispose(); - process.env.ApplicationInsightsAgent_EXTENSION_VERSION = origEnv; - }) - - it("should setup and start the SDK", () => { - // Setup env vars before requiring loader - const logger = new LoggerSpy(); - const origEnv = process.env.ApplicationInsightsAgent_EXTENSION_VERSION; - process.env.ApplicationInsightsAgent_EXTENSION_VERSION = "~2"; - const alreadyExistsStub = sinon.stub(Helpers, "sdkAlreadyExists", () => false); - - // Test - const Default = require("../../Bootstrap/Default") as typeof DefaultTypes; - Default.setLogger(new DiagnosticLogger(logger)); - const instance = Default.setupAndStart("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.deepEqual(instance, appInsights); - - // Cleanup - alreadyExistsStub.restore(); - instance.dispose(); - process.env.ApplicationInsightsAgent_EXTENSION_VERSION = origEnv; - - // start was called once - assert.equal(startSpy.callCount, 1); - - // No logging was done - assert.equal(logger.logCount, 1); - assert.equal(logger.errorCount, 0); - }); - - it("should not setup and start the SDK if no setupString is provided", () => { - // Setup - const logger = new LoggerSpy(); - const origEnv = process.env.ApplicationInsightsAgent_EXTENSION_VERSION; - const origIkey = process.env.APPINSIGHTS_INSTRUMENTATIONKEY; - const origCs = process.env.APPLICATIONINSIGHTS_CONNECTION_STRING; - process.env.ApplicationInsightsAgent_EXTENSION_VERSION = "~2"; - delete process.env.APPINSIGHTS_INSTRUMENTATIONKEY; - delete process.env.APPLICATIONINSIGHTS_CONNECTION_STRING; - const alreadyExistsStub = sinon.stub(Helpers, "sdkAlreadyExists", () => false); - - // Test - const Default = require("../../Bootstrap/Default") as typeof DefaultTypes; - Default.setLogger(new DiagnosticLogger(logger)); - const instance = Default.setupAndStart(); - assert.equal(instance, null); - - // Cleanup - alreadyExistsStub.restore(); - process.env.ApplicationInsightsAgent_EXTENSION_VERSION = origEnv; - - // start was never called - assert.equal(startSpy.callCount, 0); - - // No logging was done - assert.equal(logger.logCount, 0); - assert.equal(logger.errorCount, 1, "Should log if attach is attempted"); - - process.env.APPINSIGHTS_INSTRUMENTATIONKEY = origIkey; - process.env.APPLICATIONINSIGHTS_CONNECTION_STRING = origCs; - }); -}); diff --git a/Tests/Bootstrap/DiagnosticLogger.spec.ts b/Tests/Bootstrap/DiagnosticLogger.spec.ts deleted file mode 100644 index a21c52f55..000000000 --- a/Tests/Bootstrap/DiagnosticLogger.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); -import { AgentLogger } from "../../Bootstrap/DataModel"; -import { DiagnosticLogger } from "../../Bootstrap/DiagnosticLogger"; -import { NoopLogger } from "../../Bootstrap/NoopLogger"; -import * as DataModel from "../../Bootstrap/DataModel"; - -class TestWriter implements AgentLogger { - prev: any; - - log(message?: any, ...optional: any[]): void { - this.prev = message; - } - - error(message?: any, ...optional: any[]): void { - this.log(message, ...optional); - } -} - -describe("DiagnosticLogger", () => { - const logger = new DiagnosticLogger(new NoopLogger()); - const stub = sinon.stub(logger["_writer"], "log"); - const version = require("../../../package.json").version; - - afterEach(() => { - stub.reset(); - }) - - describe("#DiagnosticLogger.DefaultEnvelope", () => { - it("should have the correct version string", () => { - assert.equal(DiagnosticLogger.DefaultEnvelope.properties.sdkVersion, version); - }); - }); - - describe("#DiagnosticLogger.logMessage", () => { - it("should log all required fields", () => { - const expectedDate = new Date().toISOString(); - logger.logMessage("Some message"); - assert.deepEqual(stub.args[0][0], { - level: DataModel.SeverityLevel.INFO, - message: "Some message", - logger: "applicationinsights.extension.diagnostics", - time: expectedDate, - properties: { - language: "nodejs", - operation: "Startup", - siteName: undefined, - ikey: undefined, - extensionVersion: undefined, - sdkVersion: version, - subscriptionId: null, - } - } as DataModel.DiagnosticLog) - }) - }); -}); diff --git a/Tests/Bootstrap/FileWriter.spec.ts b/Tests/Bootstrap/FileWriter.spec.ts deleted file mode 100644 index 2607c17de..000000000 --- a/Tests/Bootstrap/FileWriter.spec.ts +++ /dev/null @@ -1,186 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); -import path = require("path"); -import os = require("os"); -import fs = require("fs"); -import { FileWriter, homedir } from "../../Bootstrap/FileWriter"; -import FileHelpers = require("../../Bootstrap/Helpers/FileHelpers"); - -describe("FileWriter", () => { - if (!FileWriter.isNodeVersionCompatible()) { - return; - } - - const filedir = path.join(homedir, "LogFiles/ApplicationInsights/test"); - describe("#constructor()", () => { - it("should return a ready FileWriter", () => { - if (parseInt(process.versions.node.split(".")[0]) >= 1) { - const writer = new FileWriter(filedir, "test.txt"); - assert.deepEqual(writer["_ready"], true); - assert.deepEqual(fs.existsSync(filedir), true); - } else { - assert.ok(true, "skipped"); - } - }) - }); - - describe("#log()", () => { - it("should not log if the FileWriter is not ready", () => { - const writer = new FileWriter(filedir, "test.txt"); - const stub = sinon.stub(writer, "_writeFile"); - writer["_ready"] = false; - - assert.ok(stub.notCalled); - writer.log("foo"); - assert.ok(stub.notCalled); - - stub.restore(); - }); - }); - - describe("#error()", () => { - it("should call log()", () => { - const writer = new FileWriter(filedir, "test.txt"); - const stub = sinon.stub(writer, "log"); - - assert.ok(stub.notCalled); - writer.error("example"); - assert.ok(stub.calledOnce); - assert.deepEqual(stub.args[0][0], "example"); - stub.restore(); - }); - }); - - describe("#_writeFile()", () => { - it("should write a new file in custom folder", (done) => { - const writer = new FileWriter(os.tmpdir(), "tempfile.txt"); - writer.callback = (err) => { - assert.deepEqual(err, null); - const content = fs.readFileSync(path.join(os.tmpdir(), "tempfile.txt"), "utf8"); - assert.deepEqual(content, "temp:foo"); - done(); - } - writer.log("temp:foo"); - }); - - it("should write a new file", (done) => { - const writer = new FileWriter(filedir, "newfile.txt"); - writer.callback = (err) => { - assert.deepEqual(err, null); - const content = fs.readFileSync(path.join(filedir, "newfile.txt"), "utf8"); - assert.deepEqual(content, "newfile #1"); - done(); - } - writer["_writeFile"]("newfile #1"); - }); - - it("should overwrite an existing file, if one already exists", (done) => { - const writer = new FileWriter(filedir, "test.txt"); - writer.callback = (err) => { - assert.deepEqual(err, null); - const content = fs.readFileSync(path.join(filedir, "test.txt"), "utf8"); - assert.deepEqual(content, "write #1"); - writer.callback = (err) => { - assert.deepEqual(err, null); - const content = fs.readFileSync(path.join(filedir, "test.txt"), "utf8"); - assert.deepEqual(content, "write #2"); - done(); - } - writer["_writeFile"]("write #2"); - } - writer["_writeFile"]("write #1"); - }); - }); - - describe("#_appendFile()", () => { - it("should append the file contents", (done) => { - let counter = 0; - try { - // Try to delete the file we are appending - fs.unlinkSync(path.join(filedir, "append.txt")); - } catch (e) {} - const writer = new FileWriter(filedir, "append.txt", { append: true }); - writer.callback = (err) => { - if (counter < 3) { - counter += 1; - writer.log(`line #${counter}`); - } else { - const content = fs.readFileSync(path.join(filedir, "append.txt"), "utf8"); - assert.deepEqual(content, "line #0\nline #1\nline #2\nline #3\n"); - done(); - } - } - writer.log(`line #${counter}`); - }); - }); - - describe("#_addCloseHandler()", () => { - it("should add the delete handler when configured to do so", (done) => { - const writer = new FileWriter(filedir, "test.txt"); - writer.callback = (err) => { - assert.equal(err, null); - done(); - } - writer.log("example"); - }); - }); - - describe("#_shouldRenameFile()", () => { - it("should return true when the date has changed", (done) => { - try { - // Try to delete the file we are testing - fs.unlinkSync(path.join(filedir, "clocktest.txt")); - } catch (e) {} - const sandbox = sinon.sandbox.create(); - const clock = sandbox.useFakeTimers(Date.now()); - const writer = new FileWriter(filedir, "clocktest.txt"); - writer.callback = (err) => { - assert.equal(err, null); - writer["_shouldRenameFile"]((err1, shouldRename1) => { - assert.deepEqual(err1, null); - assert.deepEqual(shouldRename1, false); - clock.tick(86400000); // 1 day - writer["_shouldRenameFile"]((err2, shouldRename2) => { - assert.deepEqual(err2, null); - assert.deepEqual(shouldRename2, true); - clock.restore(); - sandbox.restore(); - done(); - }); - }); - } - writer.log("message"); - }); - }); - - describe("#renameCurrentFile()", () => { - it("should rename the current file", (done) => { - const writer = new FileWriter(filedir, "renametest.txt"); - writer.callback = (err) => { - assert.deepEqual(err, null); - const birthdate = new Date(fs.statSync(path.join(filedir, "renametest.txt")).birthtime); - - // Rename the file - FileHelpers.renameCurrentFile(filedir, "renametest.txt", (err, renamedfullpath) => { - // Assert previously named file no longer exists - try { - const content = fs.readFileSync(path.join(filedir, "renametest.txt")); - assert.ok(false, "File should no longer exist"); - } catch (e) { - assert.deepEqual(e.code, "ENOENT", "File should no longer exist"); - } - - // Assert renamed file has identical contents and was renamed properly - assert.deepEqual(renamedfullpath, path.join(filedir, `renametest-${birthdate.toISOString().replace(/[T:\.]/g, "_").replace("Z", "")}.txt.old`)); - const content = fs.readFileSync(renamedfullpath, "utf8"); - assert.deepEqual(content, "foo"); - - // Cleanup - fs.unlinkSync(renamedfullpath); - done(); - }); - } - writer.log("foo"); // create the file - }); - }); -}); diff --git a/Tests/Bootstrap/StatusLogger.spec.ts b/Tests/Bootstrap/StatusLogger.spec.ts deleted file mode 100644 index 11a3c14fb..000000000 --- a/Tests/Bootstrap/StatusLogger.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import * as fs from "fs"; -import assert = require("assert"); -import sinon = require("sinon"); -import path = require("path"); -import os = require("os"); -import { StatusLogger } from "../../Bootstrap/StatusLogger"; -import { FileWriter, homedir } from "../../Bootstrap/FileWriter"; - -describe("#logStatus()", () => { - it("should write a status file to disk", (done) => { - const filepath = path.join(homedir, "LogFiles/ApplicationInsights/status"); - const filename = StatusLogger.DEFAULT_FILE_NAME; - const fileWriter = new FileWriter(filepath, filename); - if (!FileWriter.isNodeVersionCompatible()) { - done(); - } else { - const statusLogger = new StatusLogger(fileWriter); - statusLogger.logStatus(StatusLogger.DEFAULT_STATUS, (err: Error) => { - assert.equal(err, null); - const buffer = JSON.parse(fs.readFileSync(path.join(filepath, filename), "utf8")); - assert.deepEqual(buffer, StatusLogger.DEFAULT_STATUS); - done(); - }); - } - }); - - it("should write status to console", () => { - const consoleStub = sinon.stub(console, "log"); - - // Act - const statusLogger = new StatusLogger(console); - statusLogger.logStatus(StatusLogger.DEFAULT_STATUS); - - // Assert - assert.ok(consoleStub.calledOnce); - assert.deepEqual(consoleStub.args[0][0], StatusLogger.DEFAULT_STATUS); - - // Cleanup - consoleStub.restore(); - }); -}); diff --git a/Tests/EndToEnd.tests.ts b/Tests/EndToEnd.tests.ts deleted file mode 100644 index 0699c1a51..000000000 --- a/Tests/EndToEnd.tests.ts +++ /dev/null @@ -1,973 +0,0 @@ -import http = require("http"); -import https = require("https"); -import assert = require("assert"); -import path = require("path") -import os = require("os") -import fs = require('fs'); -import sinon = require("sinon"); -import events = require("events"); -import child_process = require("child_process"); -import nock = require("nock"); -import AppInsights = require("../applicationinsights"); -import Sender = require("../Library/Sender"); -import Traceparent = require("../Library/Traceparent"); -import { EventEmitter } from "events"; -import { CorrelationContextManager } from "../AutoCollection/CorrelationContextManager"; -import Constants = require("../Declarations/Constants"); -import Contracts = require("../Declarations/Contracts"); -import HeartBeat = require("../AutoCollection/HeartBeat"); -import TelemetryClient = require("../Library/TelemetryClient"); -import Context = require("../Library/Context"); -import Util = require("../Library/Util"); -import { JsonConfig } from "../Library/JsonConfig"; -import { FileAccessControl } from "../Library/FileAccessControl"; -import FileSystemHelper = require("../Library/FileSystemHelper"); - -/** - * A fake response class that passes by default - */ -class fakeResponse { - private callbacks: { [event: string]: (data?: any) => void } = Object.create(null); - public setEncoding(): void { }; - public statusCode: number = 200; - private _responseData: any; - - constructor(private passImmediately: boolean = true, responseData?: any) { - this._responseData = responseData ? responseData : "data"; - } - - public on(event: string, callback: () => void) { - if (!this.callbacks[event]) { - this.callbacks[event] = callback; - } else { - var lastCallback = this.callbacks[event]; - this.callbacks[event] = () => { - callback(); - lastCallback(); - }; - } - - if (event == "end" && this.passImmediately) { - this.pass(true); - } - } - - public emit(eventName: string, ...args: any[]): boolean { - return true; - } - - public addListener(eventName: string, listener: () => void): void { - this.on(eventName, listener); - } - - public removeListener(eventName: string, listener: () => void) { - - } - - public pass(test = false): void { - this.callbacks["data"] ? this.callbacks["data"](this._responseData) : null; - this.callbacks["end"] ? this.callbacks["end"]() : null; - this.callbacks["finish"] ? this.callbacks["finish"]() : null; - } - - public end = this.pass; - public once = this.on; -} - -/** - * A fake request class that fails by default - */ -class fakeRequest { - private callbacks: { [event: string]: Function } = Object.create(null); - public write(): void { } - public headers: { [id: string]: string } = {}; - public agent = { protocol: 'http' }; - private _responseData: any; - - constructor(private failImmediatly: boolean = true, public url: string = undefined, responseData?: any) { - this._responseData = responseData; - } - - public on(event: string, callback: Function) { - this.callbacks[event] = callback; - if (event === "error" && this.failImmediatly) { - setImmediate(() => this.fail()); - } - } - - public emit(eventName: string, ...args: any[]): boolean { - return true; - } - - public addListener(eventName: string, listener: Function): void { - this.on(eventName, listener); - } - - public removeListener(eventName: string, listener?: Function) { - - } - - public fail(): void { - this.callbacks["error"] ? this.callbacks["error"]() : null; - } - - public end(): void { - this.callbacks["end"] ? this.callbacks["end"](new fakeResponse(true, this._responseData)) : null; - } -} - -/** - * A fake http server - */ -class fakeHttpServer extends events.EventEmitter { - public setCallback(callback: any) { - this.on("request", callback); - } - - public listen(port: any, host?: any, backlog?: any, callback?: any) { - this.emit("listening"); - } - - public emitRequest(url: string) { - var request = new fakeRequest(false, url); - var response = new fakeResponse(false); - this.emit("request", request, response); - request.end(); - } -} - -/** - * A fake https server class that doesn't require ssl certs - */ -class fakeHttpsServer extends events.EventEmitter { - - public setCallback(callback: any) { - this.on("request", callback); - } - - public listen(port: any, host?: any, backlog?: any, callback?: any) { - this.emit("listening"); - } - - public emitRequest(url: string) { - var request = new fakeRequest(false, url); - var response = new fakeResponse(false); - this.emit("request", request, response); - request.end(); - response.pass(); - } -} - -describe("EndToEnd", () => { - var sandbox: sinon.SinonSandbox; - var originalEnv = {}; - let interceptor: nock.Interceptor; - - var breezeResponse: Contracts.BreezeResponse = { - itemsAccepted: 1, - itemsReceived: 1, - errors: [] - }; - - before(() => { - sandbox = sinon.sandbox.create(); - var newEnv = <{ [id: string]: string }>{}; - Util.tlsRestrictedAgent = new https.Agent(); - newEnv["APPLICATION_INSIGHTS_NO_STATSBEAT"] = "true"; - newEnv["TMP"] = process.env.TMP; - newEnv["TMPDIR"] = process.env.TMPDIR; - newEnv["TEMP"] = process.env.TEMP; - originalEnv = process.env; - process.env = newEnv; - - interceptor = nock(Constants.DEFAULT_BREEZE_ENDPOINT) - .post("/v2.1/track", (body: string) => { - return true; - }); - nock.disableNetConnect(); - }); - - after(() => { - process.env = originalEnv; - nock.cleanAll(); - nock.enableNetConnect(); - }); - - describe("Basic usage", () => { - let nockScope: nock.Scope; - - before(() => { - nockScope = interceptor.reply(200, breezeResponse).persist(); - }); - - beforeEach(() => { - JsonConfig["_instance"] = undefined; - }); - - afterEach(() => { - // Dispose the default app insights client and auto collectors so that they can be reconfigured - // cleanly for each test - sandbox.restore(); - - CorrelationContextManager.reset(); - AppInsights.dispose(); - }); - - it("should send telemetry", (done) => { - const expectedTelemetryData: AppInsights.Contracts.AvailabilityTelemetry = { - duration: 100, id: "id1", message: "message1", success: true, name: "name1", runLocation: "east us" - }; - - var client = new AppInsights.TelemetryClient("iKey"); - - client.trackEvent({ name: "test event" }); - client.trackException({ exception: new Error("test error") }); - client.trackMetric({ name: "test metric", value: 3 }); - client.trackTrace({ message: "test trace" }); - client.trackAvailability(expectedTelemetryData); - - client.flush({ - callback: (response) => { - assert.ok(response, "response should not be empty"); - assert.ok(response !== "no data to send", "response should have data"); - done(); - } - }); - }); - - it("should collect http request telemetry", (done) => { - var fakeHttpSrv = new fakeHttpServer(); - sandbox.stub(http, 'createServer', (callback: (req: http.ServerRequest, res: http.ServerResponse) => void) => { - fakeHttpSrv.setCallback(callback); - return fakeHttpSrv; - }); - - AppInsights - .setup("ikey") - .setAutoCollectRequests(true) - .start(); - - var track = sandbox.stub(AppInsights.defaultClient, 'track'); - http.createServer((req, res) => { - assert.equal(track.callCount, 0); - res.end(); - assert.equal(track.callCount, 1); - done(); - }); - - fakeHttpSrv.emitRequest("http://localhost:0/test"); - }); - - it("should collect https request telemetry", (done) => { - var fakeHttpSrv = new fakeHttpServer(); - sandbox.stub(https, 'createServer', (options: any, callback: (req: http.ServerRequest, res: http.ServerResponse) => void) => { - fakeHttpSrv.setCallback(callback); - return fakeHttpSrv; - }); - - AppInsights - .setup("ikey") - .setAutoCollectRequests(true) - .start(); - - var track = sandbox.stub(AppInsights.defaultClient, 'track'); - https.createServer(null, (req: http.ServerRequest, res: http.ServerResponse) => { - assert.equal(track.callCount, 0); - res.end(); - assert.equal(track.callCount, 1); - done(); - }); - - fakeHttpSrv.emitRequest("http://localhost:0/test"); - }); - - it("should collect http dependency telemetry", (done) => { - var eventEmitter = new EventEmitter(); - (eventEmitter).method = "GET"; - sandbox.stub(http, 'request', (url: string, c: Function) => { - process.nextTick(c); - return eventEmitter; - }); - - AppInsights - .setup("ikey") - .setAutoCollectDependencies(true) - .start(); - - var track = sandbox.stub(AppInsights.defaultClient, 'track'); - - http.request('http://test.com', (c) => { - assert.equal(track.callCount, 0); - eventEmitter.emit("response", {}); - assert.equal(track.callCount, 1); - done(); - }); - }); - - it("should collect https dependency telemetry", (done) => { - sandbox.restore(); - var eventEmitter = new EventEmitter(); - (eventEmitter).method = "GET"; - sandbox.stub(https, 'request', (url: string, c: Function) => { - process.nextTick(c); - return eventEmitter; - }); - - AppInsights - .setup("ikey") - .setAutoCollectDependencies(true) - .start(); - - var track = sandbox.stub(AppInsights.defaultClient, 'track'); - - https.request('https://test.com', (c) => { - assert.equal(track.callCount, 0); - eventEmitter.emit("response", {}); - assert.equal(track.callCount, 1); - done(); - }); - }); - - it("should add correlation context if not available", (done) => { - var eventEmitter = new EventEmitter(); - (eventEmitter).method = "GET"; - sandbox.stub(http, 'request', (url: string, c: Function) => { - process.nextTick(c); - return eventEmitter; - }); - - let generateContextSpy = sandbox.spy(CorrelationContextManager, "generateContextObject"); - AppInsights - .setup("ikey") - .setAutoCollectDependencies(true) - .setAutoDependencyCorrelation(true); - AppInsights.start(); - sandbox.stub(AppInsights.defaultClient, 'track'); - - http.request('http://test.com', (c) => { - assert.equal(generateContextSpy.callCount, 1); - done(); - }); - }); - }); - - describe("W3C mode", () => { - let nockScope: nock.Scope; - - before(() => { - nockScope = interceptor.reply(200, breezeResponse).persist(); - }); - - beforeEach(() => { - JsonConfig["_instance"] = undefined; - }); - - afterEach(() => { - // Dispose the default app insights client and auto collectors so that they can be reconfigured - // cleanly for each test - sandbox.restore(); - CorrelationContextManager.reset(); - AppInsights.dispose(); - }); - - it("should pass along traceparent/tracestate header if present in current operation", (done) => { - var eventEmitter = new EventEmitter(); - (eventEmitter as any).headers = {}; - (eventEmitter as any)["getHeader"] = function (name: string) { return this.headers[name]; }; - (eventEmitter as any)["setHeader"] = function (name: string, value: string) { this.headers[name] = value; }; - (eventEmitter).method = "GET"; - sandbox.stub(https, 'request', (url: string, c: Function) => { - process.nextTick(c); - return eventEmitter; - }); - - AppInsights - .setup("ikey") - .setAutoCollectDependencies(true) - .start(); - - sandbox.stub(CorrelationContextManager, "getCurrentContext", () => ({ - operation: { - traceparent: new Traceparent("00-5e84aff3af474588a42dcbf3bd1db95f-1fc066fb77fa43a3-00"), - tracestate: "sometracestate" - }, - customProperties: { - serializeToHeader: (): null => null - } - })); - https.request('https://test.com', (c) => { - eventEmitter.emit("response", {}); - assert.ok((eventEmitter as any).headers["request-id"].match(/^\|[0-z]{32}\.[0-z]{16}\./g)); - assert.ok((eventEmitter as any).headers.traceparent.match(/^00-5e84aff3af474588a42dcbf3bd1db95f-[0-z]{16}-00$/)); - assert.notEqual((eventEmitter as any).headers.traceparent, "00-5e84aff3af474588a42dcbf3bd1db95f-1fc066fb77fa43a3-00"); - assert.equal((eventEmitter as any).headers.tracestate, "sometracestate"); - AppInsights.defaultClient.flush(); - done(); - }); - }); - - it("should create and pass a traceparent header if w3c is enabled", (done) => { - var eventEmitter = new EventEmitter(); - (eventEmitter as any).headers = {}; - (eventEmitter as any)["getHeader"] = function (name: string) { return this.headers[name]; }; - (eventEmitter as any)["setHeader"] = function (name: string, value: string) { this.headers[name] = value; }; - (eventEmitter).method = "GET"; - sandbox.stub(https, 'request', (url: string, c: Function) => { - process.nextTick(c); - return eventEmitter; - }); - var CorrelationIdManager = require("../Library/CorrelationIdManager"); - - AppInsights - .setup("ikey") - .setAutoCollectDependencies(true) - .start(); - - CorrelationIdManager.w3cEnabled = true; - - sandbox.stub(CorrelationContextManager, "getCurrentContext", () => ({ - operation: { - }, - customProperties: { - serializeToHeader: (): null => null - } - })); - https.request('https://test.com', (c) => { - eventEmitter.emit("response", {}); - assert.ok((eventEmitter as any).headers.traceparent.match(/^00-[0-z]{32}-[0-z]{16}-[0-9a-f]{2}/g), "traceparent header is passed, 00-W3C-W3C-00"); - assert.ok((eventEmitter as any).headers["request-id"].match(/^\|[0-z]{32}\.[0-z]{16}\./g), "back compat header is also passed, |W3C.W3C." + (eventEmitter as any).headers["request-id"]); - CorrelationIdManager.w3cEnabled = false; - AppInsights.defaultClient.flush(); - done(); - }); - }); - }); - - describe("Disk retry mode", () => { - var CorrelationIdManager = require("../Library/CorrelationIdManager"); - var writeFile: sinon.SinonStub; - var writeFileSync: sinon.SinonStub; - var readFile: sinon.SinonStub; - var lstat: sinon.SinonStub; - var mkdir: sinon.SinonStub; - var existsSync: sinon.SinonStub; - var readdir: sinon.SinonStub; - var readdirSync: sinon.SinonStub; - var stat: sinon.SinonStub; - var statSync: sinon.SinonStub; - var mkdirSync: sinon.SinonStub; - var spawn: sinon.SinonStub; - var spawnSync: sinon.SinonStub; - - let nockScope: nock.Scope; - - beforeEach(() => { - nockScope = interceptor.reply(503, { "errors": [{ "index": 0, "statusCode": 503 }] }); - AppInsights.defaultClient = undefined; - sandbox.stub(CorrelationIdManager, 'queryCorrelationId'); // TODO: Fix method of stubbing requests to allow CID to be part of E2E tests - writeFile = sandbox.stub(FileSystemHelper, 'writeFileAsync'); - writeFileSync = sandbox.stub(fs, 'writeFileSync'); - existsSync = sandbox.stub(fs, 'existsSync').returns(true); - readdir = sandbox.stub(FileSystemHelper, 'readdirAsync').returns(['1.ai.json']); - readdirSync = sandbox.stub(fs, 'readdirSync').returns(['1.ai.json']); - stat = sandbox.stub(FileSystemHelper, 'statAsync').returns({ isFile: () => true, size: 8000 }); - statSync = sandbox.stub(fs, 'statSync').returns({ isFile: () => true, size: 8000 }); - lstat = sandbox.stub(FileSystemHelper, 'lstatAsync').returns({ isDirectory: () => true }); - mkdir = sandbox.stub(FileSystemHelper, 'mkdirAsync').returns(null); - mkdirSync = sandbox.stub(fs, 'mkdirSync').returns(null); - readFile = sandbox.stub(FileSystemHelper, 'readFileAsync').returns(''); - spawn = sandbox.stub(child_process, 'spawn').returns({ - on: (type: string, cb: any) => { - if (type === 'close') { - cb(0); - } - }, - stdout: { - on: (type: string, cb: any) => { - if (type === 'data') { - cb('stdoutmock'); - } - } - } - }); - if (child_process.spawnSync) { - spawnSync = sandbox.stub(child_process, 'spawnSync').returns({ status: 0, stdout: 'stdoutmock' }); - } - JsonConfig["_instance"] = undefined; - }); - - afterEach(() => { - sandbox.restore(); - AppInsights.dispose(); - }); - - it("disabled by default for new clients", (done) => { - var client = new AppInsights.TelemetryClient("key"); - client.trackEvent({ name: "test event" }); - - client.flush({ - callback: (response: any) => { - // yield for the caching behavior - setImmediate(() => { - assert(writeFile.callCount === 0); - done(); - }); - } - }); - }); - - it("enabled by default for default client", (done) => { - AppInsights.setup("key").start(); - var client = AppInsights.defaultClient; - client.trackEvent({ name: "test event" }); - client.flush({ - callback: (response: any) => { - // yield for the caching behavior - setImmediate(() => { - assert.equal(writeFile.callCount, 1); - done(); - }); - } - }); - }); - - it("stores data to disk when enabled", (done) => { - var client = new AppInsights.TelemetryClient("key"); - client.channel.setUseDiskRetryCaching(true); - - client.trackEvent({ name: "test event" }); - - client.flush({ - callback: (response: any) => { - // yield for the caching behavior - setImmediate(() => { - assert.equal(writeFile.callCount, 1); - assert.equal( - path.dirname(writeFile.firstCall.args[0]), - path.join(os.tmpdir(), Sender.TEMPDIR_PREFIX + "key")); - assert.equal(writeFile.firstCall.args[2].mode, 0o600, "File must not have weak permissions"); - assert.equal(spawn.callCount, 0); - done(); - }); - } - }); - }); - - it("uses WindowsIdentity to get the identity for ICACLS", (done) => { - var client = new AppInsights.TelemetryClient("uniquekey"); - client.channel.setUseDiskRetryCaching(true); - var origICACLS = FileAccessControl.USE_ICACLS; - FileAccessControl.USE_ICACLS = true; // Simulate ICACLS environment even on *nix - - // Clear ICACLS caches for test purposes - FileAccessControl["ACLED_DIRECTORIES"] = {}; - FileAccessControl["ACL_IDENTITY"] = null; - - client.trackEvent({ name: "test event" }); - client.flush({ - callback: (response: any) => { - // yield for the caching behavior - setImmediate(() => { - assert.equal(writeFile.callCount, 1); - assert.equal(spawn.callCount, 2); - - // First external call should be to powershell to query WindowsIdentity - assert(spawn.firstCall.args[0].indexOf('powershell.exe')); - assert.equal(spawn.firstCall.args[1][0], "-Command"); - assert.equal(spawn.firstCall.args[1][1], "[System.Security.Principal.WindowsIdentity]::GetCurrent().Name"); - assert.equal(FileAccessControl["ACL_IDENTITY"], 'stdoutmock'); - - // Next call should be to ICACLS (with the acquired identity) - assert(spawn.lastCall.args[0].indexOf('icacls.exe')); - assert.equal(spawn.lastCall.args[1][3], "/grant"); - assert.equal(spawn.lastCall.args[1][4], "stdoutmock:(OI)(CI)F"); - - FileAccessControl["USE_ICACLS"] = origICACLS; - done(); - }); - } - }); - }); - - it("refuses to store data if ACL identity fails", (done) => { - spawn.restore(); - var tempSpawn = sandbox.stub(child_process, 'spawn').returns({ - on: (type: string, cb: any) => { - if (type == 'close') { - cb(2000); // return non-zero status code - } - }, - stdout: { - on: (type: string, cb: any) => { - return; // do nothing - } - } - }); - var client = new AppInsights.TelemetryClient("uniquekey"); - client.channel.setUseDiskRetryCaching(true); - var origICACLS = FileAccessControl.USE_ICACLS; - FileAccessControl.USE_ICACLS = true; // Simulate ICACLS environment even on *nix - // Clear ICACLS caches for test purposes - FileAccessControl["ACLED_DIRECTORIES"] = {}; - FileAccessControl["ACL_IDENTITY"] = null; - - client.trackEvent({ name: "test event" }); - client.flush({ - callback: (response: any) => { - // yield for the caching behavior - setImmediate(() => { - assert(writeFile.callCount === 0); - assert.equal(tempSpawn.callCount, 1); - FileAccessControl.USE_ICACLS = origICACLS; - done(); - }); - } - }); - }); - - it("refuses to query for ACL identity twice", (done) => { - spawn.restore(); - var tempSpawn = sandbox.stub(child_process, 'spawn').returns({ - on: (type: string, cb: any) => { - if (type == 'close') { - cb(2000); // return non-zero status code - } - }, - stdout: { - on: (type: string, cb: any) => { - return; // do nothing - } - } - }); - var client = new AppInsights.TelemetryClient("uniquekey"); - client.channel.setUseDiskRetryCaching(true); - var origICACLS = FileAccessControl.USE_ICACLS; - FileAccessControl.USE_ICACLS = true; // Simulate ICACLS environment even on *nix - - // Clear ICACLS caches for test purposes - FileAccessControl["ACLED_DIRECTORIES"] = {}; - FileAccessControl["ACL_IDENTITY"] = null; - - client.trackEvent({ name: "test event" }); - - client.flush({ - callback: (response: any) => { - // yield for the caching behavior - setTimeout(() => { - assert(writeFile.callCount === 0); - assert.equal(tempSpawn.callCount, 1); - - client.trackEvent({ name: "test event" }); - - client.flush({ - callback: (response: any) => { - // yield for the caching behavior - setImmediate(() => { - // The call counts shouldnt have changed - assert(writeFile.callCount === 0); - assert.equal(tempSpawn.callCount, 1); - FileAccessControl.USE_ICACLS = origICACLS; - done(); - }); - } - }); - }, 100); - } - }); - }); - - it("refuses to query for ACL identity twice (process never returned)", (done) => { - spawn.restore(); - var tempSpawn = sandbox.stub(child_process, 'spawn').returns({ - on: (type: string, cb: any) => { - return; // do nothing - }, - stdout: { - on: (type: string, cb: any) => { - return; // do nothing - } - } - }); - - var client = new AppInsights.TelemetryClient("uniquekey"); - client.channel.setUseDiskRetryCaching(true); - var origICACLS = FileAccessControl.USE_ICACLS; - FileAccessControl.USE_ICACLS = true; // Simulate ICACLS environment even on *nix - - // Clear ICACLS caches for test purposes - FileAccessControl["ACLED_DIRECTORIES"] = {}; - FileAccessControl["ACL_IDENTITY"] = null; - - client.trackEvent({ name: "test event" }); - client.flush({ - callback: (response: any) => { - // yield for the caching behavior - setImmediate(() => { - assert(writeFile.callCount === 0); - assert.equal(tempSpawn.callCount, 1); - client.trackEvent({ name: "test event" }); - client.flush({ - callback: (response: any) => { - // yield for the caching behavior - setImmediate(() => { - // The call counts shouldnt have changed - assert(writeFile.callCount === 0); - assert.equal(tempSpawn.callCount, 1); - FileAccessControl.USE_ICACLS = origICACLS; - done(); - }); - } - }); - }); - } - }); - }); - - it("refuses to store data if ICACLS fails", (done) => { - spawn.restore(); - var tempSpawn = sandbox.stub(child_process, 'spawn').returns({ - on: (type: string, cb: any) => { - if (type == 'close') { - cb(2000); // return non-zero status code - } - } - }); - - var client = new AppInsights.TelemetryClient("uniquekey"); - client.channel.setUseDiskRetryCaching(true); - var origICACLS = FileAccessControl.USE_ICACLS; - FileAccessControl.USE_ICACLS = true; // Simulate ICACLS environment even on *nix - - // Clear ICACLS caches for test purposes - FileAccessControl["ACLED_DIRECTORIES"] = {}; - FileAccessControl["ACL_IDENTITY"] = null; - - client.trackEvent({ name: "test event" }); - client.flush({ - callback: (response: any) => { - // yield for the caching behavior - setImmediate(() => { - assert(writeFile.callCount === 0); - assert.equal(tempSpawn.callCount, 1); - FileAccessControl.USE_ICACLS = origICACLS; - done(); - }); - } - }); - }); - - it("creates directory when nonexistent", (done) => { - lstat.restore(); - sandbox.stub(FileSystemHelper, 'lstatAsync').throws({ code: "ENOENT" }); - var client = new AppInsights.TelemetryClient("key"); - client.channel.setUseDiskRetryCaching(true); - - client.trackEvent({ name: "test event" }); - client.flush({ - callback: (response: any) => { - setTimeout(() => { - assert.equal(mkdir.callCount, 1); - assert.equal(mkdir.firstCall.args[0], path.join(os.tmpdir(), Sender.TEMPDIR_PREFIX + "key")); - assert.equal(writeFile.callCount, 1); - assert.equal( - path.dirname(writeFile.firstCall.args[0]), - path.join(os.tmpdir(), Sender.TEMPDIR_PREFIX + "key")); - assert.equal(writeFile.firstCall.args[2].mode, 0o600, "File must not have weak permissions"); - done(); - }, 100); - } - }); - }); - - it("does not store data when limit is below directory size", (done) => { - var client = new AppInsights.TelemetryClient("key"); - client.channel.setUseDiskRetryCaching(true, null, 10); // 10 bytes is less than synthetic directory size (see file size in stat mock) - - client.trackEvent({ name: "test event" }); - - client.flush({ - callback: (response: any) => { - // yield for the caching behavior - setImmediate(() => { - assert(writeFile.callCount === 0); - done(); - }); - } - }); - }); - - it("checks for files when connection is back online", (done) => { - var client = new AppInsights.TelemetryClient("key"); - client.channel.setUseDiskRetryCaching(true, 0); - client.trackEvent({ name: "test event" }); - client.flush({ - callback: (response: any) => { - // yield for the caching behavior - setImmediate(() => { - assert.equal(writeFile.callCount, 1); - interceptor.reply(200, breezeResponse); - client.trackEvent({ name: "test event" }); - client.flush({ - callback: (response: any) => { - // wait until sdk looks for offline files - setTimeout(() => { - assert.equal(readdir.callCount, 2); - assert.equal(readFile.callCount, 1); - assert.equal( - path.dirname(readFile.firstCall.args[0]), - path.join(os.tmpdir(), Sender.TEMPDIR_PREFIX + "key")); - done(); - }, 100); - } - }); - }); - } - }); - }); - - it("cache payload synchronously when process crashes", () => { - var client = new AppInsights.TelemetryClient("key2"); - client.channel.setUseDiskRetryCaching(true); - - client.trackEvent({ name: "test event" }); - client.channel.triggerSend(true); - - assert(existsSync.callCount === 1); - assert(writeFileSync.callCount === 1); - assert.equal(spawnSync.callCount, os.type() === "Windows_NT" ? 1 : 0); // This is implicitly testing caching of ACL identity (otherwise call count would be 2 like it is the non-sync time) - assert.equal( - path.dirname(writeFileSync.firstCall.args[0]), - path.join(os.tmpdir(), Sender.TEMPDIR_PREFIX + "key2")); - assert.equal(writeFileSync.firstCall.args[2].mode, 0o600, "File must not have weak permissions"); - }); - - it("use WindowsIdentity to get ACL identity when process crashes (ICACLS)", () => { - var client = new AppInsights.TelemetryClient("key22"); - client.channel.setUseDiskRetryCaching(true); - var origICACLS = FileAccessControl.USE_ICACLS; - FileAccessControl.USE_ICACLS = true; // Simulate ICACLS environment even on *nix - - // Clear ICACLS caches for test purposes - FileAccessControl["ACLED_DIRECTORIES"] = {}; - FileAccessControl["ACL_IDENTITY"] = null; - - client.trackEvent({ name: "test event" }); - client.channel.triggerSend(true); - - // First external call should be to powershell to query WindowsIdentity - assert(spawnSync.firstCall.args[0].indexOf('powershell.exe')); - assert.equal(spawnSync.firstCall.args[1][0], "-Command"); - assert.equal(spawnSync.firstCall.args[1][1], "[System.Security.Principal.WindowsIdentity]::GetCurrent().Name"); - assert.equal(FileAccessControl["ACL_IDENTITY"], 'stdoutmock'); - - // Next call should be to ICACLS (with the acquired identity) - assert(spawnSync.lastCall.args[0].indexOf('icacls.exe')); - assert.equal(spawnSync.lastCall.args[1][3], "/grant"); - assert.equal(spawnSync.lastCall.args[1][4], "stdoutmock:(OI)(CI)F"); - - FileAccessControl.USE_ICACLS = origICACLS; - }); - - it("refuses to cache payload when process crashes if ICACLS fails", () => { - spawnSync.restore(); - var tempSpawnSync = sandbox.stub(child_process, 'spawnSync').returns({ status: 2000 }); - var client = new AppInsights.TelemetryClient("key3"); // avoid icacls cache by making key unique - client.channel.setUseDiskRetryCaching(true); - var origICACLS = FileAccessControl.USE_ICACLS; - FileAccessControl.USE_ICACLS = true; // Simulate ICACLS environment even on *nix - - client.trackEvent({ name: "test event" }); - client.channel.triggerSend(true); - - assert(existsSync.callCount === 1); - assert(writeFileSync.callCount === 0); - - if (child_process.spawnSync) { - assert.equal(tempSpawnSync.callCount, 1); - FileAccessControl.USE_ICACLS = origICACLS; - } - }); - }); - - describe("Heartbeat metrics for VM", () => { - beforeEach(() => { - JsonConfig["_instance"] = undefined; - }); - - afterEach(() => { - sandbox.restore(); - }); - - it("should collect correct VM information from JSON response", (done) => { - // set up stub - const vmDataJSON = `{ - "vmId": "1", - "subscriptionId": "2", - "osType": "Windows_NT" - }`; - var stub: sinon.SinonStub = sandbox.stub(http, "request", (options: any, callback: any) => { - var req = new fakeRequest(false, "http://169.254.169.254", vmDataJSON); - req.on("end", callback); - return req; - }); - - // set up sdk - const client = new TelemetryClient("key"); - const heartbeat: HeartBeat = new HeartBeat(client); - heartbeat.enable(true); - HeartBeat.INSTANCE.enable(true); - const trackMetricStub = sandbox.stub(heartbeat["_client"], "trackMetric"); - - heartbeat["trackHeartBeat"](client.config, () => { - assert.equal(trackMetricStub.callCount, 1, "should call trackMetric for the VM heartbeat metric"); - assert.equal(trackMetricStub.args[0][0].name, "HeartBeat", "should use correct name for heartbeat metric"); - assert.equal(trackMetricStub.args[0][0].value, 0, "value should be 0"); - const keys = Object.keys(trackMetricStub.args[0][0].properties); - assert.equal(keys.length, 5, "should have 4 kv pairs added when resource type is VM"); - assert.equal(keys[0], "sdk", "sdk should be added as a key"); - assert.equal(keys[1], "osType", "osType should be added as a key"); - assert.equal(keys[2], "azInst_vmId", "azInst_vmId should be added as a key"); - assert.equal(keys[3], "azInst_subscriptionId", "azInst_subscriptionId should be added as a key"); - assert.equal(keys[4], "azInst_osType", "azInst_osType should be added as a key"); - - const properties = trackMetricStub.args[0][0].properties; - assert.equal(properties["sdk"], Context.sdkVersion, "sdk version should be read from Context"); - assert.equal(properties["osType"], os.type(), "osType should be read from os library"); - assert.equal(properties["azInst_vmId"], "1", "azInst_vmId should be read from response"); - assert.equal(properties["azInst_subscriptionId"], "2", "azInst_subscriptionId should be read from response"); - assert.equal(properties["azInst_osType"], "Windows_NT", "azInst_osType should be read from response"); - done(); - }); - }); - - it("should only send name and value properties for heartbeat metric when get VM request fails", (done) => { - // set up stub - var stub: sinon.SinonStub = sandbox.stub(http, "request", (options: any, callback: any) => { - var req = new fakeRequest(true, "http://169.254.169.254"); - return req; - }); - - // set up sdk - const client = new TelemetryClient("key"); - const heartbeat: HeartBeat = new HeartBeat(client); - heartbeat.enable(true); - HeartBeat.INSTANCE.enable(true); - const trackMetricStub = sandbox.stub(heartbeat["_client"], "trackMetric"); - - heartbeat["trackHeartBeat"](client.config, () => { - assert.equal(trackMetricStub.callCount, 1, "should call trackMetric as heartbeat metric"); - assert.equal(trackMetricStub.args[0][0].name, "HeartBeat", "should use correct name for heartbeat metric"); - assert.equal(trackMetricStub.args[0][0].value, 0, "value should be 0"); - const keys = Object.keys(trackMetricStub.args[0][0].properties); - assert.equal(keys.length, 2, "should have 2 kv pairs added when resource type is not web app, not function app, not VM"); - assert.equal(keys[0], "sdk", "sdk should be added as a key"); - assert.equal(keys[1], "osType", "osType should be added as a key"); - - const properties = trackMetricStub.args[0][0].properties; - assert.equal(properties["sdk"], Context.sdkVersion, "sdk version should be read from Context"); - assert.equal(properties["osType"], os.type(), "osType should be read from os library"); - done(); - }); - }); - }); -}); diff --git a/Tests/FunctionalTests/Runner/TestSequence.json b/Tests/FunctionalTests/Runner/TestSequence.json deleted file mode 100644 index 481e767e7..000000000 --- a/Tests/FunctionalTests/Runner/TestSequence.json +++ /dev/null @@ -1,15 +0,0 @@ -[ -{"path": "/_longRunTest", "steps": ["PostgresQuery", "HttpGet", "AITrackDep", "RedisSet"]}, -{"path": "/_perfRun", "steps": ["PostgresQuery", "HttpGet", "BunyanTrace", "RedisSet"]}, -{"path": "/dependencyTestBasic", "steps": ["HttpGet", "Timeout", "HttpGet"]}, -{"path": "/manualTracks", "steps": ["AITrackDep", "AITrackTrace", "AITrackExc"]}, -{"path": "/deepRequest", "steps": ["HttpGet", "Timeout", "Timeout", "HttpGet", "AITrackDep", "AITrackTrace", "Timeout", "AITrackExc"]}, -{"path": "/diagChannelMongo", "steps": ["MongoInsert", "MongoInsertMany", "MongoUpdateOne", "MongoCreateIndex", "MongoFind"]}, -{"path": "/diagChannelBunyan", "steps": ["BunyanFatal", "BunyanError", "BunyanWarn", "BunyanInfo", "BunyanDebug", "BunyanTrace"]}, -{"path": "/diagChannelConsole", "steps": ["ConsoleLog", "ConsoleWarn", "ConsoleError", "ConsoleInfo"]}, -{"path": "/diagChannelMySql", "steps": ["MySQLQuery", "Timeout", "MySQLQuery"]}, -{"path": "/diagChannelRedis", "steps": ["RedisGet", "RedisSet", "RedisSet2", "RedisHset", "RedisHkeys", "RedisHincrby"]}, -{"path": "/diagChannelWinston", "steps": ["WinstonError", "WinstonWarn", "WinstonInfo", "WinstonVerbose", "WinstonDebug", "WinstonSilly", "WinstonError2", "WinstonWarn2", "WinstonInfo2"]}, -{"path": "/diagChannelPostgres", "steps": ["PostgresQuery", "Timeout", "PostgresQuery"]}, -{"path": "/deepRequestDiagChannel", "steps": ["HttpGet", "RedisGet", "PostgresQuery", "RedisSet", "BunyanWarn", "AITrackTrace", "MongoInsert", "AITrackExc"]} -] diff --git a/Tests/FunctionalTests/TestApp/Main.js b/Tests/FunctionalTests/TestApp/Main.js deleted file mode 100644 index 9304410d3..000000000 --- a/Tests/FunctionalTests/TestApp/Main.js +++ /dev/null @@ -1,80 +0,0 @@ -var Config = require("./Config"); -var appInsights = null; -if (Config.AppInsightsEnabled) { - appInsights = require("applicationinsights"); - appInsights.setup(Config.InstrumentationKey); - appInsights.defaultClient.config.endpointUrl = Config.EndpointBaseAddress+"/v2.1/track"; - appInsights.defaultClient.config.profileQueryEndpoint = Config.EndpointBaseAddress; - appInsights.defaultClient.config.samplingPercentage = parseFloat(Config.SampleRate); - appInsights.Configuration.setAutoDependencyCorrelation(Config.UseAutoCorrelation); - appInsights.Configuration.setAutoCollectRequests(Config.UseAutoRequests); - appInsights.Configuration.setAutoCollectPerformance(Config.UseAutoPerformance); - appInsights.Configuration.setAutoCollectExceptions(Config.UseAutoExceptions); - appInsights.Configuration.setAutoCollectDependencies(Config.UseAutoDependencies); - appInsights.Configuration.setAutoCollectConsole(Config.UseAutoConsole, Config.UseAutoConsoleLog); - appInsights.Configuration.setUseDiskRetryCaching(Config.UseDiskCaching); - appInsights.Configuration.setDistributedTracingMode(Config.DistributedTracingMode); - appInsights.start(); -} - -var Tasks = require("./Tasks"); -var port = parseInt(Config.ServerPort); -var bodyParser = require('body-parser'); -var express = require("express"); -var http = require("http"); -var app = express(); -app.use(bodyParser.json()); - -app.get("/", (req, res) => { - res.send("OK"); -}); - -/** - * Receive route configuration object of the following form as POST body: - * [ - * {path: "/dependencyTest", steps:["HttpGet", "Timeout", "MongoInsert"]}, - * ... - * ] - * - * This input will create routes on this server that perform those tasks. - * The available tasks are defined in /Tasks/index.js - */ -app.post("/_configure", (req, res) => { - var stepConfig = req.body; - - var runTasks = (tasks, cb) => { - if (!tasks || tasks.length == 0) { - cb(); - return; - } - tasks = tasks.slice(0); - var task = tasks.shift(); - Tasks[task](()=>runTasks(tasks, cb)); - } - - var generateStepRoute = (route) => { - app.get(route.path, (rq, rs) => { - runTasks(route.steps, ()=>rs.send("OK")); - }); - } - - for (var i=0; i < stepConfig.length; i++) { - generateStepRoute(stepConfig[i]); - } - - res.send("OK"); -}); - -app.get("/_close", (req, res) => { - res.end("OK"); - server.close(); - if (Config.AppInsightsEnabled) { - appInsights.defaultClient.flush(); - appInsights.dispose(); - process.exit(0); - } -}); - -var server = app.listen(port, () => { - console.log("TestApp Ready!"); -}); diff --git a/Tests/FunctionalTests/TestApp/Tasks/AISDK.js b/Tests/FunctionalTests/TestApp/Tasks/AISDK.js deleted file mode 100644 index bd3f1d022..000000000 --- a/Tests/FunctionalTests/TestApp/Tasks/AISDK.js +++ /dev/null @@ -1,22 +0,0 @@ -var Config = require("../Config"); - -var appInsights = null; -if (Config.AppInsightsEnabled) { - appInsights = require("applicationinsights"); -} - -function wrap(fn) { - return (cb) => { - if (Config.AppInsightsEnabled) { - fn(); - } - cb(); - return; - } -} - -module.exports = { - trackDependency: wrap(()=>appInsights.defaultClient.trackDependency({name: "Manual dependency", dependencyTypeName: "Manual", duration: 200, success: true})), - trackException: wrap(()=>appInsights.defaultClient.trackException({exception: new Error("Manual track error")})), - trackTrace: wrap(()=>appInsights.defaultClient.trackTrace({message: "Manual track trace"})) -} \ No newline at end of file diff --git a/Tests/FunctionalTests/TestApp/Tasks/Postgres.js b/Tests/FunctionalTests/TestApp/Tasks/Postgres.js deleted file mode 100644 index b83cbf646..000000000 --- a/Tests/FunctionalTests/TestApp/Tasks/Postgres.js +++ /dev/null @@ -1,40 +0,0 @@ -var Config = require("../Config"); -var pg = require('pg'); - -var ready = false; -var client = null; - -function connect() { - client = new pg.Pool({connectionString: Config.PostgresConnectionString}); - client.connect((err) => { - if (err) { - setTimeout(connect, 500); - return; - } - client.query(` - CREATE TABLE IF NOT EXISTS test_table ( - id SERIAL, - data varchar(100) NOT NULL default '', - PRIMARY KEY (id) - );`, () => { - ready = true; - }); - }); -} -connect(); - -function query(callback) { - if (!ready) { - setTimeout(() => query(callback), 500); - return; - } - - client.query(`SELECT * FROM test_table`, (v, x) => { - callback() - }); -} - - -module.exports = { - query: query -} \ No newline at end of file diff --git a/Tests/Library/AuthorizationHandler.tests.ts b/Tests/Library/AuthorizationHandler.tests.ts deleted file mode 100644 index f4e7aedf7..000000000 --- a/Tests/Library/AuthorizationHandler.tests.ts +++ /dev/null @@ -1,74 +0,0 @@ -import assert = require("assert"); -import https = require("https"); -import sinon = require("sinon"); -import azureCore = require("@azure/core-http"); - -import AuthorizationHandler = require("../../Library/AuthorizationHandler"); -import Config = require("../../Library/Config"); -import Util = require("../../Library/Util"); - -class TestTokenCredential implements azureCore.TokenCredential { - private _expiresOn: Date; - private _numberOfRefreshs = 0; - - constructor(expiresOn?: Date) { - this._expiresOn = expiresOn || new Date(); - } - - async getToken(scopes: string | string[], options?: any): Promise { - this._numberOfRefreshs++; - return { - token: "testToken" + this._numberOfRefreshs, - expiresOnTimestamp: this._expiresOn - }; - } -} - -describe("Library/AuthorizationHandler", () => { - - var sandbox: sinon.SinonSandbox; - Util.tlsRestrictedAgent = new https.Agent(); - - beforeEach(() => { - sandbox = sinon.sandbox.create(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - describe("#addAuthorizationHeader()", () => { - it("should add Authorization header to options", async () => { - var config = new Config(""); - config.aadTokenCredential = new TestTokenCredential(); - var handler = new AuthorizationHandler(config.aadTokenCredential); - var options = { - method: "POST", - headers: <{ [key: string]: string }>{ - "Content-Type": "application/x-json-stream" - } - }; - await handler.addAuthorizationHeader(options); - assert.equal(options.headers["authorization"], "Bearer testToken1"); - }); - - it("should refresh token if expired", async () => { - var config = new Config(""); - var tokenCredential = new TestTokenCredential(new Date(new Date().getMilliseconds() - 500)); - config.aadTokenCredential = tokenCredential; - var handler = new AuthorizationHandler(config.aadTokenCredential); - var options: https.RequestOptions = { - method: "POST", - headers: <{ [key: string]: string }>{ - "Content-Type": "application/x-json-stream" - }, - protocol: "HTTPS" - }; - await handler.addAuthorizationHeader(options); - assert.equal(options.headers["authorization"], "Bearer testToken1"); - await handler.addAuthorizationHeader(options); - assert.equal(tokenCredential["_numberOfRefreshs"], 2); - assert.equal(options.headers["authorization"], "Bearer testToken2"); - }); - }); -}); \ No newline at end of file diff --git a/Tests/Library/Channel.tests.ts b/Tests/Library/Channel.tests.ts deleted file mode 100644 index 2382ef073..000000000 --- a/Tests/Library/Channel.tests.ts +++ /dev/null @@ -1,150 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); - -import Channel = require("../../Library/Channel"); -import Contracts = require("../../Declarations/Contracts"); -import Logging = require("../../Library/Logging"); - -class ChannelMock extends Channel { - public getBuffer() { - return this._buffer; - } - - public getTimeoutHandle() { - return this._timeoutHandle; - } -} - -describe("Library/Channel", () => { - - var testEnvelope = new Contracts.Envelope(); - var sender = { - saveOnCrash: <(s: string) => void>((str) => null), - send: <(b: Buffer) => void>((buffer) => null) - }; - - var sendSpy = sinon.spy(sender, "send"); - var saveSpy = sinon.spy(sender, "saveOnCrash"); - - var channel: ChannelMock; - var config: any; - var clock: any; - before(() => clock = sinon.useFakeTimers()); - after(() => clock.restore()); - - beforeEach(() => { - config = { - isDisabled: false, - batchSize: 3, - batchInterval: 10 - }; - - channel = new ChannelMock( - () => config.isDisabled, - () => config.batchSize, - () => config.batchInterval, - sender); - }); - - afterEach(() => { - sendSpy.reset(); - saveSpy.reset(); - }); - - describe("#send(envelope)", () => { - it("should enqueue telemetry", () => { - channel.send(testEnvelope); - clock.tick(config.batchInterval); - assert.ok(sendSpy.calledOnce); - assert.equal(sendSpy.firstCall.args[0][0], testEnvelope); - }); - - it("should do nothing if disabled", () => { - config.isDisabled = true; - channel.send(testEnvelope); - clock.tick(config.batchInterval); - assert.ok(sendSpy.notCalled); - }); - - it("should log warning if invalid input is passed", () => { - var warnStub = sinon.stub(Logging, "warn"); - channel.send(undefined); - channel.send(null); - channel.send(""); - assert.ok(warnStub.calledThrice); - warnStub.restore(); - }); - - it("should flush the buffer when full", () => { - for (var i = 0; i < config.batchSize; i++) { - channel.send(testEnvelope); - } - - assert.ok(sendSpy.calledOnce); - assert.ok(channel.getBuffer().length === 0); - }); - - it("should add the payload to the buffer", () => { - channel.send(testEnvelope); - assert.ok(channel.getBuffer().length === 1); - assert.ok(channel.getBuffer()[0] === testEnvelope); - }); - - it("should start the timer if not started", () => { - assert.ok(!channel.getTimeoutHandle()); - channel.send(testEnvelope); - assert.ok(channel.getTimeoutHandle()); - }); - - it("should clear timeout handle after flushing", () => { - for (var i = 0; i < config.batchSize; i++) { - channel.send(testEnvelope); - } - - assert.ok(!channel.getTimeoutHandle(), "cleared after buffer full"); - - channel.send(testEnvelope); - clock.tick(config.batchInterval); - assert.ok(!channel.getTimeoutHandle(), "cleared after batch interval"); - }); - }); - - describe("#triggerSend(isCrash)", () => { - it("should clear timeout handle", () => { - channel.send(testEnvelope); - channel.triggerSend(false); - assert.ok(sendSpy.calledOnce); - assert.ok(saveSpy.notCalled); - assert.ok(channel.getBuffer().length === 0); - assert.ok(!channel.getTimeoutHandle()); - }); - - it("should save to disk if crashing", () => { - channel.send(testEnvelope); - channel.triggerSend(true); - assert.ok(sendSpy.notCalled); - assert.ok(saveSpy.calledOnce); - assert.ok(channel.getBuffer().length === 0); - assert.ok(!channel.getTimeoutHandle()); - }); - - it("should not send if empty", () => { - channel.triggerSend(false); - assert.ok(sendSpy.notCalled); - assert.ok(saveSpy.notCalled); - }); - - it("should call callback when empty", () => { - var callback = sinon.spy(); - channel.triggerSend(false, callback); - assert.ok(callback.calledOnce); - }); - - it("should call callback when crashing", () => { - channel.send(testEnvelope); - var callback = sinon.spy(); - channel.triggerSend(true, callback); - assert.ok(callback.calledOnce); - }); - }); -}); diff --git a/Tests/Library/Client.tests.ts b/Tests/Library/Client.tests.ts deleted file mode 100644 index 553a1967a..000000000 --- a/Tests/Library/Client.tests.ts +++ /dev/null @@ -1,1129 +0,0 @@ -import assert = require("assert"); -import https = require("https"); -import sinon = require("sinon"); -import Sinon = require("sinon"); -import eventEmitter = require('events'); -import azureCore = require("@azure/core-http"); - -import AutoCollecPreAggregatedMetrics = require("../../AutoCollection/PreAggregatedMetrics"); -import Client = require("../../Library/NodeClient"); -import Config = require("../../Library/Config"); -import Contracts = require("../../Declarations/Contracts"); -import RequestResponseHeaders = require("../../Library/RequestResponseHeaders"); -import Util = require("../../Library/Util"); -import EnvelopeFactory = require("../../Library/EnvelopeFactory"); - -describe("Library/TelemetryClient", () => { - - Util.tlsRestrictedAgent = new https.Agent(); - - var iKey = "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"; - var appId = "Application-Key-12345-6789A"; - var name = "name"; - var value = 3; - var testEventTelemetry = { name: "testEvent" }; - var properties: { [key: string]: string; } = { - p1: "p1", p2: "p2", common: "commonArg" - }; - const requestExpectedProperties = { - ...properties, - "_MS.ProcessedByMetricExtractors": "(Name:'Requests', Ver:'1.1')", - } - const dependencyExpectedProperties = { - ...properties, - "_MS.ProcessedByMetricExtractors": "(Name:'Dependencies', Ver:'1.1')", - } - const exceptionExpectedProperties = { - ...properties, - "_MS.ProcessedByMetricExtractors": "(Name:'Exceptions', Ver:'1.1')", - } - const traceExpectedProperties = { - "_MS.ProcessedByMetricExtractors": "(Name:'Traces', Ver:'1.1')", - } - var failedProperties: { [key: string]: string; } = { - p1: "p1", p2: "p2", common: "commonArg", errorProp: "errorVal" - }; - var measurements: { [key: string]: number; } = { m1: 1, m2: 2 }; - var client = new Client(iKey); - client.config.correlationId = `cid-v1:${appId}`; - var trackStub: Sinon.SinonStub; - var triggerStub: Sinon.SinonStub; - var sendStub: Sinon.SinonStub; - var saveOnCrashStub: Sinon.SinonStub; - - before(() => { - trackStub = sinon.stub(client, "track"); - triggerStub = sinon.stub(client.channel, "triggerSend"); - sendStub = sinon.stub(client.channel, "send"); - saveOnCrashStub = sinon.stub(client.channel._sender, "saveOnCrash"); - }); - after(() => { - trackStub.restore(); - triggerStub.restore(); - sendStub.restore(); - saveOnCrashStub.restore(); - - }); - - afterEach(() => { - sendStub.reset(); - client.clearTelemetryProcessors(); - saveOnCrashStub.reset(); - }) - - var invalidInputHelper = (name: string) => { - assert.doesNotThrow(() => (client)[name](null, null), "#1"); - assert.doesNotThrow(() => (client)[name](undefined, undefined), "#2"); - assert.doesNotThrow(() => (client)[name]({}, {}), "#3"); - assert.doesNotThrow(() => (client)[name]([], []), "#4"); - assert.doesNotThrow(() => (client)[name]("", ""), "#5"); - assert.doesNotThrow(() => (client)[name](1, 1), "#6"); - assert.doesNotThrow(() => (client)[name](true, true), "#7"); - }; - - describe("#constructor()", () => { - it("should initialize config", () => { - var client = new Client("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.ok(client.config); - assert.ok(client.config.instrumentationKey); - }); - - it("should initialize context", () => { - var client = new Client("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.ok(client.context); - assert.ok(client.context.tags); - }); - - it("should initialize common properties", () => { - var client = new Client("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.ok(client.commonProperties); - }); - - it("should initialize channel", () => { - var client = new Client("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.ok(client.channel); - }); - - it("should initialize authorization handler", () => { - var client = new Client("InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;"); - client.config.aadTokenCredential = { - async getToken(scopes: string | string[], options?: any): Promise { - return { token: "testToken", }; - } - }; - assert.ok(client.getAuthorizationHandler(client.config)); - }); - }); - - describe("#trackEvent()", () => { - it("should track Event with correct data", () => { - trackStub.reset(); - client.trackEvent({ name: name }); - client.trackEvent({ name: name, properties }); - client.trackEvent({ name: name, properties, measurements }); - - assert.ok(trackStub.calledThrice); - - var eventTelemetry1 = trackStub.firstCall.args[0]; - var eventTelemetry2 = trackStub.secondCall.args[0]; - var eventTelemetry3 = trackStub.thirdCall.args[0]; - - assert.equal(eventTelemetry1.name, name); - assert.equal(eventTelemetry2.name, name); - assert.deepEqual(eventTelemetry2.properties, properties); - assert.equal(eventTelemetry3.name, name); - assert.deepEqual(eventTelemetry3.properties, properties); - assert.equal(eventTelemetry3.measurements, measurements); - }); - - it("should not crash with invalid input", () => { - invalidInputHelper("trackEvent"); - }); - }); - - describe("#trackPageView()", () => { - it("should track Page View with correct data", () => { - trackStub.reset(); - client.trackPageView({ name: name }); - client.trackPageView({ name: name, properties, measurements }); - client.trackPageView({ name: name, url: "https://www.test.com", duration: 100 }); - - assert.ok(trackStub.calledThrice); - - var eventTelemetry1 = trackStub.firstCall.args[0]; - var eventTelemetry2 = trackStub.secondCall.args[0]; - var eventTelemetry3 = trackStub.thirdCall.args[0]; - - assert.equal(eventTelemetry1.name, name); - assert.equal(eventTelemetry2.name, name); - assert.deepEqual(eventTelemetry2.properties, properties); - assert.deepEqual(eventTelemetry2.measurements, measurements); - assert.equal(eventTelemetry3.name, name); - assert.equal(eventTelemetry3.url, "https://www.test.com"); - assert.equal(eventTelemetry3.duration, 100); - }); - - it("should not crash with invalid input", () => { - invalidInputHelper("trackPageView"); - }); - }); - - describe("#trackTrace()", () => { - it("should track Trace with correct data", () => { - trackStub.reset(); - client.trackTrace({ message: name }); - client.trackTrace({ message: name, severity: 0 }); - client.trackTrace({ message: name, severity: 0, properties: properties }); - - assert.ok(trackStub.calledThrice); - - var traceTelemetry1 = trackStub.firstCall.args[0]; - var traceTelemetry2 = trackStub.secondCall.args[0]; - var traceTelemetry3 = trackStub.thirdCall.args[0]; - - assert.equal(traceTelemetry1.message, name); - assert.equal(traceTelemetry2.message, name); - assert.deepEqual(traceTelemetry2.severity, 0); - assert.equal(traceTelemetry3.message, name); - assert.deepEqual(traceTelemetry3.severity, 0); - assert.equal(traceTelemetry3.properties, properties); - }); - - it("should not crash with invalid input", () => { - invalidInputHelper("trackTrace"); - }); - }); - - - describe("#trackAvailability()", () => { - it("should track availability with correct data", () => { - trackStub.reset(); - const expectedTelemetryData: Contracts.AvailabilityTelemetry = { - duration: 100, id: "id1", message: "message1", success: true, name: "name1", runLocation: "east us" - }; - - client.trackAvailability(expectedTelemetryData); - - assert.ok(trackStub.calledOnce); - - const availabilityTelemetry = trackStub.firstCall.args[0]; - - assert.equal(availabilityTelemetry.message, expectedTelemetryData.message); - assert.equal(availabilityTelemetry.name, expectedTelemetryData.name); - assert.equal(availabilityTelemetry.runLocation, expectedTelemetryData.runLocation); - }); - - it("should not crash with invalid input", () => { - invalidInputHelper("trackAvailability"); - }); - }); - - describe("#trackMetric()", () => { - it("should track Metric with correct data", () => { - trackStub.reset(); - var count = 1; - var min = 0; - var max = 0; - var stdev = 0; - client.trackMetric({ name: name, value: value }); - client.trackMetric({ name: name, value: value, count: count, min: min, max: max, stdDev: stdev, properties: properties }); - - assert.ok(trackStub.calledTwice); - - var metricTelemetry1 = trackStub.firstCall.args[0]; - var metricTelemetry2 = trackStub.secondCall.args[0]; - - assert.equal(metricTelemetry1.name, name); - assert.equal(metricTelemetry1.value, value); - - assert.equal(metricTelemetry2.name, name); - assert.equal(metricTelemetry2.value, value); - assert.equal(metricTelemetry2.count, count); - assert.equal(metricTelemetry2.min, min); - assert.equal(metricTelemetry2.max, max); - assert.equal(metricTelemetry2.stdDev, stdev); - assert.deepEqual(metricTelemetry2.properties, properties); - }); - - it("should not crash with invalid input", () => { - invalidInputHelper("trackMetric"); - }); - }); - - describe("request tracking", () => { - var response = { - emitFinish: function (): void { - if (this.finishCallback) { - this.finishCallback(); - } - }, - once: function (event: string, callback: Function): eventEmitter.EventEmitter { - if (event === 'finish') { - this.finishCallback = callback; - } - return new eventEmitter.EventEmitter(); - }, - statusCode: 200, - headers: <{ [id: string]: string }>{}, - getHeader: function (name: string) { return this.headers[name]; }, - setHeader: function (name: string, value: string) { this.headers[name] = value; }, - }; - - var request = { - emitError: function (): void { - if (this.errorCallback) { - var error = { - "errorProp": "errorVal" - } - this.errorCallback(error); - } - }, - emitResponse: function (): void { - if (this.responseCallback) { - this.responseCallback(response); - } - }, - on: function (event: string, callback: (error: any) => void): void { - if (event === 'error') { - this.errorCallback = callback; - } else if (event === 'response') { - this.responseCallback = callback; - } - }, - method: "GET", - url: "/search?q=test", - connection: { - encrypted: false - }, - agent: { - protocol: 'http' - }, - headers: <{ [id: string]: string }>{ - host: "bing.com" - }, - getHeader: function (name: string) { return this.headers[name]; }, - setHeader: function (name: string, value: string) { this.headers[name] = value; }, - }; - - afterEach(() => { - delete request.headers[RequestResponseHeaders.requestContextHeader]; - delete response.headers[RequestResponseHeaders.requestContextHeader]; - client.config = new Config(iKey); - client.config.correlationId = `cid-v1:${appId}`; - }); - - function parseDuration(duration: string): number { - if (!duration) { - return 0; - } - - var parts = duration.match("(\\d\\d):(\\d\\d):(\\d\\d).(\\d\\d\\d)"); - return parseInt(parts[1]) * 60 * 60 * 1000 + parseInt(parts[2]) * 60 * 1000 + parseInt(parts[3]) * 1000 + parseInt(parts[4]); - } - - describe("#trackNodeHttpRequest()", () => { - var clock: Sinon.SinonFakeTimers; - - before(() => { - clock = sinon.useFakeTimers(); - }); - - after(() => { - clock.restore(); - }); - - it("should not crash with invalid input", () => { - invalidInputHelper("trackRequest"); - }); - - it('should track request with correct data on response finish event ', () => { - trackStub.reset(); - clock.reset(); - client.trackNodeHttpRequest({ request: request, response: response, properties: properties }); - - // finish event was not emitted yet - assert.ok(trackStub.notCalled); - - // emit finish event - clock.tick(10); - response.emitFinish(); - - assert.ok(trackStub.calledOnce); - var requestTelemetry = trackStub.firstCall.args[0]; - - assert.equal(requestTelemetry.resultCode, "200"); - assert.deepEqual(requestTelemetry.properties, properties); - assert.equal(requestTelemetry.duration, 10); - }); - - it('should track request with correct tags on response finish event', () => { - trackStub.reset(); - clock.reset(); - client.trackNodeHttpRequest({ request: request, response: response, properties: properties }); - - // emit finish event - response.emitFinish(); - - // validate - var args = trackStub.args; - assert.ok(trackStub.calledOnce); - var requestTelemetry = trackStub.firstCall.args[0]; - var tags = requestTelemetry.tagOverrides; - - assert.equal(tags["ai.operation.name"], "GET /search"); - assert.equal(tags["ai.device.id"], ""); - assert.equal(tags["ai.device.type"], null); - }); - - it('should track request with tagOverrides on response finish event', () => { - trackStub.reset(); - clock.reset(); - client.trackNodeHttpRequest({ request: request, response: response, properties: properties, tagOverrides: { "custom": "A", "ai.device.id": "B" } }); - - // emit finish event - response.emitFinish(); - - // validate - var args = trackStub.args; - assert.ok(trackStub.calledOnce); - var requestTelemetry = trackStub.firstCall.args[0]; - var tags = requestTelemetry.tagOverrides; - - assert.equal(tags["ai.operation.name"], "GET /search"); - assert.equal(tags["ai.device.id"], "B"); - assert.equal(tags["custom"], "A"); - assert.equal(tags["ai.device.type"], null); - }); - - it('should track request with correct data on request error event #1', () => { - trackStub.reset(); - clock.reset(); - client.trackNodeHttpRequest({ request: request, response: response, properties: properties }); - - // finish event was not emitted yet - assert.ok(trackStub.notCalled); - - // emit finish event - clock.tick(10); - request.emitError(); - assert.ok(trackStub.calledOnce); - var requestTelemetry = trackStub.firstCall.args[0]; - - assert.equal(requestTelemetry.success, false); - assert.deepEqual(requestTelemetry.properties, failedProperties); - assert.equal(requestTelemetry.duration, 10); - }); - - it('should use source and target correlationId headers', () => { - trackStub.reset(); - clock.reset(); - - // Simulate an incoming request that has a different source correlationId header. - let testCorrelationId = 'cid-v1:Application-Id-98765-4321A'; - request.headers[RequestResponseHeaders.requestContextHeader] = `${RequestResponseHeaders.requestContextSourceKey}=${testCorrelationId}`; - - client.trackNodeHttpRequest({ request: request, response: response, properties: properties }); - - // finish event was not emitted yet - assert.ok(trackStub.notCalled); - - // emit finish event - clock.tick(10); - response.emitFinish(); - assert.ok(trackStub.calledOnce); - var requestTelemetry = trackStub.firstCall.args[0]; - assert.equal(requestTelemetry.source, testCorrelationId); - - // The client's correlationId should have been added as the response target correlationId header. - assert.equal(response.headers[RequestResponseHeaders.requestContextHeader], - `${RequestResponseHeaders.requestContextSourceKey}=${client.config.correlationId}`); - }); - - it('should NOT use source and target correlationId headers when url is on the excluded list', () => { - trackStub.reset(); - clock.reset(); - - client.config.correlationHeaderExcludedDomains = ["bing.com"]; - - // Simulate an incoming request that has a different source ikey hash header. - let testCorrelationId = 'cid-v1:Application-Id-98765-4321A'; - request.headers[RequestResponseHeaders.requestContextHeader] = `${RequestResponseHeaders.requestContextSourceKey}=${testCorrelationId}`; - - client.trackNodeHttpRequest({ request: request, response: response, properties: properties }); - - // finish event was not emitted yet - assert.ok(trackStub.notCalled); - - // emit finish event - clock.tick(10); - response.emitFinish(); - assert.ok(trackStub.calledOnce); - - assert.equal(response.headers[RequestResponseHeaders.requestContextHeader], undefined); - }); - }); - - describe("#trackNodeHttpRequestSync()", () => { - it('should track request with correct data synchronously', () => { - trackStub.reset(); - client.trackNodeHttpRequestSync({ request: request, response: response, duration: 100, properties: properties }); - assert.ok(trackStub.calledOnce); - var requestTelemetry = trackStub.firstCall.args[0]; - - assert.equal(requestTelemetry.resultCode, "200"); - assert.equal(requestTelemetry.duration, 100); - assert.deepEqual(requestTelemetry.properties, properties); - - var tags = requestTelemetry.tagOverrides; - - assert.equal(tags["ai.operation.name"], "GET /search"); - assert.equal(tags["ai.device.id"], ""); - assert.equal(tags["ai.device.type"], null); - }); - it('should track request with correct data and tag overrides synchronously', () => { - trackStub.reset(); - client.trackNodeHttpRequestSync({ request: request, response: response, duration: 100, properties: properties, tagOverrides: { "custom": "A", "ai.device.id": "B" } }); - assert.ok(trackStub.calledOnce); - var requestTelemetry = trackStub.firstCall.args[0]; - - assert.equal(requestTelemetry.resultCode, "200"); - assert.equal(requestTelemetry.duration, 100); - assert.deepEqual(requestTelemetry.properties, properties); - - var tags = requestTelemetry.tagOverrides; - - assert.equal(tags["ai.operation.name"], "GET /search"); - assert.equal(tags["ai.device.id"], "B"); - assert.equal(tags["custom"], "A"); - assert.equal(tags["ai.device.type"], null); - }); - }); - - describe("#trackNodeHttpDependency()", () => { - var clock: Sinon.SinonFakeTimers; - - before(() => { - clock = sinon.useFakeTimers(); - }); - - after(() => { - clock.restore(); - }); - - it("should not crash with invalid input", () => { - invalidInputHelper("trackNodeHttpDependency"); - }); - - it('should track request with correct data from request options', () => { - trackStub.reset(); - clock.reset(); - client.trackNodeHttpDependency({ - options: { - host: 'bing.com', - path: '/search?q=test' - }, - request: request, properties: properties, - tagOverrides: { "custom": "A", "ai.device.id": "B" } - }); - - // response event was not emitted yet - assert.ok(trackStub.notCalled); - - // emit response event - clock.tick(10); - request.emitResponse(); - assert.ok(trackStub.calledOnce); - var dependencyTelemetry = trackStub.firstCall.args[0]; - - - assert.equal(dependencyTelemetry.success, true); - assert.equal(dependencyTelemetry.duration, 10); - assert.equal(dependencyTelemetry.name, "GET /search"); - assert.equal(dependencyTelemetry.data, "http://bing.com/search?q=test"); - assert.equal(dependencyTelemetry.target, "bing.com"); - assert.equal(dependencyTelemetry.dependencyTypeName, Contracts.RemoteDependencyDataConstants.TYPE_HTTP); - assert.deepEqual(dependencyTelemetry.properties, properties); - assert.deepEqual(dependencyTelemetry.tagOverrides, { "custom": "A", "ai.device.id": "B" }); - }); - - it('should track request with correct data on response event', () => { - trackStub.reset(); - clock.reset(); - client.trackNodeHttpDependency({ options: 'http://bing.com/search?q=test', request: request, properties: properties }); - - // response event was not emitted yet - assert.ok(trackStub.notCalled); - - // emit response event - clock.tick(10); - request.emitResponse(); - assert.ok(trackStub.calledOnce); - var dependencyTelemetry = trackStub.firstCall.args[0]; - - assert.equal(dependencyTelemetry.success, true); - assert.equal(dependencyTelemetry.duration, 10); - assert.equal(dependencyTelemetry.name, "GET /search"); - assert.equal(dependencyTelemetry.data, "http://bing.com/search?q=test"); - assert.equal(dependencyTelemetry.target, "bing.com"); - assert.equal(dependencyTelemetry.dependencyTypeName, Contracts.RemoteDependencyDataConstants.TYPE_HTTP); - assert.deepEqual(dependencyTelemetry.properties, properties); - }); - - it('should track request with correct data on request error event #2', () => { - trackStub.reset(); - clock.reset(); - client.trackNodeHttpDependency({ options: 'http://bing.com/search?q=test', request: request, properties: properties }); - - // error event was not emitted yet - assert.ok(trackStub.notCalled); - - // emit error event - clock.tick(10); - request.emitError(); - assert.ok(trackStub.calledOnce); - var dependencyTelemetry = trackStub.firstCall.args[0]; - - assert.equal(dependencyTelemetry.success, false); - assert.equal(dependencyTelemetry.duration, 10); - assert.equal(dependencyTelemetry.name, "GET /search"); - assert.equal(dependencyTelemetry.data, "http://bing.com/search?q=test"); - assert.equal(dependencyTelemetry.target, "bing.com"); - assert.deepEqual(dependencyTelemetry.properties, failedProperties); - }); - - it('should use source and target correlationId headers', () => { - trackStub.reset(); - clock.reset(); - client.trackNodeHttpDependency({ - options: { - host: 'bing.com', - path: '/search?q=test' - }, - request: request, properties: properties - }); - - // The client's correlationId should have been added as the request source correlationId header. - assert.equal(request.headers[RequestResponseHeaders.requestContextHeader], - `${RequestResponseHeaders.requestContextSourceKey}=${client.config.correlationId}`); - - // response event was not emitted yet - assert.ok(trackStub.notCalled); - - // Simulate a response from another service that includes a target correlationId header. - const targetCorrelationId = "cid-v1:Application-Key-98765-4321A"; - response.headers[RequestResponseHeaders.requestContextHeader] = - `${RequestResponseHeaders.requestContextTargetKey}=${targetCorrelationId}`; - - // emit response event - clock.tick(10); - request.emitResponse(); - assert.ok(trackStub.calledOnce); - var dependencyTelemetry = trackStub.firstCall.args[0]; - - assert.equal(dependencyTelemetry.target, "bing.com | " + targetCorrelationId); - assert.equal(dependencyTelemetry.dependencyTypeName, "Http (tracked component)"); - }); - - it('should not set source correlationId headers when the host is on a excluded domain list', () => { - trackStub.reset(); - clock.reset(); - - client.config.correlationHeaderExcludedDomains = ["*.domain.com"] - client.trackNodeHttpDependency({ - options: { - host: 'excluded.domain.com', - path: '/search?q=test' - }, - request: request, properties: properties - }); - - // The client's correlationId should NOT have been added for excluded domains - assert.equal(request.headers[RequestResponseHeaders.requestContextHeader], null); - }); - }); - }); - - describe("#trackException()", () => { - it("should track Exception with correct data - Error only", () => { - trackStub.reset(); - client.trackException({ exception: new Error(name) }); - - assert.ok(trackStub.calledOnce); - - var exceptionTelemetry = trackStub.firstCall.args[0]; - - - assert.equal(exceptionTelemetry.exception.message, name); - }); - - it("should track Exception with correct data - Error and properties", () => { - trackStub.reset(); - client.trackException({ exception: new Error(name), properties: properties }); - - assert.ok(trackStub.calledOnce); - - var exceptionTelemetry = trackStub.firstCall.args[0]; - assert.equal(exceptionTelemetry.exception.message, name); - assert.deepEqual(exceptionTelemetry.properties, properties); - }); - - it("should track Exception with correct data - Error, properties and measurements", () => { - trackStub.reset(); - client.trackException({ exception: new Error(name), properties: properties, measurements: measurements }); - - assert.ok(trackStub.calledOnce); - - var exceptionTelemetry = trackStub.firstCall.args[0]; - - assert.equal(exceptionTelemetry.exception.message, name); - assert.deepEqual(exceptionTelemetry.properties, properties); - assert.deepEqual(exceptionTelemetry.measurements, measurements); - }); - - it("should not crash with invalid input", () => { - invalidInputHelper("trackException"); - }); - }); - - describe("#ProcessedByMetricExtractors()", () => { - - before(() => { - let preAggregatedMetrics = new AutoCollecPreAggregatedMetrics(client); - preAggregatedMetrics.enable(true); - }); - - it("exception telemetry", () => { - trackStub.restore(); - var createEnvelopeSpy = sinon.spy(EnvelopeFactory, "createEnvelope"); - client.trackException({ exception: new Error(name), properties: properties }); - assert.ok(createEnvelopeSpy.calledOnce); - var envelopeCreated = createEnvelopeSpy.firstCall.returnValue; - var obj0 = >envelopeCreated.data; - createEnvelopeSpy.restore(); - assert.equal(obj0.baseData.exceptions[0].message, name); - assert.deepEqual(obj0.baseData.properties, exceptionExpectedProperties); - }); - - it("trace telemetry", () => { - trackStub.restore(); - var createEnvelopeSpy = sinon.spy(EnvelopeFactory, "createEnvelope"); - client.trackTrace({ message: name }); - assert.ok(createEnvelopeSpy.calledOnce); - var envelopeCreated = createEnvelopeSpy.firstCall.returnValue; - var obj0 = >envelopeCreated.data; - createEnvelopeSpy.restore(); - - assert.equal(obj0.baseData.message, name); - assert.deepEqual(obj0.baseData.properties, traceExpectedProperties); - }); - }); - - describe("#trackDependency()", () => { - it("should create envelope with correct properties", () => { - trackStub.restore(); - var commandName = "http://bing.com/search?q=test"; - var dependencyTypeName = "dependencyTypeName"; - var createEnvelopeSpy = sinon.spy(EnvelopeFactory, "createEnvelope"); - client.trackDependency({ name: name, data: commandName, duration: value, success: true, resultCode: "0", dependencyTypeName: dependencyTypeName, properties: properties }); - assert.ok(createEnvelopeSpy.calledOnce); - - - var envelopeCreated = createEnvelopeSpy.firstCall.returnValue; - var obj0 = >envelopeCreated.data; - createEnvelopeSpy.restore(); - - assert.equal(obj0.baseData.name, name); - assert.equal(obj0.baseData.data, commandName); - assert.equal(obj0.baseData.target, 'bing.com'); - assert.equal(obj0.baseData.duration, Util.msToTimeSpan(value)); - assert.equal(obj0.baseData.success, true); - assert.equal(obj0.baseData.type, dependencyTypeName); - assert.deepEqual(obj0.baseData.properties, dependencyExpectedProperties); - }); - - it("should create envelope with correct properties (numeric result code)", () => { - trackStub.restore(); - var commandName = "http://bing.com/search?q=test"; - var dependencyTypeName = "dependencyTypeName"; - var createEnvelopeSpy = sinon.spy(EnvelopeFactory, "createEnvelope"); - client.trackDependency({ name: name, data: commandName, duration: value, success: true, resultCode: 0, dependencyTypeName: dependencyTypeName, properties: properties }); - assert.ok(createEnvelopeSpy.calledOnce); - - - var envelopeCreated = createEnvelopeSpy.firstCall.returnValue; - var obj0 = >envelopeCreated.data; - createEnvelopeSpy.restore(); - - assert.equal(obj0.baseData.name, name); - assert.equal(obj0.baseData.data, commandName); - assert.equal(obj0.baseData.target, 'bing.com'); - assert.equal(obj0.baseData.duration, Util.msToTimeSpan(value)); - assert.equal(obj0.baseData.success, true); - assert.equal(obj0.baseData.type, dependencyTypeName); - assert.deepEqual(obj0.baseData.properties, dependencyExpectedProperties); - }); - - it("should process the id when specified", () => { - trackStub.restore(); - var commandName = "http://bing.com/search?q=test"; - var dependencyTypeName = "dependencyTypeName"; - var createEnvelopeSpy = sinon.spy(EnvelopeFactory, "createEnvelope"); - client.trackDependency({ id: "testid", name: name, data: commandName, duration: value, success: true, resultCode: "0", dependencyTypeName: dependencyTypeName, properties: properties }); - assert.ok(createEnvelopeSpy.calledOnce); - - - var envelopeCreated = createEnvelopeSpy.firstCall.returnValue; - var obj0 = >envelopeCreated.data; - createEnvelopeSpy.restore(); - assert.equal(obj0.baseData.id, "testid"); - assert.deepEqual(obj0.baseData.properties, dependencyExpectedProperties); - }); - - it("should auto-generate the id when not specified", () => { - trackStub.restore(); - var commandName = "http://bing.com/search?q=test"; - var dependencyTypeName = "dependencyTypeName"; - var createEnvelopeSpy = sinon.spy(EnvelopeFactory, "createEnvelope"); - client.trackDependency({ name: name, data: commandName, duration: value, success: true, resultCode: "0", dependencyTypeName: dependencyTypeName, properties: properties }); - assert.ok(createEnvelopeSpy.calledOnce); - - - var envelopeCreated = createEnvelopeSpy.firstCall.returnValue; - var obj0 = >envelopeCreated.data; - createEnvelopeSpy.restore(); - assert.ok(!!obj0.baseData.id); - assert.deepEqual(obj0.baseData.properties, dependencyExpectedProperties); - }); - - it("should autopopulate target field for url data", () => { - trackStub.restore(); - var commandName = "http://bing.com/search?q=test"; - var dependencyTypeName = "dependencyTypeName"; - var createEnvelopeSpy = sinon.spy(EnvelopeFactory, "createEnvelope"); - client.trackDependency({ name: name, data: commandName, duration: value, success: true, resultCode: "0", dependencyTypeName: dependencyTypeName, properties: properties }); - assert.ok(createEnvelopeSpy.calledOnce); - - - var envelopeCreated = createEnvelopeSpy.firstCall.returnValue; - var obj0 = >envelopeCreated.data; - createEnvelopeSpy.restore(); - assert.equal(obj0.baseData.target, "bing.com"); - }); - - it("should not autopopulate target field for non-url data", () => { - trackStub.restore(); - var commandName = "NOT A URL"; - var dependencyTypeName = "dependencyTypeName"; - var createEnvelopeSpy = sinon.spy(EnvelopeFactory, "createEnvelope"); - client.trackDependency({ name: name, data: commandName, duration: value, success: true, resultCode: "0", dependencyTypeName: dependencyTypeName, properties: properties }); - assert.ok(createEnvelopeSpy.calledOnce); - - - var envelopeCreated = createEnvelopeSpy.firstCall.returnValue; - var obj0 = >envelopeCreated.data; - createEnvelopeSpy.restore(); - assert.equal(obj0.baseData.target, null); - }); - }); - - describe("#trackRequest()", () => { - it("should create envelope with correct properties", () => { - trackStub.restore(); - var url = "http://bing.com/search?q=test"; - var createEnvelopeSpy = sinon.spy(EnvelopeFactory, "createEnvelope"); - client.trackRequest({ url: url, source: "source", name: name, duration: value, success: true, resultCode: "200", properties: properties }); - assert.ok(createEnvelopeSpy.calledOnce); - - - var envelopeCreated = createEnvelopeSpy.firstCall.returnValue; - var obj0 = >envelopeCreated.data; - createEnvelopeSpy.restore(); - - assert.equal(obj0.baseData.name, name); - assert.equal(obj0.baseData.url, url); - assert.equal(obj0.baseData.source, 'source'); - assert.equal(obj0.baseData.duration, Util.msToTimeSpan(value)); - assert.equal(obj0.baseData.success, true); - assert.equal(obj0.baseData.responseCode, "200"); - assert.deepEqual(obj0.baseData.properties, requestExpectedProperties); - }); - - it("should create envelope with correct properties (numeric resultCode)", () => { - trackStub.restore(); - var url = "http://bing.com/search?q=test"; - var createEnvelopeSpy = sinon.spy(EnvelopeFactory, "createEnvelope"); - client.trackRequest({ url: url, source: "source", name: name, duration: value, success: true, resultCode: 200, properties: properties }); - assert.ok(createEnvelopeSpy.calledOnce); - - - var envelopeCreated = createEnvelopeSpy.firstCall.returnValue; - var obj0 = >envelopeCreated.data; - createEnvelopeSpy.restore(); - - assert.equal(obj0.baseData.name, name); - assert.equal(obj0.baseData.url, url); - assert.equal(obj0.baseData.source, 'source'); - assert.equal(obj0.baseData.duration, Util.msToTimeSpan(value)); - assert.equal(obj0.baseData.success, true); - assert.equal(obj0.baseData.responseCode, "200"); - assert.deepEqual(obj0.baseData.properties, requestExpectedProperties); - }); - - it("should process the id when specified", () => { - trackStub.restore(); - var url = "http://bing.com/search?q=test"; - var createEnvelopeSpy = sinon.spy(EnvelopeFactory, "createEnvelope"); - client.trackRequest({ id: "testid", url: url, source: "source", name: name, duration: value, success: true, resultCode: "200", properties: properties }); - assert.ok(createEnvelopeSpy.calledOnce); - - - var envelopeCreated = createEnvelopeSpy.firstCall.returnValue; - var obj0 = >envelopeCreated.data; - createEnvelopeSpy.restore(); - - assert.equal(obj0.baseData.id, "testid"); - assert.deepEqual(obj0.baseData.properties, requestExpectedProperties); - }); - - it("should auto-generate the id when not specified", () => { - trackStub.restore(); - var url = "http://bing.com/search?q=test"; - var createEnvelopeSpy = sinon.spy(EnvelopeFactory, "createEnvelope"); - client.trackRequest({ url: url, source: "source", name: name, duration: value, success: true, resultCode: "200", properties: properties }); - assert.ok(createEnvelopeSpy.calledOnce); - - - var envelopeCreated = createEnvelopeSpy.firstCall.returnValue; - var obj0 = >envelopeCreated.data; - createEnvelopeSpy.restore(); - - assert.ok(!!obj0.baseData.id); - assert.deepEqual(obj0.baseData.properties, requestExpectedProperties); - }); - }); - - describe("#flush()", () => { - - afterEach(() => { - client.clearTelemetryProcessors(); - saveOnCrashStub.reset(); - sendStub.restore(); - sendStub = sinon.stub(client.channel, "send"); - triggerStub.restore(); - triggerStub = sinon.stub(client.channel, "triggerSend"); - }); - - it("should invoke the sender", () => { - triggerStub.reset(); - client.flush(); - assert.ok(triggerStub.calledOnce); - }); - - it("should accept a callback", () => { - triggerStub.reset(); - var callback = sinon.spy(); - client.flush({ callback: callback }); - assert.strictEqual(triggerStub.firstCall.args[0], false); - assert.strictEqual(triggerStub.firstCall.args[1], callback); - }); - - it("should save on disk when isAppCrashing option is set to true", () => { - sendStub.reset(); - client.flush({ isAppCrashing: true }); - assert.ok(sendStub.notCalled, "saveOnCrash should be called, not send"); - saveOnCrashStub.reset(); - - // temporarily restore send and trigger stubs to allow saveOnCrash to be called - sendStub.restore(); - triggerStub.restore(); - - // fake something in the buffer - var testEnvelope = new Contracts.Envelope(); - client.channel._buffer.push(testEnvelope); - client.flush({ isAppCrashing: true }); - - assert.ok(saveOnCrashStub.calledOnce); - saveOnCrashStub.restore(); - - }); - - }); - - describe("#track()", () => { - it("should pass data to the channel", () => { - sendStub.reset(); - - trackStub.restore(); - client.track(testEventTelemetry, Contracts.TelemetryType.Event); - trackStub = sinon.stub(client, "track"); - - assert.ok(sendStub.calledOnce); - }); - - - it("should send the envelope that was created", () => { - sendStub.reset(); - var createEnvelopeSpy = sinon.spy(EnvelopeFactory, "createEnvelope"); - trackStub.restore(); - client.track(testEventTelemetry, Contracts.TelemetryType.Event); - trackStub = sinon.stub(client, "track"); - - var expected = createEnvelopeSpy.firstCall.returnValue; - var actual = sendStub.firstCall.args[0]; - createEnvelopeSpy.restore(); - - assert.deepEqual(actual, expected); - }); - - it("should use timestamp if it was set", () => { - var timestamp = new Date("Mon Aug 28 2017 11:44:17"); - var createEnvelopeSpy = sinon.spy(EnvelopeFactory, "createEnvelope"); - trackStub.restore(); - client.trackEvent({ name: "eventName", time: timestamp }); - trackStub = sinon.stub(client, "track"); - var envelope = createEnvelopeSpy.firstCall.returnValue; - createEnvelopeSpy.restore(); - assert.equal(envelope.time, timestamp.toISOString()); - }); - - it("telemetry processor can change the envelope", () => { - trackStub.restore(); - var expectedName = "I was here"; - - client.addTelemetryProcessor((env) => { - env.name = expectedName; - return true; - }); - - client.track(testEventTelemetry, Contracts.TelemetryType.Event); - - assert.equal(sendStub.callCount, 1, "send called once"); - - var actualData = sendStub.firstCall.args[0] as Contracts.Envelope; - assert.equal(actualData.name, expectedName, "envelope name should be changed by the processor"); - }); - - it("setAutoPopulateAzureProperties", () => { - trackStub.restore(); - const env = <{ [id: string]: string }>{}; - const originalEnv = process.env; - env.WEBSITE_SITE_NAME = "testRole"; - env.WEBSITE_INSTANCE_ID = "627cc493-f310-47de-96bd-71410b7dec09"; - process.env = env; - client.setAutoPopulateAzureProperties(true); - client.track(testEventTelemetry, Contracts.TelemetryType.Event); - process.env = originalEnv; - assert.equal(sendStub.callCount, 1, "send called once"); - var actualData = sendStub.firstCall.args[0] as Contracts.Envelope; - assert.equal(actualData.tags[client.context.keys.cloudRole], "testRole"); - assert.equal(actualData.tags[client.context.keys.cloudRoleInstance], "627cc493-f310-47de-96bd-71410b7dec09"); - }); - - it("telemetry processor can access the context object", () => { - trackStub.restore(); - var expectedName = "I was here"; - - client.addTelemetryProcessor((env, contextObjects) => { - env.name = contextObjects["name"]; - return true; - }); - testEventTelemetry.contextObjects = { "name": expectedName }; - - client.track(testEventTelemetry, Contracts.TelemetryType.Event); - testEventTelemetry.contextObjects = undefined; - - assert.equal(sendStub.callCount, 1, "send called once"); - - var actualData = sendStub.firstCall.args[0] as Contracts.Envelope; - assert.equal(actualData.name, expectedName, "envelope name should be changed by the processor"); - }); - - it("telemetry processors are executed in a right order", () => { - trackStub.restore(); - - client.addTelemetryProcessor((env) => { - env.name = "First"; - return true; - }); - - client.addTelemetryProcessor((env) => { - env.name += ", Second"; - return true; - }); - - client.addTelemetryProcessor((env) => { - env.name += ", Third"; - return true; - }); - client.track(testEventTelemetry, Contracts.TelemetryType.Event); - assert.equal(sendStub.callCount, 1, "send called once"); - - var actualData = sendStub.firstCall.args[0] as Contracts.Envelope; - assert.equal(actualData.name, "First, Second, Third", "processors should executed in the right order"); - }); - - it("envelope rejected by the telemetry processor will not be sent", () => { - trackStub.restore(); - - client.addTelemetryProcessor((env) => { - return false; - }); - - client.track(testEventTelemetry, Contracts.TelemetryType.Event); - - assert.ok(sendStub.notCalled, "send should not be called"); - }); - - it("envelope is sent when processor throws exception", () => { - trackStub.restore(); - - client.addTelemetryProcessor((env): boolean => { - throw "telemetry processor failed"; - }); - - client.addTelemetryProcessor((env): boolean => { - env.name = "more data"; - return true; - }); - - client.track(testEventTelemetry, Contracts.TelemetryType.Event); - - assert.ok(sendStub.called, "send should be called despite telemetry processor failure"); - var actualData = sendStub.firstCall.args[0] as Contracts.Envelope; - assert.equal(actualData.name, "more data", "more data is added as part of telemetry processor"); - }); - }); - - describe("#addTelemetryProcessor()", () => { - it("adds telemetry processor to the queue", () => { - trackStub.restore(); - var processorExecuted = false; - - client.addTelemetryProcessor((env) => { - processorExecuted = true; - return true; - }); - - client.track(testEventTelemetry, Contracts.TelemetryType.Event); - - assert.ok(processorExecuted, "telemetry processor should be executed"); - }); - }); - - describe("#clearTelemetryProcessors()", () => { - it("removes all processors from the telemetry processors list", () => { - trackStub.restore(); - var processorExecuted = false; - - client.addTelemetryProcessor((env) => { - processorExecuted = true; - return true; - }); - - client.clearTelemetryProcessors(); - client.track(testEventTelemetry, Contracts.TelemetryType.Event); - - assert.ok(!processorExecuted, "telemetry processor should NOT be executed"); - }); - }); -}); diff --git a/Tests/Library/Config.tests.ts b/Tests/Library/Config.tests.ts deleted file mode 100644 index 79d1c6ae5..000000000 --- a/Tests/Library/Config.tests.ts +++ /dev/null @@ -1,207 +0,0 @@ -import assert = require("assert"); -import path = require("path"); -import sinon = require("sinon"); -var http = require("http"); -var https = require("https"); -import Config = require("../../Library/Config"); -import Constants = require("../../Declarations/Constants"); - -import { JsonConfig } from "../../Library/JsonConfig"; - -const ENV_connectionString = "APPLICATIONINSIGHTS_CONNECTION_STRING"; - -describe("Library/Config", () => { - - var iKey = "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"; - let originalEnv: NodeJS.ProcessEnv; - var sandbox: sinon.SinonSandbox; - - beforeEach(() => { - originalEnv = process.env; - sandbox = sinon.sandbox.create(); - }); - - afterEach(() => { - process.env = originalEnv; - sandbox.restore(); - JsonConfig["_instance"] = undefined; - }); - - describe("#constructor", () => { - describe("connection string && API && environment variable prioritization", () => { - it("connection string set via in code setup", () => { - var env = { [ENV_connectionString]: "InStruMenTatioNKey=cs.env", [Config.ENV_iKey]: "ikey.env" }; - process.env = env; - const config = new Config("InStruMenTatioNKey=cs.code"); - assert.deepEqual(config.instrumentationKey, "cs.code"); - }); - - it("instrumentation key set via in code setup", () => { - var env = { [ENV_connectionString]: "InStruMenTatioNKey=CS.env", [Config.ENV_iKey]: "ikey.env" }; - process.env = env; - const config = new Config("ikey.code"); - assert.deepEqual(config.instrumentationKey, "ikey.code"); - }); - - it("connection string set via environment variable", () => { - var env = { [ENV_connectionString]: "InStruMenTatioNKey=cs.env", [Config.ENV_iKey]: "ikey.env" }; - process.env = env; - const config = new Config(); - assert.deepEqual(config.instrumentationKey, "cs.env"); - }); - - it("instrumentation key set via environment variable", () => { - var env = { [Config.ENV_iKey]: "ikey.env" }; - process.env = env; - const config = new Config(); - assert.deepEqual(config.instrumentationKey, "ikey.env"); - }); - - it("should parse the host of livemetrics host, if provided", () => { - const config = new Config("InStruMenTatioNKey=ikey;LiveEndpoint=https://live.applicationinsights.io/foo/bar"); - assert.deepEqual(config.quickPulseHost, "live.applicationinsights.io"); - }); - - it("should parse the host of livemetrics host from location+suffix, if provided", () => { - const config = new Config("InStruMenTatioNKey=ikey;Location=wus2;EndpointSuffix=example.com"); - assert.deepEqual(config.quickPulseHost, "wus2.live.example.com"); - }); - - it("merge JSON config", () => { - JsonConfig["_instance"] = undefined; - const env = <{ [id: string]: string }>{}; - const customConfigJSONPath = path.resolve(__dirname, "../../../Tests/Library/config.json"); - env["APPLICATIONINSIGHTS_CONFIGURATION_FILE"] = customConfigJSONPath; // Load JSON config - process.env = env; - const config = new Config(); - assert.equal(config["_connectionString"], "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/"); - assert.equal(config.endpointUrl, "testEndpointUrl/v2.1/track"); - assert.equal(config.maxBatchSize, 150); - assert.equal(config.maxBatchIntervalMs, 12000); - assert.equal(config.disableAppInsights, false); - assert.equal(config.samplingPercentage, 30); - assert.equal(config.correlationIdRetryIntervalMs, 15000); - assert.equal(config.correlationHeaderExcludedDomains[0], "domain1"); - assert.equal(config.correlationHeaderExcludedDomains[1], "domain2"); - assert.equal(config.proxyHttpUrl, "testProxyHttpUrl"); - assert.equal(config.proxyHttpsUrl, "testProxyHttpsUrl"); - assert.equal(config.ignoreLegacyHeaders, false); - assert.equal(config.enableAutoCollectExternalLoggers, false); - assert.equal(config.enableAutoCollectConsole, false); - assert.equal(config.enableAutoCollectExceptions, false); - assert.equal(config.enableAutoCollectPerformance, false); - assert.equal(config.enableAutoCollectPreAggregatedMetrics, false); - assert.equal(config.enableAutoCollectHeartbeat, false); - assert.equal(config.enableAutoCollectRequests, false); - assert.equal(config.enableAutoCollectDependencies, false); - assert.equal(config.enableAutoDependencyCorrelation, false); - assert.equal(config.enableUseAsyncHooks, false); - assert.equal(config.disableStatsbeat, false); - assert.equal(config.enableAutoCollectExtendedMetrics, false); - assert.equal(config.distributedTracingMode, 0); - assert.equal(config.enableUseDiskRetryCaching, false); - assert.equal(config.enableResendInterval, 123); - assert.equal(config.enableMaxBytesOnDisk, 456); - assert.equal(config.enableInternalDebugLogging, false); - assert.equal(config.disableStatsbeat, false); - assert.equal(config.enableInternalWarningLogging, false); - assert.equal(config.enableSendLiveMetrics, false); - assert.equal(config.extendedMetricDisablers, "gc,heap"); - assert.equal(config.quickPulseHost, "testquickpulsehost.com"); - }); - }); - - describe("constructor(ikey)", () => { - beforeEach(() => { - sinon.stub(http, 'request'); - sinon.stub(https, 'request'); - }); - afterEach(() => { - http.request.restore(); - https.request.restore(); - }); - it("should throw if no iKey is available", () => { - var env = {}; - process.env = env; - assert.throws(() => new Config()); - }); - - it("should read iKey from environment", () => { - var env = <{ [id: string]: string }>{}; - env[Config.ENV_iKey] = iKey; - process.env = env; - var config = new Config(); - assert.equal(config.instrumentationKey, iKey); - }); - - it("should read iKey from azure environment", () => { - var env = <{ [id: string]: string }>{}; - env[Config.ENV_azurePrefix + Config.ENV_iKey] = iKey; - process.env = env; - var config = new Config(); - assert.equal(config.instrumentationKey, iKey); - }); - - it("should initialize valid values", () => { - var config = new Config("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert(typeof config.instrumentationKey === "string"); - assert(typeof config.endpointUrl === "string"); - assert(typeof config.maxBatchSize === "number"); - assert(typeof config.maxBatchIntervalMs === "number"); - assert(typeof config.disableAppInsights === "boolean"); - assert(typeof config.samplingPercentage === "number"); - assert(typeof config.correlationIdRetryIntervalMs === "number"); - assert(typeof config.correlationHeaderExcludedDomains === "object"); - assert(typeof config.quickPulseHost === "string"); - }); - - it("should initialize values that we claim in README", () => { - var config = new Config("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert(config.maxBatchSize === 250); - assert(config.maxBatchIntervalMs === 15000); - assert(config.disableAppInsights === false); - assert(config.samplingPercentage === 100); - assert(config.correlationIdRetryIntervalMs === 30000); - assert(config.proxyHttpUrl === undefined); - assert(config.proxyHttpsUrl === undefined); - - assert.equal(config.quickPulseHost, Constants.DEFAULT_LIVEMETRICS_HOST); - }); - - it("should initialize values that we claim in README (2)", () => { - process.env.http_proxy = "test"; - process.env.https_proxy = "test2"; - JsonConfig["_instance"] = undefined; - var config = new Config("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert(config.proxyHttpUrl === "test"); - assert(config.proxyHttpsUrl === "test2"); - delete process.env.http_proxy; - delete process.env.https_proxy; - }); - - it("should add azure domain to excluded list", () => { - var config = new Config("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.equal(config.correlationHeaderExcludedDomains[0].toString(), "*.core.windows.net"); - }); - - it("instrumentation key validation-valid key passed", () => { - var warnStub = sandbox.stub(console, "warn"); - var config = new Config("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.ok(warnStub.notCalled, "warning was not raised"); - }); - - it("instrumentation key validation-invalid key passed", () => { - var warnStub = sandbox.stub(console, "warn"); - var config = new Config("1aa11111bbbb1ccc8dddeeeeffff3333"); - assert.ok(warnStub.calledOn, "warning was raised"); - }); - - it("instrumentation key validation-invalid key passed", () => { - var warnStub = sandbox.stub(console, "warn"); - var config = new Config("abc"); - assert.ok(warnStub.calledOn, "warning was raised"); - }); - - }); - }); -}); diff --git a/Tests/Library/ConnectionStringParser.tests.ts b/Tests/Library/ConnectionStringParser.tests.ts deleted file mode 100644 index 09b3b4f66..000000000 --- a/Tests/Library/ConnectionStringParser.tests.ts +++ /dev/null @@ -1,93 +0,0 @@ -import assert = require("assert"); - -import sinon = require("sinon"); -import Constants = require("../../Declarations/Constants"); -import ConnectionStringParser = require("../../Library/ConnectionStringParser"); - -describe("ConnectionStringParser", () => { - describe("#parse()", () => { - it("should parse all valid fields", () => { - const instrumentationKey = "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"; - const ingestionEndpoint = "ingest"; - const liveEndpoint = "live"; - var connectionString = `InstrumentationKey=${instrumentationKey};IngestionEndpoint=${ingestionEndpoint};LiveEndpoint=${liveEndpoint};`; - const result = ConnectionStringParser.parse(connectionString); - assert.deepEqual(result.instrumentationkey, instrumentationKey); - assert.deepEqual(result.ingestionendpoint, ingestionEndpoint); - assert.deepEqual(result.liveendpoint, liveEndpoint); - }); - - it("should ignore invalid fields", () => { - const instrumentationKey = "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"; - const ingestionEndpoint = "ingest"; - const liveEndpoint = "live"; - const connectionString = `Instrume.ntationKey=${instrumentationKey};Ingestion.Endpoint=${ingestionEndpoint};LiveEnd.point=${liveEndpoint}`; - - const result = ConnectionStringParser.parse(connectionString); - assert.deepEqual(result.instrumentationkey, undefined); - assert.deepEqual(result.ingestionendpoint, Constants.DEFAULT_BREEZE_ENDPOINT); - assert.deepEqual(result.liveendpoint, Constants.DEFAULT_LIVEMETRICS_ENDPOINT); - }); - - const runTest = (options: { - connectionString: string, - expectedInstrumentationKey?: string, - expectedBreezeEndpoint: string, - expectedLiveMetricsEndpoint: string, - }) => { - const result = ConnectionStringParser.parse(options.connectionString); - if (options.expectedInstrumentationKey) assert.deepEqual(result.instrumentationkey, options.expectedInstrumentationKey); - assert.deepEqual(result.ingestionendpoint, options.expectedBreezeEndpoint); - assert.deepEqual(result.liveendpoint, options.expectedLiveMetricsEndpoint); - } - - it("should use correct default endpoints", () => { - runTest({ - connectionString: "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333", - expectedInstrumentationKey: "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333", - expectedBreezeEndpoint: Constants.DEFAULT_BREEZE_ENDPOINT, - expectedLiveMetricsEndpoint: Constants.DEFAULT_LIVEMETRICS_ENDPOINT - }); - }); - - it("should use correct endpoints when using EndpointSuffix", () => { - runTest({ - connectionString: "InstrumentationKey=00000000-0000-0000-0000-000000000000;EndpointSuffix=ai.contoso.com", - expectedBreezeEndpoint: "https://dc.ai.contoso.com", - expectedLiveMetricsEndpoint: "https://live.ai.contoso.com" - }); - }); - - it("should use correct endpoints when using EndpointSuffix with explicit override", () => { - runTest({ - connectionString: "InstrumentationKey=00000000-0000-0000-0000-000000000000;EndpointSuffix=ai.contoso.com;LiveEndpoint=https://custom.live.contoso.com:444", - expectedBreezeEndpoint: "https://dc.ai.contoso.com", - expectedLiveMetricsEndpoint: "https://custom.live.contoso.com:444" - }); - }); - - it("should parse EndpointSuffix + Location", () => { - runTest({ - connectionString: "InstrumentationKey=00000000-0000-0000-0000-000000000000;EndpointSuffix=ai.contoso.com;Location=westus2", - expectedBreezeEndpoint: "https://westus2.dc.ai.contoso.com", - expectedLiveMetricsEndpoint: "https://westus2.live.ai.contoso.com" - }); - }); - - it("should parse EndpointSuffix + Location + Endpoint Override", () => { - runTest({ - connectionString: "InstrumentationKey=00000000-0000-0000-0000-000000000000;EndpointSuffix=ai.contoso.com;Location=westus2;LiveEndpoint=https://custom.contoso.com:444", - expectedBreezeEndpoint: "https://westus2.dc.ai.contoso.com", - expectedLiveMetricsEndpoint: "https://custom.contoso.com:444" - }); - }); - - it("should parse Endpoint Override", () => { - runTest({ - connectionString: "InstrumentationKey=00000000-0000-0000-0000-000000000000;LiveEndpoint=http://custom.live.endpoint.com:444", - expectedBreezeEndpoint: Constants.DEFAULT_BREEZE_ENDPOINT, - expectedLiveMetricsEndpoint: "http://custom.live.endpoint.com:444" - }); - }); - }); -}); diff --git a/Tests/Library/Context.tests.ts b/Tests/Library/Context.tests.ts deleted file mode 100644 index 956ec1f63..000000000 --- a/Tests/Library/Context.tests.ts +++ /dev/null @@ -1,82 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); -import fs = require("fs"); -import os = require("os"); -import path = require("path"); - -import Context = require("../../Library/Context"); - -describe("Library/Context", () => { - describe("#constructor()", () => { - var stubs: Array = []; - - before(() => { - // Create custom package json - var jsonContent = JSON.stringify({ "version": "testVersion" }); - var testFilePath = path.resolve(__dirname, "testpackage.json"); - fs.writeFile(testFilePath, jsonContent, () => { }); - }); - - after(() => { - var testFilePath = path.resolve(__dirname, "testpackage.json") - fs.unlink(testFilePath, (err) => { }); - }); - - beforeEach(() => { - stubs = [ - sinon.stub(os, "hostname", () => "host"), - sinon.stub(os, "type", () => "type"), - sinon.stub(os, "arch", () => "arch"), - sinon.stub(os, "release", () => "release"), - sinon.stub(os, "platform", () => "platform") - ]; - }); - - afterEach(() => { - stubs.forEach((s, i, arr) => s.restore()); - }); - - it("should initialize default context", () => { - var context = new Context(); - var defaultkeys = [ - context.keys.cloudRoleInstance, - context.keys.deviceOSVersion, - context.keys.internalSdkVersion, - context.keys.cloudRole, - context.keys.applicationVersion - ]; - - for (var i = 0; i < defaultkeys.length; i++) { - var key = defaultkeys[i]; - assert.ok(!!context.tags[key], key = " is set"); - } - }); - - it("should set internalSdkVersion to 'node:'", () => { - var context = new Context(); - const packageJsonPath = path.resolve(__dirname, "../../../", "./package.json"); - let packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); - assert.strictEqual(context.tags[context.keys.internalSdkVersion], "node:" + packageJson.version); - assert.strictEqual(Context.sdkVersion, packageJson.version); - }); - - it("should correctly set device context", () => { - var context = new Context(); - assert.equal(context.tags[context.keys.cloudRoleInstance], "host"); - assert.equal(context.tags[context.keys.deviceOSVersion], "type release"); - assert.equal(context.tags[context.keys.cloudRole], Context.DefaultRoleName); - - assert.equal(context.tags["ai.device.osArchitecture"], "arch"); - assert.equal(context.tags["ai.device.osPlatform"], "platform"); - }); - - // TODO: Unreliable test, applicationVersion is being added during build - // it("should correctly set application version", () => { - // var context = new Context(); - // assert.equal(context.tags[context.keys.applicationVersion], "unknown"); - // var testFilePath = path.resolve(__dirname, "testpackage.json") - // context = new Context(testFilePath); - // assert.equal(context.tags[context.keys.applicationVersion], "testVersion"); - // }); - }); -}); diff --git a/Tests/Library/EnvelopeFactoryTests.ts b/Tests/Library/EnvelopeFactoryTests.ts deleted file mode 100644 index 9f779efce..000000000 --- a/Tests/Library/EnvelopeFactoryTests.ts +++ /dev/null @@ -1,287 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); -import http = require("http"); - -import EnvelopeFactory = require("../../Library/EnvelopeFactory"); -import Contracts = require("../../Declarations/Contracts"); -import Client = require("../../Library/TelemetryClient"); -import Util = require("../../Library/Util"); - -describe("Library/EnvelopeFactory", () => { - - var properties: { [key: string]: string; } = { p1: "p1", p2: "p2", common: "commonArg" }; - var mockData = { baseData: { properties: {} }, baseType: "BaseTestData" }; - describe("#createEnvelope()", () => { - var commonproperties: { [key: string]: string } = { common1: "common1", common2: "common2", common: "common" }; - it("should assign common properties to the data", () => { - var client1 = new Client("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - client1.commonProperties = commonproperties; - client1.config.samplingPercentage = 99; - var eventTelemetry = { name: "name" }; - eventTelemetry.properties = properties; - var env = EnvelopeFactory.createEnvelope(eventTelemetry, Contracts.TelemetryType.Event, commonproperties, client1.context, client1.config); - - // check sample rate - assert.equal(env.sampleRate, client1.config.samplingPercentage); - - var envData: Contracts.Data = >env.data; - - // check common properties - assert.equal(envData.baseData.properties.common1, (commonproperties).common1); - assert.equal(envData.baseData.properties.common2, (commonproperties).common2); - - // check argument properties - assert.equal(envData.baseData.properties.p1, (properties).p1); - assert.equal(envData.baseData.properties.p2, (properties).p2); - - // check that argument properties overwrite common properties1 - assert.equal(envData.baseData.properties.common, (properties).common); - }); - - it("should allow tags to be overwritten", () => { - - var client = new Client("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - var env = EnvelopeFactory.createEnvelope({ name: "name" }, Contracts.TelemetryType.Event, commonproperties, client.context, client.config); - assert.deepEqual(env.tags, client.context.tags, "tags are set by default"); - var customTag = <{ [id: string]: string }>{ "ai.cloud.roleInstance": "override" }; - var expected: { [id: string]: string } = {}; - for (var tag in client.context.tags) { - expected[tag] = customTag[tag] || client.context.tags[tag]; - } - env = EnvelopeFactory.createEnvelope({ name: "name", tagOverrides: customTag }, Contracts.TelemetryType.Event, commonproperties, client.context, client.config); - assert.deepEqual(env.tags, expected) - }); - - it("should have valid name", function () { - var client = new Client("key"); - var envelope = EnvelopeFactory.createEnvelope({ name: "name" }, Contracts.TelemetryType.Event, commonproperties, client.context, client.config); - assert.equal(envelope.name, "Microsoft.ApplicationInsights.key.Event"); - }); - - it("should sanitize properties", () => { - var client1 = new Client("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - let commonProps = { - "commonProperty": 123, - }; - var eventTelemetry = { name: "name" }; - eventTelemetry.properties = { - "prop1": false, - "prop2": 123, - "prop3": { "subProp1": "someValue" } - }; - var env = EnvelopeFactory.createEnvelope(eventTelemetry, Contracts.TelemetryType.Event, (commonProps), client1.context, client1.config); - var envData: Contracts.Data = >env.data; - - // check properties - assert.equal(envData.baseData.properties.commonProperty, "123"); - assert.equal(envData.baseData.properties.prop1, "false"); - assert.equal(envData.baseData.properties.prop2, "123"); - assert.equal(envData.baseData.properties.prop3, "{\"subProp1\":\"someValue\"}"); - }); - }); - - describe("#createDependencyData()", () => { - it("should accept a telemetry item without a name", () => { - assert.doesNotThrow(() => { - var envelope = EnvelopeFactory.createEnvelope({ - name: null, - data: "GET https://example.com", - duration: 123, - success: true, - resultCode: 200 - }, Contracts.TelemetryType.Dependency); - assert.equal((envelope.data as Contracts.Data).baseData.name, undefined); - }); - }); - }); - - describe("#createExceptionData()", () => { - var simpleError: Error; - - beforeEach(() => { - try { - throw Error("simple error"); - } catch (e) { - simpleError = e; - } - }); - - it("fills empty 'method' with ''", () => { - simpleError.stack = " at \t (/path/file.js:12:34)\n" + simpleError.stack; - - var envelope = EnvelopeFactory.createEnvelope({ exception: simpleError }, Contracts.TelemetryType.Exception); - var exceptionData = >envelope.data; - var actual = exceptionData.baseData.exceptions[0].parsedStack[0].method; - var expected = ""; - - assert.deepEqual(actual, expected); - }); - - it("fills empty 'method' with ''", () => { - simpleError.stack = " at Context. (\t:12:34)\n" + simpleError.stack; - - var envelope = EnvelopeFactory.createEnvelope({ exception: simpleError }, Contracts.TelemetryType.Exception); - var exceptionData = >envelope.data; - - var actual = exceptionData.baseData.exceptions[0].parsedStack[0].fileName; - var expected = ""; - - assert.deepEqual(actual, expected); - }); - - it("fills stack when provided a scoped package", () => { - simpleError.stack = " at Context.foo (C:/@foo/bar/example.js:123:45)\n" + simpleError.stack; - - var envelope = EnvelopeFactory.createEnvelope({ exception: simpleError }, Contracts.TelemetryType.Exception); - var exceptionData = >envelope.data; - - var actual = exceptionData.baseData.exceptions[0].parsedStack[0]; - - assert.deepEqual(actual, { - fileName: "C:/@foo/bar/example.js", - line: 123, - level: 0, - sizeInBytes: 141, - assembly: "at Context.foo (C:/@foo/bar/example.js:123:45)", - method: "Context.foo" - }); - }); - - it("fills stack when provided a scoped package", () => { - simpleError.stack = " at C:/@foo/bar/example.js:123:45\n" + simpleError.stack; - - var envelope = EnvelopeFactory.createEnvelope({ exception: simpleError }, Contracts.TelemetryType.Exception); - var exceptionData = >envelope.data; - - var actual = exceptionData.baseData.exceptions[0].parsedStack[0]; - - assert.deepEqual(actual, { - fileName: "C:/@foo/bar/example.js", - line: 123, - level: 0, - sizeInBytes: 127, - assembly: "at C:/@foo/bar/example.js:123:45", - method: "" - }); - }); - - it("fills 'severityLevel' with Error when not specified", () => { - var envelope = EnvelopeFactory.createEnvelope({ exception: simpleError }, Contracts.TelemetryType.Exception); - var exceptionData = >envelope.data; - - var actual = exceptionData.baseData.severityLevel; - var expected = Contracts.SeverityLevel.Error; - - assert.deepEqual(actual, expected); - }); - - it("fills 'severityLevel' with the given value when specified", () => { - var envelope = EnvelopeFactory.createEnvelope({ exception: simpleError, severity: Contracts.SeverityLevel.Warning }, Contracts.TelemetryType.Exception); - var exceptionData = >envelope.data; - - var actual = exceptionData.baseData.severityLevel; - var expected = Contracts.SeverityLevel.Warning; - - assert.deepEqual(actual, expected); - }); - }); - - describe("AvailabilityData", () => { - let availabilityTelemetry: Contracts.AvailabilityTelemetry; - beforeEach(() => { - availabilityTelemetry = { - success: true, - duration: 100, - measurements: { "m1": 1 }, - runLocation: "west us", - properties: { - "prop1": "prop1 value" - }, - message: "availability test message", - name: "availability test name", - id: "availability test id", - }; - }); - - it("creates when id not set", () => { - availabilityTelemetry.id = undefined; - - var envelope = EnvelopeFactory.createEnvelope(availabilityTelemetry, Contracts.TelemetryType.Availability); - var data = >envelope.data; - assert.ok(data.baseData.id != null); - }); - - it("creates data with given content", () => { - var envelope = EnvelopeFactory.createEnvelope(availabilityTelemetry, Contracts.TelemetryType.Availability); - var data = >envelope.data; - - assert.deepEqual(data.baseType, "AvailabilityData"); - - assert.deepEqual(data.baseData.id, availabilityTelemetry.id); - assert.deepEqual(data.baseData.measurements, availabilityTelemetry.measurements); - assert.deepEqual(data.baseData.success, availabilityTelemetry.success); - assert.deepEqual(data.baseData.runLocation, availabilityTelemetry.runLocation); - assert.deepEqual(data.baseData.name, availabilityTelemetry.name); - assert.deepEqual(data.baseData.properties, availabilityTelemetry.properties); - assert.deepEqual(data.baseData.duration, Util.msToTimeSpan(availabilityTelemetry.duration)); - - }); - }); - - describe("PageViewData", () => { - let pageViewTelemetry: Contracts.PageViewTelemetry; - beforeEach(() => { - pageViewTelemetry = { - duration: 100, - measurements: { "m1": 1 }, - properties: { - "prop1": "prop1 value" - }, - url: "https://www.test.com", - name: "availability test name", - }; - }); - - it("creates data with given content", () => { - var envelope = EnvelopeFactory.createEnvelope(pageViewTelemetry, Contracts.TelemetryType.PageView); - var data = >envelope.data; - - assert.deepEqual(data.baseType, "PageViewData"); - - assert.deepEqual(data.baseData.url, pageViewTelemetry.url); - assert.deepEqual(data.baseData.measurements, pageViewTelemetry.measurements); - assert.deepEqual(data.baseData.name, pageViewTelemetry.name); - assert.deepEqual(data.baseData.properties, pageViewTelemetry.properties); - assert.deepEqual(data.baseData.duration, Util.msToTimeSpan(pageViewTelemetry.duration)); - - }); - }); - - describe("MetricData", () => { - let metricTelemetry: Contracts.MetricTelemetry; - beforeEach(() => { - metricTelemetry = { - name: "TestName", - value: 123, - namespace: "TestNamespace", - count: 456, - min: 1, - max: 8, - stdDev: 4 - }; - }); - - it("creates data with given content", () => { - var envelope = EnvelopeFactory.createEnvelope(metricTelemetry, Contracts.TelemetryType.Metric); - var data = >envelope.data; - - assert.deepEqual(data.baseType, "MetricData"); - assert.deepEqual(data.baseData.metrics[0].name, metricTelemetry.name); - assert.deepEqual(data.baseData.metrics[0].value, metricTelemetry.value); - assert.deepEqual(data.baseData.metrics[0].ns, metricTelemetry.namespace); - assert.deepEqual(data.baseData.metrics[0].min, metricTelemetry.min); - assert.deepEqual(data.baseData.metrics[0].max, metricTelemetry.max); - assert.deepEqual(data.baseData.metrics[0].stdDev, metricTelemetry.stdDev); - }); - }); -}); diff --git a/Tests/Library/InternalAzureLogger.tests.ts b/Tests/Library/InternalAzureLogger.tests.ts deleted file mode 100644 index dba7fbdb1..000000000 --- a/Tests/Library/InternalAzureLogger.tests.ts +++ /dev/null @@ -1,114 +0,0 @@ -import assert = require("assert"); -import fs = require("fs"); -import sinon = require("sinon"); - -import InternalAzureLogger = require("../../Library/InternalAzureLogger"); -import FileSystemHelper = require("../../Library/FileSystemHelper"); - -describe("Library/InternalAzureLogger", () => { - - var sandbox: sinon.SinonSandbox; - - before(() => { - sandbox = sinon.sandbox.create(); - }); - - beforeEach(() => { - InternalAzureLogger["_instance"] = null; - InternalAzureLogger["_fileCleanupTimer"] = setInterval(() => { }, 0); - }); - - afterEach(() => { - sandbox.restore(); - }); - - describe("Write to file", () => { - - let internalLogger: InternalAzureLogger = null; - var originalEnv = process.env["APPLICATIONINSIGHTS_LOG_DESTINATION"]; - - before(() => { - process.env["APPLICATIONINSIGHTS_LOG_DESTINATION"] = "file"; - internalLogger = InternalAzureLogger.getInstance(); - }); - - after(() => { - process.env["APPLICATIONINSIGHTS_LOG_DESTINATION"] = originalEnv; - }) - - it("should log message to file", (done) => { - var writeSpy = sandbox.spy(FileSystemHelper, "appendFileAsync"); - internalLogger["_storeToDisk"]("testMessage").then(() => { - assert.ok(writeSpy.called); - assert.ok(writeSpy.lastCall.args[0].indexOf("applicationinsights.log") > 0); - assert.equal(writeSpy.lastCall.args[1], "testMessage\r\n"); - done(); - }).catch((error) => { done(error); }); - }); - - it("should create backup file", (done) => { - var writeSpy = sandbox.spy(FileSystemHelper, "writeFileAsync"); - var readSpy = sandbox.spy(FileSystemHelper, "readFileAsync"); - internalLogger.maxSizeBytes = 0; - internalLogger["_storeToDisk"]("backupTestMessage").then(() => { - assert.ok(readSpy.calledOnce); - assert.ok(writeSpy.calledTwice); - //assert.equal(writeSpy.args[0][0], "C:\Users\hectorh\AppData\Local\Temp\appInsights-node\1636481017787.applicationinsights.log"); // Backup file format - assert.ok(typeof writeSpy.args[0][1]); - //assert.equal(writeSpy.args[1][0], "C:\Users\hectorh\AppData\Local\Temp\appInsights-node\applicationinsights.log"); // Main file format - assert.equal(writeSpy.args[1][1], "backupTestMessage\r\n"); - done(); - }).catch((error) => { done(error); }); - }); - - it("should create multiple backup files", (done) => { - var writeSpy = sandbox.spy(FileSystemHelper, "writeFileAsync"); - var readSpy = sandbox.spy(FileSystemHelper, "readFileAsync"); - internalLogger.maxSizeBytes = 0; - internalLogger.maxHistory = 2; - internalLogger["_storeToDisk"]("testMessage").then(() => { - internalLogger["_storeToDisk"]("testMessage").then(() => { - assert.equal(writeSpy.callCount, 4); - assert.ok(readSpy.calledTwice); - done(); - }).catch((error) => { done(error); }); - }).catch((error) => { done(error); }); - }); - - it("should start file cleanup task", () => { - InternalAzureLogger["_fileCleanupTimer"] = null; - var setIntervalSpy = sandbox.spy(global, "setInterval"); - internalLogger = InternalAzureLogger.getInstance(); - assert.ok(setIntervalSpy.called); - }); - - it("should remove backup files", (done) => { - var unlinkSpy = sandbox.spy(FileSystemHelper, "unlinkAsync"); - internalLogger.maxHistory = 0; - internalLogger["_fileCleanupTask"]().then(() => { - assert.ok(unlinkSpy.called); - FileSystemHelper.readdirAsync(internalLogger["_tempDir"]).then((files) => { - assert.equal(files.length, 1); - done(); - }); - }); - }); - - it("cleanup should keep configured number of backups", (done) => { - var unlinkSpy = sandbox.spy(FileSystemHelper, "unlinkAsync"); - internalLogger.maxHistory = 1; - internalLogger.maxSizeBytes = 0; - internalLogger["_storeToDisk"]("testMessage").then(() => { - internalLogger["_storeToDisk"]("testMessage").then(() => { - internalLogger["_fileCleanupTask"]().then(() => { - assert.ok(unlinkSpy.called); - FileSystemHelper.readdirAsync(internalLogger["_tempDir"]).then((files) => { - assert.equal(files.length, 2); - done(); - }).catch((error) => { done(error); }); - }).catch((error) => { done(error); }); - }).catch((error) => { done(error); }); - }).catch((error) => { done(error); }); - }); - }); -}); diff --git a/Tests/Library/Logging.tests.ts b/Tests/Library/Logging.tests.ts deleted file mode 100644 index 6951cceb2..000000000 --- a/Tests/Library/Logging.tests.ts +++ /dev/null @@ -1,111 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); - -import Logging = require("../../Library/Logging"); -import InternalAzureLogger = require("../../Library/InternalAzureLogger"); - -describe("Library/Logging", () => { - - var sandbox: sinon.SinonSandbox; - - beforeEach(() => { - sandbox = sinon.sandbox.create(); - InternalAzureLogger["_instance"] = null; - }); - - afterEach(() => { - sandbox.restore(); - }); - - describe("Log to console", () => { - it("should log message to console", () => { - var env1 = <{ [id: string]: string }>{}; - env1["APPLICATIONINSIGHTS_LOG_DESTINATION"] = "console"; - var originalEnv = process.env; - process.env = env1; - Logging.enableDebug = true; - var consoleStub = sandbox.stub(console, "info"); - Logging.info("test"); - process.env = originalEnv; - assert.ok(consoleStub.called); - }); - - it("should not log message to console if disabled", () => { - var env1 = <{ [id: string]: string }>{}; - env1["APPLICATIONINSIGHTS_LOG_DESTINATION"] = "file"; - var originalEnv = process.env; - process.env = env1; - Logging.enableDebug = true; - var consoleStub = sandbox.stub(console, "info"); - Logging.info("test"); - process.env = originalEnv; - assert.ok(consoleStub.notCalled); - }); - }); - - describe("#info(message, ...optionalParams: any)", () => { - it("should do nothing if disabled", () => { - var originalSetting = Logging.disableWarnings; - Logging.enableDebug = false; - var infoStub = sandbox.stub(InternalAzureLogger.getInstance(), "info"); - Logging.info("test"); - assert.ok(infoStub.notCalled); - Logging.enableDebug = originalSetting; - }); - - it("should log 'info' if called", () => { - var originalSetting = Logging.enableDebug; - Logging.enableDebug = true; - var infoStub = sandbox.stub(InternalAzureLogger.getInstance(), "info"); - Logging.info("test"); - assert.ok(infoStub.calledOnce); - Logging.enableDebug = originalSetting; - }); - }); - - describe("#warn(message, ...optionalParams: any)", () => { - it("should do nothing if disabled", () => { - var originalSetting = Logging.disableWarnings; - Logging.disableWarnings = true - var warnStub = sandbox.stub(InternalAzureLogger.getInstance(), "warning"); - Logging.warn("test"); - assert.ok(warnStub.notCalled); - Logging.enableDebug = originalSetting; - }); - - it("should log 'warn' if enabled", () => { - var originalSetting = Logging.disableWarnings; - Logging.disableWarnings = false; - var warnStub = sandbox.stub(InternalAzureLogger.getInstance(), "warning"); - Logging.warn("test"); - assert.ok(warnStub.calledOnce); - Logging.enableDebug = originalSetting; - }); - }); - - describe("Log to file", () => { - it("should log message to file", () => { - var env1 = <{ [id: string]: string }>{}; - env1["APPLICATIONINSIGHTS_LOG_DESTINATION"] = "file"; - var originalEnv = process.env; - process.env = env1; - Logging.enableDebug = true; - var fileStub = sandbox.stub(InternalAzureLogger.getInstance(), "_storeToDisk"); - Logging.info("test"); - process.env = originalEnv; - assert.ok(fileStub.called); - }); - - it("should not log message to file if disabled", () => { - var env1 = <{ [id: string]: string }>{}; - env1["APPLICATIONINSIGHTS_LOG_DESTINATION"] = "console"; - var originalEnv = process.env; - process.env = env1; - Logging.enableDebug = true; - var fileStub = sandbox.stub(InternalAzureLogger.getInstance(), "_storeToDisk"); - Logging.info("test"); - process.env = originalEnv; - assert.ok(fileStub.notCalled); - }); - }); -}); diff --git a/Tests/Library/QuickPulseEnvelopeFactory.tests.ts b/Tests/Library/QuickPulseEnvelopeFactory.tests.ts deleted file mode 100644 index 9a881a212..000000000 --- a/Tests/Library/QuickPulseEnvelopeFactory.tests.ts +++ /dev/null @@ -1,21 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); - -import Contracts = require("../../Declarations/Contracts"); -import Constants = require("../../Declarations/Constants"); - -describe("Library/QuickPulseEnvelopeFactory", () => { - describe("QPS Constants", () => { - it("should convert TelemetryTypeValues to QuickPulseType", () => { - const keys = Object.keys(Contracts.TelemetryTypeString); - assert.ok(keys.length > 0); - keys.forEach((key: Contracts.TelemetryTypeKeys) => { - const value = Contracts.TelemetryTypeString[key]; - const qpsType = Constants.TelemetryTypeStringToQuickPulseType[value]; - const qpsDocType = Constants.TelemetryTypeStringToQuickPulseDocumentType[value]; - assert.equal(qpsType, Constants.QuickPulseType[key]); - assert.equal(qpsDocType, Constants.QuickPulseDocumentType[key]); - }) - }); - }); -}); diff --git a/Tests/Library/QuickPulseStateManager.tests.ts b/Tests/Library/QuickPulseStateManager.tests.ts deleted file mode 100644 index 23bf732aa..000000000 --- a/Tests/Library/QuickPulseStateManager.tests.ts +++ /dev/null @@ -1,345 +0,0 @@ -import assert = require("assert"); -import https = require("https"); -import sinon = require("sinon"); - -import QuickPulseClient = require("../../Library/QuickPulseStateManager"); -import Contracts = require("../../Declarations/Contracts"); -import AuthorizationHandler = require("../../Library/AuthorizationHandler"); -import { IncomingMessage } from "http"; -import Config = require("../../Library/Config"); -import QuickPulseSender = require("../../Library/QuickPulseSender"); -import Util = require("../../Library/Util"); - -describe("Library/QuickPulseStateManager", () => { - Util.tlsRestrictedAgent = new https.Agent(); - - describe("#constructor", () => { - let qps; - afterEach(() => { - qps = null; - }); - - it("should create a config with ikey", () => { - qps = new QuickPulseClient(new Config("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333")); - - assert.ok(qps.config); - assert.equal(qps.config.instrumentationKey, "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.ok(qps.context); - assert.equal(qps["_isEnabled"], false); - assert.equal(qps["_isCollectingData"], false); - assert.ok(qps["_sender"]); - assert.ok(Object.keys(qps["_metrics"]).length === 0); - assert.ok(qps["_documents"].length === 0); - assert.ok(qps["_collectors"].length === 0); - }); - - it("should reuse authorization handler if provided", () => { - var config = new Config("InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;"); - var handler = new AuthorizationHandler({ - async getToken(scopes: string | string[], options?: any): Promise { - return { token: "testToken", }; - } - }); - var getAuthorizationHandler = () => { - return handler; - }; - qps = new QuickPulseClient(config, null, getAuthorizationHandler); - assert.equal(qps["_sender"]["_getAuthorizationHandler"](config), handler); - }); - }); - - describe("#enable", () => { - let qps: QuickPulseClient; - - beforeEach(() => { - qps = new QuickPulseClient(new Config("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333")); - }) - afterEach(() => { - qps = null; - }); - it("should call _goQuickPulse() when isEnabled == true", () => { - const qpsStub = sinon.stub(qps, "_goQuickPulse"); - - assert.ok(qpsStub.notCalled); - qps.enable(true); - assert.ok(qpsStub.calledOnce); - assert.equal(qps["_isEnabled"], true); - - qpsStub.restore(); - }); - - it("should clear timeout handle when isEnabled == false", () => { - assert.equal(qps["_handle"], undefined); - qps["_isEnabled"] = true; - (qps["_handle"]) = setTimeout(() => { throw new Error("this error should be cancelled") }, 1000); - qps["_handle"].unref(); - assert.ok(qps["_handle"]); - - qps.enable(false); - assert.equal(qps["_handle"], undefined); - assert.equal(qps["_isEnabled"], false); - }) - }); - - describe("#reset", () => { - it("should reset metric and document buffers", () => { - let qps = new QuickPulseClient(new Config("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333")); - (qps["_metrics"]) = { foo: "bar" }; - (qps["_documents"]) = [{ foo: "bar" }]; - - assert.ok(qps["_metrics"].foo); - assert.ok(qps["_documents"].length > 0) - assert.ok((qps["_documents"][0]).foo); - - qps["_resetQuickPulseBuffer"](); - assert.ok(!qps["_metrics"].foo); - assert.ok(qps["_documents"].length === 0) - }) - }); - - describe("#_goQuickPulse", () => { - let qps: QuickPulseClient; - let postStub: sinon.SinonStub; - let pingStub: sinon.SinonStub; - - beforeEach(() => { - qps = new QuickPulseClient(new Config("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333")); - postStub = sinon.stub(qps, "_post"); - pingStub = sinon.stub(qps, "_ping"); - }) - afterEach(() => { - qps = null; - postStub.restore(); - pingStub.restore(); - }); - - it("should call _ping when not collecting data", () => { - qps.enable(true) - - assert.ok(pingStub.calledOnce); - assert.ok(postStub.notCalled); - - qps.enable(false); - }); - - it("should call _post when collecting data", () => { - assert.ok(pingStub.notCalled); - assert.ok(postStub.notCalled); - - qps["_isCollectingData"] = true; - qps.enable(true) - - assert.ok(postStub.calledOnce); - assert.ok(pingStub.notCalled); - - qps.enable(false); - }); - }); - - describe("#_goQuickPulsePingPollingHint", () => { - let qps: QuickPulseClient; - let postStub: sinon.SinonStub; - let pingStub: sinon.SinonStub; - let clock: sinon.SinonFakeTimers; - - beforeEach(() => { - clock = sinon.useFakeTimers(); - qps = new QuickPulseClient(new Config("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333")); - postStub = sinon.stub(qps, "_post"); - pingStub = sinon.stub(qps, "_ping"); - }) - afterEach(() => { - qps = null; - postStub.restore(); - pingStub.restore(); - clock.restore(); - }); - - it("should call _ping at once every 5000ms when no pollingIntervalHint is set", () => { - qps.enable(true); - - clock.tick(10000); - assert.equal(pingStub.callCount, 3); - assert.ok(postStub.notCalled); - qps.enable(false); - }); - - - it("should call _ping at a rate according to interval hint", () => { - qps["_pollingIntervalHint"] = 1000; - qps.enable(true); - - clock.tick(10000); - assert.equal(pingStub.callCount, 11); - assert.ok(postStub.notCalled); - qps.enable(false); - }); - }); - - describe("#_goQuickPulsePingWithAllHeaders", () => { - let qps: QuickPulseClient; - let submitDataStub: sinon.SinonStub; - let clock: sinon.SinonFakeTimers; - - beforeEach(() => { - clock = sinon.useFakeTimers(); - qps = new QuickPulseClient(new Config("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333")); - submitDataStub = sinon.stub(qps['_sender'], "_submitData"); - }) - afterEach(() => { - qps = null; - submitDataStub.restore(); - clock.restore(); - }); - - it("should call _ping with all expected headers set set", () => { - qps['context'].tags[qps['context'].keys.cloudRoleInstance] = 'instance1'; - qps['context'].tags[qps['context'].keys.cloudRole] = 'role1'; - qps.enable(true); - - let callArgs = submitDataStub.args; - assert.equal((callArgs[0][4][0] as any)['name'], 'x-ms-qps-stream-id'); - assert.ok((callArgs[0][4][0] as any)['value'].length > 0); - assert.equal((callArgs[0][4][1] as any)['name'], 'x-ms-qps-machine-name'); - assert.ok((callArgs[0][4][1] as any)['value'].length > 0); - assert.equal((callArgs[0][4][2] as any)['name'], 'x-ms-qps-role-name'); - assert.equal((callArgs[0][4][2] as any)['value'], 'role1'); - assert.equal((callArgs[0][4][3] as any)['name'], 'x-ms-qps-instance-name'); - assert.equal((callArgs[0][4][3] as any)['value'], 'instance1'); - assert.equal((callArgs[0][4][4] as any)['name'], 'x-ms-qps-invariant-version'); - assert.equal((callArgs[0][4][4] as any)['value'], '1'); - - assert.equal(submitDataStub.callCount, 1); - - qps.enable(false); - }); - - it("should call _ping with all expected headers set", () => { - qps.enable(true); - qps['_redirectedHost'] = 'www.example.com'; - - let callArgs = submitDataStub.args; - - clock.tick(10000); - - qps.enable(false); - - assert.equal(submitDataStub.callCount, 3); - assert.equal(callArgs[0][1], undefined); - assert.equal(callArgs[1][1], 'www.example.com'); - assert.equal(callArgs[2][1], 'www.example.com'); - - }); - }); - - describe("#_goQuickPulse", () => { - let qps: QuickPulseClient; - - beforeEach(() => { - qps = new QuickPulseClient(new Config("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333")); - - }) - afterEach(() => { - qps = null; - }); - - it("should call _quickPulseDone and set the _rediectedHost and pollingIntervalHint", () => { - - qps['_quickPulseDone'](true, { statusCode: 200 } as IncomingMessage, 'www.example.com', 2000); - - assert.equal(qps['_redirectedHost'], 'www.example.com'); - assert.equal(qps['_pollingIntervalHint'], 2000); - assert.equal(qps['_isCollectingData'], true); - assert.equal(qps['_lastSendSucceeded'], true); - }); - - it("should call _quickPulseDone and not set the _rediectedHost and pollingIntervalHint if the arguments are null", () => { - qps['_pollingIntervalHint'] = 2000; - qps['_redirectedHost'] = 'www.example.com'; - qps['_quickPulseDone'](true, { statusCode: 200 } as IncomingMessage, null, 0); - - assert.equal(qps['_redirectedHost'], 'www.example.com'); - assert.equal(qps['_pollingIntervalHint'], 2000); - - qps['_quickPulseDone'](true, { statusCode: 200 } as IncomingMessage, 'www.quickpulse.com', 5000); - - assert.equal(qps['_redirectedHost'], 'www.quickpulse.com'); - assert.equal(qps['_pollingIntervalHint'], 5000); - }); - }); - - describe("#addDocuments", () => { - var sandbox: sinon.SinonSandbox; - let qps: QuickPulseClient; - - beforeEach(() => { - sandbox = sinon.sandbox.create(); - qps = new QuickPulseClient(new Config("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333")); - }); - - afterEach(() => { - sandbox.restore(); - qps = null; - }); - - it("should add document if sending data", () => { - sandbox.stub(qps, "_goQuickPulse"); - var testEnvelope: any = { name:"Test", tags:[]}; - testEnvelope.data = { baseType: "ExceptionData", baseData: {} }; - qps.enable(true); - qps["_isCollectingData"] = true; - assert.equal(qps['_documents'].length, 0); - qps.addDocument(testEnvelope); - assert.equal(qps['_documents'].length, 1); - }); - - it("should not add document if not sending data", () => { - sandbox.stub(qps, "_goQuickPulse"); - var testEnvelope = new Contracts.Envelope(); - qps.enable(true); - assert.equal(qps['_documents'].length, 0); - qps.addDocument(testEnvelope); - assert.equal(qps['_documents'].length, 0); - }); - }); - - describe("#AuthorizationHandler ", () => { - var sandbox: sinon.SinonSandbox; - let envelope: Contracts.EnvelopeQuickPulse = { - Documents: null, - Instance: "", - RoleName: "", - InstrumentationKey: "", - InvariantVersion: 1, - MachineName: "", - Metrics: null, - StreamId: "", - Timestamp: "", - Version: "" - }; - - beforeEach(() => { - sandbox = sinon.sandbox.create(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - it("should add token if handler present", () => { - var handler = new AuthorizationHandler({ - async getToken(scopes: string | string[], options?: any): Promise { - return { token: "testToken", }; - } - }); - var getAuthorizationHandler = () => { - return handler; - }; - var config = new Config("InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;"); - var addHeaderStub = sandbox.stub(handler, "addAuthorizationHeader"); - let sender = new QuickPulseSender(config, getAuthorizationHandler); - sender.post(envelope, "", () => { }); - assert.ok(addHeaderStub.calledOnce); - }); - }); -}); diff --git a/Tests/Library/QuickPulseUtil.tests.ts b/Tests/Library/QuickPulseUtil.tests.ts deleted file mode 100644 index 428ae2f51..000000000 --- a/Tests/Library/QuickPulseUtil.tests.ts +++ /dev/null @@ -1,48 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); - -import QuickPulseUtil = require("../../Library/QuickPulseUtil"); - -describe("Library/QuickPulseUtil", () => { - describe("#getTransmissionTime", () => { - const runTest = (returns: number, expected: number) => { - const stub = sinon.stub(Date, "now").returns(returns); - assert.equal(QuickPulseUtil.getTransmissionTime(), expected); - stub.restore(); - } - - it("should return correct transmission time", () => { - runTest( - Date.UTC( - 2020, 7, 5, - 22, 15, 0, - ), // 8/5/2020 10:15:00 PM UTC - 637322625000000000, - ); - - runTest( - Date.UTC( - 2020, 7, 5, - 22, 15, 1, - ), // 8/5/2020 10:15:01 PM UTC - 637322625010000000, - ); - - runTest( - Date.UTC( - 9999, 11, 31, - 23, 59, 59, - ), // 12/31/9999 11:59:59 PM UTC - 3155378975990000000, - ); - - runTest( - Date.UTC( - 2020, 7, 6, - 10, 31, 28, - ), // 8/6/2020 10:31:28 AM UTC - 637323066880000000, - ); - }); - }); -}); diff --git a/Tests/Library/Sender.tests.ts b/Tests/Library/Sender.tests.ts deleted file mode 100644 index 0a48ee3ef..000000000 --- a/Tests/Library/Sender.tests.ts +++ /dev/null @@ -1,444 +0,0 @@ -import assert = require("assert"); -import https = require("https"); -import sinon = require("sinon"); -import nock = require("nock"); - -import Sender = require("../../Library/Sender"); -import Config = require("../../Library/Config"); -import Constants = require("../../Declarations/Constants"); -import Contracts = require("../../Declarations/Contracts"); -import AuthorizationHandler = require("../../Library/AuthorizationHandler"); -import Util = require("../../Library/Util"); -import Statsbeat = require("../../AutoCollection/Statsbeat"); -import Logging = require("../../Library/Logging"); -import { FileAccessControl } from "../../Library/FileAccessControl"; -import FileSystemHelper = require("../../Library/FileSystemHelper"); - -class SenderMock extends Sender { - public getResendInterval() { - return this._resendInterval; - } -} - -describe("Library/Sender", () => { - - Util.tlsRestrictedAgent = new https.Agent(); - var testEnvelope = new Contracts.Envelope(); - var sandbox: sinon.SinonSandbox; - let interceptor: nock.Interceptor; - let nockScope: nock.Scope; - - before(() => { - interceptor = nock(Constants.DEFAULT_BREEZE_ENDPOINT) - .post("/v2.1/track", (body: string) => { - return true; - }); - }); - - beforeEach(() => { - sandbox = sinon.sandbox.create(); - }); - - afterEach(() => { - sandbox.restore(); - if (nockScope && nockScope.restore) { - nockScope.restore(); - } - }); - - after(() => { - nock.cleanAll(); - }); - - - describe("#send(envelope)", () => { - var sender: Sender; - - before(() => { - sender = new Sender(new Config("2bb22222-bbbb-1ccc-8ddd-eeeeffff3333")); - FileAccessControl.USE_ICACLS = false; - sender.setDiskRetryMode(true); - }); - - after(() => { - FileAccessControl["USE_ICACLS"] = true; - sender.setDiskRetryMode(false); - }); - - it("should not crash JSON.stringify", () => { - var a = { b: null }; - a.b = a; - var warnStub = sandbox.stub(Logging, "warn"); - assert.doesNotThrow(() => sender.send([a])); - assert.ok(warnStub.calledOnce); - }); - - it("should try to send telemetry from disk when 200", (done) => { - var breezeResponse: Contracts.BreezeResponse = { - itemsAccepted: 1, - itemsReceived: 1, - errors: [] - }; - let diskEnvelope = new Contracts.Envelope(); - diskEnvelope.name = "DiskEnvelope"; - sender["_storeToDisk"]([diskEnvelope]); - var sendSpy = sandbox.spy(sender, "send"); - nockScope = interceptor.reply(200, breezeResponse); - nockScope.persist(); - sender["_resendInterval"] = 100; - sender.send([testEnvelope], (responseText) => { - // Wait for resend timer - setTimeout(() => { - assert.ok(sendSpy.calledTwice); - assert.equal(sendSpy.secondCall.args[0][0].name, "DiskEnvelope"); - done(); - }, 200) - - }); - }); - - it("should put telemetry in disk when retryable code is returned", (done) => { - var envelope = new Contracts.Envelope(); - envelope.name = "TestRetryable"; - nockScope = interceptor.reply(408, null); - var storeStub = sandbox.stub(sender, "_storeToDisk"); - sender.send([envelope], (responseText) => { - assert.ok(storeStub.calledOnce); - assert.equal(storeStub.firstCall.args[0][0].name, "TestRetryable"); - done(); - }); - }); - - it("should retry only failed events in partial content response", (done) => { - var breezeResponse: Contracts.BreezeResponse = { - itemsAccepted: 2, - itemsReceived: 4, - errors: [{ - index: 0, - statusCode: 408, - message: "" - }, { - index: 2, - statusCode: 123, - message: "" - }] - }; - var envelopes = []; - for (var i = 0; i < 4; i++) { - var newEnvelope = new Contracts.Envelope(); - newEnvelope.name = "TestPartial" + i; - envelopes.push(newEnvelope); - } - nockScope = interceptor.reply(206, breezeResponse); - var storeStub = sandbox.stub(sender, "_storeToDisk"); - sender.send(envelopes, () => { - assert.ok(storeStub.calledOnce); - assert.equal(storeStub.firstCall.args[0].length, 1); - assert.equal(storeStub.firstCall.args[0][0].name, "TestPartial0"); - done(); - }); - }); - }); - - describe("#setOfflineMode(value, resendInterval)", () => { - var sender: SenderMock; - beforeEach(() => { - sender = new SenderMock(new Config("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333")); - }); - - after(() => { - sender.setDiskRetryMode(false); - }); - - it("default resend interval is 60 seconds", () => { - sender.setDiskRetryMode(true); - assert.equal(Sender.WAIT_BETWEEN_RESEND, sender.getResendInterval()); - }); - - it("resend interval can be configured", () => { - sender.setDiskRetryMode(true, 0); - assert.equal(0, sender.getResendInterval()); - - sender.setDiskRetryMode(true, 1234); - assert.equal(1234, sender.getResendInterval()); - - sender.setDiskRetryMode(true, 1234.56); - assert.equal(1234, sender.getResendInterval()); - }); - - it("resend interval can't be negative", () => { - sender.setDiskRetryMode(true, -1234); - assert.equal(Sender.WAIT_BETWEEN_RESEND, sender.getResendInterval()); - }); - }); - - describe("#endpoint redirect", () => { - it("should change ingestion endpoint when redirect response code is returned (308)", (done) => { - let redirectHost = "https://test"; - let redirectLocation = redirectHost + "/v2.1/track"; - // Fake redirect endpoint - let redirectInterceptor = nock(redirectHost) - .post("/v2.1/track", (body: string) => { - return true; - }); - redirectInterceptor.reply(200, {}); - - nockScope = interceptor.reply(308, {}, { "Location": redirectLocation }); - var testSender = new Sender(new Config("2bb22222-bbbb-1ccc-8ddd-eeeeffff3333")); - var sendSpy = sandbox.spy(testSender, "send"); - testSender.send([testEnvelope], (responseText) => { - assert.equal(testSender["_redirectedHost"], redirectLocation); - assert.ok(sendSpy.callCount === 2); // Original and redirect calls - done(); - }); - }); - - it("should change ingestion endpoint when temporary redirect response code is returned (307)", (done) => { - let redirectHost = "https://test"; - let redirectLocation = redirectHost + "/v2.1/track"; - // Fake redirect endpoint - let redirectInterceptor = nock(redirectHost) - .post("/v2.1/track", (body: string) => { - return true; - }); - redirectInterceptor.reply(200, {}); - - nockScope = interceptor.reply(307, {}, { "Location": redirectLocation }); - var testSender = new Sender(new Config("2bb22222-bbbb-1ccc-8ddd-eeeeffff3333")); - var sendSpy = sandbox.spy(testSender, "send"); - testSender.send([testEnvelope], (responseText) => { - assert.equal(testSender["_redirectedHost"], redirectLocation); - assert.ok(sendSpy.callCount === 2); // Original and redirect calls - done(); - }); - }); - - it("should not change ingestion endpoint if redirect is not triggered", (done) => { - nockScope = interceptor.reply(200, {}, { "Location": "testLocation" }); - var testSender = new Sender(new Config("2bb22222-bbbb-1ccc-8ddd-eeeeffff3333")); - testSender.send([testEnvelope], (responseText) => { - assert.equal(testSender["_redirectedHost"], null); - done(); - }); - }); - - it("should use redirect URL for following requests", (done) => { - let redirectHost = "https://testlocation"; - let redirectLocation = redirectHost + "/v2.1/track"; - // Fake redirect endpoint - let redirectInterceptor = nock(redirectHost) - .post("/v2.1/track", (body: string) => { - return true; - }); - - redirectInterceptor.reply(200, { "redirectProperty": true }).persist(); - - nockScope = interceptor.reply(308, {}, { "Location": redirectLocation }); - var testSender = new Sender(new Config("2bb22222-bbbb-1ccc-8ddd-eeeeffff3333")); - var sendSpy = sandbox.spy(testSender, "send"); - testSender.send([testEnvelope], (resposneText) => { - assert.equal(testSender["_redirectedHost"], redirectLocation); - assert.equal(resposneText, '{"redirectProperty":true}'); - assert.ok(sendSpy.calledTwice); - testSender.send([testEnvelope], (secondResponseText) => { - assert.equal(secondResponseText, '{"redirectProperty":true}'); - assert.ok(sendSpy.calledThrice); - done(); - }); - }); - }); - - it("should stop redirecting when circular redirect is triggered", (done) => { - let redirectHost = "https://circularredirect"; - // Fake redirect endpoint - let redirectInterceptor = nock(redirectHost) - .post("/v2.1/track", (body: string) => { - return true; - }); - redirectInterceptor.reply(307, {}, { "Location": Constants.DEFAULT_BREEZE_ENDPOINT + "/v2.1/track" }).persist(); - - nockScope = interceptor.reply(307, {}, { "Location": redirectHost + "/v2.1/track" }); - var testSender = new Sender(new Config("2bb22222-bbbb-1ccc-8ddd-eeeeffff3333")); - var sendSpy = sandbox.spy(testSender, "send"); - testSender.send([testEnvelope], (responseText) => { - assert.equal(responseText, "Error sending telemetry because of circular redirects."); - assert.equal(sendSpy.callCount, 10); - done(); - }); - }); - - }); - - describe("#fileCleanupTask", () => { - var sender: Sender; - - after(() => { - FileAccessControl["USE_ICACLS"] = true; - sender.setDiskRetryMode(false); - }); - - it("must clean old files from temp location", (done) => { - var deleteSpy = sandbox.spy(FileSystemHelper, "unlinkAsync"); - sender = new Sender(new Config("3bb33333-bbbb-1ccc-8ddd-eeeeffff3333")); - FileAccessControl["USE_ICACLS"] = false; - (sender.constructor).CLEANUP_TIMEOUT = 500; - (sender.constructor).FILE_RETEMPTION_PERIOD = 1; - var taskSpy = sandbox.spy(sender, "_fileCleanupTask"); - sender.setDiskRetryMode(true); - let diskEnvelope = new Contracts.Envelope(); - diskEnvelope.name = "DiskEnvelope"; - sender["_storeToDisk"]([diskEnvelope]); - setTimeout(() => { - assert.ok(taskSpy.calledOnce); - assert.ok(deleteSpy.called); - done(); - }, 600); - }); - }); - - describe("#AuthorizationHandler ", () => { - before(() => { - nock("https://dc.services.visualstudio.com") - .post("/v2.1/track", (body: string) => { - return true; - }) - .reply(200, { - itemsAccepted: 1, - itemsReceived: 1, - errors: [] - }) - .persist(); - }); - - var sandbox: sinon.SinonSandbox; - - beforeEach(() => { - sandbox = sinon.sandbox.create(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - after(() => { - nock.cleanAll(); - }); - - it("should add token if handler present", () => { - var handler = new AuthorizationHandler({ - async getToken(scopes: string | string[], options?: any): Promise { - return { token: "testToken", }; - } - }); - var getAuthorizationHandler = () => { - return handler; - }; - var config = new Config("InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - var addHeaderStub = sandbox.stub(handler, "addAuthorizationHeader"); - - var sender = new Sender(config, getAuthorizationHandler); - sender.send([testEnvelope]); - assert.ok(addHeaderStub.calledOnce); - }); - - it("should put telemetry to disk if auth fails", () => { - var handler = new AuthorizationHandler({ - async getToken(scopes: string | string[], options?: any): Promise { - return { token: "testToken", }; - } - }); - var getAuthorizationHandler = () => { - return handler; - }; - var config = new Config("InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;"); - var addHeaderStub = sandbox.stub(handler, "addAuthorizationHeader", () => { throw new Error(); }); - - var sender = new Sender(config, getAuthorizationHandler); - sender["_enableDiskRetryMode"] = true; - var storeToDiskStub = sandbox.stub(sender, "_storeToDisk"); - let envelope = new Contracts.Envelope(); - envelope.name = "TestEnvelope"; - sender.send([envelope]); - assert.ok(addHeaderStub.calledOnce); - assert.ok(storeToDiskStub.calledOnce); - assert.equal(storeToDiskStub.firstCall.args[0][0].name, "TestEnvelope"); - }); - }); - - describe("#Statsbeat counters", () => { - Statsbeat.CONNECTION_STRING = "InstrumentationKey=2aa22222-bbbb-1ccc-8ddd-eeeeffff3333;" - var breezeResponse: Contracts.BreezeResponse = { - itemsAccepted: 1, - itemsReceived: 1, - errors: [] - }; - - let config = new Config("2bb22222-bbbb-1ccc-8ddd-eeeeffff3333"); - let statsbeat = new Statsbeat(config); - let statsbeatSender = new Sender(config, null, null, null, statsbeat); - - it("Succesful requests", (done) => { - var statsbeatSpy = sandbox.spy(statsbeat, "countRequest"); - nockScope = interceptor.reply(200, breezeResponse); - statsbeatSender.send([testEnvelope], () => { - assert.ok(statsbeatSpy.calledOnce); - assert.equal(statsbeatSpy.args[0][0], 0); // Category - assert.equal(statsbeatSpy.args[0][1], "dc.services.visualstudio.com"); // Endpoint - assert.ok(!isNaN(statsbeatSpy.args[0][2])); // Duration - assert.equal(statsbeatSpy.args[0][3], true); // Success - done(); - - }); - }); - - it("Failed requests", (done) => { - var statsbeatSpy = sandbox.spy(statsbeat, "countRequest"); - nockScope = interceptor.reply(400, breezeResponse); - statsbeatSender.send([testEnvelope], () => { - assert.ok(statsbeatSpy.calledOnce); - assert.equal(statsbeatSpy.args[0][0], 0); // Category - assert.equal(statsbeatSpy.args[0][1], "dc.services.visualstudio.com"); // Endpoint - assert.ok(!isNaN(statsbeatSpy.args[0][2])); // Duration - assert.equal(statsbeatSpy.args[0][3], false); // Failed - done(); - }); - }); - - it("Retry counts", (done) => { - statsbeatSender.setDiskRetryMode(true); - var statsbeatSpy = sandbox.spy(statsbeat, "countRequest"); - var retrySpy = sandbox.spy(statsbeat, "countRetry"); - nockScope = interceptor.reply(206, breezeResponse); - statsbeatSender.send([testEnvelope], () => { - assert.ok(statsbeatSpy.calledOnce); - assert.ok(retrySpy.calledOnce); - done(); - }); - }); - - it("Throttle counts", (done) => { - statsbeatSender.setDiskRetryMode(true); - var statsbeatSpy = sandbox.spy(statsbeat, "countRequest"); - var throttleSpy = sandbox.spy(statsbeat, "countThrottle"); - nockScope = interceptor.reply(429, breezeResponse); - statsbeatSender.send([testEnvelope], () => { - assert.ok(statsbeatSpy.calledOnce); - assert.ok(throttleSpy.calledOnce); - done(); - }); - }); - - it("Exception counts", (done) => { - statsbeatSender.setDiskRetryMode(false); - var statsbeatSpy = sandbox.spy(statsbeat, "countRequest"); - var exceptionSpy = sandbox.spy(statsbeat, "countException"); - nockScope = interceptor.replyWithError("Test Error"); - statsbeatSender.send([testEnvelope], () => { - assert.equal(statsbeatSpy.callCount, 0); - assert.ok(exceptionSpy.calledOnce); - done(); - }); - }); - - }); -}); \ No newline at end of file diff --git a/Tests/Library/Util.tests.ts b/Tests/Library/Util.tests.ts deleted file mode 100644 index 5a6a2ce85..000000000 --- a/Tests/Library/Util.tests.ts +++ /dev/null @@ -1,528 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); -var http = require("http"); -var https = require("https"); -import url = require('url'); - -import Util = require("../../Library/Util"); -import { Configuration } from "../../applicationinsights"; - -describe("Library/Util", () => { - - describe("#getCookie(name, cookie)", () => { - - var test = (cookie: string, query: string, expected: string) => { - var actual = Util.getCookie(query, cookie); - assert.equal(expected, actual, "cookie is parsed correctly"); - } - - it("should parse expected input", () => { - test("testCookie=id|acq|renewal", "testCookie", "id|acq|renewal"); - }); - - it("should parse expected input with another cookie present before", () => { - test("other=foo; testCookie=id|acq|renewal", "testCookie", "id|acq|renewal"); - }); - - it("should parse expected input with another cookie present after", () => { - test("another=bar; ;a=testCookie=; testCookie=id|acq|renewal; other=foo|3|testCookie=", "testCookie", "id|acq|renewal"); - }); - - it("should ignore similar names", () => { - test("xtestCookiex=id|acq|renewal", "testCookie", ""); - }); - - it("should not crash on unexpected input", () => { - test("", "testCookie", ""); - }); - }); - - describe("#trim(str)", () => { - it("should not crash", () => { - assert.doesNotThrow(() => Util.trim(undefined)); - assert.doesNotThrow(() => Util.trim(null)); - assert.doesNotThrow(() => Util.trim("")); - assert.doesNotThrow(() => Util.trim(3)); - assert.doesNotThrow(() => Util.trim({})); - assert.doesNotThrow(() => Util.trim([])); - }); - - it("should trim strings", () => { - assert.equal(Util.trim(""), ""); - assert.equal(Util.trim("\t"), ""); - assert.equal(Util.trim("\n"), ""); - assert.equal(Util.trim("\t\n\r test \t\n\r"), "test"); - assert.equal(Util.trim("\t\n\r test \t\n\r test \t\n\r"), "test \t\n\r test"); - }); - }); - - describe("#w3cTraceId()", () => { - it("should generate a valid trace id", () => { - var mathStub = sinon.stub(Math, "random", () => 0); - var expected = "00000000000040008000000000000000"; - var actual = Util.w3cTraceId(); - assert.equal(actual, expected, "expected guid was generated"); - mathStub.restore(); - }); - it("should generate a valid trace id (conformance rules)", () => { - var alreadySeen: {[id: string] : boolean} = {}; - for (var i=0; i < 10; i++) { - var traceId = Util.w3cTraceId(); - assert.equal(traceId.length, 32); - assert.equal(traceId, traceId.toLowerCase()); - assert.equal(traceId, traceId.replace(/[^a-z0-9]/g, '')); - assert.ok(!alreadySeen[traceId]); - alreadySeen[traceId] = true; - } - }); - }); - - describe("#isArray(obj)", () => { - it("should detect if an object is an array", () => { - assert.ok(Util.isArray([])); - assert.ok(!Util.isArray("sdf")); - assert.ok(Util.isArray([0, 1])); - assert.ok(!Util.isArray({ length: "" })); - assert.ok(!Util.isArray({ length: 10 })); - }); - }); - - describe("#isError(obj)", () => { - it("should detect if an object is an instance of Error", () => { - class MyError extends Error { - constructor() { - super(); - } - } - - assert.ok(!Util.isError(undefined)); - assert.ok(!Util.isError(null)); - assert.ok(!Util.isError(true)); - assert.ok(!Util.isError(1)); - assert.ok(!Util.isError("")); - assert.ok(!Util.isError([])); - assert.ok(!Util.isError({})); - assert.ok(Util.isError(new Error())); - assert.ok(Util.isError(new MyError())); - }); - }); - - describe("#random32()", () => { - let test = (i: number, expected: number) => { - let mathStub = sinon.stub(Math, "random", () => i); - assert.equal(Util.random32(), expected); - mathStub.restore(); - } - it("should generate a number in the range [-0x80000000..0x7FFFFFFF]", () => { - test(0, 0); - test(0.125, 0x20000000); - test(0.25, 0x40000000); - test(0.5, -0x80000000); - test(0.75, -0x40000000); - test(1.0, 0); - }); - }); - - describe("#randomu32()", () => { - let test = (i: number, expected: number) => { - let mathStub = sinon.stub(Math, "random", () => i); - assert.equal(Util.randomu32(), expected); - mathStub.restore(); - } - it("should generate a number in the range [0x00000000..0xFFFFFFFF]", () => { - test(0, 0x80000000); - test(0.125, 0xA0000000); - test(0.25, 0xC0000000); - test(0.5, 0x00000000); - test(0.75, 0x40000000); - test(1.0, 0x80000000); - }); - }); - - describe("#uint32ArrayToBase64()", () => { - it("should convert an 32-bit array to Base64", () => { - assert.equal(Util.int32ArrayToBase64([-1, -1, -1, -1]), "/////////////////////w"); - assert.equal(Util.int32ArrayToBase64([0, 0, 0, 0]), "AAAAAAAAAAAAAAAAAAAAAA"); - assert.equal(Util.int32ArrayToBase64([0x1234567]), "ASNFZw"); - }); - }); - - describe("#msToTimeSpan(totalMs)", () => { - var test = (input: number, expected: string, message: string) => { - var actual = Util.msToTimeSpan(input); - assert.equal(expected, actual, message); - } - - it("should convert milliseconds to a c# timespan", () => { - test(0, "00:00:00.000", "zero"); - test(1, "00:00:00.001", "milliseconds digit 1"); - test(10, "00:00:00.010", "milliseconds digit 2"); - test(100, "00:00:00.100", "milliseconds digit 3"); - test(1 * 1000, "00:00:01.000", "seconds digit 1"); - test(10 * 1000, "00:00:10.000", "seconds digit 2"); - test(1 * 60 * 1000, "00:01:00.000", "minutes digit 1"); - test(10 * 60 * 1000, "00:10:00.000", "minutes digit 2"); - test(1 * 60 * 60 * 1000, "01:00:00.000", "hours digit 1"); - test(10 * 60 * 60 * 1000, "10:00:00.000", "hours digit 2"); - test(24 * 60 * 60 * 1000, "1.00:00:00.000", "hours overflow"); - test(11 * 3600000 + 11 * 60000 + 11111, "11:11:11.111", "all digits"); - test(5 * 86400000 + 13 * 3600000 + 9 * 60000 + 8 * 1000 + 789, "5.13:09:08.789", "all digits with days"); - test(1001.505, "00:00:01.001505", "fractional milliseconds"); - test(1001.5, "00:00:01.0015", "fractional milliseconds - not all precision 1"); - test(1001.55, "00:00:01.00155", "fractional milliseconds - not all precision 2"); - test(1001.5059, "00:00:01.0015059", "fractional milliseconds - all digits"); - test(1001.50559, "00:00:01.0015056", "fractional milliseconds - too many digits, round up"); - }); - - it("should handle invalid input", () => { - test("", "00:00:00.000", "invalid input"); - test("'", "00:00:00.000", "invalid input"); - test(NaN, "00:00:00.000", "invalid input"); - test({}, "00:00:00.000", "invalid input"); - test([], "00:00:00.000", "invalid input"); - test(-1, "00:00:00.000", "invalid input"); - }); - }); - - describe("#validateStringMap", () => { - it("should only allow string:string", () => { - assert.equal(Util.validateStringMap(undefined), undefined); - assert.equal(Util.validateStringMap(1), undefined); - assert.equal(Util.validateStringMap(true), undefined); - assert.equal(Util.validateStringMap("test"), undefined); - assert.equal(Util.validateStringMap(():void => null), undefined); - assert.deepEqual(Util.validateStringMap({ a: {} }), { a: "{}" }); - assert.deepEqual(Util.validateStringMap({ a: 3, b: "test" }), { a: "3", b: "test" }); - assert.deepEqual(Util.validateStringMap({ a: 0, b: null, c: undefined, d: [], e: '', f: -1, g: true, h: false }), { a: "0", b: "", c: "", d: "[]", e: "", f: "-1", g: "true", h: "false" }); - assert.deepEqual(Util.validateStringMap({ d: new Date("1995-12-17T03:24:00") }), { d: new Date("1995-12-17T03:24:00").toJSON() }); - }); - it("skips functions", () => { - assert.deepEqual(Util.validateStringMap({ f: function () { } }), { }); - }); - it("should gracefully handle errors", () => { - const vanillaError = new Error("Test userland error"); - const mapped = Util.validateStringMap({ error: vanillaError }); - const stringMapped = JSON.parse(mapped.error); - assert.equal(stringMapped.message, "Test userland error"); - assert.equal(stringMapped.stack, undefined); - assert.equal(stringMapped.code, ""); - const errorWithCode = new Error("Test error with code"); - (errorWithCode as any).code = 418; - const idMapped = Util.validateStringMap({ error: errorWithCode }); - assert.equal(JSON.parse(idMapped.error).code, 418); - }); - it("supports object and string .toJSON return values", () => { - const complex = { - secret: "private", - isPublic: "public", - toJSON: function() { - return { - isPublic: this.isPublic, - } - }, - }; - const d = new Date(1971, 5, 28); - const mapped = Util.validateStringMap({ date: d, complex }); - assert.deepEqual(JSON.parse(mapped.complex), { isPublic: "public" }); - assert.equal(mapped.date, d.toJSON()); - }); - it("should handle circular references", () => { - const circObj = <{[key: string]: any}>{}; - circObj.test = true; - circObj.circular = circObj; - circObj.arr = [0, 1, circObj.circular]; - var nodeVer = process.versions.node.split("."); - if (parseInt(nodeVer[0]) >= 12) { - // node12 changed the error string - assert.deepEqual( - Util.validateStringMap(circObj), - { - test: "true", - circular: "Object (Error: Converting circular structure to JSON\n --> starting at object with constructor 'Object'\n --- property 'circular' closes the circle)", - arr: "Array (Error: Converting circular structure to JSON\n --> starting at object with constructor 'Object'\n --- property 'circular' closes the circle)", - } - ); - } else { - assert.deepEqual( - Util.validateStringMap(circObj), - { - test: "true", - circular: "Object (Error: Converting circular structure to JSON)", - arr: "Array (Error: Converting circular structure to JSON)", - } - ); - } - }); - }); - - describe("#canIncludeCorrelationHeader", () => { - it("should return true if arguments are missing", () => { - assert.equal(Util.canIncludeCorrelationHeader(null, null), true); - assert.equal(Util.canIncludeCorrelationHeader({ config: null }, null), true); - assert.equal(Util.canIncludeCorrelationHeader({ config: { correlationHeaderExcludedDomains: [] } }, null), true); - }); - - it("should return true if domain is not on the excluded list", () => { - let client = { config: { correlationHeaderExcludedDomains: ["example.com", "bing.net", "abc.bing.com"] } }; - let url = "http://bing.com/search?q=example.com"; - - assert.equal(Util.canIncludeCorrelationHeader(client, url), true); - }); - - it("should return false if domain is on the excluded list", () => { - let client = { config: { correlationHeaderExcludedDomains: ["bing.com", "bing.net"] } }; - let url = "http://bing.com/search?q=node"; - - assert.equal(Util.canIncludeCorrelationHeader(client, url), false); - - let urlSecure = "https://bing.com/search?q=node"; - - assert.equal(Util.canIncludeCorrelationHeader(client, urlSecure), false); - - let secondDomainUrl = "http://bing.net/search?q=node"; - - assert.equal(Util.canIncludeCorrelationHeader(client, secondDomainUrl), false); - }); - - it("can take wildcards in the excluded domain list", () => { - let client = { config: { correlationHeaderExcludedDomains: ["*.bing.com"] } }; - let url = "https://abc.def.bing.com"; - - assert.equal(Util.canIncludeCorrelationHeader(client, url), false); - }); - }); - - describe("#makeRequest()", () => { - const proxyUrl = "http://10.0.0.1:3128"; - const proxyUrlHttps = "https://10.0.0.1:3128"; - const proxyUrlParsed = new url.URL(proxyUrl); - const options = { - method: "GET", - headers: <{ [key: string]: string }>{ - "Content-Type": "application/x-json-stream" - } - }; - - describe("for http request", () => { - const requestUrl = "http://abc.def.bing.com"; - const requestUrlParsed = new url.URL(requestUrl); - - beforeEach(() => { - if (process.env.hasOwnProperty('https_proxy')) { - delete process.env.https_proxy; - } - if (process.env.hasOwnProperty('http_proxy')) { - delete process.env.http_proxy; - } - if (process.env.hasOwnProperty('no_proxy')) { - delete process.env.no_proxy; - } - sinon.stub(http, 'request'); - sinon.stub(https, 'request'); - }); - - afterEach(() => { - http.request.restore(); - https.request.restore(); - }); - - it("should not override options when http_proxy not defined", () => { - const callback = sinon.spy(); - const expectedOptions = { - ...options, - host: requestUrlParsed.hostname, - port: requestUrlParsed.port, - path: requestUrlParsed.pathname - }; - const config: any = {proxyHttpUrl: undefined, proxyHttpsUrl: undefined}; - - const req = Util.makeRequest(config, requestUrl, options, callback); - - assert.equal(http.request.calledOnce, true); - assert.deepEqual(http.request.getCall(0).args[0], expectedOptions); - assert.deepEqual(http.request.getCall(0).args[1], callback); - }); - - it("should not override options when http_proxy not defined and https_proxy is defined", () => { - const callback = sinon.spy(); - const expectedOptions = { - ...options, - host: requestUrlParsed.hostname, - port: requestUrlParsed.port, - path: requestUrlParsed.pathname - }; - - const config: any = {proxyHttpUrl: undefined, proxyHttpsUrl: proxyUrl}; - const req = Util.makeRequest(config, requestUrl, options, callback); - - assert.equal(http.request.calledOnce, true); - assert.deepEqual(http.request.getCall(0).args[0], expectedOptions); - assert.deepEqual(http.request.getCall(0).args[1], callback); - }); - - it("should override options when http_proxy is defined with the correct values", () => { - const callback = sinon.spy(); - const expectedOptions = { - ...options, - host: proxyUrlParsed.hostname, - port: proxyUrlParsed.port, - path: requestUrl, - headers: {...options.headers, - Host: requestUrlParsed.hostname, - }, - }; - - const config: any = {proxyHttpUrl: proxyUrl, proxyHttpsUrl: undefined}; - const req = Util.makeRequest(config, requestUrl, options, callback); - - assert.equal(http.request.calledOnce, true); - assert.deepEqual(http.request.getCall(0).args[0], expectedOptions); - assert.deepEqual(http.request.getCall(0).args[1], callback); - }); - }); - - - describe("for https request", () => { - const requestUrl = "https://abc.def.bing.com"; - const requestUrlParsed = new url.URL(requestUrl); - - beforeEach(() => { - if (process.env.hasOwnProperty('https_proxy')) { - delete process.env.https_proxy; - } - if (process.env.hasOwnProperty('http_proxy')) { - delete process.env.http_proxy; - } - if (process.env.hasOwnProperty('no_proxy')) { - delete process.env.no_proxy; - } - sinon.stub(http, 'request'); - sinon.stub(https, 'request'); - }); - - afterEach(() => { - http.request.restore(); - https.request.restore(); - }); - - it("should not override options when https_proxy not defined", () => { - const callback = sinon.spy(); - const expectedOptions = { - ...options, - agent: Util.keepAliveAgent, - host: requestUrlParsed.hostname, - port: requestUrlParsed.port, - path: requestUrlParsed.pathname, - }; - - const config: any = {proxyHttpUrl: undefined, proxyHttpsUrl: undefined}; - const req = Util.makeRequest(config, requestUrl, options, callback); - - assert.equal(https.request.calledOnce, true, "https.request should be called"); - assert.deepEqual(https.request.getCall(0).args[0], expectedOptions); - assert.deepEqual(https.request.getCall(0).args[1], callback); - }); - - it("should not override options when https_proxy not defined (custom agent - not applied)", () => { - const customAgent = new https.Agent(); - const reqOptions = { - ...options, - agent: customAgent - } - const callback = sinon.spy(); - const expectedOptions = { - ...options, - agent: Util.keepAliveAgent, - host: requestUrlParsed.hostname, - port: requestUrlParsed.port, - path: requestUrlParsed.pathname, - }; - - const config: any = {proxyHttpUrl: undefined, proxyHttpsUrl: undefined}; - const req = Util.makeRequest(config, requestUrl, reqOptions, callback); - - assert.equal(https.request.calledOnce, true, "https.request should be called"); - assert.deepEqual(https.request.getCall(0).args[0], expectedOptions); - assert.deepEqual(https.request.getCall(0).args[1], callback); - }); - - it("should not override options when https_proxy not defined (custom agent - applied)", () => { - const customAgent = new https.Agent(); - const callback = sinon.spy(); - const expectedOptions = { - ...options, - agent: customAgent, - host: requestUrlParsed.hostname, - port: requestUrlParsed.port, - path: requestUrlParsed.pathname, - }; - - const config: any = {proxyHttpUrl: undefined, proxyHttpsUrl: undefined, httpsAgent: customAgent}; - const req = Util.makeRequest(config, requestUrl, options, callback); - - assert.equal(https.request.calledOnce, true, "https.request should be called"); - assert.deepEqual(https.request.getCall(0).args[0], expectedOptions); - assert.deepEqual(https.request.getCall(0).args[1], callback); - }); - - it("should not override options when https_proxy not defined and http_proxy is defined", () => { - const callback = sinon.spy(); - const expectedOptions = { - ...options, - agent: Util.keepAliveAgent, - host: requestUrlParsed.hostname, - port: requestUrlParsed.port, - path: requestUrlParsed.pathname, - }; - - const config: any = {proxyHttpUrl: proxyUrl, proxyHttpsUrl: undefined}; - const req = Util.makeRequest(config, requestUrl, options, callback); - - assert.equal(https.request.calledOnce, true); - assert.deepEqual(https.request.getCall(0).args[0], expectedOptions); - assert.deepEqual(https.request.getCall(0).args[1], callback); - }); - - it("should override options when https_proxy is defined with the correct values", () => { - const callback = sinon.spy(); - const expectedOptions = { - ...options, - host: proxyUrlParsed.hostname, - port: proxyUrlParsed.port, - path: requestUrl, - headers: {...options.headers, - Host: requestUrlParsed.hostname, - } - }; - - const config: any = {proxyHttpUrl: undefined, proxyHttpsUrl: proxyUrl}; - const req = Util.makeRequest(config, requestUrl, options, callback); - - assert.equal(https.request.calledOnce, false); - assert.equal(http.request.calledOnce, true); - assert.deepEqual(http.request.getCall(0).args[0], expectedOptions); - assert.deepEqual(http.request.getCall(0).args[1], callback); - }); - - it("should not override options when https_proxy is defined with a proxy using https", () => { - const callback = sinon.spy(); - const expectedOptions = { - ...options, - agent: Util.keepAliveAgent, - host: requestUrlParsed.hostname, - port: requestUrlParsed.port, - path: requestUrlParsed.pathname, - }; - const config: any = {proxyHttpUrl: undefined, proxyHttpsUrl: proxyUrlHttps}; - - const req = Util.makeRequest(config, requestUrl, options, callback); - - assert.equal(https.request.calledOnce, true); - assert.equal(http.request.calledOnce, false); - assert.deepEqual(https.request.getCall(0).args[0], expectedOptions); - assert.deepEqual(https.request.getCall(0).args[1], callback); - }); - }); - - }); -}); diff --git a/Tests/Library/jsonConfig.tests.ts b/Tests/Library/jsonConfig.tests.ts deleted file mode 100644 index 3eee680b5..000000000 --- a/Tests/Library/jsonConfig.tests.ts +++ /dev/null @@ -1,158 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); -import fs = require("fs"); -import path = require("path"); -import AppInsights = require("../../applicationinsights"); -import Logging = require("../../Library/Logging"); -import { JsonConfig } from "../../Library/JsonConfig"; - - -describe("Json Config", () => { - var sandbox: sinon.SinonSandbox; - let originalEnv: NodeJS.ProcessEnv; - - beforeEach(() => { - originalEnv = process.env; - sandbox = sinon.sandbox.create(); - JsonConfig["_instance"] = undefined; - }); - - afterEach(() => { - process.env = originalEnv; - AppInsights.dispose(); - sandbox.restore(); - }); - - after(()=>{ - JsonConfig["_instance"] = undefined; - }); - - - describe("config path", () => { - it("Default file path", () => { - let fileSpy = sandbox.spy(fs, "readFileSync"); - let loggerSpy = sandbox.spy(Logging, "info"); - const config = JsonConfig.getInstance(); - assert.equal(loggerSpy.callCount, 0); - assert.equal(fileSpy.called, 1); - let defaultPath = path.resolve(process.cwd(), "applicationinsights.json"); - assert.equal(fileSpy.args[0][0], defaultPath); - assert.equal(config.proxyHttpUrl, undefined); - }); - - it("Absolute file path", () => { - const env = <{ [id: string]: string }>{}; - const customConfigJSONPath = path.resolve(__dirname, "../../../Tests/Library/config.json"); - env["APPLICATIONINSIGHTS_CONFIGURATION_FILE"] = customConfigJSONPath; - process.env = env; - const config = JsonConfig.getInstance(); - assert.equal(config.connectionString, "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/"); - }); - - it("Relative file path", () => { - const env = <{ [id: string]: string }>{}; - const customConfigJSONPath = "./Tests/Library/config.json"; - env["APPLICATIONINSIGHTS_CONFIGURATION_FILE"] = customConfigJSONPath; - process.env = env; - const config = JsonConfig.getInstance(); - assert.equal(config.connectionString, "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/"); - }); - }); - - describe("configuration values", () => { - it("Should take configurations from JSON config file", () => { - const env = <{ [id: string]: string }>{}; - const customConfigJSONPath = path.resolve(__dirname, "../../../Tests/Library/config.json"); - env["APPLICATIONINSIGHTS_CONFIGURATION_FILE"] = customConfigJSONPath; - process.env = env; - const config = JsonConfig.getInstance(); - assert.equal(config.connectionString, "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/"); - assert.equal(config.endpointUrl, "testEndpointUrl"); - assert.equal(config.maxBatchSize, 150); - assert.equal(config.maxBatchIntervalMs, 12000); - assert.equal(config.disableAppInsights, false); - assert.equal(config.samplingPercentage, 30); - assert.equal(config.correlationIdRetryIntervalMs, 15000); - assert.equal(config.correlationHeaderExcludedDomains[0], "domain1"); - assert.equal(config.correlationHeaderExcludedDomains[1], "domain2"); - assert.equal(config.proxyHttpUrl, "testProxyHttpUrl"); - assert.equal(config.proxyHttpsUrl, "testProxyHttpsUrl"); - assert.equal(config.ignoreLegacyHeaders, false); - assert.equal(config.enableAutoCollectExternalLoggers, false); - assert.equal(config.enableAutoCollectConsole, false); - assert.equal(config.enableAutoCollectExceptions, false); - assert.equal(config.enableAutoCollectPerformance, false); - assert.equal(config.enableAutoCollectPreAggregatedMetrics, false); - assert.equal(config.enableAutoCollectHeartbeat, false); - assert.equal(config.enableAutoCollectRequests, false); - assert.equal(config.enableAutoCollectDependencies, false); - assert.equal(config.enableAutoDependencyCorrelation, false); - assert.equal(config.enableUseAsyncHooks, false); - assert.equal(config.disableStatsbeat, false); - assert.equal(config.enableAutoCollectExtendedMetrics, false); - assert.equal(config.noHttpAgentKeepAlive, false); - assert.equal(config.distributedTracingMode, 0); - assert.equal(config.enableUseDiskRetryCaching, false); - assert.equal(config.enableResendInterval, 123); - assert.equal(config.enableMaxBytesOnDisk, 456); - assert.equal(config.enableInternalDebugLogging, false); - assert.equal(config.disableStatsbeat, false); - assert.equal(config.enableInternalWarningLogging, false); - assert.equal(config.enableSendLiveMetrics, false); - assert.equal(config.extendedMetricDisablers, "gc,heap"); - assert.equal(config.noDiagnosticChannel, false); - assert.equal(config.noPatchModules, "console,redis"); - assert.equal(config.quickPulseHost, "testquickpulsehost.com"); - }); - - it("Should take configurations from environment variables", () => { - const env = <{ [id: string]: string }>{}; - env["APPLICATIONINSIGHTS_CONNECTION_STRING"] = "TestConnectionString"; - env["APPLICATION_INSIGHTS_DISABLE_EXTENDED_METRIC"] = "gc"; - env["APPLICATION_INSIGHTS_NO_PATCH_MODULES"] = "azuresdk"; - env["APPLICATION_INSIGHTS_DISABLE_ALL_EXTENDED_METRICS"] = "true"; - env["APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL"] = "true"; - env["APPLICATION_INSIGHTS_NO_STATSBEAT"] = "true"; - env["APPLICATION_INSIGHTS_NO_HTTP_AGENT_KEEP_ALIVE"] = "true"; - env["http_proxy"] = "testProxyHttpUrl2"; - env["https_proxy"] = "testProxyHttpsUrl2"; - process.env = env; - const config = JsonConfig.getInstance(); - assert.equal(config.connectionString, "TestConnectionString"); - assert.equal(config.proxyHttpUrl, "testProxyHttpUrl2"); - assert.equal(config.proxyHttpsUrl, "testProxyHttpsUrl2"); - assert.equal(config.extendedMetricDisablers, "gc"); - assert.equal(config.disableAllExtendedMetrics, true); - assert.equal(config.noDiagnosticChannel, true); - assert.equal(config.noHttpAgentKeepAlive, true); - assert.equal(config.noPatchModules, "azuresdk"); - assert.equal(config.disableStatsbeat, true); - }); - - it("Should take configurations from JSON config file over environment variables if both are configured", () => { - const env = <{ [id: string]: string }>{}; - const customConfigJSONPath = path.resolve(__dirname, "../../../Tests/Library/config.json"); - env["APPLICATIONINSIGHTS_CONFIGURATION_FILE"] = customConfigJSONPath; - env["APPLICATIONINSIGHTS_CONNECTION_STRING"] = "TestConnectionString"; - env["APPLICATION_INSIGHTS_DISABLE_EXTENDED_METRIC"] = "gc"; - env["APPLICATION_INSIGHTS_NO_PATCH_MODULES"] = "azuresdk"; - env["APPLICATION_INSIGHTS_DISABLE_ALL_EXTENDED_METRICS"] = "true"; - env["APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL"] = "true"; - env["APPLICATION_INSIGHTS_NO_STATSBEAT"] = "true"; - env["APPLICATION_INSIGHTS_NO_HTTP_AGENT_KEEP_ALIVE"] = "true"; - env["http_proxy"] = "testProxyHttpUrl2"; - env["https_proxy"] = "testProxyHttpsUrl2"; - process.env = env; - const config = JsonConfig.getInstance(); - assert.equal(config.connectionString, "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/"); - assert.equal(config.proxyHttpUrl, "testProxyHttpUrl"); - assert.equal(config.proxyHttpsUrl, "testProxyHttpsUrl"); - assert.equal(config.extendedMetricDisablers, "gc,heap"); - assert.equal(config.disableAllExtendedMetrics, false); - assert.equal(config.noDiagnosticChannel, false); - assert.equal(config.noHttpAgentKeepAlive, false); - assert.equal(config.noPatchModules, "console,redis"); - assert.equal(config.disableStatsbeat, false); - }); - }); -}); diff --git a/Tests/TelemetryProcessors/AzureRoleEnvironmentTelemetryInitializer.tests.ts b/Tests/TelemetryProcessors/AzureRoleEnvironmentTelemetryInitializer.tests.ts deleted file mode 100644 index 6a6fdfc9f..000000000 --- a/Tests/TelemetryProcessors/AzureRoleEnvironmentTelemetryInitializer.tests.ts +++ /dev/null @@ -1,34 +0,0 @@ -import assert = require("assert"); -import Client = require("../../Library/TelemetryClient"); -import { Contracts } from "../../applicationinsights"; - -import AzureProps = require("../../TelemetryProcessors/AzureRoleEnvironmentTelemetryInitializer"); - -describe("TelemetryProcessors/AzureRoleEnvironmentTelemetryInitializer", () => { - var ikey = "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"; - var envelope: Contracts.Envelope = { - ver: 2, - name: "name", - data: { - baseType: "SomeData" - }, - iKey: ikey, - sampleRate: 100, - seq: "", - time: "", - tags: [] - }; - var client = new Client(ikey); - - describe("#azureRoleEnvironmentTelemetryProcessor()", () => { - it("will add cloud role", () => { - const env = <{ [id: string]: string }>{}; - const originalEnv = process.env; - env.WEBSITE_SITE_NAME = "testRole"; - process.env = env; - AzureProps.azureRoleEnvironmentTelemetryProcessor(envelope, client.context); - assert.equal(envelope.tags[client.context.keys.cloudRole], "testRole"); - process.env = originalEnv; - }); - }); -}); \ No newline at end of file diff --git a/Tests/TelemetryProcessors/PerformanceMetricsTelemetryProcessor.tests.ts b/Tests/TelemetryProcessors/PerformanceMetricsTelemetryProcessor.tests.ts deleted file mode 100644 index 043b89ebb..000000000 --- a/Tests/TelemetryProcessors/PerformanceMetricsTelemetryProcessor.tests.ts +++ /dev/null @@ -1,54 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); - -import Config = require("../../Library/Config"); -import QuickPulse = require("../../TelemetryProcessors/PerformanceMetricsTelemetryProcessor"); -import QuickPulseStateManager = require("../../Library/QuickPulseStateManager"); -import AutoCollectPerformance = require("../../AutoCollection/Performance"); -import { Contracts } from "../../applicationinsights"; - -describe("TelemetryProcessors/PerformanceMetricsTelemetryProcessor", () => { - describe("#PerformanceMetricsTelemetryProcessor()", () => { - var envelope: Contracts.Envelope = { - ver: 2, - name: "name", - data: { - baseType: "SomeData" - }, - iKey: ikey, - sampleRate: 100, - seq: "", - time: "", - tags: [] - }; - var ikey = "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"; - - it("should return true if no client provided", () => { - var qpSpy = sinon.spy(QuickPulse, "performanceMetricsTelemetryProcessor"); - - var res = QuickPulse.performanceMetricsTelemetryProcessor(envelope); - assert.ok(qpSpy.calledOnce) - assert.equal(res, true, "returns true"); - - qpSpy.restore(); - }); - - it("should add document to the provided client", () => { - var qpSpy = sinon.spy(QuickPulse, "performanceMetricsTelemetryProcessor"); - var client: QuickPulseStateManager = new QuickPulseStateManager(new Config(ikey)); - var addDocumentStub = sinon.stub(client, "addDocument"); - - // Act - var res = QuickPulse.performanceMetricsTelemetryProcessor(envelope, client); - - // Test - assert.ok(qpSpy.calledOnce); - assert.equal(res, true); - assert.ok(addDocumentStub.calledOnce); - - - qpSpy.restore(); - addDocumentStub.restore(); - }); - }); -}); diff --git a/Tests/TelemetryProcessors/PreAggregatedMetricsTelemetryProcessor.tests.ts b/Tests/TelemetryProcessors/PreAggregatedMetricsTelemetryProcessor.tests.ts deleted file mode 100644 index dc75159e8..000000000 --- a/Tests/TelemetryProcessors/PreAggregatedMetricsTelemetryProcessor.tests.ts +++ /dev/null @@ -1,88 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); -import Client = require("../../Library/TelemetryClient"); -import TelemetryProcessor = require("../../TelemetryProcessors/PreAggregatedMetricsTelemetryProcessor"); -import AutoCollecPreAggregatedMetrics = require("../../AutoCollection/PreAggregatedMetrics"); -import { Contracts } from "../../applicationinsights"; - -describe("TelemetryProcessors/PreAggregatedMetricsTelemetryProcessor", () => { - - let envelope: Contracts.Envelope; - let client: Client; - before(() => { - var ikey = "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"; - envelope = { - ver: 2, - name: "name", - data: { - baseType: "SomeData" - }, - iKey: ikey, - sampleRate: 100, - seq: "", - time: "", - tags: [] - }; - client = new Client(ikey); - let preagg = new AutoCollecPreAggregatedMetrics(client); - preagg.enable(true); - }); - - describe("#preAggregatedMetricsTelemetryProcessor()", () => { - it("Exception telemetry", () => { - var pgSpy = sinon.spy(AutoCollecPreAggregatedMetrics, "countException"); - var exception = new Contracts.ExceptionData(); - var data = new Contracts.Data(); - data.baseData = exception; - envelope.data = data; - envelope.data.baseType = "ExceptionData"; - var res = TelemetryProcessor.preAggregatedMetricsTelemetryProcessor(envelope, client.context); - var testEnv = envelope; - assert.equal(testEnv.data.baseData.properties["_MS.ProcessedByMetricExtractors"], "(Name:'Exceptions', Ver:'1.1')"); - assert.ok(pgSpy.calledOnce); - pgSpy.restore(); - }); - - it("Trace telemetry", () => { - var pgSpy = sinon.spy(AutoCollecPreAggregatedMetrics, "countTrace"); - var trace: Contracts.TraceTelemetry = { message: "" }; - var data = new Contracts.Data(); - data.baseData = trace; - envelope.data = data; - envelope.data.baseType = "MessageData"; - var res = TelemetryProcessor.preAggregatedMetricsTelemetryProcessor(envelope, client.context); - var testEnv = envelope; - assert.equal(testEnv.data.baseData.properties["_MS.ProcessedByMetricExtractors"], "(Name:'Traces', Ver:'1.1')"); - assert.ok(pgSpy.calledOnce); - pgSpy.restore(); - }); - - it("Dependency telemetry", () => { - var pgSpy = sinon.spy(AutoCollecPreAggregatedMetrics, "countDependency"); - var dependency: Contracts.DependencyTelemetry = { name: "", dependencyTypeName: "", data: "", duration: 1, resultCode: "", success: false }; - var data = new Contracts.Data(); - data.baseData = dependency; - envelope.data = data; - envelope.data.baseType = "RemoteDependencyData"; - var res = TelemetryProcessor.preAggregatedMetricsTelemetryProcessor(envelope, client.context); - var testEnv = envelope; - assert.equal(testEnv.data.baseData.properties["_MS.ProcessedByMetricExtractors"], "(Name:'Dependencies', Ver:'1.1')"); - assert.ok(pgSpy.calledOnce); - pgSpy.restore(); - }); - - it("Request telemetry", () => { - var pgSpy = sinon.spy(AutoCollecPreAggregatedMetrics, "countRequest"); - var request: Contracts.RequestTelemetry = { name: "", url: "", duration: 1, resultCode: "", success: false }; - var data = new Contracts.Data(); - data.baseData = request; - envelope.data = data; - envelope.data.baseType = "RequestData"; - var res = TelemetryProcessor.preAggregatedMetricsTelemetryProcessor(envelope, client.context); - var testEnv = envelope; - assert.equal(testEnv.data.baseData.properties["_MS.ProcessedByMetricExtractors"], "(Name:'Requests', Ver:'1.1')"); - assert.ok(pgSpy.calledOnce); - pgSpy.restore(); - }); - }); -}); diff --git a/Tests/TelemetryProcessors/SamplingTelemetryProcessor.tests.ts b/Tests/TelemetryProcessors/SamplingTelemetryProcessor.tests.ts deleted file mode 100644 index 5b284f314..000000000 --- a/Tests/TelemetryProcessors/SamplingTelemetryProcessor.tests.ts +++ /dev/null @@ -1,153 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); -import Client = require("../../Library/TelemetryClient"); - -import Sampling = require("../../TelemetryProcessors/SamplingTelemetryProcessor"); - -describe("TelemetryProcessors/SamplingTelemetryProcessor", () => { - var iKey = "Instrumentation-Key-12345-6789A"; - var name = "name"; - var value = 3; - var mockData = { baseData: { properties: {} }, baseType: "BaseTestData" }; - var client = new Client(iKey); - - describe("#samplingTelemetryProcessor()", () => { - it("will not send data on 0% sampling", () => { - mockData.sampleRate = 0; - - var result = Sampling.samplingTelemetryProcessor(mockData, {correlationContext: null}); - - assert.equal(result, false, "data should not pass"); - }); - - it("will send MetricData data on 0% sampling", () => { - mockData.sampleRate = 0; - - mockData.baseType = "MetricData"; - var result = Sampling.samplingTelemetryProcessor(mockData, {correlationContext: null}); - mockData.baseType = "BaseTestData"; - - assert.equal(result, false, "data should not pass"); - }); - - it("will send data roughly 1/3 of the time on 33% sampling", () => { - var iterations = 1000; - var accepted = 0; - mockData.sampleRate = 33; - - for (var i=0; i (iterations * 0.25), "data should pass more than 25% of the time"); - assert.ok(accepted < (iterations * 0.45), "data should pass less than 45% the time"); - }); - - it("will send data roughly 1/2 of the time on 50% sampling", () => { - var iterations = 1000; - var accepted = 0; - mockData.sampleRate = 50; - - for (var i=0; i (iterations * 0.40), "data should pass more than 40% of the time"); - assert.ok(accepted < (iterations * 0.60), "data should pass less than 60% the time"); - }); - - it("will send data all of the time on 100% sampling", () => { - var iterations = 1000; - var accepted = 0; - mockData.sampleRate = 100; - - for (var i=0; i { - var iterations = 1000; - var accepted = 0; - mockData.sampleRate = 33; - - for (var i=0; i{correlationContext: {operation: {id: "a"}}}); - if (result) accepted++; - } - - assert.equal(accepted, iterations, "data should pass 100% of the time"); - }); - - it("will keep all telemetry from an operation together if correlation tracking is enabled #2", () => { - var iterations = 1000; - var accepted = 0; - mockData.sampleRate = 33; - - for (var i=0; i{correlationContext: {operation: {id: "abc"}}}); - if (result) accepted++; - } - - assert.equal(accepted, 0, "data should pass 0% of the time"); - }); - }); - describe("#getSamplingHashCode()", () => { - it("has results consistent with .net", () => { - // test array is produced by .net sdk test - var testArray = [ - ["ss", 1179811869], - ["kxi", 34202699], - ["wr", 1281077591], - ["ynehgfhyuiltaiqovbpyhpm", 2139623659], - ["iaxxtklcw", 1941943012], - ["hjwvqjiiwhoxrtsjma", 1824011880], - ["rpiauyg", 251412007], - ["jekvjvh", 9189387], - ["hq", 1807146729], - ["kgqxrftjhefkwlufcxibwjcy", 270215819], - ["lkfc", 1228617029], - ["skrnpybqqu", 223230949], - ["px", 70671963], - ["dtn", 904623389], - ["nqfcxobaequ", 397313566], - ["togxlt", 948170633], - ["jvvdkhnahkaujxarkd", 1486894898], - ["mcloukvkamiaqja", 56804453], - ["ornuu", 1588005865], - ["otodvlhtvu", 1544494884], - ["uhpwhasnvmnykjkitla", 981289895], - ["itbnryqnjcgpmgivlghqtg", 1923061690], - ["wauetkdnivwlafbfhiedsfx", 2114415420], - ["fniwmeidbvd", 508699380], - ["vuwdgoxspstvj", 1821547235], - ["y", 1406544563], - ["pceqcixfb", 1282453766], - ["aentke", 255756533], - ["ni", 1696510239], - ["lbwehevltlnl", 1466602040], - ["ymxql", 1974582171], - ["mvqbaosfuip", 1560556398], - ["urmwofajwmmlornynglm", 701710403], - ["buptyvonyacerrt", 1315240646], - ["cxsqcnyieliatqnwc", 76148095], - ["svvco", 1849105799], - ["luwmjhwyt", 553630912], - ["lisvmmug", 822987687], - ["mmntilfbmxwuyij", 882214597], - ["hqmyv", 1510970959], - ]; - - var csharpMax = 2147483647; - for (var i = 0; i < testArray.length; ++i) { - var res = Sampling.getSamplingHashCode(testArray[i][0]); - assert.equal(res, testArray[i][1]/csharpMax * 100); - } - }); - }); -}); \ No newline at end of file diff --git a/Tests/applicationInsights.tests.ts b/Tests/applicationInsights.tests.ts deleted file mode 100644 index 543ac391f..000000000 --- a/Tests/applicationInsights.tests.ts +++ /dev/null @@ -1,238 +0,0 @@ -import assert = require("assert"); -import sinon = require("sinon"); -import { DistributedTracingModes } from "../applicationinsights"; - -describe("ApplicationInsights", () => { - - var sandbox: sinon.SinonSandbox; - - before(() => { - sandbox = sinon.sandbox.create(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - - describe("#setup()", () => { - var AppInsights = require("../applicationinsights"); - var Console = require("../AutoCollection/Console"); - var Exceptions = require("../AutoCollection/Exceptions"); - var Performance = require("../AutoCollection/Performance"); - var HttpRequests = require("../AutoCollection/HttpRequests"); - var HttpDependencies = require("../AutoCollection/HttpDependencies"); - beforeEach(() => { - Console.INSTANCE = undefined; - Exceptions.INSTANCE = undefined; - Performance.INSTANCE = undefined; - HttpRequests.INSTANCE = undefined; - HttpDependencies.INSTANCE = undefined; - }); - - it("should not warn if setup is called once", () => { - var warnStub = sandbox.stub(console, "warn"); - AppInsights.defaultClient = undefined; - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.ok(warnStub.notCalled, "warning was not raised"); - }); - - it("should warn if setup is called twice", () => { - var warnStub = sandbox.stub(console, "warn"); - AppInsights.defaultClient = undefined; - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.ok(warnStub.calledOn, "warning was raised"); - }); - - it("should not overwrite default client if called more than once", () => { - var warnStub = sandbox.stub(console, "warn"); - AppInsights.defaultClient = undefined; - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - var client = AppInsights.defaultClient; - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.ok(client === AppInsights.defaultClient, "client is not overwritten"); - }); - }); - - describe("#start()", () => { - var AppInsights = require("../applicationinsights"); - var Console = require("../AutoCollection/Console"); - var Exceptions = require("../AutoCollection/Exceptions"); - var Performance = require("../AutoCollection/Performance"); - var HttpRequests = require("../AutoCollection/HttpRequests"); - var HttpDependencies = require("../AutoCollection/HttpDependencies"); - - beforeEach(() => { - Console.INSTANCE = undefined; - Exceptions.INSTANCE = undefined; - Performance.INSTANCE = undefined; - HttpRequests.INSTANCE = undefined; - HttpDependencies.INSTANCE = undefined; - }); - - afterEach(() => AppInsights.defaultClient = undefined); - - it("should warn if start is called before setup", () => { - var warnStub = sandbox.stub(console, "warn"); - AppInsights.start(); - assert.ok(warnStub.calledOn, "warning was raised"); - }); - - it("should not warn if start is called after setup", () => { - var warnStub = sandbox.stub(console, "warn"); - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333").start(); - assert.ok(warnStub.notCalled, "warning was not raised"); - }); - - it("should not start live metrics", () => { - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333").start(); - assert.equal(AppInsights.liveMetricsClient, undefined, "live metrics client is not defined"); - }); - - it("should not start live metrics", () => { - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333").setSendLiveMetrics(false).start(); - assert.equal(AppInsights.liveMetricsClient, undefined, "live metrics client is not defined"); - }); - }); - - describe("#setDistributedTracingMode", () => { - var AppInsights = require("../applicationinsights"); - var CorrelationIdManager = require("../Library/CorrelationIdManager"); - - beforeEach(() => { - AppInsights.dispose(); - }); - afterEach(() => { - AppInsights.dispose(); - }) - - it("should enable W3C tracing mode by default", () => { - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333").start(); - assert.equal(CorrelationIdManager.w3cEnabled, true); - }); - - it("should be able to enable W3C tracing mode via enum", () => { - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333").setDistributedTracingMode(DistributedTracingModes.AI_AND_W3C).start(); - assert.ok(CorrelationIdManager.w3cEnabled); - }); - - it("should be able to enable W3C tracing mode via number", () => { - assert.equal(DistributedTracingModes.AI_AND_W3C, 1); - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333").setDistributedTracingMode(1).start(); - assert.ok(CorrelationIdManager.w3cEnabled); - }); - }); - - describe("#setAutoCollect", () => { - var AppInsights = require("../applicationinsights"); - var Console = require("../AutoCollection/Console"); - var Exceptions = require("../AutoCollection/Exceptions"); - var Performance = require("../AutoCollection/Performance"); - var HttpRequests = require("../AutoCollection/HttpRequests"); - var HttpDependencies = require("../AutoCollection/HttpDependencies"); - - beforeEach(() => { - AppInsights.defaultClient = undefined; - Console.INSTANCE = undefined; - Exceptions.INSTANCE = undefined; - Performance.INSTANCE = undefined; - HttpRequests.INSTANCE = undefined; - HttpDependencies.INSTANCE = undefined; - }); - - it("auto-collection is initialized by default", () => { - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333").start(); - //assert.ok(Console.INSTANCE.isInitialized()); - assert.ok(Exceptions.INSTANCE.isInitialized()); - assert.ok(Performance.INSTANCE.isInitialized()); - assert.ok(HttpRequests.INSTANCE.isInitialized()); - assert.ok(HttpRequests.INSTANCE.isAutoCorrelating()); - assert.ok(HttpDependencies.INSTANCE.isInitialized()); - }); - - it("auto-collection is not initialized if disabled before 'start'", () => { - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333") - .setAutoCollectConsole(false) - .setAutoCollectExceptions(false) - .setAutoCollectPerformance(false) - .setAutoCollectRequests(false) - .setAutoCollectDependencies(false) - .setAutoDependencyCorrelation(false) - .start(); - - assert.ok(!Console.INSTANCE.isInitialized()); - assert.ok(!Exceptions.INSTANCE.isInitialized()); - assert.ok(!Performance.INSTANCE.isInitialized()); - assert.ok(!HttpRequests.INSTANCE.isInitialized()); - assert.ok(!HttpRequests.INSTANCE.isAutoCorrelating()); - assert.ok(!HttpDependencies.INSTANCE.isInitialized()); - }); - }); - - describe("#Provide access to contracts", () => { - var AppInsights = require("../applicationinsights"); - var Contracts = require("../Declarations/Contracts"); - - it("should provide access to severity levels", () => { - assert.equal(AppInsights.Contracts.SeverityLevel.Information, Contracts.SeverityLevel.Information); - }); - }); - - describe("#getCorrelationContext", () => { - var AppInsights = require("../applicationinsights"); - var Contracts = require("../Declarations/Contracts"); - var CCM = require("../AutoCollection/CorrelationContextManager").CorrelationContextManager; - var origGCC = CCM.getCurrentContext; - - beforeEach(() => { - CCM.getCurrentContext = () => 'context'; - }); - - afterEach(() => { - CCM.getCurrentContext = origGCC; - CCM.hasEverEnabled = false; - CCM.cls = undefined; - CCM.disable(); - AppInsights.dispose(); - }); - - it("should provide a context if correlating", () => { - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333") - .setAutoDependencyCorrelation(true) - .start(); - assert.equal(AppInsights.getCorrelationContext(), 'context'); - }); - - it("should provide a cls-hooked context if force flag set to true", () => { - if (CCM.canUseClsHooked()) { - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333") - .setAutoDependencyCorrelation(true, true) - .start(); - assert.equal(AppInsights.getCorrelationContext(), 'context'); - if (CCM.isNodeVersionCompatible()) { - assert.equal(CCM.cls, require('cls-hooked')); - } - } - }); - - it("should provide a continuation-local-storage context if force flag set to false", () => { - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333") - .setAutoDependencyCorrelation(true, false) - .start(); - assert.equal(AppInsights.getCorrelationContext(), 'context'); - if (CCM.isNodeVersionCompatible()) { - assert.equal(CCM.cls, require('continuation-local-storage')); - } - }); - - it("should not provide a context if not correlating", () => { - AppInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333") - .setAutoDependencyCorrelation(false) - .start(); - assert.equal(AppInsights.getCorrelationContext(), null); - }); - }); -}); diff --git a/applicationinsights.ts b/applicationinsights.ts index 43155d1e3..31be6e2e7 100644 --- a/applicationinsights.ts +++ b/applicationinsights.ts @@ -1,448 +1,14 @@ -import CorrelationContextManager = require("./AutoCollection/CorrelationContextManager"); // Keep this first -import AutoCollectConsole = require("./AutoCollection/Console"); -import AutoCollectExceptions = require("./AutoCollection/Exceptions"); -import AutoCollectPerformance = require("./AutoCollection/Performance"); -import AutoCollecPreAggregatedMetrics = require("./AutoCollection/PreAggregatedMetrics"); -import HeartBeat = require("./AutoCollection/HeartBeat"); -import AutoCollectHttpDependencies = require("./AutoCollection/HttpDependencies"); -import AutoCollectHttpRequests = require("./AutoCollection/HttpRequests"); -import CorrelationIdManager = require("./Library/CorrelationIdManager"); -import Logging = require("./Library/Logging"); -import QuickPulseClient = require("./Library/QuickPulseStateManager"); -import { IncomingMessage } from "http"; -import { SpanContext } from "@opentelemetry/api"; -import { AutoCollectNativePerformance, IDisabledExtendedMetrics } from "./AutoCollection/NativePerformance"; - -// We export these imports so that SDK users may use these classes directly. -// They're exposed using "export import" so that types are passed along as expected -export import TelemetryClient = require("./Library/NodeClient"); -export import Contracts = require("./Declarations/Contracts"); -export import azureFunctionsTypes = require("./Library/Functions"); - - -export enum DistributedTracingModes { - /** - * (Default) Send Application Insights correlation headers - */ - - AI = 0, - - /** - * Send both W3C Trace Context headers and back-compatibility Application Insights headers - */ - AI_AND_W3C -} - -// Default autocollection configuration -let defaultConfig = _getDefaultAutoCollectConfig(); -let _isConsole = defaultConfig.isConsole(); -let _isConsoleLog = defaultConfig.isConsoleLog(); -let _isExceptions = defaultConfig.isExceptions(); -let _isPerformance = defaultConfig.isPerformance(); -let _isPreAggregatedMetrics = defaultConfig.isPreAggregatedMetrics(); -let _isHeartBeat = defaultConfig.isHeartBeat(); // off by default for now -let _isRequests = defaultConfig.isRequests(); -let _isDependencies = defaultConfig.isDependencies(); -let _isDiskRetry = defaultConfig.isDiskRetry(); -let _isCorrelating = defaultConfig.isCorrelating(); -let _forceClsHooked: boolean; -let _isSendingLiveMetrics = defaultConfig.isSendingLiveMetrics(); // Off by default -let _isNativePerformance = defaultConfig.isNativePerformance(); -let _disabledExtendedMetrics: IDisabledExtendedMetrics; - -function _getDefaultAutoCollectConfig() { - return { - isConsole: () => true, - isConsoleLog: () => false, - isExceptions: () => true, - isPerformance: () => true, - isPreAggregatedMetrics: () => true, - isHeartBeat: () => false, // off by default for now - isRequests: () => true, - isDependencies: () => true, - isDiskRetry: () => true, - isCorrelating: () => true, - isSendingLiveMetrics: () => false, // Off by default - isNativePerformance: () => true - } -} - -let _diskRetryInterval: number = undefined; -let _diskRetryMaxBytes: number = undefined; - -let _console: AutoCollectConsole; -let _exceptions: AutoCollectExceptions; -let _performance: AutoCollectPerformance; -let _preAggregatedMetrics: AutoCollecPreAggregatedMetrics; -let _heartbeat: HeartBeat; -let _nativePerformance: AutoCollectNativePerformance; -let _serverRequests: AutoCollectHttpRequests; -let _clientRequests: AutoCollectHttpDependencies; - -let _isStarted = false; - -/** -* The default client, initialized when setup was called. To initialize a different client -* with its own configuration, use `new TelemetryClient(instrumentationKey?)`. -*/ -export let defaultClient: TelemetryClient; -export let liveMetricsClient: QuickPulseClient; -let _performanceLiveMetrics: AutoCollectPerformance; - -/** - * Initializes the default client. Should be called after setting - * configuration options. - * - * @param setupString the Connection String or Instrumentation Key to use. Optional, if - * this is not specified, the value will be read from the environment - * variable APPLICATIONINSIGHTS_CONNECTION_STRING or APPINSIGHTS_INSTRUMENTATIONKEY. - * @returns {Configuration} the configuration class to initialize - * and start the SDK. - */ -export function setup(setupString?: string) { - if (!defaultClient) { - defaultClient = new TelemetryClient(setupString); - _initializeConfig(); - _console = new AutoCollectConsole(defaultClient); - _exceptions = new AutoCollectExceptions(defaultClient); - _performance = new AutoCollectPerformance(defaultClient); - _preAggregatedMetrics = new AutoCollecPreAggregatedMetrics(defaultClient); - _heartbeat = new HeartBeat(defaultClient); - _serverRequests = new AutoCollectHttpRequests(defaultClient); - _clientRequests = new AutoCollectHttpDependencies(defaultClient); - if (!_nativePerformance) { - _nativePerformance = new AutoCollectNativePerformance(defaultClient); - } - } else { - Logging.info("The default client is already setup"); - } - - if (defaultClient && defaultClient.channel) { - defaultClient.channel.setUseDiskRetryCaching(_isDiskRetry, _diskRetryInterval, _diskRetryMaxBytes); - } - - return Configuration; -} - -/** - * Starts automatic collection of telemetry. Prior to calling start no - * telemetry will be *automatically* collected, though manual collection - * is enabled. - * @returns {ApplicationInsights} this class - */ -export function start() { - if (!!defaultClient) { - _isStarted = true; - _console.enable(_isConsole, _isConsoleLog); - _exceptions.enable(_isExceptions); - _performance.enable(_isPerformance); - _preAggregatedMetrics.enable(_isPreAggregatedMetrics); - _heartbeat.enable(_isHeartBeat); - _nativePerformance.enable(_isNativePerformance, _disabledExtendedMetrics); - _serverRequests.useAutoCorrelation(_isCorrelating, _forceClsHooked); - _serverRequests.enable(_isRequests); - _clientRequests.enable(_isDependencies); - if (liveMetricsClient && _isSendingLiveMetrics) { - liveMetricsClient.enable(_isSendingLiveMetrics); - } - } else { - Logging.warn("Start cannot be called before setup"); - } - - return Configuration; -} - -function _initializeConfig() { - _isConsole = defaultClient.config.enableAutoCollectExternalLoggers !== undefined ? defaultClient.config.enableAutoCollectExternalLoggers : _isConsole; - _isConsoleLog = defaultClient.config.enableAutoCollectConsole !== undefined ? defaultClient.config.enableAutoCollectConsole : _isConsoleLog; - _isExceptions = defaultClient.config.enableAutoCollectExceptions !== undefined ? defaultClient.config.enableAutoCollectExceptions : _isExceptions; - _isPerformance = defaultClient.config.enableAutoCollectPerformance !== undefined ? defaultClient.config.enableAutoCollectPerformance : _isPerformance; - _isPreAggregatedMetrics = defaultClient.config.enableAutoCollectPreAggregatedMetrics !== undefined ? defaultClient.config.enableAutoCollectPreAggregatedMetrics : _isPreAggregatedMetrics; - _isHeartBeat = defaultClient.config.enableAutoCollectHeartbeat !== undefined ? defaultClient.config.enableAutoCollectHeartbeat : _isHeartBeat; - _isRequests = defaultClient.config.enableAutoCollectRequests !== undefined ? defaultClient.config.enableAutoCollectRequests : _isRequests; - _isDependencies = defaultClient.config.enableAutoDependencyCorrelation !== undefined ? defaultClient.config.enableAutoDependencyCorrelation : _isDependencies; - _isCorrelating = defaultClient.config.enableAutoDependencyCorrelation !== undefined ? defaultClient.config.enableAutoDependencyCorrelation : _isCorrelating; - _forceClsHooked = defaultClient.config.enableUseAsyncHooks !== undefined ? defaultClient.config.enableUseAsyncHooks : _forceClsHooked; - const extendedMetricsConfig = AutoCollectNativePerformance.parseEnabled(defaultClient.config.enableAutoCollectExtendedMetrics, defaultClient.config); - _isNativePerformance = extendedMetricsConfig.isEnabled; - _disabledExtendedMetrics = extendedMetricsConfig.disabledMetrics; -} - -/** - * Returns an object that is shared across all code handling a given request. - * This can be used similarly to thread-local storage in other languages. - * Properties set on this object will be available to telemetry processors. - * - * Do not store sensitive information here. - * Custom properties set on this object can be exposed in a future SDK - * release via outgoing HTTP headers. - * This is to allow for correlating data cross-component. - * - * This method will return null if automatic dependency correlation is disabled. - * @returns A plain object for request storage or null if automatic dependency correlation is disabled. - */ -export function getCorrelationContext(): CorrelationContextManager.CorrelationContext { - if (_isCorrelating) { - return CorrelationContextManager.CorrelationContextManager.getCurrentContext(); - } - - return null; -} - -/** - * **(Experimental!)** - * Starts a fresh context or propagates the current internal one. - */ -export function startOperation(context: SpanContext, name: string): CorrelationContextManager.CorrelationContext | null; -export function startOperation(context: azureFunctionsTypes.Context, request: azureFunctionsTypes.HttpRequest): CorrelationContextManager.CorrelationContext | null; -export function startOperation(context: azureFunctionsTypes.Context, name: string): CorrelationContextManager.CorrelationContext | null; -export function startOperation(context: IncomingMessage | azureFunctionsTypes.HttpRequest, request?: never): CorrelationContextManager.CorrelationContext | null; -export function startOperation(context: azureFunctionsTypes.Context | (IncomingMessage | azureFunctionsTypes.HttpRequest) | (SpanContext), request?: azureFunctionsTypes.HttpRequest | string): CorrelationContextManager.CorrelationContext | null { - return CorrelationContextManager.CorrelationContextManager.startOperation(context, request); -} - -/** - * Returns a function that will get the same correlation context within its - * function body as the code executing this function. - * Use this method if automatic dependency correlation is not propagating - * correctly to an asynchronous callback. - */ -export function wrapWithCorrelationContext(fn: T, context?: CorrelationContextManager.CorrelationContext): T { - return CorrelationContextManager.CorrelationContextManager.wrapCallback(fn, context); -} - -/** - * The active configuration for global SDK behaviors, such as autocollection. - */ -export class Configuration { - // Convenience shortcut to ApplicationInsights.start - public static start = start; - - /** - * Sets the distributed tracing modes. If W3C mode is enabled, W3C trace context - * headers (traceparent/tracestate) will be parsed in all incoming requests, and included in outgoing - * requests. In W3C mode, existing back-compatibility AI headers will also be parsed and included. - * Enabling W3C mode will not break existing correlation with other Application Insights instrumented - * services. Default=AI - */ - public static setDistributedTracingMode(value: DistributedTracingModes) { - CorrelationIdManager.w3cEnabled = value === DistributedTracingModes.AI_AND_W3C; - return Configuration; - } - - /** - * Sets the state of console and logger tracking (enabled by default for third-party loggers only) - * @param value if true logger activity will be sent to Application Insights - * @param collectConsoleLog if true, logger autocollection will include console.log calls (default false) - * @returns {Configuration} this class - */ - public static setAutoCollectConsole(value: boolean, collectConsoleLog: boolean = false) { - _isConsole = value; - _isConsoleLog = collectConsoleLog; - if (_isStarted) { - _console.enable(value, collectConsoleLog); - } - - return Configuration; - } - - /** - * Sets the state of exception tracking (enabled by default) - * @param value if true uncaught exceptions will be sent to Application Insights - * @returns {Configuration} this class - */ - public static setAutoCollectExceptions(value: boolean) { - _isExceptions = value; - if (_isStarted) { - _exceptions.enable(value); - } - - return Configuration; - } - - /** - * Sets the state of performance tracking (enabled by default) - * @param value if true performance counters will be collected every second and sent to Application Insights - * @param collectExtendedMetrics if true, extended metrics counters will be collected every minute and sent to Application Insights - * @returns {Configuration} this class - */ - public static setAutoCollectPerformance(value: boolean, collectExtendedMetrics: boolean | IDisabledExtendedMetrics = true) { - _isPerformance = value; - const extendedMetricsConfig = AutoCollectNativePerformance.parseEnabled(collectExtendedMetrics, defaultClient.config); - _isNativePerformance = extendedMetricsConfig.isEnabled; - _disabledExtendedMetrics = extendedMetricsConfig.disabledMetrics; - if (_isStarted) { - _performance.enable(value); - _nativePerformance.enable(extendedMetricsConfig.isEnabled, extendedMetricsConfig.disabledMetrics); - } - - return Configuration; - } - - /** - * Sets the state of pre aggregated metrics tracking (enabled by default) - * @param value if true pre aggregated metrics will be collected every minute and sent to Application Insights - * @returns {Configuration} this class - */ - public static setAutoCollectPreAggregatedMetrics(value: boolean) { - _isPreAggregatedMetrics = value; - if (_isStarted) { - _preAggregatedMetrics.enable(value); - } - - return Configuration; - } - - /** - * Sets the state of request tracking (enabled by default) - * @param value if true HeartBeat metric data will be collected every 15 mintues and sent to Application Insights - * @returns {Configuration} this class - */ - public static setAutoCollectHeartbeat(value: boolean) { - _isHeartBeat = value; - if (_isStarted) { - _heartbeat.enable(value); - } - - return Configuration; - } - - /** - * Sets the state of request tracking (enabled by default) - * @param value if true requests will be sent to Application Insights - * @returns {Configuration} this class - */ - public static setAutoCollectRequests(value: boolean) { - _isRequests = value; - if (_isStarted) { - _serverRequests.enable(value); - } - - return Configuration; - } - - /** - * Sets the state of dependency tracking (enabled by default) - * @param value if true dependencies will be sent to Application Insights - * @returns {Configuration} this class - */ - public static setAutoCollectDependencies(value: boolean) { - _isDependencies = value; - if (_isStarted) { - _clientRequests.enable(value); - } - - return Configuration; - } - - /** - * Sets the state of automatic dependency correlation (enabled by default) - * @param value if true dependencies will be correlated with requests - * @param useAsyncHooks if true, forces use of experimental async_hooks module to provide correlation. If false, instead uses only patching-based techniques. If left blank, the best option is chosen for you based on your version of Node.js. - * @returns {Configuration} this class - */ - public static setAutoDependencyCorrelation(value: boolean, useAsyncHooks?: boolean) { - _isCorrelating = value; - _forceClsHooked = useAsyncHooks; - if (_isStarted) { - _serverRequests.useAutoCorrelation(value, useAsyncHooks); - } - - return Configuration; - } - - /** - * Enable or disable disk-backed retry caching to cache events when client is offline (enabled by default) - * Note that this method only applies to the default client. Disk-backed retry caching is disabled by default for additional clients. - * For enable for additional clients, use client.channel.setUseDiskRetryCaching(true). - * These cached events are stored in your system or user's temporary directory and access restricted to your user when possible. - * @param value if true events that occured while client is offline will be cached on disk - * @param resendInterval The wait interval for resending cached events. - * @param maxBytesOnDisk The maximum size (in bytes) that the created temporary directory for cache events can grow to, before caching is disabled. - * @returns {Configuration} this class - */ - public static setUseDiskRetryCaching(value: boolean, resendInterval?: number, maxBytesOnDisk?: number) { - _isDiskRetry = value; - _diskRetryInterval = resendInterval; - _diskRetryMaxBytes = maxBytesOnDisk; - if (defaultClient && defaultClient.channel) { - defaultClient.channel.setUseDiskRetryCaching(_isDiskRetry, _diskRetryInterval, _diskRetryMaxBytes); - } - return Configuration; - } - - /** - * Enables debug and warning logging for AppInsights itself. - * @param enableDebugLogging if true, enables debug logging - * @param enableWarningLogging if true, enables warning logging - * @returns {Configuration} this class - */ - public static setInternalLogging(enableDebugLogging = false, enableWarningLogging = true) { - Logging.enableDebug = enableDebugLogging; - Logging.disableWarnings = !enableWarningLogging; - return Configuration; - } - - /** - * Enables communication with Application Insights Live Metrics. - * @param enable if true, enables communication with the live metrics service - */ - public static setSendLiveMetrics(enable = false) { - if (!defaultClient) { - // Need a defaultClient so that we can add the QPS telemetry processor to it - Logging.warn("Live metrics client cannot be setup without the default client"); - return Configuration; - } - - if (!liveMetricsClient && enable) { - // No qps client exists. Create one and prepare it to be enabled at .start() - liveMetricsClient = new QuickPulseClient(defaultClient.config, defaultClient.context, defaultClient.getAuthorizationHandler); - _performanceLiveMetrics = new AutoCollectPerformance(liveMetricsClient as any, 1000, true); - liveMetricsClient.addCollector(_performanceLiveMetrics); - defaultClient.quickPulseClient = liveMetricsClient; // Need this so we can forward all manual tracks to live metrics via PerformanceMetricsTelemetryProcessor - } else if (liveMetricsClient) { - // qps client already exists; enable/disable it - liveMetricsClient.enable(enable); - } - _isSendingLiveMetrics = enable; - return Configuration; - } -} - -/** - * Disposes the default client and all the auto collectors so they can be reinitialized with different configuration -*/ -export function dispose() { - CorrelationIdManager.w3cEnabled = true; // reset to default - defaultClient = null; - _isStarted = false; - if (_console) { - _console.dispose(); - } - if (_exceptions) { - _exceptions.dispose(); - } - if (_performance) { - _performance.dispose(); - } - if (_preAggregatedMetrics) { - _preAggregatedMetrics.dispose(); - } - if (_heartbeat) { - _heartbeat.dispose(); - } - if (_nativePerformance) { - _nativePerformance.dispose(); - } - if (_serverRequests) { - _serverRequests.dispose(); - } - if (_clientRequests) { - _clientRequests.dispose(); - } - if (liveMetricsClient) { - liveMetricsClient.enable(false); - _isSendingLiveMetrics = false; - liveMetricsClient = undefined; - } -} \ No newline at end of file +export { + setup, + start, + getCorrelationContext, + startOperation, + wrapWithCorrelationContext, + Configuration, + defaultClient, + Contracts, + DistributedTracingModes, + HttpRequest, + TelemetryClient, + dispose, +} from "./src/shim/applicationinsights"; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index eefd1e7d4..fa1ee3c5b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,1985 +1,5548 @@ { "name": "applicationinsights", - "version": "2.2.1", - "lockfileVersion": 1, + "version": "3.0.0-beta.12", + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@azure/abort-controller": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.0.4.tgz", - "integrity": "sha512-lNUmDRVGpanCsiUN3NWxFTdwmdFI53xwhkTFfHDGTYk46ca7Ind3nanJc+U6Zj9Tv+9nTCWRBscWEW1DyKOpTw==", - "requires": { - "tslib": "^2.0.0" + "packages": { + "": { + "name": "applicationinsights", + "version": "3.0.0-beta.12", + "license": "MIT", + "dependencies": { + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.0.0", + "@azure/core-rest-pipeline": "^1.9.2", + "@azure/identity": "^3.1.3", + "@azure/monitor-opentelemetry": "^1.3.0", + "@azure/monitor-opentelemetry-exporter": "^1.0.0-beta.21", + "@azure/opentelemetry-instrumentation-azure-sdk": "^1.0.0-beta.5", + "@opentelemetry/api": "^1.8.0", + "@opentelemetry/api-logs": "^0.49.1", + "@opentelemetry/core": "^1.22.0", + "@opentelemetry/exporter-logs-otlp-http": "^0.49.1", + "@opentelemetry/exporter-metrics-otlp-http": "^0.49.1", + "@opentelemetry/exporter-trace-otlp-http": "^0.49.1", + "@opentelemetry/otlp-exporter-base": "^0.49.1", + "@opentelemetry/resources": "^1.22.0", + "@opentelemetry/sdk-logs": "^0.49.1", + "@opentelemetry/sdk-metrics": "^1.22.0", + "@opentelemetry/sdk-trace-base": "^1.22.0", + "@opentelemetry/sdk-trace-node": "^1.22.0", + "@opentelemetry/semantic-conventions": "^1.22.0", + "diagnostic-channel": "1.1.1", + "diagnostic-channel-publishers": "1.0.8" + }, + "devDependencies": { + "@azure/functions": "^3.2.0", + "@types/long": "^4.0.2", + "@types/microsoft__typescript-etw": "^0.1.0", + "@types/mocha": "^7.0.2", + "@types/node": "^8.0.0", + "@types/semver": "7.3.9", + "@types/sinon": "^10.0.12", + "@typescript-eslint/eslint-plugin": "^5.37.0", + "@typescript-eslint/parser": "^5.37.0", + "eslint": "^8.0.0", + "eslint-plugin-node": "^11.1.0", + "mocha": "^10.0.0", + "nock": "^12.0.3", + "nyc": "^15.0.0", + "prettier": "^2.5.1", + "sinon": "^9.0.2", + "typescript": "~4.8.0" + }, + "engines": { + "node": ">=8.0.0" } }, - "@azure/core-asynciterator-polyfill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz", - "integrity": "sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg==" + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "@azure/core-auth": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.3.2.tgz", - "integrity": "sha512-7CU6DmCHIZp5ZPiZ9r3J17lTKMmYsm/zGvNkjArQwPkrLlZ1TZ+EUYfGgh2X31OLMVAQCTJZW4cXHJi02EbJnA==", - "requires": { - "@azure/abort-controller": "^1.0.0", - "tslib": "^2.2.0" + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" } }, - "@azure/core-http": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-2.2.3.tgz", - "integrity": "sha512-xr8AeszxP418rI//W38NfJDDr0kbVAPZkURZnZ+Fle+lLWeURjDE5zNIuocA1wUPoKSP8iXc0ApW6nPtbLGswA==", - "requires": { + "node_modules/@azure/abort-controller": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.1.tgz", + "integrity": "sha512-NhzeNm5zu2fPlwGXPUjzsRCRuPx5demaZyNcyNYJDqpa/Sbxzvo/RYt9IwUaAOnDW5+r7J9UOE6f22TQnb9nhQ==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.7.1.tgz", + "integrity": "sha512-dyeQwvgthqs/SlPVQbZQetpslXceHd4i5a7M/7z/lGEAVwnSluabnQOjF2/dk/hhWgMISusv1Ytp4mQ8JNy62A==", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.1.tgz", + "integrity": "sha512-hHYFx9lz0ZpbO5W+iotU9tmIX1jPcoIjYUEUaWGuMi1628LCQ/z05TUR4P+NRtMgyoHQuyVYyGQiD3PC47kaIA==", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.9.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.6.1", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.15.1.tgz", + "integrity": "sha512-ZxS6i3eHxh86u+1eWZJiYywoN2vxvsSoAUx60Mny8cZ4nTwvt7UzVVBJO+m2PW2KIJfNiXMt59xBa59htOWL4g==", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.3.0", + "@azure/logger": "^1.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.1.1.tgz", + "integrity": "sha512-qPbYhN1pE5XQ2jPKIHP33x8l3oBu1UqIWnYqZZ3OYnYjzY0xqIHjn49C+ptsPD9yC7uyWI9Zm7iZUZLs2R4DhQ==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.8.1.tgz", + "integrity": "sha512-L3voj0StUdJ+YKomvwnTv7gHzguJO+a6h30pmmZdRprJCM+RJlGMPxzuh4R7lhQu1jNmEtaHX5wvTgWLDAmbGQ==", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/functions": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@azure/functions/-/functions-3.5.1.tgz", + "integrity": "sha512-6UltvJiuVpvHSwLcK/Zc6NfUwlkDLOFFx97BHCJzlWNsfiWwzwmTsxJXg4kE/LemKTHxPpfoPE+kOJ8hAdiKFQ==", + "dependencies": { + "iconv-lite": "^0.6.3", + "long": "^4.0.0", + "uuid": "^8.3.0" + } + }, + "node_modules/@azure/identity": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-3.4.2.tgz", + "integrity": "sha512-0q5DL4uyR0EZ4RXQKD8MadGH6zTIcloUoS/RVbCpNpej4pwte0xpqYxk8K97Py2RiuUvI7F4GXpoT4046VfufA==", + "dependencies": { "@azure/abort-controller": "^1.0.0", - "@azure/core-asynciterator-polyfill": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-tracing": "1.0.0-preview.13", + "@azure/core-auth": "^1.5.0", + "@azure/core-client": "^1.4.0", + "@azure/core-rest-pipeline": "^1.1.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.6.1", "@azure/logger": "^1.0.0", - "@types/node-fetch": "^2.5.0", - "@types/tunnel": "^0.0.3", - "form-data": "^4.0.0", - "node-fetch": "^2.6.6", - "process": "^0.11.10", - "tough-cookie": "^4.0.0", - "tslib": "^2.2.0", - "tunnel": "^0.0.6", - "uuid": "^8.3.0", - "xml2js": "^0.4.19" - } - }, - "@azure/core-tracing": { - "version": "1.0.0-preview.13", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", - "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", - "requires": { - "@opentelemetry/api": "^1.0.1", + "@azure/msal-browser": "^3.5.0", + "@azure/msal-node": "^2.5.1", + "events": "^3.0.0", + "jws": "^4.0.0", + "open": "^8.0.0", + "stoppable": "^1.1.0", "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "@azure/logger": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.3.tgz", - "integrity": "sha512-aK4s3Xxjrx3daZr3VylxejK3vG5ExXck5WOHDJ8in/k9AqlfIyFMMT1uG7u8mNjX+QRILTIn0/Xgschfh/dQ9g==", - "requires": { + "node_modules/@azure/identity/node_modules/@azure/abort-controller": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", + "dependencies": { "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" } }, - "@mapbox/node-pre-gyp": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz", - "integrity": "sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg==", - "dev": true, - "requires": { - "detect-libc": "^1.0.3", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.5", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - } - }, - "@opentelemetry/api": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.0.4.tgz", - "integrity": "sha512-BuJuXRSJNQ3QoKA6GWWDyuLpOUck+9hAXNMCnrloc1aWVoy6Xq6t9PUV08aBZ4Lutqq2LEHM486bpZqoViScog==" + "node_modules/@azure/logger": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.1.1.tgz", + "integrity": "sha512-/+4TtokaGgC+MnThdf6HyIH9Wrjp+CnCn3Nx3ggevN7FFjjNyjqg0yLlc2i9S+Z2uAzI8GYOo35Nzb1MhQ89MA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "@opentelemetry/core": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.0.1.tgz", - "integrity": "sha512-90nQ2X6b/8X+xjcLDBYKooAcOsIlwLRYm+1VsxcX5cHl6V4CSVmDpBreQSDH/A21SqROzapk6813008SatmPpQ==", - "requires": { - "@opentelemetry/semantic-conventions": "1.0.1" + "node_modules/@azure/monitor-opentelemetry": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/monitor-opentelemetry/-/monitor-opentelemetry-1.3.0.tgz", + "integrity": "sha512-aRC9tO6B/hm3aSSTzUKiGIUdsCzpowGw8z+XbZmAOAHVma1dcMgnXzgO7SP/ASLe9VhMwTOImeRy4MIESDmWjg==", + "dependencies": { + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.0.0", + "@azure/core-rest-pipeline": "^1.1.0", + "@azure/functions": "^3.2.0", + "@azure/logger": "^1.0.0", + "@azure/monitor-opentelemetry-exporter": "1.0.0-beta.21", + "@azure/opentelemetry-instrumentation-azure-sdk": "^1.0.0-beta.5", + "@microsoft/applicationinsights-web-snippet": "^1.1.2", + "@opentelemetry/api": "^1.8.0", + "@opentelemetry/api-logs": "^0.49.1", + "@opentelemetry/core": "^1.22.0", + "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation-bunyan": "^0.36.0", + "@opentelemetry/instrumentation-http": "^0.49.1", + "@opentelemetry/instrumentation-mongodb": "^0.40.0", + "@opentelemetry/instrumentation-mysql": "^0.36.0", + "@opentelemetry/instrumentation-pg": "^0.39.0", + "@opentelemetry/instrumentation-redis": "^0.37.0", + "@opentelemetry/instrumentation-redis-4": "^0.37.0", + "@opentelemetry/resource-detector-azure": "^0.2.4", + "@opentelemetry/resources": "^1.22.0", + "@opentelemetry/sdk-logs": "^0.49.1", + "@opentelemetry/sdk-metrics": "^1.22.0", + "@opentelemetry/sdk-node": "^0.49.1", + "@opentelemetry/sdk-trace-base": "^1.22.0", + "@opentelemetry/sdk-trace-node": "^1.22.0", + "@opentelemetry/semantic-conventions": "^1.22.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "@opentelemetry/resources": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.0.1.tgz", - "integrity": "sha512-p8DevOaAEepPucUtImR4cZKHOE2L1jgQAtkdZporV+XnxPA/HqCHPEESyUVuo4f5M0NUlL6k5Pba75KwNJlTRg==", - "requires": { - "@opentelemetry/core": "1.0.1", - "@opentelemetry/semantic-conventions": "1.0.1" + "node_modules/@azure/monitor-opentelemetry-exporter": { + "version": "1.0.0-beta.21", + "resolved": "https://registry.npmjs.org/@azure/monitor-opentelemetry-exporter/-/monitor-opentelemetry-exporter-1.0.0-beta.21.tgz", + "integrity": "sha512-2geSfdILDovNrjSYCTO6iG3OsZTQUIFSHYnW9Olao8KogYlbre1Pu02iwYW2KqDaXxMaRyT8ifRFEJJX2INqAA==", + "dependencies": { + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.0.0", + "@azure/core-rest-pipeline": "^1.1.0", + "@opentelemetry/api": "^1.7.0", + "@opentelemetry/api-logs": "^0.48.0", + "@opentelemetry/core": "^1.21.0", + "@opentelemetry/resources": "^1.21.0", + "@opentelemetry/sdk-logs": "^0.48.0", + "@opentelemetry/sdk-metrics": "^1.21.0", + "@opentelemetry/sdk-trace-base": "^1.21.0", + "@opentelemetry/semantic-conventions": "^1.21.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" } }, - "@opentelemetry/sdk-trace-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.0.1.tgz", - "integrity": "sha512-JVSAepTpW7dnqfV7XFN0zHj1jXGNd5OcvIGQl76buogqffdgJdgJWQNrOuUJaus56zrOtlzqFH+YtMA9RGEg8w==", - "requires": { - "@opentelemetry/core": "1.0.1", - "@opentelemetry/resources": "1.0.1", - "@opentelemetry/semantic-conventions": "1.0.1" + "node_modules/@azure/monitor-opentelemetry-exporter/node_modules/@opentelemetry/api": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.7.0.tgz", + "integrity": "sha512-AdY5wvN0P2vXBi3b29hxZgSFvdhdxPB9+f0B6s//P9Q8nibRWeA3cHm8UmLpio9ABigkVHJ5NMPk+Mz8VCCyrw==", + "engines": { + "node": ">=8.0.0" } }, - "@opentelemetry/semantic-conventions": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.0.1.tgz", - "integrity": "sha512-7XU1sfQ8uCVcXLxtAHA8r3qaLJ2oq7sKtEwzZhzuEXqYmjW+n+J4yM3kNo0HQo3Xp1eUe47UM6Wy6yuAvIyllg==" + "node_modules/@azure/monitor-opentelemetry-exporter/node_modules/@opentelemetry/api-logs": { + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.48.0.tgz", + "integrity": "sha512-1/aMiU4Eqo3Zzpfwu51uXssp5pzvHFObk8S9pKAiXb1ne8pvg1qxBQitYL1XUiAMEXFzgjaidYG2V6624DRhhw==", + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } }, - "@types/cls-hooked": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@types/cls-hooked/-/cls-hooked-4.3.3.tgz", - "integrity": "sha512-gNstDTb/ty5h6gJd6YpSPgsLX9LmRpaKJqGFp7MRlYxhwp4vXXKlJ9+bt1TZ9KbVNXE+Mbxy2AYXcpY21DDtJw==", - "dev": true, - "requires": { - "@types/node": "*" + "node_modules/@azure/monitor-opentelemetry-exporter/node_modules/@opentelemetry/core": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.21.0.tgz", + "integrity": "sha512-KP+OIweb3wYoP7qTYL/j5IpOlu52uxBv5M4+QhSmmUfLyTgu1OIS71msK3chFo1D6Y61BIH3wMiMYRCxJCQctA==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.21.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.8.0" } }, - "@types/mocha": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-7.0.2.tgz", - "integrity": "sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w==", - "dev": true + "node_modules/@azure/monitor-opentelemetry-exporter/node_modules/@opentelemetry/resources": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.21.0.tgz", + "integrity": "sha512-1Z86FUxPKL6zWVy2LdhueEGl9AHDJcx+bvHStxomruz6Whd02mE3lNUMjVJ+FGRoktx/xYQcxccYb03DiUP6Yw==", + "dependencies": { + "@opentelemetry/core": "1.21.0", + "@opentelemetry/semantic-conventions": "1.21.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.8.0" + } }, - "@types/node": { - "version": "8.10.66", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", - "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==" + "node_modules/@azure/monitor-opentelemetry-exporter/node_modules/@opentelemetry/sdk-logs": { + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.48.0.tgz", + "integrity": "sha512-lRcA5/qkSJuSh4ItWCddhdn/nNbVvnzM+cm9Fg1xpZUeTeozjJDBcHnmeKoOaWRnrGYBdz6UTY6bynZR9aBeAA==", + "dependencies": { + "@opentelemetry/core": "1.21.0", + "@opentelemetry/resources": "1.21.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.8.0", + "@opentelemetry/api-logs": ">=0.39.1" + } }, - "@types/node-fetch": { - "version": "2.5.12", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.12.tgz", - "integrity": "sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==", - "requires": { - "@types/node": "*", - "form-data": "^3.0.0" - }, - "dependencies": { - "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - } + "node_modules/@azure/monitor-opentelemetry-exporter/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.21.0.tgz", + "integrity": "sha512-lkC8kZYntxVKr7b8xmjCVUgE0a8xgDakPyDo9uSWavXPyYqLgYYGdEd2j8NxihRyb6UwpX3G/hFUF4/9q2V+/g==", + "engines": { + "node": ">=14" } }, - "@types/sinon": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-2.1.2.tgz", - "integrity": "sha1-iNu9KZEMT9G1+9PHM6cj8nfqtYM=", - "dev": true + "node_modules/@azure/msal-browser": { + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.11.1.tgz", + "integrity": "sha512-tZFJnP5ZpgkmazSriEDW+Xl3/4WI823uhnYhWCHPkGywFWEZoPA5VkiCK8x4x8ECXp3mGr5qEI82MU43PBiaKA==", + "dependencies": { + "@azure/msal-common": "14.8.1" + }, + "engines": { + "node": ">=0.8.0" + } }, - "@types/tunnel": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", - "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==", - "requires": { - "@types/node": "*" + "node_modules/@azure/msal-common": { + "version": "14.8.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.8.1.tgz", + "integrity": "sha512-9HfBMDTIgtFFkils+o6gO/aGEoLLuc4z+QLLfhy/T1bTNPiVsX/9CjaBPMZGnMltN/IlMkU5SGGNggGh55p5xA==", + "engines": { + "node": ">=0.8.0" } }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true + "node_modules/@azure/msal-node": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.6.6.tgz", + "integrity": "sha512-j+1hW81ccglIYWukXufzRA4O71BCmpbmCO66ECDyE9FuPno6SjiR+K+mIk4tg6aQ7/UO2QA/EnRmT6YN0EF1Hw==", + "dependencies": { + "@azure/msal-common": "14.8.1", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=16" + } }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "requires": { - "debug": "4" + "node_modules/@azure/opentelemetry-instrumentation-azure-sdk": { + "version": "1.0.0-beta.5", + "resolved": "https://registry.npmjs.org/@azure/opentelemetry-instrumentation-azure-sdk/-/opentelemetry-instrumentation-azure-sdk-1.0.0-beta.5.tgz", + "integrity": "sha512-fsUarKQDvjhmBO4nIfaZkfNSApm1hZBzcvpNbSrXdcUBxu7lRvKsV5DnwszX7cnhLyVOW9yl1uigtRQ1yDANjA==", + "dependencies": { + "@azure/core-tracing": "^1.0.0", + "@azure/logger": "^1.0.0", + "@opentelemetry/api": "^1.4.1", + "@opentelemetry/core": "^1.15.2", + "@opentelemetry/instrumentation": "^0.41.2", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", - "dev": true + "node_modules/@azure/opentelemetry-instrumentation-azure-sdk/node_modules/@opentelemetry/instrumentation": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.41.2.tgz", + "integrity": "sha512-rxU72E0pKNH6ae2w5+xgVYZLzc5mlxAbGzF4shxMVK8YC2QQsfN38B2GPbj0jvrKWWNUElfclQ+YTykkNg/grw==", + "dependencies": { + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "1.4.2", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.1", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true + "node_modules/@azure/opentelemetry-instrumentation-azure-sdk/node_modules/import-in-the-middle": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz", + "integrity": "sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw==", + "dependencies": { + "acorn": "^8.8.2", + "acorn-import-assertions": "^1.9.0", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/@babel/code-frame": { + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", "dev": true, - "requires": { - "color-convert": "^1.9.0" + "dependencies": { + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "node_modules/@babel/compat-data": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.1.tgz", + "integrity": "sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==", "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "engines": { + "node": ">=6.9.0" } }, - "applicationinsights-native-metrics": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/applicationinsights-native-metrics/-/applicationinsights-native-metrics-0.0.7.tgz", - "integrity": "sha512-dStduFRRNDJFZNvIYkRTxtvrzzHU0jdCjoKXBncDIBwsxgmIFF6n6KEe2zhIdEjYcCwgJNszJ5BeSjctcKiRRA==", + "node_modules/@babel/core": { + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.3.tgz", + "integrity": "sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==", "dev": true, - "requires": { - "@mapbox/node-pre-gyp": "^1.0.8", - "nan": "^2.14.0" + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.1", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.24.1", + "@babel/parser": "^7.24.1", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.1", + "@babel/types": "^7.24.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "aproba": { + "node_modules/@babel/core/node_modules/convert-source-map": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, - "are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" + "bin": { + "semver": "bin/semver.js" } }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/@babel/generator": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.1.tgz", + "integrity": "sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==", "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "async-hook-jl": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/async-hook-jl/-/async-hook-jl-1.7.6.tgz", - "integrity": "sha512-gFaHkFfSxTjvoxDMYqDuGHlcRyUuamF8s+ZTtJdDzqjws4mCt7v0vuV79/E2Wr2/riMQgtG4/yUtXWs1gZ7JMg==", - "requires": { - "stack-chain": "^1.3.7" + "dependencies": { + "@babel/types": "^7.24.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "async-listener": { - "version": "0.6.10", - "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz", - "integrity": "sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw==", - "requires": { - "semver": "^5.3.0", - "shimmer": "^1.1.0" - }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dev": true, "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "engines": { + "node": ">=6.9.0" } }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, - "requires": { - "fill-range": "^7.0.1" + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "node_modules/@babel/helper-module-imports": { + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", + "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "dependencies": { + "@babel/types": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "dependencies": { + "@babel/types": "^7.22.5" }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", + "node_modules/@babel/helper-string-parser": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" + "engines": { + "node": ">=6.9.0" } }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.1.tgz", + "integrity": "sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==", "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "dependencies": { + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.1", + "@babel/types": "^7.24.0" }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", + "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", + "dev": true, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "cls-hooked": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/cls-hooked/-/cls-hooked-4.2.2.tgz", - "integrity": "sha512-J4Xj5f5wq/4jAvcdgoGsL3G103BtWpZrMo8NEinRltN+xpTZdI+M38pyQqhuFU/P792xkMFvnKSf+Lm81U1bxw==", - "requires": { - "async-hook-jl": "^1.7.6", - "emitter-listener": "^1.0.1", - "semver": "^5.4.1" + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" } }, - "color-convert": { + "node_modules/@babel/highlight/node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "requires": { + "dependencies": { "color-name": "1.1.3" } }, - "color-name": { + "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, - "color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", + "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz", + "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.1", + "@babel/generator": "^7.24.1", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.24.1", + "@babel/types": "^7.24.0", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.4.tgz", + "integrity": "sha512-MqBisuxTkYvPFnEiu+dag3xG/NBUDzSbAFAWlzfkGnQkjVZ6by3h4atbBc+Ikqup1z5BfB4BN18gKWR1YyppNw==", + "dependencies": { + "@grpc/proto-loader": "^0.7.10", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.12", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.12.tgz", + "integrity": "sha512-DCVwMxqYzpUCiDMl7hQ384FqP4T3DbNpXU8pt681l3UWCip1WUiD5JrkImUwCB9a7f2cq4CUTmi5r/xIMRPY1Q==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.4", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@grpc/proto-loader/node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@microsoft/applicationinsights-web-snippet": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-snippet/-/applicationinsights-web-snippet-1.1.2.tgz", + "integrity": "sha512-qPoOk3MmEx3gS6hTc1/x8JWQG5g4BvRdH7iqZMENBsKCL927b7D7Mvl19bh3sW9Ucrg1fVrF+4hqShwQNdqLxQ==" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.8.0.tgz", + "integrity": "sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.49.1.tgz", + "integrity": "sha512-kaNl/T7WzyMUQHQlVq7q0oV4Kev6+0xFwqzofryC66jgGMacd0QH5TwfpbUwSTby+SdAdprAe5UKMvBw4tKS5Q==", + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.22.0.tgz", + "integrity": "sha512-Nfdxyg8YtWqVWkyrCukkundAjPhUXi93JtVQmqDT1mZRVKqA7e2r7eJCrI+F651XUBMp0hsOJSGiFk3QSpaIJw==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.22.0.tgz", + "integrity": "sha512-0VoAlT6x+Xzik1v9goJ3pZ2ppi6+xd3aUfg4brfrLkDBHRIVjMP0eBHrKrhB+NKcDyMAg8fAbGL3Npg/F6AwWA==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.49.1.tgz", + "integrity": "sha512-3QoBnIGCmEkujynUP0mK155QtOM0MSf9FNrEw7u9ieCFsoMiyatg2hPp+alEDONJ8N8wGEK+wP2q3icgXBiggw==", + "dependencies": { + "@opentelemetry/api-logs": "0.49.1", + "@opentelemetry/core": "1.22.0", + "@opentelemetry/otlp-exporter-base": "0.49.1", + "@opentelemetry/otlp-transformer": "0.49.1", + "@opentelemetry/sdk-logs": "0.49.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.49.1.tgz", + "integrity": "sha512-t/sWYqfcwn81SvYHIyYJDlJD2CjFz3/h2t4j+XCtdoHAfu+WVJQmwLsGYJPlCDp3UXQfFpMJMWvLlvtD2SL+rg==", + "dependencies": { + "@opentelemetry/core": "1.22.0", + "@opentelemetry/otlp-exporter-base": "0.49.1", + "@opentelemetry/otlp-transformer": "0.49.1", + "@opentelemetry/resources": "1.22.0", + "@opentelemetry/sdk-metrics": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.49.1.tgz", + "integrity": "sha512-Zbd7f3zF7fI2587MVhBizaW21cO/SordyrZGtMtvhoxU6n4Qb02Gx71X4+PzXH620e0+JX+Pcr9bYb1HTeVyJA==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.22.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.49.1", + "@opentelemetry/otlp-transformer": "0.49.1", + "@opentelemetry/resources": "1.22.0", + "@opentelemetry/sdk-trace-base": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.49.1.tgz", + "integrity": "sha512-KOLtZfZvIrpGZLVvblKsiVQT7gQUZNKcUUH24Zz6Xbi7LJb9Vt6xtUZFYdR5IIjvt47PIqBKDWUQlU0o1wAsRw==", + "dependencies": { + "@opentelemetry/core": "1.22.0", + "@opentelemetry/otlp-exporter-base": "0.49.1", + "@opentelemetry/otlp-transformer": "0.49.1", + "@opentelemetry/resources": "1.22.0", + "@opentelemetry/sdk-trace-base": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.49.1.tgz", + "integrity": "sha512-n8ON/c9pdMyYAfSFWKkgsPwjYoxnki+6Olzo+klKfW7KqLWoyEkryNkbcMIYnGGNXwdkMIrjoaP0VxXB26Oxcg==", + "dependencies": { + "@opentelemetry/core": "1.22.0", + "@opentelemetry/otlp-exporter-base": "0.49.1", + "@opentelemetry/otlp-proto-exporter-base": "0.49.1", + "@opentelemetry/otlp-transformer": "0.49.1", + "@opentelemetry/resources": "1.22.0", + "@opentelemetry/sdk-trace-base": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.22.0.tgz", + "integrity": "sha512-XcFs6rGvcTz0qW5uY7JZDYD0yNEXdekXAb6sFtnZgY/cHY6BQ09HMzOjv9SX+iaXplRDcHr1Gta7VQKM1XXM6g==", + "dependencies": { + "@opentelemetry/core": "1.22.0", + "@opentelemetry/resources": "1.22.0", + "@opentelemetry/sdk-trace-base": "1.22.0", + "@opentelemetry/semantic-conventions": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.49.1.tgz", + "integrity": "sha512-0DLtWtaIppuNNRRllSD4bjU8ZIiLp1cDXvJEbp752/Zf+y3gaLNaoGRGIlX4UHhcsrmtL+P2qxi3Hodi8VuKiQ==", + "dependencies": { + "@opentelemetry/api-logs": "0.49.1", + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "1.7.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-bunyan": { + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.36.0.tgz", + "integrity": "sha512-sHD5BSiqSrgWow7VmugEFzV8vGdsz5m+w1v9tK6YwRzuAD7vbo57chluq+UBzIqStoCH+0yOzRzSALH7hrfffg==", + "dependencies": { + "@opentelemetry/api-logs": "^0.49.1", + "@opentelemetry/instrumentation": "^0.49.1", + "@types/bunyan": "1.8.9" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.49.1.tgz", + "integrity": "sha512-Yib5zrW2s0V8wTeUK/B3ZtpyP4ldgXj9L3Ws/axXrW1dW0/mEFKifK50MxMQK9g5NNJQS9dWH7rvcEGZdWdQDA==", + "dependencies": { + "@opentelemetry/core": "1.22.0", + "@opentelemetry/instrumentation": "0.49.1", + "@opentelemetry/semantic-conventions": "1.22.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongodb": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.40.0.tgz", + "integrity": "sha512-ldlJUW/1UlnGtIWBt7fIUl+7+TGOKxIU+0Js5ukpXfQc07ENYFeck5TdbFjvYtF8GppPErnsZJiFiRdYm6Pv/Q==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/sdk-metrics": "^1.9.1", + "@opentelemetry/semantic-conventions": "^1.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql": { + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.36.0.tgz", + "integrity": "sha512-2mt/032SLkiuddzMrq3YwM0bHksXRep69EzGRnBfF+bCbwYvKLpqmSFqJZ9T3yY/mBWj+tvdvc1+klXGrh2QnQ==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/semantic-conventions": "^1.0.0", + "@types/mysql": "2.15.22" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pg": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.39.1.tgz", + "integrity": "sha512-pX5ujDOyGpPcrZlzaD3LJzmyaSMMMKAP+ffTHJp9vasvZJr+LifCk53TMPVUafcXKV/xX/IIkvADO+67M1Z25g==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/semantic-conventions": "^1.0.0", + "@opentelemetry/sql-common": "^0.40.0", + "@types/pg": "8.6.1", + "@types/pg-pool": "2.0.4" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-redis": { + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.37.0.tgz", + "integrity": "sha512-9G0T74kheu37k+UvyBnAcieB5iowxska3z2rhUcSTL8Cl0y/CvMn7sZ7txkUbXt0rdX6qeEUdMLmbsY2fPUM7Q==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/redis-common": "^0.36.1", + "@opentelemetry/semantic-conventions": "^1.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-redis-4": { + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.37.0.tgz", + "integrity": "sha512-WNO+HALvPPvjbh7UEEIuay0Z0d2mIfSCkBZbPRwZttDGX6LYGc2WnRgJh3TnYqjp7/y9IryWIbajAFIebj1OBA==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/redis-common": "^0.36.1", + "@opentelemetry/semantic-conventions": "^1.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.49.1.tgz", + "integrity": "sha512-z6sHliPqDgJU45kQatAettY9/eVF58qVPaTuejw9YWfSRqid9pXPYeegDCSdyS47KAUgAtm+nC28K3pfF27HWg==", + "dependencies": { + "@opentelemetry/core": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.49.1.tgz", + "integrity": "sha512-DNDNUWmOqtKTFJAyOyHHKotVox0NQ/09ETX8fUOeEtyNVHoGekAVtBbvIA3AtK+JflP7LC0PTjlLfruPM3Wy6w==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.22.0", + "@opentelemetry/otlp-exporter-base": "0.49.1", + "protobufjs": "^7.2.3" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-proto-exporter-base": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-proto-exporter-base/-/otlp-proto-exporter-base-0.49.1.tgz", + "integrity": "sha512-x1qB4EUC7KikUl2iNuxCkV8yRzrSXSyj4itfpIO674H7dhI7Zv37SFaOJTDN+8Z/F50gF2ISFH9CWQ4KCtGm2A==", + "dependencies": { + "@opentelemetry/core": "1.22.0", + "@opentelemetry/otlp-exporter-base": "0.49.1", + "protobufjs": "^7.2.3" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.49.1.tgz", + "integrity": "sha512-Z+koA4wp9L9e3jkFacyXTGphSWTbOKjwwXMpb0CxNb0kjTHGUxhYRN8GnkLFsFo5NbZPjP07hwAqeEG/uCratQ==", + "dependencies": { + "@opentelemetry/api-logs": "0.49.1", + "@opentelemetry/core": "1.22.0", + "@opentelemetry/resources": "1.22.0", + "@opentelemetry/sdk-logs": "0.49.1", + "@opentelemetry/sdk-metrics": "1.22.0", + "@opentelemetry/sdk-trace-base": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.22.0.tgz", + "integrity": "sha512-qBItJm9ygg/jCB5rmivyGz1qmKZPsL/sX715JqPMFgq++Idm0x+N9sLQvWFHFt2+ZINnCSojw7FVBgFW6izcXA==", + "dependencies": { + "@opentelemetry/core": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.22.0.tgz", + "integrity": "sha512-pMLgst3QIwrUfepraH5WG7xfpJ8J3CrPKrtINK0t7kBkuu96rn+HDYQ8kt3+0FXvrZI8YJE77MCQwnJWXIrgpA==", + "dependencies": { + "@opentelemetry/core": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/redis-common": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.36.1.tgz", + "integrity": "sha512-YjfNEr7DK1Ymc5H0bzhmqVvMcCs+PUEUerzrpTFdHfZxj3HpnnjZTIFKx/gxiL/sajQ8dxycjlreoYTVYKBXlw==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resource-detector-azure": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.2.5.tgz", + "integrity": "sha512-O/s4MW9UhLtOebNcdtM5sXm2tZ7O8Ow0avkuFqwwZYTeBcI7ipJs9L8mv8q4bP8K9AQabLLBYw+vOOpN7aH/dA==", + "dependencies": { + "@opentelemetry/resources": "^1.10.1", + "@opentelemetry/semantic-conventions": "^1.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.22.0.tgz", + "integrity": "sha512-+vNeIFPH2hfcNL0AJk/ykJXoUCtR1YaDUZM+p3wZNU4Hq98gzq+7b43xbkXjadD9VhWIUQqEwXyY64q6msPj6A==", + "dependencies": { + "@opentelemetry/core": "1.22.0", + "@opentelemetry/semantic-conventions": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.49.1.tgz", + "integrity": "sha512-gCzYWsJE0h+3cuh3/cK+9UwlVFyHvj3PReIOCDOmdeXOp90ZjKRoDOJBc3mvk1LL6wyl1RWIivR8Rg9OToyesw==", + "dependencies": { + "@opentelemetry/core": "1.22.0", + "@opentelemetry/resources": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.9.0", + "@opentelemetry/api-logs": ">=0.39.1" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.22.0.tgz", + "integrity": "sha512-k6iIx6H3TZ+BVMr2z8M16ri2OxWaljg5h8ihGJxi/KQWcjign6FEaEzuigXt5bK9wVEhqAcWLCfarSftaNWkkg==", + "dependencies": { + "@opentelemetry/core": "1.22.0", + "@opentelemetry/resources": "1.22.0", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/sdk-node": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.49.1.tgz", + "integrity": "sha512-feBIT85ndiSHXsQ2gfGpXC/sNeX4GCHLksC4A9s/bfpUbbgbCSl0RvzZlmEpCHarNrkZMwFRi4H0xFfgvJEjrg==", + "dependencies": { + "@opentelemetry/api-logs": "0.49.1", + "@opentelemetry/core": "1.22.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.49.1", + "@opentelemetry/exporter-trace-otlp-http": "0.49.1", + "@opentelemetry/exporter-trace-otlp-proto": "0.49.1", + "@opentelemetry/exporter-zipkin": "1.22.0", + "@opentelemetry/instrumentation": "0.49.1", + "@opentelemetry/resources": "1.22.0", + "@opentelemetry/sdk-logs": "0.49.1", + "@opentelemetry/sdk-metrics": "1.22.0", + "@opentelemetry/sdk-trace-base": "1.22.0", + "@opentelemetry/sdk-trace-node": "1.22.0", + "@opentelemetry/semantic-conventions": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.22.0.tgz", + "integrity": "sha512-pfTuSIpCKONC6vkTpv6VmACxD+P1woZf4q0K46nSUvXFvOFqjBYKFaAMkKD3M1mlKUUh0Oajwj35qNjMl80m1Q==", + "dependencies": { + "@opentelemetry/core": "1.22.0", + "@opentelemetry/resources": "1.22.0", + "@opentelemetry/semantic-conventions": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.22.0.tgz", + "integrity": "sha512-gTGquNz7ue8uMeiWPwp3CU321OstQ84r7PCDtOaCicjbJxzvO8RZMlEC4geOipTeiF88kss5n6w+//A0MhP1lQ==", + "dependencies": { + "@opentelemetry/context-async-hooks": "1.22.0", + "@opentelemetry/core": "1.22.0", + "@opentelemetry/propagator-b3": "1.22.0", + "@opentelemetry/propagator-jaeger": "1.22.0", + "@opentelemetry/sdk-trace-base": "1.22.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.22.0.tgz", + "integrity": "sha512-CAOgFOKLybd02uj/GhCdEeeBjOS0yeoDeo/CA7ASBSmenpZHAKGB3iDm/rv3BQLcabb/OprDEsSQ1y0P8A7Siw==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sql-common": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.40.0.tgz", + "integrity": "sha512-vSqRJYUPJVjMFQpYkQS3ruexCPSZJ8esne3LazLwtCPaPRvzZ7WG3tX44RouAn7w4wMp8orKguBqtt+ng2UTnw==", + "dependencies": { + "@opentelemetry/core": "^1.1.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.3.1.tgz", + "integrity": "sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", + "dev": true + }, + "node_modules/@types/bunyan": { + "version": "1.8.9", + "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.9.tgz", + "integrity": "sha512-ZqS9JGpBxVOvsawzmVt30sP++gSQMTejCkIAQ3VdadOcRE8izTyW66hufvwLeH+YEGP6Js2AW7Gz+RMyvrEbmw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "dev": true + }, + "node_modules/@types/microsoft__typescript-etw": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@types/microsoft__typescript-etw/-/microsoft__typescript-etw-0.1.3.tgz", + "integrity": "sha512-qbO0IoTPJERhGWOvdw9iQbRjM7OGc6+fHVziSKcw566BDSQpEdkGxXcE8BKSHJOP6mkTRobf4QxqRE/aKhrCxg==", + "dev": true + }, + "node_modules/@types/mocha": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-7.0.2.tgz", + "integrity": "sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w==", + "dev": true + }, + "node_modules/@types/mysql": { + "version": "2.15.22", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.22.tgz", + "integrity": "sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==" + }, + "node_modules/@types/pg": { + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.1.tgz", + "integrity": "sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "node_modules/@types/pg-pool": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.4.tgz", + "integrity": "sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ==", + "dependencies": { + "@types/pg": "*" + } + }, + "node_modules/@types/semver": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz", + "integrity": "sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ==", + "dev": true + }, + "node_modules/@types/shimmer": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.0.5.tgz", + "integrity": "sha512-9Hp0ObzwwO57DpLFF0InUjUm/II8GmKAvzbefxQTihCb7KI6yc9yzf0nLc4mVdby5N4DRCgQM2wCup9KTieeww==" + }, + "node_modules/@types/sinon": { + "version": "10.0.20", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.20.tgz", + "integrity": "sha512-2APKKruFNCAZgx3daAyACGzWuJ028VVCUDk6o2rw/Z4PXT0ogwdV4KUegW0MwVs0Zu59auPXbbuBJHF12Sx1Eg==", + "dev": true, + "dependencies": { + "@types/sinonjs__fake-timers": "*" + } + }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz", + "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "dependencies": { + "default-require-extensions": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "dependencies": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001600", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz", + "integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==" + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/default-require-extensions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", + "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", + "dev": true, + "dependencies": { + "strip-bom": "^4.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "engines": { + "node": ">=8" + } + }, + "node_modules/diagnostic-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/diagnostic-channel/-/diagnostic-channel-1.1.1.tgz", + "integrity": "sha512-r2HV5qFkUICyoaKlBEpLKHjxMXATUf/l+h8UZPGBHGLy4DDiY2sOLcIctax4eRnTw5wH2jTMExLntGPJ8eOJxw==", + "dependencies": { + "semver": "^7.5.3" + } + }, + "node_modules/diagnostic-channel-publishers": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.8.tgz", + "integrity": "sha512-HmSm9hXxSPxA9BaLGY98QU1zsdjeCk113KjAYGPCen1ZP6mhVaTPzHd6UYv5r21DnWANi+f+NyPOHruGT9jpqQ==", + "peerDependencies": { + "diagnostic-channel": "*" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.722", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.722.tgz", + "integrity": "sha512-5nLE0TWFFpZ80Crhtp4pIp8LXCztjYX41yUcV6b+bKR2PqzjskTMOOlBi1VjBHlvHwS+4gar7kNKOrsbsewEZQ==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "dependencies": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.0" + } + }, + "node_modules/eslint-plugin-node/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasha/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-in-the-middle": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.7.1.tgz", + "integrity": "sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==", + "dependencies": { + "acorn": "^8.8.2", + "acorn-import-assertions": "^1.9.0", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "dependencies": { + "append-transform": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-processinfo": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "dev": true, + "dependencies": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" } }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, - "continuation-local-storage": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz", - "integrity": "sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==", - "requires": { - "async-listener": "^0.6.0", - "emitter-listener": "^1.1.1" + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true, - "requires": { - "ms": "2.1.2" + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true }, - "diagnostic-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/diagnostic-channel/-/diagnostic-channel-1.1.0.tgz", - "integrity": "sha512-fwujyMe1gj6rk6dYi9hMZm0c8Mz8NDMVl2LB4iaYh3+LIAThZC8RKFGXWG0IML2OxAit/ZFRgZhMkhQ3d/bobQ==", - "requires": { - "semver": "^5.3.0" + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } + "engines": { + "node": ">=6" } }, - "diagnostic-channel-publishers": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.4.tgz", - "integrity": "sha512-GDRAOrcNTPk4DhYzM2BauMnq7nKdFWmSFjWnEu8dT8Xf/ZXUbpORrqNAhIWsy2tqRjHG7QkmYjMUL4/EGSM2GA==" + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } }, - "emitter-listener": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz", - "integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==", - "requires": { - "shimmer": "^1.2.0" + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" } }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "node_modules/just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", "dev": true }, - "es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", - "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" - }, - "dependencies": { - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - } + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" } }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" } }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, - "requires": { - "to-regex-range": "^5.0.1" + "dependencies": { + "json-buffer": "3.0.1" } }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "requires": { - "locate-path": "^3.0.0" + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "flat": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", - "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "requires": { - "is-buffer": "~2.0.3" + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "formatio": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz", - "integrity": "sha1-XtPM1jZVEJc4NGXZlhmRAOhhYek=", - "dev": true, - "requires": { - "samsam": "~1.1" - } + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" }, - "fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "dev": true }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" }, - "gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "dev": true, - "requires": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - } + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" }, - "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "dependencies": { + "yallist": "^3.0.2" } }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "requires": { - "is-glob": "^4.0.1" + "bin": { + "semver": "bin/semver.js" } }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, - "requires": { - "function-bind": "^1.1.1" + "engines": { + "node": ">= 8" } }, - "has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, - "requires": { - "has-symbols": "^1.0.2" + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" } }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "requires": { - "agent-base": "6", - "debug": "4" + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "node_modules/mocha": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz", + "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==", "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "8.1.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" } }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, - "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" + "dependencies": { + "balanced-match": "^1.0.0" } }, - "is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", "dev": true, - "requires": { - "has-bigints": "^1.0.1" + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" } }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "requires": { - "binary-extensions": "^2.0.0" + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" } }, - "is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "node_modules/module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", "dev": true }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "node_modules/nise": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/nise/-/nise-4.1.0.tgz", + "integrity": "sha512-eQMEmGN/8arp0xsvGoQ+B1qvSkR73B1nWSCh7nOt5neMCtwcQVYQGdzQMhcNscktTsWB54xnlSQFzOAPJD8nXA==", "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" + "dependencies": { + "@sinonjs/commons": "^1.7.0", + "@sinonjs/fake-timers": "^6.0.0", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" } }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "node_modules/nock": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/nock/-/nock-12.0.3.tgz", + "integrity": "sha512-QNb/j8kbFnKCiyqi9C5DD0jH/FubFGj5rt9NQFONXwQm3IPB0CULECg/eS3AU1KgZb/6SwUa4/DTRKhVxkGABw==", "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" + "dependencies": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.13", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">= 10.13" } }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", "dev": true, - "requires": { - "is-extglob": "^2.1.1" + "dependencies": { + "process-on-spawn": "^1.0.0" + }, + "engines": { + "node": ">=8" } }, - "is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "is-number-object": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", - "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "node_modules/nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "dependencies": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "bin": { + "nyc": "bin/nyc.js" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/nyc/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" } }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "node_modules/nyc/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "is-shared-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", - "dev": true - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "node_modules/nyc/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "node_modules/nyc/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, - "requires": { - "has-symbols": "^1.0.2" + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" } }, - "is-typed-array": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", - "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", + "node_modules/nyc/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", - "has-tostringtag": "^1.0.0" + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "is-weakref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", - "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", + "node_modules/nyc/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, - "requires": { - "call-bind": "^1.0.0" + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" } }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "node_modules/nyc/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "node_modules/nyc/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" } }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "node_modules/nyc/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "node_modules/nyc/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" } }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "node_modules/nyc/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } }, - "log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "requires": { - "chalk": "^2.4.2" + "dependencies": { + "wrappy": "1" } }, - "lolex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.3.2.tgz", - "integrity": "sha1-fD2mL/yzDw9agKJWbKJORdigHzE=", - "dev": true + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, - "requires": { - "yallist": "^4.0.0" + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "make-dir": { + "node_modules/p-limit": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "requires": { - "semver": "^6.0.0" - }, "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" - }, - "mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "requires": { - "mime-db": "1.51.0" + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", "dev": true, - "requires": { - "brace-expansion": "^1.1.7" + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } }, - "minipass": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", - "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "node_modules/package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", "dev": true, - "requires": { - "yallist": "^4.0.0" + "dependencies": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + }, + "engines": { + "node": ">=8" } }, - "minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "requires": { - "minimist": "^1.2.5" + "engines": { + "node": ">=8" } }, - "mocha": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz", - "integrity": "sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ==", + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "requires": { - "ansi-colors": "3.2.3", - "browser-stdout": "1.3.1", - "chokidar": "3.3.0", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "3.0.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.5", - "ms": "2.1.1", - "node-environment-flags": "1.0.6", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } + "engines": { + "node": ">=0.10.0" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", - "dev": true + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, - "nock": { - "version": "11.9.1", - "resolved": "https://registry.npmjs.org/nock/-/nock-11.9.1.tgz", - "integrity": "sha512-U5wPctaY4/ar2JJ5Jg4wJxlbBfayxgKbiAeGh+a1kk6Pwnc2ZEuKviLyDSG6t0uXl56q7AALIxoM6FJrBSsVXA==", + "node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", "dev": true, - "requires": { - "debug": "^4.1.0", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.13", - "mkdirp": "^0.5.0", - "propagate": "^2.0.0" + "dependencies": { + "isarray": "0.0.1" } }, - "node-environment-flags": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", - "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, - "requires": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } + "engines": { + "node": ">=8" } }, - "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "requires": { - "whatwg-url": "^5.0.0" + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" } }, - "node-mocks-http": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/node-mocks-http/-/node-mocks-http-1.2.3.tgz", - "integrity": "sha1-9PvqcFCnU183cD5tnMd+l9hAqgI=", - "dev": true + "node_modules/pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" }, - "nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "dev": true, - "requires": { - "abbrev": "1" + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" } }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, - "npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "requires": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "object.getownpropertydescriptors": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz", - "integrity": "sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw==", + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, - "requires": { - "wrappy": "1" + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" } }, - "p-limit": { + "node_modules/pkg-dir/node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, - "requires": { + "dependencies": { "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, - "requires": { - "p-limit": "^2.0.0" + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" } }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } }, - "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + "node_modules/process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "dependencies": { + "fromentries": "^1.2.0" + }, + "engines": { + "node": ">=8" + } }, - "propagate": { + "node_modules/propagate": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", - "dev": true + "dev": true, + "engines": { + "node": ">= 8" + } }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + "node_modules/protobufjs": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.6.tgz", + "integrity": "sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "node_modules/protobufjs/node_modules/@types/node": { + "version": "20.11.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", + "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/protobufjs/node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } }, - "readable-stream": { + "node_modules/readdirp": { "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" } }, - "readdirp": { + "node_modules/regexpp": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true, - "requires": { - "picomatch": "^2.0.4" + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", + "dev": true, + "dependencies": { + "es6-error": "^4.0.1" + }, + "engines": { + "node": ">=4" } }, - "require-directory": { + "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.3.0.tgz", + "integrity": "sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw==", + "dependencies": { + "debug": "^4.1.1", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=8.6.0" + } }, - "require-main-filename": { + "node_modules/require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, - "rimraf": { + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, - "requires": { + "dependencies": { "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" } }, - "safe-buffer": { + "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "samsam": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz", - "integrity": "sha1-vsEf3IOp/aBjQBIQ5AF2wwJNFWc=", - "dev": true + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + "node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, - "requires": { - "lru-cache": "^6.0.0" + "dependencies": { + "randombytes": "^2.1.0" } }, - "set-blocking": { + "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, - "shimmer": { + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shimmer": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/sinon": { + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", + "integrity": "sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==", + "deprecated": "16.1.1", "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "dependencies": { + "@sinonjs/commons": "^1.8.1", + "@sinonjs/fake-timers": "^6.0.1", + "@sinonjs/samsam": "^5.3.1", + "diff": "^4.0.2", + "nise": "^4.0.4", + "supports-color": "^7.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" } }, - "signal-exit": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", - "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", - "dev": true + "node_modules/sinon/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "sinon": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.17.6.tgz", - "integrity": "sha1-pDEW21lXfIKWNWr+4T+vwjMuWOE=", + "node_modules/spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", "dev": true, - "requires": { - "formatio": "1.1.1", - "lolex": "1.3.2", - "samsam": "1.1.2", - "util": ">=0.10.3 <1" + "dependencies": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "engines": { + "node": ">=8" } }, - "sprintf-js": { + "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "stack-chain": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/stack-chain/-/stack-chain-1.3.7.tgz", - "integrity": "sha1-0ZLJ/06moiyUxN1FkXHj8AzqEoU=" + "node_modules/stoppable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", + "engines": { + "node": ">=4", + "npm": ">=6" + } }, - "string-width": { + "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { + "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "engines": { + "node": ">=8" } }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, - "requires": { - "safe-buffer": "~5.2.0" + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "requires": { - "ansi-regex": "^5.0.1" + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "supports-color": { + "node_modules/test-exclude": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, - "requires": { - "has-flag": "^3.0.0" + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" } }, - "tar": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, - "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - } + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" } }, - "to-regex-range": { + "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "requires": { + "dependencies": { "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "tough-cookie": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", - "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", - "requires": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.1.2" - } - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - }, - "tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } }, - "typescript": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz", - "integrity": "sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==", + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, - "unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", - "which-boxed-primitive": "^1.0.2" + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" - }, - "util": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", - "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, - "requires": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "safe-buffer": "^5.1.2", - "which-typed-array": "^1.1.2" + "engines": { + "node": ">=4" } }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", "dev": true, - "requires": { - "isexe": "^2.0.0" + "dependencies": { + "is-typedarray": "^1.0.0" } }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "node_modules/typescript": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", "dev": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" } }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, - "which-typed-array": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", - "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.7" + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - }, "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } + "punycode": "^2.1.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" } }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true + }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } }, - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" } }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "node_modules/yargs-unparser/node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "requires": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } } } diff --git a/package.json b/package.json index f0af528b1..9e33e481f 100644 --- a/package.json +++ b/package.json @@ -3,14 +3,14 @@ "author": "Microsoft Application Insights Team", "license": "MIT", "bugs": "https://github.com/microsoft/ApplicationInsights-node.js/issues", - "version": "2.2.1", + "version": "3.0.0-beta.12", "description": "Microsoft Application Insights module for Node.js", "repository": { "type": "git", "url": "https://github.com/microsoft/ApplicationInsights-node.js" }, - "main": "./out/applicationinsights.js", - "types": "./out/applicationinsights.d.ts", + "main": "./out/src/index.js", + "types": "./out/src/index.d.ts", "keywords": [ "exception monitoring", "request monitoring", @@ -26,51 +26,65 @@ ], "scripts": { "clean": "rm -rf ./out && rm -rf ./node_modules", - "build": "npm run build:deps && npm run build:compile", + "build": "npm run build:compile", "build:deps": "npm update --dev", "build:compile": "tsc --project ./tsconfig.json", + "format": "prettier --write --config .prettierrc.json --ignore-path .prettierignore \"src/**/*.ts\" \"test/**/*.ts\"", "prepare": "npm run build:compile", "prepublishOnly": "npm run build", + "lint": "eslint ./ --fix", "pretest": "npm run build", - "test": "npm run test:ts && npm run test:js", - "test:debug": "mocha ./out/Tests --inspect-brk --recursive --no-exit", - "test:ts": "mocha ./out/Tests --recursive --exit", - "test:js": "mocha ./Tests/js --recursive --exit", - "functionaltest": "npm run build && npm pack && node --use_strict ./Tests/FunctionalTests/RunFunctionalTests.js", - "backcompattest": "npm run build && npm pack && node --use_strict ./Tests/BackCompatibility/RunBackCompatTests.js" + "test": "nyc mocha ./out/test --recursive", + "test:debug": "nyc mocha ./out/test --inspect-brk --recursive", + "test:unit": "nyc mocha ./out/test/unitTests --recursive", + "test:e2e": "nyc mocha ./out/test/endToEnd --recursive", + "functionaltest": "npm run build && npm pack && node --use_strict ./test/functionalTests/runFunctionalTests.js", + "backcompattest": "npm run build && npm pack && node --use_strict ./test/backCompatibility/runBackCompatTests.js" }, "engines": { "node": ">=8.0.0" }, "devDependencies": { - "@types/cls-hooked": "^4.3.3", + "@azure/functions": "^3.2.0", + "@types/long": "^4.0.2", + "@types/microsoft__typescript-etw": "^0.1.0", "@types/mocha": "^7.0.2", "@types/node": "^8.0.0", - "@types/sinon": "2.1.2", - "applicationinsights-native-metrics": "0.0.7", - "mocha": "^7.1.1", - "nock": "^11.9.1", - "node-mocks-http": "1.2.3", - "sinon": "1.17.6", - "typescript": "4.1.2" + "@types/semver": "7.3.9", + "@types/sinon": "^10.0.12", + "@typescript-eslint/eslint-plugin": "^5.37.0", + "@typescript-eslint/parser": "^5.37.0", + "eslint": "^8.0.0", + "eslint-plugin-node": "^11.1.0", + "mocha": "^10.0.0", + "nock": "^12.0.3", + "nyc": "^15.0.0", + "prettier": "^2.5.1", + "sinon": "^9.0.2", + "typescript": "~4.8.0" }, "dependencies": { - "@azure/core-http": "^2.2.3", - "@opentelemetry/api": "^1.0.4", - "@opentelemetry/core": "^1.0.1", - "@opentelemetry/sdk-trace-base": "^1.0.1", - "@opentelemetry/semantic-conventions": "^1.0.1", - "cls-hooked": "^4.2.2", - "continuation-local-storage": "^3.2.1", - "diagnostic-channel": "1.1.0", - "diagnostic-channel-publishers": "1.0.4" - }, - "peerDependencies": { - "applicationinsights-native-metrics": "*" - }, - "peerDependenciesMeta": { - "applicationinsights-native-metrics": { - "optional": true - } + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.0.0", + "@azure/core-rest-pipeline": "^1.9.2", + "@azure/identity": "^3.1.3", + "@azure/monitor-opentelemetry": "^1.3.0", + "@azure/monitor-opentelemetry-exporter": "^1.0.0-beta.21", + "@azure/opentelemetry-instrumentation-azure-sdk": "^1.0.0-beta.5", + "@opentelemetry/api": "^1.8.0", + "@opentelemetry/api-logs": "^0.49.1", + "@opentelemetry/core": "^1.22.0", + "@opentelemetry/exporter-logs-otlp-http": "^0.49.1", + "@opentelemetry/exporter-metrics-otlp-http": "^0.49.1", + "@opentelemetry/exporter-trace-otlp-http": "^0.49.1", + "@opentelemetry/otlp-exporter-base": "^0.49.1", + "@opentelemetry/resources": "^1.22.0", + "@opentelemetry/sdk-logs": "^0.49.1", + "@opentelemetry/sdk-metrics": "^1.22.0", + "@opentelemetry/sdk-trace-base": "^1.22.0", + "@opentelemetry/sdk-trace-node": "^1.22.0", + "@opentelemetry/semantic-conventions": "^1.22.0", + "diagnostic-channel": "1.1.1", + "diagnostic-channel-publishers": "1.0.8" } } diff --git a/policheck-exclusions.xml b/policheck-exclusions.xml new file mode 100644 index 000000000..bd46cd62b --- /dev/null +++ b/policheck-exclusions.xml @@ -0,0 +1,11 @@ + + + + GENERATED + + + + + + + \ No newline at end of file diff --git a/src/agent/agentLoader.ts b/src/agent/agentLoader.ts new file mode 100644 index 000000000..9b7ded98f --- /dev/null +++ b/src/agent/agentLoader.ts @@ -0,0 +1,287 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { ManagedIdentityCredential } from "@azure/identity"; +import { Util } from "../shared/util"; +import { ConsoleWriter } from "./diagnostics/writers/consoleWriter"; +import { DiagnosticLogger } from "./diagnostics/diagnosticLogger"; +import { StatusLogger } from "./diagnostics/statusLogger"; +import { AZURE_MONITOR_AUTO_ATTACH, DiagnosticMessageId, IDiagnosticLog, IDiagnosticLogger, NODE_JS_RUNTIME_MAJOR_VERSION } from "./types"; +import { AzureMonitorOpenTelemetryOptions } from "../types"; +import { useAzureMonitor } from "../main"; + + +const forceStart = process.env.APPLICATIONINSIGHTS_FORCE_START === "true"; +// Azure Connection String +const ENV_connectionString = "APPLICATIONINSIGHTS_CONNECTION_STRING"; +const ENV_AZURE_PREFIX = "APPSETTING_"; // Azure adds this prefix to all environment variables +const ENV_IKEY = "APPINSIGHTS_INSTRUMENTATIONKEY"; // This key is provided in the readme +const LEGACY_ENV_IKEY = "APPINSIGHTS_INSTRUMENTATION_KEY"; +const LINUX_USER_APPLICATION_INSIGHTS_PATH = "/node_modules/applicationinsights/out/applicationinsights.js"; + + +export class AgentLoader { + protected _canLoad: boolean; + protected _options: AzureMonitorOpenTelemetryOptions; + protected _instrumentationKey: string; + protected _diagnosticLogger: IDiagnosticLogger; + protected _statusLogger: StatusLogger; + protected _isWindows: boolean; + protected _isLinux: boolean; + private _aadCredential: any; // Types not available as library should not be loaded in older versions of Node.js runtime + + constructor() { + // Open Telemetry and AAD packages unsusable in older versions of Node.js runtime + // https://github.com/open-telemetry/opentelemetry-js?tab=readme-ov-file#supported-runtimes + if (NODE_JS_RUNTIME_MAJOR_VERSION < 14) { + this._canLoad = false; + } + else { + this._canLoad = true; + this._aadCredential = this._getAuthenticationCredential(); + // Default options + this._options = { + azureMonitorExporterOptions: { + disableOfflineStorage: false, + }, + enableAutoCollectExceptions: true, + enableAutoCollectPerformance: true, + samplingRatio: 1, // Sample all telemetry by default + instrumentationOptions: { + azureSdk: { + enabled: true + }, + http: { + enabled: true + }, + mongoDb: { + enabled: true + }, + mySql: { + enabled: true + }, + postgreSql: { + enabled: true + }, + redis4: { + enabled: true + }, + redis: { + enabled: true + }, + } + }; + + const connectionString = process.env[ENV_connectionString]; + if (connectionString) { + this._instrumentationKey = this._getInstrumentationKey(connectionString); + } + else { + const instrumentationKey = + process.env[ENV_IKEY] || + process.env[ENV_AZURE_PREFIX + ENV_IKEY] || + process.env[LEGACY_ENV_IKEY] || + process.env[ENV_AZURE_PREFIX + LEGACY_ENV_IKEY]; + this._instrumentationKey = instrumentationKey || "unknown"; + + } + + + // Default diagnostic using console + this._diagnosticLogger = new DiagnosticLogger(this._instrumentationKey, new ConsoleWriter()); + this._statusLogger = new StatusLogger(this._instrumentationKey, new ConsoleWriter()); + this._isWindows = process.platform === 'win32'; + this._isLinux = process.platform === 'linux'; + } + } + + private _getInstrumentationKey(connectionString: string) { + if (connectionString) { + const kvPairs = connectionString.split(";"); + for (let i = 0; i < kvPairs.length; i++) { + const kvParts = kvPairs[i].split("="); + if (kvParts.length === 2 && kvParts[0].toLowerCase() === "instrumentationkey") { + return kvParts[1]; + } + } + } + return ""; + } + + // Exposed so ETW logger could be provider in IPA code + public setLogger(logger: IDiagnosticLogger) { + this._diagnosticLogger = logger; + } + + public initialize() { + if (!this._canLoad) { + const msg = `Cannot load Azure Monitor Application Insights Distro because of unsupported Node.js runtime, currently running in version ${NODE_JS_RUNTIME_MAJOR_VERSION}`; + console.log(msg); + return; + } + if (this._validate()) { + try { + // Set environment variable to auto attach so the distro is aware of the attach state + process.env[AZURE_MONITOR_AUTO_ATTACH] = "true"; + // Initialize Distro + this._options.azureMonitorExporterOptions.credential = this._aadCredential; + useAzureMonitor(this._options); + // Agent successfully initialized + const diagnosticLog: IDiagnosticLog = { + message: "Azure Monitor Application Insights Distro was started succesfully.", + messageId: DiagnosticMessageId.attachSuccessful + }; + this._diagnosticLogger.logMessage(diagnosticLog); + this._statusLogger.logStatus({ + AgentInitializedSuccessfully: true + }); + } + catch (error) { + const msg = `Error initializaing Azure Monitor Application Insights Distro.${Util.getInstance().dumpObj(error)}`; + const diagnosticLog: IDiagnosticLog = { + message: msg, + messageId: DiagnosticMessageId.unknownError + }; + this._diagnosticLogger.logMessage(diagnosticLog); + this._statusLogger.logStatus({ + AgentInitializedSuccessfully: false, + Reason: msg + }) + } + } + } + + private _validate(): boolean { + try { + if (!forceStart && this._sdkAlreadyExists()) { + this._statusLogger.logStatus({ + AgentInitializedSuccessfully: false, + SDKPresent: true, + Reason: "Azure Monitor Application Insights Distro already available." + }) + return false; + } + if (this._instrumentationKey === "unknown") { + const diagnosticLog: IDiagnosticLog = { + message: "Azure Monitor Application Insights Distro wanted to be started, but no Connection String was provided", + messageId: DiagnosticMessageId.missingIkey + }; + this._diagnosticLogger.logMessage(diagnosticLog); + this._statusLogger.logStatus({ + AgentInitializedSuccessfully: false, + Reason: diagnosticLog.message + }); + return false; + } + return true; + } + catch (err: any) { + const msg = `Failed to validate Azure Monitor Application Insights Distro initialization.${Util.getInstance().dumpObj(err)}`; + console.log(msg); + if (this._diagnosticLogger) { + const diagnosticLog: IDiagnosticLog = { + message: msg, + messageId: DiagnosticMessageId.unknownError + }; + this._diagnosticLogger.logMessage(diagnosticLog); + } + if (this._statusLogger) { + this._statusLogger.logStatus({ + AgentInitializedSuccessfully: false, + Reason: msg + }); + } + } + } + + private _getAuthenticationCredential(): any { + let credential = undefined; + // Try to add AAD Token Credential + try { + const authenticationString = process.env["APPLICATIONINSIGHTS_AUTHENTICATION_STRING"]; + if (authenticationString) { + const kvPairs = authenticationString.split(";"); + const result = kvPairs.reduce((fields: any, kv: string) => { + const kvParts = kv.split("="); + if (kvParts.length === 2) { // only save fields with valid formats + const key = kvParts[0].toLowerCase(); + const value = kvParts[1]; + fields[key] = value as string; + } + return fields; + }, {}); + if (result["authorization"] && result["authorization"] === "AAD") { + const clientId = result["clientid"]; + if (clientId) { + console.log('AppInsightsAgent: ClientId found, trying to authenticate using Managed Identity.'); + credential = new ManagedIdentityCredential(clientId); + } + else { + console.log('AppInsightsAgent: Trying to authenticate using System assigned Managed Identity.'); + credential = new ManagedIdentityCredential(); // System assigned identity + } + } + } + } + catch (authError: any) { + const msg = `Failed to get authentication credential and enable AAD.${Util.getInstance().dumpObj(authError)}`; + console.log(msg); + if (this._diagnosticLogger) { + const diagnosticLog: IDiagnosticLog = { + message: msg, + messageId: DiagnosticMessageId.aadEnabled + }; + this._diagnosticLogger.logMessage(diagnosticLog); + } + } + return credential; + } + + private _sdkAlreadyExists(): boolean { + try { + // appInstance should either resolve to user SDK or crash. If it resolves to attach SDK, user probably modified their NODE_PATH + let shimInstance: string; + let distroInstance: string; + try { + // Node 8.9+ Windows + if (this._isWindows) { + shimInstance = (require.resolve as any)("applicationinsights", { paths: [process.cwd()] }); + distroInstance = (require.resolve as any)("@azure/monitor-opentelemetry", { paths: [process.cwd()] }); + } + // Node 8.9+ Linux + else if (this._isLinux) { + shimInstance = `${process.cwd()}${(require.resolve as any)("applicationinsights", { paths: [process.cwd()] })}`; + distroInstance = `${process.cwd()}${(require.resolve as any)("@azure/monitor-opentelemetry", { paths: [process.cwd()] })}`; + } + } catch (e) { + // Node <8.9 + shimInstance = require.resolve(`${process.cwd()}/node_modules/applicationinsights`); + distroInstance = require.resolve(`${process.cwd()}/node_modules/@azure/monitor-opentelemetry`); + } + /** + * If loaded instance is in Azure machine home path do not attach the SDK, this means customer already instrumented their app. + * Linux App Service doesn't append the full cwd to the require.resolve, so we need to check for the relative path we expect + * if application insights is being imported in the user app code. + */ + if ( + shimInstance.indexOf("home") > -1 || distroInstance.indexOf("home") > -1 || + (shimInstance === LINUX_USER_APPLICATION_INSIGHTS_PATH && this._isLinux) + ) { + const diagnosticLog: IDiagnosticLog = { + message: `Azure Monitor Distro or Application Insights already exists. Module is already installed in this application; not re-attaching. Location: ${shimInstance}`, + messageId: DiagnosticMessageId.sdkExists + }; + this._diagnosticLogger.logMessage(diagnosticLog); + return true; + } + else { + // ApplicationInsights or Azure Monitor Distro could be loaded outside of customer application, attach in this case + return false; + } + + } catch (e) { + // crashed while trying to resolve "applicationinsights", so SDK does not exist. Attach appinsights + return false; + } + } +} diff --git a/src/agent/aksLoader.ts b/src/agent/aksLoader.ts new file mode 100644 index 000000000..248cab925 --- /dev/null +++ b/src/agent/aksLoader.ts @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as os from 'os'; +import * as path from 'path'; +import { DiagnosticLogger } from './diagnostics/diagnosticLogger'; +import { FileWriter } from "./diagnostics/writers/fileWriter"; +import { StatusLogger } from "./diagnostics/statusLogger"; +import { AgentLoader } from "./agentLoader"; + +export class AKSLoader extends AgentLoader { + + constructor() { + super(); + if (this._canLoad) { + // AKS specific configuration + this._options.otlpMetricExporterConfig = { + // Add OTLP if env variable is present + enabled: process.env["OTEL_EXPORTER_OTLP_METRICS_ENDPOINT"] ? true : false + }; + + let statusLogDir = '/var/log/applicationinsights/'; + if (this._isWindows) { + if (process.env.HOME) { + statusLogDir = path.join(process.env.HOME, "LogFiles", "ApplicationInsights", "status"); + } + else { + statusLogDir = path.join(os.tmpdir(), "Microsoft", "ApplicationInsights", "StatusMonitor", "LogFiles", "ApplicationInsights", "status"); + } + } + this._statusLogger = new StatusLogger(this._instrumentationKey, new FileWriter(statusLogDir, 'status_nodejs.json', { + append: false, + deleteOnExit: false, + renamePolicy: 'overwrite', + sizeLimit: 1024 * 1024, + })); + + this._diagnosticLogger = new DiagnosticLogger( + this._instrumentationKey, + new FileWriter( + statusLogDir, + 'applicationinsights-extension.log', + { + append: true, + deleteOnExit: false, + renamePolicy: 'overwrite', + sizeLimit: 1024 * 1024, // 1 MB + } + ) + ); + } + } +} diff --git a/src/agent/appServicesLoader.ts b/src/agent/appServicesLoader.ts new file mode 100644 index 000000000..9f9886dad --- /dev/null +++ b/src/agent/appServicesLoader.ts @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as os from 'os'; +import * as path from 'path'; +import { Attributes } from '@opentelemetry/api'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; +import { Resource } from '@opentelemetry/resources'; +import { DiagnosticLogger } from './diagnostics/diagnosticLogger'; +import { FileWriter } from "./diagnostics/writers/fileWriter"; +import { StatusLogger } from "./diagnostics/statusLogger"; +import { AgentLoader } from "./agentLoader"; + +export class AppServicesLoader extends AgentLoader { + + constructor() { + super(); + if (this._canLoad) { + // Azure App Services specific configuration + const resourceAttributes: Attributes = {}; + if (process.env.WEBSITE_SITE_NAME) { + resourceAttributes[SemanticResourceAttributes.SERVICE_NAME] = + process.env.WEBSITE_SITE_NAME; + } + if (process.env.WEBSITE_INSTANCE_ID) { + resourceAttributes[SemanticResourceAttributes.SERVICE_INSTANCE_ID] = + process.env.WEBSITE_INSTANCE_ID; + } + const resource = new Resource(resourceAttributes); + this._options.resource = resource; + + let statusLogDir = '/var/log/applicationinsights/'; + if (this._isWindows) { + if (process.env.HOME) { + statusLogDir = path.join(process.env.HOME, "LogFiles", "ApplicationInsights", "status"); + } + else { + statusLogDir = path.join(os.tmpdir(), "Microsoft", "ApplicationInsights", "StatusMonitor", "LogFiles", "ApplicationInsights", "status"); + } + } + this._statusLogger = new StatusLogger(this._instrumentationKey, new FileWriter(statusLogDir, 'status_nodejs.json', { + append: false, + deleteOnExit: false, + renamePolicy: 'overwrite', + sizeLimit: 1024 * 1024, + })); + + this._diagnosticLogger = new DiagnosticLogger( + this._instrumentationKey, + new FileWriter( + statusLogDir, + 'applicationinsights-extension.log', + { + append: true, + deleteOnExit: false, + renamePolicy: 'overwrite', + sizeLimit: 1024 * 1024, // 1 MB + } + ) + ); + } + } +} diff --git a/src/agent/azureFunctionsLoader.ts b/src/agent/azureFunctionsLoader.ts new file mode 100644 index 000000000..d8dad0998 --- /dev/null +++ b/src/agent/azureFunctionsLoader.ts @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { Resource } from "@opentelemetry/resources"; +import { AgentLoader } from "./agentLoader"; +import { DiagnosticLogger } from "./diagnostics/diagnosticLogger"; +import { StatusLogger } from "./diagnostics/statusLogger"; +import { AzureFunctionsWriter } from "./diagnostics/writers/azureFunctionsWriter"; +import { Attributes } from "@opentelemetry/api"; +import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions"; + +export class AzureFunctionsLoader extends AgentLoader { + + constructor() { + super(); + if (this._canLoad) { + // Azure Fn specific configuration + this._options.enableAutoCollectPerformance = false; + process.env["APPLICATION_INSIGHTS_NO_STANDARD_METRICS"] = "disable"; + const resourceAttributes: Attributes = {}; + if (process.env.WEBSITE_SITE_NAME) { + resourceAttributes[SemanticResourceAttributes.SERVICE_NAME] = + process.env.WEBSITE_SITE_NAME; + } + if (process.env.WEBSITE_INSTANCE_ID) { + resourceAttributes[SemanticResourceAttributes.SERVICE_INSTANCE_ID] = + process.env.WEBSITE_INSTANCE_ID; + } + const resource = new Resource(resourceAttributes); + this._options.resource = resource; + + const writer = new AzureFunctionsWriter(this._instrumentationKey); + this._diagnosticLogger = new DiagnosticLogger(this._instrumentationKey, writer); + this._statusLogger = new StatusLogger(this._instrumentationKey, writer); + } + } +} diff --git a/src/agent/diagnostics/baseDiagnosticLogger.ts b/src/agent/diagnostics/baseDiagnosticLogger.ts new file mode 100644 index 000000000..06c6bcaa3 --- /dev/null +++ b/src/agent/diagnostics/baseDiagnosticLogger.ts @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { IAgentLogger, IDiagnosticLog, IDiagnosticLogger, LOGGER_LANGUAGE, LOGGER_NAME } from "../types"; +import { AZURE_MONITOR_OPENTELEMETRY_VERSION } from "../../types"; + + +export class BaseDiagnosticLogger implements IDiagnosticLogger { + protected _extensionVersion: string; + protected _instrumentationKey: string; + protected _loggerName: string; + protected _language: string; + protected _sdkVersion: string; + protected _siteName: string; + protected _subscriptionId: string; + protected _agentLogger: IAgentLogger; + + constructor(instrumentationKey: string, agentLogger: IAgentLogger = console) { + this._agentLogger = agentLogger; + this._instrumentationKey = instrumentationKey; + this._loggerName = LOGGER_NAME; + this._language = LOGGER_LANGUAGE; + this._siteName = process.env.WEBSITE_SITE_NAME; + this._extensionVersion = process.env.ApplicationInsightsAgent_EXTENSION_VERSION; + this._sdkVersion = AZURE_MONITOR_OPENTELEMETRY_VERSION; + this._subscriptionId = process.env.WEBSITE_OWNER_NAME ? process.env.WEBSITE_OWNER_NAME.split("+")[0] : null; + } + + public logMessage(diagnosticLog: IDiagnosticLog) { + // No OP + } +} diff --git a/src/agent/diagnostics/diagnosticLogger.ts b/src/agent/diagnostics/diagnosticLogger.ts new file mode 100644 index 000000000..4516e2eeb --- /dev/null +++ b/src/agent/diagnostics/diagnosticLogger.ts @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { IAgentLogger, IDiagnosticLog } from "../types"; +import { BaseDiagnosticLogger } from "./baseDiagnosticLogger"; + + +export class DiagnosticLogger extends BaseDiagnosticLogger { + constructor(instrumentationKey: string, agentLogger: IAgentLogger = console) { + super(instrumentationKey, agentLogger); + } + + public logMessage(diagnosticLog: IDiagnosticLog) { + this._addCommonProperties(diagnosticLog); + this._agentLogger.log(diagnosticLog); + } + + private _addCommonProperties(diagnosticLog: IDiagnosticLog) { + diagnosticLog.time = new Date().toUTCString(); + diagnosticLog.extensionVersion = this._extensionVersion; + diagnosticLog.instrumentationKey = this._instrumentationKey; + diagnosticLog.language = this._language; + diagnosticLog.loggerName = this._loggerName; + diagnosticLog.siteName = this._siteName; + diagnosticLog.sdkVersion = this._sdkVersion; + diagnosticLog.subscriptionId = this._subscriptionId; + } +} diff --git a/src/agent/diagnostics/statusLogger.ts b/src/agent/diagnostics/statusLogger.ts new file mode 100644 index 000000000..034547f32 --- /dev/null +++ b/src/agent/diagnostics/statusLogger.ts @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as os from "os"; +import { FileWriter } from "./writers/fileWriter"; +import { IAgentLogger, IStatusContract, LOGGER_LANGUAGE } from "../types"; +import { AZURE_MONITOR_OPENTELEMETRY_VERSION } from "../../types"; + + +export class StatusLogger { + private _instrumentationKey: string; + private _language: string; + private _sdkVersion: string; + private _processId: string; + private _machineName: string; + private _agentLogger: IAgentLogger; + + constructor(instrumentationKey: string, agentLogger: IAgentLogger = console) { + this._agentLogger = agentLogger; + this._instrumentationKey = instrumentationKey; + this._language = LOGGER_LANGUAGE; + this._sdkVersion = AZURE_MONITOR_OPENTELEMETRY_VERSION; + this._machineName = os.hostname(); + this._processId = String(process.pid); + } + + public logStatus(status: IStatusContract, cb?: (err: Error) => void) { + this._addCommonProperties(status); + if (typeof cb === "function" && this._agentLogger instanceof FileWriter) { + this._agentLogger.callback = cb; + } + this._agentLogger.log(status); + } + + private _addCommonProperties(status: IStatusContract) { + status.AppType = this._language; + status.MachineName = this._machineName; + status.PID = this._processId; + status.SdkVersion = this._sdkVersion; + status.Ikey = this._instrumentationKey; + } +} \ No newline at end of file diff --git a/src/agent/diagnostics/writers/azureFunctionsWriter.ts b/src/agent/diagnostics/writers/azureFunctionsWriter.ts new file mode 100644 index 000000000..694536742 --- /dev/null +++ b/src/agent/diagnostics/writers/azureFunctionsWriter.ts @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { AZURE_MONITOR_OPENTELEMETRY_VERSION } from "../../../types"; +import { AZURE_APP_NAME, IAgentLogger } from "../../types"; + +const AZURE_FUNCTIONS_DIAGNOSTIC_PREFIX = "LanguageWorkerConsoleLogMS_APPLICATION_INSIGHTS_LOGS"; + +export class AzureFunctionsWriter implements IAgentLogger { + private _appName: string; + private _instrumentationKey: string; + private _agentVersion: string; + + constructor(instrumentationKey: string) { + this._instrumentationKey = instrumentationKey; + this._appName = AZURE_APP_NAME; + this._agentVersion = AZURE_MONITOR_OPENTELEMETRY_VERSION; + } + + public log(log: any) { + console.info(this._getAzureFnLog(log)); + } + + public error(log: any) { + console.error(this._getAzureFnLog(log)); + } + + private _getAzureFnLog(log: any): string { + const output = `${AZURE_FUNCTIONS_DIAGNOSTIC_PREFIX} ${log.time},${log.level},${log.logger},\"${log.message}\",${this._appName},${this._instrumentationKey},${this._agentVersion},node.js`; + return output; + } +} diff --git a/src/agent/diagnostics/writers/consoleWriter.ts b/src/agent/diagnostics/writers/consoleWriter.ts new file mode 100644 index 000000000..8332bd677 --- /dev/null +++ b/src/agent/diagnostics/writers/consoleWriter.ts @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { Util } from "../../../shared/util"; +import { IAgentLogger } from "../../types"; + +export class ConsoleWriter implements IAgentLogger { + log(message?: any, ...optional: any[]) { + console.log(Util.getInstance().stringify(message)); + } + + error(message?: any, ...optional: any[]) { + console.error(Util.getInstance().stringify(message)); + } +} diff --git a/Bootstrap/Helpers/FileHelpers.ts b/src/agent/diagnostics/writers/fileHelpers.ts similarity index 91% rename from Bootstrap/Helpers/FileHelpers.ts rename to src/agent/diagnostics/writers/fileHelpers.ts index 961e87059..ded3f8086 100644 --- a/Bootstrap/Helpers/FileHelpers.ts +++ b/src/agent/diagnostics/writers/fileHelpers.ts @@ -1,10 +1,11 @@ -"use strict"; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. import * as path from "path"; import * as fs from "fs"; import * as os from "os"; -export const homedir = os.homedir ? os.homedir() : (process.env[(process.platform == "win32") ? "USERPROFILE" : "HOME"]); +export const homedir = os.homedir ? os.homedir() : (process.env[(process.platform === "win32") ? "USERPROFILE" : "HOME"]); /** * Zero dependencies: recursive mkdir @@ -51,8 +52,6 @@ export function makeStatusDirs(filepath: string): boolean { } } - - export function renameCurrentFile(filepath: string, filename: string, callback?: (err: Error | null, destfullpath?: string) => void): void { const fullpath = path.join(filepath, filename); const basename = path.basename(filename, path.extname(filename)); @@ -62,9 +61,9 @@ export function renameCurrentFile(filepath: string, filename: string, callback?: } const createDate = new Date(stats.birthtime); - const destfilename = basename + "-" + - createDate.toISOString().replace(/[T:\.]/g, "_").replace("Z", "") + - path.extname(filename) + ".old"; + const destfilename = `${basename }-${ + createDate.toISOString().replace(/[T:\.]/g, "_").replace("Z", "") + }${path.extname(filename) }.old`; const destfullpath = path.join(filepath, destfilename); fs.rename(fullpath, destfullpath, (renameErr) => { if (typeof callback === "function") { diff --git a/Bootstrap/FileWriter.ts b/src/agent/diagnostics/writers/fileWriter.ts similarity index 81% rename from Bootstrap/FileWriter.ts rename to src/agent/diagnostics/writers/fileWriter.ts index 8d97eff42..2bed57318 100644 --- a/Bootstrap/FileWriter.ts +++ b/src/agent/diagnostics/writers/fileWriter.ts @@ -1,10 +1,11 @@ -"use strict"; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. import * as path from "path"; import * as fs from "fs"; -import * as os from "os"; -import * as DataModel from "./DataModel"; -import * as FileHelpers from "./Helpers/FileHelpers"; +import { makeStatusDirs, renameCurrentFile } from "./fileHelpers"; +import { IAgentLogger } from "../../types"; +import { Util } from "../../../shared/util"; export interface FileWriterOptions { append: boolean; // Overwrite or append on file write (false) @@ -14,10 +15,11 @@ export interface FileWriterOptions { chmod: number; // Linux only } -export const homedir = FileHelpers.homedir; -export class FileWriter implements DataModel.AgentLogger { - public callback = (_err: Error) => { }; // no-op +export class FileWriter implements IAgentLogger { + public callback = (_err: Error) => { + // no-op + }; private _ready = false; private _options: FileWriterOptions; private static _fullpathsToDelete: string[] = []; @@ -25,7 +27,7 @@ export class FileWriter implements DataModel.AgentLogger { private static DEFAULT_OPTIONS: FileWriterOptions = { append: false, deleteOnExit: true, - sizeLimit: 10*1024, + sizeLimit: 10 * 1024, renamePolicy: "stop", chmod: 0o644 // rw/r/r } @@ -38,7 +40,7 @@ export class FileWriter implements DataModel.AgentLogger { // leave at "keep at single file only", "write up to certain size limit", "clear old file on process startup" constructor(private _filepath: string, private _filename: string, options?: Partial) { this._options = { ...FileWriter.DEFAULT_OPTIONS, ...options }; - this._ready = FileWriter.isNodeVersionCompatible() && FileHelpers.makeStatusDirs(this._filepath); + this._ready = FileWriter.isNodeVersionCompatible() && makeStatusDirs(this._filepath); if (this._options.deleteOnExit) { FileWriter._addCloseHandler(); FileWriter._fullpathsToDelete.push(path.join(this._filepath, this._filename)); @@ -47,21 +49,21 @@ export class FileWriter implements DataModel.AgentLogger { public log(message: any) { if (this._ready) { - let data = typeof message === "object" - ? JSON.stringify(message) + const data = typeof message === "object" + ? Util.getInstance().stringify(message) : message.toString(); // Check if existing file needs to be renamed this._shouldRenameFile((err, shouldRename) => { - if (err) return; + if (err) { return; } if (shouldRename) { if (this._options.renamePolicy === "rolling") { - FileHelpers.renameCurrentFile(this._filepath, this._filename, (renameErr, renamedFullpath) => { - if (renameErr) return; + renameCurrentFile(this._filepath, this._filename, (renameErr, renamedFullpath) => { + if (renameErr) { return; } FileWriter._fullpathsToDelete.push(renamedFullpath); this._options.append - ? this._appendFile(data + "\n") + ? this._appendFile(`${data}\n`) : this._writeFile(data); }); } else if (this._options.renamePolicy === "overwrite") { @@ -73,18 +75,14 @@ export class FileWriter implements DataModel.AgentLogger { } } else { this._options.append - ? this._appendFile(data + "\n") - : this._writeFile(data); + ? this._appendFile(`${data}\n`) + : this._writeFile(data); } }); } } - public error(message: any) { - this.log(message); - } - private _appendFile(message: string) { const fullpath = path.join(this._filepath, this._filename); fs.appendFile(fullpath, message, (err) => { diff --git a/src/agent/types.ts b/src/agent/types.ts new file mode 100644 index 000000000..cc7029442 --- /dev/null +++ b/src/agent/types.ts @@ -0,0 +1,47 @@ +export const LOGGER_NAME = "applicationinsights.extension.diagnostics"; +export const LOGGER_LANGUAGE = "nodejs"; +export const NODE_JS_RUNTIME_MAJOR_VERSION = parseInt(process.versions.node.split('.')[0], 10); +export const AZURE_APP_NAME = process.env.WEBSITE_SITE_NAME || 'unknown'; +export const AZURE_MONITOR_AUTO_ATTACH = "AZURE_MONITOR_AUTO_ATTACH"; + +export interface IAgentLogger { + log(message: any, ...optional: any[]): void; +} + +export interface IDiagnosticLogger { + logMessage(log?: IDiagnosticLog): void; +} + +export interface IDiagnosticLog { + time?: string; + message: string; + extensionVersion?: string; + language?: string; + loggerName?: string; + subscriptionId?: string; + siteName?: string; + instrumentationKey?: string; + sdkVersion?: string; + messageId?: string; +} + +export interface IStatusContract { + AgentInitializedSuccessfully?: boolean; + Reason?: string; + SDKPresent?: boolean; + AppType?: string; + MachineName?: string; + PID?: string; + SdkVersion?: string; + Ikey?: string; +} + +export const DiagnosticMessageId = { + "attachSuccessful": "3000", + "sdkExists": "3001", + "missingIkey": "3002", + "setupAlreadyCalled": "3003", + "prefixFailed": "3004", + "aadEnabled": "3005", + "unknownError": "3006", +} diff --git a/src/declarations/contracts/index.ts b/src/declarations/contracts/index.ts new file mode 100644 index 000000000..2ff45f1a9 --- /dev/null +++ b/src/declarations/contracts/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +export * from "./telemetryTypes"; diff --git a/src/declarations/contracts/telemetryTypes/availabilityTelemetry.ts b/src/declarations/contracts/telemetryTypes/availabilityTelemetry.ts new file mode 100644 index 000000000..f0b8f7083 --- /dev/null +++ b/src/declarations/contracts/telemetryTypes/availabilityTelemetry.ts @@ -0,0 +1,41 @@ +import { Telemetry } from "./telemetry"; + +/** + * Telemetry type used for availability web test results. + */ +export interface AvailabilityTelemetry extends Telemetry { + /** + * Identifier of a test run. Use it to correlate steps of test run and telemetry generated by the service. + */ + id: string; + + /** + * Name of the test that these availability results represent. + */ + name: string; + + /** + * Request duration in ms + */ + duration: number; + + /** + * Success flag. + */ + success: boolean; + + /** + * Name of the location where the test was run from. + */ + runLocation: string; + + /** + * Diagnostic message for the result. + */ + message: string; + + /** + * Metrics associated with this event, displayed in Metrics Explorer on the portal. + */ + measurements?: { [key: string]: number }; +} diff --git a/src/declarations/contracts/telemetryTypes/dependencyTelemetry.ts b/src/declarations/contracts/telemetryTypes/dependencyTelemetry.ts new file mode 100644 index 000000000..cdcb92169 --- /dev/null +++ b/src/declarations/contracts/telemetryTypes/dependencyTelemetry.ts @@ -0,0 +1,25 @@ +import { Telemetry } from "./telemetry"; + +/** + * Telemetry about the call to remote component + */ +export interface DependencyTelemetry extends Telemetry { + /** Identifier of a dependency call instance. Used for correlation with the request telemetry item corresponding to this dependency call. */ + id?: string; + /** Name of the command initiated with this dependency call. Low cardinality value. Examples are stored procedure name and URL path template. */ + name: string; + /** Result code of a dependency call. Examples are SQL error code and HTTP status code. */ + resultCode?: string | number; + /** Command initiated by this dependency call. Examples are SQL statement and HTTP URL with all query parameters. */ + data?: string; + /** Dependency type name. Very low cardinality value for logical grouping of dependencies and interpretation of other fields like commandName and resultCode. Examples are SQL, Azure table, and HTTP. */ + dependencyTypeName?: string; + /** Target site of a dependency call. Examples are server name, host address. */ + target?: string; + /** Remote call duration in ms. */ + duration: number; + /** Indication of successful or unsuccessful call. */ + success?: boolean; + /** Collection of custom measurements. */ + measurements?: { [propertyName: string]: number }; +} diff --git a/Declarations/Contracts/TelemetryTypes/EventTelemetry.ts b/src/declarations/contracts/telemetryTypes/eventTelemetry.ts similarity index 71% rename from Declarations/Contracts/TelemetryTypes/EventTelemetry.ts rename to src/declarations/contracts/telemetryTypes/eventTelemetry.ts index c896934f8..8d14087ec 100644 --- a/Declarations/Contracts/TelemetryTypes/EventTelemetry.ts +++ b/src/declarations/contracts/telemetryTypes/eventTelemetry.ts @@ -1,18 +1,17 @@ -import { Telemetry } from "./Telemetry"; +import { Telemetry } from "./telemetry"; /** * Telemetry about the custom event of interest, such application workflow event, business logic event (purchase) and anything that * you would like to track and aggregate by count. Event can contain measurements such as purchase amount associated with purchase event */ -export interface EventTelemetry extends Telemetry -{ +export interface EventTelemetry extends Telemetry { /** * Name of the event */ - name: string; - + name: string; + /** * Metrics associated with this event, displayed in Metrics Explorer on the portal. */ - measurements?: { [key: string]: number; }; -} \ No newline at end of file + measurements?: { [key: string]: number }; +} diff --git a/Declarations/Contracts/TelemetryTypes/ExceptionTelemetry.ts b/src/declarations/contracts/telemetryTypes/exceptionTelemetry.ts similarity index 53% rename from Declarations/Contracts/TelemetryTypes/ExceptionTelemetry.ts rename to src/declarations/contracts/telemetryTypes/exceptionTelemetry.ts index c721f831a..310c7effe 100644 --- a/Declarations/Contracts/TelemetryTypes/ExceptionTelemetry.ts +++ b/src/declarations/contracts/telemetryTypes/exceptionTelemetry.ts @@ -1,22 +1,21 @@ -import { Telemetry } from "./Telemetry"; -import Contracts = require("../"); +import { Telemetry } from "./telemetry"; +import { SeverityLevel } from "../../generated"; /** * Telemetry about the exception thrown by the application */ -export interface ExceptionTelemetry extends Telemetry -{ +export interface ExceptionTelemetry extends Telemetry { /** * Exception thrown */ - exception: Error; + exception: Error; /** * Metrics associated with this exception, displayed in Metrics Explorer on the portal. Defaults to empty */ - measurements?: { [key: string]: number; }; + measurements?: { [key: string]: number }; /** * Exception severity level */ - severity?: Contracts.SeverityLevel; -} \ No newline at end of file + severity?: SeverityLevel; +} diff --git a/src/declarations/contracts/telemetryTypes/index.ts b/src/declarations/contracts/telemetryTypes/index.ts new file mode 100644 index 000000000..92ed744b9 --- /dev/null +++ b/src/declarations/contracts/telemetryTypes/index.ts @@ -0,0 +1,12 @@ +export * from "./dependencyTelemetry"; +export * from "./eventTelemetry"; +export * from "./exceptionTelemetry"; +export * from "./metricTelemetry"; +export * from "./requestTelemetry"; +export * from "./traceTelemetry"; +export * from "./telemetry"; +export * from "./nodeHttpDependencyTelemetry"; +export * from "./nodeHttpRequestTelemetry"; +export * from "./availabilityTelemetry"; +export * from "./pageViewTelemetry"; +export * from "./telemetryType"; diff --git a/Declarations/Contracts/TelemetryTypes/MetricTelemetry.ts b/src/declarations/contracts/telemetryTypes/metricTelemetry.ts similarity index 69% rename from Declarations/Contracts/TelemetryTypes/MetricTelemetry.ts rename to src/declarations/contracts/telemetryTypes/metricTelemetry.ts index 1b342b4ab..3f42a4f0c 100644 --- a/Declarations/Contracts/TelemetryTypes/MetricTelemetry.ts +++ b/src/declarations/contracts/telemetryTypes/metricTelemetry.ts @@ -1,10 +1,16 @@ -import { Telemetry } from "./Telemetry"; +import { DataPointType } from "../../generated"; +import { Telemetry } from "./telemetry"; /** * Telemetry encapsulating a custom metric, i.e. aggregated numeric values describing value, count, frequency and distribution of * of a particular indicator. */ export interface MetricTelemetry extends Telemetry { + /** List of metrics. Only one metric in the list is currently supported by Application Insights storage. If multiple data points were sent only the first one will be used. */ + metrics?: MetricPointTelemetry[]; +} + +export interface MetricPointTelemetry { /** * A string that identifies the metric. */ @@ -23,7 +29,7 @@ export interface MetricTelemetry extends Telemetry { /** * Type of metric being sent, e.g. Pre-agg metrics have kind=Aggregation */ - kind?: string; + kind?: DataPointType; /** * The number of samples used to get this value @@ -44,4 +50,4 @@ export interface MetricTelemetry extends Telemetry { * The standard deviation of the set */ stdDev?: number; -} \ No newline at end of file +} diff --git a/Declarations/Contracts/TelemetryTypes/NodeHttpDependencyTelemetry.ts b/src/declarations/contracts/telemetryTypes/nodeHttpDependencyTelemetry.ts similarity index 50% rename from Declarations/Contracts/TelemetryTypes/NodeHttpDependencyTelemetry.ts rename to src/declarations/contracts/telemetryTypes/nodeHttpDependencyTelemetry.ts index a097dc262..e0bc577b7 100644 --- a/Declarations/Contracts/TelemetryTypes/NodeHttpDependencyTelemetry.ts +++ b/src/declarations/contracts/telemetryTypes/nodeHttpDependencyTelemetry.ts @@ -1,19 +1,18 @@ -import { Telemetry } from "./Telemetry"; -import http = require("http"); -import https = require("https"); +import { Telemetry } from "./telemetry"; +import * as http from "http"; +import * as https from "https"; /** * Object encapsulating information about the outgoing request */ -export interface NodeHttpDependencyTelemetry extends Telemetry -{ +export interface NodeHttpDependencyTelemetry extends Telemetry { /** * Request options that will be used to instrument outgoing request */ - options: string | URL | http.RequestOptions | https.RequestOptions; + options: string | URL | http.RequestOptions | https.RequestOptions; /** * Outgoing HTTP request object */ request: http.ClientRequest; -} \ No newline at end of file +} diff --git a/Declarations/Contracts/TelemetryTypes/NodeHttpRequestTelemetry.ts b/src/declarations/contracts/telemetryTypes/nodeHttpRequestTelemetry.ts similarity index 73% rename from Declarations/Contracts/TelemetryTypes/NodeHttpRequestTelemetry.ts rename to src/declarations/contracts/telemetryTypes/nodeHttpRequestTelemetry.ts index 3ba0fd12e..125ac277c 100644 --- a/Declarations/Contracts/TelemetryTypes/NodeHttpRequestTelemetry.ts +++ b/src/declarations/contracts/telemetryTypes/nodeHttpRequestTelemetry.ts @@ -1,11 +1,10 @@ -import { Telemetry } from "./Telemetry"; -import http = require("http"); +import { Telemetry } from "./telemetry"; +import * as http from "http"; /** * Object encapsulating information about the incoming HTTP request */ -export interface NodeHttpRequestTelemetry extends Telemetry -{ +export interface NodeHttpRequestTelemetry extends Telemetry { /** * HTTP request object */ @@ -15,7 +14,7 @@ export interface NodeHttpRequestTelemetry extends Telemetry * HTTP response object */ response: http.ServerResponse; - + /** * HTTP request duration. Used only for synchronous tracks. */ @@ -24,5 +23,5 @@ export interface NodeHttpRequestTelemetry extends Telemetry /** * Error that occurred while processing the request. Used only for synchronous tracks. */ - error?: any -} \ No newline at end of file + error?: any; +} diff --git a/src/declarations/contracts/telemetryTypes/pageViewTelemetry.ts b/src/declarations/contracts/telemetryTypes/pageViewTelemetry.ts new file mode 100644 index 000000000..66b2d31a2 --- /dev/null +++ b/src/declarations/contracts/telemetryTypes/pageViewTelemetry.ts @@ -0,0 +1,19 @@ +import { Telemetry } from "./telemetry"; + +/** + * Telemetry type used for availability web test results. + */ +export interface PageViewTelemetry extends Telemetry { + /** Identifier of a page view instance. Used for correlation between page view and other telemetry items. */ + id: string; + /** Event name. Keep it low cardinality to allow proper grouping and useful metrics. */ + name: string; + /** Request URL with all query string parameters */ + url?: string; + /** Request duration in milliseconds. */ + duration?: number; + /** Fully qualified page URI or URL of the referring page; if unknown, leave blank */ + referredUri?: string; + /** Collection of custom measurements. */ + measurements?: { [propertyName: string]: number }; +} diff --git a/src/declarations/contracts/telemetryTypes/requestTelemetry.ts b/src/declarations/contracts/telemetryTypes/requestTelemetry.ts new file mode 100644 index 000000000..03125c830 --- /dev/null +++ b/src/declarations/contracts/telemetryTypes/requestTelemetry.ts @@ -0,0 +1,21 @@ +import { Telemetry } from "./telemetry"; + +/** + * Telemetry about the incoming request processed by the application + */ +export interface RequestTelemetry extends Telemetry { + /** Identifier of a request call instance. Used for correlation between request and other telemetry items. */ + id?: string; + /** Name of the request. Represents code path taken to process request. Low cardinality value to allow better grouping of requests. For HTTP requests it represents the HTTP method and URL path template like 'GET /values/{id}'. */ + name?: string; + /** Request duration in ms. */ + duration: number; + /** Indication of successful or unsuccessful call. */ + success: boolean; + /** Result of a request execution. HTTP status code for HTTP requests. */ + resultCode: string; + /** Request URL with all query string parameters. */ + url?: string; + /** Collection of custom measurements. */ + measurements?: { [propertyName: string]: number }; +} diff --git a/src/declarations/contracts/telemetryTypes/telemetry.ts b/src/declarations/contracts/telemetryTypes/telemetry.ts new file mode 100644 index 000000000..f56d43795 --- /dev/null +++ b/src/declarations/contracts/telemetryTypes/telemetry.ts @@ -0,0 +1,13 @@ +/** + * Base telemetry interface encapsulating coming properties + */ +export interface Telemetry { + /** + * Telemetry time stamp. When it is not specified, current timestamp will be used. + */ + time?: Date; + /** + * Additional data used to filter events and metrics in the portal. Defaults to empty. + */ + properties?: { [key: string]: any }; +} diff --git a/src/declarations/contracts/telemetryTypes/telemetryType.ts b/src/declarations/contracts/telemetryTypes/telemetryType.ts new file mode 100644 index 000000000..8bc09ef30 --- /dev/null +++ b/src/declarations/contracts/telemetryTypes/telemetryType.ts @@ -0,0 +1,13 @@ +/** + * Telemetry types supported by this SDK + */ +export enum TelemetryType { + Event, + Exception, + Trace, + Metric, + Request, + Dependency, + Availability, + PageView, +} diff --git a/src/declarations/contracts/telemetryTypes/traceTelemetry.ts b/src/declarations/contracts/telemetryTypes/traceTelemetry.ts new file mode 100644 index 000000000..484a0ba01 --- /dev/null +++ b/src/declarations/contracts/telemetryTypes/traceTelemetry.ts @@ -0,0 +1,15 @@ +import { Telemetry } from "./telemetry"; +import { SeverityLevel } from "../../generated"; + +/** + * Trace telemetry reports technical, usually detailed information about the environment, + * usage of resources, performance, capacity etc + */ +export interface TraceTelemetry extends Telemetry { + /** Trace message */ + message: string; + /** Trace severity level. */ + severity?: SeverityLevel; + /** Collection of custom measurements. */ + measurements?: { [propertyName: string]: number }; +} diff --git a/src/declarations/generated/applicationInsightsClient.ts b/src/declarations/generated/applicationInsightsClient.ts new file mode 100644 index 000000000..a323d1ce2 --- /dev/null +++ b/src/declarations/generated/applicationInsightsClient.ts @@ -0,0 +1,77 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is regenerated. + */ + +import * as coreClient from "@azure/core-client"; +import * as Parameters from "./models/parameters"; +import * as Mappers from "./models/mappers"; +import { ApplicationInsightsClientContext } from "./applicationInsightsClientContext"; +import { + ApplicationInsightsClientOptionalParams, + TelemetryItem, + TrackOptionalParams, + TrackOperationResponse, +} from "./models"; + +export class ApplicationInsightsClient extends ApplicationInsightsClientContext { + /** + * Initializes a new instance of the ApplicationInsightsClient class. + * @param options The parameter options + */ + constructor(options?: ApplicationInsightsClientOptionalParams) { + super(options); + } + + /** + * This operation sends a sequence of telemetry events that will be monitored by Azure Monitor. + * @param body The list of telemetry events to track. + * @param options The options parameters. + */ + track(body: TelemetryItem[], options?: TrackOptionalParams): Promise { + return this.sendOperationRequest({ body, options }, trackOperationSpec); + } +} +// Operation Specifications +const serializer = coreClient.createSerializer(Mappers, /* isXml */ false); + +const trackOperationSpec: coreClient.OperationSpec = { + path: "/track", + httpMethod: "POST", + responses: { + 200: { + bodyMapper: Mappers.TrackResponse, + }, + 206: { + bodyMapper: Mappers.TrackResponse, + }, + 400: { + bodyMapper: Mappers.TrackResponse, + isError: true, + }, + 402: { + bodyMapper: Mappers.TrackResponse, + isError: true, + }, + 429: { + bodyMapper: Mappers.TrackResponse, + isError: true, + }, + 500: { + bodyMapper: Mappers.TrackResponse, + isError: true, + }, + 503: { + bodyMapper: Mappers.TrackResponse, + isError: true, + }, + }, + requestBody: Parameters.body, + urlParameters: [Parameters.host], + headerParameters: [Parameters.contentType, Parameters.accept], + mediaType: "json", + serializer, +}; diff --git a/src/declarations/generated/applicationInsightsClientContext.ts b/src/declarations/generated/applicationInsightsClientContext.ts new file mode 100644 index 000000000..2b8116969 --- /dev/null +++ b/src/declarations/generated/applicationInsightsClientContext.ts @@ -0,0 +1,47 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is regenerated. + */ + +import * as coreClient from "@azure/core-client"; +import { ApplicationInsightsClientOptionalParams } from "./models"; + +export class ApplicationInsightsClientContext extends coreClient.ServiceClient { + host: string; + + /** + * Initializes a new instance of the ApplicationInsightsClientContext class. + * @param options The parameter options + */ + constructor(options?: ApplicationInsightsClientOptionalParams) { + // Initializing default values for options + if (!options) { + options = {}; + } + const defaults: ApplicationInsightsClientOptionalParams = { + requestContentType: "application/json; charset=utf-8", + }; + + const packageDetails = `azsdk-js-monitor-opentelemetry-exporter/1.0.0-beta.7`; + const userAgentPrefix = + options.userAgentOptions && options.userAgentOptions.userAgentPrefix + ? `${options.userAgentOptions.userAgentPrefix} ${packageDetails}` + : `${packageDetails}`; + + const optionsWithDefaults = { + ...defaults, + ...options, + userAgentOptions: { + userAgentPrefix, + }, + baseUri: options.endpoint || "{Host}/v2.1", + }; + super(optionsWithDefaults); + + // Assigning values to Constant parameters + this.host = options.host || "https://dc.services.visualstudio.com"; + } +} diff --git a/src/declarations/generated/index.ts b/src/declarations/generated/index.ts new file mode 100644 index 000000000..43f28f411 --- /dev/null +++ b/src/declarations/generated/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is regenerated. + */ + +export * from "./models"; +export { ApplicationInsightsClient } from "./applicationInsightsClient"; +export { ApplicationInsightsClientContext } from "./applicationInsightsClientContext"; diff --git a/src/declarations/generated/models/index.ts b/src/declarations/generated/models/index.ts new file mode 100644 index 000000000..48b1467ff --- /dev/null +++ b/src/declarations/generated/models/index.ts @@ -0,0 +1,390 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is regenerated. + */ + +import * as coreClient from "@azure/core-client"; + +/** System variables for a telemetry item. */ +export interface TelemetryItem { + /** Envelope version. For internal use only. By assigning this the default, it will not be serialized within the payload unless changed to a value other than #1. */ + version?: number; + /** Type name of telemetry data item. */ + name: string; + /** Event date time when telemetry item was created. This is the wall clock time on the client when the event was generated. There is no guarantee that the client's time is accurate. This field must be formatted in UTC ISO 8601 format, with a trailing 'Z' character, as described publicly on https://en.wikipedia.org/wiki/ISO_8601#UTC. Note: the number of decimal seconds digits provided are variable (and unspecified). Consumers should handle this, i.e. managed code consumers should not use format 'O' for parsing as it specifies a fixed length. Example: 2009-06-15T13:45:30.0000000Z. */ + time: Date; + /** Sampling rate used in application. This telemetry item represents 100 / sampleRate actual telemetry items. */ + sampleRate?: number; + /** Sequence field used to track absolute order of uploaded events. */ + sequence?: string; + /** The instrumentation key of the Application Insights resource. */ + instrumentationKey?: string; + /** Key/value collection of context properties. See ContextTagKeys for information on available properties. */ + tags?: { [propertyName: string]: string }; + /** Telemetry data item. */ + data?: MonitorBase; +} + +/** Data struct to contain only C section with custom fields. */ +export interface MonitorBase { + /** Name of item (B section) if any. If telemetry data is derived straight from this, this should be null. */ + baseType?: string; + /** The data payload for the telemetry request */ + baseData?: MonitorDomain; +} + +/** The abstract common base of all domains. */ +export interface MonitorDomain { + /** Describes unknown properties. The value of an unknown property can be of "any" type. */ + [property: string]: any; + /** Schema version */ + version?: number; +} + +/** Response containing the status of each telemetry item. */ +export interface TrackResponse { + /** The number of items received. */ + itemsReceived?: number; + /** The number of items accepted. */ + itemsAccepted?: number; + /** An array of error detail objects. */ + errors?: TelemetryErrorDetails[]; +} + +/** The error details */ +export interface TelemetryErrorDetails { + /** The index in the original payload of the item. */ + index?: number; + /** The item specific [HTTP Response status code](#Response Status Codes). */ + statusCode?: number; + /** The error message. */ + message?: string; +} + +/** Metric data single measurement. */ +export interface MetricDataPoint { + /** Namespace of the metric. */ + namespace?: string; + /** Name of the metric. */ + name: string; + /** Metric type. Single measurement or the aggregated value. */ + dataPointType?: DataPointType; + /** Single value for measurement. Sum of individual measurements for the aggregation. */ + value: number; + /** Metric weight of the aggregated metric. Should not be set for a measurement. */ + count?: number; + /** Minimum value of the aggregated metric. Should not be set for a measurement. */ + min?: number; + /** Maximum value of the aggregated metric. Should not be set for a measurement. */ + max?: number; + /** Standard deviation of the aggregated metric. Should not be set for a measurement. */ + stdDev?: number; +} + +/** Exception details of the exception in a chain. */ +export interface TelemetryExceptionDetails { + /** In case exception is nested (outer exception contains inner one), the id and outerId properties are used to represent the nesting. */ + id?: number; + /** The value of outerId is a reference to an element in ExceptionDetails that represents the outer exception */ + outerId?: number; + /** Exception type name. */ + typeName?: string; + /** Exception message. */ + message: string; + /** Indicates if full exception stack is provided in the exception. The stack may be trimmed, such as in the case of a StackOverflow exception. */ + hasFullStack?: boolean; + /** Text describing the stack. Either stack or parsedStack should have a value. */ + stack?: string; + /** List of stack frames. Either stack or parsedStack should have a value. */ + parsedStack?: StackFrame[]; +} + +/** Stack frame information. */ +export interface StackFrame { + level: number; + /** Method name. */ + method: string; + /** Name of the assembly (dll, jar, etc.) containing this function. */ + assembly?: string; + /** File name or URL of the method implementation. */ + fileName?: string; + /** Line number of the code implementation. */ + line?: number; +} + +/** Instances of AvailabilityData represent the result of executing an availability test. */ +export type AvailabilityData = MonitorDomain & { + /** Identifier of a test run. Use it to correlate steps of test run and telemetry generated by the service. */ + id: string; + /** Name of the test that these availability results represent. */ + name: string; + /** Duration in format: DD.HH:MM:SS.MMMMMM. Must be less than 1000 days. */ + duration: string; + /** Success flag. */ + success: boolean; + /** Name of the location where the test was run from. */ + runLocation?: string; + /** Diagnostic message for the result. */ + message?: string; + /** Collection of custom properties. */ + properties?: { [propertyName: string]: string }; + /** Collection of custom measurements. */ + measurements?: { [propertyName: string]: number }; +}; + +/** Instances of Event represent structured event records that can be grouped and searched by their properties. Event data item also creates a metric of event count by name. */ +export type TelemetryEventData = MonitorDomain & { + /** Event name. Keep it low cardinality to allow proper grouping and useful metrics. */ + name: string; + /** Collection of custom properties. */ + properties?: { [propertyName: string]: string }; + /** Collection of custom measurements. */ + measurements?: { [propertyName: string]: number }; +}; + +/** An instance of Exception represents a handled or unhandled exception that occurred during execution of the monitored application. */ +export type TelemetryExceptionData = MonitorDomain & { + /** Exception chain - list of inner exceptions. */ + exceptions: TelemetryExceptionDetails[]; + /** Severity level. Mostly used to indicate exception severity level when it is reported by logging library. */ + severityLevel?: SeverityLevel; + /** Identifier of where the exception was thrown in code. Used for exceptions grouping. Typically a combination of exception type and a function from the call stack. */ + problemId?: string; + /** Collection of custom properties. */ + properties?: { [propertyName: string]: string }; + /** Collection of custom measurements. */ + measurements?: { [propertyName: string]: number }; +}; + +/** Instances of Message represent printf-like trace statements that are text-searched. Log4Net, NLog and other text-based log file entries are translated into instances of this type. The message does not have measurements. */ +export type MessageData = MonitorDomain & { + /** Trace message */ + message: string; + /** Trace severity level. */ + severityLevel?: SeverityLevel; + /** Collection of custom properties. */ + properties?: { [propertyName: string]: string }; + /** Collection of custom measurements. */ + measurements?: { [propertyName: string]: number }; +}; + +/** An instance of the Metric item is a list of measurements (single data points) and/or aggregations. */ +export type MetricsData = MonitorDomain & { + /** List of metrics. Only one metric in the list is currently supported by Application Insights storage. If multiple data points were sent only the first one will be used. */ + metrics: MetricDataPoint[]; + /** Collection of custom properties. */ + properties?: { [propertyName: string]: string }; +}; + +/** An instance of PageView represents a generic action on a page like a button click. It is also the base type for PageView. */ +export type PageViewData = MonitorDomain & { + /** Identifier of a page view instance. Used for correlation between page view and other telemetry items. */ + id: string; + /** Event name. Keep it low cardinality to allow proper grouping and useful metrics. */ + name: string; + /** Request URL with all query string parameters */ + url?: string; + /** Request duration in format: DD.HH:MM:SS.MMMMMM. For a page view (PageViewData), this is the duration. For a page view with performance information (PageViewPerfData), this is the page load time. Must be less than 1000 days. */ + duration?: string; + /** Fully qualified page URI or URL of the referring page; if unknown, leave blank */ + referredUri?: string; + /** Collection of custom properties. */ + properties?: { [propertyName: string]: string }; + /** Collection of custom measurements. */ + measurements?: { [propertyName: string]: number }; +}; + +/** An instance of PageViewPerf represents: a page view with no performance data, a page view with performance data, or just the performance data of an earlier page request. */ +export type PageViewPerfData = MonitorDomain & { + /** Identifier of a page view instance. Used for correlation between page view and other telemetry items. */ + id: string; + /** Event name. Keep it low cardinality to allow proper grouping and useful metrics. */ + name: string; + /** Request URL with all query string parameters */ + url?: string; + /** Request duration in format: DD.HH:MM:SS.MMMMMM. For a page view (PageViewData), this is the duration. For a page view with performance information (PageViewPerfData), this is the page load time. Must be less than 1000 days. */ + duration?: string; + /** Performance total in TimeSpan 'G' (general long) format: d:hh:mm:ss.fffffff */ + perfTotal?: string; + /** Network connection time in TimeSpan 'G' (general long) format: d:hh:mm:ss.fffffff */ + networkConnect?: string; + /** Sent request time in TimeSpan 'G' (general long) format: d:hh:mm:ss.fffffff */ + sentRequest?: string; + /** Received response time in TimeSpan 'G' (general long) format: d:hh:mm:ss.fffffff */ + receivedResponse?: string; + /** DOM processing time in TimeSpan 'G' (general long) format: d:hh:mm:ss.fffffff */ + domProcessing?: string; + /** Collection of custom properties. */ + properties?: { [propertyName: string]: string }; + /** Collection of custom measurements. */ + measurements?: { [propertyName: string]: number }; +}; + +/** An instance of Remote Dependency represents an interaction of the monitored component with a remote component/service like SQL or an HTTP endpoint. */ +export type RemoteDependencyData = MonitorDomain & { + /** Identifier of a dependency call instance. Used for correlation with the request telemetry item corresponding to this dependency call. */ + id?: string; + /** Name of the command initiated with this dependency call. Low cardinality value. Examples are stored procedure name and URL path template. */ + name: string; + /** Result code of a dependency call. Examples are SQL error code and HTTP status code. */ + resultCode?: string; + /** Command initiated by this dependency call. Examples are SQL statement and HTTP URL with all query parameters. */ + data?: string; + /** Dependency type name. Very low cardinality value for logical grouping of dependencies and interpretation of other fields like commandName and resultCode. Examples are SQL, Azure table, and HTTP. */ + type?: string; + /** Target site of a dependency call. Examples are server name, host address. */ + target?: string; + /** Request duration in format: DD.HH:MM:SS.MMMMMM. Must be less than 1000 days. */ + duration: string; + /** Indication of successful or unsuccessful call. */ + success?: boolean; + /** Collection of custom properties. */ + properties?: { [propertyName: string]: string }; + /** Collection of custom measurements. */ + measurements?: { [propertyName: string]: number }; +}; + +/** An instance of Request represents completion of an external request to the application to do work and contains a summary of that request execution and the results. */ +export type RequestData = MonitorDomain & { + /** Identifier of a request call instance. Used for correlation between request and other telemetry items. */ + id: string; + /** Name of the request. Represents code path taken to process request. Low cardinality value to allow better grouping of requests. For HTTP requests it represents the HTTP method and URL path template like 'GET /values/{id}'. */ + name?: string; + /** Request duration in format: DD.HH:MM:SS.MMMMMM. Must be less than 1000 days. */ + duration: string; + /** Indication of successful or unsuccessful call. */ + success: boolean; + /** Result of a request execution. HTTP status code for HTTP requests. */ + responseCode: string; + /** Source of the request. Examples are the instrumentation key of the caller or the ip address of the caller. */ + source?: string; + /** Request URL with all query string parameters. */ + url?: string; + /** Collection of custom properties. */ + properties?: { [propertyName: string]: string }; + /** Collection of custom measurements. */ + measurements?: { [propertyName: string]: number }; +}; + +/** Known values of {@link DataPointType} that the service accepts. */ +export enum KnownDataPointType { + Measurement = "Measurement", + Aggregation = "Aggregation", +} + +/** + * Defines values for DataPointType. \ + * {@link KnownDataPointType} can be used interchangeably with DataPointType, + * this enum contains the known values that the service supports. + * ### Known values supported by the service + * **Measurement** \ + * **Aggregation** + */ +export type DataPointType = string; + +/** Known values of {@link SeverityLevel} that the service accepts. */ +export enum KnownSeverityLevel { + Verbose = "Verbose", + Information = "Information", + Warning = "Warning", + Error = "Error", + Critical = "Critical", +} + +/** + * Defines values for SeverityLevel. \ + * {@link KnownSeverityLevel} can be used interchangeably with SeverityLevel, + * this enum contains the known values that the service supports. + * ### Known values supported by the service + * **Verbose** \ + * **Information** \ + * **Warning** \ + * **Error** \ + * **Critical** + */ +export type SeverityLevel = string; + +/** Known values of {@link ContextTagKeys} that the service accepts. */ +export enum KnownContextTagKeys { + AiApplicationVer = "ai.application.ver", + AiDeviceId = "ai.device.id", + AiDeviceLocale = "ai.device.locale", + AiDeviceModel = "ai.device.model", + AiDeviceOemName = "ai.device.oemName", + AiDeviceOsVersion = "ai.device.osVersion", + AiDeviceType = "ai.device.type", + AiLocationIp = "ai.location.ip", + AiLocationCountry = "ai.location.country", + AiLocationProvince = "ai.location.province", + AiLocationCity = "ai.location.city", + AiOperationId = "ai.operation.id", + AiOperationName = "ai.operation.name", + AiOperationParentId = "ai.operation.parentId", + AiOperationSyntheticSource = "ai.operation.syntheticSource", + AiOperationCorrelationVector = "ai.operation.correlationVector", + AiSessionId = "ai.session.id", + AiSessionIsFirst = "ai.session.isFirst", + AiUserAccountId = "ai.user.accountId", + AiUserId = "ai.user.id", + AiUserAuthUserId = "ai.user.authUserId", + AiCloudRole = "ai.cloud.role", + AiCloudRoleVer = "ai.cloud.roleVer", + AiCloudRoleInstance = "ai.cloud.roleInstance", + AiCloudLocation = "ai.cloud.location", + AiInternalSdkVersion = "ai.internal.sdkVersion", + AiInternalAgentVersion = "ai.internal.agentVersion", + AiInternalNodeName = "ai.internal.nodeName", +} + +/** + * Defines values for ContextTagKeys. \ + * {@link KnownContextTagKeys} can be used interchangeably with ContextTagKeys, + * this enum contains the known values that the service supports. + * ### Known values supported by the service + * **ai.application.ver** \ + * **ai.device.id** \ + * **ai.device.locale** \ + * **ai.device.model** \ + * **ai.device.oemName** \ + * **ai.device.osVersion** \ + * **ai.device.type** \ + * **ai.location.ip** \ + * **ai.location.country** \ + * **ai.location.province** \ + * **ai.location.city** \ + * **ai.operation.id** \ + * **ai.operation.name** \ + * **ai.operation.parentId** \ + * **ai.operation.syntheticSource** \ + * **ai.operation.correlationVector** \ + * **ai.session.id** \ + * **ai.session.isFirst** \ + * **ai.user.accountId** \ + * **ai.user.id** \ + * **ai.user.authUserId** \ + * **ai.cloud.role** \ + * **ai.cloud.roleVer** \ + * **ai.cloud.roleInstance** \ + * **ai.cloud.location** \ + * **ai.internal.sdkVersion** \ + * **ai.internal.agentVersion** \ + * **ai.internal.nodeName** + */ +export type ContextTagKeys = string; + +/** Optional parameters. */ +export type TrackOptionalParams = coreClient.OperationOptions + +/** Contains response data for the track operation. */ +export type TrackOperationResponse = TrackResponse; + +/** Optional parameters. */ +export interface ApplicationInsightsClientOptionalParams extends coreClient.ServiceClientOptions { + /** Breeze endpoint: https://dc.services.visualstudio.com */ + host?: string; + /** Overrides client endpoint. */ + endpoint?: string; +} diff --git a/src/declarations/generated/models/mappers.ts b/src/declarations/generated/models/mappers.ts new file mode 100644 index 000000000..0ad250452 --- /dev/null +++ b/src/declarations/generated/models/mappers.ts @@ -0,0 +1,940 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is regenerated. + */ + +import * as coreClient from "@azure/core-client"; + +export const TelemetryItem: coreClient.CompositeMapper = { + type: { + name: "Composite", + className: "TelemetryItem", + modelProperties: { + version: { + defaultValue: 1, + serializedName: "ver", + type: { + name: "Number", + }, + }, + name: { + serializedName: "name", + required: true, + type: { + name: "String", + }, + }, + time: { + serializedName: "time", + required: true, + type: { + name: "DateTime", + }, + }, + sampleRate: { + defaultValue: 100, + serializedName: "sampleRate", + type: { + name: "Number", + }, + }, + sequence: { + constraints: { + MaxLength: 64, + }, + serializedName: "seq", + type: { + name: "String", + }, + }, + instrumentationKey: { + serializedName: "iKey", + type: { + name: "String", + }, + }, + tags: { + serializedName: "tags", + type: { + name: "Dictionary", + value: { type: { name: "String" } }, + }, + }, + data: { + serializedName: "data", + type: { + name: "Composite", + className: "MonitorBase", + }, + }, + }, + }, +}; + +export const MonitorBase: coreClient.CompositeMapper = { + type: { + name: "Composite", + className: "MonitorBase", + modelProperties: { + baseType: { + serializedName: "baseType", + type: { + name: "String", + }, + }, + baseData: { + serializedName: "baseData", + type: { + name: "Composite", + className: "MonitorDomain", + }, + }, + }, + }, +}; + +export const MonitorDomain: coreClient.CompositeMapper = { + type: { + name: "Composite", + className: "MonitorDomain", + additionalProperties: { type: { name: "Object" } }, + modelProperties: { + version: { + defaultValue: 2, + serializedName: "ver", + required: true, + type: { + name: "Number", + }, + }, + }, + }, +}; + +export const TrackResponse: coreClient.CompositeMapper = { + type: { + name: "Composite", + className: "TrackResponse", + modelProperties: { + itemsReceived: { + serializedName: "itemsReceived", + type: { + name: "Number", + }, + }, + itemsAccepted: { + serializedName: "itemsAccepted", + type: { + name: "Number", + }, + }, + errors: { + serializedName: "errors", + type: { + name: "Sequence", + element: { + type: { + name: "Composite", + className: "TelemetryErrorDetails", + }, + }, + }, + }, + }, + }, +}; + +export const TelemetryErrorDetails: coreClient.CompositeMapper = { + type: { + name: "Composite", + className: "TelemetryErrorDetails", + modelProperties: { + index: { + serializedName: "index", + type: { + name: "Number", + }, + }, + statusCode: { + serializedName: "statusCode", + type: { + name: "Number", + }, + }, + message: { + serializedName: "message", + type: { + name: "String", + }, + }, + }, + }, +}; + +export const MetricDataPoint: coreClient.CompositeMapper = { + type: { + name: "Composite", + className: "MetricDataPoint", + modelProperties: { + namespace: { + constraints: { + MaxLength: 256, + }, + serializedName: "ns", + type: { + name: "String", + }, + }, + name: { + constraints: { + MaxLength: 1024, + }, + serializedName: "name", + required: true, + type: { + name: "String", + }, + }, + dataPointType: { + serializedName: "kind", + type: { + name: "String", + }, + }, + value: { + serializedName: "value", + required: true, + type: { + name: "Number", + }, + }, + count: { + serializedName: "count", + nullable: true, + type: { + name: "Number", + }, + }, + min: { + serializedName: "min", + nullable: true, + type: { + name: "Number", + }, + }, + max: { + serializedName: "max", + nullable: true, + type: { + name: "Number", + }, + }, + stdDev: { + serializedName: "stdDev", + nullable: true, + type: { + name: "Number", + }, + }, + }, + }, +}; + +export const TelemetryExceptionDetails: coreClient.CompositeMapper = { + type: { + name: "Composite", + className: "TelemetryExceptionDetails", + modelProperties: { + id: { + serializedName: "id", + type: { + name: "Number", + }, + }, + outerId: { + serializedName: "outerId", + type: { + name: "Number", + }, + }, + typeName: { + constraints: { + MaxLength: 1024, + }, + serializedName: "typeName", + type: { + name: "String", + }, + }, + message: { + constraints: { + MaxLength: 32768, + }, + serializedName: "message", + required: true, + type: { + name: "String", + }, + }, + hasFullStack: { + defaultValue: true, + serializedName: "hasFullStack", + type: { + name: "Boolean", + }, + }, + stack: { + constraints: { + MaxLength: 32768, + }, + serializedName: "stack", + type: { + name: "String", + }, + }, + parsedStack: { + serializedName: "parsedStack", + type: { + name: "Sequence", + element: { + type: { + name: "Composite", + className: "StackFrame", + }, + }, + }, + }, + }, + }, +}; + +export const StackFrame: coreClient.CompositeMapper = { + type: { + name: "Composite", + className: "StackFrame", + modelProperties: { + level: { + serializedName: "level", + required: true, + type: { + name: "Number", + }, + }, + method: { + constraints: { + MaxLength: 1024, + }, + serializedName: "method", + required: true, + type: { + name: "String", + }, + }, + assembly: { + constraints: { + MaxLength: 1024, + }, + serializedName: "assembly", + type: { + name: "String", + }, + }, + fileName: { + constraints: { + MaxLength: 1024, + }, + serializedName: "fileName", + type: { + name: "String", + }, + }, + line: { + serializedName: "line", + type: { + name: "Number", + }, + }, + }, + }, +}; + +export const AvailabilityData: coreClient.CompositeMapper = { + type: { + name: "Composite", + className: "AvailabilityData", + additionalProperties: { type: { name: "Object" } }, + modelProperties: { + ...MonitorDomain.type.modelProperties, + id: { + constraints: { + MaxLength: 512, + }, + serializedName: "id", + required: true, + type: { + name: "String", + }, + }, + name: { + constraints: { + MaxLength: 1024, + }, + serializedName: "name", + required: true, + type: { + name: "String", + }, + }, + duration: { + serializedName: "duration", + required: true, + type: { + name: "String", + }, + }, + success: { + serializedName: "success", + required: true, + type: { + name: "Boolean", + }, + }, + runLocation: { + constraints: { + MaxLength: 1024, + }, + serializedName: "runLocation", + type: { + name: "String", + }, + }, + message: { + constraints: { + MaxLength: 8192, + }, + serializedName: "message", + type: { + name: "String", + }, + }, + properties: { + serializedName: "properties", + type: { + name: "Dictionary", + value: { type: { name: "String" }, constraints: { MaxLength: 8192 } }, + }, + }, + measurements: { + serializedName: "measurements", + type: { + name: "Dictionary", + value: { type: { name: "Number" } }, + }, + }, + }, + }, +}; + +export const TelemetryEventData: coreClient.CompositeMapper = { + type: { + name: "Composite", + className: "TelemetryEventData", + additionalProperties: { type: { name: "Object" } }, + modelProperties: { + ...MonitorDomain.type.modelProperties, + name: { + constraints: { + MaxLength: 512, + }, + serializedName: "name", + required: true, + type: { + name: "String", + }, + }, + properties: { + serializedName: "properties", + type: { + name: "Dictionary", + value: { type: { name: "String" }, constraints: { MaxLength: 8192 } }, + }, + }, + measurements: { + serializedName: "measurements", + type: { + name: "Dictionary", + value: { type: { name: "Number" } }, + }, + }, + }, + }, +}; + +export const TelemetryExceptionData: coreClient.CompositeMapper = { + type: { + name: "Composite", + className: "TelemetryExceptionData", + additionalProperties: { type: { name: "Object" } }, + modelProperties: { + ...MonitorDomain.type.modelProperties, + exceptions: { + serializedName: "exceptions", + required: true, + type: { + name: "Sequence", + element: { + type: { + name: "Composite", + className: "TelemetryExceptionDetails", + }, + }, + }, + }, + severityLevel: { + serializedName: "severityLevel", + nullable: true, + type: { + name: "String", + }, + }, + problemId: { + constraints: { + MaxLength: 1024, + }, + serializedName: "problemId", + type: { + name: "String", + }, + }, + properties: { + serializedName: "properties", + type: { + name: "Dictionary", + value: { type: { name: "String" }, constraints: { MaxLength: 8192 } }, + }, + }, + measurements: { + serializedName: "measurements", + type: { + name: "Dictionary", + value: { type: { name: "Number" } }, + }, + }, + }, + }, +}; + +export const MessageData: coreClient.CompositeMapper = { + type: { + name: "Composite", + className: "MessageData", + additionalProperties: { type: { name: "Object" } }, + modelProperties: { + ...MonitorDomain.type.modelProperties, + message: { + constraints: { + MaxLength: 32768, + }, + serializedName: "message", + required: true, + type: { + name: "String", + }, + }, + severityLevel: { + serializedName: "severityLevel", + type: { + name: "String", + }, + }, + properties: { + serializedName: "properties", + type: { + name: "Dictionary", + value: { type: { name: "String" }, constraints: { MaxLength: 8192 } }, + }, + }, + measurements: { + serializedName: "measurements", + type: { + name: "Dictionary", + value: { type: { name: "Number" } }, + }, + }, + }, + }, +}; + +export const MetricsData: coreClient.CompositeMapper = { + type: { + name: "Composite", + className: "MetricsData", + additionalProperties: { type: { name: "Object" } }, + modelProperties: { + ...MonitorDomain.type.modelProperties, + metrics: { + serializedName: "metrics", + required: true, + type: { + name: "Sequence", + element: { + type: { + name: "Composite", + className: "MetricDataPoint", + }, + }, + }, + }, + properties: { + serializedName: "properties", + type: { + name: "Dictionary", + value: { type: { name: "String" }, constraints: { MaxLength: 8192 } }, + }, + }, + }, + }, +}; + +export const PageViewData: coreClient.CompositeMapper = { + type: { + name: "Composite", + className: "PageViewData", + additionalProperties: { type: { name: "Object" } }, + modelProperties: { + ...MonitorDomain.type.modelProperties, + id: { + constraints: { + MaxLength: 512, + }, + serializedName: "id", + required: true, + type: { + name: "String", + }, + }, + name: { + constraints: { + MaxLength: 1024, + }, + serializedName: "name", + required: true, + type: { + name: "String", + }, + }, + url: { + constraints: { + MaxLength: 2048, + }, + serializedName: "url", + type: { + name: "String", + }, + }, + duration: { + serializedName: "duration", + type: { + name: "String", + }, + }, + referredUri: { + constraints: { + MaxLength: 2048, + }, + serializedName: "referredUri", + type: { + name: "String", + }, + }, + properties: { + serializedName: "properties", + type: { + name: "Dictionary", + value: { type: { name: "String" }, constraints: { MaxLength: 8192 } }, + }, + }, + measurements: { + serializedName: "measurements", + type: { + name: "Dictionary", + value: { type: { name: "Number" } }, + }, + }, + }, + }, +}; + +export const PageViewPerfData: coreClient.CompositeMapper = { + type: { + name: "Composite", + className: "PageViewPerfData", + additionalProperties: { type: { name: "Object" } }, + modelProperties: { + ...MonitorDomain.type.modelProperties, + id: { + constraints: { + MaxLength: 512, + }, + serializedName: "id", + required: true, + type: { + name: "String", + }, + }, + name: { + constraints: { + MaxLength: 1024, + }, + serializedName: "name", + required: true, + type: { + name: "String", + }, + }, + url: { + constraints: { + MaxLength: 2048, + }, + serializedName: "url", + type: { + name: "String", + }, + }, + duration: { + serializedName: "duration", + type: { + name: "String", + }, + }, + perfTotal: { + serializedName: "perfTotal", + type: { + name: "String", + }, + }, + networkConnect: { + serializedName: "networkConnect", + type: { + name: "String", + }, + }, + sentRequest: { + serializedName: "sentRequest", + type: { + name: "String", + }, + }, + receivedResponse: { + serializedName: "receivedResponse", + type: { + name: "String", + }, + }, + domProcessing: { + serializedName: "domProcessing", + type: { + name: "String", + }, + }, + properties: { + serializedName: "properties", + type: { + name: "Dictionary", + value: { type: { name: "String" }, constraints: { MaxLength: 8192 } }, + }, + }, + measurements: { + serializedName: "measurements", + type: { + name: "Dictionary", + value: { type: { name: "Number" } }, + }, + }, + }, + }, +}; + +export const RemoteDependencyData: coreClient.CompositeMapper = { + type: { + name: "Composite", + className: "RemoteDependencyData", + additionalProperties: { type: { name: "Object" } }, + modelProperties: { + ...MonitorDomain.type.modelProperties, + id: { + constraints: { + MaxLength: 512, + }, + serializedName: "id", + type: { + name: "String", + }, + }, + name: { + constraints: { + MaxLength: 1024, + }, + serializedName: "name", + required: true, + type: { + name: "String", + }, + }, + resultCode: { + constraints: { + MaxLength: 1024, + }, + serializedName: "resultCode", + type: { + name: "String", + }, + }, + data: { + constraints: { + MaxLength: 8192, + }, + serializedName: "data", + type: { + name: "String", + }, + }, + type: { + constraints: { + MaxLength: 1024, + }, + serializedName: "type", + type: { + name: "String", + }, + }, + target: { + constraints: { + MaxLength: 1024, + }, + serializedName: "target", + type: { + name: "String", + }, + }, + duration: { + serializedName: "duration", + required: true, + type: { + name: "String", + }, + }, + success: { + defaultValue: true, + serializedName: "success", + type: { + name: "Boolean", + }, + }, + properties: { + serializedName: "properties", + type: { + name: "Dictionary", + value: { type: { name: "String" }, constraints: { MaxLength: 8192 } }, + }, + }, + measurements: { + serializedName: "measurements", + type: { + name: "Dictionary", + value: { type: { name: "Number" } }, + }, + }, + }, + }, +}; + +export const RequestData: coreClient.CompositeMapper = { + type: { + name: "Composite", + className: "RequestData", + additionalProperties: { type: { name: "Object" } }, + modelProperties: { + ...MonitorDomain.type.modelProperties, + id: { + constraints: { + MaxLength: 512, + }, + serializedName: "id", + required: true, + type: { + name: "String", + }, + }, + name: { + constraints: { + MaxLength: 1024, + }, + serializedName: "name", + type: { + name: "String", + }, + }, + duration: { + serializedName: "duration", + required: true, + type: { + name: "String", + }, + }, + success: { + defaultValue: true, + serializedName: "success", + required: true, + type: { + name: "Boolean", + }, + }, + responseCode: { + constraints: { + MaxLength: 1024, + }, + serializedName: "responseCode", + required: true, + type: { + name: "String", + }, + }, + source: { + constraints: { + MaxLength: 1024, + }, + serializedName: "source", + type: { + name: "String", + }, + }, + url: { + constraints: { + MaxLength: 2048, + }, + serializedName: "url", + type: { + name: "String", + }, + }, + properties: { + serializedName: "properties", + type: { + name: "Dictionary", + value: { type: { name: "String" }, constraints: { MaxLength: 8192 } }, + }, + }, + measurements: { + serializedName: "measurements", + type: { + name: "Dictionary", + value: { type: { name: "Number" } }, + }, + }, + }, + }, +}; diff --git a/src/declarations/generated/models/parameters.ts b/src/declarations/generated/models/parameters.ts new file mode 100644 index 000000000..ef876b83a --- /dev/null +++ b/src/declarations/generated/models/parameters.ts @@ -0,0 +1,62 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is regenerated. + */ + +import { OperationParameter, OperationURLParameter } from "@azure/core-client"; + +export const contentType: OperationParameter = { + parameterPath: ["options", "contentType"], + mapper: { + defaultValue: "application/json", + isConstant: true, + serializedName: "Content-Type", + type: { + name: "String", + }, + }, +}; + +export const body: OperationParameter = { + parameterPath: "body", + mapper: { + serializedName: "body", + required: true, + type: { + name: "Sequence", + element: { + type: { + name: "Composite", + className: "TelemetryItem", + }, + }, + }, + }, +}; + +export const accept: OperationParameter = { + parameterPath: "accept", + mapper: { + defaultValue: "application/json", + isConstant: true, + serializedName: "Accept", + type: { + name: "String", + }, + }, +}; + +export const host: OperationURLParameter = { + parameterPath: "host", + mapper: { + serializedName: "Host", + required: true, + type: { + name: "String", + }, + }, + skipEncoding: true, +}; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 000000000..2c81e0269 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. +export { TelemetryClient } from "./shim/telemetryClient"; +export { AzureMonitorOpenTelemetryOptions, InstrumentationOptions } from "./types"; +export { KnownSeverityLevel } from "./declarations/generated"; +export { + AvailabilityTelemetry, + TraceTelemetry, + ExceptionTelemetry, + EventTelemetry, + PageViewTelemetry, + Telemetry, +} from "./declarations/contracts"; +export { useAzureMonitor, shutdownAzureMonitor } from "./main"; + +// To support the shim +export * from "./shim/applicationinsights"; diff --git a/src/logs/autoCollectLogs.ts b/src/logs/autoCollectLogs.ts new file mode 100644 index 000000000..c0ce083c9 --- /dev/null +++ b/src/logs/autoCollectLogs.ts @@ -0,0 +1,20 @@ +import { InstrumentationOptions } from "../types"; +import { enablePublishers } from "./diagnostic-channel/initialization"; +enablePublishers(); + +export class AutoCollectLogs { + + public enable(options: InstrumentationOptions) { + // eslint-disable-next-line @typescript-eslint/no-var-requires + require("./diagnostic-channel/console.sub").enable(options.console); + // eslint-disable-next-line @typescript-eslint/no-var-requires + require("./diagnostic-channel/winston.sub").enable(options.winston); + } + + public shutdown() { + // eslint-disable-next-line @typescript-eslint/no-var-requires + require("./diagnostic-channel/console.sub").dispose(); + // eslint-disable-next-line @typescript-eslint/no-var-requires + require("./diagnostic-channel/winston.sub").dispose(); + } +} diff --git a/src/logs/diagnostic-channel/console.sub.ts b/src/logs/diagnostic-channel/console.sub.ts new file mode 100644 index 000000000..0e23a4a0a --- /dev/null +++ b/src/logs/diagnostic-channel/console.sub.ts @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for details. + +import { Logger, LogRecord, SeverityNumber, logs } from "@opentelemetry/api-logs"; +import { InstrumentationConfig } from "@opentelemetry/instrumentation"; +import { channel, IStandardEvent, trueFilter } from "diagnostic-channel"; +import { console as consolePub } from "diagnostic-channel-publishers"; + + +let logger: Logger; +let logSendingLevel: SeverityNumber; + +const subscriber = (event: IStandardEvent) => { + const severity = (event.data.message as string | Error) instanceof Error ? SeverityNumber.ERROR : (event.data.stderr + ? SeverityNumber.WARN + : SeverityNumber.INFO); + if (logSendingLevel <= severity) { + let message = event.data.message.toString(); + // Message can have a trailing newline + if (message.lastIndexOf("\n") === message.length - 1) { + message = message.substring(0, message.length - 1); + } + const logRecord: LogRecord = { + body: message, + severityNumber: severity + }; + logger.emit(logRecord); + } +}; + +export function enable(config?: InstrumentationConfig & { logSendingLevel?: SeverityNumber }) { + if (config?.enabled) { + logger = logs.getLogger("ApplicationInsightsConsoleLogger"); + logSendingLevel = config.logSendingLevel || SeverityNumber.UNSPECIFIED; + channel.subscribe("console", subscriber, trueFilter); + } +} + +export function dispose() { + channel.unsubscribe("console", subscriber); +} diff --git a/src/logs/diagnostic-channel/initialization.ts b/src/logs/diagnostic-channel/initialization.ts new file mode 100644 index 000000000..62c11709a --- /dev/null +++ b/src/logs/diagnostic-channel/initialization.ts @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for details. + +// Don't reference modules from these directly. Use only for types. +import * as DiagChannelPublishers from "diagnostic-channel-publishers"; +import { diag } from "@opentelemetry/api"; + + +const TAG = "DiagnosticChannel"; +let isInitialized = false; + +export function enablePublishers() { + // Only register monkey patchs once + if (!isInitialized) { + isInitialized = true; + // eslint-disable-next-line @typescript-eslint/no-var-requires + const publishers: typeof DiagChannelPublishers = require("diagnostic-channel-publishers"); + const modules: { [key: string]: any } = { + bunyan: publishers.bunyan, + console: publishers.console, + winston: publishers.winston, + }; + + for (const mod in modules) { + modules[mod].enable(); + diag.info(TAG, `Subscribed to ${mod} events`); + } + } +} diff --git a/src/logs/diagnostic-channel/winston.sub.ts b/src/logs/diagnostic-channel/winston.sub.ts new file mode 100644 index 000000000..07f364f22 --- /dev/null +++ b/src/logs/diagnostic-channel/winston.sub.ts @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for details. + +import { Logger, LogRecord, logs, SeverityNumber } from "@opentelemetry/api-logs"; +import { channel, IStandardEvent, trueFilter } from "diagnostic-channel"; +import { winston } from "diagnostic-channel-publishers"; +import { InstrumentationConfig } from "@opentelemetry/instrumentation"; + + +let logger: Logger; +let logSendingLevel: SeverityNumber; + +const winstonToAILevelMap: { [key: string]: (og: string) => number } = { + syslog(og: string) { + const map: { [key: string]: number } = { + emerg: SeverityNumber.FATAL3, + alert: SeverityNumber.FATAL2, + crit: SeverityNumber.FATAL, + error: SeverityNumber.ERROR, + warning: SeverityNumber.WARN, + notice: SeverityNumber.INFO2, + info: SeverityNumber.INFO, + debug: SeverityNumber.DEBUG, + }; + + return map[og] === undefined ? SeverityNumber.INFO : map[og]; + }, + npm(og: string) { + const map: { [key: string]: number } = { + error: SeverityNumber.ERROR, + warn: SeverityNumber.WARN, + info: SeverityNumber.INFO, + http: SeverityNumber.DEBUG3, + verbose: SeverityNumber.DEBUG2, + debug: SeverityNumber.DEBUG, + silly: SeverityNumber.TRACE, + }; + + return map[og] === undefined ? SeverityNumber.INFO : map[og]; + }, + unknown(og: string) { + return SeverityNumber.INFO; + }, +}; + +const subscriber = (event: IStandardEvent) => { + const severity = winstonToAILevelMap[event.data.levelKind](event.data.level); + if (logSendingLevel <= severity) { + const message = event.data.message.toString(); + let logRecord: LogRecord = { + body: message, + severityNumber: severity, + attributes: event.data.meta + }; + logger.emit(logRecord); + } +}; + +export function enable(config?: InstrumentationConfig & { logSendingLevel?: SeverityNumber }) { + if (config?.enabled) { + logger = logs.getLogger("ApplicationInsightsConsoleLogger"); + logSendingLevel = config.logSendingLevel || SeverityNumber.UNSPECIFIED; + channel.subscribe("winston", subscriber, trueFilter); + } +} + +export function dispose() { + channel.unsubscribe("winston", subscriber); +} diff --git a/src/logs/exceptions.ts b/src/logs/exceptions.ts new file mode 100644 index 000000000..d98c8045d --- /dev/null +++ b/src/logs/exceptions.ts @@ -0,0 +1,178 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for details. +import { logs } from "@opentelemetry/api-logs"; +import { Util } from "../shared/util"; +import { LogApi } from "../shim/logsApi"; +import { LoggerProvider } from "@opentelemetry/sdk-logs"; + +type ExceptionHandle = "uncaughtExceptionMonitor" | "uncaughtException" | "unhandledRejection"; +const UNCAUGHT_EXCEPTION_HANDLER_NAME: ExceptionHandle = "uncaughtException"; +const UNHANDLED_REJECTION_HANDLER_NAME: ExceptionHandle = "unhandledRejection"; +const FALLBACK_ERROR_MESSAGE = + "A promise was rejected without providing an error. Application Insights generated this error stack for you."; + +export class AutoCollectExceptions { + private _exceptionListenerHandle?: (error: Error | undefined) => void; + private _rejectionListenerHandle?: (error: Error | undefined) => void; + private _client: LogApi; + + constructor(client: LogApi) { + this._client = client; + this._exceptionListenerHandle = this._handleException.bind( + this, + true, + UNCAUGHT_EXCEPTION_HANDLER_NAME + ); + this._rejectionListenerHandle = this._handleException.bind( + this, + false, + UNHANDLED_REJECTION_HANDLER_NAME + ); // never rethrows + (process).on( + UNCAUGHT_EXCEPTION_HANDLER_NAME, + this._exceptionListenerHandle + ); + (process).on( + UNHANDLED_REJECTION_HANDLER_NAME, + this._rejectionListenerHandle + ); + } + + public shutdown() { + if (this._exceptionListenerHandle) { + if (this._exceptionListenerHandle) { + process.removeListener( + UNCAUGHT_EXCEPTION_HANDLER_NAME, + this._exceptionListenerHandle + ); + } + if (this._rejectionListenerHandle) { + process.removeListener( + UNHANDLED_REJECTION_HANDLER_NAME, + this._rejectionListenerHandle + ); + } + } + this._exceptionListenerHandle = undefined; + this._rejectionListenerHandle = undefined; + delete this._exceptionListenerHandle; + delete this._rejectionListenerHandle; + } + + private _handleException( + reThrow: boolean, + name: ExceptionHandle, + error: Error | undefined = new Error(FALLBACK_ERROR_MESSAGE) + ) { + if (this._client) { + this._client.trackException({ exception: error }); + try { + (logs.getLoggerProvider() as LoggerProvider).forceFlush().then(() => { + // only rethrow when we are the only listener + if (reThrow && name && process.listeners(name as any).length === 1) { + // eslint-disable-next-line no-console + console.error(error); + // eslint-disable-next-line no-process-exit + process.exit(1); + } + }); + } catch (error) { + console.error(`Could not get the loggerProvider upon handling a tracked exception: ${error}`); + } + } else { + // eslint-disable-next-line no-console + console.error(error); + process.exit(1); + } + } +} + + +// regex to match stack frames from ie/chrome/ff +// methodName=$2, fileName=$4, lineNo=$5, column=$6 +const stackFramesRegex = /^(\s+at)?(.*?)(\@|\s\(|\s)([^\(\n]+):(\d+):(\d+)(\)?)$/; + +export class _StackFrame { + public sizeInBytes = 0; + public level: number; + public method: string; + public assembly: string; + public fileName: string; + public line: number; + + private _baseSize = 58; //'{"method":"","level":,"assembly":"","fileName":"","line":}'.length + + constructor(frame: string, level: number) { + this.level = level; + this.method = ""; + this.assembly = Util.getInstance().trim(frame); + const matches = frame.match(stackFramesRegex); + if (matches && matches.length >= 5) { + this.method = Util.getInstance().trim(matches[2]) || this.method; + this.fileName = Util.getInstance().trim(matches[4]) || ""; + this.line = parseInt(matches[5]) || 0; + } + + this.sizeInBytes += this.method.length; + this.sizeInBytes += this.fileName.length; + this.sizeInBytes += this.assembly.length; + + // todo: these might need to be removed depending on how the back-end settles on their size calculation + this.sizeInBytes += this._baseSize; + this.sizeInBytes += this.level.toString().length; + this.sizeInBytes += this.line.toString().length; + } +} + +export function parseStack(stack: any): _StackFrame[] { + let parsedStack: _StackFrame[] = undefined; + if (typeof stack === "string") { + const frames = stack.split("\n"); + parsedStack = []; + let level = 0; + + let totalSizeInBytes = 0; + for (let i = 0; i <= frames.length; i++) { + const frame = frames[i]; + if (stackFramesRegex.test(frame)) { + const parsedFrame = new _StackFrame(frames[i], level++); + totalSizeInBytes += parsedFrame.sizeInBytes; + parsedStack.push(parsedFrame); + } + } + + // DP Constraint - exception parsed stack must be < 32KB + // remove frames from the middle to meet the threshold + const exceptionParsedStackThreshold = 32 * 1024; + if (totalSizeInBytes > exceptionParsedStackThreshold) { + let left = 0; + let right = parsedStack.length - 1; + let size = 0; + let acceptedLeft = left; + let acceptedRight = right; + + while (left < right) { + // check size + const lSize = parsedStack[left].sizeInBytes; + const rSize = parsedStack[right].sizeInBytes; + size += lSize + rSize; + + if (size > exceptionParsedStackThreshold) { + // remove extra frames from the middle + const howMany = acceptedRight - acceptedLeft + 1; + parsedStack.splice(acceptedLeft, howMany); + break; + } + + // update pointers + acceptedLeft = left; + acceptedRight = right; + + left++; + right--; + } + } + } + + return parsedStack; +} diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 000000000..faf326ecb --- /dev/null +++ b/src/main.ts @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { shutdownAzureMonitor as distroShutdownAzureMonitor, useAzureMonitor as distroUseAzureMonitor } from "@azure/monitor-opentelemetry"; +import { ProxyTracerProvider, diag, metrics, trace } from "@opentelemetry/api"; +import { logs } from "@opentelemetry/api-logs"; +import { MeterProvider, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics"; +import { BatchLogRecordProcessor, LoggerProvider } from "@opentelemetry/sdk-logs"; +import { BasicTracerProvider, BatchSpanProcessor, NodeTracerProvider } from "@opentelemetry/sdk-trace-node"; +import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http"; +import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http"; +import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http"; + +import { AutoCollectLogs } from "./logs/autoCollectLogs"; +import { AutoCollectExceptions } from "./logs/exceptions"; +import { AzureMonitorOpenTelemetryOptions } from "./types"; +import { ApplicationInsightsConfig } from "./shared/configuration/config"; +import { LogApi } from "./shim/logsApi"; +import { PerformanceCounterMetrics } from "./metrics/performanceCounters"; +import { AzureMonitorSpanProcessor } from "./traces/spanProcessor"; + +let autoCollectLogs: AutoCollectLogs; +let exceptions: AutoCollectExceptions; +let perfCounters: PerformanceCounterMetrics; + +/** + * Initialize Azure Monitor + * @param options Configuration + */ +export function useAzureMonitor(options?: AzureMonitorOpenTelemetryOptions) { + distroUseAzureMonitor(options); + const internalConfig = new ApplicationInsightsConfig(options); + const logApi = new LogApi(logs.getLogger("ApplicationInsightsLogger")); + autoCollectLogs = new AutoCollectLogs(); + if (internalConfig.enableAutoCollectExceptions) { + exceptions = new AutoCollectExceptions(logApi); + } + if (internalConfig.enableAutoCollectPerformance) { + try { + perfCounters = new PerformanceCounterMetrics(internalConfig); + // Add SpanProcessor to calculate Request Metrics + if (typeof (trace.getTracerProvider() as BasicTracerProvider).addSpanProcessor === "function") { + (trace.getTracerProvider() as BasicTracerProvider).addSpanProcessor(new AzureMonitorSpanProcessor(perfCounters)); + } + } catch (err) { + diag.error("Failed to initialize PerformanceCounterMetrics: ", err); + } + } + autoCollectLogs.enable(internalConfig.instrumentationOptions); + _addOtlpExporters(internalConfig); +} + +/** +* Shutdown Azure Monitor +*/ +export async function shutdownAzureMonitor() { + await distroShutdownAzureMonitor(); + autoCollectLogs.shutdown(); + exceptions?.shutdown(); + perfCounters?.shutdown(); +} + +/** + *Try to send all queued telemetry if present. + */ +export async function flushAzureMonitor() { + try { + await (metrics.getMeterProvider() as MeterProvider).forceFlush(); + await (trace.getTracerProvider() as BasicTracerProvider).forceFlush(); + await (logs.getLoggerProvider() as LoggerProvider).forceFlush(); + } catch (err) { + diag.error("Failed to flush telemetry", err); + } +} + +function _addOtlpExporters(internalConfig: ApplicationInsightsConfig) { + if (internalConfig.otlpMetricExporterConfig?.enabled) { + const otlpMetricsExporter = new OTLPMetricExporter(internalConfig.otlpMetricExporterConfig); + const otlpMetricReader = new PeriodicExportingMetricReader({ + exporter: otlpMetricsExporter, + }); + try { + (metrics.getMeterProvider() as MeterProvider).addMetricReader(otlpMetricReader); + } + catch (err) { + diag.error("Failed to set OTLP Metric Exporter", err); + } + } + if (internalConfig.otlpLogExporterConfig?.enabled) { + const otlpLogExporter = new OTLPLogExporter(internalConfig.otlpLogExporterConfig); + const otlpLogProcessor = new BatchLogRecordProcessor(otlpLogExporter); + try { + (logs.getLoggerProvider() as LoggerProvider).addLogRecordProcessor(otlpLogProcessor); + } + catch (err) { + diag.error("Failed to set OTLP Log Exporter", err); + } + } + if (internalConfig.otlpTraceExporterConfig?.enabled) { + const otlpTraceExporter = new OTLPTraceExporter(internalConfig.otlpTraceExporterConfig); + const otlpSpanProcessor = new BatchSpanProcessor(otlpTraceExporter); + try { + ((trace.getTracerProvider() as ProxyTracerProvider).getDelegate() as NodeTracerProvider).addSpanProcessor(otlpSpanProcessor); + } + catch (err) { + diag.error("Failed to set OTLP Trace Exporter", err); + } + } +} diff --git a/src/metrics/performanceCounters.ts b/src/metrics/performanceCounters.ts new file mode 100644 index 000000000..6525cbb15 --- /dev/null +++ b/src/metrics/performanceCounters.ts @@ -0,0 +1,291 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as os from "os"; +import { + Histogram, + Meter, + ObservableCallback, + ObservableGauge, + ObservableResult, + SpanKind, + ValueType, +} from "@opentelemetry/api"; +import { AzureMonitorMetricExporter } from "@azure/monitor-opentelemetry-exporter"; +import { + MeterProvider, + MeterProviderOptions, + PeriodicExportingMetricReader, + PeriodicExportingMetricReaderOptions, +} from "@opentelemetry/sdk-metrics"; +import { ReadableSpan } from "@opentelemetry/sdk-trace-base"; +import { PerformanceCounterMetricNames } from "./types"; +import { ApplicationInsightsConfig } from "../shared/configuration/config"; + +/** + * Azure Monitor PerformanceCounter Metrics + */ +export class PerformanceCounterMetrics { + private _internalConfig: ApplicationInsightsConfig; + private _collectionInterval = 60000; // 60 seconds + private _meterProvider: MeterProvider; + private _azureExporter: AzureMonitorMetricExporter; + private _metricReader: PeriodicExportingMetricReader; + private _meter: Meter; + private _requestDurationHistogram: Histogram; + private _requestRateGauge: ObservableGauge; + private _requestRateGaugeCallback: ObservableCallback; + private _memoryPrivateBytesGauge: ObservableGauge; + private _memoryPrivateBytesGaugeCallback: ObservableCallback; + private _memoryAvailableBytesGauge: ObservableGauge; + private _memoryAvailableBytesGaugeCallback: ObservableCallback; + private _processorTimeGauge: ObservableGauge; + private _processorTimeGaugeCallback: ObservableCallback; + private _processTimeGauge: ObservableGauge; + private _processTimeGaugeCallback: ObservableCallback; + private _totalCount = 0; + private _intervalExecutionTime = 0; + private _lastRequestRate: { count: number; time: number; executionInterval: number }; + private _lastAppCpuUsage: { user: number; system: number }; + private _lastHrtime: number[]; + private _lastCpus: { + model: string; + speed: number; + times: { user: number; nice: number; sys: number; idle: number; irq: number }; + }[]; + private _lastCpusProcess: { + model: string; + speed: number; + times: { user: number; nice: number; sys: number; idle: number; irq: number }; + }[]; + + /** + * Creates performance counter instruments. + * @param options - Distro configuration. + * @param config - Application Insights configuration. + */ + constructor(config: ApplicationInsightsConfig, options?: { collectionInterval: number }) { + this._internalConfig = config; + this._lastCpus = os.cpus(); + this._lastCpusProcess = os.cpus(); + this._lastAppCpuUsage = (process as any).cpuUsage(); + this._lastHrtime = process.hrtime(); + + this._lastRequestRate = { + count: this._totalCount, + time: +new Date(), + executionInterval: this._intervalExecutionTime, + }; + + const meterProviderConfig: MeterProviderOptions = { + resource: this._internalConfig.resource, + }; + this._meterProvider = new MeterProvider(meterProviderConfig); + this._azureExporter = new AzureMonitorMetricExporter(this._internalConfig.azureMonitorExporterOptions); + const metricReaderOptions: PeriodicExportingMetricReaderOptions = { + exporter: this._azureExporter as any, + exportIntervalMillis: options?.collectionInterval || this._collectionInterval, + }; + this._metricReader = new PeriodicExportingMetricReader(metricReaderOptions); + this._meterProvider.addMetricReader(this._metricReader); + this._meter = this._meterProvider.getMeter("AzureMonitorPerformanceCountersMeter"); + + this._lastRequestRate = { count: 0, time: 0, executionInterval: 0 }; + + // Create Instruments + this._requestDurationHistogram = this._meter.createHistogram( + PerformanceCounterMetricNames.REQUEST_DURATION, + { valueType: ValueType.DOUBLE } + ); + this._requestRateGauge = this._meter.createObservableGauge( + PerformanceCounterMetricNames.REQUEST_RATE, + { + description: "Incoming Requests Average Execution Time", + valueType: ValueType.DOUBLE, + } + ); + this._memoryPrivateBytesGauge = this._meter.createObservableGauge( + PerformanceCounterMetricNames.PRIVATE_BYTES, + { description: "Amount of memory process has used in bytes", valueType: ValueType.INT } + ); + this._memoryAvailableBytesGauge = this._meter.createObservableGauge( + PerformanceCounterMetricNames.AVAILABLE_BYTES, + { description: "Amount of available memory in bytes", valueType: ValueType.INT } + ); + this._processorTimeGauge = this._meter.createObservableGauge( + PerformanceCounterMetricNames.PROCESSOR_TIME, + { + description: "Processor time as a percentage", + valueType: ValueType.DOUBLE, + } + ); + this._processTimeGauge = this._meter.createObservableGauge( + PerformanceCounterMetricNames.PROCESS_TIME, + { + description: "Process CPU usage as a percentage", + valueType: ValueType.DOUBLE, + } + ); + + // Add callbacks + this._requestRateGaugeCallback = this._getRequestRate.bind(this); + this._memoryPrivateBytesGaugeCallback = this._getPrivateMemory.bind(this); + this._memoryAvailableBytesGaugeCallback = this._getAvailableMemory.bind(this); + this._processorTimeGaugeCallback = this._getProcessorTime.bind(this); + this._processTimeGaugeCallback = this._getProcessTime.bind(this); + this._memoryPrivateBytesGauge.addCallback(this._memoryPrivateBytesGaugeCallback); + this._memoryAvailableBytesGauge.addCallback(this._memoryAvailableBytesGaugeCallback); + this._processTimeGauge.addCallback(this._processTimeGaugeCallback); + this._processorTimeGauge.addCallback(this._processorTimeGaugeCallback); + this._requestRateGauge.addCallback(this._requestRateGaugeCallback); + } + + /** + * Shutdown Meter Provider it will return no-op Meters after being called. + */ + public async shutdown(): Promise { + return this._meterProvider.shutdown(); + } + + /** + * Force flush Meter Provider. + */ + public async flush(): Promise { + return this._meterProvider.forceFlush(); + } + + /** + *Get OpenTelemetry MeterProvider + */ + public getMeterProvider(): MeterProvider { + return this._meterProvider; + } + + /** + * Record Span metrics + */ + public recordSpan(span: ReadableSpan): void { + if (span.kind !== SpanKind.SERVER) { + return; + } + this._totalCount++; + const durationMs = span.duration[0]; + this._intervalExecutionTime += durationMs; + this._requestDurationHistogram.record(durationMs); + } + + private _getRequestRate(observableResult: ObservableResult) { + const currentTime = +new Date(); + const intervalRequests = this._totalCount - this._lastRequestRate.count || 0; + const elapsedMs = currentTime - this._lastRequestRate.time; + if (elapsedMs > 0) { + const elapsedSeconds = elapsedMs / 1000; + const requestsPerSec = intervalRequests / elapsedSeconds; + observableResult.observe(requestsPerSec); + } + this._lastRequestRate = { + count: this._totalCount, + time: currentTime, + executionInterval: this._lastRequestRate.executionInterval, + }; + } + + private _getPrivateMemory(observableResult: ObservableResult) { + observableResult.observe(process.memoryUsage().rss); + } + + private _getAvailableMemory(observableResult: ObservableResult) { + observableResult.observe(os.freemem()); + } + + private _getTotalCombinedCpu(cpus: os.CpuInfo[], lastCpus: os.CpuInfo[]) { + let totalUser = 0; + let totalSys = 0; + let totalNice = 0; + let totalIdle = 0; + let totalIrq = 0; + for (let i = 0; !!cpus && i < cpus.length; i++) { + const cpu = cpus[i]; + const lastCpu = lastCpus[i]; + const times = cpu.times; + const lastTimes = lastCpu.times; + // user cpu time (or) % CPU time spent in user space + let user = times.user - lastTimes.user; + user = user > 0 ? user : 0; // Avoid negative values + totalUser += user; + // system cpu time (or) % CPU time spent in kernel space + let sys = times.sys - lastTimes.sys; + sys = sys > 0 ? sys : 0; // Avoid negative values + totalSys += sys; + // user nice cpu time (or) % CPU time spent on low priority processes + let nice = times.nice - lastTimes.nice; + nice = nice > 0 ? nice : 0; // Avoid negative values + totalNice += nice; + // idle cpu time (or) % CPU time spent idle + let idle = times.idle - lastTimes.idle; + idle = idle > 0 ? idle : 0; // Avoid negative values + totalIdle += idle; + // irq (or) % CPU time spent servicing/handling hardware interrupts + let irq = times.irq - lastTimes.irq; + irq = irq > 0 ? irq : 0; // Avoid negative values + totalIrq += irq; + } + const combinedTotal = totalUser + totalSys + totalNice + totalIdle + totalIrq; + return { + combinedTotal: combinedTotal, + totalUser: totalUser, + totalIdle: totalIdle, + }; + } + + private _getProcessorTime(observableResult: ObservableResult) { + // this reports total ms spent in each category since the OS was booted, to calculate percent it is necessary + // to find the delta since the last measurement + const cpus = os.cpus(); + if (cpus && cpus.length && this._lastCpus && cpus.length === this._lastCpus.length) { + const cpuTotals = this._getTotalCombinedCpu(cpus, this._lastCpus); + + const value = + cpuTotals.combinedTotal > 0 + ? ((cpuTotals.combinedTotal - cpuTotals.totalIdle) / cpuTotals.combinedTotal) * 100 + : 0; + observableResult.observe(value); + } + this._lastCpus = cpus; + } + + private _getProcessTime(observableResult: ObservableResult) { + // this reports total ms spent in each category since the OS was booted, to calculate percent it is necessary + // to find the delta since the last measurement + const cpus = os.cpus(); + if ( + cpus && + cpus.length && + this._lastCpusProcess && + cpus.length === this._lastCpusProcess.length + ) { + // Calculate % of total cpu time (user + system) this App Process used (Only supported by node v6.1.0+) + let appCpuPercent: number | undefined = undefined; + const appCpuUsage = (process as any).cpuUsage(); + const hrtime = process.hrtime(); + const totalApp = + appCpuUsage.user - + this._lastAppCpuUsage.user + + (appCpuUsage.system - this._lastAppCpuUsage.system) || 0; + + if (typeof this._lastHrtime !== "undefined" && this._lastHrtime.length === 2) { + const elapsedTime = + (hrtime[0] - this._lastHrtime[0]) * 1e6 + (hrtime[1] - this._lastHrtime[1]) / 1e3 || 0; // convert to microseconds + + appCpuPercent = (100 * totalApp) / (elapsedTime * cpus.length); + } + // Set previous + this._lastAppCpuUsage = appCpuUsage; + this._lastHrtime = hrtime; + const cpuTotals = this._getTotalCombinedCpu(cpus, this._lastCpusProcess); + const value = appCpuPercent || (cpuTotals.totalUser / cpuTotals.combinedTotal) * 100; + observableResult.observe(value); + } + this._lastCpusProcess = cpus; + } +} diff --git a/src/metrics/types.ts b/src/metrics/types.ts new file mode 100644 index 000000000..d2713ee6f --- /dev/null +++ b/src/metrics/types.ts @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** + * Disable Standard Metrics environment variable name. + */ +export const APPLICATION_INSIGHTS_NO_STANDARD_METRICS = "APPLICATION_INSIGHTS_NO_STANDARD_METRICS"; + +export interface StandardMetricBaseDimensions { + metricId?: string; + cloudRoleInstance?: string; + cloudRoleName?: string; + IsAutocollected?: string; +} + +export interface MetricRequestDimensions extends StandardMetricBaseDimensions { + requestSuccess?: string; + requestResultCode?: string; +} + +export interface MetricDependencyDimensions extends StandardMetricBaseDimensions { + dependencyType?: string; + dependencyTarget?: string; + dependencySuccess?: string; + dependencyResultCode?: string; +} + +export enum StandardMetricNames { + HTTP_REQUEST_DURATION = "azureMonitor.http.requestDuration", + HTTP_DEPENDENCY_DURATION = "azureMonitor.http.dependencyDuration", + EXCEPTION_COUNT = "azureMonitor.exceptionCount", + TRACE_COUNT = "azureMonitor.traceCount", +} + +export enum PerformanceCounterMetricNames { + PRIVATE_BYTES = "\\Process(??APP_WIN32_PROC??)\\Private Bytes", + AVAILABLE_BYTES = "\\Memory\\Available Bytes", + PROCESSOR_TIME = "\\Processor(_Total)\\% Processor Time", + PROCESS_TIME = "\\Process(??APP_WIN32_PROC??)\\% Processor Time", + REQUEST_RATE = "\\ASP.NET Applications(??APP_W3SVC_PROC??)\\Requests/Sec", + REQUEST_DURATION = "\\ASP.NET Applications(??APP_W3SVC_PROC??)\\Request Execution Time", +} diff --git a/src/shared/configuration/config.ts b/src/shared/configuration/config.ts new file mode 100644 index 000000000..3f286eda4 --- /dev/null +++ b/src/shared/configuration/config.ts @@ -0,0 +1,167 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { AzureMonitorExporterOptions } from "@azure/monitor-opentelemetry-exporter"; +import { diag } from "@opentelemetry/api"; +import { + Resource, + ResourceDetectionConfig, + detectResourcesSync, + envDetectorSync, +} from "@opentelemetry/resources"; +import { JsonConfig } from "./jsonConfig"; +import { AzureMonitorOpenTelemetryOptions, OTLPExporterConfig, InstrumentationOptions } from "../../types"; + + +export class ApplicationInsightsConfig { + public enableAutoCollectExceptions: boolean; + /** OTLP Trace Exporter Configuration */ + public otlpTraceExporterConfig: OTLPExporterConfig; + /** OTLP Metric Exporter Configuration */ + public otlpMetricExporterConfig: OTLPExporterConfig; + /** OTLP Log Exporter Configuration */ + public otlpLogExporterConfig: OTLPExporterConfig; + + /** The rate of telemetry items tracked that should be transmitted (Default 1.0) */ + public samplingRatio: number; + /** Azure Monitor Exporter Configuration */ + public azureMonitorExporterOptions: AzureMonitorExporterOptions; + /** + * OpenTelemetry Instrumentations configuration included as part of Azure Monitor (azureSdk, http, mongoDb, mySql, postgreSql, redis, redis4) + */ + public instrumentationOptions: InstrumentationOptions; + + private _resource: Resource; + + public set resource(resource: Resource) { + this._resource = this._resource.merge(resource); + } + + /** + *Get OpenTelemetry Resource + */ + public get resource(): Resource { + return this._resource; + } + + /** + * Sets the state of performance tracking (enabled by default) + * if true performance counters will be collected every second and sent to Azure Monitor + */ + public enableAutoCollectPerformance: boolean; + + constructor(options?: AzureMonitorOpenTelemetryOptions) { + // Default values + this.otlpLogExporterConfig = {}; + this.otlpMetricExporterConfig = {}; + this.otlpTraceExporterConfig = {}; + this.enableAutoCollectPerformance = true; + this.enableAutoCollectExceptions = true; + this.enableAutoCollectPerformance = true; + + this.azureMonitorExporterOptions = {}; + this.samplingRatio = 1; + this.instrumentationOptions = { + http: { enabled: true }, + azureSdk: { enabled: false }, + mongoDb: { enabled: false }, + mySql: { enabled: false }, + postgreSql: { enabled: false }, + redis: { enabled: false }, + redis4: { enabled: false }, + console: { enabled: false }, + bunyan: { enabled: false }, + winston: { enabled: false }, + }; + this._resource = this._getDefaultResource(); + + // Merge JSON configuration file if available + this._mergeConfig(); + // Check for explicitly passed options when instantiating client + // This will take precedence over other settings + if (options) { + this.enableAutoCollectExceptions = + options.enableAutoCollectExceptions || this.enableAutoCollectExceptions; + this.enableAutoCollectPerformance = + options.enableAutoCollectPerformance || this.enableAutoCollectPerformance; + this.otlpTraceExporterConfig = Object.assign( + this.otlpTraceExporterConfig, + options.otlpTraceExporterConfig + ); + this.otlpMetricExporterConfig = Object.assign( + this.otlpMetricExporterConfig, + options.otlpMetricExporterConfig + ); + this.otlpLogExporterConfig = Object.assign( + this.otlpLogExporterConfig, + options.otlpLogExporterConfig + ); + + // Merge default with provided options + this.azureMonitorExporterOptions = Object.assign( + this.azureMonitorExporterOptions, + options.azureMonitorExporterOptions + ); + this.instrumentationOptions = Object.assign( + this.instrumentationOptions, + options.instrumentationOptions + ); + this.resource = Object.assign(this.resource, options.resource); + this.samplingRatio = options.samplingRatio || this.samplingRatio; + } + } + + private _mergeConfig() { + try { + const jsonConfig = JsonConfig.getInstance(); + this.enableAutoCollectPerformance = + jsonConfig.enableAutoCollectPerformance !== undefined + ? jsonConfig.enableAutoCollectPerformance + : this.enableAutoCollectPerformance; + this.enableAutoCollectExceptions = + jsonConfig.enableAutoCollectExceptions !== undefined + ? jsonConfig.enableAutoCollectExceptions + : this.enableAutoCollectExceptions; + + + this.otlpTraceExporterConfig = Object.assign( + this.otlpTraceExporterConfig, + jsonConfig.otlpTraceExporterConfig + ); + this.otlpMetricExporterConfig = Object.assign( + this.otlpMetricExporterConfig, + jsonConfig.otlpMetricExporterConfig + ); + this.otlpLogExporterConfig = Object.assign( + this.otlpLogExporterConfig, + jsonConfig.otlpLogExporterConfig + ); + + this.samplingRatio = + jsonConfig.samplingRatio !== undefined ? jsonConfig.samplingRatio : this.samplingRatio; + + this.azureMonitorExporterOptions = Object.assign( + this.azureMonitorExporterOptions, + jsonConfig.azureMonitorExporterOptions + ); + this.instrumentationOptions = Object.assign( + this.instrumentationOptions, + jsonConfig.instrumentationOptions + ); + + } catch (error) { + diag.error("Failed to load JSON config file values.", error); + } + } + + private _getDefaultResource(): Resource { + let resource = Resource.default(); + // Load resource attributes from env + const detectResourceConfig: ResourceDetectionConfig = { + detectors: [envDetectorSync], + }; + const envResource = detectResourcesSync(detectResourceConfig); + resource = resource.merge(envResource); + return resource; + } +} diff --git a/src/shared/configuration/jsonConfig.ts b/src/shared/configuration/jsonConfig.ts new file mode 100644 index 000000000..c276e9b7b --- /dev/null +++ b/src/shared/configuration/jsonConfig.ts @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as fs from "fs"; +import * as path from "path"; +import { AzureMonitorExporterOptions } from "@azure/monitor-opentelemetry-exporter"; +import { diag } from "@opentelemetry/api"; +import { AzureMonitorOpenTelemetryOptions, OTLPExporterConfig, InstrumentationOptions } from "../../types"; + + +const ENV_CONFIGURATION_FILE = "APPLICATIONINSIGHTS_CONFIGURATION_FILE"; +const ENV_CONTENT = "APPLICATIONINSIGHTS_CONFIGURATION_CONTENT"; + +export class JsonConfig { + private static _instance: JsonConfig; + public enableAutoCollectExceptions: boolean; + /** OTLP Trace Exporter Configuration */ + public otlpTraceExporterConfig?: OTLPExporterConfig; + /** OTLP Metric Exporter Configuration */ + public otlpMetricExporterConfig?: OTLPExporterConfig; + /** OTLP Log Exporter Configuration */ + public otlpLogExporterConfig?: OTLPExporterConfig; + /** + * Sets the state of performance tracking (enabled by default) + * if true performance counters will be collected every second and sent to Azure Monitor + */ + public enableAutoCollectPerformance?: boolean; + /** The rate of telemetry items tracked that should be transmitted (Default 1.0) */ + public samplingRatio?: number; + /** Azure Monitor Exporter Configuration */ + public azureMonitorExporterOptions?: AzureMonitorExporterOptions; + /** + * OpenTelemetry Instrumentations configuration included as part of Azure Monitor (azureSdk, http, mongoDb, mySql, postgreSql, redis, redis4) + */ + public instrumentationOptions?: InstrumentationOptions; + + public static getInstance() { + if (!JsonConfig._instance) { + JsonConfig._instance = new JsonConfig(); + } + return JsonConfig._instance; + } + + constructor() { + this._loadJsonFile(); + } + + private _loadJsonFile() { + let jsonString = ""; + const contentJsonConfig = process.env[ENV_CONTENT]; + // JSON string added directly in env variable + if (contentJsonConfig) { + jsonString = contentJsonConfig; + } + // JSON file + else { + const configFileName = "applicationinsights.json"; + const rootPath = path.join(__dirname, "../../../"); // Root of folder (__dirname = ../dist-esm/src) + let tempDir = path.join(rootPath, configFileName); // default + const configFile = process.env[ENV_CONFIGURATION_FILE]; + if (configFile) { + if (path.isAbsolute(configFile)) { + tempDir = configFile; + } else { + tempDir = path.join(rootPath, configFile); // Relative path to applicationinsights folder + } + } + try { + jsonString = fs.readFileSync(tempDir, "utf8"); + } catch (err) { + diag.info("Failed to read JSON config file: ", err); + } + } + try { + const jsonConfig: AzureMonitorOpenTelemetryOptions = JSON.parse(jsonString); + this.enableAutoCollectExceptions = jsonConfig.enableAutoCollectExceptions; + this.otlpLogExporterConfig = jsonConfig.otlpLogExporterConfig; + this.otlpMetricExporterConfig = jsonConfig.otlpMetricExporterConfig; + this.otlpTraceExporterConfig = jsonConfig.otlpTraceExporterConfig; + this.enableAutoCollectPerformance = jsonConfig.enableAutoCollectPerformance; + + this.azureMonitorExporterOptions = jsonConfig.azureMonitorExporterOptions; + this.samplingRatio = jsonConfig.samplingRatio; + this.instrumentationOptions = jsonConfig.instrumentationOptions; + } catch (err) { + diag.info("Missing or invalid JSON config file: ", err); + } + } +} diff --git a/src/shared/util/attributeLogRecordProcessor.ts b/src/shared/util/attributeLogRecordProcessor.ts new file mode 100644 index 000000000..47f8ac784 --- /dev/null +++ b/src/shared/util/attributeLogRecordProcessor.ts @@ -0,0 +1,21 @@ +import { LogRecord, LogRecordProcessor } from "@opentelemetry/sdk-logs"; + +export class AttributeLogProcessor implements LogRecordProcessor { + private _attributes: { [key: string]: string }; + constructor(attributes: { [key: string]: string }) { + this._attributes = attributes; + } + + // Override onEmit to apply log record attributes before exporting + onEmit(record: LogRecord) { + record.setAttributes(this._attributes); + } + + shutdown(): Promise { + return Promise.resolve(); + } + + forceFlush(): Promise { + return Promise.resolve(); + } +} diff --git a/src/shared/util/attributeSpanProcessor.ts b/src/shared/util/attributeSpanProcessor.ts new file mode 100644 index 000000000..be831eae0 --- /dev/null +++ b/src/shared/util/attributeSpanProcessor.ts @@ -0,0 +1,25 @@ +import { SpanProcessor, Span } from "@opentelemetry/sdk-trace-base"; + +export class AttributeSpanProcessor implements SpanProcessor { + private _attributes: { [key: string]: string }; + constructor(attributes: { [key: string]: string }) { + this._attributes = attributes; + } + + // Implement onStart to apply span attributes before exporting + onStart(span: Span): void { + span.setAttributes(this._attributes); + } + + onEnd(): void { + return; + } + + shutdown(): Promise { + return Promise.resolve(); + } + + forceFlush(): Promise { + return Promise.resolve(); + } +} diff --git a/src/shared/util/common.ts b/src/shared/util/common.ts new file mode 100644 index 000000000..d2202be00 --- /dev/null +++ b/src/shared/util/common.ts @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as http from "http"; + +export function ignoreOutgoingRequestHook(request: http.RequestOptions): boolean { + if (request && request.headers) { + if ( + (request.headers["User-Agent"] && + request.headers["User-Agent"] + .toString() + .indexOf("azsdk-js-monitor-opentelemetry-exporter") > -1) || + (request.headers["user-agent"] && + request.headers["user-agent"] + .toString() + .indexOf("azsdk-js-monitor-opentelemetry-exporter") > -1) + ) { + return true; + } + } + return false; +} diff --git a/Declarations/Contracts/Generated/ContextTagKeys.ts b/src/shared/util/contextTagKeys.ts similarity index 97% rename from Declarations/Contracts/Generated/ContextTagKeys.ts rename to src/shared/util/contextTagKeys.ts index 6ced98f5f..0446b2d8c 100644 --- a/Declarations/Contracts/Generated/ContextTagKeys.ts +++ b/src/shared/util/contextTagKeys.ts @@ -1,5 +1,3 @@ -// THIS FILE WAS AUTOGENERATED -"use strict"; class ContextTagKeys { /** @@ -93,7 +91,7 @@ class ContextTagKeys { public userAuthUserId: string; /** - * Name of the role the application is a part of. For Azure environment, this should be initialized with + * Name of the role the application is a part of. For Azure environment, this should be initialized with * [Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment]::CurrentRoleInstance.Role.Name * See more details here: https://dzone.com/articles/accessing-azure-role-0 * It is recommended that you initialize environment variable with this value during machine startup, and then set context field from environment variable @@ -102,7 +100,7 @@ class ContextTagKeys { public cloudRole: string; /** - * Name of the instance where the application is running. For Azure environment, this should be initialized with + * Name of the instance where the application is running. For Azure environment, this should be initialized with * [Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment]::CurrentRoleInstance.Id * See more details here: https://dzone.com/articles/accessing-azure-role-0 * It is recommended that you initialize environment variable with this value during machine startup, and then set context field from environment variable @@ -152,4 +150,4 @@ class ContextTagKeys { this.internalNodeName = "ai.internal.nodeName"; } } -export = ContextTagKeys; +export = ContextTagKeys; \ No newline at end of file diff --git a/Library/FileSystemHelper.ts b/src/shared/util/fileSystemHelper.ts similarity index 87% rename from Library/FileSystemHelper.ts rename to src/shared/util/fileSystemHelper.ts index 8277fbbaa..b17f9d56e 100644 --- a/Library/FileSystemHelper.ts +++ b/src/shared/util/fileSystemHelper.ts @@ -1,5 +1,8 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + import * as fs from "fs"; -import path = require("path"); +import * as path from "path"; import { promisify } from "util"; export const statAsync = promisify(fs.stat); @@ -53,24 +56,23 @@ export const getShallowDirectorySize = async (directory: string): Promise { - let files = fs.readdirSync(directory); + const files = fs.readdirSync(directory); let totalSize = 0; for (let i = 0; i < files.length; i++) { totalSize += fs.statSync(path.join(directory, files[i])).size; } return totalSize; -} +}; /** -* Computes the size (in bytes) of a file asynchronously. -*/ + * Computes the size (in bytes) of a file asynchronously. + */ export const getShallowFileSize = async (filePath: string): Promise => { const fileStats = await statAsync(filePath); if (fileStats.isFile()) { return fileStats.size; } -} - +}; diff --git a/src/shared/util/index.ts b/src/shared/util/index.ts new file mode 100644 index 000000000..454e41732 --- /dev/null +++ b/src/shared/util/index.ts @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +export { + accessAsync, + appendFileAsync, + confirmDirExists, + getShallowDirectorySize, + getShallowDirectorySizeSync, + getShallowFileSize, + readdirAsync, + readFileAsync, + statAsync, + lstatAsync, + mkdirAsync, + writeFileAsync, + unlinkAsync, +} from "./fileSystemHelper"; +export { Util } from "./util"; diff --git a/src/shared/util/util.ts b/src/shared/util/util.ts new file mode 100644 index 000000000..b1e6758d5 --- /dev/null +++ b/src/shared/util/util.ts @@ -0,0 +1,139 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { diag } from "@opentelemetry/api"; + + +export class Util { + private static _instance: Util; + private _listenerAttached = false; + + public isNodeExit = false; + + static getInstance() { + if (!Util._instance) { + Util._instance = new Util(); + } + return Util._instance; + } + + public constructor() { + this._addCloseHandler(); + } + + /** + * helper method to trim strings (IE8 does not implement String.prototype.trim) + */ + public trim(str: string): string { + if (typeof str === "string") { + return str.replace(/^\s+|\s+$/g, ""); + } + return ""; + } + + /** + * Check if an object is of type Array + */ + public isArray(obj: any): boolean { + return Object.prototype.toString.call(obj) === "[object Array]"; + } + + /** + * Check if an object is of type Error + */ + public isError(obj: any): boolean { + return obj instanceof Error; + } + + public isPrimitive(input: any): boolean { + const propType = typeof input; + return propType === "string" || propType === "number" || propType === "boolean"; + } + + /** + * Check if an object is of type Date + */ + public isDate(obj: any): boolean { + return Object.prototype.toString.call(obj) === "[object Date]"; + } + + /** + * Convert milliseconds to Breeze expected time. + * @internal + */ + public msToTimeSpan(ms: number): string { + let totalms = ms; + if (Number.isNaN(totalms) || totalms < 0 || !Number.isFinite(ms)) { + totalms = 0; + } + + let sec = ((totalms / 1000) % 60).toFixed(7).replace(/0{0,4}$/, ""); + let min = `${Math.floor(totalms / (1000 * 60)) % 60}`; + let hour = `${Math.floor(totalms / (1000 * 60 * 60)) % 24}`; + const days = Math.floor(totalms / (1000 * 60 * 60 * 24)); + + sec = sec.indexOf(".") < 2 ? `0${sec}` : sec; + min = min.length < 2 ? `0${min}` : min; + hour = hour.length < 2 ? `0${hour}` : hour; + const daysText = days > 0 ? `${days}.` : ""; + + return `${daysText + hour}:${min}:${sec}`; + } + + /** + * Using JSON.stringify, by default Errors do not serialize to something useful: + * Simplify a generic Node Error into a simpler map for customDimensions + * Custom errors can still implement toJSON to override this functionality + */ + protected extractError(err: Error): { message: string; code: string } { + // Error is often subclassed so may have code OR id properties: + // https://nodejs.org/api/errors.html#errors_error_code + const looseError = err as any; + return { + message: err.message, + code: looseError.code || looseError.id || "", + }; + } + + public isDbDependency(dependencyType: string) { + return ( + dependencyType.indexOf("SQL") > -1 || + dependencyType === "mysql" || + dependencyType === "postgresql" || + dependencyType === "mongodb" || + dependencyType === "redis" + ); + } + + /** + * Returns string representation of an object suitable for diagnostics diag. + */ + public dumpObj(object: any): string { + const objectTypeDump: string = Object["prototype"].toString.call(object); + let propertyValueDump = ""; + if (objectTypeDump === "[object Error]") { + propertyValueDump = `{ stack: '${object.stack}', message: '${object.message}', name: '${object.name}'`; + } else { + propertyValueDump = JSON.stringify(object); + } + + return objectTypeDump + propertyValueDump; + } + + public stringify(payload: any) { + try { + return JSON.stringify(payload); + } catch (error) { + diag.warn("Failed to serialize payload", error, payload); + } + } + + private _addCloseHandler() { + if (!this._listenerAttached) { + process.on("exit", () => { + this.isNodeExit = true; + }); + this._listenerAttached = true; + } + } +} diff --git a/src/shim/applicationinsights.ts b/src/shim/applicationinsights.ts new file mode 100644 index 000000000..d4b8c9d8f --- /dev/null +++ b/src/shim/applicationinsights.ts @@ -0,0 +1,301 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as http from "http"; +import * as azureFunctionsTypes from "@azure/functions"; +import { DiagConsoleLogger, SpanContext, diag } from "@opentelemetry/api"; +import { Span } from "@opentelemetry/sdk-trace-base"; +import { CorrelationContextManager } from "./correlationContextManager"; +import { ICorrelationContext, HttpRequest, DistributedTracingModes } from "./types"; +import { TelemetryClient } from "./telemetryClient"; +import * as Contracts from "../declarations/contracts"; +import { Util } from "../shared/util"; + +// We export these imports so that SDK users may use these classes directly. +// They're exposed using "export import" so that types are passed along as expected +export { Contracts, DistributedTracingModes, HttpRequest, TelemetryClient }; + +/** + * The default client, initialized when setup was called. To initialize a different client + * with its own configuration, use `new TelemetryClient(instrumentationKey?)`. + */ +export let defaultClient: TelemetryClient; + +/** + * Initializes the default client. Should be called after setting + * configuration options. + * + * @param setupString the Connection String or Instrumentation Key to use. Optional, if + * this is not specified, the value will be read from the environment + * variable APPLICATIONINSIGHTS_CONNECTION_STRING or APPINSIGHTS_INSTRUMENTATIONKEY. + * @returns {Configuration} the configuration class to initialize + * and start the SDK. + */ +export function setup(setupString?: string) { + if (!defaultClient) { + defaultClient = new TelemetryClient(setupString); + } else { + defaultClient.pushWarningToLog("Setup has already been called once. To set up a new client, please use TelemetryClient instead.") + } + return Configuration; +} + +/** + * Starts automatic collection of telemetry. Prior to calling start no + * telemetry will be *automatically* collected, though manual collection + * is enabled. + * @returns {ApplicationInsights} this class + */ +export function start() { + try { + if (!defaultClient) { + diag.setLogger(new DiagConsoleLogger()); + diag.warn("Start cannot be called before setup. Please call setup() first."); + } else { + defaultClient.initialize(); + } + return Configuration; + } catch (error) { + diag.warn(`Failed to start default client: ${Util.getInstance().dumpObj(error)}`); + } +} + +/** + * Returns an object that is shared across all code handling a given request. + * This can be used similarly to thread-local storage in other languages. + * Properties set on this object will be available to telemetry processors. + * + * Do not store sensitive information here. + * Custom properties set on this object can be exposed in a future SDK + * release via outgoing HTTP headers. + * This is to allow for correlating data cross-component. + * + * This method will return null if automatic dependency correlation is disabled. + * @returns A plain object for request storage or null if automatic dependency correlation is disabled. + */ +export function getCorrelationContext(): ICorrelationContext { + return CorrelationContextManager.getCurrentContext(); +} + +/** + * **(Experimental!)** + * Starts a fresh context or propagates the current internal one. + */ +export function startOperation( + arg1: azureFunctionsTypes.Context | (http.IncomingMessage | azureFunctionsTypes.HttpRequest) | SpanContext | Span, + arg2?: HttpRequest | string +): ICorrelationContext | null { + return CorrelationContextManager.startOperation(arg1, arg2); +} + +/** + * Returns a function that will get the same correlation context within its + * function body as the code executing this function. + * Use this method if automatic dependency correlation is not propagating + * correctly to an asynchronous callback. + */ +export function wrapWithCorrelationContext(fn: T, context?: ICorrelationContext): T { + return CorrelationContextManager.wrapCallback(fn, context); +} + +/** + * The active configuration for global SDK behaviors, such as auto collection. + */ +export class Configuration { + // Convenience shortcut to ApplicationInsights.start + public static start = start; + + /** + * Only W3C traing mode is currently suppported so this method informs the user if they attempt to set the value. + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public static setDistributedTracingMode(value: number) { + if (defaultClient) { + defaultClient.config.distributedTracingMode = value; + } + return Configuration; + } + + /** + * Sets the state of console and logger tracking (enabled by default for third-party loggers only) + * @param value if true logger activity will be sent to Application Insights + * @param collectConsoleLog if true, logger autocollection will include console.log calls (default false) + * @returns {Configuration} this class + */ + public static setAutoCollectConsole(value: boolean, collectConsoleLog = false) { + if (defaultClient) { + defaultClient.config.enableAutoCollectExternalLoggers = value; + defaultClient.config.enableAutoCollectConsole = collectConsoleLog; + } + return Configuration; + } + + /** + * Sets the state of exception tracking (enabled by default) + * @param value if true uncaught exceptions will be sent to Application Insights + * @returns {Configuration} this class + */ + public static setAutoCollectExceptions(value: boolean) { + if (defaultClient) { + defaultClient.config.enableAutoCollectExceptions = value; + } + return Configuration; + } + + /** + * Sets the state of performance tracking (enabled by default) + * @param value if true performance counters will be collected every second and sent to Application Insights + * @param collectExtendedMetrics if true, extended metrics counters will be collected every minute and sent to Application Insights + * @returns {Configuration} this class + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public static setAutoCollectPerformance(value: boolean, collectExtendedMetrics: any) { + if (defaultClient) { + defaultClient.config.enableAutoCollectPerformance = value; + defaultClient.config.enableAutoCollectExtendedMetrics = collectExtendedMetrics; + } + return Configuration; + } + + /** + * Sets the state of pre aggregated metrics tracking (enabled by default) + * @param value if true pre aggregated metrics will be collected every minute and sent to Application Insights + * @returns {Configuration} this class + */ + public static setAutoCollectPreAggregatedMetrics(value: boolean) { + if (defaultClient) { + defaultClient.config.enableAutoCollectPreAggregatedMetrics = value; + } + return Configuration; + } + + /** + * Sets the state of request tracking (enabled by default) + * @param value if true HeartBeat metric data will be collected every 15 minutes and sent to Application Insights + * @returns {Configuration} this class + */ + public static setAutoCollectHeartbeat(value: boolean) { + if (defaultClient) { + defaultClient.config.enableAutoCollectHeartbeat = value; + } + return Configuration; + } + + /** + * Sets the state of Web snippet injection + * @param value if true Web snippet will try to be injected in server response + * @param WebSnippetConnectionString if provided, Web snippet injection will use this ConnectionString. Default to use the connectionString in Node.js app initialization. + * @returns {Configuration} this class + */ + public static enableWebInstrumentation(value: boolean, WebSnippetConnectionString?: string) { + if (defaultClient) { + defaultClient.config.enableWebInstrumentation = value; + defaultClient.config.webInstrumentationConnectionString = WebSnippetConnectionString; + } + return Configuration; + } + + /** + * Sets the state of request tracking (enabled by default) + * @param value if true requests will be sent to Application Insights + * @returns {Configuration} this class + */ + public static setAutoCollectRequests(value: boolean) { + if (defaultClient) { + defaultClient.config.enableAutoCollectRequests = value; + } + return Configuration; + } + + /** + * Sets the state of dependency tracking (enabled by default) + * @param value if true dependencies will be sent to Application Insights + * @returns {Configuration} this class + */ + public static setAutoCollectDependencies(value: boolean) { + if (defaultClient) { + defaultClient.config.enableAutoCollectDependencies = value; + } + return Configuration; + } + + /** + * Sets the state of automatic dependency correlation (enabled by default) + * @param value if true dependencies will be correlated with requests + * @param useAsyncHooks if true, forces use of experimental async_hooks module to provide correlation. If false, instead uses only patching-based techniques. If left blank, the best option is chosen for you based on your version of Node.js. + * @returns {Configuration} this class + */ + public static setAutoDependencyCorrelation(value: boolean, useAsyncHooks?: boolean) { + if (defaultClient) { + defaultClient.config.enableAutoDependencyCorrelation = value; + defaultClient.config.enableUseAsyncHooks = useAsyncHooks; + } + return Configuration; + } + + /** + * Enable or disable disk-backed retry caching to cache events when client is offline (enabled by default) + * Note that this method only applies to the default client. Disk-backed retry caching is disabled by default for additional clients. + * For enable for additional clients, use client.channel.setUseDiskRetryCaching(true). + * These cached events are stored in your system or user's temporary directory and access restricted to your user when possible. + * @param value if true events that occured while client is offline will be cached on disk + * @param resendInterval The wait interval for resending cached events. + * @param maxBytesOnDisk The maximum size (in bytes) that the created temporary directory for cache events can grow to, before caching is disabled. + * @returns {Configuration} this class + */ + public static setUseDiskRetryCaching(value: boolean, resendInterval?: number, maxBytesOnDisk?: number) { + if (defaultClient) { + defaultClient.config.enableUseDiskRetryCaching = value; + defaultClient.config.enableResendInterval = resendInterval; + defaultClient.config.enableMaxBytesOnDisk = maxBytesOnDisk; + } + return Configuration; + } + + /** + * Enables debug and warning Logger for AppInsights itself. + * @param enableDebugLogger if true, enables debug Logger + * @param enableWarningLogger if true, enables warning Logger + * @returns {Configuration} this class + */ + public static setInternalLogging(enableDebugLogger = false, enableWarningLogger = true) { + if (defaultClient) { + defaultClient.config.enableInternalDebugLogging = enableDebugLogger; + defaultClient.config.enableInternalWarningLogging = enableWarningLogger; + } + return Configuration; + } + + /** + * Enable automatic incoming request tracking when using Azure Functions + * @param value if true auto collection of incoming requests will be enabled + * @returns {Configuration} this class + */ + public static setAutoCollectIncomingRequestAzureFunctions(value: boolean) { + if (defaultClient) { + defaultClient.config.enableAutoCollectIncomingRequestAzureFunctions = value; + } + return Configuration; + } + + /** + * Enables communication with Application Insights Live Metrics. + * @param enable if true, enables communication with the live metrics service + */ + public static setSendLiveMetrics(enable = false) { + if (defaultClient) { + defaultClient.config.enableSendLiveMetrics = enable; + } + return Configuration; + } +} + +/** + * Disposes the default client and all the auto collectors so they can be reinitialized with different configuration + */ +export function dispose() { + if (defaultClient) { + defaultClient.shutdown(); + } + defaultClient = null; +} diff --git a/src/shim/context.ts b/src/shim/context.ts new file mode 100644 index 000000000..30a21331c --- /dev/null +++ b/src/shim/context.ts @@ -0,0 +1,14 @@ +import ContextTagKeys = require("../shared/util/contextTagKeys"); + +export class Context { + public keys: ContextTagKeys; + public tags: { [key: string]: string }; + public static DefaultRoleName = "Web"; + public static appVersion: { [path: string]: string } = {}; + public static sdkVersion: string = null; + + constructor(packageJsonPath?: string) { + this.keys = new ContextTagKeys(); + this.tags = <{ [key: string]: string }>{}; + } +} diff --git a/src/shim/correlationContextManager.ts b/src/shim/correlationContextManager.ts new file mode 100644 index 000000000..717116428 --- /dev/null +++ b/src/shim/correlationContextManager.ts @@ -0,0 +1,279 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as events from "events"; +import * as http from "http"; +import * as azureFunctionsTypes from "@azure/functions"; +import { context, SpanContext, trace, Context, diag } from "@opentelemetry/api"; +import { TraceState } from "@opentelemetry/core"; +import { Span } from "@opentelemetry/sdk-trace-base"; +import { ICorrelationContext, ITraceparent, ITracestate, HttpRequest, ICustomProperties } from "./types"; +import { Util } from "../shared/util"; + + +const CONTEXT_NAME = "ApplicationInsights-Context"; + +export class CorrelationContextManager { + // Context is taken from the trace API and not context API so we need a flag to disable this functionality + private static _isDisabled = false; + + /** + * Converts an OpenTelemetry SpanContext object to an ICorrelationContext object for backwards compatibility with ApplicationInsights + * @param spanContext OpenTelmetry SpanContext object + * @param parentId spanId of the parent span + * @param name OpenTelemetry human readable name of the span + * @param traceState String of key value pairs for additional trace context + * @returns ICorrelationContext object + */ + public static spanToContextObject(spanContext: SpanContext, parentId?: string, name?: string, traceState?: TraceState): ICorrelationContext { + // Generate a basic ITraceparent to satisfy the ICorrelationContext interface + const traceContext: ITraceparent = { + legacyRootId: "", + traceId: spanContext?.traceId, + spanId: spanContext?.spanId, + traceFlag: spanContext?.traceFlags?.toString(), + parentId: parentId, + version: "00" + }; + + return this.generateContextObject(traceContext.traceId, traceContext.parentId, name, traceContext, traceState); + } + + /** + * Provides the current Context. + * The context is the most recent one entered into for the current + * logical chain of execution, including across asynchronous calls. + * @returns ICorrelationContext object + */ + public static getCurrentContext(): ICorrelationContext | null { + if (!this._isDisabled) { + // Gets the active span and extracts the context to populate and return the ICorrelationContext object + let activeSpan: Span = trace.getSpan(context.active()) as Span; + + // If no active span exists, create a new one. This is needed if runWithContext() is executed without an active span + if (!activeSpan) { + activeSpan = trace.getTracer(CONTEXT_NAME).startSpan(CONTEXT_NAME) as Span; + } + const traceStateObj: TraceState = new TraceState(activeSpan?.spanContext()?.traceState?.serialize()); + + return this.spanToContextObject(activeSpan?.spanContext(), activeSpan?.parentSpanId, activeSpan?.name, traceStateObj); + } + return null; + } + + /** + * Helper to generate objects conforming to the CorrelationContext interface + * @param operationId String assigned to a series of related telemetry items - equivalent to OpenTelemetry traceId + * @param parentId spanId of the parent span + * @param operationName Human readable name of the span + * @param traceparent Context conveying string in the format version-traceId-spanId-traceFlag + * @param tracestate String of key value pairs for additional trace context + * @returns ICorrelationContext object + */ + public static generateContextObject( + operationId: string, + parentId?: string, + operationName?: string, + traceparent?: ITraceparent, + tracestate?: TraceState + ): ICorrelationContext { + // Cast OpenTelemetry TraceState object to ITracestate object + const ITraceState: ITracestate = { + fieldmap: tracestate?.serialize()?.split(",") + }; + + return { + operation: { + name: operationName, + id: operationId, + parentId: parentId, + traceparent: traceparent, + tracestate: ITraceState, + }, + // Headers are not being used so custom properties will always be stubbed out + customProperties: { + getProperty(prop: string) { return "" }, + setProperty(prop: string) { return "" }, + } as ICustomProperties, + } + } + + /** + * Runs a function inside a given Context. + * All logical children of the execution path that entered this Context + * will receive this Context object on calls to GetCurrentContext. + * @param ctx Context to run the function within + * @param fn Function to run within the stated context + * @returns any + */ + public static runWithContext(ctx: ICorrelationContext, fn: () => any): any { + // Creates a new Context object containing the values from the ICorrelationContext object, then sets the active context to the new Context + try { + const newContext: Context = trace.setSpanContext(context.active(), this._contextObjectToSpanContext(ctx)); + return context.with(newContext, fn); + } catch (error) { + diag.warn("Error binding to session context", Util.getInstance().dumpObj(error)); + } + return fn(); + } + + /** + * Wrapper for cls-hooked bindEmitter method + * @param emitter emitter to bind to the current context + */ + public static wrapEmitter(emitter: events.EventEmitter): void { + try { + context.bind(context.active(), emitter); + } catch (error) { + diag.warn("Error binding to session context", Util.getInstance().dumpObj(error)); + } + } + + /** + * Patches a callback to restore the correct Context when getCurrentContext + * is run within it. This is necessary if automatic correlation fails to work + * with user-included libraries. + * The supplied callback will be given the same context that was present for + * the call to wrapCallback + * @param fn Function to be wrapped in the provided context + * @param ctx Context to bind the function to + * @returns Generic type T + */ + public static wrapCallback(fn: T, ctx?: ICorrelationContext): T { + try { + if (ctx) { + // Create the new context and bind it if context is passed + const newContext: Context = trace.setSpanContext(context.active(), this._contextObjectToSpanContext(ctx)); + return context.bind(newContext, fn); + } + // If no context is passed, bind to the current context + return context.bind(context.active(), fn); + } catch (error) { + diag.error("Error binding to session context", Util.getInstance().dumpObj(error)); + return fn; + } + } + + /** + * Enables the CorrelationContextManager + * @param forceClsHooked unused parameter used to satisfy backward compatibility + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public static enable(forceClsHooked?: boolean) { + diag.info("Enabling the context manager is no longer necessary and this method is a no-op."); + } + + /** + * Creates a new correlation context + * @param input Any kind of object we can extract context information from + * @param request HTTP request we can pull context information from in the form of the request's headers + * @returns IcorrelationContext object + */ + public static startOperation( + input: azureFunctionsTypes.Context | (http.IncomingMessage | azureFunctionsTypes.HttpRequest) | SpanContext | Span, + request?: HttpRequest | string + ): ICorrelationContext { + const traceContext = input && (input as azureFunctionsTypes.Context).traceContext || null; + const span = input && (input as Span).spanContext ? input as Span : null; + const spanContext = input && (input as SpanContext).traceId ? input as SpanContext : null; + const headers = input && (input as http.IncomingMessage | azureFunctionsTypes.HttpRequest).headers; + + if (span) { + trace.setSpanContext(context.active(), span.spanContext()); + return this.spanToContextObject( + span.spanContext(), + span.parentSpanId, + ); + } + + if (spanContext) { + trace.setSpanContext(context.active(), spanContext); + return this.spanToContextObject( + spanContext, + ); + } + + if (traceContext || headers) { + let traceparent = null; + let tracestate = null; + if (traceContext) { + // Use the headers on the request from Azure Functions to set the active context + const azureFnRequest = request as azureFunctionsTypes.HttpRequest; + + // If the traceparent isn't defined on the azure function headers set it to the request-id + if (azureFnRequest?.headers) { + // request-id is a GUID-based unique identifier for the request + traceparent = azureFnRequest.headers.traceparent ? azureFnRequest.headers.traceparent : azureFnRequest.headers["request-id"]; + tracestate = azureFnRequest.headers.tracestate; + } + + if (!traceparent) { + traceparent = traceContext.traceparent; + } + if (!tracestate) { + tracestate = traceContext.tracestate; + } + } + + // If headers is defined instead of traceContext, use the headers to set the traceparent and tracestate + if (headers) { + traceparent = headers.traceparent ? headers.traceparent.toString() : null; + tracestate = headers.tracestate ? headers.tracestate.toString() : tracestate; + } + + const traceArray: string[] = traceparent?.split("-"); + + const tracestateObj: TraceState = new TraceState(); + tracestate?.split(",").forEach((pair) => { + const kv = pair.split("="); + tracestateObj.set(kv[0], kv[1]); + }); + + return this.generateContextObject( + traceArray[1], + traceArray[2], + null, + { + legacyRootId: "", + parentId: "", + spanId: traceArray[2], + traceFlag: "", + traceId: traceArray[1], + version: "00", + }, + tracestateObj + ); + } + diag.warn("startOperation was called with invalid arguments"); + return null; + } + + /** + * Disables the CorrelationContextManager + */ + public static disable() { + diag.warn("It will not be possible to re-enable the current context manager after disabling it!"); + this._isDisabled = true; + context.disable(); + } + + /** + * Resets the namespace + */ + public static reset() { + diag.info("This is a no-op and exists only for compatibility reasons."); + } + + /** + * Converts ApplicationInsights' ICorrelationContext to an OpenTelemetry SpanContext + * @param ctx ICorrelationContext object to convert to a SpanContext + * @returns OpenTelemetry SpanContext + */ + private static _contextObjectToSpanContext(ctx: ICorrelationContext): SpanContext { + return { + traceId: ctx.operation.id, + spanId: ctx.operation.traceparent?.spanId ?? "", + traceFlags: ctx.operation.traceparent?.traceFlag ? Number(ctx.operation.traceparent?.traceFlag) : undefined, + }; + } +} diff --git a/src/shim/logsApi.ts b/src/shim/logsApi.ts new file mode 100644 index 000000000..eff478a05 --- /dev/null +++ b/src/shim/logsApi.ts @@ -0,0 +1,249 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { Logger as OtelLogger, LogRecord } from "@opentelemetry/api-logs"; +import { LogRecord as SDKLogRecord } from "@opentelemetry/sdk-logs"; +import { Attributes, diag } from "@opentelemetry/api"; +import { IdGenerator, RandomIdGenerator } from "@opentelemetry/sdk-trace-base"; + +import * as Contracts from "../declarations/contracts"; +import { + AvailabilityData, + KnownSeverityLevel, + MessageData, + MonitorDomain, + PageViewData, + TelemetryEventData, + TelemetryExceptionData, + TelemetryExceptionDetails +} from "../declarations/generated"; +import { Util } from "../shared/util"; +import { parseStack } from "../logs/exceptions"; + +/** + * Log manual API to generate Application Insights telemetry + */ +export class LogApi { + private _idGenerator: IdGenerator; + private _logger: OtelLogger; + + /** + * Constructs a new client of LogApi + */ + constructor(logger: OtelLogger) { + this._idGenerator = new RandomIdGenerator(); + this._logger = logger; + } + + /** + * Log information about availability of an application + * @param telemetry Object encapsulating tracking options + */ + public trackAvailability(telemetry: Contracts.AvailabilityTelemetry): void { + try { + const logRecord = this._availabilityToLogRecord( + telemetry + ); + this._logger.emit(logRecord); + } catch (err) { + diag.error("Failed to send telemetry.", err); + } + } + + /** + * Log a page view + * @param telemetry Object encapsulating tracking options + */ + public trackPageView(telemetry: Contracts.PageViewTelemetry): void { + try { + const logRecord = this._pageViewToLogRecord( + telemetry + ); + this._logger.emit(logRecord); + } catch (err) { + diag.error("Failed to send telemetry.", err); + } + } + + /** + * Log a trace message + * @param telemetry Object encapsulating tracking options + */ + public trackTrace(telemetry: Contracts.TraceTelemetry): void { + try { + const logRecord = this._traceToLogRecord(telemetry) as SDKLogRecord; + this._logger.emit(logRecord); + } catch (err) { + diag.error("Failed to send telemetry.", err); + } + } + + /** + * Log an exception + * @param telemetry Object encapsulating tracking options + */ + public trackException(telemetry: Contracts.ExceptionTelemetry): void { + if (telemetry && telemetry.exception && !Util.getInstance().isError(telemetry.exception)) { + telemetry.exception = new Error(telemetry.exception.toString()); + } + try { + const logRecord = this._exceptionToLogRecord( + telemetry + ) as SDKLogRecord; + this._logger.emit(logRecord); + } catch (err) { + diag.error("Failed to send telemetry.", err); + } + } + + /** + * Log a user action or other occurrence. + * @param telemetry Object encapsulating tracking options + */ + public trackEvent(telemetry: Contracts.EventTelemetry): void { + try { + const logRecord = this._eventToLogRecord(telemetry); + this._logger.emit(logRecord); + } catch (err) { + diag.error("Failed to send telemetry.", err); + } + } + + private _telemetryToLogRecord( + telemetry: Contracts.Telemetry, + baseType: string, + baseData: MonitorDomain + ): LogRecord { + try { + const attributes: Attributes = {}; + if (telemetry.properties) { + for (const [key, value] of Object.entries(telemetry.properties)) { + // Serialize Error objects as strings to avoid serialization errors + if (value?.constructor.name === "Error") { + attributes[key] = Util.getInstance().stringify( + { + name: value.name, + message: value.message, + stack: value.stack, + cause: value.cause, + } + ); + } else { + attributes[key] = typeof value === 'object' + ? Util.getInstance().stringify(value) + : value; + } + } + } + + const record: LogRecord = { attributes: attributes, body: Util.getInstance().stringify(baseData) }; + record.attributes["_MS.baseType"] = baseType; + return record; + } + catch (err) { + diag.warn("Failed to convert telemetry event to Log Record.", err); + } + } + + /** + * Availability Log to LogRecord parsing. + * @internal + */ + private _availabilityToLogRecord( + telemetry: Contracts.AvailabilityTelemetry + ): LogRecord { + const baseType = "AvailabilityData"; + const baseData: AvailabilityData = { + id: telemetry.id || this._idGenerator.generateSpanId(), + name: telemetry.name, + duration: Util.getInstance().msToTimeSpan(telemetry.duration), + success: telemetry.success, + runLocation: telemetry.runLocation, + message: telemetry.message, + measurements: telemetry.measurements, + version: 2, + }; + const record = this._telemetryToLogRecord(telemetry, baseType, baseData); + return record; + } + + /** + * Exception to LogRecord parsing. + * @internal + */ + private _exceptionToLogRecord( + telemetry: Contracts.ExceptionTelemetry + ): LogRecord { + const baseType = "ExceptionData"; + const stack = telemetry.exception["stack"]; + const parsedStack = parseStack(stack); + const exceptionDetails: TelemetryExceptionDetails = { + message: telemetry.exception.message, + typeName: telemetry.exception.name, + parsedStack: parsedStack, + hasFullStack: Util.getInstance().isArray(parsedStack) && parsedStack.length > 0, + }; + + const baseData: TelemetryExceptionData = { + severityLevel: telemetry.severity || KnownSeverityLevel.Error, + exceptions: [exceptionDetails], + measurements: telemetry.measurements, + version: 2, + }; + const record = this._telemetryToLogRecord(telemetry, baseType, baseData); + return record; + } + + /** + * Trace to LogRecord parsing. + * @internal + */ + private _traceToLogRecord(telemetry: Contracts.TraceTelemetry): LogRecord { + const baseType = "MessageData"; + const baseData: MessageData = { + message: telemetry.message, + severityLevel: telemetry.severity || KnownSeverityLevel.Information, + measurements: telemetry.measurements, + version: 2, + }; + const record = this._telemetryToLogRecord(telemetry, baseType, baseData); + return record; + } + + /** + * PageView to LogRecord parsing. + * @internal + */ + private _pageViewToLogRecord( + telemetry: Contracts.PageViewTelemetry + ): LogRecord { + const baseType = "PageViewData"; + const baseData: PageViewData = { + id: telemetry.id || this._idGenerator.generateSpanId(), + name: telemetry.name, + duration: Util.getInstance().msToTimeSpan(telemetry.duration), + url: telemetry.url, + referredUri: telemetry.referredUri, + measurements: telemetry.measurements, + version: 2, + }; + + const record = this._telemetryToLogRecord(telemetry, baseType, baseData); + return record; + } + + /** + * Event to LogRecord parsing. + * @internal + */ + private _eventToLogRecord(telemetry: Contracts.EventTelemetry): LogRecord { + const baseType = "EventData"; + const baseData: TelemetryEventData = { + name: telemetry.name, + measurements: telemetry.measurements, + version: 2, + }; + const record = this._telemetryToLogRecord(telemetry, baseType, baseData); + return record; + } +} diff --git a/src/shim/nodeClient.ts b/src/shim/nodeClient.ts new file mode 100644 index 000000000..684fa7bec --- /dev/null +++ b/src/shim/nodeClient.ts @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { diag } from "@opentelemetry/api"; +import { Contracts, TelemetryClient } from "./applicationinsights"; + +class NodeClient extends TelemetryClient { + /** + * Log RequestTelemetry from HTTP request and response. This method will log immediately without waiting for request completion + * and it requires duration parameter to be specified on NodeHttpRequestTelemetry object. + * Use trackNodeHttpRequest function to log the telemetry after request completion + * @param telemetry Object encapsulating incoming request, response and duration information + */ + public trackNodeHttpRequestSync(telemetry: Contracts.NodeHttpRequestTelemetry) { + diag.warn("trackNodeHttpRequestSync is not implemented and is a no-op."); + } + + /** + * Log RequestTelemetry from HTTP request and response. This method will `follow` the request to completion. + * Use trackNodeHttpRequestSync function to log telemetry immediately without waiting for request completion + * @param telemetry Object encapsulating incoming request and response information + */ + public trackNodeHttpRequest(telemetry: Contracts.NodeHttpRequestTelemetry) { + diag.warn("trackNodeHttpRequest is not implemented and is a no-op."); + } + + /** + * Log DependencyTelemetry from outgoing HTTP request. This method will instrument the outgoing request and append + * the specified headers and will log the telemetry when outgoing request is complete + * @param telemetry Object encapsulating outgoing request information + */ + public trackNodeHttpDependency(telemetry: Contracts.NodeHttpRequestTelemetry) { + diag.warn("trackNodeHttpDependency is not implemented and is a no-op."); + } +} + +export = NodeClient; diff --git a/src/shim/shim-config.ts b/src/shim/shim-config.ts new file mode 100644 index 000000000..cc971ed29 --- /dev/null +++ b/src/shim/shim-config.ts @@ -0,0 +1,372 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for details. +import http = require("http"); +import https = require("https"); +import azureCoreAuth = require("@azure/core-auth"); +import { diag } from "@opentelemetry/api"; +import { HttpInstrumentationConfig } from "@opentelemetry/instrumentation-http"; +import { DistributedTracingModes, IConfig, IDisabledExtendedMetrics, IWebInstrumentationConfig, UNSUPPORTED_MSG } from "./types"; +import { ShimJsonConfig } from "./shim-jsonConfig"; +import { AzureMonitorOpenTelemetryOptions, InstrumentationOptions, InstrumentationOptionsType } from "../types"; + +const ENV_azurePrefix = "APPSETTING_"; // Azure adds this prefix to all environment variables +const ENV_iKey = "APPINSIGHTS_INSTRUMENTATIONKEY"; // This key is provided in the readme +const legacy_ENV_iKey = "APPINSIGHTS_INSTRUMENTATION_KEY"; +const ENV_profileQueryEndpoint = "APPINSIGHTS_PROFILE_QUERY_ENDPOINT"; +const ENV_quickPulseHost = "APPINSIGHTS_QUICKPULSE_HOST"; +const ENV_nativeMetricsDisableAll = "APPLICATION_INSIGHTS_DISABLE_ALL_EXTENDED_METRICS"; +const ENV_nativeMetricsDisablers = "APPLICATION_INSIGHTS_DISABLE_EXTENDED_METRIC"; +class Config implements IConfig { + public connectionString: string; + public endpointUrl: string; + public maxBatchSize: number; + public maxBatchIntervalMs: number; + public disableAppInsights: boolean; + public samplingPercentage: number; + public correlationHeaderExcludedDomains: string[]; + public proxyHttpUrl: string; + public proxyHttpsUrl: string; + public httpAgent: http.Agent; + public httpsAgent: https.Agent; + public ignoreLegacyHeaders: boolean; + public aadTokenCredential?: azureCoreAuth.TokenCredential; + public enableAutoCollectConsole: boolean; + public enableLoggerErrorToTrace: boolean; + public enableAutoCollectExceptions: boolean; + public enableAutoCollectPerformance: boolean; + public enableAutoCollectExternalLoggers: boolean; + public enableAutoCollectPreAggregatedMetrics: boolean; + public enableAutoCollectHeartbeat: boolean; + public enableAutoCollectRequests: boolean; + public enableAutoCollectDependencies: boolean; + public enableAutoDependencyCorrelation: boolean; + public enableAutoCollectIncomingRequestAzureFunctions: boolean; + public enableSendLiveMetrics: boolean; + public enableUseDiskRetryCaching: boolean; + public enableUseAsyncHooks: boolean; + public distributedTracingMode: DistributedTracingModes; + + public enableAutoCollectExtendedMetrics: boolean | IDisabledExtendedMetrics; + public enableResendInterval: number; + public enableMaxBytesOnDisk: number; + public enableInternalDebugLogging: boolean; + public enableInternalWarningLogging: boolean; + public disableAllExtendedMetrics: boolean; + public extendedMetricDisablers: string; + public quickPulseHost: string; + public enableWebInstrumentation: boolean; + public webInstrumentationConfig: IWebInstrumentationConfig[]; + public webInstrumentationSrc: string; + public webInstrumentationConnectionString?: string; + public noPatchModules: string; + public noDiagnosticChannel: boolean; + + private _configWarnings: string[]; + + /** + * Creates a new Config instance + * @param setupString Connection String, instrumentationKey is no longer supported here + */ + constructor(setupString?: string, configWarnings?: string[]){ + // Load config values from env variables and JSON if available + this._mergeConfig(); + this.connectionString = setupString; + this.webInstrumentationConfig = this.webInstrumentationConfig || null; + this.ignoreLegacyHeaders = true; + this.webInstrumentationConnectionString = this.webInstrumentationConnectionString || ""; + this._configWarnings = configWarnings || []; + } + + private _mergeConfig() { + const jsonConfig = ShimJsonConfig.getInstance(); + this.connectionString = jsonConfig.connectionString; + this.correlationHeaderExcludedDomains = jsonConfig.correlationHeaderExcludedDomains; + this.disableAllExtendedMetrics = jsonConfig.disableAllExtendedMetrics; + this.disableAppInsights = jsonConfig.disableAppInsights; + this.distributedTracingMode = jsonConfig.distributedTracingMode; + this.enableAutoCollectConsole = jsonConfig.enableAutoCollectConsole; + this.enableLoggerErrorToTrace = jsonConfig.enableLoggerErrorToTrace; + this.enableAutoCollectDependencies = jsonConfig.enableAutoCollectDependencies; + this.enableAutoCollectIncomingRequestAzureFunctions = jsonConfig.enableAutoCollectIncomingRequestAzureFunctions; + this.enableAutoCollectExceptions = jsonConfig.enableAutoCollectExceptions; + this.enableAutoCollectExtendedMetrics = jsonConfig.enableAutoCollectExtendedMetrics; + this.enableAutoCollectExternalLoggers = jsonConfig.enableAutoCollectExternalLoggers; + this.enableAutoCollectHeartbeat = jsonConfig.enableAutoCollectHeartbeat; + this.enableAutoCollectPerformance = jsonConfig.enableAutoCollectPerformance; + this.enableAutoCollectPreAggregatedMetrics = jsonConfig.enableAutoCollectPreAggregatedMetrics; + this.enableAutoCollectRequests = jsonConfig.enableAutoCollectRequests; + this.enableAutoDependencyCorrelation = jsonConfig.enableAutoDependencyCorrelation; + this.enableInternalDebugLogging = jsonConfig.enableInternalDebugLogging; + this.enableInternalWarningLogging = jsonConfig.enableInternalWarningLogging; + this.enableResendInterval = jsonConfig.enableResendInterval; + this.enableMaxBytesOnDisk = jsonConfig.enableMaxBytesOnDisk; + this.enableSendLiveMetrics = jsonConfig.enableSendLiveMetrics; + this.enableUseAsyncHooks = jsonConfig.enableUseAsyncHooks; + this.enableUseDiskRetryCaching = jsonConfig.enableUseDiskRetryCaching; + this.endpointUrl = jsonConfig.endpointUrl; + this.extendedMetricDisablers = jsonConfig.extendedMetricDisablers; + this.ignoreLegacyHeaders = jsonConfig.ignoreLegacyHeaders; + this.maxBatchIntervalMs = jsonConfig.maxBatchIntervalMs; + this.maxBatchSize = jsonConfig.maxBatchSize; + this.proxyHttpUrl = jsonConfig.proxyHttpUrl; + this.proxyHttpsUrl = jsonConfig.proxyHttpsUrl; + this.quickPulseHost = jsonConfig.quickPulseHost; + this.samplingPercentage = jsonConfig.samplingPercentage; + this.enableWebInstrumentation = jsonConfig.enableWebInstrumentation; + this.webInstrumentationConnectionString = jsonConfig.webInstrumentationConnectionString; + this.webInstrumentationConfig = jsonConfig.webInstrumentationConfig; + this.webInstrumentationSrc = jsonConfig.webInstrumentationSrc; + this.noPatchModules = jsonConfig.noPatchModules; + this.noDiagnosticChannel = jsonConfig.noDiagnosticChannel + } + + /** + * Parse the config property to set the appropriate values on the AzureMonitorOpenTelemetryOptions + */ + public parseConfig(): AzureMonitorOpenTelemetryOptions { + const options: AzureMonitorOpenTelemetryOptions = { + azureMonitorExporterOptions: { + connectionString: this.connectionString, + disableOfflineStorage: false, + }, + enableAutoCollectPerformance: true, + enableAutoCollectExceptions: true, + instrumentationOptions: { + http: { enabled: true }, + azureSdk: { enabled: true }, + mongoDb: { enabled: true }, + mySql: { enabled: true }, + redis: { enabled: true }, + redis4: { enabled: true }, + postgreSql: { enabled: true }, + bunyan: { enabled: true }, + }, + otlpTraceExporterConfig: {}, + otlpMetricExporterConfig: {}, + otlpLogExporterConfig: {}, + enableLiveMetrics: true, + }; + (options.instrumentationOptions as InstrumentationOptions) = { + ...options.instrumentationOptions, + console: { enabled: true }, + winston: { enabled: true }, + }; + if (this.samplingPercentage) { + options.samplingRatio = this.samplingPercentage / 100; + } + options.instrumentationOptions = { + ...options.instrumentationOptions, + http: { + ...options.instrumentationOptions?.http, + } as HttpInstrumentationConfig, + } + if (this.aadTokenCredential) { + options.azureMonitorExporterOptions.credential = this.aadTokenCredential; + } + if (typeof (this.enableAutoCollectConsole) === "boolean") { + const setting: boolean = this.enableAutoCollectConsole; + (options.instrumentationOptions as InstrumentationOptions) = { + ...options.instrumentationOptions, + console: { enabled: setting }, + }; + } + if (typeof (this.enableAutoCollectExceptions) === "boolean") { + options.enableAutoCollectExceptions = this.enableAutoCollectExceptions; + } + + if (this.enableAutoCollectDependencies === false && this.enableAutoCollectRequests === false) { + options.instrumentationOptions.http.enabled = false; + } + else { + if (this.enableAutoCollectDependencies === false) { + options.instrumentationOptions = { + ...options.instrumentationOptions, + http: { + ...options.instrumentationOptions?.http, + enabled: true, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + ignoreOutgoingRequestHook: (request: http.RequestOptions) => true, + } as HttpInstrumentationConfig + }; + } + if (this.enableAutoCollectRequests === false) { + options.instrumentationOptions = { + ...options.instrumentationOptions, + http: { + ...options.instrumentationOptions?.http, + enabled: true, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + ignoreIncomingRequestHook: (request: http.RequestOptions) => true, + } as HttpInstrumentationConfig + }; + } + } + if (typeof (this.enableAutoCollectPerformance) === "boolean") { + options.enableAutoCollectPerformance = this.enableAutoCollectPerformance; + } + if (typeof (this.enableAutoCollectExternalLoggers) === "boolean") { + (options.instrumentationOptions as InstrumentationOptions) = { + ...options.instrumentationOptions, + winston: { enabled: this.enableAutoCollectExternalLoggers }, + bunyan: { enabled: this.enableAutoCollectExternalLoggers }, + }; + } + if (this.enableUseDiskRetryCaching === false) { + options.azureMonitorExporterOptions.disableOfflineStorage = true; + } + if (this.proxyHttpUrl || this.proxyHttpsUrl) { + try { + const proxyUrl = new URL(this.proxyHttpsUrl || this.proxyHttpUrl); + options.azureMonitorExporterOptions.proxyOptions = { + host: proxyUrl.hostname, + port: Number(proxyUrl.port), + }; + } + catch (err) { + diag.warn("failed to parse proxy URL."); + } + } + if (this.maxBatchIntervalMs) { + options.otlpTraceExporterConfig = { ...options.otlpTraceExporterConfig, timeoutMillis: this.maxBatchIntervalMs }; + options.otlpMetricExporterConfig = { ...options.otlpMetricExporterConfig, timeoutMillis: this.maxBatchIntervalMs }; + options.otlpLogExporterConfig = { ...options.otlpLogExporterConfig, timeoutMillis: this.maxBatchIntervalMs }; + } + if (this.enableInternalWarningLogging === true) { + // Do not override env variable if present + if (!process.env["APPLICATIONINSIGHTS_INSTRUMENTATION_LOGGING_LEVEL"]) { + process.env["APPLICATIONINSIGHTS_INSTRUMENTATION_LOGGING_LEVEL"] = "WARN"; + } + + } + if (this.enableInternalDebugLogging === true) { + // Do not override env variable if present + if (!process.env["APPLICATIONINSIGHTS_INSTRUMENTATION_LOGGING_LEVEL"]) { + process.env["APPLICATIONINSIGHTS_INSTRUMENTATION_LOGGING_LEVEL"] = "DEBUG"; + } + } + if (this.enableAutoCollectPreAggregatedMetrics === false) { + // Do not override env variable if present + if (!process.env["APPLICATION_INSIGHTS_NO_STANDARD_METRICS"]) { + process.env["APPLICATION_INSIGHTS_NO_STANDARD_METRICS"] = "disable"; + } + } + + if (this.noDiagnosticChannel === true) { + // Disable all instrumentations except http to conform with AppInsights 2.x behavior + for (const mod in options.instrumentationOptions) { + if (mod !== "http") { + (options.instrumentationOptions as InstrumentationOptionsType)[mod] = { enabled: false }; + } + } + } + + if (this.noPatchModules && this.noDiagnosticChannel !== true) { + const unpatchedModules: string[] = this.noPatchModules.split(","); + // Convert module names not supported by new InstrumentationOptions + for (let i = 0; i < unpatchedModules.length; i++) { + if (unpatchedModules[i] === "pg-pool" || unpatchedModules[i] === "pg") { + unpatchedModules[i] = "postgresql"; + } else if (unpatchedModules[i] === "mongodb-core") { + unpatchedModules[i] = "mongodb"; + } else if (unpatchedModules[i] === "redis") { + unpatchedModules.push("redis4") + } + } + + // Disable instrumentation for unpatched modules + for (const mod in options.instrumentationOptions) { + if (unpatchedModules.indexOf(mod.toLowerCase()) !== -1) { + (options.instrumentationOptions as InstrumentationOptionsType)[mod] = { enabled: false }; + } + } + } + + if (typeof (this.enableSendLiveMetrics) === "boolean") { + options.enableLiveMetrics = this.enableSendLiveMetrics; + } + + // BROWSER SDK LOADER + if (this.enableWebInstrumentation === true) { + options.browserSdkLoaderOptions = { + enabled: this.enableWebInstrumentation, + connectionString: this.webInstrumentationConnectionString, + } + } + + // NOT SUPPORTED CONFIGURATION OPTIONS + if ( + this.enableAutoCollectExtendedMetrics === true || + typeof(this.enableAutoCollectExtendedMetrics) === "object" && Object.keys(this.enableAutoCollectExtendedMetrics).length > 0 || + typeof(this.disableAllExtendedMetrics) === "boolean" || + process.env[ENV_nativeMetricsDisableAll] || + process.env[ENV_nativeMetricsDisablers] || + this.extendedMetricDisablers + ) { + this._configWarnings.push(`Extended metrics are no longer supported. ${UNSUPPORTED_MSG}`); + } + if (this.disableAppInsights) { + this._configWarnings.push(`disableAppInsights configuration no longer supported. ${UNSUPPORTED_MSG}`); + } + if (this.enableAutoCollectHeartbeat === true) { + this._configWarnings.push(`Heartbeat metrics are no longer supported. ${UNSUPPORTED_MSG}`); + } + if (this.enableAutoDependencyCorrelation === false) { + this._configWarnings.push(`Auto dependency correlation cannot be turned off anymore. ${UNSUPPORTED_MSG}`); + } + if (typeof (this.enableAutoCollectIncomingRequestAzureFunctions) === "boolean") { + this._configWarnings.push(`Auto request generation in Azure Functions is no longer supported. ${UNSUPPORTED_MSG}`); + } + if (this.enableUseAsyncHooks === false) { + this._configWarnings.push(`The use of non async hooks is no longer supported. ${UNSUPPORTED_MSG}`); + } + if (this.distributedTracingMode === DistributedTracingModes.AI) { + this._configWarnings.push(`AI only distributed tracing mode is no longer supported. ${UNSUPPORTED_MSG}`); + } + if (this.enableResendInterval) { + this._configWarnings.push(`The resendInterval configuration option is not supported by the shim. ${UNSUPPORTED_MSG}`); + } + if (this.enableMaxBytesOnDisk) { + this._configWarnings.push(`The maxBytesOnDisk configuration option is not supported by the shim. ${UNSUPPORTED_MSG}`); + } + if (this.ignoreLegacyHeaders === false) { + this._configWarnings.push(`LegacyHeaders are not supported by the shim. ${UNSUPPORTED_MSG}`); + } + if (this.maxBatchSize) { + this._configWarnings.push(`The maxBatchSize configuration option is not supported by the shim. ${UNSUPPORTED_MSG}`); + } + if (this.enableLoggerErrorToTrace) { + this._configWarnings.push(`The enableLoggerErrorToTrace configuration option is not supported by the shim. ${UNSUPPORTED_MSG}`); + } + if (this.httpAgent || this.httpsAgent) { + this._configWarnings.push(`The httpAgent and httpsAgent configuration options are not supported by the shim. ${UNSUPPORTED_MSG}`); + } + if ( + this.webInstrumentationConfig || this.webInstrumentationSrc + ) { + this._configWarnings.push(`The webInstrumentation config and src options are not supported by the shim. ${UNSUPPORTED_MSG}`); + } + if (this.quickPulseHost) { + this._configWarnings.push(`The quickPulseHost configuration option is not supported by the shim. ${UNSUPPORTED_MSG}`); + } + if (this.correlationHeaderExcludedDomains) { + this._configWarnings.push(`The correlationHeaderExcludedDomains configuration option is not supported by the shim. ${UNSUPPORTED_MSG}`); + } + if ( + process.env[ENV_iKey] || + process.env[legacy_ENV_iKey] || + process.env[ENV_azurePrefix + ENV_iKey] || + process.env[ENV_azurePrefix + legacy_ENV_iKey] + ) { + this._configWarnings.push(`The iKey configuration option is not supported by the shim. Please configure the the connection string instead. ${UNSUPPORTED_MSG}`); + } + if (process.env[ENV_profileQueryEndpoint]) { + this._configWarnings.push(`The profileQueryEndpoint configuration option is not supported by the shim. ${UNSUPPORTED_MSG}`); + } + if (process.env[ENV_quickPulseHost]) { + this._configWarnings.push(`Please configure the quickPulseHost in the connection string instead. ${UNSUPPORTED_MSG}`); + } + return options; + } +} + +export = Config; \ No newline at end of file diff --git a/Library/JsonConfig.ts b/src/shim/shim-jsonConfig.ts similarity index 56% rename from Library/JsonConfig.ts rename to src/shim/shim-jsonConfig.ts index 132f031f1..a54bfe197 100644 --- a/Library/JsonConfig.ts +++ b/src/shim/shim-jsonConfig.ts @@ -1,154 +1,173 @@ -import fs = require("fs"); -import path = require("path"); - -import Logging = require('./Logging'); -import { IJsonConfig } from "../Declarations/Interfaces"; -import { DistributedTracingModes } from "../applicationinsights"; -import { IDisabledExtendedMetrics } from "../AutoCollection/NativePerformance"; +import * as fs from "fs"; +import * as path from "path"; +import * as http from "http"; +import * as https from "https"; +import * as azureCoreAuth from "@azure/core-auth"; +import { DistributedTracingModes, IDisabledExtendedMetrics, IJsonConfig, IWebInstrumentationConfig } from "./types"; +import { diag } from "@opentelemetry/api"; const ENV_CONFIGURATION_FILE = "APPLICATIONINSIGHTS_CONFIGURATION_FILE"; -// Azure Connection String +const ENV_CONTENT = "APPLICATIONINSIGHTS_CONFIGURATION_CONTENT"; const ENV_connectionString = "APPLICATIONINSIGHTS_CONNECTION_STRING"; -// Native Metrics Opt Outs -const ENV_nativeMetricsDisablers = "APPLICATION_INSIGHTS_DISABLE_EXTENDED_METRIC"; -const ENV_nativeMetricsDisableAll = "APPLICATION_INSIGHTS_DISABLE_ALL_EXTENDED_METRICS" const ENV_http_proxy = "http_proxy"; const ENV_https_proxy = "https_proxy"; -const ENV_noDiagnosticChannel = "APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL" -const ENV_noStatsbeat = "APPLICATION_INSIGHTS_NO_STATSBEAT"; +const ENV_noDiagnosticChannel = "APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL"; const ENV_noHttpAgentKeepAlive = "APPLICATION_INSIGHTS_NO_HTTP_AGENT_KEEP_ALIVE"; const ENV_noPatchModules = "APPLICATION_INSIGHTS_NO_PATCH_MODULES"; +const ENV_webInstrumentationEnable = "APPLICATIONINSIGHTS_WEB_INSTRUMENTATION_ENABLED"; +const ENV_webInstrumentation_connectionString = "APPLICATIONINSIGHTS_WEB_INSTRUMENTATION_CONNECTION_STRING"; +const ENV_webInstrumentation_source = "APPLICATIONINSIGHTS_WEB_INSTRUMENTATION_SOURCE"; -export class JsonConfig implements IJsonConfig { - private static _instance: JsonConfig; +export class ShimJsonConfig implements IJsonConfig { + private static _instance: ShimJsonConfig; - public connectionString: string; - public instrumentationKey: string; public endpointUrl: string; + public connectionString: string; + public disableAllExtendedMetrics: boolean; + public extendedMetricDisablers: string; + public proxyHttpUrl: string; + public proxyHttpsUrl: string; + public noDiagnosticChannel: boolean; + public noHttpAgentKeepAlive: boolean; + public noPatchModules: string; public maxBatchSize: number; public maxBatchIntervalMs: number; public disableAppInsights: boolean; public samplingPercentage: number; - public correlationIdRetryIntervalMs: number; public correlationHeaderExcludedDomains: string[]; - public proxyHttpUrl: string; - public proxyHttpsUrl: string; + public httpAgent: http.Agent; + public httpsAgent: https.Agent; public ignoreLegacyHeaders: boolean; - public distributedTracingMode: DistributedTracingModes; - public enableAutoCollectExternalLoggers: boolean; + public aadTokenCredential?: azureCoreAuth.TokenCredential; public enableAutoCollectConsole: boolean; - public enableAutoCollectExceptions: boolean; + public enableLoggerErrorToTrace: boolean; public enableAutoCollectPerformance: boolean; - public enableAutoCollectExtendedMetrics: boolean | IDisabledExtendedMetrics; + public enableAutoCollectExternalLoggers: boolean; public enableAutoCollectPreAggregatedMetrics: boolean; public enableAutoCollectHeartbeat: boolean; public enableAutoCollectRequests: boolean; public enableAutoCollectDependencies: boolean; public enableAutoDependencyCorrelation: boolean; - public enableUseAsyncHooks: boolean; + public enableAutoCollectIncomingRequestAzureFunctions: boolean; + public enableSendLiveMetrics: boolean; public enableUseDiskRetryCaching: boolean; + public enableUseAsyncHooks: boolean; + public distributedTracingMode: DistributedTracingModes; + public enableAutoCollectExtendedMetrics: boolean | IDisabledExtendedMetrics; public enableResendInterval: number; public enableMaxBytesOnDisk: number; public enableInternalDebugLogging: boolean; public enableInternalWarningLogging: boolean; - public enableSendLiveMetrics: boolean; - public disableAllExtendedMetrics: boolean; - public extendedMetricDisablers: string; - public disableStatsbeat: boolean; - public noDiagnosticChannel: boolean; - public noPatchModules: string; - public noHttpAgentKeepAlive: boolean; public quickPulseHost: string; + public enableWebInstrumentation: boolean; + public enableAutoCollectExceptions: boolean; + public webInstrumentationConnectionString?: string; + public webInstrumentationConfig: IWebInstrumentationConfig[]; + public webInstrumentationSrc: string; - - static getInstance() { - if (!JsonConfig._instance) { - JsonConfig._instance = new JsonConfig(); + public static getInstance() { + if (!ShimJsonConfig._instance) { + ShimJsonConfig._instance = new ShimJsonConfig(); } - return JsonConfig._instance; + return ShimJsonConfig._instance; } constructor() { - // Load env variables first + // Load environment variables first this.connectionString = process.env[ENV_connectionString]; - this.disableAllExtendedMetrics = !!process.env[ENV_nativeMetricsDisableAll]; - this.extendedMetricDisablers = process.env[ENV_nativeMetricsDisablers]; this.proxyHttpUrl = process.env[ENV_http_proxy]; this.proxyHttpsUrl = process.env[ENV_https_proxy]; this.noDiagnosticChannel = !!process.env[ENV_noDiagnosticChannel]; - this.disableStatsbeat = !!process.env[ENV_noStatsbeat]; this.noHttpAgentKeepAlive = !!process.env[ENV_noHttpAgentKeepAlive]; this.noPatchModules = process.env[ENV_noPatchModules] || ""; + this.enableWebInstrumentation = !!process.env[ENV_webInstrumentationEnable]; + this.webInstrumentationSrc = process.env[ENV_webInstrumentation_source] || ""; + this.webInstrumentationConnectionString = process.env[ENV_webInstrumentation_connectionString] || ""; + this._loadJsonFile(); } private _loadJsonFile() { - let configFileName = "applicationinsights.json"; - let rootPath = path.join(__dirname, "../../"); // Root of applicationinsights folder (__dirname = ../out/Library) - let tempDir = path.join(rootPath, configFileName); // default - let configFile = process.env[ENV_CONFIGURATION_FILE]; - if (configFile) { - if (path.isAbsolute(configFile)) { - tempDir = configFile; + let jsonString = ""; + const contentJsonConfig = process.env[ENV_CONTENT]; + // JSON string added directly in env variable + if (contentJsonConfig) { + jsonString = contentJsonConfig; + } + // JSON file + else { + const configFileName = "applicationinsights.json"; + const rootPath = path.join(__dirname, "../../../"); // Root of folder (__dirname = ../dist-esm/src) + let tempDir = path.join(rootPath, configFileName); // default + const configFile = process.env[ENV_CONFIGURATION_FILE]; + if (configFile) { + if (path.isAbsolute(configFile)) { + tempDir = configFile; + } else { + tempDir = path.join(rootPath, configFile); // Relative path to applicationinsights folder + } } - else { - tempDir = path.join(rootPath, configFile);// Relative path to applicationinsights folder + try { + jsonString = fs.readFileSync(tempDir, "utf8"); + } catch (err) { + diag.info("Failed to read JSON config file: ", err); } } try { - const jsonConfig: IJsonConfig = JSON.parse(fs.readFileSync(tempDir, "utf8")); - if (jsonConfig.disableStatsbeat != undefined) { - this.disableStatsbeat = jsonConfig.disableStatsbeat; - } - if (jsonConfig.disableAllExtendedMetrics != undefined) { - this.disableAllExtendedMetrics = jsonConfig.disableStatsbeat; - } - if (jsonConfig.noDiagnosticChannel != undefined) { - this.noDiagnosticChannel = jsonConfig.noDiagnosticChannel; - } - if (jsonConfig.noHttpAgentKeepAlive != undefined) { - this.noHttpAgentKeepAlive = jsonConfig.noHttpAgentKeepAlive; - } - if (jsonConfig.connectionString != undefined) { + const jsonConfig: IJsonConfig = JSON.parse(jsonString); + if (jsonConfig.connectionString !== undefined) { this.connectionString = jsonConfig.connectionString; } - if (jsonConfig.extendedMetricDisablers != undefined) { - this.extendedMetricDisablers = jsonConfig.extendedMetricDisablers; + if (jsonConfig.disableAllExtendedMetrics !== undefined) { + this.disableAllExtendedMetrics = jsonConfig.disableAllExtendedMetrics; } - if (jsonConfig.noDiagnosticChannel != undefined) { - this.noDiagnosticChannel = jsonConfig.noDiagnosticChannel; + if (jsonConfig.extendedMetricDisablers !== undefined) { + this.extendedMetricDisablers = jsonConfig.extendedMetricDisablers; } - if (jsonConfig.proxyHttpUrl != undefined) { + if (jsonConfig.proxyHttpUrl !== undefined) { this.proxyHttpUrl = jsonConfig.proxyHttpUrl; } - if (jsonConfig.proxyHttpsUrl != undefined) { + if (jsonConfig.proxyHttpsUrl !== undefined) { this.proxyHttpsUrl = jsonConfig.proxyHttpsUrl; } - if (jsonConfig.proxyHttpsUrl != undefined) { - this.proxyHttpsUrl = jsonConfig.proxyHttpsUrl; + if (jsonConfig.noDiagnosticChannel !== undefined) { + this.noDiagnosticChannel = jsonConfig.noDiagnosticChannel; + } + if (jsonConfig.noHttpAgentKeepAlive !== undefined) { + this.noHttpAgentKeepAlive = jsonConfig.noHttpAgentKeepAlive; } - if (jsonConfig.noPatchModules != undefined) { + if (jsonConfig.noPatchModules !== undefined) { this.noPatchModules = jsonConfig.noPatchModules; } + if (jsonConfig.enableWebInstrumentation !== undefined) { + this.enableWebInstrumentation = jsonConfig.enableWebInstrumentation; + } + if (jsonConfig.webInstrumentationSrc !== undefined) { + this.webInstrumentationSrc = jsonConfig.webInstrumentationSrc; + } + if (jsonConfig.webInstrumentationConnectionString !== undefined) { + this.webInstrumentationConnectionString = jsonConfig.webInstrumentationConnectionString; + } this.endpointUrl = jsonConfig.endpointUrl; + this.samplingPercentage = jsonConfig.samplingPercentage; + this.enableAutoCollectExternalLoggers = jsonConfig.enableAutoCollectExternalLoggers; + this.enableAutoCollectConsole = jsonConfig.enableAutoCollectConsole; + this.enableLoggerErrorToTrace = jsonConfig.enableLoggerErrorToTrace; + this.enableAutoCollectExceptions = jsonConfig.enableAutoCollectExceptions; + this.enableAutoCollectPerformance = jsonConfig.enableAutoCollectPerformance; + this.enableAutoCollectPreAggregatedMetrics = jsonConfig.enableAutoCollectPreAggregatedMetrics; + this.enableAutoDependencyCorrelation = jsonConfig.enableAutoDependencyCorrelation; this.maxBatchSize = jsonConfig.maxBatchSize; this.maxBatchIntervalMs = jsonConfig.maxBatchIntervalMs; this.disableAppInsights = jsonConfig.disableAppInsights; - this.samplingPercentage = jsonConfig.samplingPercentage; - this.correlationIdRetryIntervalMs = jsonConfig.correlationIdRetryIntervalMs; this.correlationHeaderExcludedDomains = jsonConfig.correlationHeaderExcludedDomains; this.ignoreLegacyHeaders = jsonConfig.ignoreLegacyHeaders; this.distributedTracingMode = jsonConfig.distributedTracingMode; - this.enableAutoCollectExternalLoggers = jsonConfig.enableAutoCollectExternalLoggers; - this.enableAutoCollectConsole = jsonConfig.enableAutoCollectConsole; - this.enableAutoCollectExceptions = jsonConfig.enableAutoCollectExceptions; - this.enableAutoCollectPerformance = jsonConfig.enableAutoCollectPerformance; this.enableAutoCollectExtendedMetrics = jsonConfig.enableAutoCollectExtendedMetrics; - this.enableAutoCollectPreAggregatedMetrics = jsonConfig.enableAutoCollectPreAggregatedMetrics; this.enableAutoCollectHeartbeat = jsonConfig.enableAutoCollectHeartbeat; this.enableAutoCollectRequests = jsonConfig.enableAutoCollectRequests; this.enableAutoCollectDependencies = jsonConfig.enableAutoCollectDependencies; - this.enableAutoDependencyCorrelation = jsonConfig.enableAutoDependencyCorrelation; + this.enableAutoCollectIncomingRequestAzureFunctions = jsonConfig.enableAutoCollectIncomingRequestAzureFunctions; this.enableUseAsyncHooks = jsonConfig.enableUseAsyncHooks; this.enableUseDiskRetryCaching = jsonConfig.enableUseDiskRetryCaching; this.enableResendInterval = jsonConfig.enableResendInterval; @@ -156,10 +175,10 @@ export class JsonConfig implements IJsonConfig { this.enableInternalDebugLogging = jsonConfig.enableInternalDebugLogging; this.enableInternalWarningLogging = jsonConfig.enableInternalWarningLogging; this.enableSendLiveMetrics = jsonConfig.enableSendLiveMetrics; + this.webInstrumentationConfig = jsonConfig.webInstrumentationConfig; this.quickPulseHost = jsonConfig.quickPulseHost; - } - catch (err) { - Logging.info("Missing or invalid JSON config file: ", err); + } catch (err) { + diag.info("Missing or invalid JSON config file: ", err); } } -} +} \ No newline at end of file diff --git a/src/shim/telemetryClient.ts b/src/shim/telemetryClient.ts new file mode 100644 index 000000000..08a0f7941 --- /dev/null +++ b/src/shim/telemetryClient.ts @@ -0,0 +1,342 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { Attributes, context, metrics, ProxyTracerProvider, SpanKind, SpanOptions, SpanStatusCode, diag, trace } from "@opentelemetry/api"; +import { logs } from "@opentelemetry/api-logs"; +import { LoggerProvider } from "@opentelemetry/sdk-logs"; +import { + SEMATTRS_DB_STATEMENT, + SEMATTRS_DB_SYSTEM, + SEMATTRS_HTTP_METHOD, + SEMATTRS_HTTP_STATUS_CODE, + SEMATTRS_HTTP_URL, + SEMATTRS_PEER_SERVICE, + SEMATTRS_RPC_GRPC_STATUS_CODE, + SEMATTRS_RPC_METHOD, + SEMATTRS_RPC_SYSTEM +} from "@opentelemetry/semantic-conventions"; +import * as Contracts from "../declarations/contracts"; +import { TelemetryItem as Envelope } from "../declarations/generated"; +import { Context } from "./context"; +import { Util } from "../shared/util"; +import Config = require("./shim-config"); +import { AttributeSpanProcessor } from "../shared/util/attributeSpanProcessor"; +import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node"; +import { AttributeLogProcessor } from "../shared/util/attributeLogRecordProcessor"; +import { LogApi } from "./logsApi"; +import { flushAzureMonitor, shutdownAzureMonitor, useAzureMonitor } from "../main"; +import { AzureMonitorOpenTelemetryOptions } from "../types"; +import { UNSUPPORTED_MSG } from "./types"; + +/** + * Application Insights telemetry client provides interface to track telemetry items, register telemetry initializers and + * and manually trigger immediate sending (flushing) + */ +export class TelemetryClient { + public context: Context; + public commonProperties: { [key: string]: string }; + public config: Config; + private _attributeSpanProcessor: AttributeSpanProcessor; + private _attributeLogProcessor: AttributeLogProcessor; + private _logApi: LogApi; + private _isInitialized: boolean; + private _options: AzureMonitorOpenTelemetryOptions; + private _configWarnings: string[] = []; + + /** + * Constructs a new instance of TelemetryClient + * @param setupString the Connection String or Instrumentation Key to use (read from environment variable if not specified) + */ + constructor(input?: string) { + const config = new Config(input, this._configWarnings); + this.config = config; + this.commonProperties = {}; + this.context = new Context(); + this._isInitialized = false; + } + + public initialize() { + this._isInitialized = true; + // Parse shim config to Azure Monitor options + this._options = this.config.parseConfig(); + useAzureMonitor(this._options); + try { + // LoggerProvider would be initialized when client is instantiated + // Get Logger from global provider + this._logApi = new LogApi(logs.getLogger("ApplicationInsightsLogger")); + this._attributeSpanProcessor = new AttributeSpanProcessor({ ...this.context.tags, ...this.commonProperties }); + ((trace.getTracerProvider() as ProxyTracerProvider).getDelegate() as NodeTracerProvider).addSpanProcessor(this._attributeSpanProcessor); + + this._attributeLogProcessor = new AttributeLogProcessor({ ...this.context.tags, ...this.commonProperties }); + (logs.getLoggerProvider() as LoggerProvider).addLogRecordProcessor(this._attributeLogProcessor); + + // Warn if any config warnings were generated during parsing + for (let i = 0; i < this._configWarnings.length; i++) { + diag.warn(this._configWarnings[i]); + this._attributeLogProcessor = new AttributeLogProcessor({ ...this.context.tags, ...this.commonProperties }); + (logs.getLoggerProvider() as LoggerProvider).addLogRecordProcessor(this._attributeLogProcessor); + } + } + catch (error) { + diag.error(`Failed to initialize TelemetryClient ${error}`); + } + } + + /** + * Log information about availability of an application + * @param telemetry Object encapsulating tracking options + */ + public trackAvailability(telemetry: Contracts.AvailabilityTelemetry): void { + if (!this._isInitialized) { + this.initialize(); + } + this._logApi.trackAvailability(telemetry); + } + + /** + * Log a page view + * @param telemetry Object encapsulating tracking options + */ + public trackPageView(telemetry: Contracts.PageViewTelemetry): void { + if (!this._isInitialized) { + this.initialize(); + } + this._logApi.trackPageView(telemetry); + } + + /** + * Log a trace message + * @param telemetry Object encapsulating tracking options + */ + public trackTrace(telemetry: Contracts.TraceTelemetry): void { + if (!this._isInitialized) { + this.initialize(); + } + this._logApi.trackTrace(telemetry); + } + + /** + * Log an exception + * @param telemetry Object encapsulating tracking options + */ + public trackException(telemetry: Contracts.ExceptionTelemetry): void { + if (!this._isInitialized) { + this.initialize(); + } + this._logApi.trackException(telemetry); + } + + /** + * Log a user action or other occurrence. + * @param telemetry Object encapsulating tracking options + */ + public trackEvent(telemetry: Contracts.EventTelemetry): void { + if (!this._isInitialized) { + this.initialize(); + } + this._logApi.trackEvent(telemetry); + } + + /** + * Log a numeric value that is not associated with a specific event. Typically used to send regular reports of performance indicators. + * To send a single measurement, use just the first two parameters. If you take measurements very frequently, you can reduce the + * telemetry bandwidth by aggregating multiple measurements and sending the resulting average at intervals. + * @param telemetry Object encapsulating tracking options + */ + public trackMetric(telemetry: Contracts.MetricPointTelemetry & Contracts.MetricTelemetry): void { + if (!this._isInitialized) { + this.initialize(); + } + // Create custom metric + try { + const meter = metrics.getMeterProvider().getMeter("ApplicationInsightsMetrics"); + const histogram = meter.createHistogram(telemetry.name); + histogram.record(telemetry.value, { ...telemetry.properties, ...this.commonProperties, ...this.context.tags }); + } catch (error) { + diag.error(`Failed to record metric: ${error}`); + } + } + + /** + * Log a request. Note that the default client will attempt to collect HTTP requests automatically so only use this for requests + * that aren't automatically captured or if you've disabled automatic request collection. + * + * @param telemetry Object encapsulating tracking options + */ + public trackRequest(telemetry: Contracts.RequestTelemetry): void { + if (!this._isInitialized) { + this.initialize(); + } + const startTime = telemetry.time || new Date(); + const endTime = startTime.getTime() + telemetry.duration; + + const ctx = context.active(); + const attributes: Attributes = { + ...telemetry.properties, + }; + attributes[SEMATTRS_HTTP_METHOD] = "HTTP"; + attributes[SEMATTRS_HTTP_URL] = telemetry.url; + attributes[SEMATTRS_HTTP_STATUS_CODE] = telemetry.resultCode; + const options: SpanOptions = { + kind: SpanKind.SERVER, + attributes: attributes, + startTime: startTime, + }; + const span: any = trace.getTracer("ApplicationInsightsTracer") + .startSpan(telemetry.name, options, ctx); + span.setStatus({ + code: telemetry.success ? SpanStatusCode.OK : SpanStatusCode.ERROR, + }); + span.end(endTime); + } + + /** + * Log a dependency. Note that the default client will attempt to collect dependencies automatically so only use this for dependencies + * that aren't automatically captured or if you've disabled automatic dependency collection. + * + * @param telemetry Object encapsulating tracking option + * */ + public trackDependency(telemetry: Contracts.DependencyTelemetry) { + if (!this._isInitialized) { + this.initialize(); + } + const startTime = telemetry.time || new Date(); + const endTime = startTime.getTime() + telemetry.duration; + if (telemetry && !telemetry.target && telemetry.data) { + // url.parse().host returns null for non-urls, + // making this essentially a no-op in those cases + // If this logic is moved, update jsdoc in DependencyTelemetry.target + // url.parse() is deprecated, update to use WHATWG URL API instead + try { + telemetry.target = new URL(telemetry.data).host; + } catch (error) { + // set target as null to be compliant with previous behavior + telemetry.target = null; + diag.warn(this.constructor.name, "Failed to create URL.", error); + } + } + const ctx = context.active(); + const attributes: Attributes = { + ...telemetry.properties, + }; + if (telemetry.dependencyTypeName) { + if (telemetry.dependencyTypeName.toLowerCase().indexOf("http") > -1) { + attributes[SEMATTRS_HTTP_METHOD] = "HTTP"; + attributes[SEMATTRS_HTTP_URL] = telemetry.data; + attributes[SEMATTRS_HTTP_STATUS_CODE] = telemetry.resultCode; + } else if (Util.getInstance().isDbDependency(telemetry.dependencyTypeName)) { + attributes[SEMATTRS_DB_SYSTEM] = telemetry.dependencyTypeName; + attributes[SEMATTRS_DB_STATEMENT] = telemetry.data; + } else if (telemetry.dependencyTypeName.toLowerCase().indexOf("rpc") > -1) { + attributes[SEMATTRS_RPC_SYSTEM] = telemetry.dependencyTypeName; + attributes[SEMATTRS_RPC_METHOD] = telemetry.data; + attributes[SEMATTRS_RPC_GRPC_STATUS_CODE] = telemetry.resultCode; + } + } + if (telemetry.target) { + attributes[SEMATTRS_PEER_SERVICE] = telemetry.target; + } + const options: SpanOptions = { + kind: SpanKind.CLIENT, + attributes: attributes, + startTime: startTime, + }; + const span: any = trace.getTracer("ApplicationInsightsTracer") + .startSpan(telemetry.name, options, ctx); + span.setStatus({ + code: telemetry.success ? SpanStatusCode.OK : SpanStatusCode.ERROR, + }); + span.end(endTime); + } + /** + * Generic track method for all telemetry types + * @param data the telemetry to send + * @param telemetryType specify the type of telemetry you are tracking from the list of Contracts.DataTypes + */ + public track(telemetry: Contracts.Telemetry, telemetryType: Contracts.TelemetryType) { + throw new Error(`Not implemented. Please use the specific track method for the type of telemetry you are tracking. ${UNSUPPORTED_MSG}`); + } + + /** + * Automatically populate telemetry properties like RoleName when running in Azure + * + * @param value if true properties will be populated + */ + public setAutoPopulateAzureProperties() { + // NO-OP + } + + /** + * Get Authorization handler + */ + public getAuthorizationHandler(config: Config): void { + diag.warn(`getAuthorizationHandler is not supported in ApplicationInsights any longer. ${UNSUPPORTED_MSG}`); + } + + /* + * Get Statsbeat instance + */ + public getStatsbeat(): any { + return null; + } + + public setUseDiskRetryCaching( + value: boolean, + resendInterval?: number, + maxBytesOnDisk?: number + ) { + throw new Error("Not implemented"); + } + + /** + * Adds telemetry processor to the collection. Telemetry processors will be called one by one + * before telemetry item is pushed for sending and in the order they were added. + * + * @param telemetryProcessor function, takes Envelope, and optional context object and returns boolean + */ + public addTelemetryProcessor( + telemetryProcessor: ( + envelope: Envelope, + contextObjects?: { [name: string]: any } + ) => boolean + ) { + diag.warn(`addTelemetryProcessor is not supported in ApplicationInsights any longer. ${UNSUPPORTED_MSG}`); + } + + /* + * Removes all telemetry processors + */ + public clearTelemetryProcessors() { + throw new Error("Not implemented"); + } + + public trackNodeHttpRequestSync(telemetry: Contracts.NodeHttpRequestTelemetry) { + diag.warn("trackNodeHttpRequestSync is not implemented and is a no-op. Please use trackRequest instead."); + } + + public trackNodeHttpRequest(telemetry: Contracts.NodeHttpRequestTelemetry) { + diag.warn("trackNodeHttpRequest is not implemented and is a no-op. Please use trackRequest instead."); + } + + public trackNodeHttpDependency(telemetry: Contracts.NodeHttpRequestTelemetry) { + diag.warn("trackNodeHttpDependency is not implemented and is a no-op. Please use trackDependency instead."); + } + + /** + * Immediately send all queued telemetry. + */ + public async flush(): Promise { + return flushAzureMonitor(); + } + + /** + * Shutdown client + */ + public async shutdown(): Promise { + return shutdownAzureMonitor(); + } + + public pushWarningToLog(warning: string) { + this._configWarnings.push(warning); + } +} diff --git a/Declarations/Interfaces.ts b/src/shim/types.ts similarity index 52% rename from Declarations/Interfaces.ts rename to src/shim/types.ts index 7351d9359..91cc73f1a 100644 --- a/Declarations/Interfaces.ts +++ b/src/shim/types.ts @@ -1,13 +1,133 @@ -import http = require('http'); -import https = require('https'); -import azureCore = require("@azure/core-http"); -import { DistributedTracingModes } from "../applicationinsights"; -import { IDisabledExtendedMetrics } from "../AutoCollection/NativePerformance"; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +import { TokenCredential } from "@azure/core-auth"; +import * as http from "http"; +import https = require("https"); + +export const UNSUPPORTED_MSG = "Please reference the Azure Monitor OpenTelemetry Migration Doc for more information. If this functionality is required, please revert to Application Insights 2.X SDK."; +export enum DistributedTracingModes { + /** + * Send Application Insights correlation headers + */ + AI = 0, + + /** + * (Default) Send both W3C Trace Context headers and back-compatibility Application Insights headers + */ + AI_AND_W3C +} + +/** + * Interface which defines which specific extended metrics should be disabled + * + * @export + * @interface IDisabledExtendedMetrics + */ +export interface IDisabledExtendedMetrics { + gc?: boolean; + heap?: boolean; + loop?: boolean; +} + + +export interface ITraceparent { + legacyRootId: string; + parentId: string; + spanId: string; + traceFlag: string; + traceId: string; + version: string; +} + +export interface ITracestate { + fieldmap: string[]; +} + +export interface ICorrelationContext { + operation: { + name: string; + id: string; + parentId: string; // Always used for dependencies, may be ignored in favor of incoming headers for requests + traceparent?: ITraceparent; // w3c context trace + tracestate?: ITracestate; // w3c context state + }; + /** Do not store sensitive information here. + * Properties here are exposed via outgoing HTTP headers for correlating data cross-component. + */ + customProperties: ICustomProperties; +} + +export interface ICustomProperties { + /** + * Get a custom property from the correlation context + */ + getProperty(key: string): string; + /** + * Store a custom property in the correlation context. + * Do not store sensitive information here. + * Properties stored here are exposed via outgoing HTTP headers for correlating data cross-component. + * The characters ',' and '=' are disallowed within keys or values. + */ + setProperty(key: string, value: string): void; +} + +/** + * The context object can be used for writing logs, reading data from bindings, setting outputs and using + * the context.done callback when your exported function is synchronous. A context object is passed + * to your function from the Azure Functions runtime on function invocation. + */ +export interface Context { + traceContext: TraceContext; +} + +/** + * HTTP request object. Provided to your function when using HTTP Bindings. + */ +export interface HttpRequest { + method: string | null; + url: string; + headers: { + [key: string]: string; + }; +} + +/** + * TraceContext information to enable distributed tracing scenarios. + */ +export interface TraceContext { + /** Describes the position of the incoming request in its trace graph in a portable, fixed-length format. */ + traceparent: string | null | undefined; + /** Extends traceparent with vendor-specific data. */ + tracestate: string | null | undefined; + /** Holds additional properties being sent as part of request telemetry. */ + attributes: + | { + [k: string]: string; + } + | null + | undefined; +} + +/** + * Subset of Connection String fields which this SDK can parse. Lower-typecased to + * allow for case-insensitivity across field names + * @type ConnectionStringKey + */ +export interface ConnectionString { + instrumentationkey?: string; + ingestionendpoint?: string; + liveendpoint?: string; + location?: string; + endpointsuffix?: string; + + // Note: this is a node types backcompat equivalent to + // type ConnectionString = { [key in ConnectionStringKey]?: string } +} + +export type ConnectionStringKey = "instrumentationkey" | "ingestionendpoint" | "liveendpoint" | "location"| "endpointsuffix"; export interface IBaseConfig { - /** Application Insights resource instrumentation key */ - instrumentationKey: string; /** The ingestion endpoint to send telemetry payloads to */ endpointUrl: string; /** The maximum number of telemetry items to include in a payload to the ingestion endpoint (Default 250) */ @@ -18,8 +138,6 @@ export interface IBaseConfig { disableAppInsights: boolean; /** The percentage of telemetry items tracked that should be transmitted (Default 100) */ samplingPercentage: number; - /** The time to wait before retrying to retrieve the id for cross-component correlation (Default 30000) */ - correlationIdRetryIntervalMs: number; /** A list of domains to exclude from cross-component header injection */ correlationHeaderExcludedDomains: string[]; /** A proxy server for SDK HTTP traffic (Optional, Default pulled from `http_proxy` environment variable) */ @@ -46,6 +164,10 @@ export interface IBaseConfig { * if true, logger autocollection will include console.log calls (default false) */ enableAutoCollectConsole: boolean; + /** + * Sets tracking error logs from loggers (console, bunyan, winston) as traces. If true errors will be returned as traces + */ + enableLoggerErrorToTrace: boolean; /** * Sets the state of exception tracking (enabled by default) * if true uncaught exceptions will be sent to Application Insights @@ -127,31 +249,67 @@ export interface IBaseConfig { */ extendedMetricDisablers: string; /** - * Disable Statsbeat - */ - disableStatsbeat: boolean; - /** * Live Metrics custom host */ quickPulseHost: string; + /** + * Enable web instrumentation and automatic monitoring, default to false + */ + enableWebInstrumentation: boolean; + /** + * Enable automatic incoming request tracking when running in Azure Functions + */ + enableAutoCollectIncomingRequestAzureFunctions: boolean; + /** + * Application Insights resource connection string for web instrumentation and automatic monitoring + * Note: if no VALID connection string is provided here, web instrumentation will use the connection string during initializing Nodejs SDK + */ + webInstrumentationConnectionString?: string; + /** + * Application Insights web Instrumentation config + * NOTE: if no config is provided here, web instrumentation will use default values + * IMPORTANT NOTE: please convert any functions and objects to double-quoted strings, otherwise they will be skipped. + * For example: if you want to pass in a function: function() { return 'hi'; }, + * you SHOULD wrap it in double-quoted string: "function () {\n return \"hi\";\n}" + * see more Application Insights web Instrumentation config details at: https://github.com/microsoft/ApplicationInsights-JS#configuration + */ + webInstrumentationConfig?: IWebInstrumentationConfig[]; + /** + * Application Insights web Instrumentation CDN url + * NOTE: this config can be changed from env variable: APPLICATIONINSIGHTS_WEB_INSTRUMENTATION_SOURCE or Json Config: webInstrumentationSrc + * If no resouce is provided here, default CDN endpoint: https://js.monitor.azure.com/scripts/b/ai will be used + * see more details at: https://github.com/microsoft/ApplicationInsights-JS + */ + webInstrumentationSrc?: string; +} + +export interface IConfig extends IBaseConfig { + /** AAD TokenCredential to use to authenticate the app */ + aadTokenCredential?: TokenCredential; + + /** An http.Agent to use for SDK HTTP traffic (Optional, Default undefined) */ + httpAgent: http.Agent; + + /** An https.Agent to use for SDK HTTPS traffic (Optional, Default undefined) */ + httpsAgent: https.Agent; } export interface IEnvironmentConfig { /** Connection String used to send telemetry payloads to */ connectionString: string; /** - * In order to track context across asynchronous calls, - * some changes are required in third party libraries such as mongodb and redis. - * By default ApplicationInsights will use diagnostic-channel-publishers to monkey-patch some of these libraries. - * This property is to disable the feature. + * In order to track context across asynchronous calls, + * some changes are required in third party libraries such as mongodb and redis. + * By default ApplicationInsights will use diagnostic-channel-publishers to monkey-patch some of these libraries. + * This property is to disable the feature. * Note that by setting this flag, events may no longer be correctly associated with the right operation. */ noDiagnosticChannel: boolean; /** - * Disable individual monkey-patches. - * Set `noPatchModules` to a comma separated list of packages to disable. - * e.g. `"noPatchModules": "console,redis"` to avoid patching the console and redis packages. - * The following modules are available: `azuresdk, bunyan, console, mongodb, mongodb-core, mysql, redis, winston, pg`, and `pg-pool`. + * Disable individual monkey-patches. + * Set `noPatchModules` to a comma separated list of packages to disable. + * e.g. `"noPatchModules": "console,redis"` to avoid patching the console and redis packages. + * The following modules are available: `azuresdk, bunyan, console, mongodb, mongodb-core, mysql, redis, winston, pg`, and `pg-pool`. */ noPatchModules: string; /** @@ -160,13 +318,16 @@ export interface IEnvironmentConfig { noHttpAgentKeepAlive: boolean; } -export interface IJsonConfig extends IBaseConfig, IEnvironmentConfig { }; +export interface IJsonConfig extends IBaseConfig, IEnvironmentConfig { } -export interface IConfig extends IBaseConfig { - /** An http.Agent to use for SDK HTTP traffic (Optional, Default undefined) */ - httpAgent: http.Agent; - /** An https.Agent to use for SDK HTTPS traffic (Optional, Default undefined) */ - httpsAgent: https.Agent; - /** AAD TokenCredential to use to authenticate the app */ - aadTokenCredential?: azureCore.TokenCredential; -} +export interface IWebInstrumentationConfig { + /** + * Name of Application Insights web Instrumentation config to be changed + * see more Application Insights web Instrumentation config details at: https://github.com/microsoft/ApplicationInsights-JS#configuration + */ + name: string; + /** + * value provided to replace the default config value above + */ + value: string | boolean | number; +} \ No newline at end of file diff --git a/src/traces/spanProcessor.ts b/src/traces/spanProcessor.ts new file mode 100644 index 000000000..4354e4b07 --- /dev/null +++ b/src/traces/spanProcessor.ts @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { Context } from "@opentelemetry/api"; +import { ReadableSpan, Span, SpanProcessor } from "@opentelemetry/sdk-trace-base"; +import { PerformanceCounterMetrics } from "../metrics/performanceCounters"; + +/** + * Azure Monitor Span Processor. + * @internal + */ +export class AzureMonitorSpanProcessor implements SpanProcessor { + private readonly _metricHandler: PerformanceCounterMetrics; + + constructor(metricHandler: PerformanceCounterMetrics) { + this._metricHandler = metricHandler; + } + + forceFlush(): Promise { + return Promise.resolve(); + } + + onStart(span: Span, _context: Context): void { + return; + } + + onEnd(span: ReadableSpan): void { + this._metricHandler.recordSpan(span); + } + + shutdown(): Promise { + return Promise.resolve(); + } +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 000000000..f57357699 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { AzureMonitorOpenTelemetryOptions as DistroOptions, InstrumentationOptions as DistroInstrumentationOptions } from "@azure/monitor-opentelemetry"; +import { SeverityNumber } from "@opentelemetry/api-logs"; +import { InstrumentationConfig } from "@opentelemetry/instrumentation"; +import { OTLPExporterNodeConfigBase } from "@opentelemetry/otlp-exporter-base"; + + +export const AZURE_MONITOR_OPENTELEMETRY_VERSION = "1.0.0-beta.12"; +export const DEFAULT_ROLE_NAME = "Web"; +process.env["AZURE_MONITOR_DISTRO_VERSION"] = AZURE_MONITOR_OPENTELEMETRY_VERSION; + +/** + * Azure Monitor OpenTelemetry Options + */ +export interface AzureMonitorOpenTelemetryOptions extends DistroOptions { + /** + * Sets the state of exception tracking (enabled by default) + * if true uncaught exceptions will be sent to Application Insights + */ + enableAutoCollectExceptions?: boolean; + /** OTLP Trace Exporter Configuration */ + otlpTraceExporterConfig?: OTLPExporterConfig; + /** OTLP Metric Exporter Configuration */ + otlpMetricExporterConfig?: OTLPExporterConfig; + /** OTLP Log Exporter Configuration */ + otlpLogExporterConfig?: OTLPExporterConfig; + /** + * Sets the state of performance tracking (enabled by default) + * if true performance counters will be collected every second and sent to Azure Monitor + */ + enableAutoCollectPerformance?: boolean; +} + +export interface InstrumentationOptions extends DistroInstrumentationOptions { + /** Console Instrumentation Config */ + console?: InstrumentationConfig & { logSendingLevel?: SeverityNumber }; + /** Winston Instrumentation Config */ + winston?: InstrumentationConfig & { logSendingLevel?: SeverityNumber }; +} + +/** + * OTLP Exporter Options + */ +export interface OTLPExporterConfig extends OTLPExporterNodeConfigBase { + /** Enable/Disable OTLP Exporter */ + enabled?: boolean; +} + +export interface InstrumentationOptionsType { + [key: string]: { enabled: boolean } +} diff --git a/Tests/BackCompatibility/RunBackCompatTests.js b/test/backCompatibility/RunBackCompatTests.js similarity index 96% rename from Tests/BackCompatibility/RunBackCompatTests.js rename to test/backCompatibility/RunBackCompatTests.js index 67b37111f..ff08f18e5 100644 --- a/Tests/BackCompatibility/RunBackCompatTests.js +++ b/test/backCompatibility/RunBackCompatTests.js @@ -1,6 +1,6 @@ -const fs = require('fs'); -const path = require('path'); -const childProcess = require('child_process'); +const fs } from 'fs'); +const path } from 'path'); +const childProcess } from 'child_process'); function help() { console.log( diff --git a/test/backCompatibility/node10Types/main.ts b/test/backCompatibility/node10Types/main.ts new file mode 100644 index 000000000..218b2682d --- /dev/null +++ b/test/backCompatibility/node10Types/main.ts @@ -0,0 +1,6 @@ +import appInsights = require("applicationinsights"); +import http = require("http"); + +appInsights.setup().start(); +appInsights.defaultClient.trackEvent({ name: "Test event" }); +appInsights.defaultClient.trackNodeHttpDependency({ options: {}, request: http.request({}) }); diff --git a/Tests/BackCompatibility/Node10Types/package-lock.json b/test/backCompatibility/node10Types/package-lock.json similarity index 100% rename from Tests/BackCompatibility/Node10Types/package-lock.json rename to test/backCompatibility/node10Types/package-lock.json diff --git a/Tests/BackCompatibility/Node10Types/package.json b/test/backCompatibility/node10Types/package.json similarity index 100% rename from Tests/BackCompatibility/Node10Types/package.json rename to test/backCompatibility/node10Types/package.json diff --git a/Tests/BackCompatibility/Node10Types/tsconfig.json b/test/backCompatibility/node10Types/tsconfig.json similarity index 100% rename from Tests/BackCompatibility/Node10Types/tsconfig.json rename to test/backCompatibility/node10Types/tsconfig.json diff --git a/test/backCompatibility/oldTSC/main.ts b/test/backCompatibility/oldTSC/main.ts new file mode 100644 index 000000000..218b2682d --- /dev/null +++ b/test/backCompatibility/oldTSC/main.ts @@ -0,0 +1,6 @@ +import appInsights = require("applicationinsights"); +import http = require("http"); + +appInsights.setup().start(); +appInsights.defaultClient.trackEvent({ name: "Test event" }); +appInsights.defaultClient.trackNodeHttpDependency({ options: {}, request: http.request({}) }); diff --git a/test/backCompatibility/oldTSC/package-lock.json b/test/backCompatibility/oldTSC/package-lock.json new file mode 100644 index 000000000..7f3bfd6fb --- /dev/null +++ b/test/backCompatibility/oldTSC/package-lock.json @@ -0,0 +1,3986 @@ +{ + "name": "applicationinsights", + "version": "3.0.0-beta.12", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true + }, + "@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@azure/abort-controller": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@azure/core-auth": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.5.0.tgz", + "integrity": "sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-util": "^1.1.0", + "tslib": "^2.2.0" + } + }, + "@azure/core-client": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.7.3.tgz", + "integrity": "sha512-kleJ1iUTxcO32Y06dH9Pfi9K4U+Tlb111WXEnbt7R/ne+NLRwppZiTGJuTD5VVoxTMK5NTbEtm5t2vcdNCFe2g==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.9.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.0.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + } + }, + "@azure/core-rest-pipeline": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.12.0.tgz", + "integrity": "sha512-+MnSB0vGZjszSzr5AW8z93/9fkDu2RLtWmAN8gskURq7EW2sSwqy8jZa0V26rjuBVkwhdA3Hw8z3VWoeBUOw+A==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.3.0", + "@azure/logger": "^1.0.0", + "form-data": "^4.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "tslib": "^2.2.0" + } + }, + "@azure/core-tracing": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz", + "integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@azure/core-util": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.4.0.tgz", + "integrity": "sha512-eGAyJpm3skVQoLiRqm/xPa+SXi/NPDdSHMxbRAz2lSprd+Zs+qrpQGQQ2VQ3Nttu+nSZR4XoYQC71LbEI7jsig==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + } + }, + "@azure/functions": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@azure/functions/-/functions-3.5.1.tgz", + "integrity": "sha512-6UltvJiuVpvHSwLcK/Zc6NfUwlkDLOFFx97BHCJzlWNsfiWwzwmTsxJXg4kE/LemKTHxPpfoPE+kOJ8hAdiKFQ==", + "requires": { + "iconv-lite": "^0.6.3", + "long": "^4.0.0", + "uuid": "^8.3.0" + } + }, + "@azure/identity": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-3.2.4.tgz", + "integrity": "sha512-t63oyi2LAn+ZAehYA7SDlhJDd1J0eLO3a21mxTaJcXqKW/tbRbKmo/BeyyTIXbBaoeTFn0xnyQHyomwndTqKUA==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.4.0", + "@azure/core-rest-pipeline": "^1.1.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.0.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^2.37.1", + "@azure/msal-common": "^13.1.0", + "@azure/msal-node": "^1.17.3", + "events": "^3.0.0", + "jws": "^4.0.0", + "open": "^8.0.0", + "stoppable": "^1.1.0", + "tslib": "^2.2.0", + "uuid": "^8.3.0" + } + }, + "@azure/logger": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz", + "integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@azure/monitor-opentelemetry": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/@azure/monitor-opentelemetry/-/monitor-opentelemetry-1.0.0-beta.2.tgz", + "integrity": "sha512-xDJluJF34NOJxY4maXx/asmxOVvH3H7EL2+SQ+cRlMm8JLKgduaUsG8UmX4BQa0l1FJIh8A5rrxzm73qHvgH1Q==", + "requires": { + "@azure/functions": "^3.2.0", + "@azure/monitor-opentelemetry-exporter": "1.0.0-beta.15", + "@azure/opentelemetry-instrumentation-azure-sdk": "^1.0.0-beta.3", + "@opentelemetry/api": "^1.4.1", + "@opentelemetry/api-logs": "^0.41.2", + "@opentelemetry/core": "^1.15.2", + "@opentelemetry/instrumentation": "^0.41.2", + "@opentelemetry/instrumentation-http": "^0.41.2", + "@opentelemetry/instrumentation-mongodb": "^0.36.0", + "@opentelemetry/instrumentation-mysql": "^0.34.0", + "@opentelemetry/instrumentation-pg": "^0.36.0", + "@opentelemetry/instrumentation-redis": "^0.35.0", + "@opentelemetry/instrumentation-redis-4": "^0.35.0", + "@opentelemetry/resources": "^1.15.0", + "@opentelemetry/sdk-logs": "^0.41.0", + "@opentelemetry/sdk-metrics": "^1.15.0", + "@opentelemetry/sdk-trace-base": "^1.15.0", + "@opentelemetry/sdk-trace-node": "^1.15.0", + "@opentelemetry/semantic-conventions": "^1.15.0", + "tslib": "^2.2.0" + } + }, + "@azure/monitor-opentelemetry-exporter": { + "version": "1.0.0-beta.15", + "resolved": "https://registry.npmjs.org/@azure/monitor-opentelemetry-exporter/-/monitor-opentelemetry-exporter-1.0.0-beta.15.tgz", + "integrity": "sha512-aKKVZpGU9ixCe9zuQ7i0XRVYBVSTbTJgBtwlY17Ab+TQWnU6Lh9QbaEr0kmlmhazfaE26K/ZSgtoL+VzZiuFzQ==", + "requires": { + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.0.0", + "@azure/core-rest-pipeline": "^1.1.0", + "@opentelemetry/api": "^1.4.1", + "@opentelemetry/api-logs": "^0.41.2", + "@opentelemetry/core": "^1.15.2", + "@opentelemetry/resources": "^1.15.2", + "@opentelemetry/sdk-logs": "^0.41.2", + "@opentelemetry/sdk-metrics": "^1.15.2", + "@opentelemetry/sdk-trace-base": "^1.15.2", + "@opentelemetry/semantic-conventions": "^1.15.2", + "tslib": "^2.2.0" + } + }, + "@azure/msal-browser": { + "version": "2.38.2", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-2.38.2.tgz", + "integrity": "sha512-71BeIn2we6LIgMplwCSaMq5zAwmalyJR3jFcVOZxNVfQ1saBRwOD+P77nLs5vrRCedVKTq8RMFhIOdpMLNno0A==", + "requires": { + "@azure/msal-common": "13.3.0" + } + }, + "@azure/msal-common": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-13.3.0.tgz", + "integrity": "sha512-/VFWTicjcJbrGp3yQP7A24xU95NiDMe23vxIU1U6qdRPFsprMDNUohMudclnd+WSHE4/McqkZs/nUU3sAKkVjg==" + }, + "@azure/msal-node": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.18.2.tgz", + "integrity": "sha512-bLbuhF9Q5cgwj9tt8R7EV9MbCwGuFgZiv6Gw0VvHx5AcWHhh2m8hYginGagB4EucxKueFDwZP6aZVAxfuD/lUQ==", + "requires": { + "@azure/msal-common": "13.3.0", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + } + }, + "@azure/opentelemetry-instrumentation-azure-sdk": { + "version": "1.0.0-beta.5", + "resolved": "https://registry.npmjs.org/@azure/opentelemetry-instrumentation-azure-sdk/-/opentelemetry-instrumentation-azure-sdk-1.0.0-beta.5.tgz", + "integrity": "sha512-fsUarKQDvjhmBO4nIfaZkfNSApm1hZBzcvpNbSrXdcUBxu7lRvKsV5DnwszX7cnhLyVOW9yl1uigtRQ1yDANjA==", + "requires": { + "@azure/core-tracing": "^1.0.0", + "@azure/logger": "^1.0.0", + "@opentelemetry/api": "^1.4.1", + "@opentelemetry/core": "^1.15.2", + "@opentelemetry/instrumentation": "^0.41.2", + "tslib": "^2.2.0" + } + }, + "@babel/code-frame": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", + "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.22.10", + "chalk": "^2.4.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/compat-data": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", + "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", + "dev": true + }, + "@babel/core": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.11.tgz", + "integrity": "sha512-lh7RJrtPdhibbxndr6/xx0w8+CVlY5FJZiaSz908Fpy+G0xkBFTvwLcKJFF4PJxVfGhVWNebikpWGnOoC71juQ==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.10", + "@babel/generator": "^7.22.10", + "@babel/helper-compilation-targets": "^7.22.10", + "@babel/helper-module-transforms": "^7.22.9", + "@babel/helpers": "^7.22.11", + "@babel/parser": "^7.22.11", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.11", + "@babel/types": "^7.22.11", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", + "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", + "dev": true, + "requires": { + "@babel/types": "^7.22.10", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz", + "integrity": "sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.5", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "dev": true + }, + "@babel/helper-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "dev": true, + "requires": { + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-module-imports": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", + "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-module-transforms": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", + "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.5" + } + }, + "@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", + "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "dev": true + }, + "@babel/helpers": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.11.tgz", + "integrity": "sha512-vyOXC8PBWaGc5h7GMsNx68OH33cypkEDJCHvYVVgVbbxJDROYVtexSk0gK5iCF1xNjRIN2s8ai7hwkWDq5szWg==", + "dev": true, + "requires": { + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.11", + "@babel/types": "^7.22.11" + } + }, + "@babel/highlight": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", + "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.11.tgz", + "integrity": "sha512-R5zb8eJIBPJriQtbH/htEQy4k7E2dHWlD2Y2VT07JCzwYZHBxV5ZYtM0UhXSNMT74LyxuM+b1jdL7pSesXbC/g==", + "dev": true + }, + "@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" + } + }, + "@babel/traverse": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.11.tgz", + "integrity": "sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.10", + "@babel/generator": "^7.22.10", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.22.11", + "@babel/types": "^7.22.11", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "dependencies": { + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", + "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", + "to-fast-properties": "^2.0.0" + } + }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.0.tgz", + "integrity": "sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + } + }, + "@eslint/js": { + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", + "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", + "dev": true + }, + "@humanwhocodes/config-array": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", + "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dev": true, + "requires": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@opentelemetry/api": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz", + "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==" + }, + "@opentelemetry/api-logs": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.41.2.tgz", + "integrity": "sha512-JEV2RAqijAFdWeT6HddYymfnkiRu2ASxoTBr4WsnGJhOjWZkEy6vp+Sx9ozr1NaIODOa2HUyckExIqQjn6qywQ==", + "requires": { + "@opentelemetry/api": "^1.0.0" + } + }, + "@opentelemetry/context-async-hooks": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.15.2.tgz", + "integrity": "sha512-VAMHG67srGFQDG/N2ns5AyUT9vUcoKpZ/NpJ5fDQIPfJd7t3ju+aHwvDsMcrYBWuCh03U3Ky6o16+872CZchBg==" + }, + "@opentelemetry/core": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.15.2.tgz", + "integrity": "sha512-+gBv15ta96WqkHZaPpcDHiaz0utiiHZVfm2YOYSqFGrUaJpPkMoSuLBB58YFQGi6Rsb9EHos84X6X5+9JspmLw==", + "requires": { + "@opentelemetry/semantic-conventions": "1.15.2" + } + }, + "@opentelemetry/exporter-logs-otlp-http": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.41.2.tgz", + "integrity": "sha512-9nG6rvMK1toG3On7JAeKpEXJeaTmFzmMKDXE3+gvqye3SORqoRuJDbh+o3IXL2K7qmPn/34m9pl9PgArUei0nQ==", + "requires": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/otlp-exporter-base": "0.41.2", + "@opentelemetry/otlp-transformer": "0.41.2", + "@opentelemetry/sdk-logs": "0.41.2" + } + }, + "@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.41.2.tgz", + "integrity": "sha512-+YeIcL4nuldWE89K8NBLImpXCvih04u1MBnn8EzvoywG2TKR5JC3CZEPepODIxlsfGSgP8W5khCEP1NHZzftYw==", + "requires": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/otlp-exporter-base": "0.41.2", + "@opentelemetry/otlp-transformer": "0.41.2", + "@opentelemetry/resources": "1.15.2", + "@opentelemetry/sdk-metrics": "1.15.2" + } + }, + "@opentelemetry/exporter-trace-otlp-http": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.41.2.tgz", + "integrity": "sha512-Y0fGLipjZXLMelWtlS1/MDtrPxf25oM408KukRdkN31a1MEFo4h/ZkNwS7ZfmqHGUa+4rWRt2bi6JBiqy7Ytgw==", + "requires": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/otlp-exporter-base": "0.41.2", + "@opentelemetry/otlp-transformer": "0.41.2", + "@opentelemetry/resources": "1.15.2", + "@opentelemetry/sdk-trace-base": "1.15.2" + } + }, + "@opentelemetry/instrumentation": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.41.2.tgz", + "integrity": "sha512-rxU72E0pKNH6ae2w5+xgVYZLzc5mlxAbGzF4shxMVK8YC2QQsfN38B2GPbj0jvrKWWNUElfclQ+YTykkNg/grw==", + "requires": { + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "1.4.2", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.1", + "shimmer": "^1.2.1" + } + }, + "@opentelemetry/instrumentation-http": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.41.2.tgz", + "integrity": "sha512-dzOC6xkfK0LM6Dzo91aInLdSbdIzKA0IgSDnyLi6YZ0Z7c1bfrFncFx/3gZs8vi+KXLALgfMlpzE7IYDW/cM3A==", + "requires": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/instrumentation": "0.41.2", + "@opentelemetry/semantic-conventions": "1.15.2", + "semver": "^7.5.1" + } + }, + "@opentelemetry/instrumentation-mongodb": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.36.1.tgz", + "integrity": "sha512-//FdYXGcUO08Y1tVPXlcEvUYCnRU8tlBgYBe3ZMjF7E1GMaFti4Xy6sAHVreyl0ZQjBTaGtHdyORHIOTKUM4ZA==", + "requires": { + "@opentelemetry/instrumentation": "^0.41.2", + "@opentelemetry/sdk-metrics": "^1.9.1", + "@opentelemetry/semantic-conventions": "^1.0.0" + } + }, + "@opentelemetry/instrumentation-mysql": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.34.1.tgz", + "integrity": "sha512-zQq7hN3ILm1vIJCGeKHRc4pTK8LOmkTt8oKWf0v+whFs7axieIhXZMoCqIBm6BigLy3Trg5iaKyuSrx7kO6q2g==", + "requires": { + "@opentelemetry/instrumentation": "^0.41.2", + "@opentelemetry/semantic-conventions": "^1.0.0", + "@types/mysql": "2.15.21" + } + }, + "@opentelemetry/instrumentation-pg": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.36.1.tgz", + "integrity": "sha512-k8L7RSRTQ6e+DbHEXZB8Tmf/efkQnWKeClpZb3TEdb34Pvme4PmcpG2zb6JtM99nNrshNlVDLCZ90U3xDneTbw==", + "requires": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.41.2", + "@opentelemetry/semantic-conventions": "^1.0.0", + "@opentelemetry/sql-common": "^0.40.0", + "@types/pg": "8.6.1", + "@types/pg-pool": "2.0.3" + } + }, + "@opentelemetry/instrumentation-redis": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.35.1.tgz", + "integrity": "sha512-zY7eTzGyJCMX/0o04Q9yLy7gllf7Zh4s+g7Kv1d2cMLtTt9zGSlncqj49uNCnneywnpMNRUIwcmd+Ch1bQeh+g==", + "requires": { + "@opentelemetry/instrumentation": "^0.41.2", + "@opentelemetry/redis-common": "^0.36.1", + "@opentelemetry/semantic-conventions": "^1.0.0" + } + }, + "@opentelemetry/instrumentation-redis-4": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.35.1.tgz", + "integrity": "sha512-tQ07wvtjUbHSvvhPPvWyZjYTSzVBTpC746ro5szLnniodvxtKkmP/N+R9KyFXfyH7wwaLIR1Scgq3XSGSppt+Q==", + "requires": { + "@opentelemetry/instrumentation": "^0.41.2", + "@opentelemetry/redis-common": "^0.36.1", + "@opentelemetry/semantic-conventions": "^1.0.0" + } + }, + "@opentelemetry/otlp-exporter-base": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.41.2.tgz", + "integrity": "sha512-pfwa6d+Dax3itZcGWiA0AoXeVaCuZbbqUTsCtOysd2re8C2PWXNxDONUfBWsn+KgxAdi+ljwTjJGiaVLDaIEvQ==", + "requires": { + "@opentelemetry/core": "1.15.2" + } + }, + "@opentelemetry/otlp-transformer": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.41.2.tgz", + "integrity": "sha512-jJbPwB0tNu2v+Xi0c/v/R3YBLJKLonw1p+v3RVjT2VfzeUyzSp/tBeVdY7RZtL6dzZpA9XSmp8UEfWIFQo33yA==", + "requires": { + "@opentelemetry/api-logs": "0.41.2", + "@opentelemetry/core": "1.15.2", + "@opentelemetry/resources": "1.15.2", + "@opentelemetry/sdk-logs": "0.41.2", + "@opentelemetry/sdk-metrics": "1.15.2", + "@opentelemetry/sdk-trace-base": "1.15.2" + } + }, + "@opentelemetry/propagator-b3": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.15.2.tgz", + "integrity": "sha512-ZSrL3DpMEDsjD8dPt9Ze3ue53nEXJt512KyxXlLgLWnSNbe1mrWaXWkh7OLDoVJh9LqFw+tlvAhDVt/x3DaFGg==", + "requires": { + "@opentelemetry/core": "1.15.2" + } + }, + "@opentelemetry/propagator-jaeger": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.15.2.tgz", + "integrity": "sha512-6m1yu7PVDIRz6BwA36lacfBZJCfAEHKgu+kSyukNwVdVjsTNeyD9xNPQnkl0WN7Rvhk8/yWJ83tLPEyGhk1wCQ==", + "requires": { + "@opentelemetry/core": "1.15.2" + } + }, + "@opentelemetry/redis-common": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.36.1.tgz", + "integrity": "sha512-YjfNEr7DK1Ymc5H0bzhmqVvMcCs+PUEUerzrpTFdHfZxj3HpnnjZTIFKx/gxiL/sajQ8dxycjlreoYTVYKBXlw==" + }, + "@opentelemetry/resources": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.15.2.tgz", + "integrity": "sha512-xmMRLenT9CXmm5HMbzpZ1hWhaUowQf8UB4jMjFlAxx1QzQcsD3KFNAVX/CAWzFPtllTyTplrA4JrQ7sCH3qmYw==", + "requires": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/semantic-conventions": "1.15.2" + } + }, + "@opentelemetry/sdk-logs": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.41.2.tgz", + "integrity": "sha512-smqKIw0tTW15waj7BAPHFomii5c3aHnSE4LQYTszGoK5P9nZs8tEAIpu15UBxi3aG31ZfsLmm4EUQkjckdlFrw==", + "requires": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/resources": "1.15.2" + } + }, + "@opentelemetry/sdk-metrics": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.15.2.tgz", + "integrity": "sha512-9aIlcX8GnhcsAHW/Wl8bzk4ZnWTpNlLtud+fxUfBtFATu6OZ6TrGrF4JkT9EVrnoxwtPIDtjHdEsSjOqisY/iA==", + "requires": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/resources": "1.15.2", + "lodash.merge": "^4.6.2" + } + }, + "@opentelemetry/sdk-trace-base": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.2.tgz", + "integrity": "sha512-BEaxGZbWtvnSPchV98qqqqa96AOcb41pjgvhfzDij10tkBhIu9m0Jd6tZ1tJB5ZHfHbTffqYVYE0AOGobec/EQ==", + "requires": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/resources": "1.15.2", + "@opentelemetry/semantic-conventions": "1.15.2" + } + }, + "@opentelemetry/sdk-trace-node": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.15.2.tgz", + "integrity": "sha512-5deakfKLCbPpKJRCE2GPI8LBE2LezyvR17y3t37ZI3sbaeogtyxmBaFV+slmG9fN8OaIT+EUsm1QAT1+z59gbQ==", + "requires": { + "@opentelemetry/context-async-hooks": "1.15.2", + "@opentelemetry/core": "1.15.2", + "@opentelemetry/propagator-b3": "1.15.2", + "@opentelemetry/propagator-jaeger": "1.15.2", + "@opentelemetry/sdk-trace-base": "1.15.2", + "semver": "^7.5.1" + } + }, + "@opentelemetry/semantic-conventions": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz", + "integrity": "sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw==" + }, + "@opentelemetry/sql-common": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.40.0.tgz", + "integrity": "sha512-vSqRJYUPJVjMFQpYkQS3ruexCPSZJ8esne3LazLwtCPaPRvzZ7WG3tX44RouAn7w4wMp8orKguBqtt+ng2UTnw==", + "requires": { + "@opentelemetry/core": "^1.1.0" + } + }, + "@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@sinonjs/samsam": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.3.1.tgz", + "integrity": "sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "@sinonjs/text-encoding": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", + "dev": true + }, + "@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==" + }, + "@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "dev": true + }, + "@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "dev": true + }, + "@types/microsoft__typescript-etw": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@types/microsoft__typescript-etw/-/microsoft__typescript-etw-0.1.1.tgz", + "integrity": "sha512-zdgHyZJEwbFKI6zhOqWPsNMhlrAk6qMrn9VMA6VQtRt/F+jNJKeaHIMysuO9oTLv0fWcli0gwUrMv8MeFyb3Sw==", + "dev": true + }, + "@types/mocha": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-7.0.2.tgz", + "integrity": "sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w==", + "dev": true + }, + "@types/mysql": { + "version": "2.15.21", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.21.tgz", + "integrity": "sha512-NPotx5CVful7yB+qZbWtXL2fA4e7aEHkihHLjklc6ID8aq7bhguHgeIoC1EmSNTAuCgI6ZXrjt2ZSaXnYX0EUg==", + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==" + }, + "@types/pg": { + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.1.tgz", + "integrity": "sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==", + "requires": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "@types/pg-pool": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.3.tgz", + "integrity": "sha512-fwK5WtG42Yb5RxAwxm3Cc2dJ39FlgcaNiXKvtTLAwtCn642X7dgel+w1+cLWwpSOFImR3YjsZtbkfjxbHtFAeg==", + "requires": { + "@types/pg": "*" + } + }, + "@types/semver": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz", + "integrity": "sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ==", + "dev": true + }, + "@types/shimmer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.0.2.tgz", + "integrity": "sha512-dKkr1bTxbEsFlh2ARpKzcaAmsYixqt9UyCdoEZk8rHyE4iQYcDCyvSjDSf7JUWJHlJiTtbIoQjxKh6ViywqDAg==" + }, + "@types/sinon": { + "version": "10.0.16", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.16.tgz", + "integrity": "sha512-j2Du5SYpXZjJVJtXBokASpPRj+e2z+VUhCPHmM6WMfe3dpHu6iVKJMU6AiBcMp/XTAYnEj6Wc1trJUWwZ0QaAQ==", + "dev": true, + "requires": { + "@types/sinonjs__fake-timers": "*" + } + }, + "@types/sinonjs__fake-timers": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz", + "integrity": "sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "dependencies": { + "@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + } + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==" + }, + "acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==" + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "requires": { + "default-require-extensions": "^3.0.0" + } + }, + "applicationinsights-native-metrics": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/applicationinsights-native-metrics/-/applicationinsights-native-metrics-0.0.8.tgz", + "integrity": "sha512-3areRz2+Q1EPWbQYokfOgYXLkZyBzeReI9716nw97p697gFUfGqYgwJ5fCopftvHycW2zuRWTFJWmA70S5605A==", + "dev": true, + "requires": { + "@mapbox/node-pre-gyp": "^1.0.8", + "nan": "^2.14.0" + } + }, + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "dev": true + }, + "are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "browserslist": { + "version": "4.21.10", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", + "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001517", + "electron-to-chromium": "^1.4.477", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.11" + } + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "requires": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001523", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001523.tgz", + "integrity": "sha512-I5q5cisATTPZ1mc588Z//pj/Ox80ERYDfR71YnvY7raS/NOk8xXlZcB0sF7JdqaV//kOaa6aus7lRfpdnt1eBA==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + }, + "cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==" + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true + }, + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "default-require-extensions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", + "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", + "dev": true, + "requires": { + "strip-bom": "^4.0.0" + } + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true + }, + "detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "dev": true + }, + "diagnostic-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/diagnostic-channel/-/diagnostic-channel-1.1.1.tgz", + "integrity": "sha512-r2HV5qFkUICyoaKlBEpLKHjxMXATUf/l+h8UZPGBHGLy4DDiY2sOLcIctax4eRnTw5wH2jTMExLntGPJ8eOJxw==", + "requires": { + "semver": "^7.5.3" + } + }, + "diagnostic-channel-publishers": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.7.tgz", + "integrity": "sha512-SEECbY5AiVt6DfLkhkaHNeshg1CogdLLANA8xlG/TKvS+XUgvIKl7VspJGYiEdL5OUyzMVnr7o0AwB7f+/Mjtg==" + }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "electron-to-chromium": { + "version": "1.4.502", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.502.tgz", + "integrity": "sha512-xqeGw3Gr6o3uyHy/yKjdnDQHY2RQvXcGC2cfHjccK1IGkH6cX1WQBN8EeC/YpwPhGkBaikDTecJ8+ssxSVRQlw==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.46.0.tgz", + "integrity": "sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.1", + "@eslint/js": "^8.46.0", + "@humanwhocodes/config-array": "^0.11.10", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.2", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "dependencies": { + "eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + } + } + }, + "eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "requires": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + } + }, + "eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "requires": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + }, + "espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "requires": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + } + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dev": true, + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + } + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true + }, + "hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "requires": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-in-the-middle": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz", + "integrity": "sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw==", + "requires": { + "acorn": "^8.8.2", + "acorn-import-assertions": "^1.9.0", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-core-module": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "requires": { + "has": "^1.0.3" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "requires": { + "append-transform": "^2.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "istanbul-lib-processinfo": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "dev": true, + "requires": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^8.3.2" + } + }, + "istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "requires": { + "semver": "^7.5.3" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, + "istanbul-reports": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + }, + "jsonwebtoken": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz", + "integrity": "sha512-K8wx7eJ5TPvEjuiVSkv167EVboBDv9PZdDoF7BgeQnBLVvZWW9clr2PsQHVJDTKaEIH5JBIwHujGcHp7GgI2eg==", + "requires": { + "jws": "^3.2.2", + "lodash": "^4.17.21", + "ms": "^2.1.1", + "semver": "^7.3.8" + }, + "dependencies": { + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + } + } + }, + "just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "dev": true + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "requires": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + }, + "dependencies": { + "jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + } + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "dev": true + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + }, + "dependencies": { + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "dev": true, + "requires": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "nan": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "dev": true + }, + "nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "nise": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/nise/-/nise-4.1.0.tgz", + "integrity": "sha512-eQMEmGN/8arp0xsvGoQ+B1qvSkR73B1nWSCh7nOt5neMCtwcQVYQGdzQMhcNscktTsWB54xnlSQFzOAPJD8nXA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0", + "@sinonjs/fake-timers": "^6.0.0", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, + "nock": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/nock/-/nock-12.0.3.tgz", + "integrity": "sha512-QNb/j8kbFnKCiyqi9C5DD0jH/FubFGj5rt9NQFONXwQm3IPB0CULECg/eS3AU1KgZb/6SwUa4/DTRKhVxkGABw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.13", + "propagate": "^2.0.0" + } + }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "requires": { + "process-on-spawn": "^1.0.0" + } + }, + "node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "dev": true + }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dev": true, + "requires": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "requires": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, + "optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "requires": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "requires": { + "isarray": "0.0.1" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" + }, + "pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + }, + "pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "requires": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + } + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } + } + }, + "postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" + }, + "postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==" + }, + "postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" + }, + "postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "requires": { + "xtend": "^4.0.0" + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true + }, + "process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "requires": { + "fromentries": "^1.2.0" + } + }, + "propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true + }, + "punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", + "dev": true, + "requires": { + "es6-error": "^4.0.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "require-in-the-middle": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.2.0.tgz", + "integrity": "sha512-3TLx5TGyAY6AOqLBoXmHkNql0HIf2RGbuMgCDT2WO/uGVAPJs6h7Kl+bN6TIZGd9bWhWPwnDnTHGtW8Iu77sdw==", + "requires": { + "debug": "^4.1.1", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.1" + } + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "resolve": { + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", + "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "requires": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "sinon": { + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", + "integrity": "sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.8.1", + "@sinonjs/fake-timers": "^6.0.1", + "@sinonjs/samsam": "^5.3.1", + "diff": "^4.0.2", + "nise": "^4.0.4", + "supports-color": "^7.1.0" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + } + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "requires": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "stoppable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "tar": { + "version": "6.1.15", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz", + "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", + "dev": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typescript": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "dev": true + }, + "update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true + }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} \ No newline at end of file diff --git a/Tests/BackCompatibility/OldTSC/package.json b/test/backCompatibility/oldTSC/package.json similarity index 100% rename from Tests/BackCompatibility/OldTSC/package.json rename to test/backCompatibility/oldTSC/package.json diff --git a/Tests/BackCompatibility/OldTSC/tsconfig.json b/test/backCompatibility/oldTSC/tsconfig.json similarity index 100% rename from Tests/BackCompatibility/OldTSC/tsconfig.json rename to test/backCompatibility/oldTSC/tsconfig.json diff --git a/test/certs/generateKey.sh b/test/certs/generateKey.sh new file mode 100644 index 000000000..2b89c5500 --- /dev/null +++ b/test/certs/generateKey.sh @@ -0,0 +1,7 @@ +# +# Usage: generateKey.sh +# +# Generates private key for test cert +# + +openssl req -x509 -nodes -newkey rsa -keyout server-key.pem -out server-cert.pem -days 3650 -subj "/C=CL/ST=RM/L=OpenTelemetryTest/O=Root/OU=Test/CN=ca" diff --git a/test/certs/server-cert.pem b/test/certs/server-cert.pem new file mode 100644 index 000000000..4dd904f08 --- /dev/null +++ b/test/certs/server-cert.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDozCCAougAwIBAgIUZdBdHwmbcYbMUvOesYRyYlg11JMwDQYJKoZIhvcNAQEL +BQAwYTELMAkGA1UEBhMCQ0wxCzAJBgNVBAgMAlJNMRowGAYDVQQHDBFPcGVuVGVs +ZW1ldHJ5VGVzdDENMAsGA1UECgwEUm9vdDENMAsGA1UECwwEVGVzdDELMAkGA1UE +AwwCY2EwHhcNMjEwNjA4MDgxNTE2WhcNMzEwNjA2MDgxNTE2WjBhMQswCQYDVQQG +EwJDTDELMAkGA1UECAwCUk0xGjAYBgNVBAcMEU9wZW5UZWxlbWV0cnlUZXN0MQ0w +CwYDVQQKDARSb290MQ0wCwYDVQQLDARUZXN0MQswCQYDVQQDDAJjYTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALw9Shaqp1O1zgYK8z5pnvuHK3o9cUNT ++EJz5KLADn11HSgfj5S96s6lDP9mUy8oDXdk9zSH8QsPp/maVh0AM1WrPDDTBJT5 +vdPyUezzn4wvBrJSG/mmy/p4QU1srmE0ueuhgot97GZa9qu+gOqGei3YTjrbx14H +jyvexx+QhqrpUg46qAm67pJHC5jN6LmufoIvepyvQCEbKN+n2B2tAn9on1wo7UmB +igTEiWxk1Gn70IJuyTEbpHoLFviQ5kvTYXM/2KHMOXjZM9fQxtbseNUmj8VK3+tS +5jBdIROZxKxh3r9rV7SBHrblTWB3CZ/NsJblZuzMazLWkYS1/JZud9ECAwEAAaNT +MFEwHQYDVR0OBBYEFP6+DMoQBCg7tOYrSUpWKQrbfJ5yMB8GA1UdIwQYMBaAFP6+ +DMoQBCg7tOYrSUpWKQrbfJ5yMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAGbkwraWuKnhcE8GrLCO0qLq5GaF+WvxqVzgPvG3G6bQJuEnS6LT65nR +ICU82vCQBikW6lDfpsiwcVqOmgefUmjI99Aw5S1dkYy2J+WZrU/EgX445JLGJMpB +Bh0QajFT6gdQiuURqTmxAhNj4VrUqtrNapP8vFKerR7nTrORG0ELEcvzVYCLNvDm +OYWvLEMZifNWKY6hRdhZXho5hfu7/YPUhZUpcSxkTUsDnG3gzkTyX1TzeWe7wlJr +dtNIk50lUCR6TlSpTaVB+6uhVjaLZpZwBAT5H0dWQXm0Ww5AYsEAT9Uzh9fZYu4m +iMhztKH3PWjDB+jSzcOv9FXeYWgprWk= +-----END CERTIFICATE----- diff --git a/test/endToEnd/endToEnd.tests.ts b/test/endToEnd/endToEnd.tests.ts new file mode 100644 index 000000000..60d7ca3af --- /dev/null +++ b/test/endToEnd/endToEnd.tests.ts @@ -0,0 +1,714 @@ +// import * as http from "http"; +// import * as https from "https"; +// import * as assert from "assert"; +// import * as os from "os"; +// import * as fs from "fs"; +// import * as sinon from "sinon"; +// import * as events from "events"; +// import { EventEmitter } from "events"; +// import * as child_process from "child_process"; +// import * as nock from "nock"; + +// import * as AppInsights from "../../src/applicationinsights"; +// import * as Constants from "../../src/declarations/constants"; +// import * as Contracts from "../../src/declarations/contracts"; +// import { HeartBeat } from "../../src/library/heartBeat"; +// import { TelemetryClient } from "../../src/library"; +// import { Util } from "../../src/library/util"; +// import { JsonConfig } from "../../src/library/configuration"; +// import * as FileSystemHelper from "../../src/library/util"; + +// /** +// * A fake response class that passes by default +// */ +// class fakeResponse { +// private callbacks: { [event: string]: (data?: any) => void } = Object.create(null); +// public setEncoding(): void {} +// public statusCode: number = 200; +// private _responseData: any; + +// constructor(private passImmediately: boolean = true, responseData?: any) { +// this._responseData = responseData ? responseData : "data"; +// } + +// public on(event: string, callback: () => void) { +// if (!this.callbacks[event]) { +// this.callbacks[event] = callback; +// } else { +// var lastCallback = this.callbacks[event]; +// this.callbacks[event] = () => { +// callback(); +// lastCallback(); +// }; +// } + +// if (event == "end" && this.passImmediately) { +// this.pass(true); +// } +// } + +// public emit(eventName: string, ...args: any[]): boolean { +// return true; +// } + +// public addListener(eventName: string, listener: () => void): void { +// this.on(eventName, listener); +// } + +// public removeListener(eventName: string, listener: () => void) {} + +// public pass(test = false): void { +// this.callbacks["data"] ? this.callbacks["data"](this._responseData) : null; +// this.callbacks["end"] ? this.callbacks["end"]() : null; +// this.callbacks["finish"] ? this.callbacks["finish"]() : null; +// } + +// public end = this.pass; +// public once = this.on; +// } + +// /** +// * A fake request class that fails by default +// */ +// class fakeRequest { +// private callbacks: { [event: string]: Function } = Object.create(null); +// public write(): void {} +// public headers: { [id: string]: string } = {}; +// public agent = { protocol: "http" }; +// private _responseData: any; + +// constructor( +// private failImmediatly: boolean = true, +// public url: string = undefined, +// responseData?: any +// ) { +// this._responseData = responseData; +// } + +// public on(event: string, callback: Function) { +// this.callbacks[event] = callback; +// if (event === "error" && this.failImmediatly) { +// setImmediate(() => this.fail()); +// } +// } + +// public emit(eventName: string, ...args: any[]): boolean { +// return true; +// } + +// public addListener(eventName: string, listener: Function): void { +// this.on(eventName, listener); +// } + +// public removeListener(eventName: string, listener?: Function) {} + +// public fail(): void { +// this.callbacks["error"] ? this.callbacks["error"]() : null; +// } + +// public end(): void { +// this.callbacks["end"] +// ? this.callbacks["end"](new fakeResponse(true, this._responseData)) +// : null; +// } +// } + +// /** +// * A fake http server +// */ +// class fakeHttpServer extends events.EventEmitter { +// public setCallback(callback: any) { +// this.on("request", callback); +// } + +// public listen(port: any, host?: any, backlog?: any, callback?: any) { +// this.emit("listening"); +// } + +// public emitRequest(url: string) { +// var request = new fakeRequest(false, url); +// var response = new fakeResponse(false); +// this.emit("request", request, response); +// request.end(); +// } +// } + +// /** +// * A fake https server class that doesn't require ssl certs +// */ +// class fakeHttpsServer extends events.EventEmitter { +// public setCallback(callback: any) { +// this.on("request", callback); +// } + +// public listen(port: any, host?: any, backlog?: any, callback?: any) { +// this.emit("listening"); +// } + +// public emitRequest(url: string) { +// var request = new fakeRequest(false, url); +// var response = new fakeResponse(false); +// this.emit("request", request, response); +// request.end(); +// response.pass(); +// } +// } + +// describe("EndToEnd", () => { +// var sandbox: sinon.SinonSandbox; +// var originalEnv = {}; +// let interceptor: nock.Interceptor; + +// var breezeResponse: Contracts.BreezeResponse = { +// itemsAccepted: 1, +// itemsReceived: 1, +// errors: [], +// }; + +// before(() => { +// sandbox = sinon.createSandbox(); +// var newEnv = <{ [id: string]: string }>{}; +// Util.getInstance().tlsRestrictedAgent = new https.Agent(); +// newEnv["APPLICATION_INSIGHTS_NO_STATSBEAT"] = "true"; +// newEnv["TMP"] = process.env.TMP; +// newEnv["TMPDIR"] = process.env.TMPDIR; +// newEnv["TEMP"] = process.env.TEMP; +// originalEnv = process.env; +// process.env = newEnv; + +// interceptor = nock(Constants.DEFAULT_BREEZE_ENDPOINT).post( +// "/v2.1/track", +// (body: string) => { +// return true; +// } +// ); +// nock.disableNetConnect(); +// }); + +// after(() => { +// process.env = originalEnv; +// nock.cleanAll(); +// nock.enableNetConnect(); +// }); + +// describe("Basic usage", () => { +// let nockScope: nock.Scope; + +// before(() => { +// nockScope = interceptor.reply(200, breezeResponse).persist(); +// }); + +// beforeEach(() => { +// JsonConfig["_instance"] = undefined; +// }); + +// afterEach(() => { +// // Dispose the default app insights client and auto collectors so that they can be reconfigured +// // cleanly for each test +// sandbox.restore(); +// // TODO: Correlation management pending work +// // CorrelationContextManager.reset(); +// AppInsights.dispose(); +// }); + +// it("should send telemetry", (done) => { +// const expectedTelemetryData: AppInsights.Contracts.AvailabilityTelemetry = { +// duration: 100, +// id: "id1", +// message: "message1", +// success: true, +// name: "name1", +// runLocation: "east us", +// }; + +// var client = new AppInsights.TelemetryClient("iKey"); + +// client.trackEvent({ name: "test event" }); +// client.trackException({ exception: new Error("test error") }); +// client.trackMetric({ metrics: [{ name: "test metric", value: 3 }] }); +// client.trackTrace({ message: "test trace" }); +// client.trackAvailability(expectedTelemetryData); + +// client.flush({ +// callback: (response) => { +// assert.ok(response, "response should not be empty"); +// assert.ok(response !== "no data to send", "response should have data"); +// done(); +// }, +// }); +// }); + +// it("should collect http request telemetry", (done) => { +// var fakeHttpSrv = new fakeHttpServer(); +// sandbox +// .stub(http, "createServer") +// .callsFake( +// (callback: (req: http.ServerRequest, res: http.ServerResponse) => void) => { +// fakeHttpSrv.setCallback(callback); +// return fakeHttpSrv as any; +// } +// ); + +// AppInsights.setup("ikey").setAutoCollectRequests(true).start(); + +// var track = sandbox.stub(AppInsights.defaultClient, "track"); +// http.createServer((req, res) => { +// assert.equal(track.callCount, 0); +// res.end(); +// assert.equal(track.callCount, 1); +// done(); +// }); + +// fakeHttpSrv.emitRequest("http://localhost:0/test"); +// }); + +// it("should collect https request telemetry", (done) => { +// var fakeHttpSrv = new fakeHttpServer(); +// sandbox +// .stub(https, "createServer") +// .callsFake( +// ( +// options: any, +// callback: (req: http.ServerRequest, res: http.ServerResponse) => void +// ) => { +// fakeHttpSrv.setCallback(callback); +// return fakeHttpSrv as any; +// } +// ); + +// AppInsights.setup("ikey").setAutoCollectRequests(true).start(); + +// var track = sandbox.stub(AppInsights.defaultClient, "track"); +// https.createServer(null, (req: http.ServerRequest, res: http.ServerResponse) => { +// assert.equal(track.callCount, 0); +// res.end(); +// assert.equal(track.callCount, 1); +// done(); +// }); + +// fakeHttpSrv.emitRequest("http://localhost:0/test"); +// }); + +// it("should collect http dependency telemetry", (done) => { +// var eventEmitter = new EventEmitter(); +// (eventEmitter).method = "GET"; +// sandbox.stub(http, "request").callsFake((url: string, c: Function) => { +// process.nextTick(c); +// return eventEmitter as any; +// }); + +// AppInsights.setup("ikey").setAutoCollectDependencies(true).start(); + +// var track = sandbox.stub(AppInsights.defaultClient, "track"); + +// http.request("http://test.com", (c) => { +// assert.equal(track.callCount, 0); +// eventEmitter.emit("response", {}); +// assert.equal(track.callCount, 1); +// done(); +// }); +// }); + +// it("should collect https dependency telemetry", (done) => { +// sandbox.restore(); +// var eventEmitter = new EventEmitter(); +// (eventEmitter).method = "GET"; +// sandbox.stub(https, "request").callsFake((url: string, c: Function) => { +// process.nextTick(c); +// return eventEmitter as any; +// }); + +// AppInsights.setup("ikey").setAutoCollectDependencies(true).start(); + +// var track = sandbox.stub(AppInsights.defaultClient, "track"); + +// https.request("https://test.com", (c) => { +// assert.equal(track.callCount, 0); +// eventEmitter.emit("response", {}); +// assert.equal(track.callCount, 1); +// done(); +// }); +// }); + +// it("should add correlation context if not available", (done) => { +// var eventEmitter = new EventEmitter(); +// (eventEmitter).method = "GET"; +// sandbox.stub(http, "request").callsFake((url: string, c: Function) => { +// process.nextTick(c); +// return eventEmitter as any; +// }); + +// // TODO: Correlation Context implementation +// // let generateContextSpy = sandbox.spy(CorrelationContextManager, "generateContextObject"); +// // AppInsights +// // .setup("ikey") +// // .setAutoCollectDependencies(true) +// // .setAutoDependencyCorrelation(true); +// // AppInsights.start(); +// // sandbox.stub(AppInsights.defaultClient, 'track'); + +// // http.request('http://test.com', (c) => { +// // assert.equal(generateContextSpy.callCount, 1); +// // done(); +// // }); +// }); +// }); + +// describe("W3C mode", () => { +// let nockScope: nock.Scope; + +// before(() => { +// nockScope = interceptor.reply(200, breezeResponse).persist(); +// }); + +// beforeEach(() => { +// JsonConfig["_instance"] = undefined; +// }); + +// afterEach(() => { +// // Dispose the default app insights client and auto collectors so that they can be reconfigured +// // cleanly for each test +// sandbox.restore(); +// // TODO: Correlation management pending work +// // CorrelationContextManager.reset(); +// AppInsights.dispose(); +// }); + +// it("should pass along traceparent/tracestate header if present in current operation", (done) => { +// var eventEmitter = new EventEmitter(); +// (eventEmitter as any).headers = {}; +// (eventEmitter as any)["getHeader"] = function (name: string) { +// return this.headers[name]; +// }; +// (eventEmitter as any)["setHeader"] = function (name: string, value: string) { +// this.headers[name] = value; +// }; +// (eventEmitter).method = "GET"; +// sandbox.stub(https, "request").callsFake((url: string, c: Function) => { +// process.nextTick(c); +// return eventEmitter as any; +// }); + +// AppInsights.setup("ikey").setAutoCollectDependencies(true).start(); + +// // TODO: Correlation Context implementation +// // sandbox.stub(CorrelationContextManager, "getCurrentContext").callsFake(() => ({ +// // operation: { +// // traceparent: "", +// // tracestate: "sometracestate" +// // }, +// // customProperties: { +// // serializeToHeader: (): null => null +// // } +// // })); +// https.request("https://test.com", (c) => { +// eventEmitter.emit("response", {}); +// assert.ok( +// (eventEmitter as any).headers["request-id"].match(/^\|[0-z]{32}\.[0-z]{16}\./g) +// ); +// assert.ok( +// (eventEmitter as any).headers.traceparent.match( +// /^00-5e84aff3af474588a42dcbf3bd1db95f-[0-z]{16}-00$/ +// ) +// ); +// assert.notEqual( +// (eventEmitter as any).headers.traceparent, +// "00-5e84aff3af474588a42dcbf3bd1db95f-1fc066fb77fa43a3-00" +// ); +// assert.equal((eventEmitter as any).headers.tracestate, "sometracestate"); +// AppInsights.defaultClient.flush(); +// done(); +// }); +// }); + +// it("should create and pass a traceparent header if w3c is enabled", (done) => { +// var eventEmitter = new EventEmitter(); +// (eventEmitter as any).headers = {}; +// (eventEmitter as any)["getHeader"] = function (name: string) { +// return this.headers[name]; +// }; +// (eventEmitter as any)["setHeader"] = function (name: string, value: string) { +// this.headers[name] = value; +// }; +// (eventEmitter).method = "GET"; +// sandbox.stub(https, "request").callsFake((url: string, c: Function) => { +// process.nextTick(c); +// return eventEmitter as any; +// }); +// AppInsights.setup("ikey").setAutoCollectDependencies(true).start(); + +// // TODO: Correlation Context implementation +// // sandbox.stub(CorrelationContextManager, "getCurrentContext").callsFake(() => ({ +// // operation: { +// // }, +// // customProperties: { +// // serializeToHeader: (): null => null +// // } +// // })); +// https.request("https://test.com", (c) => { +// eventEmitter.emit("response", {}); +// assert.ok( +// (eventEmitter as any).headers.traceparent.match( +// /^00-[0-z]{32}-[0-z]{16}-[0-9a-f]{2}/g +// ), +// "traceparent header is passed, 00-W3C-W3C-00" +// ); +// assert.ok( +// (eventEmitter as any).headers["request-id"].match(/^\|[0-z]{32}\.[0-z]{16}\./g), +// "back compat header is also passed, |W3C.W3C." + +// (eventEmitter as any).headers["request-id"] +// ); +// AppInsights.defaultClient.flush(); +// done(); +// }); +// }); +// }); + +// describe("Disk retry mode", () => { +// var writeFile: sinon.SinonStub; +// var writeFileSync: sinon.SinonStub; +// var readFile: sinon.SinonStub; +// var lstat: sinon.SinonStub; +// var mkdir: sinon.SinonStub; +// var existsSync: sinon.SinonStub; +// var readdir: sinon.SinonStub; +// var readdirSync: sinon.SinonStub; +// var stat: sinon.SinonStub; +// var statSync: sinon.SinonStub; +// var mkdirSync: sinon.SinonStub; +// var spawn: sinon.SinonStub; +// var spawnSync: sinon.SinonStub; + +// let nockScope: nock.Scope; + +// beforeEach(() => { +// nockScope = interceptor.reply(503, { errors: [{ index: 0, statusCode: 503 }] }); +// AppInsights.dispose(); +// writeFile = sandbox.stub(FileSystemHelper, "writeFileAsync"); +// writeFileSync = sandbox.stub(fs, "writeFileSync"); +// existsSync = sandbox.stub(fs, "existsSync").value(true); +// readdir = sandbox.stub(FileSystemHelper, "readdirAsync").value(["1.ai.json"]); +// readdirSync = sandbox.stub(fs, "readdirSync").value(["1.ai.json"]); +// stat = sandbox +// .stub(FileSystemHelper, "statAsync") +// .value({ isFile: () => true, size: 8000 }); +// statSync = sandbox.stub(fs, "statSync").value({ isFile: () => true, size: 8000 }); +// lstat = sandbox.stub(FileSystemHelper, "lstatAsync").value({ isDirectory: () => true }); +// mkdir = sandbox.stub(FileSystemHelper, "mkdirAsync").value(null); +// mkdirSync = sandbox.stub(fs, "mkdirSync").value(null); +// readFile = sandbox.stub(FileSystemHelper, "readFileAsync").value(""); +// spawn = sandbox.stub(child_process, "spawn").value({ +// on: (type: string, cb: any) => { +// if (type === "close") { +// cb(0); +// } +// }, +// stdout: { +// on: (type: string, cb: any) => { +// if (type === "data") { +// cb("stdoutmock"); +// } +// }, +// }, +// }); +// if (child_process.spawnSync) { +// spawnSync = sandbox +// .stub(child_process, "spawnSync") +// .value({ status: 0, stdout: "stdoutmock" }); +// } +// JsonConfig["_instance"] = undefined; +// }); + +// afterEach(() => { +// sandbox.restore(); +// AppInsights.dispose(); +// }); + +// it("disabled by default for new clients", (done) => { +// var client = new AppInsights.TelemetryClient("key"); +// client.trackEvent({ name: "test event" }); + +// client.flush({ +// callback: (response: any) => { +// // yield for the caching behavior +// setImmediate(() => { +// assert(writeFile.callCount === 0); +// done(); +// }); +// }, +// }); +// }); + +// it("enabled by default for default client", (done) => { +// AppInsights.setup("key").start(); +// var client = AppInsights.defaultClient; +// client.trackEvent({ name: "test event" }); +// client.flush({ +// callback: (response: any) => { +// // yield for the caching behavior +// setImmediate(() => { +// assert.equal(writeFile.callCount, 1); +// done(); +// }); +// }, +// }); +// }); + +// it("cache payload synchronously when process crashes", () => { +// var client = new AppInsights.TelemetryClient("key2"); +// // TODO: Persistance is not configurable in Exporter +// //client.channel.setUseDiskRetryCaching(true); + +// client.trackEvent({ name: "test event" }); +// client.flush({ isAppCrashing: true }); + +// assert(existsSync.callCount === 1); +// assert(writeFileSync.callCount === 1); +// assert.equal(spawnSync.callCount, os.type() === "Windows_NT" ? 1 : 0); // This is implicitly testing caching of ACL identity (otherwise call count would be 2 like it is the non-sync time) +// // TODO: This test should validate external persist is called only +// // assert.equal( +// // path.dirname(writeFileSync.firstCall.args[0]), +// // path.join(os.tmpdir(), Sender.TEMPDIR_PREFIX + "key2")); +// // assert.equal(writeFileSync.firstCall.args[2].mode, 0o600, "File must not have weak permissions"); +// }); +// }); + +// describe("Heartbeat metrics for VM", () => { +// beforeEach(() => { +// JsonConfig["_instance"] = undefined; +// }); + +// afterEach(() => { +// sandbox.restore(); +// }); + +// it("should collect correct VM information from JSON response", (done) => { +// // set up stub +// const vmDataJSON = `{ +// "vmId": "1", +// "subscriptionId": "2", +// "osType": "Windows_NT" +// }`; +// let func = (options: any, callback: any) => { +// var req = new fakeRequest(false, "http://169.254.169.254", vmDataJSON); +// req.on("end", callback); +// return req as any; +// }; +// var stub: sinon.SinonStub = sandbox.stub(http, "request").callsFake(func); + +// // set up sdk +// const client = new TelemetryClient("key"); +// const heartbeat: HeartBeat = new HeartBeat(client.metricHandler, client.config); +// heartbeat.enable(true); +// const trackMetricStub = sandbox.stub(heartbeat["_handler"], "trackMetric"); + +// heartbeat["trackHeartBeat"](client.config, () => { +// assert.equal( +// trackMetricStub.callCount, +// 1, +// "should call trackMetric for the VM heartbeat metric" +// ); +// assert.equal( +// trackMetricStub.args[0][0].metrics[0].name, +// "HeartBeat", +// "should use correct name for heartbeat metric" +// ); +// assert.equal(trackMetricStub.args[0][0].metrics[0].value, 0, "value should be 0"); +// const keys = Object.keys(trackMetricStub.args[0][0].properties); +// assert.equal( +// keys.length, +// 5, +// "should have 4 kv pairs added when resource type is VM" +// ); +// assert.equal(keys[0], "sdk", "sdk should be added as a key"); +// assert.equal(keys[1], "osType", "osType should be added as a key"); +// assert.equal(keys[2], "azInst_vmId", "azInst_vmId should be added as a key"); +// assert.equal( +// keys[3], +// "azInst_subscriptionId", +// "azInst_subscriptionId should be added as a key" +// ); +// assert.equal(keys[4], "azInst_osType", "azInst_osType should be added as a key"); + +// const properties = trackMetricStub.args[0][0].properties; +// assert.equal( +// properties["sdk"], +// heartbeat["_handler"].getContext().sdkVersion, +// "sdk version should be read from Context" +// ); +// assert.equal( +// properties["osType"], +// os.type(), +// "osType should be read from os library" +// ); +// assert.equal( +// properties["azInst_vmId"], +// "1", +// "azInst_vmId should be read from response" +// ); +// assert.equal( +// properties["azInst_subscriptionId"], +// "2", +// "azInst_subscriptionId should be read from response" +// ); +// assert.equal( +// properties["azInst_osType"], +// "Windows_NT", +// "azInst_osType should be read from response" +// ); +// done(); +// }); +// }); + +// it("should only send name and value properties for heartbeat metric when get VM request fails", (done) => { +// // set up stub +// var stub: sinon.SinonStub = sandbox +// .stub(http, "request") +// .callsFake((options: any, callback: any) => { +// var req = new fakeRequest(true, "http://169.254.169.254"); +// return req as any; +// }); + +// // set up sdk +// const client = new TelemetryClient("key"); +// const heartbeat: HeartBeat = new HeartBeat(client.metricHandler, client.config); +// heartbeat.enable(true); +// const trackMetricStub = sandbox.stub(heartbeat["_handler"], "trackMetric"); + +// heartbeat["trackHeartBeat"](client.config, () => { +// assert.equal( +// trackMetricStub.callCount, +// 1, +// "should call trackMetric as heartbeat metric" +// ); +// assert.equal( +// trackMetricStub.args[0][0].metrics[0].name, +// "HeartBeat", +// "should use correct name for heartbeat metric" +// ); +// assert.equal(trackMetricStub.args[0][0].metrics[0].value, 0, "value should be 0"); +// const keys = Object.keys(trackMetricStub.args[0][0].properties); +// assert.equal( +// keys.length, +// 2, +// "should have 2 kv pairs added when resource type is not web app, not function app, not VM" +// ); +// assert.equal(keys[0], "sdk", "sdk should be added as a key"); +// assert.equal(keys[1], "osType", "osType should be added as a key"); + +// const properties = trackMetricStub.args[0][0].properties; +// assert.equal( +// properties["sdk"], +// heartbeat["_handler"].getContext().sdkVersion, +// "sdk version should be read from Context" +// ); +// assert.equal( +// properties["osType"], +// os.type(), +// "osType should be read from os library" +// ); +// done(); +// }); +// }); +// }); +// }); diff --git a/Tests/js/endToEnd.js b/test/endToEnd/js/endToEnd.js similarity index 73% rename from Tests/js/endToEnd.js rename to test/endToEnd/js/endToEnd.js index f546fcf5e..f3bd8c774 100644 --- a/Tests/js/endToEnd.js +++ b/test/endToEnd/js/endToEnd.js @@ -1,18 +1,24 @@ const assert = require('assert'); const https = require('https'); const sinon = require('sinon'); +const nock = require('nock'); + +let nockScope = nock("https://centralus-0.in.applicationinsights.azure.com").post( + "/v2.1/track", + (body) => { + return true; + } +).persist(); // Special embedded test cases for testing if app can close if (process.argv.indexOf('embeddedTestCase-AppTerminates1') > -1) { - var appInsights = require('../..'); - appInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - appInsights.defaultClient.config.httpsAgent = new https.Agent({ keepAlive: false }); + var appInsights = require('../../../out/src/applicationinsights'); + appInsights.setup("InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/"); appInsights.start(); return; } else if (process.argv.indexOf('embeddedTestCase-AppTerminates2') > -1) { - var appInsights = require('../..'); - appInsights.setup("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - appInsights.defaultClient.config.httpsAgent = new https.Agent({ keepAlive: false }); + var appInsights = require('../../../out/src/applicationinsights'); + appInsights.setup("InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/"); appInsights.start(); appInsights.defaultClient.trackEvent({ name: 'testEvent' }); appInsights.defaultClient.flush(); @@ -20,9 +26,14 @@ if (process.argv.indexOf('embeddedTestCase-AppTerminates1') > -1) { } describe('module', function () { + + after(() => { + nock.cleanAll(); + }); + describe('#require', function () { it('loads the applicationinsights module', function (done) { - assert.doesNotThrow(function () { return require('../..') }); + assert.doesNotThrow(function () { return require('../../../out/src/applicationinsights') }); done(); }); }); @@ -60,23 +71,6 @@ describe('module', function () { }); }); - describe('rejected promises', function () { - it('should not crash on rejected promises containing no callstack', function () { - var appInsights = require('../../'); - appInsights.setup('1aa11111-bbbb-1ccc-8ddd-eeeeffff3333'); - appInsights.defaultClient.config.httpsAgent = new https.Agent({ keepAlive: false }); - appInsights.start(); - assert.ok(appInsights.defaultClient); - assert.doesNotThrow(function () { - if (typeof Promise !== 'undefined') { - Promise.reject(); - } - }); - appInsights.defaultClient.flush(); - appInsights.dispose(); - }); - }); - describe('uncaught exceptions', function () { var UNCAUGHT_EXCEPTION = 'uncaughtException'; var UNCAUGHT_EXCEPTION_MONITOR = 'uncaughtExceptionMonitor'; @@ -107,9 +101,8 @@ describe('module', function () { }); it('should crash on uncaught exceptions', function () { - var appInsights = require('../../'); - appInsights.setup('1aa11111-bbbb-1ccc-8ddd-eeeeffff3333').setAutoCollectExceptions(true); - appInsights.defaultClient.config.httpsAgent = new https.Agent({ keepAlive: false }); + var appInsights = require('../../../out/src/applicationinsights'); + appInsights.setup('InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/').setAutoCollectExceptions(true); appInsights.start(); assert.ok(appInsights.defaultClient); var handler; @@ -127,9 +120,8 @@ describe('module', function () { }); it('should not crash on uncaught exceptions if multiple handlers exist', function () { - var appInsights = require('../../'); - appInsights.setup('1aa11111-bbbb-1ccc-8ddd-eeeeffff3333').setAutoCollectExceptions(true); - appInsights.defaultClient.config.httpsAgent = new https.Agent({ keepAlive: false }); + var appInsights = require('../../../out/src/applicationinsights'); + appInsights.setup('InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/').setAutoCollectExceptions(true); appInsights.start(); process.addListener(UNCAUGHT_EXCEPTION, function () { }); assert.ok(appInsights.defaultClient); diff --git a/Tests/FunctionalTests/RunFunctionalTests.js b/test/functionalTests/runFunctionalTests.js similarity index 91% rename from Tests/FunctionalTests/RunFunctionalTests.js rename to test/functionalTests/runFunctionalTests.js index 9a38a93ba..c21a4f356 100644 --- a/Tests/FunctionalTests/RunFunctionalTests.js +++ b/test/functionalTests/runFunctionalTests.js @@ -130,13 +130,13 @@ function main() { // Prepare runner and testapp console.log("Installing Runner and TestApp dependencies..."); - if (run("npm install", "./Runner").code !== 0 || run("npm install", "./TestApp").code !== 0) { + if (run("npm install", "./runner").code !== 0 || run("npm install", "./testApp").code !== 0) { console.error("Could not install dependencies!"); return 1; } console.log("Installing " + path); - run("npm uninstall applicationinsights", "./TestApp"); - if (run("npm install --no-save " + path, "./TestApp").code !== 0) { + run("npm uninstall applicationinsights", "./testApp"); + if (run("npm install --no-save " + path, "./testApp").code !== 0) { console.error("Could not install SDK!"); return 1; } @@ -144,8 +144,8 @@ function main() { // Run tests console.log("Running functional tests..."); console.log("=======================\n"); - const testApp = runAsync("node --use_strict Main.js", "./TestApp"); - const runnerStatus = runLive("node --use_strict Main.js" + (perfMode ? " -perfmode": ""), "./Runner").code; + const testApp = runAsync("node --use_strict main.js", "./testApp"); + const runnerStatus = runLive("node --use_strict main.js" + (perfMode ? " -perfmode": ""), "./runner").code; console.log("\n======================="); // Clean up @@ -159,4 +159,4 @@ function main() { } -process.exit(main()); +process.exit(main()); \ No newline at end of file diff --git a/Tests/FunctionalTests/Runner/AppConnector.js b/test/functionalTests/runner/appConnector.js similarity index 96% rename from Tests/FunctionalTests/Runner/AppConnector.js rename to test/functionalTests/runner/appConnector.js index 8b5749feb..6801ddeaf 100644 --- a/Tests/FunctionalTests/Runner/AppConnector.js +++ b/test/functionalTests/runner/appConnector.js @@ -1,5 +1,5 @@ -var Config = require("./Config"); -var Utils = require("./Utils"); +var Config = require("./config"); +var Utils = require("./utils"); /** @param {string} url */ function getOk(url) { diff --git a/Tests/FunctionalTests/Runner/Config.js b/test/functionalTests/runner/config.js similarity index 87% rename from Tests/FunctionalTests/Runner/Config.js rename to test/functionalTests/runner/config.js index 113075146..a9685eec1 100644 --- a/Tests/FunctionalTests/Runner/Config.js +++ b/test/functionalTests/runner/config.js @@ -1,6 +1,6 @@ var Config = { RunnerPort: "9091", - TestAppAddress: "http://localhost:9099", + TestAppAddress: "https://localhost:9099", WaitTime: 20 * 1000, PerfCounterFrequency: 60 * 1000, StressTestTime: 1.2 * 60 * 1000 diff --git a/Tests/FunctionalTests/Runner/Ingestion.js b/test/functionalTests/runner/ingestion.js similarity index 76% rename from Tests/FunctionalTests/Runner/Ingestion.js rename to test/functionalTests/runner/ingestion.js index 78d928589..ae0536c37 100644 --- a/Tests/FunctionalTests/Runner/Ingestion.js +++ b/test/functionalTests/runner/ingestion.js @@ -1,11 +1,12 @@ // A simple ingestion server to test AI SDK without mocking -var http = require("http"); +var fs = require("fs"); +var https = require("https"); +var path = require("path"); var zlib = require("zlib"); -var Config = require("./Config"); -var TestValidation = require("./TestValidation").TestValidation; +var Config = require("./config"); +var TestValidation = require("./testValidation").TestValidation; -var _APPID = "DUMMYAPPID"; -var _IKEY = "TESTIKEY"; +var _IKEY = "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"; class Ingestion { constructor() { @@ -14,12 +15,16 @@ class Ingestion { this.correlatedTelemetry = {}; this.telemetryCount = 0; this.testValidator = new TestValidation(this); - this.server = http.createServer(function (request, response) { - // Handle appid - if (request.url.indexOf("/api/profiles") > -1) { - response.end(_APPID); - return; - } else if (request.url.indexOf("/v2.1/track") > -1) { + + this.server = https.createServer({ + key: fs.readFileSync( + path.join(__dirname, '../../', 'certs', 'server-key.pem') + ), + cert: fs.readFileSync( + path.join(__dirname, '../../', 'certs', 'server-cert.pem') + ), + }, (request, response) => { + if (request.url.indexOf("/v2.1/track") > -1) { var processor = request; var data = ""; if (request.headers["content-encoding"] && request.headers["content-encoding"].toLowerCase() === "gzip") { @@ -28,12 +33,12 @@ class Ingestion { processor = gunzip; } processor - .on('data', (d)=>data+=d) - .on('error', ()=>null) - .on('end', (d)=>{ - data += (d||""); + .on('data', (d) => data += d) + .on('error', () => null) + .on('end', (d) => { + data += (d || ""); let items = data.split("\n"); - items.forEach(function(item) { + items.forEach(function (item) { item = JSON.parse(item); if (!Array.isArray(item)) { item = [item]; @@ -60,7 +65,7 @@ class Ingestion { if (!item || !item.iKey || item.iKey !== _IKEY) { console.log("INGESTION: Unexpected ikey or malformed data"); return; - } + } var type = "unknown"; if (item.data && item.data.baseType) { type = item.data.baseType; diff --git a/Tests/FunctionalTests/Runner/Main.js b/test/functionalTests/runner/main.js similarity index 97% rename from Tests/FunctionalTests/Runner/Main.js rename to test/functionalTests/runner/main.js index be73ec168..0decd3501 100644 --- a/Tests/FunctionalTests/Runner/Main.js +++ b/test/functionalTests/runner/main.js @@ -1,11 +1,11 @@ var fs = require('fs'); var os = require('os'); -var Config = require("./Config"); -var Ingestion = new (require("./Ingestion"))(); -var TestSequence = require("./TestSequence.json"); -var Utils = require("./Utils"); -var AppConnector = require("./AppConnector"); +var Config = require("./config"); +var Ingestion = new (require("./ingestion"))(); +var TestSequence = require("./testSequence.json"); +var Utils = require("./utils"); +var AppConnector = require("./appConnector"); var successfulRun = true; let startTime = null; @@ -226,4 +226,4 @@ if (!perfMode) { Utils.Logging.info("CPU Usage: " + (100 - ~~(100 * idleDifference / totalDifference)) + "%"); lastCPU = newCPU; }, 30 * 1000); -} +} \ No newline at end of file diff --git a/Tests/FunctionalTests/Runner/package.json b/test/functionalTests/runner/package.json similarity index 100% rename from Tests/FunctionalTests/Runner/package.json rename to test/functionalTests/runner/package.json diff --git a/Tests/FunctionalTests/Runner/TaskExpectations.js b/test/functionalTests/runner/taskExpectations.js similarity index 64% rename from Tests/FunctionalTests/Runner/TaskExpectations.js rename to test/functionalTests/runner/taskExpectations.js index d40aae0cc..910e4ee64 100644 --- a/Tests/FunctionalTests/Runner/TaskExpectations.js +++ b/test/functionalTests/runner/taskExpectations.js @@ -66,69 +66,49 @@ module.exports = { "MongoInsert": outputContract( "RemoteDependencyData", (telemetry) => { - return telemetry.data.baseData.name === "insert" && + return telemetry.data.baseData.name === "mongodb.insert" && telemetry.data.baseData.success === true && - telemetry.data.baseData.target === "testapp" && + telemetry.data.baseData.target === "localhost|testapp" && telemetry.data.baseData.type === "mongodb"; } ), "MongoInsertMany": outputContract( "RemoteDependencyData", (telemetry) => { - return telemetry.data.baseData.name === "insert" && + return telemetry.data.baseData.name === "mongodb.insert" && telemetry.data.baseData.success === true && - telemetry.data.baseData.target === "testapp" && + telemetry.data.baseData.target === "localhost|testapp" && telemetry.data.baseData.type === "mongodb"; } ), "MongoFind": outputContract( "RemoteDependencyData", (telemetry) => { - return telemetry.data.baseData.name === "find" && + return telemetry.data.baseData.name === "mongodb.find" && telemetry.data.baseData.success === true && - telemetry.data.baseData.target === "testapp" && + telemetry.data.baseData.data === "{\"testrecord\":\"?\"}" && + telemetry.data.baseData.target === "localhost|testapp" && telemetry.data.baseData.type === "mongodb"; } ), "MongoUpdateOne": outputContract( "RemoteDependencyData", (telemetry) => { - return telemetry.data.baseData.name === "update" && + return telemetry.data.baseData.name === "mongodb.update" && telemetry.data.baseData.success === true && - telemetry.data.baseData.target === "testapp" && + telemetry.data.baseData.target === "localhost|testapp" && telemetry.data.baseData.type === "mongodb"; } ), "MongoCreateIndex": outputContract( "RemoteDependencyData", (telemetry) => { - return telemetry.data.baseData.name === "createIndexes" && - telemetry.data.baseData.data === "createIndexes" && - telemetry.data.baseData.target === "testapp" && + return telemetry.data.baseData.name === "mongodb.createIndexes" && + telemetry.data.baseData.data === "{\"createIndexes\":\"?\",\"indexes\":\"?\"}" && + telemetry.data.baseData.target === "localhost|testapp" && telemetry.data.baseData.type === "mongodb"; } ), - "AITrackDep": outputContract( - "RemoteDependencyData", - (telemetry) => { - return telemetry.data.baseData.name === "Manual dependency" && - telemetry.data.baseData.success === true && - telemetry.data.baseData.type === "Manual" && - telemetry.data.baseData.duration === '00:00:00.200'; - } - ), - "AITrackTrace": outputContract( - "MessageData", - (telemetry) => { - return telemetry.data.baseData.message === "Manual track trace"; - } - ), - "AITrackExc": outputContract( - "ExceptionData", - (telemetry) => { - return telemetry.data.baseData.exceptions[0].message === "Manual track error"; - } - ), "Timeout": outputContract( null, null @@ -143,140 +123,123 @@ module.exports = { "MessageData", (telemetry) => { return JSON.parse(telemetry.data.baseData.message).msg === "test fatal" && - telemetry.data.baseData.severityLevel === 4; + telemetry.data.baseData.severityLevel === "Critical"; } ), "BunyanError": outputContract( "MessageData", (telemetry) => { return JSON.parse(telemetry.data.baseData.message).msg === "test error" && - telemetry.data.baseData.severityLevel === 3; + telemetry.data.baseData.severityLevel === "Error"; } ), "BunyanWarn": outputContract( "MessageData", (telemetry) => { return JSON.parse(telemetry.data.baseData.message).msg === "test warn" && - telemetry.data.baseData.severityLevel === 2 + telemetry.data.baseData.severityLevel === "Warning" } ), "BunyanInfo": outputContract( "MessageData", (telemetry) => { return JSON.parse(telemetry.data.baseData.message).msg === "test info" && - telemetry.data.baseData.severityLevel === 1 + telemetry.data.baseData.severityLevel === "Information" } ), "BunyanDebug": outputContract( "MessageData", (telemetry) => { - return JSON.parse(telemetry.data.baseData.message).msg === "test debug" && - telemetry.data.baseData.severityLevel === 0; + return JSON.parse(telemetry.data.baseData.message).msg === "test debug" && + telemetry.data.baseData.severityLevel === "Verbose"; } ), "BunyanTrace": outputContract( "MessageData", (telemetry) => { - return JSON.parse(telemetry.data.baseData.message).msg === "test trace" && - telemetry.data.baseData.severityLevel === 0; + return JSON.parse(telemetry.data.baseData.message).msg === "test trace" && + telemetry.data.baseData.severityLevel === "Verbose"; } ), "ConsoleError": outputContract( "MessageData", (telemetry) => { return telemetry.data.baseData.message === "Test console.error" && - telemetry.data.baseData.severityLevel === 2; + telemetry.data.baseData.severityLevel === "Warning"; } ), "ConsoleWarn": outputContract( "MessageData", (telemetry) => { return telemetry.data.baseData.message === "Test console.warn" && - telemetry.data.baseData.severityLevel === 2; + telemetry.data.baseData.severityLevel === "Warning"; } ), "ConsoleInfo": outputContract( "MessageData", (telemetry) => { return telemetry.data.baseData.message === "Test console.info" && - telemetry.data.baseData.severityLevel === 1; + telemetry.data.baseData.severityLevel === "Information"; } ), "ConsoleLog": outputContract( "MessageData", (telemetry) => { return telemetry.data.baseData.message === "Test console.log" && - telemetry.data.baseData.severityLevel === 1; - } - ), - "ConsoleAssert": outputContract( - "MessageData", - (telemetry) => { - return telemetry.data.baseData.message.indexOf("AssertionError") === 0 && - telemetry.data.baseData.message.indexOf("Test console.assert") > 0 && - telemetry.data.baseData.severityLevel === 2; + telemetry.data.baseData.severityLevel === "Information"; } ), "MySQLQuery": outputContract( "RemoteDependencyData", (telemetry) => { - return telemetry.data.baseData.name === "SELECT * FROM 'test_table'" && + return telemetry.data.baseData.name === "SELECT" && telemetry.data.baseData.data === "SELECT * FROM 'test_table'" && - telemetry.data.baseData.target.indexOf(":33060") > -1 && + telemetry.data.baseData.target ==="localhost|testdb" && telemetry.data.baseData.type == "mysql"; } ), "RedisGet": outputContract( "RemoteDependencyData", (telemetry) => { - return telemetry.data.baseData.name === "get" && - telemetry.data.baseData.data === "get" && - telemetry.data.baseData.target.indexOf(":6379") > -1 && + return telemetry.data.baseData.name === "redis-get" && + telemetry.data.baseData.data === "get testkey" && + telemetry.data.baseData.target === "localhost" && telemetry.data.baseData.type === "redis"; } ), "RedisSet": outputContract( "RemoteDependencyData", (telemetry) => { - return telemetry.data.baseData.name === "set" && - telemetry.data.baseData.data === "set" && - telemetry.data.baseData.target.indexOf(":6379") > -1 && - telemetry.data.baseData.type === "redis"; - } - ), - "RedisSet2": outputContract( - "RemoteDependencyData", - (telemetry) => { - return telemetry.data.baseData.name === "set" && - telemetry.data.baseData.data === "set" && - telemetry.data.baseData.target.indexOf(":6379") > -1 && + return telemetry.data.baseData.name === "redis-set" && + telemetry.data.baseData.data === "set testkey [1 other arguments]" && + telemetry.data.baseData.target === "localhost" && telemetry.data.baseData.type === "redis"; } ), "RedisHset": outputContract( "RemoteDependencyData", (telemetry) => { - return telemetry.data.baseData.name === "hset" && - telemetry.data.baseData.data === "hset" && - telemetry.data.baseData.target.indexOf(":6379") > -1 && + return telemetry.data.baseData.name === "redis-hset" && + telemetry.data.baseData.data === "hset testhash testfield [1 other arguments]" && + telemetry.data.baseData.target === "localhost" && telemetry.data.baseData.type === "redis"; } ), "RedisHkeys": outputContract( "RemoteDependencyData", (telemetry) => { - return telemetry.data.baseData.name === "hkeys" && - telemetry.data.baseData.data === "hkeys" && - telemetry.data.baseData.target.indexOf(":6379") > -1 && + return telemetry.data.baseData.name === "redis-hkeys" && + telemetry.data.baseData.data === "hkeys [1 other arguments]" && + telemetry.data.baseData.target === "localhost" && telemetry.data.baseData.type === "redis"; } ), "RedisHincrby": outputContract( "RemoteDependencyData", (telemetry) => { - return telemetry.data.baseData.name === "hincrby" && - telemetry.data.baseData.data === "hincrby" && - telemetry.data.baseData.target.indexOf(":6379") > -1 && + return telemetry.data.baseData.name === "redis-hincrby" && + telemetry.data.baseData.data === "hincrby testhash testfield 1" && + telemetry.data.baseData.target === "localhost" && telemetry.data.baseData.type === "redis"; } ), @@ -284,72 +247,72 @@ module.exports = { "MessageData", (telemetry) => { return telemetry.data.baseData.message === "test error" && - telemetry.data.baseData.severityLevel === 3; + telemetry.data.baseData.severityLevel === "Error"; } ), "WinstonWarn": outputContract( "MessageData", (telemetry) => { return telemetry.data.baseData.message === "test warn" && - telemetry.data.baseData.severityLevel === 2; + telemetry.data.baseData.severityLevel === "Warning"; } ), "WinstonInfo": outputContract( "MessageData", (telemetry) => { return telemetry.data.baseData.message === "test info" && - telemetry.data.baseData.severityLevel === 1; + telemetry.data.baseData.severityLevel === "Information"; } ), "WinstonVerbose": outputContract( "MessageData", (telemetry) => { return telemetry.data.baseData.message === "test verbose" && - telemetry.data.baseData.severityLevel === 0; + telemetry.data.baseData.severityLevel === "Verbose"; } ), "WinstonDebug": outputContract( "MessageData", (telemetry) => { return telemetry.data.baseData.message === "test debug" && - telemetry.data.baseData.severityLevel === 0; + telemetry.data.baseData.severityLevel === "Verbose"; } ), "WinstonSilly": outputContract( "MessageData", (telemetry) => { return telemetry.data.baseData.message === "test silly" && - telemetry.data.baseData.severityLevel === 0; + telemetry.data.baseData.severityLevel === "Verbose"; } ), "WinstonError2": outputContract( "MessageData", (telemetry) => { return telemetry.data.baseData.message === "test error" && - telemetry.data.baseData.severityLevel === 3; + telemetry.data.baseData.severityLevel === "Error"; } ), "WinstonWarn2": outputContract( "MessageData", (telemetry) => { return telemetry.data.baseData.message === "test warn" && - telemetry.data.baseData.severityLevel === 2; + telemetry.data.baseData.severityLevel === "Warning"; } ), "WinstonInfo2": outputContract( "MessageData", (telemetry) => { return telemetry.data.baseData.message === "test info" && - telemetry.data.baseData.severityLevel === 1; + telemetry.data.baseData.severityLevel === "Information"; } ), "PostgresQuery": outputContract( "RemoteDependencyData", (telemetry) => { - return telemetry.data.baseData.name === "SELECT * FROM test_table" && - telemetry.data.baseData.data === "SELECT * FROM test_table" && - telemetry.data.baseData.target.indexOf(":5432") > -1 && - telemetry.data.baseData.type == "postgres"; + return telemetry.data.baseData.name === "pg.query:SELECT" && + telemetry.data.baseData.data === "SELECT NOW()" && + telemetry.data.baseData.target === "localhost|postgres" && + telemetry.data.baseData.type == "postgresql"; } ) } diff --git a/test/functionalTests/runner/testSequence.json b/test/functionalTests/runner/testSequence.json new file mode 100644 index 000000000..b889e712a --- /dev/null +++ b/test/functionalTests/runner/testSequence.json @@ -0,0 +1,9 @@ +[ + {"path": "/dependencyTestBasic", "steps": ["HttpGet", "Timeout", "HttpGet"]}, + {"path": "/deepRequest", "steps": ["HttpGet", "Timeout", "Timeout", "HttpGet", "Timeout"]}, + {"path": "/mongo", "steps": ["MongoInsert", "MongoInsertMany", "MongoUpdateOne", "MongoCreateIndex", "MongoFind"]}, + {"path": "/mySql", "steps": ["MySQLQuery", "Timeout"]}, + {"path": "/redis", "steps": ["RedisGet", "RedisSet", "RedisHset", "RedisHkeys", "RedisHincrby"]}, + {"path": "/diagChannelConsole", "steps": ["ConsoleLog", "ConsoleWarn", "ConsoleError", "ConsoleInfo"]}, + {"path": "/diagChannelWinston", "steps": ["WinstonError", "WinstonWarn", "WinstonInfo", "WinstonVerbose", "WinstonDebug", "WinstonSilly", "WinstonError2", "WinstonWarn2", "WinstonInfo2"]} + ] \ No newline at end of file diff --git a/Tests/FunctionalTests/Runner/TestValidation.js b/test/functionalTests/runner/testValidation.js similarity index 87% rename from Tests/FunctionalTests/Runner/TestValidation.js rename to test/functionalTests/runner/testValidation.js index a66d5fe77..cb242f38b 100644 --- a/Tests/FunctionalTests/Runner/TestValidation.js +++ b/test/functionalTests/runner/testValidation.js @@ -1,5 +1,5 @@ -var Utils = require("./Utils"); -var TaskExpectations = require("./TaskExpectations"); +var Utils = require("./utils"); +var TaskExpectations = require("./taskExpectations"); module.exports.TestValidation = class TestValidation { constructor(ingestion) { @@ -51,15 +51,15 @@ module.exports.TestValidation = class TestValidation { // Helper fn for validator that runs on all telemetry items const baseValidator = (item) => { - return item.tags['ai.application.ver'] === "1.0.0" && // version of TestApp - item.tags['ai.internal.sdkVersion'].indexOf("node:") === 0; // sdk should always report a version starting with "node:" + return true; + return item.tags['ai.internal.sdkVersion'].indexOf("node") > -1; // sdk should always report a version including "node:" }; // Helper fn to find item in the datset const findItem = (correlationId, type, fn, stepName, childContract) => { const _correlationId = correlationId; if (!type) return true; - for (var i = 0; i{ + test.steps.forEach((step) => { const expectation = TaskExpectations[step]; const success = findItem(correlationId, expectation.expectedTelemetryType, expectation.telemetryVerifier, step, expectation.childContract); @@ -91,7 +91,7 @@ module.exports.TestValidation = class TestValidation { }); // Did we find all of the items in the data set? - if (dataSet.length > 1){ + if (dataSet.length > 1) { Utils.Logging.error("FAILED EXPECTATION - Unexpected child telemetry item(s)!"); Utils.Logging.error(JSON.stringify(dataSet, null, 2)); hadFailed = true; @@ -115,14 +115,14 @@ module.exports.TestValidation = class TestValidation { let success = true; return Utils.Logging.enterSubunit("Validating performance counters...") .then(() => { - Utils.Logging.info("Expecting "+ expectedEach + " instance(s) each of all " + perfTypes.length + " performance counters"); + Utils.Logging.info("Expecting " + expectedEach + " instance(s) each of all " + perfTypes.length + " performance counters"); const metricTelemetry = this.ingestion.telemetry["MetricData"]; perfTypes.forEach((metricType) => { let count = 0; if (metricTelemetry) { - for (let i = 0; i 0 && telemetry.data.baseData.metrics[0].name === metricType) { count++; } } @@ -144,4 +144,4 @@ module.exports.TestValidation = class TestValidation { }) .then((success) => { Utils.Logging.exitSubunit(); return success; }); } -} +} \ No newline at end of file diff --git a/Tests/FunctionalTests/Runner/Utils.js b/test/functionalTests/runner/utils.js similarity index 100% rename from Tests/FunctionalTests/Runner/Utils.js rename to test/functionalTests/runner/utils.js diff --git a/Tests/FunctionalTests/TestApp/Config.js b/test/functionalTests/testApp/config.js similarity index 80% rename from Tests/FunctionalTests/TestApp/Config.js rename to test/functionalTests/testApp/config.js index bf6a0fb98..f693992ce 100644 --- a/Tests/FunctionalTests/TestApp/Config.js +++ b/test/functionalTests/testApp/config.js @@ -1,14 +1,12 @@ -var appInsights = require("applicationinsights"); - var Config = { ServerPort: "9099", - EndpointBaseAddress: "http://localhost:9091", + EndpointBaseAddress: "https://localhost:9091", MongoConnectionString: "mongodb://localhost:27017", MongoDbName: "testapp", MySqlConnectionString: "mysql://root:dummypw@localhost:33060/testdb", RedisConnectionString: "redis://localhost:63790", PostgresConnectionString: "pg://postgres:dummypw@localhost:54320/postgres", - InstrumentationKey: "TESTIKEY", + InstrumentationKey: "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333", AppInsightsEnabled: true, UseAutoCorrelation: true, UseAutoRequests: true, @@ -18,8 +16,7 @@ var Config = { UseAutoConsole: true, UseAutoConsoleLog: true, UseDiskCaching: false, - DistributedTracingMode: appInsights.DistributedTracingModes.AI_AND_W3C, - SampleRate: "100", + SampleRate: "1", } // Allow config overrides from env variables diff --git a/test/functionalTests/testApp/main.js b/test/functionalTests/testApp/main.js new file mode 100644 index 000000000..7dfbfefe3 --- /dev/null +++ b/test/functionalTests/testApp/main.js @@ -0,0 +1,114 @@ +var testconfig = require("./config"); +var appInsights = null; +if (testconfig.AppInsightsEnabled) { + + const { useAzureMonitor, shutdownAzureMonitor } = require("../../../out/src/"); + + let options = { + azureMonitorExporterOptions: { + connectionString:`InstrumentationKey=${testconfig.InstrumentationKey};IngestionEndpoint=${testconfig.EndpointBaseAddress}` + }, + samplingRatio: parseFloat(testconfig.SampleRate), + instrumentationOptions: { + azureSdk: { + enabled: true + }, + http: { + enabled: true, + ignoreOutgoingRequestHook: (options) => { + // Ignore outgoing requests with /test path + if (options.path === '/v2.1/track') { + return true; + } + return false; + } + }, + mongoDb: { + enabled: true + }, + mySql: { + enabled: true + }, + postgreSql: { + enabled: true + }, + redis: { + enabled: true + }, + redis4: { + enabled: true + }, + console: { + enabled: true, + }, + bunyan: { + enabled: true, + }, + winston: { + enabled: true, + }, + } + }; + + useAzureMonitor(options); + } + + var Tasks = require("./tasks"); + var port = parseInt(testconfig.ServerPort); + var bodyParser = require('body-parser'); + var express = require("express"); + var app = express(); + app.use(bodyParser.json()); + + app.get("/", (req, res) => { + res.send("OK"); + }); + + /** + * Receive route configuration object of the following form as POST body: + * [ + * {path: "/dependencyTest", steps:["HttpGet", "Timeout", "MongoInsert"]}, + * ... + * ] + * + * This input will create routes on this server that perform those tasks. + * The available tasks are defined in /Tasks/index.js + */ + app.post("/_configure", (req, res) => { + var stepConfig = req.body; + + var runTasks = (tasks, cb) => { + if (!tasks || tasks.length == 0) { + cb(); + return; + } + tasks = tasks.slice(0); + var task = tasks.shift(); + Tasks[task](() => runTasks(tasks, cb)); + } + + var generateStepRoute = (route) => { + app.get(route.path, (rq, rs) => { + runTasks(route.steps, () => rs.send("OK")); + }); + } + + for (var i = 0; i < stepConfig.length; i++) { + generateStepRoute(stepConfig[i]); + } + + res.send("OK"); + }); + + app.get("/_close", (req, res) => { + res.end("OK"); + server.close(); + if (testconfig.AppInsightsEnabled) { + shutdownAzureMonitor(); + process.exit(0); + } + }); + + var server = app.listen(port, () => { + console.log("TestApp Ready!"); + }); \ No newline at end of file diff --git a/Tests/FunctionalTests/TestApp/package.json b/test/functionalTests/testApp/package.json similarity index 97% rename from Tests/FunctionalTests/TestApp/package.json rename to test/functionalTests/testApp/package.json index 4b60e7b75..d5867443d 100644 --- a/Tests/FunctionalTests/TestApp/package.json +++ b/test/functionalTests/testApp/package.json @@ -19,7 +19,7 @@ "express": "4.15.2", "mongodb": "^3.1.13", "mysql": "^2.16.0", - "pg": "^7.8.0", + "pg": "^8.6.0", "redis": "3.1.1", "url": "0.11.0", "winston": "2.3.1" diff --git a/Tests/FunctionalTests/TestApp/Tasks/AzureSDKStorage.js b/test/functionalTests/testApp/tasks/azureSDKStorage.js similarity index 93% rename from Tests/FunctionalTests/TestApp/Tasks/AzureSDKStorage.js rename to test/functionalTests/testApp/tasks/azureSDKStorage.js index bf2a78584..8664e1045 100644 --- a/Tests/FunctionalTests/TestApp/Tasks/AzureSDKStorage.js +++ b/test/functionalTests/testApp/tasks/azureSDKStorage.js @@ -28,4 +28,4 @@ try { console.log(e.message); } -module.exports = Object.assign({}, module.exports); +module.exports = Object.assign({}, module.exports); \ No newline at end of file diff --git a/Tests/FunctionalTests/TestApp/Tasks/AzureSdkEventHubs.js b/test/functionalTests/testApp/tasks/azureSdkEventHubs.js similarity index 93% rename from Tests/FunctionalTests/TestApp/Tasks/AzureSdkEventHubs.js rename to test/functionalTests/testApp/tasks/azureSdkEventHubs.js index e3e49d2ce..db4972656 100644 --- a/Tests/FunctionalTests/TestApp/Tasks/AzureSdkEventHubs.js +++ b/test/functionalTests/testApp/tasks/azureSdkEventHubs.js @@ -23,4 +23,4 @@ try { console.log(e.message); } -module.exports = Object.assign({}, module.exports); +module.exports = Object.assign({}, module.exports); \ No newline at end of file diff --git a/Tests/FunctionalTests/TestApp/Tasks/Bunyan.js b/test/functionalTests/testApp/tasks/bunyan.js similarity index 74% rename from Tests/FunctionalTests/TestApp/Tasks/Bunyan.js rename to test/functionalTests/testApp/tasks/bunyan.js index 698510d8f..30ed8f3ae 100644 --- a/Tests/FunctionalTests/TestApp/Tasks/Bunyan.js +++ b/test/functionalTests/testApp/tasks/bunyan.js @@ -1,5 +1,5 @@ -var bunyan = require('bunyan'); -var log = bunyan.createLogger({name: 'TestApp', level: 0}); +var bunyan = require('bunyan'); +var log = bunyan.createLogger({name: 'TestApp', level: 0}); function logFn(type) { return (callback) => { diff --git a/Tests/FunctionalTests/TestApp/Tasks/HttpGet.js b/test/functionalTests/testApp/tasks/httpGet.js similarity index 51% rename from Tests/FunctionalTests/TestApp/Tasks/HttpGet.js rename to test/functionalTests/testApp/tasks/httpGet.js index 9c335c509..8be1beb1e 100644 --- a/Tests/FunctionalTests/TestApp/Tasks/HttpGet.js +++ b/test/functionalTests/testApp/tasks/httpGet.js @@ -1,11 +1,12 @@ -var http = require("http"); -var URL = require('url'); -var Config = require("../Config"); +var https = require("https"); +var Config = require("../config"); + +process.removeAllListeners('warning'); +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; /** Make a HTTP request */ module.exports = (callback) => { - var url = URL.parse(Config.EndpointBaseAddress); - http.get({host: url.hostname, path: "/", port: url.port}, (resp) => { + https.get(Config.EndpointBaseAddress, (resp) => { var data = ""; resp.on("data", (d) => { data += d; diff --git a/Tests/FunctionalTests/TestApp/Tasks/index.js b/test/functionalTests/testApp/tasks/index.js similarity index 69% rename from Tests/FunctionalTests/TestApp/Tasks/index.js rename to test/functionalTests/testApp/tasks/index.js index 14b060f02..740d73485 100644 --- a/Tests/FunctionalTests/TestApp/Tasks/index.js +++ b/test/functionalTests/testApp/tasks/index.js @@ -1,16 +1,15 @@ -var AISDK = require("./AISDK"); -var Mongo = require("./Mongo"); -var MySQL = require("./MySQL"); -var Bunyan = require("./Bunyan"); -var Winston = require("./Winston"); -var Redis = require("./Redis"); -var Utils = require("./Utils"); -var Postgres = require("./Postgres"); -var AzureSdkStorage = require("./AzureSDKStorage"); -var AzureSdkEventHubs = require("./AzureSdkEventHubs"); +var Mongo = require("./mongo"); +var MySQL = require("./mySQL"); +var Bunyan = require("./bunyan"); +var Winston = require("./winston"); +var Redis = require("./redis"); +var Utils = require("./utils"); +var Postgres = require("./postgres"); +var AzureSdkStorage = require("./azureSDKStorage"); +var AzureSdkEventHubs = require("./azureSdkEventHubs"); module.exports = { - HttpGet: require("./HttpGet"), + HttpGet: require("./httpGet"), AzureSdkEventHubsSend: AzureSdkEventHubs.sendMessage, AzureSdkCreate: AzureSdkStorage.createContainer, AzureSdkDelete: AzureSdkStorage.deleteContainer, @@ -29,7 +28,6 @@ module.exports = { ConsoleWarn: Utils.consoleWarn, ConsoleInfo: Utils.consoleInfo, ConsoleLog: Utils.consoleLog, - ConsoleAssert: Utils.consoleAssert, MySQLQuery: MySQL.query, RedisGet: Redis.get, RedisSet: Redis.set, @@ -47,9 +45,6 @@ module.exports = { WinstonWarn2: Winston.warn2, WinstonInfo2: Winston.info2, PostgresQuery: Postgres.query, - AITrackDep: AISDK.trackDependency, - AITrackTrace: AISDK.trackTrace, - AITrackExc: AISDK.trackException, Timeout: Utils.timeout, ThrowError: Utils.throwError -} +} \ No newline at end of file diff --git a/Tests/FunctionalTests/TestApp/Tasks/Mongo.js b/test/functionalTests/testApp/tasks/mongo.js similarity index 98% rename from Tests/FunctionalTests/TestApp/Tasks/Mongo.js rename to test/functionalTests/testApp/tasks/mongo.js index 5e6db545b..e00a13a7c 100644 --- a/Tests/FunctionalTests/TestApp/Tasks/Mongo.js +++ b/test/functionalTests/testApp/tasks/mongo.js @@ -1,4 +1,4 @@ -var Config = require("../Config"); +var Config = require("../config"); var mongo = require('mongodb').MongoClient; var ready = false; diff --git a/Tests/FunctionalTests/TestApp/Tasks/MySQL.js b/test/functionalTests/testApp/tasks/mySQL.js similarity index 96% rename from Tests/FunctionalTests/TestApp/Tasks/MySQL.js rename to test/functionalTests/testApp/tasks/mySQL.js index 628ff6f82..dae3d99c2 100644 --- a/Tests/FunctionalTests/TestApp/Tasks/MySQL.js +++ b/test/functionalTests/testApp/tasks/mySQL.js @@ -1,4 +1,4 @@ -var Config = require("../Config"); +var Config = require("../config"); var mysql = require('mysql'); var ready = false; diff --git a/test/functionalTests/testApp/tasks/postgres.js b/test/functionalTests/testApp/tasks/postgres.js new file mode 100644 index 000000000..614a5b2ec --- /dev/null +++ b/test/functionalTests/testApp/tasks/postgres.js @@ -0,0 +1,36 @@ +var Config = require("../config"); +var pg = require('pg'); + +var ready = false; +var client = null; + +function connect() { + client = new pg.Pool({ connectionString: Config.PostgresConnectionString }); + client.connect((err) => { + if (err) { + setTimeout(connect, 500); + return; + } + ready = true; + }); +} +connect(); + +function query(callback) { + if (!ready) { + setTimeout(() => query(callback), 1500); + return; + } + + client.query("SELECT NOW()", (err, ret) => { + if (err) { + console.log("Failed to query postgres." + err); + } + callback() + }); +} + + +module.exports = { + query: query +} \ No newline at end of file diff --git a/Tests/FunctionalTests/TestApp/Tasks/Redis.js b/test/functionalTests/testApp/tasks/redis.js similarity index 97% rename from Tests/FunctionalTests/TestApp/Tasks/Redis.js rename to test/functionalTests/testApp/tasks/redis.js index 3daa5430d..e6f3ec68c 100644 --- a/Tests/FunctionalTests/TestApp/Tasks/Redis.js +++ b/test/functionalTests/testApp/tasks/redis.js @@ -1,4 +1,4 @@ -var Config = require("../Config"); +var Config = require("../config"); var redis = require('redis') var ready = false; diff --git a/Tests/FunctionalTests/TestApp/Tasks/Utils.js b/test/functionalTests/testApp/tasks/utils.js similarity index 100% rename from Tests/FunctionalTests/TestApp/Tasks/Utils.js rename to test/functionalTests/testApp/tasks/utils.js diff --git a/Tests/FunctionalTests/TestApp/Tasks/Winston.js b/test/functionalTests/testApp/tasks/winston.js similarity index 93% rename from Tests/FunctionalTests/TestApp/Tasks/Winston.js rename to test/functionalTests/testApp/tasks/winston.js index 65c37840c..64c83876c 100644 --- a/Tests/FunctionalTests/TestApp/Tasks/Winston.js +++ b/test/functionalTests/testApp/tasks/winston.js @@ -1,4 +1,4 @@ -var winston= require('winston'); +var winston= require('winston'); winston.level = 'silly'; function logFn(type) { @@ -26,4 +26,4 @@ module.exports = { error2: logFn2('error'), warn2: logFn2('warn'), info2: logFn2('info') -} +} \ No newline at end of file diff --git a/test/performanceTests/README.md b/test/performanceTests/README.md new file mode 100644 index 000000000..5480f367d --- /dev/null +++ b/test/performanceTests/README.md @@ -0,0 +1,10 @@ +### Guide + +1. Copy the `sample.env` file and name it as `.env`. +2. Create an Application Insights resource and populate the `.env` file with connectionString. +3. Run the tests as follows (parameters can be modified to as appropriate): + +- Tracking Dependencies (spans) + - `npm run perf-test:node -- TrackDependencyTest --warmup 1 --iterations 1 --parallel 2 --duration 15` +- Tracking Traces (logs) + - `npm run perf-test:node -- TrackTraceTest --warmup 1 --iterations 1 --parallel 2 --duration 15` diff --git a/test/performanceTests/package-lock.json b/test/performanceTests/package-lock.json new file mode 100644 index 000000000..bbfbda8df --- /dev/null +++ b/test/performanceTests/package-lock.json @@ -0,0 +1,3329 @@ +{ + "name": "@azure-tests/perf-monitor-opentelemetry", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@azure-tests/perf-monitor-opentelemetry", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@azure-tools/test-perf": "^1.0.0", + "@opentelemetry/api": "^1.8.0", + "@opentelemetry/api-logs": "^0.49.1", + "@opentelemetry/sdk-logs": "^0.49.1", + "applicationinsights": "^3.0.0-beta.12", + "dotenv": "^16.4.5", + "tslib": "^2.6.2" + }, + "devDependencies": { + "@types/node": "^18.0.0", + "eslint": "^8.0.0", + "rimraf": "^5.0.5", + "typescript": "~5.3.3" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@azure-tools/test-perf": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@azure-tools/test-perf/-/test-perf-1.0.0.tgz", + "integrity": "sha512-wnsGpHLmMgKKvX4FX4lCwGJ2QX2aXb6QxWYJuBNE2CUbUvDyWxrwDh6NhkAwmCIXrvPsUUZ67o0w5jwr76CW/Q==", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-client": "^1.3.1", + "@azure/core-rest-pipeline": "^1.1.0", + "@azure/core-util": "^1.3.2", + "@types/minimist": "^1.2.2", + "fs-extra": "^10.0.0", + "minimist": "^1.2.8", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/abort-controller": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.1.tgz", + "integrity": "sha512-NhzeNm5zu2fPlwGXPUjzsRCRuPx5demaZyNcyNYJDqpa/Sbxzvo/RYt9IwUaAOnDW5+r7J9UOE6f22TQnb9nhQ==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.7.1.tgz", + "integrity": "sha512-dyeQwvgthqs/SlPVQbZQetpslXceHd4i5a7M/7z/lGEAVwnSluabnQOjF2/dk/hhWgMISusv1Ytp4mQ8JNy62A==", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.1.tgz", + "integrity": "sha512-hHYFx9lz0ZpbO5W+iotU9tmIX1jPcoIjYUEUaWGuMi1628LCQ/z05TUR4P+NRtMgyoHQuyVYyGQiD3PC47kaIA==", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.9.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.6.1", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.15.1.tgz", + "integrity": "sha512-ZxS6i3eHxh86u+1eWZJiYywoN2vxvsSoAUx60Mny8cZ4nTwvt7UzVVBJO+m2PW2KIJfNiXMt59xBa59htOWL4g==", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.3.0", + "@azure/logger": "^1.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.1.1.tgz", + "integrity": "sha512-qPbYhN1pE5XQ2jPKIHP33x8l3oBu1UqIWnYqZZ3OYnYjzY0xqIHjn49C+ptsPD9yC7uyWI9Zm7iZUZLs2R4DhQ==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.8.1.tgz", + "integrity": "sha512-L3voj0StUdJ+YKomvwnTv7gHzguJO+a6h30pmmZdRprJCM+RJlGMPxzuh4R7lhQu1jNmEtaHX5wvTgWLDAmbGQ==", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/functions": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@azure/functions/-/functions-3.5.1.tgz", + "integrity": "sha512-6UltvJiuVpvHSwLcK/Zc6NfUwlkDLOFFx97BHCJzlWNsfiWwzwmTsxJXg4kE/LemKTHxPpfoPE+kOJ8hAdiKFQ==", + "dependencies": { + "iconv-lite": "^0.6.3", + "long": "^4.0.0", + "uuid": "^8.3.0" + } + }, + "node_modules/@azure/identity": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-3.4.2.tgz", + "integrity": "sha512-0q5DL4uyR0EZ4RXQKD8MadGH6zTIcloUoS/RVbCpNpej4pwte0xpqYxk8K97Py2RiuUvI7F4GXpoT4046VfufA==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.5.0", + "@azure/core-client": "^1.4.0", + "@azure/core-rest-pipeline": "^1.1.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.6.1", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^3.5.0", + "@azure/msal-node": "^2.5.1", + "events": "^3.0.0", + "jws": "^4.0.0", + "open": "^8.0.0", + "stoppable": "^1.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/identity/node_modules/@azure/abort-controller": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.1.1.tgz", + "integrity": "sha512-/+4TtokaGgC+MnThdf6HyIH9Wrjp+CnCn3Nx3ggevN7FFjjNyjqg0yLlc2i9S+Z2uAzI8GYOo35Nzb1MhQ89MA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/monitor-opentelemetry": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/monitor-opentelemetry/-/monitor-opentelemetry-1.3.0.tgz", + "integrity": "sha512-aRC9tO6B/hm3aSSTzUKiGIUdsCzpowGw8z+XbZmAOAHVma1dcMgnXzgO7SP/ASLe9VhMwTOImeRy4MIESDmWjg==", + "dependencies": { + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.0.0", + "@azure/core-rest-pipeline": "^1.1.0", + "@azure/functions": "^3.2.0", + "@azure/logger": "^1.0.0", + "@azure/monitor-opentelemetry-exporter": "1.0.0-beta.21", + "@azure/opentelemetry-instrumentation-azure-sdk": "^1.0.0-beta.5", + "@microsoft/applicationinsights-web-snippet": "^1.1.2", + "@opentelemetry/api": "^1.8.0", + "@opentelemetry/api-logs": "^0.49.1", + "@opentelemetry/core": "^1.22.0", + "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation-bunyan": "^0.36.0", + "@opentelemetry/instrumentation-http": "^0.49.1", + "@opentelemetry/instrumentation-mongodb": "^0.40.0", + "@opentelemetry/instrumentation-mysql": "^0.36.0", + "@opentelemetry/instrumentation-pg": "^0.39.0", + "@opentelemetry/instrumentation-redis": "^0.37.0", + "@opentelemetry/instrumentation-redis-4": "^0.37.0", + "@opentelemetry/resource-detector-azure": "^0.2.4", + "@opentelemetry/resources": "^1.22.0", + "@opentelemetry/sdk-logs": "^0.49.1", + "@opentelemetry/sdk-metrics": "^1.22.0", + "@opentelemetry/sdk-node": "^0.49.1", + "@opentelemetry/sdk-trace-base": "^1.22.0", + "@opentelemetry/sdk-trace-node": "^1.22.0", + "@opentelemetry/semantic-conventions": "^1.22.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/monitor-opentelemetry-exporter": { + "version": "1.0.0-beta.21", + "resolved": "https://registry.npmjs.org/@azure/monitor-opentelemetry-exporter/-/monitor-opentelemetry-exporter-1.0.0-beta.21.tgz", + "integrity": "sha512-2geSfdILDovNrjSYCTO6iG3OsZTQUIFSHYnW9Olao8KogYlbre1Pu02iwYW2KqDaXxMaRyT8ifRFEJJX2INqAA==", + "dependencies": { + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.0.0", + "@azure/core-rest-pipeline": "^1.1.0", + "@opentelemetry/api": "^1.7.0", + "@opentelemetry/api-logs": "^0.48.0", + "@opentelemetry/core": "^1.21.0", + "@opentelemetry/resources": "^1.21.0", + "@opentelemetry/sdk-logs": "^0.48.0", + "@opentelemetry/sdk-metrics": "^1.21.0", + "@opentelemetry/sdk-trace-base": "^1.21.0", + "@opentelemetry/semantic-conventions": "^1.21.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/monitor-opentelemetry-exporter/node_modules/@opentelemetry/api": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.7.0.tgz", + "integrity": "sha512-AdY5wvN0P2vXBi3b29hxZgSFvdhdxPB9+f0B6s//P9Q8nibRWeA3cHm8UmLpio9ABigkVHJ5NMPk+Mz8VCCyrw==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@azure/monitor-opentelemetry-exporter/node_modules/@opentelemetry/api-logs": { + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.48.0.tgz", + "integrity": "sha512-1/aMiU4Eqo3Zzpfwu51uXssp5pzvHFObk8S9pKAiXb1ne8pvg1qxBQitYL1XUiAMEXFzgjaidYG2V6624DRhhw==", + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@azure/monitor-opentelemetry-exporter/node_modules/@opentelemetry/core": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.21.0.tgz", + "integrity": "sha512-KP+OIweb3wYoP7qTYL/j5IpOlu52uxBv5M4+QhSmmUfLyTgu1OIS71msK3chFo1D6Y61BIH3wMiMYRCxJCQctA==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.21.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.8.0" + } + }, + "node_modules/@azure/monitor-opentelemetry-exporter/node_modules/@opentelemetry/resources": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.21.0.tgz", + "integrity": "sha512-1Z86FUxPKL6zWVy2LdhueEGl9AHDJcx+bvHStxomruz6Whd02mE3lNUMjVJ+FGRoktx/xYQcxccYb03DiUP6Yw==", + "dependencies": { + "@opentelemetry/core": "1.21.0", + "@opentelemetry/semantic-conventions": "1.21.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.8.0" + } + }, + "node_modules/@azure/monitor-opentelemetry-exporter/node_modules/@opentelemetry/sdk-logs": { + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.48.0.tgz", + "integrity": "sha512-lRcA5/qkSJuSh4ItWCddhdn/nNbVvnzM+cm9Fg1xpZUeTeozjJDBcHnmeKoOaWRnrGYBdz6UTY6bynZR9aBeAA==", + "dependencies": { + "@opentelemetry/core": "1.21.0", + "@opentelemetry/resources": "1.21.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.8.0", + "@opentelemetry/api-logs": ">=0.39.1" + } + }, + "node_modules/@azure/monitor-opentelemetry-exporter/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.21.0.tgz", + "integrity": "sha512-lkC8kZYntxVKr7b8xmjCVUgE0a8xgDakPyDo9uSWavXPyYqLgYYGdEd2j8NxihRyb6UwpX3G/hFUF4/9q2V+/g==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@azure/msal-browser": { + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.11.1.tgz", + "integrity": "sha512-tZFJnP5ZpgkmazSriEDW+Xl3/4WI823uhnYhWCHPkGywFWEZoPA5VkiCK8x4x8ECXp3mGr5qEI82MU43PBiaKA==", + "dependencies": { + "@azure/msal-common": "14.8.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "14.8.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.8.1.tgz", + "integrity": "sha512-9HfBMDTIgtFFkils+o6gO/aGEoLLuc4z+QLLfhy/T1bTNPiVsX/9CjaBPMZGnMltN/IlMkU5SGGNggGh55p5xA==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.6.6.tgz", + "integrity": "sha512-j+1hW81ccglIYWukXufzRA4O71BCmpbmCO66ECDyE9FuPno6SjiR+K+mIk4tg6aQ7/UO2QA/EnRmT6YN0EF1Hw==", + "dependencies": { + "@azure/msal-common": "14.8.1", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@azure/opentelemetry-instrumentation-azure-sdk": { + "version": "1.0.0-beta.5", + "resolved": "https://registry.npmjs.org/@azure/opentelemetry-instrumentation-azure-sdk/-/opentelemetry-instrumentation-azure-sdk-1.0.0-beta.5.tgz", + "integrity": "sha512-fsUarKQDvjhmBO4nIfaZkfNSApm1hZBzcvpNbSrXdcUBxu7lRvKsV5DnwszX7cnhLyVOW9yl1uigtRQ1yDANjA==", + "dependencies": { + "@azure/core-tracing": "^1.0.0", + "@azure/logger": "^1.0.0", + "@opentelemetry/api": "^1.4.1", + "@opentelemetry/core": "^1.15.2", + "@opentelemetry/instrumentation": "^0.41.2", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/opentelemetry-instrumentation-azure-sdk/node_modules/@opentelemetry/instrumentation": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.41.2.tgz", + "integrity": "sha512-rxU72E0pKNH6ae2w5+xgVYZLzc5mlxAbGzF4shxMVK8YC2QQsfN38B2GPbj0jvrKWWNUElfclQ+YTykkNg/grw==", + "dependencies": { + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "1.4.2", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.1", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@azure/opentelemetry-instrumentation-azure-sdk/node_modules/import-in-the-middle": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz", + "integrity": "sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw==", + "dependencies": { + "acorn": "^8.8.2", + "acorn-import-assertions": "^1.9.0", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.4.tgz", + "integrity": "sha512-MqBisuxTkYvPFnEiu+dag3xG/NBUDzSbAFAWlzfkGnQkjVZ6by3h4atbBc+Ikqup1z5BfB4BN18gKWR1YyppNw==", + "dependencies": { + "@grpc/proto-loader": "^0.7.10", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.12", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.12.tgz", + "integrity": "sha512-DCVwMxqYzpUCiDMl7hQ384FqP4T3DbNpXU8pt681l3UWCip1WUiD5JrkImUwCB9a7f2cq4CUTmi5r/xIMRPY1Q==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.4", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@grpc/proto-loader/node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@microsoft/applicationinsights-web-snippet": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-snippet/-/applicationinsights-web-snippet-1.1.2.tgz", + "integrity": "sha512-qPoOk3MmEx3gS6hTc1/x8JWQG5g4BvRdH7iqZMENBsKCL927b7D7Mvl19bh3sW9Ucrg1fVrF+4hqShwQNdqLxQ==" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.8.0.tgz", + "integrity": "sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.49.1.tgz", + "integrity": "sha512-kaNl/T7WzyMUQHQlVq7q0oV4Kev6+0xFwqzofryC66jgGMacd0QH5TwfpbUwSTby+SdAdprAe5UKMvBw4tKS5Q==", + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.22.0.tgz", + "integrity": "sha512-Nfdxyg8YtWqVWkyrCukkundAjPhUXi93JtVQmqDT1mZRVKqA7e2r7eJCrI+F651XUBMp0hsOJSGiFk3QSpaIJw==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.22.0.tgz", + "integrity": "sha512-0VoAlT6x+Xzik1v9goJ3pZ2ppi6+xd3aUfg4brfrLkDBHRIVjMP0eBHrKrhB+NKcDyMAg8fAbGL3Npg/F6AwWA==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.49.1.tgz", + "integrity": "sha512-3QoBnIGCmEkujynUP0mK155QtOM0MSf9FNrEw7u9ieCFsoMiyatg2hPp+alEDONJ8N8wGEK+wP2q3icgXBiggw==", + "dependencies": { + "@opentelemetry/api-logs": "0.49.1", + "@opentelemetry/core": "1.22.0", + "@opentelemetry/otlp-exporter-base": "0.49.1", + "@opentelemetry/otlp-transformer": "0.49.1", + "@opentelemetry/sdk-logs": "0.49.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.49.1.tgz", + "integrity": "sha512-t/sWYqfcwn81SvYHIyYJDlJD2CjFz3/h2t4j+XCtdoHAfu+WVJQmwLsGYJPlCDp3UXQfFpMJMWvLlvtD2SL+rg==", + "dependencies": { + "@opentelemetry/core": "1.22.0", + "@opentelemetry/otlp-exporter-base": "0.49.1", + "@opentelemetry/otlp-transformer": "0.49.1", + "@opentelemetry/resources": "1.22.0", + "@opentelemetry/sdk-metrics": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.49.1.tgz", + "integrity": "sha512-Zbd7f3zF7fI2587MVhBizaW21cO/SordyrZGtMtvhoxU6n4Qb02Gx71X4+PzXH620e0+JX+Pcr9bYb1HTeVyJA==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.22.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.49.1", + "@opentelemetry/otlp-transformer": "0.49.1", + "@opentelemetry/resources": "1.22.0", + "@opentelemetry/sdk-trace-base": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.49.1.tgz", + "integrity": "sha512-KOLtZfZvIrpGZLVvblKsiVQT7gQUZNKcUUH24Zz6Xbi7LJb9Vt6xtUZFYdR5IIjvt47PIqBKDWUQlU0o1wAsRw==", + "dependencies": { + "@opentelemetry/core": "1.22.0", + "@opentelemetry/otlp-exporter-base": "0.49.1", + "@opentelemetry/otlp-transformer": "0.49.1", + "@opentelemetry/resources": "1.22.0", + "@opentelemetry/sdk-trace-base": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.49.1.tgz", + "integrity": "sha512-n8ON/c9pdMyYAfSFWKkgsPwjYoxnki+6Olzo+klKfW7KqLWoyEkryNkbcMIYnGGNXwdkMIrjoaP0VxXB26Oxcg==", + "dependencies": { + "@opentelemetry/core": "1.22.0", + "@opentelemetry/otlp-exporter-base": "0.49.1", + "@opentelemetry/otlp-proto-exporter-base": "0.49.1", + "@opentelemetry/otlp-transformer": "0.49.1", + "@opentelemetry/resources": "1.22.0", + "@opentelemetry/sdk-trace-base": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.22.0.tgz", + "integrity": "sha512-XcFs6rGvcTz0qW5uY7JZDYD0yNEXdekXAb6sFtnZgY/cHY6BQ09HMzOjv9SX+iaXplRDcHr1Gta7VQKM1XXM6g==", + "dependencies": { + "@opentelemetry/core": "1.22.0", + "@opentelemetry/resources": "1.22.0", + "@opentelemetry/sdk-trace-base": "1.22.0", + "@opentelemetry/semantic-conventions": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.49.1.tgz", + "integrity": "sha512-0DLtWtaIppuNNRRllSD4bjU8ZIiLp1cDXvJEbp752/Zf+y3gaLNaoGRGIlX4UHhcsrmtL+P2qxi3Hodi8VuKiQ==", + "dependencies": { + "@opentelemetry/api-logs": "0.49.1", + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "1.7.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-bunyan": { + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.36.0.tgz", + "integrity": "sha512-sHD5BSiqSrgWow7VmugEFzV8vGdsz5m+w1v9tK6YwRzuAD7vbo57chluq+UBzIqStoCH+0yOzRzSALH7hrfffg==", + "dependencies": { + "@opentelemetry/api-logs": "^0.49.1", + "@opentelemetry/instrumentation": "^0.49.1", + "@types/bunyan": "1.8.9" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.49.1.tgz", + "integrity": "sha512-Yib5zrW2s0V8wTeUK/B3ZtpyP4ldgXj9L3Ws/axXrW1dW0/mEFKifK50MxMQK9g5NNJQS9dWH7rvcEGZdWdQDA==", + "dependencies": { + "@opentelemetry/core": "1.22.0", + "@opentelemetry/instrumentation": "0.49.1", + "@opentelemetry/semantic-conventions": "1.22.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongodb": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.40.0.tgz", + "integrity": "sha512-ldlJUW/1UlnGtIWBt7fIUl+7+TGOKxIU+0Js5ukpXfQc07ENYFeck5TdbFjvYtF8GppPErnsZJiFiRdYm6Pv/Q==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/sdk-metrics": "^1.9.1", + "@opentelemetry/semantic-conventions": "^1.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql": { + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.36.0.tgz", + "integrity": "sha512-2mt/032SLkiuddzMrq3YwM0bHksXRep69EzGRnBfF+bCbwYvKLpqmSFqJZ9T3yY/mBWj+tvdvc1+klXGrh2QnQ==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/semantic-conventions": "^1.0.0", + "@types/mysql": "2.15.22" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pg": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.39.1.tgz", + "integrity": "sha512-pX5ujDOyGpPcrZlzaD3LJzmyaSMMMKAP+ffTHJp9vasvZJr+LifCk53TMPVUafcXKV/xX/IIkvADO+67M1Z25g==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/semantic-conventions": "^1.0.0", + "@opentelemetry/sql-common": "^0.40.0", + "@types/pg": "8.6.1", + "@types/pg-pool": "2.0.4" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-redis": { + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.37.0.tgz", + "integrity": "sha512-9G0T74kheu37k+UvyBnAcieB5iowxska3z2rhUcSTL8Cl0y/CvMn7sZ7txkUbXt0rdX6qeEUdMLmbsY2fPUM7Q==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/redis-common": "^0.36.1", + "@opentelemetry/semantic-conventions": "^1.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-redis-4": { + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.37.0.tgz", + "integrity": "sha512-WNO+HALvPPvjbh7UEEIuay0Z0d2mIfSCkBZbPRwZttDGX6LYGc2WnRgJh3TnYqjp7/y9IryWIbajAFIebj1OBA==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/redis-common": "^0.36.1", + "@opentelemetry/semantic-conventions": "^1.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.49.1.tgz", + "integrity": "sha512-z6sHliPqDgJU45kQatAettY9/eVF58qVPaTuejw9YWfSRqid9pXPYeegDCSdyS47KAUgAtm+nC28K3pfF27HWg==", + "dependencies": { + "@opentelemetry/core": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.49.1.tgz", + "integrity": "sha512-DNDNUWmOqtKTFJAyOyHHKotVox0NQ/09ETX8fUOeEtyNVHoGekAVtBbvIA3AtK+JflP7LC0PTjlLfruPM3Wy6w==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.22.0", + "@opentelemetry/otlp-exporter-base": "0.49.1", + "protobufjs": "^7.2.3" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-proto-exporter-base": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-proto-exporter-base/-/otlp-proto-exporter-base-0.49.1.tgz", + "integrity": "sha512-x1qB4EUC7KikUl2iNuxCkV8yRzrSXSyj4itfpIO674H7dhI7Zv37SFaOJTDN+8Z/F50gF2ISFH9CWQ4KCtGm2A==", + "dependencies": { + "@opentelemetry/core": "1.22.0", + "@opentelemetry/otlp-exporter-base": "0.49.1", + "protobufjs": "^7.2.3" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.49.1.tgz", + "integrity": "sha512-Z+koA4wp9L9e3jkFacyXTGphSWTbOKjwwXMpb0CxNb0kjTHGUxhYRN8GnkLFsFo5NbZPjP07hwAqeEG/uCratQ==", + "dependencies": { + "@opentelemetry/api-logs": "0.49.1", + "@opentelemetry/core": "1.22.0", + "@opentelemetry/resources": "1.22.0", + "@opentelemetry/sdk-logs": "0.49.1", + "@opentelemetry/sdk-metrics": "1.22.0", + "@opentelemetry/sdk-trace-base": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.22.0.tgz", + "integrity": "sha512-qBItJm9ygg/jCB5rmivyGz1qmKZPsL/sX715JqPMFgq++Idm0x+N9sLQvWFHFt2+ZINnCSojw7FVBgFW6izcXA==", + "dependencies": { + "@opentelemetry/core": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.22.0.tgz", + "integrity": "sha512-pMLgst3QIwrUfepraH5WG7xfpJ8J3CrPKrtINK0t7kBkuu96rn+HDYQ8kt3+0FXvrZI8YJE77MCQwnJWXIrgpA==", + "dependencies": { + "@opentelemetry/core": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/redis-common": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.36.1.tgz", + "integrity": "sha512-YjfNEr7DK1Ymc5H0bzhmqVvMcCs+PUEUerzrpTFdHfZxj3HpnnjZTIFKx/gxiL/sajQ8dxycjlreoYTVYKBXlw==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resource-detector-azure": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.2.5.tgz", + "integrity": "sha512-O/s4MW9UhLtOebNcdtM5sXm2tZ7O8Ow0avkuFqwwZYTeBcI7ipJs9L8mv8q4bP8K9AQabLLBYw+vOOpN7aH/dA==", + "dependencies": { + "@opentelemetry/resources": "^1.10.1", + "@opentelemetry/semantic-conventions": "^1.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.22.0.tgz", + "integrity": "sha512-+vNeIFPH2hfcNL0AJk/ykJXoUCtR1YaDUZM+p3wZNU4Hq98gzq+7b43xbkXjadD9VhWIUQqEwXyY64q6msPj6A==", + "dependencies": { + "@opentelemetry/core": "1.22.0", + "@opentelemetry/semantic-conventions": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.49.1.tgz", + "integrity": "sha512-gCzYWsJE0h+3cuh3/cK+9UwlVFyHvj3PReIOCDOmdeXOp90ZjKRoDOJBc3mvk1LL6wyl1RWIivR8Rg9OToyesw==", + "dependencies": { + "@opentelemetry/core": "1.22.0", + "@opentelemetry/resources": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.9.0", + "@opentelemetry/api-logs": ">=0.39.1" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.22.0.tgz", + "integrity": "sha512-k6iIx6H3TZ+BVMr2z8M16ri2OxWaljg5h8ihGJxi/KQWcjign6FEaEzuigXt5bK9wVEhqAcWLCfarSftaNWkkg==", + "dependencies": { + "@opentelemetry/core": "1.22.0", + "@opentelemetry/resources": "1.22.0", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/sdk-node": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.49.1.tgz", + "integrity": "sha512-feBIT85ndiSHXsQ2gfGpXC/sNeX4GCHLksC4A9s/bfpUbbgbCSl0RvzZlmEpCHarNrkZMwFRi4H0xFfgvJEjrg==", + "dependencies": { + "@opentelemetry/api-logs": "0.49.1", + "@opentelemetry/core": "1.22.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.49.1", + "@opentelemetry/exporter-trace-otlp-http": "0.49.1", + "@opentelemetry/exporter-trace-otlp-proto": "0.49.1", + "@opentelemetry/exporter-zipkin": "1.22.0", + "@opentelemetry/instrumentation": "0.49.1", + "@opentelemetry/resources": "1.22.0", + "@opentelemetry/sdk-logs": "0.49.1", + "@opentelemetry/sdk-metrics": "1.22.0", + "@opentelemetry/sdk-trace-base": "1.22.0", + "@opentelemetry/sdk-trace-node": "1.22.0", + "@opentelemetry/semantic-conventions": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.22.0.tgz", + "integrity": "sha512-pfTuSIpCKONC6vkTpv6VmACxD+P1woZf4q0K46nSUvXFvOFqjBYKFaAMkKD3M1mlKUUh0Oajwj35qNjMl80m1Q==", + "dependencies": { + "@opentelemetry/core": "1.22.0", + "@opentelemetry/resources": "1.22.0", + "@opentelemetry/semantic-conventions": "1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.22.0.tgz", + "integrity": "sha512-gTGquNz7ue8uMeiWPwp3CU321OstQ84r7PCDtOaCicjbJxzvO8RZMlEC4geOipTeiF88kss5n6w+//A0MhP1lQ==", + "dependencies": { + "@opentelemetry/context-async-hooks": "1.22.0", + "@opentelemetry/core": "1.22.0", + "@opentelemetry/propagator-b3": "1.22.0", + "@opentelemetry/propagator-jaeger": "1.22.0", + "@opentelemetry/sdk-trace-base": "1.22.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.22.0.tgz", + "integrity": "sha512-CAOgFOKLybd02uj/GhCdEeeBjOS0yeoDeo/CA7ASBSmenpZHAKGB3iDm/rv3BQLcabb/OprDEsSQ1y0P8A7Siw==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sql-common": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.40.0.tgz", + "integrity": "sha512-vSqRJYUPJVjMFQpYkQS3ruexCPSZJ8esne3LazLwtCPaPRvzZ7WG3tX44RouAn7w4wMp8orKguBqtt+ng2UTnw==", + "dependencies": { + "@opentelemetry/core": "^1.1.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@types/bunyan": { + "version": "1.8.9", + "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.9.tgz", + "integrity": "sha512-ZqS9JGpBxVOvsawzmVt30sP++gSQMTejCkIAQ3VdadOcRE8izTyW66hufvwLeH+YEGP6Js2AW7Gz+RMyvrEbmw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==" + }, + "node_modules/@types/mysql": { + "version": "2.15.22", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.22.tgz", + "integrity": "sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "18.19.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.26.tgz", + "integrity": "sha512-+wiMJsIwLOYCvUqSdKTrfkS8mpTp+MPINe6+Np4TAGFWWRWiBQ5kSq9nZGCSPkzx9mvT+uEukzpX4MOSCydcvw==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/pg": { + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.1.tgz", + "integrity": "sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "node_modules/@types/pg-pool": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.4.tgz", + "integrity": "sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ==", + "dependencies": { + "@types/pg": "*" + } + }, + "node_modules/@types/shimmer": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.0.5.tgz", + "integrity": "sha512-9Hp0ObzwwO57DpLFF0InUjUm/II8GmKAvzbefxQTihCb7KI6yc9yzf0nLc4mVdby5N4DRCgQM2wCup9KTieeww==" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/applicationinsights": { + "version": "3.0.0-beta.12", + "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-3.0.0-beta.12.tgz", + "integrity": "sha512-aPo2LNRmLVgDEzf6EpavHoNzixRg5QQX4DLXPzbNERfnYB1RFi7P+SDBuhMz6yjsAL/Mekn6KnOQ3Fqpt6nvjg==", + "dependencies": { + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.0.0", + "@azure/core-rest-pipeline": "^1.9.2", + "@azure/identity": "^3.1.3", + "@azure/monitor-opentelemetry": "^1.3.0", + "@azure/monitor-opentelemetry-exporter": "^1.0.0-beta.21", + "@azure/opentelemetry-instrumentation-azure-sdk": "^1.0.0-beta.5", + "@opentelemetry/api": "^1.8.0", + "@opentelemetry/api-logs": "^0.49.1", + "@opentelemetry/core": "^1.22.0", + "@opentelemetry/exporter-logs-otlp-http": "^0.49.1", + "@opentelemetry/exporter-metrics-otlp-http": "^0.49.1", + "@opentelemetry/exporter-trace-otlp-http": "^0.49.1", + "@opentelemetry/otlp-exporter-base": "^0.49.1", + "@opentelemetry/resources": "^1.22.0", + "@opentelemetry/sdk-logs": "^0.49.1", + "@opentelemetry/sdk-metrics": "^1.22.0", + "@opentelemetry/sdk-trace-base": "^1.22.0", + "@opentelemetry/sdk-trace-node": "^1.22.0", + "@opentelemetry/semantic-conventions": "^1.22.0", + "diagnostic-channel": "1.1.1", + "diagnostic-channel-publishers": "1.0.8" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "engines": { + "node": ">=8" + } + }, + "node_modules/diagnostic-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/diagnostic-channel/-/diagnostic-channel-1.1.1.tgz", + "integrity": "sha512-r2HV5qFkUICyoaKlBEpLKHjxMXATUf/l+h8UZPGBHGLy4DDiY2sOLcIctax4eRnTw5wH2jTMExLntGPJ8eOJxw==", + "dependencies": { + "semver": "^7.5.3" + } + }, + "node_modules/diagnostic-channel-publishers": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.8.tgz", + "integrity": "sha512-HmSm9hXxSPxA9BaLGY98QU1zsdjeCk113KjAYGPCen1ZP6mhVaTPzHd6UYv5r21DnWANi+f+NyPOHruGT9jpqQ==", + "peerDependencies": { + "diagnostic-channel": "*" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flat-cache/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/flat-cache/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-in-the-middle": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.7.1.tgz", + "integrity": "sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==", + "dependencies": { + "acorn": "^8.8.2", + "acorn-import-assertions": "^1.9.0", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "node_modules/lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-scurry": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", + "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/protobufjs": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.6.tgz", + "integrity": "sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/protobufjs/node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.3.0.tgz", + "integrity": "sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw==", + "dependencies": { + "debug": "^4.1.1", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", + "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", + "dev": true, + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/stoppable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", + "engines": { + "node": ">=4", + "npm": ">=6" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/test/performanceTests/package.json b/test/performanceTests/package.json new file mode 100644 index 000000000..4e071df83 --- /dev/null +++ b/test/performanceTests/package.json @@ -0,0 +1,49 @@ +{ + "name": "@azure-tests/perf-monitor-opentelemetry", + "sdk-type": "perf-test", + "version": "1.0.0", + "description": "", + "main": "", + "type": "module", + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@azure-tools/test-perf": "^1.0.0", + "@opentelemetry/api": "^1.8.0", + "@opentelemetry/api-logs": "^0.49.1", + "@opentelemetry/sdk-logs": "^0.49.1", + "applicationinsights": "^3.0.0-beta.12", + "dotenv": "^16.4.5", + "tslib": "^2.6.2" + }, + "devDependencies": { + "@types/node": "^18.0.0", + "eslint": "^8.0.0", + "rimraf": "^5.0.5", + "typescript": "~5.3.3" + }, + "private": true, + "scripts": { + "perf-test:node": "npm run build && node dist-esm/index.spec.js", + "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", + "build": "npm run clean && tsc -p .", + "build:samples": "echo skipped", + "build:test": "echo skipped", + "check-format": "dev-tool run vendored prettier --list-different --config ../../../../.prettierrc.json --ignore-path ../../../../.prettierignore \"test/**/*.ts\" \"*.{js,json}\"", + "clean": "rimraf --glob dist dist-* types *.tgz *.log", + "format": "dev-tool run vendored prettier --write --config ../../../../.prettierrc.json --ignore-path ../../../../.prettierignore \"test/**/*.ts\" \"*.{js,json}\"", + "integration-test:browser": "echo skipped", + "integration-test:node": "echo skipped", + "integration-test": "echo skipped", + "lint:fix": "eslint --no-eslintrc -c ../../../.eslintrc.internal.json package.json test --ext .ts --fix --fix-type [problem,suggestion]", + "lint": "eslint --no-eslintrc -c ../../../.eslintrc.internal.json package.json test --ext .ts", + "pack": "npm pack 2>&1", + "unit-test:browser": "echo skipped", + "unit-test:node": "echo skipped", + "unit-test": "echo skipped", + "test:browser": "echo skipped", + "test:node": "echo skipped", + "test": "echo skipped" + } +} diff --git a/test/performanceTests/sample.env b/test/performanceTests/sample.env new file mode 100644 index 000000000..f013481e7 --- /dev/null +++ b/test/performanceTests/sample.env @@ -0,0 +1 @@ +APPLICATIONINSIGHTS_CONNECTION_STRING= diff --git a/test/performanceTests/test/appInsightsShim.spec.ts b/test/performanceTests/test/appInsightsShim.spec.ts new file mode 100644 index 000000000..886ddc9c7 --- /dev/null +++ b/test/performanceTests/test/appInsightsShim.spec.ts @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { PerfTest } from "@azure-tools/test-perf"; +import appInsights from "applicationinsights"; +import dotenv from "dotenv"; +dotenv.config(); + +export abstract class ShimTest extends PerfTest { + constructor() { + super(); + appInsights + .setup(process.env.APPLICATIONINSIGHTS_CONNECTION_STRING || "") + .start(); + } +} diff --git a/test/performanceTests/test/index.spec.ts b/test/performanceTests/test/index.spec.ts new file mode 100644 index 000000000..63856e118 --- /dev/null +++ b/test/performanceTests/test/index.spec.ts @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { createPerfProgram } from "@azure-tools/test-perf"; +import { TrackDependencyTest } from "./trackDependency.spec.js"; +import { TrackTraceTest } from "./trackTrace.spec.js"; + +const perfProgram = createPerfProgram(TrackDependencyTest, TrackTraceTest); +perfProgram.run(); diff --git a/test/performanceTests/test/trackDependency.spec.ts b/test/performanceTests/test/trackDependency.spec.ts new file mode 100644 index 000000000..c47d089eb --- /dev/null +++ b/test/performanceTests/test/trackDependency.spec.ts @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { PerfOptionDictionary } from "@azure-tools/test-perf"; +import { ShimTest } from "./appInsightsShim.spec.js"; +import appInsights from "applicationinsights"; + +type ShimTestOptions = Record; + +export class TrackDependencyTest extends ShimTest { + public options: PerfOptionDictionary = {}; + constructor() { + super(); + } + + async run(): Promise { + try { + appInsights.defaultClient.trackDependency({ + target:"http://dbname", + name:"select customers proc", + data:"SELECT * FROM Customers", + duration:231, + resultCode:0, + success: true, + dependencyTypeName: "ZSQL" + }); + } catch (error) { + console.error("Error running track dependency perf test:", error); + process.exit(1); + } + } +} \ No newline at end of file diff --git a/test/performanceTests/test/trackTrace.spec.ts b/test/performanceTests/test/trackTrace.spec.ts new file mode 100644 index 000000000..6b1da1669 --- /dev/null +++ b/test/performanceTests/test/trackTrace.spec.ts @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { PerfOptionDictionary } from "@azure-tools/test-perf"; +import { ShimTest } from "./appInsightsShim.spec.js"; +import appInsights from "applicationinsights"; + +type ShimTestOptions = Record; + +export class TrackTraceTest extends ShimTest { + public options: PerfOptionDictionary = {}; + constructor() { + super(); + } + + async run(): Promise { + try { + appInsights.defaultClient.trackTrace({ message: "trace message" }); + } catch (error) { + console.error("Error running track trace perf test:", error); + process.exit(1); + } + } +} \ No newline at end of file diff --git a/test/performanceTests/tsconfig.json b/test/performanceTests/tsconfig.json new file mode 100644 index 000000000..3fc93dc39 --- /dev/null +++ b/test/performanceTests/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "NodeNext", + "moduleResolution": "NodeNext", + "outDir": "./dist-esm", + "declaration": true, + "allowSyntheticDefaultImports": true + }, + "include": [ + "test/**/*.ts" + ] +} diff --git a/test/unitTests/agent/agentLoader.tests.ts b/test/unitTests/agent/agentLoader.tests.ts new file mode 100644 index 000000000..08c83efc5 --- /dev/null +++ b/test/unitTests/agent/agentLoader.tests.ts @@ -0,0 +1,181 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as assert from "assert"; +import * as sinon from "sinon"; + +import { AgentLoader } from "../../../src/agent/agentLoader"; +import * as azureMonitor from "@azure/monitor-opentelemetry"; +import { DiagnosticMessageId } from "../../../src/agent/types"; + +describe("agent/agentLoader", () => { + let originalEnv: NodeJS.ProcessEnv; + let sandbox: sinon.SinonSandbox; + + const defaultConfig = { + azureMonitorExporterOptions: { + disableOfflineStorage: false, + }, + enableAutoCollectExceptions: true, + enableAutoCollectPerformance: true, + samplingRatio: 1, // Sample all telemetry by default + instrumentationOptions: { + azureSdk: { + enabled: true + }, + http: { + enabled: true + }, + mongoDb: { + enabled: true + }, + mySql: { + enabled: true + }, + postgreSql: { + enabled: true + }, + redis4: { + enabled: true + }, + redis: { + enabled: true + }, + } + }; + + before(() => { + sandbox = sinon.createSandbox(); + }); + + beforeEach(() => { + originalEnv = process.env; + }); + + afterEach(() => { + process.env = originalEnv; + sandbox.restore(); + }); + + it("should initialize constructor", () => { + const env = { + ["APPLICATIONINSIGHTS_CONNECTION_STRING"]: "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333", + }; + process.env = env; + const agent = new AgentLoader(); + const diagnosticLogger: any = agent["_diagnosticLogger"]; + + assert.strictEqual(agent["_canLoad"], true); + assert.deepStrictEqual(agent["_options"], defaultConfig); + assert.strictEqual(diagnosticLogger["_instrumentationKey"], "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); + }); + + it("should get blank instrumentation key if not defined", () => { + const env = { + ["APPLICATIONINSIGHTS_CONNECTION_STRING"]: "", + }; + process.env = env; + const agent = new AgentLoader(); + + assert(agent["_instrumentationKey"], ""); + }); + + it("should initialize when called", () => { + const env = { + ["APPLICATIONINSIGHTS_CONNECTION_STRING"]: "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333", + }; + process.env = env; + const agent = new AgentLoader(); + const diagnosticLoggerStub = sandbox.stub(agent["_diagnosticLogger"], "logMessage"); + + const initAgent = agent.initialize(); + assert.equal(process.env["AZURE_MONITOR_AUTO_ATTACH"], "true"); + assert.ok(diagnosticLoggerStub.calledOnce); + }); + + it("should send a message to the console if initialize is called when canLoad is false", () => { + const env = { + ["APPLICATIONINSIGHTS_CONNECTION_STRING"]: "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333", + }; + process.env = env; + const agent = new AgentLoader(); + const consoleLoggerStub = sandbox.stub(console, "log"); + agent["_canLoad"] = false; + + agent.initialize(); + assert.ok(consoleLoggerStub.calledOnce); + }); + + it("should call both the diagnostic and status loggers if the connection string is not defined", () => { + const env = { + ["APPLICATIONINSIGHTS_CONNECTION_STRING"]: "", + }; + process.env = env; + const agent = new AgentLoader(); + const diagnosticLoggerStub = sandbox.stub(agent["_diagnosticLogger"], "logMessage"); + const statusLoggerStub = sandbox.stub(agent["_statusLogger"], "logStatus"); + + agent["_instrumentationKey"] = "unknown"; + const validationResult: boolean = agent["_validate"](); + + assert.ok(diagnosticLoggerStub.calledOnce); + assert.ok(statusLoggerStub.calledOnce); + assert.ok(!validationResult); + }); + + it("should set logger", () => { + const env = { + ["APPLICATIONINSIGHTS_CONNECTION_STRING"]: "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333", + }; + process.env = env; + const agent = new AgentLoader(); + const logger = { + logMessage: () => { } + }; + agent.setLogger(logger); + assert.deepStrictEqual(agent["_diagnosticLogger"], logger); + }); + + it("should handle initialization errors", () => { + const env = { + ["APPLICATIONINSIGHTS_CONNECTION_STRING"]: "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333", + }; + process.env = env; + const agent = new AgentLoader(); + const azureMonitorStub = sandbox.stub(azureMonitor, "useAzureMonitor").throws(new Error("test")); + const diagnosticLoggerStub = sandbox.stub(agent["_diagnosticLogger"], "logMessage"); + const statusLoggerStub = sandbox.stub(agent["_statusLogger"], "logStatus"); + agent.initialize(); + assert.ok(azureMonitorStub.calledOnce); + assert.ok(diagnosticLoggerStub.calledOnce); + assert.ok(statusLoggerStub.calledOnce); + assert.deepEqual(statusLoggerStub.args[0][0].AgentInitializedSuccessfully, false); + assert.deepEqual(diagnosticLoggerStub.args[0][0].messageId, DiagnosticMessageId.unknownError); + }); + + it("should handle validation errors", () => { + const env = { + ["APPLICATIONINSIGHTS_CONNECTION_STRING"]: "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333", + }; + process.env = env; + const agent = new AgentLoader(); + agent["_instrumentationKey"] = "unknown"; + agent["_sdkAlreadyExists"] = () => { throw new Error("test"); return true; }; + const statusLoggerStub = sandbox.stub(agent["_statusLogger"], "logStatus"); + agent["_validate"](); + assert.deepEqual(statusLoggerStub.args[0][0].AgentInitializedSuccessfully, false); + }); + + it("should handle sdkAlreadyExits", () => { + const env = { + ["APPLICATIONINSIGHTS_CONNECTION_STRING"]: "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333", + ["APPLICATIONINSIGHTS_FORCE_START"]: "false" + }; + process.env = env; + const agent = new AgentLoader(); + agent["_sdkAlreadyExists"] = () => true; + const statusLoggerStub = sandbox.stub(agent["_statusLogger"], "logStatus"); + agent["_validate"](); + assert.deepEqual(statusLoggerStub.args[0][0].AgentInitializedSuccessfully, false); + }); +}); diff --git a/test/unitTests/agent/aksLoader.tests.ts b/test/unitTests/agent/aksLoader.tests.ts new file mode 100644 index 000000000..266943cbb --- /dev/null +++ b/test/unitTests/agent/aksLoader.tests.ts @@ -0,0 +1,77 @@ +import * as assert from "assert"; +import * as sinon from "sinon"; +import { ProxyTracerProvider, metrics, trace } from "@opentelemetry/api"; +import { logs } from "@opentelemetry/api-logs"; + +import { AKSLoader } from "../../../src/agent/aksLoader"; +import { DiagnosticLogger } from "../../../src/agent/diagnostics/diagnosticLogger"; +import { FileWriter } from "../../../src/agent/diagnostics/writers/fileWriter"; + +describe("agent/AKSLoader", () => { + let originalEnv: NodeJS.ProcessEnv; + let sandbox: sinon.SinonSandbox; + + before(() => { + sandbox = sinon.createSandbox(); + }); + + beforeEach(() => { + originalEnv = process.env; + }); + + afterEach(() => { + process.env = originalEnv; + sandbox.restore(); + }); + + it("constructor", () => { + const env = { + ["APPLICATIONINSIGHTS_CONNECTION_STRING"]: "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333", + }; + process.env = env; + const agent = new AKSLoader(); + let diagnosticLogger: any = agent["_diagnosticLogger"]; + assert.equal(diagnosticLogger["_instrumentationKey"], "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); + assert.ok(diagnosticLogger instanceof DiagnosticLogger); + assert.ok(diagnosticLogger["_agentLogger"] instanceof FileWriter); + let statusLogger: any = agent["_statusLogger"]; + assert.equal(statusLogger["_instrumentationKey"], "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); + assert.ok(statusLogger["_agentLogger"] instanceof FileWriter); + // Loader is using correct diagnostics + assert.equal(agent["_diagnosticLogger"], diagnosticLogger, "Wrong diagnosticLogger"); + assert.equal(agent["_statusLogger"], statusLogger, "Wrong statusLogger"); + }); + + it("initialize", () => { + const env = { + ["APPLICATIONINSIGHTS_CONNECTION_STRING"]: "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333", + }; + process.env = env; + const agent = new AKSLoader(); + agent.initialize(); + let meterProvider = metrics.getMeterProvider() as any; + assert.equal(meterProvider.constructor.name, "MeterProvider"); + assert.equal(meterProvider["_sharedState"]["metricCollectors"].length, 1); + assert.equal(meterProvider["_sharedState"]["metricCollectors"][0]["_metricReader"]["_exporter"].constructor.name, "AzureMonitorMetricExporter"); + let tracerProvider = ((trace.getTracerProvider() as ProxyTracerProvider).getDelegate()) as any; + assert.equal(tracerProvider.constructor.name, "NodeTracerProvider"); + assert.equal(tracerProvider["_registeredSpanProcessors"][1]["_exporter"].constructor.name, "AzureMonitorTraceExporter"); + let loggerProvider = logs.getLoggerProvider() as any; + assert.equal(loggerProvider.constructor.name, "LoggerProvider"); + assert.equal(loggerProvider["_sharedState"]["registeredLogRecordProcessors"][1]["_exporter"].constructor.name, "AzureMonitorLogExporter"); + }); + + it("should add OTLP exporter if env variable is present", () => { + const env = { + ["APPLICATIONINSIGHTS_CONNECTION_STRING"]: "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333", + ["OTEL_EXPORTER_OTLP_METRICS_ENDPOINT"]: "something", + }; + process.env = env; + const agent = new AKSLoader(); + agent.initialize(); + let meterProvider = metrics.getMeterProvider() as any; + assert.equal(meterProvider.constructor.name, "MeterProvider"); + assert.equal(meterProvider["_sharedState"]["metricCollectors"].length, 2); + assert.equal(meterProvider["_sharedState"]["metricCollectors"][1]["_metricReader"]["_exporter"].constructor.name, "OTLPMetricExporter"); + }); +}); diff --git a/test/unitTests/agent/appServicesLoader.ts b/test/unitTests/agent/appServicesLoader.ts new file mode 100644 index 000000000..95c577bf0 --- /dev/null +++ b/test/unitTests/agent/appServicesLoader.ts @@ -0,0 +1,89 @@ +import * as assert from "assert"; +import * as sinon from "sinon"; + +import { AppServicesLoader } from "../../../src/agent/appServicesLoader"; +import { DiagnosticLogger } from "../../../src/agent/diagnostics/diagnosticLogger"; +import { FileWriter } from "../../../src/agent/diagnostics/writers/fileWriter"; +import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions"; + +describe("agent/AppServicesLoader", () => { + let originalEnv: NodeJS.ProcessEnv; + let sandbox: sinon.SinonSandbox; + + before(() => { + sandbox = sinon.createSandbox(); + }); + + beforeEach(() => { + originalEnv = process.env; + }); + + afterEach(() => { + process.env = originalEnv; + sandbox.restore(); + }); + + it("constructor", () => { + const env = { + ["APPLICATIONINSIGHTS_CONNECTION_STRING"]: "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333", + ["HOME"]: "c:", + }; + process.env = env; + const agent = new AppServicesLoader(); + let diagnosticLogger: any = agent["_diagnosticLogger"]; + assert.equal(diagnosticLogger["_instrumentationKey"], "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); + + const isWindows = process.platform === 'win32'; + assert.ok(diagnosticLogger instanceof DiagnosticLogger, "Wrong diagnosticLogger type"); + assert.ok(diagnosticLogger["_agentLogger"] instanceof FileWriter, "Wrong diagnosticLogger agentLogger"); + assert.equal(diagnosticLogger["_agentLogger"]["_filename"], "applicationinsights-extension.log"); + + let statusLogger: any = agent["_statusLogger"]; + assert.equal(statusLogger["_instrumentationKey"], "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); + assert.ok(statusLogger["_agentLogger"] instanceof FileWriter, "Wrong statusLogger agentLogger"); + assert.equal(statusLogger["_agentLogger"]["_filename"], "status_nodejs.json"); + + if (isWindows) { + assert.equal(diagnosticLogger["_agentLogger"]["_filepath"], "c:\\LogFiles\\ApplicationInsights\\status"); + assert.equal(statusLogger["_agentLogger"]["_filepath"], "c:\\LogFiles\\ApplicationInsights\\status"); + } + else { + assert.equal(diagnosticLogger["_agentLogger"]["_filepath"], "/var/log/applicationinsights/"); + assert.equal(statusLogger["_agentLogger"]["_filepath"], "/var/log/applicationinsights/"); + } + // Loader is using correct diagnostics + assert.equal(agent["_diagnosticLogger"], diagnosticLogger, "Wrong diagnosticLogger"); + assert.equal(agent["_statusLogger"], statusLogger, "Wrong statusLogger"); + + }); + + it("initialize", () => { + const agent = new AppServicesLoader(); + let stub = sandbox.stub(agent, "initialize"); + agent.initialize(); + // Agent Loader called + assert.ok(stub.calledOnce); + }); + + it("should correctly set Azure Resource Attributes", () => { + const env = <{ [id: string]: string }>{}; + const originalEnv = process.env; + env.WEBSITE_SITE_NAME = "testRole"; + env.WEBSITE_INSTANCE_ID = "testRoleInstanceId"; + process.env = env; + const agent = new AppServicesLoader(); + let stub = sandbox.stub(agent, "initialize"); + agent.initialize(); + process.env = originalEnv; + // Agent Loader called + assert.ok(stub.calledOnce); + assert.equal( + agent["_options"].resource.attributes[SemanticResourceAttributes.SERVICE_INSTANCE_ID], + "testRoleInstanceId" + ); + assert.equal( + agent["_options"].resource.attributes[SemanticResourceAttributes.SERVICE_NAME], + "testRole" + ); + }); +}); diff --git a/test/unitTests/agent/azureFunctionsLoader.ts b/test/unitTests/agent/azureFunctionsLoader.ts new file mode 100644 index 000000000..c1d3f80a9 --- /dev/null +++ b/test/unitTests/agent/azureFunctionsLoader.ts @@ -0,0 +1,79 @@ +import * as assert from "assert"; +import * as sinon from "sinon"; + +import { AzureFunctionsLoader } from "../../../src/agent/azureFunctionsLoader"; +import { DiagnosticLogger } from "../../../src/agent/diagnostics/diagnosticLogger"; +import { AzureFunctionsWriter } from "../../../src/agent/diagnostics/writers/azureFunctionsWriter"; +import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions"; + +describe("agent/AzureFunctionsLoader", () => { + let originalEnv: NodeJS.ProcessEnv; + let sandbox: sinon.SinonSandbox; + + before(() => { + sandbox = sinon.createSandbox(); + }); + + beforeEach(() => { + originalEnv = process.env; + }); + + afterEach(() => { + process.env = originalEnv; + sandbox.restore(); + }); + + it("constructor", () => { + const env = { + ["APPLICATIONINSIGHTS_CONNECTION_STRING"]: + "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333", + }; + process.env = env; + const agent = new AzureFunctionsLoader(); + let diagnosticLogger: any = agent["_diagnosticLogger"]; + assert.equal( + diagnosticLogger["_instrumentationKey"], + "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333" + ); + assert.ok(diagnosticLogger instanceof DiagnosticLogger); + assert.ok(diagnosticLogger["_agentLogger"] instanceof AzureFunctionsWriter); + let statusLogger: any = agent["_statusLogger"]; + assert.equal(statusLogger["_instrumentationKey"], "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); + assert.ok(statusLogger["_agentLogger"] instanceof AzureFunctionsWriter); + // Loader is using correct diagnostics + assert.equal(agent["_diagnosticLogger"], diagnosticLogger); + assert.equal(agent["_statusLogger"], statusLogger); + }); + + it("initialize", () => { + const agent = new AzureFunctionsLoader(); + let stub = sandbox.stub(agent, "initialize"); + agent.initialize(); + // Agent Loader called + assert.ok(stub.calledOnce); + // Custom config + assert.equal(agent["_options"].enableAutoCollectPerformance, false); + }); + + it("should correctly set Azure Resource Attributes", () => { + const env = <{ [id: string]: string }>{}; + const originalEnv = process.env; + env.WEBSITE_SITE_NAME = "testRole"; + env.WEBSITE_INSTANCE_ID = "testRoleInstanceId"; + process.env = env; + const agent = new AzureFunctionsLoader(); + let stub = sandbox.stub(agent, "initialize"); + agent.initialize(); + process.env = originalEnv; + // Agent Loader called + assert.ok(stub.calledOnce); + assert.equal( + agent["_options"].resource.attributes[SemanticResourceAttributes.SERVICE_INSTANCE_ID], + "testRoleInstanceId" + ); + assert.equal( + agent["_options"].resource.attributes[SemanticResourceAttributes.SERVICE_NAME], + "testRole" + ); + }); +}); diff --git a/test/unitTests/agent/diagnostics/diagnosticLogger.tests.ts b/test/unitTests/agent/diagnostics/diagnosticLogger.tests.ts new file mode 100644 index 000000000..659b4a653 --- /dev/null +++ b/test/unitTests/agent/diagnostics/diagnosticLogger.tests.ts @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as assert from "assert"; +import * as sinon from "sinon"; +import { DiagnosticLogger } from "../../../../src/agent/diagnostics/diagnosticLogger"; + +describe("agent/diagnostics/diagnosticLogger", () => { + let sandbox: sinon.SinonSandbox; + before(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + it("should console log by default", () => { + const logger = new DiagnosticLogger("InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); + const consoleStub = sandbox.stub(console, "log"); + logger.logMessage({ message: "test" }); + assert(consoleStub.calledOnce); + }); +}); diff --git a/test/unitTests/agent/diagnostics/writers/azureFunctionsWriter.tests.ts b/test/unitTests/agent/diagnostics/writers/azureFunctionsWriter.tests.ts new file mode 100644 index 000000000..51e2eeb1f --- /dev/null +++ b/test/unitTests/agent/diagnostics/writers/azureFunctionsWriter.tests.ts @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as assert from "assert"; +import * as sinon from "sinon"; +import { AzureFunctionsWriter } from "../../../../../src/agent/diagnostics/writers/azureFunctionsWriter"; + +describe("agent//diagnostics/writers/fileWriter", () => { + let sandbox: sinon.SinonSandbox; + before(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it("should log", () => { + const writer = new AzureFunctionsWriter("testIkey"); + const consoleInfoStub = sandbox.stub(console, "info"); + writer.log("test"); + assert.ok(consoleInfoStub.calledOnce); + }); + + it("should error", () => { + const writer = new AzureFunctionsWriter("testIkey"); + const consoleErrorStub = sandbox.stub(console, "error"); + writer.error("test error"); + assert.ok(consoleErrorStub.calledOnce); + }); +}); diff --git a/test/unitTests/agent/diagnostics/writers/fileWriter.tests.ts b/test/unitTests/agent/diagnostics/writers/fileWriter.tests.ts new file mode 100644 index 000000000..1f4763bf0 --- /dev/null +++ b/test/unitTests/agent/diagnostics/writers/fileWriter.tests.ts @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as assert from "assert"; +import * as sinon from "sinon"; +import { FileWriter } from "../../../../../src/agent/diagnostics/writers/fileWriter"; +import * as fs from "fs"; + +describe("agent//diagnostics/writers/fileWriter", () => { + let sandbox: sinon.SinonSandbox; + before(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + it("should call renameCurrentFile if renamePolicy is set to rolling", () => { + const writer = new FileWriter("test", "test", { append: true, renamePolicy: "rolling" }); + const shouldRenameStub = sandbox.stub(writer, "_shouldRenameFile" as any); + writer.log("test"); + assert.ok(shouldRenameStub.calledOnce); + }); + + it("should append file", () => { + const appendStub = sandbox.stub(fs, "appendFile"); + const writer = new FileWriter("test", "test", { append: true }); + writer["_appendFile"]("test"); + assert.ok(appendStub.calledOnce); + }); + + it("should write file", () => { + const writeStub = sandbox.stub(fs, "writeFile"); + const writer = new FileWriter("test", "test", { append: false }); + writer["_writeFile"]("test"); + assert.ok(writeStub.calledOnce); + }); +}); diff --git a/test/unitTests/logs/api.tests.ts b/test/unitTests/logs/api.tests.ts new file mode 100644 index 000000000..7a3dbb9eb --- /dev/null +++ b/test/unitTests/logs/api.tests.ts @@ -0,0 +1,192 @@ +import * as assert from "assert"; +import * as sinon from "sinon"; +import * as nock from "nock"; +import { Logger } from "@opentelemetry/api-logs"; +import { LogRecord } from "@opentelemetry/sdk-logs"; + +import { + AvailabilityTelemetry, + EventTelemetry, + ExceptionTelemetry, + PageViewTelemetry, + Telemetry, + TraceTelemetry +} from "../../../src/declarations/contracts"; +import { AvailabilityData, MessageData, MonitorDomain, PageViewData, TelemetryEventData, TelemetryExceptionData } from "../../../src/declarations/generated"; +import { LogApi } from "../../../src/shim/logsApi"; + +describe("logs/API", () => { + let sandbox: sinon.SinonSandbox; + + before(() => { + sandbox = sinon.createSandbox(); + nock("https://dc.services.visualstudio.com") + .post("/v2.1/track", (body: string) => true) + .reply(200, {}) + .persist(); + nock.disableNetConnect(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + after(() => { + nock.cleanAll(); + nock.enableNetConnect(); + }); + + class TestLogger implements Logger { + + public logsEmited: Array = []; + + emit(logRecord: LogRecord): void { + this.logsEmited.push(logRecord); + } + } + + describe("#manual track APIs", () => { + it("_logToEnvelope", () => { + let testLogger = new TestLogger(); + let logApi = new LogApi(testLogger); + const telemetry: Telemetry = { + properties: { "testAttribute": "testValue" } + }; + const data: MonitorDomain = {}; + const logRecord = logApi["_telemetryToLogRecord"]( + telemetry, + "TestData", + data, + ) as LogRecord; + assert.equal(logRecord.body, "{}"); + assert.equal(logRecord.attributes["testAttribute"], "testValue"); + assert.equal(logRecord.attributes["_MS.baseType"], "TestData"); + }); + + it("should serialize errors", () => { + let testLogger = new TestLogger(); + let logApi = new LogApi(testLogger); + let error = new Error("test error"); + const telemetry: Telemetry = { + properties: { "testAttribute": "testValue", "error": error } + }; + const data: MonitorDomain = {}; + const logRecord = logApi["_telemetryToLogRecord"]( + telemetry, + "TestData", + data, + ) as LogRecord; + assert.equal(logRecord.body, "{}"); + assert.equal(logRecord.attributes["testAttribute"], "testValue"); + const errorStr: string = logRecord.attributes["error"] as string; + assert.ok(errorStr.includes("test error")); + assert.equal(logRecord.attributes["_MS.baseType"], "TestData"); + }); + + it("trackAvailability", () => { + let testLogger = new TestLogger(); + let logApi = new LogApi(testLogger); + const telemetry: AvailabilityTelemetry = { + name: "TestName", + duration: 2000, //2 seconds + id: "testId", + runLocation: "testRunLocation", + message: "testMessage", + success: false, + }; + logApi.trackAvailability(telemetry); + const logs = testLogger.logsEmited; + assert.equal(logs.length, 1); + let baseData = JSON.parse(logs[0].body) as AvailabilityData; + assert.equal(baseData.version, 2); + assert.equal(baseData.id, "testId"); + assert.equal(baseData.name, "TestName"); + assert.equal(baseData.duration, "00:00:02.000"); + assert.equal(baseData.success, false); + assert.equal(baseData.runLocation, "testRunLocation"); + assert.equal(baseData.message, "testMessage"); + assert.equal(logs[0].attributes["_MS.baseType"], "AvailabilityData"); + }); + + it("trackPageView", () => { + let testLogger = new TestLogger(); + let logApi = new LogApi(testLogger); + const telemetry: PageViewTelemetry = { + name: "TestName", + duration: 2000, //2 seconds + id: "testId", + referredUri: "testReferredUri", + url: "testUrl", + }; + logApi.trackPageView(telemetry); + const logs = testLogger.logsEmited; + assert.equal(logs.length, 1); + let baseData = JSON.parse(logs[0].body) as PageViewData; + assert.equal(baseData.version, 2); + assert.equal(baseData.id, "testId"); + assert.equal(baseData.name, "TestName"); + assert.equal(baseData.duration, "00:00:02.000"); + assert.equal(baseData.referredUri, "testReferredUri"); + assert.equal(baseData.url, "testUrl"); + assert.equal(logs[0].attributes["_MS.baseType"], "PageViewData"); + }); + + it("trackTrace", () => { + let testLogger = new TestLogger(); + let logApi = new LogApi(testLogger); + const telemetry: TraceTelemetry = { + message: "testMessage", + severity: "Information", + }; + logApi.trackTrace(telemetry); + const logs = testLogger.logsEmited; + assert.equal(logs.length, 1); + let baseData = JSON.parse(logs[0].body) as MessageData; + assert.equal(baseData.version, 2); + assert.equal(baseData.message, "testMessage"); + assert.equal(baseData.severityLevel, "Information"); + assert.equal(logs[0].attributes["_MS.baseType"], "MessageData"); + }); + + it("trackException", () => { + let testLogger = new TestLogger(); + let logApi = new LogApi(testLogger); + const measurements: { [key: string]: number } = {}; + measurements["test"] = 123; + const telemetry: ExceptionTelemetry = { + exception: new Error("TestError"), + severity: "Critical", + measurements: measurements, + }; + logApi.trackException(telemetry); + const logs = testLogger.logsEmited; + assert.equal(logs.length, 1); + let baseData = JSON.parse(logs[0].body) as TelemetryExceptionData; + assert.equal(baseData.version, 2); + assert.equal(baseData.severityLevel, "Critical"); + assert.equal(baseData.exceptions[0].message, "TestError"); + assert.equal(baseData.exceptions[0].typeName, "Error"); + assert.equal(baseData.measurements["test"], 123); + assert.equal(logs[0].attributes["_MS.baseType"], "ExceptionData"); + }); + + it("trackEvent", () => { + let testLogger = new TestLogger(); + let logApi = new LogApi(testLogger); + const measurements: { [key: string]: number } = {}; + measurements["test"] = 123; + const telemetry: EventTelemetry = { + name: "TestName", + measurements: measurements, + }; + logApi.trackEvent(telemetry); + const logs = testLogger.logsEmited; + assert.equal(logs.length, 1); + let baseData = JSON.parse(logs[0].body) as TelemetryEventData; + assert.equal(baseData.version, 2); + assert.equal(baseData.name, "TestName"); + assert.equal(baseData.measurements["test"], 123); + assert.equal(logs[0].attributes["_MS.baseType"], "EventData"); + }); + }); +}); diff --git a/test/unitTests/logs/console.tests.ts b/test/unitTests/logs/console.tests.ts new file mode 100644 index 000000000..8801524dc --- /dev/null +++ b/test/unitTests/logs/console.tests.ts @@ -0,0 +1,84 @@ +import * as assert from "assert"; +import { channel } from "diagnostic-channel"; +import { console } from "diagnostic-channel-publishers"; +import { SeverityNumber, logs } from '@opentelemetry/api-logs'; +import { + LoggerProvider, + SimpleLogRecordProcessor, + InMemoryLogRecordExporter, +} from '@opentelemetry/sdk-logs'; + +import { dispose } from "../../../src/logs/diagnostic-channel/console.sub"; +import { AutoCollectLogs } from "../../../src/logs/autoCollectLogs"; + + +describe("AutoCollection/Console", () => { + let memoryLogExporter: InMemoryLogRecordExporter; + + before(() => { + logs.disable(); + const loggerProvider = new LoggerProvider(); + memoryLogExporter = new InMemoryLogRecordExporter(); + loggerProvider.addLogRecordProcessor( + new SimpleLogRecordProcessor(memoryLogExporter) + ); + logs.setGlobalLoggerProvider(loggerProvider); + }); + + beforeEach(() => { + memoryLogExporter.getFinishedLogRecords().length = 0; // clear + }); + + afterEach(() => { + dispose(); + }); + + describe("#log and #error()", () => { + it("should log event for errors", () => { + let autoCollect = new AutoCollectLogs(); + autoCollect.enable({ + console: { enabled: true } + }); + const dummyError = new Error("test error"); + const errorEvent: console.IConsoleData = { + message: dummyError as any, + stderr: false, + }; + channel.publish("console", errorEvent); + const logRecords = memoryLogExporter.getFinishedLogRecords(); + assert.strictEqual(logRecords.length, 1); + assert.strictEqual(logRecords[0].body, "Error: test error"); + assert.strictEqual(logRecords[0].severityNumber, SeverityNumber.ERROR); + }); + + it("should log event for logs", () => { + let autoCollect = new AutoCollectLogs(); + autoCollect.enable({ + console: { enabled: true } + }); + const logEvent: console.IConsoleData = { + message: "test log", + stderr: true, + }; + channel.publish("console", logEvent); + const logRecords = memoryLogExporter.getFinishedLogRecords(); + assert.strictEqual(logRecords.length, 1); + assert.strictEqual(logRecords[0].body, "test log"); + assert.strictEqual(logRecords[0].severityNumber, SeverityNumber.WARN); + }); + + it("severityLevel", () => { + let autoCollect = new AutoCollectLogs(); + autoCollect.enable({ + console: { enabled: true, logSendingLevel: SeverityNumber.ERROR } + }); + const logEvent: console.IConsoleData = { + message: "test log", + stderr: true, + }; + channel.publish("console", logEvent); + const logRecords = memoryLogExporter.getFinishedLogRecords(); + assert.strictEqual(logRecords.length, 0); + }); + }); +}); diff --git a/test/unitTests/logs/exceptions.tests.ts b/test/unitTests/logs/exceptions.tests.ts new file mode 100644 index 000000000..3973fe46a --- /dev/null +++ b/test/unitTests/logs/exceptions.tests.ts @@ -0,0 +1,41 @@ +import * as assert from "assert"; +import * as sinon from "sinon"; + +import { AutoCollectExceptions } from "../../../src/logs/exceptions"; + +describe("AutoCollection/Exceptions", () => { + let sandbox: sinon.SinonSandbox; + + before(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it("enable auto collection", () => { + const processOnSpy = sandbox.spy(global.process, "on"); + new AutoCollectExceptions(null); + assert.equal( + processOnSpy.callCount, + 2, + "After enabling exception auto collection, there should be 2 calls to processOnSpy" + ); + assert.equal(processOnSpy.getCall(0).args[0], "uncaughtException"); + assert.equal(processOnSpy.getCall(1).args[0], "unhandledRejection"); + }); + + it("disables auto collection", () => { + const processRemoveListenerSpy = sandbox.spy(global.process, "removeListener"); + const exceptions = new AutoCollectExceptions(null); + exceptions.shutdown(); + assert.equal( + processRemoveListenerSpy.callCount, + 2, + "After enabling exception auto collection, there should be 2 calls to processOnSpy" + ); + assert.equal(processRemoveListenerSpy.getCall(0).args[0], "uncaughtException"); + assert.equal(processRemoveListenerSpy.getCall(1).args[0], "unhandledRejection"); + }); +}); diff --git a/test/unitTests/logs/winston.tests.ts b/test/unitTests/logs/winston.tests.ts new file mode 100644 index 000000000..0f6b7a8e7 --- /dev/null +++ b/test/unitTests/logs/winston.tests.ts @@ -0,0 +1,88 @@ +import * as assert from "assert"; +import { channel } from "diagnostic-channel"; +import { winston } from "diagnostic-channel-publishers"; +import { SeverityNumber, logs } from '@opentelemetry/api-logs'; +import { + LoggerProvider, + SimpleLogRecordProcessor, + InMemoryLogRecordExporter, +} from '@opentelemetry/sdk-logs'; +import { dispose } from "../../../src/logs/diagnostic-channel/winston.sub"; +import { AutoCollectLogs } from "../../../src/logs/autoCollectLogs"; + + +describe("diagnostic-channel/winston", () => { + let memoryLogExporter: InMemoryLogRecordExporter; + + before(() => { + logs.disable(); + const loggerProvider = new LoggerProvider(); + memoryLogExporter = new InMemoryLogRecordExporter(); + loggerProvider.addLogRecordProcessor( + new SimpleLogRecordProcessor(memoryLogExporter) + ); + logs.setGlobalLoggerProvider(loggerProvider); + }); + + beforeEach(() => { + memoryLogExporter.getFinishedLogRecords().length = 0; // clear + }); + + afterEach(() => { + dispose(); + }); + + it("should emit log for errors", () => { + let autoCollect = new AutoCollectLogs(); + autoCollect.enable({ + winston: { enabled: true } + }); + const dummyError = new Error("test error"); + const errorEvent: winston.IWinstonData = { + message: dummyError as any, + meta: {}, + level: "error", + levelKind: "npm", + }; + channel.publish("winston", errorEvent); + const logRecords = memoryLogExporter.getFinishedLogRecords(); + assert.strictEqual(logRecords.length, 1); + assert.strictEqual(logRecords[0].body, "Error: test error"); + assert.strictEqual(logRecords[0].severityNumber, SeverityNumber.ERROR); + }); + + it("should emit log for winston log", () => { + let autoCollect = new AutoCollectLogs(); + autoCollect.enable({ + winston: { enabled: true } + }); + const logEvent: winston.IWinstonData = { + message: "test log", + meta: { "test1": "testValue" }, + level: "debug", + levelKind: "npm", + }; + channel.publish("winston", logEvent); + const logRecords = memoryLogExporter.getFinishedLogRecords(); + assert.strictEqual(logRecords.length, 1); + assert.strictEqual(logRecords[0].body, "test log"); + assert.strictEqual(logRecords[0].severityNumber, SeverityNumber.DEBUG); + assert.strictEqual(logRecords[0].attributes["test1"], "testValue"); + }); + + it("severityLevel", () => { + let autoCollect = new AutoCollectLogs(); + autoCollect.enable({ + console: { enabled: true, logSendingLevel: SeverityNumber.ERROR } + }); + const logEvent: winston.IWinstonData = { + message: "test log", + meta: {}, + level: "debug", + levelKind: "npm", + }; + channel.publish("winston", logEvent); + const logRecords = memoryLogExporter.getFinishedLogRecords(); + assert.strictEqual(logRecords.length, 0); + }); +}); diff --git a/test/unitTests/main.tests.ts b/test/unitTests/main.tests.ts new file mode 100644 index 000000000..cbc89e268 --- /dev/null +++ b/test/unitTests/main.tests.ts @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for details. +import * as assert from "assert"; +import { ProxyTracerProvider, metrics, trace } from "@opentelemetry/api"; +import { logs } from "@opentelemetry/api-logs"; +import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http"; +import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http"; +import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http"; +import { LoggerProvider } from "@opentelemetry/sdk-logs"; +import { shutdownAzureMonitor, useAzureMonitor } from "../../src"; + +describe("ApplicationInsightsClient", () => { + afterEach(() => { + shutdownAzureMonitor(); + }); + + it("OTLP Exporters added", () => { + useAzureMonitor({ + azureMonitorExporterOptions: + { connectionString: "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333" }, + otlpMetricExporterConfig: { enabled: true }, + otlpTraceExporterConfig: { enabled: true }, + otlpLogExporterConfig: { enabled: true } + }); + let meterProvider = metrics.getMeterProvider() as any; + let metricCollectors = meterProvider["_sharedState"]["metricCollectors"]; + assert.ok(metricCollectors.length == 2, "wrong number of metric collectors"); + let otlpExporter = metricCollectors[1]["_metricReader"]["_exporter"]; + assert.ok(otlpExporter instanceof OTLPMetricExporter, "wrong exporter"); + + let tracerProvider = ((trace.getTracerProvider() as ProxyTracerProvider).getDelegate() as any); + let spanProcessors = tracerProvider["_registeredSpanProcessors"]; + assert.ok(spanProcessors.length == 3, "wrong number of spanProcessors"); + otlpExporter = spanProcessors[2]["_exporter"]; + assert.ok(otlpExporter instanceof OTLPTraceExporter, "wrong exporter"); + + let loggerProvider = ((logs.getLoggerProvider() as LoggerProvider) as any); + let logRecordProcessors = loggerProvider["_sharedState"]["registeredLogRecordProcessors"]; + assert.ok(logRecordProcessors.length == 3, "wrong number of logRecordProcessors"); + otlpExporter = logRecordProcessors[2]["_exporter"]; + assert.ok(otlpExporter instanceof OTLPLogExporter, "wrong exporter"); + }); +}); diff --git a/test/unitTests/metrics/nativePerformance.tests.ts b/test/unitTests/metrics/nativePerformance.tests.ts new file mode 100644 index 000000000..1bdeeecaf --- /dev/null +++ b/test/unitTests/metrics/nativePerformance.tests.ts @@ -0,0 +1,44 @@ +// import * as assert from "assert"; +// import * as sinon from "sinon"; +// import { Meter } from "@opentelemetry/api"; +// import { MeterProvider } from "@opentelemetry/sdk-metrics"; +// import { NativePerformanceMetrics } from "../../../src/metrics/collection/nativePerformanceMetrics"; + +// class TestEmitter { +// enable() { } +// disable() { } +// getLoopData() { } +// getGCData() { } +// } + +// describe("AutoCollection/NativePerformance", () => { +// let sandbox: sinon.SinonSandbox; +// let testMeter: Meter; + +// before(() => { +// sandbox = sinon.createSandbox(); +// const testProvider = new MeterProvider(); +// testMeter = testProvider.getMeter("test"); +// }); + +// afterEach(() => { +// sandbox.restore(); +// }); + +// describe("#Metrics", () => { +// it("init should auto collection interval if native metrics packages is installed", () => { +// const nativePerformance = new NativePerformanceMetrics(testMeter); +// nativePerformance["_emitter"] = new TestEmitter(); +// assert.ok(!nativePerformance["_handle"]); // Package is not installed in test execution, TODO: Add test where this is available +// }); + +// it("Calling enable when metrics are not available should fail gracefully", () => { +// let nativePerformance = null; + +// assert.doesNotThrow( +// () => nativePerformance = new NativePerformanceMetrics(testMeter), +// "Does not throw when native metrics are not available" +// ); +// }); +// }); +// }); diff --git a/test/unitTests/metrics/performanceCounters.tests.ts b/test/unitTests/metrics/performanceCounters.tests.ts new file mode 100644 index 000000000..104f9d2c6 --- /dev/null +++ b/test/unitTests/metrics/performanceCounters.tests.ts @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as assert from "assert"; +import * as sinon from "sinon"; +import { Attributes, SpanKind } from "@opentelemetry/api"; +import { ExportResultCode } from "@opentelemetry/core"; +import { PerformanceCounterMetrics } from "../../../src/metrics/performanceCounters"; +import { ApplicationInsightsConfig } from "../../../src/shared/configuration/config"; +import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions"; +import { Resource } from "@opentelemetry/resources"; +import { Histogram } from "@opentelemetry/sdk-metrics"; + + +describe("PerformanceCounterMetricsHandler", () => { + let autoCollect: PerformanceCounterMetrics; + let config: ApplicationInsightsConfig; + let exportStub: sinon.SinonStub; + + before(() => { + config = new ApplicationInsightsConfig(); + config.azureMonitorExporterOptions.connectionString = + "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;"; + autoCollect = new PerformanceCounterMetrics(config || config, { + collectionInterval: 100, + }); + exportStub = sinon.stub(autoCollect["_azureExporter"], "export").callsFake( + (spans: any, resultCallback: any) => + new Promise((resolve) => { + resultCallback({ + code: ExportResultCode.SUCCESS, + }); + resolve(spans); + }) + ); + }); + + afterEach(() => { + exportStub.resetHistory(); + }); + + after(async () => { + exportStub.restore(); + await autoCollect.shutdown(); + }); + + + describe("#Metrics", () => { + it("should observe instruments during collection", async () => { + let resource = new Resource({}); + resource.attributes[SemanticResourceAttributes.SERVICE_NAME] = "testcloudRoleName"; + resource.attributes[SemanticResourceAttributes.SERVICE_INSTANCE_ID] = "testcloudRoleInstance"; + let serverSpan: any = { + kind: SpanKind.SERVER, + duration: [654321], + attributes: { + "http.status_code": 200, + }, + resource: resource, + }; + + for (let i = 0; i < 10; i++) { + autoCollect.recordSpan(serverSpan); + } + + await new Promise((resolve) => setTimeout(resolve, 120)); + assert.ok(exportStub.called); + const resourceMetrics = exportStub.args[0][0]; + const scopeMetrics = resourceMetrics.scopeMetrics; + assert.strictEqual(scopeMetrics.length, 1, "scopeMetrics count"); + let metrics = scopeMetrics[0].metrics; + assert.strictEqual(metrics.length, 6, "metrics count"); + + assert.deepStrictEqual( + metrics[0].descriptor.name, + "\\ASP.NET Applications(??APP_W3SVC_PROC??)\\Request Execution Time" + ); + assert.strictEqual(metrics[0].dataPoints.length, 1, "dataPoints count"); + assert.strictEqual((metrics[0].dataPoints[0].value as Histogram).count, 10, "dataPoint count"); + assert.strictEqual((metrics[0].dataPoints[0].value as Histogram).min, 654321, "dataPoint min"); + assert.strictEqual((metrics[0].dataPoints[0].value as Histogram).max, 654321, "dataPoint max"); + assert.strictEqual((metrics[0].dataPoints[0].value as Histogram).sum, 6543210, "dataPoint sum"); + + assert.deepStrictEqual( + metrics[1].descriptor.name, + "\\ASP.NET Applications(??APP_W3SVC_PROC??)\\Requests/Sec" + ); + assert.ok(metrics[1].dataPoints[0].value> 0, "Wrong request rate value"); + + assert.deepStrictEqual( + metrics[2].descriptor.name, + "\\Process(??APP_WIN32_PROC??)\\Private Bytes" + ); + assert.ok(metrics[2].dataPoints[0].value > 0, "Wrong private bytes value"); + assert.deepStrictEqual(metrics[3].descriptor.name, "\\Memory\\Available Bytes"); + assert.ok(metrics[3].dataPoints[0].value > 0, "Wrong available bytes value"); + assert.deepStrictEqual(metrics[4].descriptor.name, "\\Processor(_Total)\\% Processor Time"); + assert.ok( + metrics[4].dataPoints[0].value >= 0 && metrics[4].dataPoints[0].value <= 100, + `Wrong Processor Time value: ${metrics[4].dataPoints[0].value}` + ); + assert.deepStrictEqual( + metrics[5].descriptor.name, + "\\Process(??APP_WIN32_PROC??)\\% Processor Time" + ); + assert.ok( + metrics[5].dataPoints[0].value >= 0 && metrics[5].dataPoints[0].value <= 100, + `Wrong Process Time value: ${metrics[5].dataPoints[0].value}` + ); + }); + + it("should not collect when disabled", async () => { + autoCollect.shutdown(); + await new Promise((resolve) => setTimeout(resolve, 120)); + assert.ok(exportStub.notCalled); + }); + }); +}); diff --git a/test/unitTests/shared/configuration/jsonConfig.tests.ts b/test/unitTests/shared/configuration/jsonConfig.tests.ts new file mode 100644 index 000000000..81ac5e28d --- /dev/null +++ b/test/unitTests/shared/configuration/jsonConfig.tests.ts @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for details. +import { JsonConfig } from "../../../../src/shared/configuration/jsonConfig"; +import * as assert from "assert"; +import * as path from "path"; +import sinon = require("sinon"); + +describe("Json Config", () => { + let originalEnv: NodeJS.ProcessEnv; + let sandbox: sinon.SinonSandbox; + + before(() => { + sandbox = sinon.createSandbox(); + }); + + beforeEach(() => { + originalEnv = process.env; + JsonConfig["_instance"] = undefined; + }); + + afterEach(() => { + process.env = originalEnv; + JsonConfig["_instance"] = undefined; + sandbox.restore(); + }); + + describe("configuration values", () => { + it("Should take configuration from JSON string in APPLICATIONINSIGHTS_CONFIGURATION_CONTENT", () => { + const env = <{ [id: string]: string }>{}; + + let inputJson = { + "enableAutoCollectExceptions": true, + "otlpTraceExporterConfig": { enabled: true }, + "otlpMetricExporterConfig": { enabled: true }, + "otlpLogExporterConfig": { enabled: true }, + "enableAutoCollectPerformance": true, + "azureMonitorExporterOptions": { connectionString: "testConnString" }, + "samplingRatio": 1, + "instrumentationOptions": { + "http": { "enabled": true }, + "azureSdk": { "enabled": false }, + "mongoDb": { "enabled": false }, + "mySql": { "enabled": false }, + "postgreSql": { "enabled": false }, + "redis": { "enabled": false }, + "redis4": { "enabled": false }, + "console": { "enabled": true }, + "bunyan": { "enabled": true }, + "winston": { "enabled": true } + } + }; + env["APPLICATIONINSIGHTS_CONFIGURATION_CONTENT"] = JSON.stringify(inputJson); + process.env = env; + const config = JsonConfig.getInstance(); + + assert.strictEqual(config.enableAutoCollectExceptions, true); + assert.strictEqual(config.instrumentationOptions.console.enabled, true); + assert.strictEqual(config.instrumentationOptions.bunyan.enabled, true); + assert.strictEqual(config.instrumentationOptions.winston.enabled, true); + assert.strictEqual(config.otlpTraceExporterConfig.enabled, true); + assert.strictEqual(config.otlpMetricExporterConfig.enabled, true); + assert.strictEqual(config.enableAutoCollectPerformance, true); + assert.strictEqual(config.samplingRatio, 1); + assert.strictEqual(config.instrumentationOptions.http.enabled, true); + assert.strictEqual(config.instrumentationOptions.azureSdk.enabled, false); + assert.strictEqual(config.instrumentationOptions.mongoDb.enabled, false); + assert.strictEqual(config.instrumentationOptions.mySql.enabled, false); + assert.strictEqual(config.instrumentationOptions.postgreSql.enabled, false); + assert.strictEqual(config.instrumentationOptions.redis.enabled, false); + assert.strictEqual(config.instrumentationOptions.redis4.enabled, false); + assert.strictEqual(config.azureMonitorExporterOptions.connectionString, "testConnString"); + }); + + it("should get config file if an absolute path", () => { + const env = <{ [id: string]: string }>{}; + env["APPLICATIONINSIGHTS_CONFIGURATION_FILE"] = "/test/test.json"; + process.env = env; + const stub = sandbox.stub(path, "isAbsolute").returns(true); + JsonConfig.getInstance(); + assert.ok(stub.calledOnce); + }); + }); +}); diff --git a/test/unitTests/shared/util.tests.ts b/test/unitTests/shared/util.tests.ts new file mode 100644 index 000000000..266bd617b --- /dev/null +++ b/test/unitTests/shared/util.tests.ts @@ -0,0 +1,111 @@ +import * as assert from "assert"; +import * as sinon from "sinon"; + +import { Util } from "../../../src/shared/util"; + +describe("Library/Util", () => { + let sandbox: sinon.SinonSandbox; + before(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + describe("#trim(str)", () => { + it("should not crash", () => { + assert.doesNotThrow(() => Util.getInstance().trim(undefined)); + assert.doesNotThrow(() => Util.getInstance().trim(null)); + assert.doesNotThrow(() => Util.getInstance().trim("")); + assert.doesNotThrow(() => Util.getInstance().trim(3)); + assert.doesNotThrow(() => Util.getInstance().trim({})); + assert.doesNotThrow(() => Util.getInstance().trim([])); + }); + + it("should trim strings", () => { + assert.equal(Util.getInstance().trim(""), ""); + assert.equal(Util.getInstance().trim("\t"), ""); + assert.equal(Util.getInstance().trim("\n"), ""); + assert.equal(Util.getInstance().trim("\t\n\r test \t\n\r"), "test"); + assert.equal( + Util.getInstance().trim("\t\n\r test \t\n\r test \t\n\r"), + "test \t\n\r test" + ); + }); + }); + + describe("#isArray(obj)", () => { + it("should detect if an object is an array", () => { + assert.ok(Util.getInstance().isArray([])); + assert.ok(!Util.getInstance().isArray("sdf")); + assert.ok(Util.getInstance().isArray([0, 1])); + assert.ok(!Util.getInstance().isArray({ length: "" })); + assert.ok(!Util.getInstance().isArray({ length: 10 })); + }); + }); + + describe("#isError(obj)", () => { + it("should detect if an object is an instance of Error", () => { + class MyError extends Error { + constructor() { + super(); + } + } + assert.ok(!Util.getInstance().isError(undefined)); + assert.ok(!Util.getInstance().isError(null)); + assert.ok(!Util.getInstance().isError(true)); + assert.ok(!Util.getInstance().isError(1)); + assert.ok(!Util.getInstance().isError("")); + assert.ok(!Util.getInstance().isError([])); + assert.ok(!Util.getInstance().isError({})); + assert.ok(Util.getInstance().isError(new Error())); + assert.ok(Util.getInstance().isError(new MyError())); + }); + }); + + describe("#msToTimeSpan(totalMs)", () => { + const test = (input: number, expected: string, message: string) => { + const actual = Util.getInstance().msToTimeSpan(input); + assert.equal(expected, actual, message); + }; + + it("should convert milliseconds to a c# timespan", () => { + test(0, "00:00:00.000", "zero"); + test(1, "00:00:00.001", "milliseconds digit 1"); + test(10, "00:00:00.010", "milliseconds digit 2"); + test(100, "00:00:00.100", "milliseconds digit 3"); + test(1 * 1000, "00:00:01.000", "seconds digit 1"); + test(10 * 1000, "00:00:10.000", "seconds digit 2"); + test(1 * 60 * 1000, "00:01:00.000", "minutes digit 1"); + test(10 * 60 * 1000, "00:10:00.000", "minutes digit 2"); + test(1 * 60 * 60 * 1000, "01:00:00.000", "hours digit 1"); + test(10 * 60 * 60 * 1000, "10:00:00.000", "hours digit 2"); + test(24 * 60 * 60 * 1000, "1.00:00:00.000", "hours overflow"); + test(11 * 3600000 + 11 * 60000 + 11111, "11:11:11.111", "all digits"); + test( + 5 * 86400000 + 13 * 3600000 + 9 * 60000 + 8 * 1000 + 789, + "5.13:09:08.789", + "all digits with days" + ); + test(1001.505, "00:00:01.001505", "fractional milliseconds"); + test(1001.5, "00:00:01.0015", "fractional milliseconds - not all precision 1"); + test(1001.55, "00:00:01.00155", "fractional milliseconds - not all precision 2"); + test(1001.5059, "00:00:01.0015059", "fractional milliseconds - all digits"); + test( + 1001.50559, + "00:00:01.0015056", + "fractional milliseconds - too many digits, round up" + ); + }); + + it("should handle invalid input", () => { + test("", "00:00:00.000", "invalid input"); + test("'", "00:00:00.000", "invalid input"); + test(NaN, "00:00:00.000", "invalid input"); + test({}, "00:00:00.000", "invalid input"); + test([], "00:00:00.000", "invalid input"); + test(-1, "00:00:00.000", "invalid input"); + }); + }); +}); diff --git a/test/unitTests/shim/applicationInsights.tests.ts b/test/unitTests/shim/applicationInsights.tests.ts new file mode 100644 index 000000000..03052496b --- /dev/null +++ b/test/unitTests/shim/applicationInsights.tests.ts @@ -0,0 +1,145 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for details. +import * as assert from "assert"; +import * as sinon from "sinon"; + +import * as appInsights from "../../../src/index"; +import { diag } from "@opentelemetry/api"; +import { InstrumentationOptions } from "../../../src/types"; +import { checkWarnings } from "./testUtils"; + +describe("ApplicationInsights", () => { + let sandbox: sinon.SinonSandbox; + const connString: string = "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/" + before(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + appInsights.dispose(); + }); + + describe("#setup()", () => { + it("should not warn if setup is called once", (done) => { + appInsights.setup(connString); + const warnings = appInsights.defaultClient["_configWarnings"]; + assert.ok(!checkWarnings("Setup has already been called once", warnings), "setup warning was incorrectly raised"); + done(); + }); + it("should warn if setup is called twice", (done) => { + const warnStub = sandbox.stub(diag, "warn"); + appInsights.setup(connString); + appInsights.setup(connString); + warnStub.args.forEach((arg) => { + if (arg.includes("Setup has already been called once")) { + assert.ok(true, "setup warning was not raised"); + } + }); + done(); + }); + it("should not overwrite default client if called more than once", (done) => { + appInsights.setup(connString); + const client = appInsights.defaultClient; + appInsights.setup(connString); + appInsights.setup(connString); + appInsights.setup(connString); + assert.ok(JSON.stringify(client) === JSON.stringify(appInsights.defaultClient), "client is not overwritten"); + done(); + }); + }); + + describe("#start()", () => { + it("should warn if start is called before setup", (done) => { + const warnStub = sandbox.stub(diag, "warn"); + appInsights.start(); + warnStub.args.forEach((arg) => { + if (arg.includes("Start cannot be called before setup. Please call setup() first.")) { + assert.ok(true, "setup warning was not raised"); + } + }); + done(); + }); + + it("should not warn if start is called after setup", () => { + appInsights.setup(connString).start(); + const warnings = appInsights.defaultClient["_configWarnings"]; + assert.ok(!checkWarnings("Start cannot be called before setup. Please call setup() first.", warnings), "warning was thrown when start was called correctly"); + }); + }); + + describe("#setAutoCollect", () => { + it("auto-collection is initialized by default", () => { + appInsights.setup(connString); + appInsights.start(); + assert.equal(appInsights.defaultClient["_options"].enableAutoCollectExceptions, true); + assert.equal(appInsights.defaultClient["_options"].enableAutoCollectPerformance, true); + assert.equal(JSON.stringify(appInsights.defaultClient["_options"].instrumentationOptions.bunyan), JSON.stringify({ enabled: true })); + }); + + it("auto-collection is not initialized if disabled before 'start'", () => { + appInsights.setup(connString) + .setAutoCollectConsole(false) + .setAutoCollectExceptions(false) + .setAutoCollectPerformance(false, false) + .setAutoCollectRequests(false) + .setAutoCollectDependencies(false) + .setAutoDependencyCorrelation(false); + appInsights.start(); + assert.equal(appInsights.defaultClient["_options"].enableAutoCollectExceptions, false); + assert.equal(appInsights.defaultClient["_options"].enableAutoCollectPerformance, false); + assert.equal(JSON.stringify(appInsights.defaultClient["_options"].instrumentationOptions.bunyan), JSON.stringify({ enabled: false })); + assert.equal(JSON.stringify((appInsights.defaultClient["_options"].instrumentationOptions as InstrumentationOptions).console), JSON.stringify({ enabled: false })); + assert.equal(JSON.stringify((appInsights.defaultClient["_options"].instrumentationOptions as InstrumentationOptions).winston), JSON.stringify({ enabled: false })); + }); + + describe("#CorrelationContext", () => { + it("should return context once AppInsights is intialized", () => { + appInsights.setup(connString).start(); + const context = appInsights.getCorrelationContext(); + assert.ok(context.operation.id); + }); + }); + + describe("#Configuration", () => { + it("should throw warning if attempting to set AI distributed tracing mode to AI", () => { + appInsights.setup(connString); + const warnings = appInsights.defaultClient["_configWarnings"]; + appInsights.Configuration.setDistributedTracingMode(appInsights.DistributedTracingModes.AI); + appInsights.start(); + assert.ok(checkWarnings("AI only distributed tracing mode is no longer supported.", warnings), "warning was not raised"); + }); + + it("should warn if attempting to set auto collection of heartbeat", () => { + appInsights.setup(connString); + const warnings = appInsights.defaultClient["_configWarnings"]; + appInsights.Configuration.setAutoCollectHeartbeat(true); + appInsights.start(); + assert.ok(checkWarnings("Heartbeat metrics are no longer supported.", warnings), "warning was not raised"); + }); + + it("should warn if attempting to set maxBytesOnDisk", () => { + appInsights.setup(connString); + const warnings = appInsights.defaultClient["_configWarnings"]; + appInsights.Configuration.setUseDiskRetryCaching(true, 1000, 10); + appInsights.start(); + assert.ok(checkWarnings("The maxBytesOnDisk configuration option is not supported by the shim.", warnings), "warning was not raised"); + }); + + it("should set internal loggers", () => { + appInsights.setup(connString); + appInsights.Configuration.setInternalLogging(true, false); + appInsights.start(); + assert.equal(process.env["APPLICATIONINSIGHTS_INSTRUMENTATION_LOGGING_LEVEL"], "DEBUG"); + }); + + it("should warn if attempting to auto collect incoming azure functions requests", () => { + appInsights.setup(connString); + const warnings = appInsights.defaultClient["_configWarnings"]; + appInsights.Configuration.setAutoCollectIncomingRequestAzureFunctions(true); + appInsights.start(); + assert.ok(checkWarnings("Auto request generation in Azure Functions is no longer supported.", warnings), "warning was not raised"); + }); + }); + }); +}); diff --git a/Tests/Library/config.json b/test/unitTests/shim/config.json similarity index 74% rename from Tests/Library/config.json rename to test/unitTests/shim/config.json index e935f8557..0c9d531e9 100644 --- a/Tests/Library/config.json +++ b/test/unitTests/shim/config.json @@ -6,7 +6,6 @@ "maxBatchIntervalMs": 12000, "disableAppInsights": false, "samplingPercentage": 30, - "correlationIdRetryIntervalMs": 15000, "correlationHeaderExcludedDomains": [ "domain1", "domain2" @@ -24,8 +23,8 @@ "enableAutoCollectRequests": false, "enableAutoCollectDependencies": false, "enableAutoDependencyCorrelation": false, + "enableAutoCollectIncomingRequestAzureFunctions": false, "enableUseAsyncHooks": false, - "disableStatsbeat": false, "noHttpAgentKeepAlive": false, "distributedTracingMode": 0, "enableUseDiskRetryCaching": false, @@ -37,5 +36,9 @@ "extendedMetricDisablers": "gc,heap", "noDiagnosticChannel": false, "noPatchModules": "console,redis", - "quickPulseHost": "testquickpulsehost.com" + "quickPulseHost": "testquickpulsehost.com", + "enableWebInstrumentation": true, + "webInstrumentationConnectionString": "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3330;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/", + "webInstrumentationConfig": [{"name": "key1","value": "key1"},{"name":"key2", "value": true}], + "webInstrumentationSrc":"webInstrumentationSourceFromJson" } \ No newline at end of file diff --git a/test/unitTests/shim/config.tests.ts b/test/unitTests/shim/config.tests.ts new file mode 100644 index 000000000..53d4cd7f1 --- /dev/null +++ b/test/unitTests/shim/config.tests.ts @@ -0,0 +1,368 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for details. +import assert = require('assert'); +import sinon = require('sinon'); +import azureCoreAuth = require("@azure/core-auth"); +import { HttpInstrumentationConfig } from '@opentelemetry/instrumentation-http'; +import Config = require('../../../src/shim/shim-config'); +import { TelemetryClient } from "../../../src/shim/telemetryClient"; +import http = require("http"); +import https = require("https"); +import { DistributedTracingModes } from '../../../applicationinsights'; +import { checkWarnings } from './testUtils'; + +class TestTokenCredential implements azureCoreAuth.TokenCredential { + private _expiresOn: Date; + private _numberOfRefreshs = 0; + + constructor(expiresOn?: Date) { + this._expiresOn = expiresOn || new Date(); + } + + async getToken(scopes: string | string[], options?: any): Promise { + this._numberOfRefreshs++; + return { + token: "testToken" + this._numberOfRefreshs, + expiresOnTimestamp: this._expiresOn + }; + } +} + +describe("shim/configuration/config", () => { + const connectionString = "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/"; + + let originalEnv: NodeJS.ProcessEnv; + let sandbox: sinon.SinonSandbox; + + beforeEach(() => { + originalEnv = process.env; + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + process.env = originalEnv; + }) + + describe("#Shim config()", () => { + it("should initialize config values", () => { + const config = new Config(connectionString); + config.endpointUrl = "https://centralus-0.in.applicationinsights.azure.com/"; + config.proxyHttpUrl = "http://localhost:8888"; + config.proxyHttpsUrl = "https://localhost:3000"; + config.correlationHeaderExcludedDomains = ["https://www.bing.com"]; + config.samplingPercentage = 50; + config.enableAutoCollectExternalLoggers = true; + config.enableAutoCollectExceptions = true; + config.enableAutoCollectConsole = true; + config.enableAutoCollectExceptions = true; + config.enableAutoCollectPerformance = true; + config.enableAutoCollectRequests = true; + config.enableAutoCollectDependencies = true; + config.aadTokenCredential = new TestTokenCredential(); + config.maxBatchIntervalMs = 1000; + config.enableUseDiskRetryCaching = true; + + let options = config.parseConfig(); + + assert.equal(options.samplingRatio, 0.5, "wrong samplingRatio"); + assert.equal(options.azureMonitorExporterOptions.connectionString, connectionString), "wrong connectionString"; + assert.equal(options.azureMonitorExporterOptions.proxyOptions.host, "localhost", "wrong host"); + assert.equal(options.azureMonitorExporterOptions.proxyOptions.port, 3000, "wrong port"); + assert.equal(JSON.stringify(options.instrumentationOptions), JSON.stringify({ + "http": { "enabled": true }, + "azureSdk": { "enabled": true }, + "mongoDb": { "enabled": true }, + "mySql": { "enabled": true }, + "redis": { "enabled": true }, + "redis4": { "enabled": true }, + "postgreSql": { "enabled": true }, + "bunyan": { "enabled": true }, + "console": { "enabled": true }, + "winston": { "enabled": true } + }), + "wrong instrumentationOptions"); + assert.equal(JSON.stringify(options.instrumentationOptions.bunyan), JSON.stringify({ enabled: true }), "wrong bunyan setting"); + assert.equal(options.enableAutoCollectExceptions, true, "wrong enableAutoCollectExceptions"); + assert.equal(options.enableAutoCollectPerformance, true, "wrong enableAutoCollectPerformance"); + assert.equal(options.azureMonitorExporterOptions.credential, config.aadTokenCredential, "wrong credential"); + assert.equal(options.instrumentationOptions.http.enabled, true); + assert.equal( + JSON.stringify(options.otlpTraceExporterConfig), + JSON.stringify({ timeoutMillis: 1000 }), "wrong otlpTraceExporterConfig" + ); + assert.equal( + JSON.stringify(options.otlpMetricExporterConfig), + JSON.stringify({ timeoutMillis: 1000 }), "wrong otlpMetricExporterConfig" + ); + assert.equal( + JSON.stringify(options.otlpLogExporterConfig), + JSON.stringify({ timeoutMillis: 1000 }), "wrong otlpLogExporterConfig" + ); + assert.equal(options.azureMonitorExporterOptions.disableOfflineStorage, false, "wrong disableOfflineStorage"); + }); + + it("should activate DEBUG internal logger", () => { + const env = <{ [id: string]: string }>{}; + process.env = env; + const config = new Config(connectionString); + config.enableInternalDebugLogging = true; + config.parseConfig(); + assert.equal(process.env["APPLICATIONINSIGHTS_INSTRUMENTATION_LOGGING_LEVEL"], "DEBUG"); + }); + + it("should activate WARN internal logger", () => { + const env = <{ [id: string]: string }>{}; + process.env = env; + const config = new Config(connectionString); + config.enableInternalWarningLogging = true; + config.parseConfig(); + assert.equal(process.env["APPLICATIONINSIGHTS_INSTRUMENTATION_LOGGING_LEVEL"], "WARN"); + }); + + it("should set context tags on logs and spans", () => { + const telemetryClient = new TelemetryClient(connectionString); + telemetryClient.context.tags = { "ai.cloud.role": "testRole", "ai.cloud.roleInstance": "testRoleInstance" }; + telemetryClient.initialize(); + telemetryClient["_attributeSpanProcessor"]["_attributes"] = { "ai.cloud.role": "testRole", "ai.cloud.roleInstance": "testRoleInstance" }; + telemetryClient["_attributeLogProcessor"]["_attributes"] = { "ai.cloud.role": "testRole", "ai.cloud.roleInstance": "testRoleInstance" }; + }); + + it("should disable instrumentations when noDiagnosticChannel is set", () => { + const config = new Config(connectionString); + config.noDiagnosticChannel = true; + let options = config.parseConfig(); + assert.equal(JSON.stringify(options.instrumentationOptions), JSON.stringify({ + "http": { "enabled": true }, + "azureSdk": { "enabled": false }, + "mongoDb": { "enabled": false }, + "mySql": { "enabled": false }, + "redis": { "enabled": false }, + "redis4": { "enabled": false }, + "postgreSql": { "enabled": false }, + "bunyan": { "enabled": false }, + "console":{ "enabled": false }, + "winston": { "enabled": false } + })); + }); + + it("should disable specific instrumentations when noPatchModules is set", () => { + const config = new Config(connectionString); + config.noPatchModules = "azuresdk,mongodb-core,redis,pg-pool"; + let options = config.parseConfig(); + assert.equal(JSON.stringify(options.instrumentationOptions), JSON.stringify({ + http: { enabled: true }, + azureSdk: { enabled: false }, + mongoDb: { enabled: false }, + mySql: { enabled: true }, + redis: { enabled: false }, + redis4: { enabled: false }, + postgreSql: { enabled: false }, + bunyan: { enabled: true }, + console: { enabled: true }, + winston: { enabled: true }, + })); + }); + + it("should disable http if dependencies and requests are disabled", () => { + const config = new Config(connectionString); + config.enableAutoCollectDependencies = false; + config.enableAutoCollectRequests = false; + let options = config.parseConfig(); + const http: HttpInstrumentationConfig = options.instrumentationOptions.http as HttpInstrumentationConfig; + const ignoreFunction = (request: http.RequestOptions) => true; + assert.equal(options.instrumentationOptions.http.enabled, false); + assert.equal(JSON.stringify(http.ignoreIncomingRequestHook), JSON.stringify(ignoreFunction)); + assert.equal(JSON.stringify(http.ignoreOutgoingRequestHook), JSON.stringify(ignoreFunction)); + }); + + it("should disable external loggers", () => { + const config = new Config(connectionString); + config.enableAutoCollectExternalLoggers = false; + let options = config.parseConfig(); + assert.equal(JSON.stringify(options.instrumentationOptions), JSON.stringify({ + "http": { "enabled": true }, + "azureSdk": { "enabled": true }, + "mongoDb": { "enabled": true }, + "mySql": { "enabled": true }, + "redis": { "enabled": true }, + "redis4": { "enabled": true }, + "postgreSql": { "enabled": true }, + "bunyan": { "enabled": false }, + "console": { "enabled": true }, + "winston": { "enabled": false } + })); + }); + + it("should disable standard metrics", () => { + const config = new Config(connectionString); + config.enableAutoCollectPreAggregatedMetrics = false; + config.parseConfig(); + assert.equal(process.env["APPLICATION_INSIGHTS_NO_STANDARD_METRICS"], "disable"); + }); + + describe("#Shim unsupported messages", () => { + it("should warn if disableAppInsights is set", () => { + const config = new Config(connectionString); + const warnings = config["_configWarnings"]; + config.disableAppInsights = true; + config.parseConfig(); + assert.ok(checkWarnings("disableAppInsights configuration no longer supported.", warnings), "warning was not raised"); + }); + + it("should warn if collect heartbeat is set", () => { + const config = new Config(connectionString); + const warnings = config["_configWarnings"]; + config.enableAutoCollectHeartbeat = true; + config.parseConfig(); + assert.ok(checkWarnings("Heartbeat metrics are no longer supported.", warnings), "warning was not raised"); + }); + + it("should warn if auto dependency correlation is set to false", () => { + const config = new Config(connectionString); + const warnings = config["_configWarnings"]; + config.enableAutoDependencyCorrelation = false; + config.parseConfig(); + assert.ok(checkWarnings("Auto dependency correlation cannot be turned off anymore.", warnings), "warning was not raised"); + }); + + it("should warn if auto request generation is azure functions is set", () => { + const config = new Config(connectionString); + const warnings = config["_configWarnings"]; + config.enableAutoCollectIncomingRequestAzureFunctions = true; + config.parseConfig(); + assert.ok(checkWarnings("Auto request generation in Azure Functions is no longer supported.", warnings), "warning was not raised"); + }); + + it("should warn if using async hooks is set to false", () => { + const config = new Config(connectionString); + const warnings = config["_configWarnings"]; + config.enableUseAsyncHooks = false; + config.parseConfig(); + assert.ok(checkWarnings("The use of non async hooks is no longer supported.", warnings), "warning was not raised"); + }); + + it("should warn if distributed tracing mode is set to AI", () => { + const config = new Config(connectionString); + const warnings = config["_configWarnings"]; + config.distributedTracingMode = DistributedTracingModes.AI; + config.parseConfig(); + assert.ok(checkWarnings("AI only distributed tracing mode is no longer supported.", warnings), "warning was not raised"); + }); + + it("should warn if resend interval is set", () => { + const config = new Config(connectionString); + const warnings = config["_configWarnings"]; + config.enableResendInterval = 1; + config.parseConfig(); + assert.ok(checkWarnings("The resendInterval configuration option is not supported by the shim.", warnings), "warning was not raised"); + }); + + it("should warn if max bytes on disk is set", () => { + const config = new Config(connectionString); + const warnings = config["_configWarnings"]; + config.enableMaxBytesOnDisk = 1; + config.parseConfig(); + assert.ok(checkWarnings("The maxBytesOnDisk configuration option is not supported by the shim.", warnings), "warning was not raised"); + }); + + it("should warn if ignore legacy headers is false", () => { + const config = new Config(connectionString); + const warnings = config["_configWarnings"]; + config.ignoreLegacyHeaders = false; + config.parseConfig(); + assert.ok(checkWarnings("LegacyHeaders are not supported by the shim.", warnings), "warning was not raised"); + }); + + it("should warn if max batch size is set", () => { + const config = new Config(connectionString); + const warnings = config["_configWarnings"]; + config.maxBatchSize = 1; + config.parseConfig(); + assert.ok(checkWarnings("The maxBatchSize configuration option is not supported by the shim.", warnings), "warning was not raised"); + }); + + it("should warn if logger errors are set to traces", () => { + const config = new Config(connectionString); + const warnings = config["_configWarnings"]; + config.enableLoggerErrorToTrace = true; + config.parseConfig(); + assert.ok(checkWarnings("The enableLoggerErrorToTrace configuration option is not supported by the shim.", warnings), "warning was not raised"); + }); + + it("should warn if httpAgent or httpsAgent are set", () => { + const config = new Config(connectionString); + const warnings = config["_configWarnings"]; + config.httpAgent = new http.Agent(); + config.httpsAgent = new https.Agent(); + config.parseConfig(); + assert.ok(checkWarnings("The httpAgent and httpsAgent configuration options are not supported by the shim.", warnings), "warning was not raised"); + }); + + it("should warn if web instrumentations are set", () => { + const config = new Config(connectionString); + const warnings = config["_configWarnings"]; + config.webInstrumentationConfig = [{name: "test", value: true}]; + config.webInstrumentationSrc = "test"; + config.parseConfig(); + assert.ok(checkWarnings("The webInstrumentation config and src options are not supported by the shim.", warnings), "warning was not raised"); + }); + + it("should warn if correlationHeaderExcludedDomains is set", () => { + const config = new Config(connectionString); + const warnings = config["_configWarnings"]; + config.correlationHeaderExcludedDomains = ["test.com"]; + config.parseConfig(); + assert.ok(checkWarnings("The correlationHeaderExcludedDomains configuration option is not supported by the shim.", warnings), "warning was not raised"); + }); + + it("should warn if extended metric disablers are set", () => { + const config = new Config(connectionString); + const warnings = config["_configWarnings"]; + config.extendedMetricDisablers = "gc"; + config.parseConfig(); + assert.ok(checkWarnings("Extended metrics are no longer supported.", warnings), "warning was not raised"); + }); + + it("should warn if extended metrics are set", () => { + const config = new Config(connectionString); + const warnings = config["_configWarnings"]; + config.enableAutoCollectExtendedMetrics = { gc: true, heap: false }; + config.parseConfig(); + assert.ok(checkWarnings("Extended metrics are no longer supported.", warnings), "warning was not raised"); + }); + + it("should warn if extended metrics are set", () => { + const config = new Config(connectionString); + const warnings = config["_configWarnings"]; + config.enableAutoCollectExtendedMetrics = { gc: true, heap: false }; + config.parseConfig(); + assert.ok(checkWarnings("Extended metrics are no longer supported.", warnings), "warning was not raised"); + }); + + it("should warn if disableAllExtendedMetrics is set", () => { + const config = new Config(connectionString); + const warnings = config["_configWarnings"]; + config.disableAllExtendedMetrics = true; + config.parseConfig(); + assert.ok(checkWarnings("Extended metrics are no longer supported.", warnings), "warning was not raised"); + }); + + it("should warn if disable all extended meetrics env var is set", () => { + const config = new Config(connectionString); + const warnings = config["_configWarnings"]; + process.env["APPLICATION_INSIGHTS_DISABLE_ALL_EXTENDED_METRICS"] = "false"; + config.parseConfig(); + assert.ok(checkWarnings("Extended metrics are no longer supported.", warnings), "warning was not raised"); + }); + + it("should warn if disable specific extended metric env var is set", () => { + const config = new Config(connectionString); + const warnings = config["_configWarnings"]; + process.env["APPLICATION_INSIGHTS_DISABLE_EXTENDED_METRIC"] = "gc,heap"; + config.parseConfig(); + assert.ok(checkWarnings("Extended metrics are no longer supported.", warnings), "warning was not raised"); + }); + }); + }); +}); diff --git a/test/unitTests/shim/correlationContextManger.tests.ts b/test/unitTests/shim/correlationContextManger.tests.ts new file mode 100644 index 000000000..fa65bec01 --- /dev/null +++ b/test/unitTests/shim/correlationContextManger.tests.ts @@ -0,0 +1,251 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for details. +import assert = require("assert"); +import sinon = require("sinon"); +import { SpanContext, context, trace } from "@opentelemetry/api"; +import * as azureFunctionTypes from "@azure/functions"; +import { CorrelationContextManager } from '../../../src/shim/correlationContextManager'; +import { ICorrelationContext } from "../../../src/shim/types"; + + +const customProperties = { + getProperty(prop: string) { return "" }, + setProperty(prop: string) { return "" }, +} + +const testContext: ICorrelationContext = { + operation: { + id: "test", + name: undefined, + parentId: undefined, + traceparent: { + // No support for legacyRootId + legacyRootId: "", + parentId: undefined, + // Same as the id field + traceId: "test", + spanId: "test", + traceFlag: "1", + // Will always be version 00 + version: "00", + }, + tracestate: { fieldmap: [""] } + }, + customProperties +}; + +const testContext2: ICorrelationContext = { + operation: { + id: "test2", + name: undefined, + parentId: undefined, + traceparent: { + legacyRootId: "", + parentId: undefined, + traceId: "test2", + spanId: "test2", + traceFlag: "1", + version: "00", + }, + tracestate: { fieldmap: [""] } + }, + customProperties +} + +// Test getCurrentContext +describe("#getCurrentContext()", () => { + it("should return the context if in a context", (done) => { + CorrelationContextManager.runWithContext(testContext, () => { + process.nextTick(() => { + assert.strictEqual(JSON.stringify(CorrelationContextManager.getCurrentContext()), JSON.stringify(testContext)); + done(); + }); + }); + }); + + it("should return the context if called by an asychronous callback in a context", (done) => { + CorrelationContextManager.runWithContext(testContext2, () => { + process.nextTick(() => { + assert.strictEqual(JSON.stringify(CorrelationContextManager.getCurrentContext()), JSON.stringify(testContext2)); + done(); + }); + }); + }); + + it("should return the correct context to asynchronous callbacks occuring in parellel", (done) => { + CorrelationContextManager.runWithContext(testContext, () => { + process.nextTick(() => { + assert.strictEqual(JSON.stringify(CorrelationContextManager.getCurrentContext()), JSON.stringify(testContext)); + }); + }); + CorrelationContextManager.runWithContext(testContext2, () => { + process.nextTick(() => { + assert.strictEqual(JSON.stringify(CorrelationContextManager.getCurrentContext()), JSON.stringify(testContext2)); + }); + }); + + setTimeout(() => done(), 10); + }); +}); + +// Test runWithContext +describe("#runWithContext()", () => { + it("should run the supplied function", () => { + CorrelationContextManager.enable(); + const fn = sinon.spy(); + CorrelationContextManager.runWithContext(testContext, fn); + + assert(fn.calledOnce); + }); +}); + +// Test wrapEmitter + +// Test wrapCallback +describe("#wrapCallback()", () => { + it("should return a function that calls the supplied function", () => { + const fn = sinon.spy(); + const wrappedFn = CorrelationContextManager.wrapCallback(fn); + wrappedFn(); + + assert.notEqual(wrappedFn, fn); + assert(fn.calledOnce); + }); + + it("should return a function that restores the context at call-time into the supplied function", (done) => { + let sharedFn = () => { + assert.equal(JSON.stringify(CorrelationContextManager.getCurrentContext()), JSON.stringify(testContext)); + } + + CorrelationContextManager.runWithContext(testContext, () => { + sharedFn = CorrelationContextManager.wrapCallback(sharedFn); + }); + + CorrelationContextManager.runWithContext(testContext2, () => { + setTimeout(() => { + sharedFn(); + }, 8); + }); + + setTimeout(() => done(), 10); + }); +}); + +// Test startOperation +describe("#startOperation()", () => { + const testSpanContext: SpanContext = { + traceId: "testtraceid", + spanId: "testspanid", + traceFlags: 0, + }; + + const testFunctionTraceContext: azureFunctionTypes.TraceContext = { + traceparent: "00-testtraceid-testspanid", + tracestate: "", + attributes: {}, + }; + + const testFunctionContext: azureFunctionTypes.Context = { + invocationId: "test", + executionContext: { + invocationId: '', + functionName: '', + functionDirectory: '', + retryContext: undefined + }, + bindings: {}, + bindingData: { + invocationId: '' + }, + traceContext: testFunctionTraceContext, + bindingDefinitions: [], + log: { error() { }, warn() { }, info() { }, verbose() { } } as azureFunctionTypes.Logger, + done: () => { }, + }; + + const testRequest: azureFunctionTypes.HttpRequest = { + method: "GET", + url: "/search", + headers: { + host: "bing.com", + traceparent: testFunctionContext.traceContext.traceparent, + }, + query: { q: 'test' }, + params: {}, + user: null, + body: {}, + rawBody: {}, + bufferBody: undefined, + get(header: string) { return this.headers[header.toLowerCase()] }, + parseFormBody: undefined, + }; + + describe("#Azure Functions", () => { + it("should start a new context with the 2nd arg http request", () => { + const context = CorrelationContextManager.startOperation(testFunctionContext, testRequest); + assert.ok(context.operation); + assert.deepEqual(context.operation.id, testFunctionTraceContext.traceparent.split("-")[1]); + assert.deepEqual(context.operation.parentId, testFunctionTraceContext.traceparent.split("-")[2]); + assert.deepEqual( + `${context.operation.traceparent.version}-${context.operation.traceparent.traceId}-${context.operation.traceparent.spanId}`, + testFunctionTraceContext.traceparent + ); + }); + + it("should start a new context with 2nd arg string", () => { + const context = CorrelationContextManager.startOperation(testFunctionContext, "GET /foo"); + assert.ok(context.operation); + assert.deepEqual(context.operation.id, testFunctionTraceContext.traceparent.split("-")[1]); + assert.deepEqual(context.operation.parentId, testFunctionTraceContext.traceparent.split("-")[2]); + assert.deepEqual( + `${context.operation.traceparent.version}-${context.operation.traceparent.traceId}-${context.operation.traceparent.spanId}`, + testFunctionTraceContext.traceparent + ); + }); + + it("should start a new context with no request", () => { + const context = CorrelationContextManager.startOperation(testFunctionContext, "GET /test"); + assert.ok(context.operation); + assert.deepEqual(context.operation.id, testFunctionTraceContext.traceparent.split("-")[1]); + assert.deepEqual(context.operation.parentId, testFunctionTraceContext.traceparent.split("-")[2]); + assert.deepEqual( + `${context.operation.traceparent.version}-${context.operation.traceparent.traceId}-${context.operation.traceparent.spanId}`, + testFunctionTraceContext.traceparent + ); + }); + }); + + describe("#SpanContext", () => { + it("should start a new context using SpanContext", () => { + const context = CorrelationContextManager.startOperation(testSpanContext, "GET /test"); + + assert.ok(context.operation); + assert.deepEqual(context.operation.id, testSpanContext.traceId); + assert.deepEqual(context.operation.parentId, context.operation.parentId); + }); + }); + + describe("#headers", () => { + it("should start a new context using the headers from an HTTP request", () => { + const context = CorrelationContextManager.startOperation(testRequest, "GET /test"); + + assert.ok(context.operation); + assert.deepEqual(context.operation.id, testFunctionTraceContext?.traceparent?.split("-")[1]); + assert.deepEqual(context.operation.parentId, testFunctionTraceContext.traceparent.split("-")[2]); + assert.deepEqual( + `${context.operation.traceparent.version}-${context.operation.traceparent.traceId}-${context.operation.traceparent.spanId}`, + testFunctionTraceContext.traceparent + ); + }); + }); + + /** + * This test must occur last as it will disable context + */ + describe("#Context.Disable", () => { + it("should return null if the context is disabled", () => { + CorrelationContextManager.disable(); + assert.strictEqual(CorrelationContextManager.getCurrentContext(), null); + }); + }); +}); diff --git a/test/unitTests/shim/jsonConfig.tests.ts b/test/unitTests/shim/jsonConfig.tests.ts new file mode 100644 index 000000000..6ab3521f8 --- /dev/null +++ b/test/unitTests/shim/jsonConfig.tests.ts @@ -0,0 +1,206 @@ +import assert = require("assert"); +import path = require("path"); +import { ShimJsonConfig } from "../../../src/shim/shim-jsonConfig"; + + +describe("Json Config", () => { + let originalEnv: NodeJS.ProcessEnv; + + beforeEach(() => { + originalEnv = process.env; + ShimJsonConfig["_instance"] = undefined; + }); + + afterEach(() => { + process.env = originalEnv; + ShimJsonConfig["_instance"] = undefined; + }); + + describe("configuration values", () => { + it("should take configurations from the JSON config file", () => { + const cutstomConfigJsonPath = path.resolve(__dirname, "../../../../test/unitTests/shim/config.json"); + process.env["APPLICATIONINSIGHTS_CONFIGURATION_FILE"] = cutstomConfigJsonPath; + + let config = ShimJsonConfig.getInstance(); + assert.equal(config.connectionString, "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/"); + assert.equal(config.endpointUrl, "testEndpointUrl"); + assert.equal(config.maxBatchSize, 150); + assert.equal(config.maxBatchIntervalMs, 12000); + assert.equal(config.disableAppInsights, false); + assert.equal(config.samplingPercentage, 30); + assert.equal(config.correlationHeaderExcludedDomains[0], "domain1"); + assert.equal(config.correlationHeaderExcludedDomains[1], "domain2"); + assert.equal(config.proxyHttpUrl, "testProxyHttpUrl"); + assert.equal(config.proxyHttpsUrl, "testProxyHttpsUrl"); + assert.equal(config.ignoreLegacyHeaders, false); + assert.equal(config.enableAutoCollectExternalLoggers, false); + assert.equal(config.enableAutoCollectConsole, false); + assert.equal(config.enableAutoCollectExceptions, false); + assert.equal(config.enableAutoCollectPerformance, false); + assert.equal(config.enableAutoCollectPreAggregatedMetrics, false); + assert.equal(config.enableAutoCollectHeartbeat, false); + assert.equal(config.enableAutoCollectRequests, false); + assert.equal(config.enableAutoCollectDependencies, false); + assert.equal(config.enableAutoDependencyCorrelation, false); + assert.equal(config.enableAutoCollectIncomingRequestAzureFunctions, false); + assert.equal(config.enableUseAsyncHooks, false); + assert.equal(config.enableAutoCollectExtendedMetrics, false); + assert.equal(config.noHttpAgentKeepAlive, false); + assert.equal(config.distributedTracingMode, 0); + assert.equal(config.enableUseDiskRetryCaching, false); + assert.equal(config.enableResendInterval, 123); + assert.equal(config.enableMaxBytesOnDisk, 456); + assert.equal(config.enableInternalDebugLogging, false); + assert.equal(config.enableInternalWarningLogging, false); + assert.equal(config.enableSendLiveMetrics, false); + assert.equal(config.extendedMetricDisablers, "gc,heap"); + assert.equal(config.noDiagnosticChannel, false); + assert.equal(config.noPatchModules, "console,redis"); + assert.equal(config.quickPulseHost, "testquickpulsehost.com"); + assert.equal(config.enableWebInstrumentation, true); + assert.equal(config.webInstrumentationConnectionString, "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3330;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/"); + assert.deepEqual(config.webInstrumentationConfig, [{ name: "key1", value: "key1" }, { name: "key2", value: true }],); + assert.equal(config.webInstrumentationSrc, "webInstrumentationSourceFromJson"); + }); + + it("Should take configurations from environment variables", () => { + const env = <{ [id: string]: string }>{}; + env["APPLICATIONINSIGHTS_CONNECTION_STRING"] = "TestConnectionString"; + env["APPLICATION_INSIGHTS_NO_PATCH_MODULES"] = "azuresdk"; + env["http_proxy"] = "testProxyHttpUrl2"; + env["https_proxy"] = "testProxyHttpsUrl2"; + env["APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL"] = "disabled"; + env["APPLICATION_INSIGHTS_NO_HTTP_AGENT_KEEP_ALIVE"] = "disabled"; + env["APPLICATIONINSIGHTS_WEB_INSTRUMENTATION_ENABLED"] = "true"; + env["APPLICATIONINSIGHTS_WEB_INSTRUMENTATION_CONNECTION_STRING"] = "WebInstrumentationConnectionString"; + env["APPLICATIONINSIGHTS_WEB_INSTRUMENTATION_SOURCE"] = "WebInstrumentationTestSource"; + process.env = env; + const config = ShimJsonConfig.getInstance(); + assert.equal(config.connectionString, "TestConnectionString"); + assert.equal(config.proxyHttpUrl, "testProxyHttpUrl2"); + assert.equal(config.proxyHttpsUrl, "testProxyHttpsUrl2"); + assert.equal(config.noDiagnosticChannel, true, "wrong noDiagnosticChannel"); + assert.equal(config.noHttpAgentKeepAlive, true, "wrong noHttpAgentKeepAlive"); + assert.equal(config.noPatchModules, "azuresdk"); + assert.equal(config.enableWebInstrumentation, true, "wrong enableWebInstrumentation"); + assert.equal(config.webInstrumentationConnectionString, "WebInstrumentationConnectionString"); + assert.equal(config.webInstrumentationSrc, "WebInstrumentationTestSource"); + }); + + it("Should take configurations from JSON config file over environment variables if both are configured", () => { + const env = <{ [id: string]: string }>{}; + const cutstomConfigJsonPath = path.resolve(__dirname, "../../../../test/unitTests/shim/config.json"); + env["APPLICATIONINSIGHTS_CONFIGURATION_FILE"] = cutstomConfigJsonPath; + env["APPLICATIONINSIGHTS_CONNECTION_STRING"] = "TestConnectionString"; + env["APPLICATION_INSIGHTS_NO_PATCH_MODULES"] = "azuresdk"; + env["http_proxy"] = "testProxyHttpUrl2"; + env["https_proxy"] = "testProxyHttpsUrl2"; + env["APPINSIGHTS_WEB_SNIPPET_ENABLED"] = "false"; + env["APPLICATIONINSIGHTS_WEB_INSTRUMENTATION_ENABLED"] = "WebInstrumentationConnectionString"; + env["APPLICATIONINSIGHTS_WEB_INSTRUMENTATION_CONNECTION_STRING"] = "true"; + env["APPLICATIONINSIGHTS_WEB_INSTRUMENTATION_SOURCE"] = "WebInstrumentationTestSource" + process.env = env; + const config = ShimJsonConfig.getInstance(); + assert.equal(config.connectionString, "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/"); + assert.equal(config.proxyHttpUrl, "testProxyHttpUrl"); + assert.equal(config.proxyHttpsUrl, "testProxyHttpsUrl"); + assert.equal(config.noDiagnosticChannel, false); + assert.equal(config.noHttpAgentKeepAlive, false); + assert.equal(config.noPatchModules, "console,redis"); + assert.equal(config.enableWebInstrumentation, true); + assert.equal(config.webInstrumentationConnectionString, "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3330;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/"); + assert.equal(config.webInstrumentationSrc, "webInstrumentationSourceFromJson"); + }); + + it("Should take configuration from JSON string in APPLICATIONINSIGHTS_CONFIGURATION_CONTENT", () => { + const env = <{ [id: string]: string }>{}; + + let inputJson = { + "connectionString": "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/", + "endpointUrl": "testEndpointUrl", + "disableAllExtendedMetrics": false, + "maxBatchSize": 150, + "maxBatchIntervalMs": 12000, + "disableAppInsights": false, + "samplingPercentage": 30, + "correlationHeaderExcludedDomains": [ + "domain1", + "domain2" + ], + "proxyHttpUrl": "testProxyHttpUrl", + "proxyHttpsUrl": "testProxyHttpsUrl", + "ignoreLegacyHeaders": false, + "enableAutoCollectExternalLoggers": false, + "enableAutoCollectConsole": false, + "enableAutoCollectExceptions": false, + "enableAutoCollectPerformance": false, + "enableAutoCollectExtendedMetrics": false, + "enableAutoCollectPreAggregatedMetrics": false, + "enableAutoCollectHeartbeat": false, + "enableAutoCollectRequests": false, + "enableAutoCollectDependencies": false, + "enableAutoDependencyCorrelation": false, + "enableAutoCollectIncomingRequestAzureFunctions": false, + "enableUseAsyncHooks": false, + "noHttpAgentKeepAlive": false, + "distributedTracingMode": 0, + "enableUseDiskRetryCaching": false, + "enableResendInterval": 123, + "enableMaxBytesOnDisk": 456, + "enableInternalDebugLogging": false, + "enableInternalWarningLogging": false, + "enableSendLiveMetrics": false, + "extendedMetricDisablers": "gc,heap", + "noDiagnosticChannel": false, + "noPatchModules": "console,redis", + "quickPulseHost": "testquickpulsehost.com", + "enableWebInstrumentation": true, + "webInstrumentationConnectionString": "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3330;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/", + "webInstrumentationConfig": [{"name": "key1","value": "key1"},{"name":"key2", "value": true}], + "webInstrumentationSrc":"webInstrumentationSourceFromJson" + }; + env["APPLICATIONINSIGHTS_CONFIGURATION_CONTENT"] = JSON.stringify(inputJson); + process.env = env; + const config = ShimJsonConfig.getInstance(); + assert.equal(config.connectionString, "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/"); + assert.equal(config.endpointUrl, "testEndpointUrl"); + assert.equal(config.maxBatchSize, 150); + assert.equal(config.maxBatchIntervalMs, 12000); + assert.equal(config.disableAppInsights, false); + assert.equal(config.samplingPercentage, 30); + assert.equal(config.correlationHeaderExcludedDomains[0], "domain1"); + assert.equal(config.correlationHeaderExcludedDomains[1], "domain2"); + assert.equal(config.proxyHttpUrl, "testProxyHttpUrl"); + assert.equal(config.proxyHttpsUrl, "testProxyHttpsUrl"); + assert.equal(config.ignoreLegacyHeaders, false); + assert.equal(config.enableAutoCollectExternalLoggers, false); + assert.equal(config.enableAutoCollectConsole, false); + assert.equal(config.enableAutoCollectExceptions, false); + assert.equal(config.enableAutoCollectPerformance, false); + assert.equal(config.enableAutoCollectPreAggregatedMetrics, false); + assert.equal(config.enableAutoCollectHeartbeat, false); + assert.equal(config.enableAutoCollectRequests, false); + assert.equal(config.enableAutoCollectDependencies, false); + assert.equal(config.enableAutoDependencyCorrelation, false); + assert.equal(config.enableAutoCollectIncomingRequestAzureFunctions, false); + assert.equal(config.enableUseAsyncHooks, false); + assert.equal(config.enableAutoCollectExtendedMetrics, false); + assert.equal(config.noHttpAgentKeepAlive, false); + assert.equal(config.distributedTracingMode, 0); + assert.equal(config.enableUseDiskRetryCaching, false); + assert.equal(config.enableResendInterval, 123); + assert.equal(config.enableMaxBytesOnDisk, 456); + assert.equal(config.enableInternalDebugLogging, false); + assert.equal(config.enableInternalWarningLogging, false); + assert.equal(config.enableSendLiveMetrics, false); + assert.equal(config.extendedMetricDisablers, "gc,heap"); + assert.equal(config.noDiagnosticChannel, false); + assert.equal(config.noPatchModules, "console,redis"); + assert.equal(config.quickPulseHost, "testquickpulsehost.com"); + assert.equal(config.enableWebInstrumentation, true); + assert.equal(config.webInstrumentationConnectionString, "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3330;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/"); + assert.deepEqual(config.webInstrumentationConfig, [{ name: "key1", value: "key1" }, { name: "key2", value: true }],); + assert.equal(config.webInstrumentationSrc, "webInstrumentationSourceFromJson"); + }); + }); +}); diff --git a/test/unitTests/shim/telemetryClient.tests.ts b/test/unitTests/shim/telemetryClient.tests.ts new file mode 100644 index 000000000..5d5ce0acb --- /dev/null +++ b/test/unitTests/shim/telemetryClient.tests.ts @@ -0,0 +1,278 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for details. +import * as assert from "assert"; +import * as nock from "nock"; +import * as sinon from "sinon"; +import { Context, ProxyTracerProvider, trace, metrics } from "@opentelemetry/api"; +import { ReadableSpan, Span, SpanProcessor } from "@opentelemetry/sdk-trace-base"; +import { DependencyTelemetry, RequestTelemetry } from "../../../src/declarations/contracts"; +import { TelemetryClient } from "../../../src/shim/telemetryClient"; +import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node"; +import { AzureMonitorExporterOptions, AzureMonitorMetricExporter } from "@azure/monitor-opentelemetry-exporter"; +import { MeterProvider, PeriodicExportingMetricReader, PeriodicExportingMetricReaderOptions, ResourceMetrics } from "@opentelemetry/sdk-metrics"; +import { LogRecord, LogRecordProcessor, LoggerProvider } from "@opentelemetry/sdk-logs"; +import { logs } from "@opentelemetry/api-logs"; +import { SEMATTRS_RPC_SYSTEM } from "@opentelemetry/semantic-conventions"; + +describe("shim/TelemetryClient", () => { + let client: TelemetryClient; + let testProcessor: TestSpanProcessor; + let tracerProvider: NodeTracerProvider; + let loggerProvider: LoggerProvider; + let testMetrics: ResourceMetrics; + let metricProvider: MeterProvider; + let logProcessor: TestLogProcessor; + let sandbox: sinon.SinonSandbox; + + before(() => { + sandbox = sinon.createSandbox(); + trace.disable(); + metrics.disable(); + nock("https://dc.services.visualstudio.com") + .post("/v2.1/track", (body: string) => true) + .reply(200, {}) + .persist(); + nock.disableNetConnect(); + + client = new TelemetryClient( + "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333" + ); + client.config.samplingPercentage = 100; + client.config.noDiagnosticChannel = true; + client.initialize(); + tracerProvider = ((trace.getTracerProvider() as ProxyTracerProvider).getDelegate() as NodeTracerProvider); + testProcessor = new TestSpanProcessor(); + tracerProvider.addSpanProcessor(testProcessor); + + loggerProvider = logs.getLoggerProvider() as LoggerProvider; + logProcessor = new TestLogProcessor({}); + loggerProvider.addLogRecordProcessor(logProcessor); + + metricProvider = metrics.getMeterProvider() as MeterProvider; + }); + + afterEach(() => { + sandbox.restore(); + testProcessor.spansProcessed = []; + }); + + + after(() => { + nock.cleanAll(); + nock.enableNetConnect(); + client.shutdown(); + }); + + class TestSpanProcessor implements SpanProcessor { + public spansProcessed: Array = []; + + forceFlush(): Promise { + return Promise.resolve(); + } + onStart(span: Span, parentContext: Context): void { + } + onEnd(span: ReadableSpan): void { + this.spansProcessed.push(span); + } + shutdown(): Promise { + return Promise.resolve(); + } + } + + class TestLogProcessor implements LogRecordProcessor { + private _attributes: { [key: string]: string }; + constructor(attributes: { [key: string]: string }) { + this._attributes = attributes; + } + + // Override onEmit to apply log record attributes before exporting + onEmit(record: LogRecord) { + record.setAttributes(this._attributes); + } + + shutdown(): Promise { + return Promise.resolve(); + } + + forceFlush(): Promise { + return Promise.resolve(); + } + } + + class TestExporter extends AzureMonitorMetricExporter { + constructor(options: AzureMonitorExporterOptions = {}) { + super(options); + } + async export( + metrics: ResourceMetrics, + ): Promise { + testMetrics = metrics; + } + } + + describe("#manual track APIs", () => { + it("trackDependency http", async () => { + const telemetry: DependencyTelemetry = { + name: "TestName", + duration: 2000, //2 seconds + resultCode: "401", + data: "http://test.com", + dependencyTypeName: "HTTP", + target: "TestTarget", + success: false, + }; + client.trackDependency(telemetry); + + await tracerProvider.forceFlush(); + const spans = testProcessor.spansProcessed; + assert.equal(spans.length, 1); + assert.equal(spans[0].name, "TestName"); + assert.equal(spans[0].endTime[0] - spans[0].startTime[0], 2); // hrTime UNIX Epoch time in seconds + assert.equal(spans[0].kind, 2, "Span Kind"); // Outgoing + assert.equal(spans[0].attributes["http.status_code"], "401"); + assert.equal(spans[0].attributes["http.url"], "http://test.com"); + assert.equal(spans[0].attributes["peer.service"], "TestTarget"); + }); + + it("trackDependency DB", async () => { + const telemetry: DependencyTelemetry = { + name: "TestName", + duration: 2000, //2 seconds + resultCode: "401", + data: "SELECT * FROM test", + dependencyTypeName: "MYSQL", + target: "TestTarget", + success: false, + }; + client.trackDependency(telemetry); + await tracerProvider.forceFlush(); + const spans = testProcessor.spansProcessed; + assert.equal(spans.length, 1); + assert.equal(spans[0].name, "TestName"); + assert.equal(spans[0].kind, 2, "Span Kind"); // Outgoing + assert.equal(spans[0].attributes["db.system"], "MYSQL"); + assert.equal(spans[0].attributes["db.statement"], "SELECT * FROM test"); + }); + + it("trackDependency RPC", async () => { + const telemetry: DependencyTelemetry = { + name: "TestName", + duration: 2000, //2 seconds + resultCode: "200", + data: "SELECT * FROM test", + dependencyTypeName: "RPC", + target: "TestTarget", + success: false, + }; + client.trackDependency(telemetry); + await tracerProvider.forceFlush(); + const spans = testProcessor.spansProcessed; + assert.equal(spans.length, 1); + assert.equal(spans[0].name, "TestName"); + assert.equal(spans[0].attributes[SEMATTRS_RPC_SYSTEM], "RPC"); + }); + + it("trackRequest", async () => { + const telemetry: RequestTelemetry = { + id: "123456", + name: "TestName", + duration: 2000, //2 seconds + resultCode: "401", + url: "http://test.com", + success: false, + }; + client.trackRequest(telemetry); + await tracerProvider.forceFlush(); + const spans = testProcessor.spansProcessed; + assert.equal(spans.length, 1); + assert.equal(spans[0].name, "TestName"); + assert.equal(spans[0].endTime[0] - spans[0].startTime[0], 2); // hrTime UNIX Epoch time in seconds + assert.equal(spans[0].kind, 1, "Span Kind"); // Incoming + assert.equal(spans[0].attributes["http.method"], "HTTP"); + assert.equal(spans[0].attributes["http.status_code"], "401"); + assert.equal(spans[0].attributes["http.url"], "http://test.com"); + }); + + it("trackMetric", async () => { + const telemetry = { + name: "TestName", + value: 100, + }; + const exporter = new TestExporter({ connectionString: "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3330;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/" }); + const metricReaderOptions: PeriodicExportingMetricReaderOptions = { + exporter: exporter, + }; + const metricReader = new PeriodicExportingMetricReader(metricReaderOptions); + metricProvider.addMetricReader(metricReader); + client.trackMetric(telemetry); + metricProvider.forceFlush(); + await new Promise((resolve) => setTimeout(resolve, 800)); + assert.equal(testMetrics.scopeMetrics[0].metrics[0].descriptor.name, "TestName"); + assert.equal(testMetrics.scopeMetrics[0].metrics[0].descriptor.type, "HISTOGRAM"); + // @ts-ignore: TypeScript is not aware of the sum existing on the value object since it's a generic type + assert.equal(testMetrics.scopeMetrics[0].metrics[0].dataPoints[0].value.sum, 100); + }); + + it("trackAvailability", async () => { + const stub = sandbox.stub(logProcessor, "onEmit"); + const telemetry = { + id: "123456", + name: "TestName", + duration: 2000, // 2 seconds + success: true, + runLocation: "TestLocation", + message: "TestMessage" + }; + client.trackAvailability(telemetry); + await loggerProvider.forceFlush(); + await new Promise((resolve) => setTimeout(resolve, 800)); + assert.ok(stub.calledOnce); + }); + + it("trackPageView", async () => { + const stub = sandbox.stub(logProcessor, "onEmit"); + const telemetry = { + id: "123456", + name: "TestName", + url: "http://test.com", + }; + client.trackPageView(telemetry); + await loggerProvider.forceFlush(); + await new Promise((resolve) => setTimeout(resolve, 800)); + assert.ok(stub.calledOnce); + }); + + it("trackEvent", async () => { + const stub = sandbox.stub(logProcessor, "onEmit"); + const telemetry = { + name: "TestName", + }; + client.trackEvent(telemetry); + await loggerProvider.forceFlush(); + await new Promise((resolve) => setTimeout(resolve, 800)); + assert.ok(stub.calledOnce); + }); + + it("trackTrace", async () => { + const stub = sandbox.stub(logProcessor, "onEmit"); + const telemetry = { + message: "test message", + }; + client.trackTrace(telemetry); + await loggerProvider.forceFlush(); + await new Promise((resolve) => setTimeout(resolve, 800)); + assert.ok(stub.calledOnce); + }); + + it("trackException", async () => { + const stub = sandbox.stub(logProcessor, "onEmit"); + const telemetry = { + exception: new Error("test error"), + }; + client.trackException(telemetry); + await loggerProvider.forceFlush(); + await new Promise((resolve) => setTimeout(resolve, 800)); + assert.ok(stub.calledOnce); + }); + }); +}); \ No newline at end of file diff --git a/test/unitTests/shim/testUtils.ts b/test/unitTests/shim/testUtils.ts new file mode 100644 index 000000000..f2f6009b3 --- /dev/null +++ b/test/unitTests/shim/testUtils.ts @@ -0,0 +1,9 @@ +export function checkWarnings(warning: string, warnings: string[]) { + let expectedWarning: any; + for (let i = 0; i < warnings.length; i++) { + if (warnings[i].toString().includes(warning)) { + expectedWarning = warnings[i]; + } + } + return expectedWarning; +} diff --git a/tsconfig.json b/tsconfig.json index a8ea0fdde..73dca6839 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "ES2017", "module": "commonjs", "moduleResolution": "node", "inlineSources": true, @@ -9,21 +9,19 @@ "noImplicitAny": true, "outDir": "./out", "typeRoots": [ - "./node_modules/@types" + "./node_modules/@types", + "./src/types" ] }, "include": [ - "Bootstrap/*.ts", - "*.ts", - "AutoCollection/diagnostic-channel/*.ts", - "Tests/**/*.ts" - ], - "files": [ - "./node_modules/@types/cls-hooked/index.d.ts" + "applicationinsights.ts", + "src/**/*.ts", + "test/**/*.ts" ], "exclude": [ "node_modules", - "Tests/FunctionalTests", - "Tests/BackCompatibility" + "test/functionalTests", + "test/backCompatibility", + "test/performanceTests", ] -} +} \ No newline at end of file