diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/.gitignore b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/.gitignore index 8a33b835b7f..3d07732c592 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/.gitignore +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/.gitignore @@ -1,4 +1,6 @@ override.json package-lock.json VSIXPackageVersion.json -LICENSE \ No newline at end of file +LICENSE + +!Build \ No newline at end of file diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Build/Build.ts b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Build/Build.ts new file mode 100644 index 00000000000..453cdf19f31 --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Build/Build.ts @@ -0,0 +1,32 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* + * --------------------------------------------------------- + * Copyright(C) Microsoft Corporation. All rights reserved. + * --------------------------------------------------------- + */ + +import * as TfsCore from "../Core/Core"; + +/** + * Represents an attachment to a build. + */ +export interface Attachment { + _links: any; + /** + * The name of the attachment. + */ + name: string; +} + +/** + * Data representation of a build. + */ +export interface Build { + /** + * The ID of the build. + */ + id: number; + + project: TfsCore.TeamProjectReference; +} + diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Build/BuildClient.ts b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Build/BuildClient.ts new file mode 100644 index 00000000000..335bd815f95 --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Build/BuildClient.ts @@ -0,0 +1,43 @@ +/* + * --------------------------------------------------------- + * Copyright(C) Microsoft Corporation. All rights reserved. + * --------------------------------------------------------- + */ + +import { IVssRestClientOptions } from "../Common/Context"; +import { RestClientBase } from "../Common/RestClientBase"; + +import * as Build from "./Build"; + +export class BuildRestClient extends RestClientBase { + constructor(options: IVssRestClientOptions) { + super(options); + } + + public static readonly RESOURCE_AREA_ID = "965220d5-5bb9-42cf-8d67-9b146df2a5a4"; + + /** + * Gets the list of attachments of a specific type that are associated with a build. + * + * @param project - Project ID or project name + * @param buildId - The ID of the build. + * @param type - The type of attachment. + */ + public async getAttachments( + project: string, + buildId: number, + type: string + ): Promise { + + return this.beginRequest({ + apiVersion: "7.2-preview.2", + routeTemplate: "{project}/_apis/build/builds/{buildId}/attachments/{type}", + routeValues: { + project: project, + buildId: buildId, + type: type + } + }); + } + +} diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Build/index.ts b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Build/index.ts new file mode 100644 index 00000000000..5a66aeccfc4 --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Build/index.ts @@ -0,0 +1,2 @@ +export * from "./Build"; +export * from "./BuildClient"; \ No newline at end of file diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/Client.ts b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/Client.ts new file mode 100644 index 00000000000..f884b3d401f --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/Client.ts @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import { getAccessToken, getService } from "azure-devops-extension-sdk"; +import { IVssRestClientOptions } from "./Context"; +import { CommonServiceIds, ILocationService } from "./CommonServices"; + +/** + * A REST client factory is the method used to create and initialize an IVssRestClient. + */ +export type RestClientFactory = { + new (options: IVssRestClientOptions): T; + RESOURCE_AREA_ID?: string; +} + +export function getClient(clientClass: RestClientFactory, clientOptions?: IVssRestClientOptions) { + + const options = clientOptions || {}; + + if (!options.rootPath) { + options.rootPath = getService(CommonServiceIds.LocationService).then(locationService => { + if (clientClass.RESOURCE_AREA_ID) { + return locationService.getResourceAreaLocation(clientClass.RESOURCE_AREA_ID); + } + else { + return locationService.getServiceLocation(); + } + }); + } + + if (!options.authTokenProvider) { + options.authTokenProvider = { + getAuthorizationHeader: (): Promise => { + return getAccessToken().then(token => token ? ("Bearer " + token) : ""); + } + }; + } + + return new clientClass(options); +} \ No newline at end of file diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/CommonServices.ts b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/CommonServices.ts new file mode 100644 index 00000000000..cd66910eeb3 --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/CommonServices.ts @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/** + * Contribution ids of core DevOps services which can be obtained from DevOps.getService + */ +export const enum CommonServiceIds { + + /** + * Service for getting URLs/locations from the host context + */ + LocationService = "ms.vss-features.location-service", + +} + +/** + * Host level for a VSS service + */ +export const enum TeamFoundationHostType { + /** + * The Deployment host + */ + Deployment = 1, + + /** + * The Enterprise host + */ + Enterprise = 2, + + /** + * The organization/project collection host + */ + Organization = 4 +} + +/** + * Service for external content to get locations + */ +export interface ILocationService { + + /** + * Gets the URL for the given REST resource area + * + * @param resourceAreaId - Id of the resource area + */ + getResourceAreaLocation(resourceAreaId: string): Promise; + + /** + * Gets the location of a remote service at a given host type. + * + * @param serviceInstanceType - The GUID of the service instance type to lookup + * @param hostType - The host type to lookup (defaults to the host type of the current page data) + */ + getServiceLocation(serviceInstanceType?: string, hostType?: TeamFoundationHostType): Promise; + + /** + * Attemps to create a url for the specified route template and paramaters. The url will include host path. + * For example, if the page url is https://dev.azure.com/foo and you try to create admin settings url for project "bar", + * the output will be /foo/bar/_admin. + * + * This will asynchronously fetch a route contribution if it has not been included in page data. + * + * @param routeId - Id of the route contribution + * @param routeValues - Route value replacements + * @param hostPath - Optional host path to use rather than the default host path for the page. + */ + routeUrl(routeId: string, routeValues?: { [key: string]: string }, hostPath?: string): Promise; +} diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/Context.d.ts b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/Context.d.ts new file mode 100644 index 00000000000..72fdf875428 --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/Context.d.ts @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/** + * Interface for a class that can retrieve authorization tokens to be used in fetch requests. + */ +export interface IAuthorizationTokenProvider { + /** + * Gets the authorization header to use in a fetch request + * + * @param forceRefresh - If true, indicates that we should get a new token, if applicable for current provider. + * @returns the value to use for the Authorization header in a request. + */ + getAuthorizationHeader(forceRefresh?: boolean): Promise; +} + +/** + * Options for a specific instance of a REST client. + */ +export interface IVssRestClientOptions { + + /** + * Auth token manager that can be used to get and attach auth tokens to requests. + * If not supplied, the default token provider is used if the serviceInstanceType option is supplied + * and is different from the hosting page's service instance type. + */ + authTokenProvider?: IAuthorizationTokenProvider; + + /** + * The root URL path to use for this client. Can be relative or absolute. + */ + rootPath?: string | Promise; + + /** + * Current session id. + */ + sessionId?: string; + + /** + * Current command for activity logging. + */ + command?: string; +} \ No newline at end of file diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/Fetch.ts b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/Fetch.ts new file mode 100644 index 00000000000..ba5fac80cee --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/Fetch.ts @@ -0,0 +1,238 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Fetch polyfill for IE11 +// import "whatwg-fetch"; + +import { IAuthorizationTokenProvider } from "./Context"; + +/** +* VSS-specific options for VSS requests +*/ +export interface IVssRequestOptions { + + /* + * Auth token manager that can be used to get and attach auth tokens to requests + */ + authTokenProvider?: IAuthorizationTokenProvider; + + /** + * Current session id. + */ + sessionId?: string; + + /** + * Current command for activity logging. + */ + command?: string; +} + +/** + * An IPreRequestEvent is sent before a fetch request is made. + */ +export interface IPreRequestEvent { + + /** + * A unique id that can be used to track this particular request (id is unique among all clients) + */ + requestId: number; + + /** + * Url of the request that is about to be issued + */ + requestUrl: string; + + /** + * Request settings being used + */ + options?: RequestInit; + + /** + * Additional VSS-specific options supplied in the request + */ + vssRequestOptions?: IVssRequestOptions; +} + +/** + * An IPostRequestEvent is sent after a fetch request has completed. + */ +export interface IPostRequestEvent { + + /** + * A unique id that can be used to track this particular request (id is unique among all clients) + */ + requestId: number; + + /** + * Url of the request that is about to be issued + */ + requestUrl: string; + + /** + * The Response returned for this request, if the request fails it will be undefined + */ + response?: Response; + + /** + * Additional VSS-specific options supplied in the request + */ + vssRequestOptions?: IVssRequestOptions; +} + +/** + * When a fetch request fails, it will throw a VssServerError. Failure is defined + * as a request that made it to the server, and the server successfully responded + * with a failure. This will be any status return that is not a status code in + * the success range (200-299). + */ +export interface VssServerError extends Error { + + /** + * The status code returned from the server. + */ + status: number; + + /** + * The raw text that was returned from the server. If any is available. + */ + responseText: string; + + /** + * If the response text was sent and it was in the form of a JSON response + * the object will be parsed and deserialized object is available here. + * + * This commonly has the exception details about the failure from the server. + * Things like the message, exception type, and stack trace will be available. + */ + serverError?: any; +} + +/** + * Issue a fetch request. This is a wrapper around the browser fetch method that handles VSS authentication + * and triggers events that can be listened to by other modules. + * + * @param requestUrl Url to send the request to + * @param options fetch settings/options for the request + * @param vssRequestOptions VSS specific request options + * + * Triggered Events: + * afterRequest -> IPostRequestEvent is sent after the request has completed. + * beforeRequest -> IPreRequestEvent is sent before the request is made. + */ +export async function issueRequest(requestUrl: string, options?: RequestInit, vssRequestOptions?: IVssRequestOptions): Promise { + let response: Response | undefined = undefined; + + // Add a X-VSS-ReauthenticationAction header to all fetch requests + if (!options) { + options = {}; + } + + let headers: Headers; + + // If options.headers is set, check if it is a Headers object, and if not, convert it to one. + if (options.headers) { + if (options.headers instanceof Headers) { + headers = options.headers; + } + else { + headers = new Headers(); + if (typeof options.headers.hasOwnProperty === "function") { + for (const key in options.headers) { + if (Object.prototype.hasOwnProperty.call(options.headers, key)) { + headers.append(key, (options.headers as { [key: string]: string })[key]); + } + } + } + } + } + else { + headers = new Headers(); + } + options.headers = headers; + + headers.append("X-VSS-ReauthenticationAction", "Suppress"); + + // Add a X-TFS-Session header with the current sessionId and command for correlation + if (vssRequestOptions) { + const sessionId = vssRequestOptions.sessionId; + const command = vssRequestOptions.command; + + if (sessionId) { + if (command) { + headers.append("X-TFS-Session", sessionId + "," + command); + } + else { + headers.append("X-TFS-Session", sessionId); + } + } + } + + // Send credentials to the same origin, we will use tokens for differing origins. + options.credentials = "same-origin"; + + let refreshToken = false; + + do { + + // Ensure we have an authorization token available from the auth manager. + if (vssRequestOptions && vssRequestOptions.authTokenProvider) { + const authHeader = await vssRequestOptions.authTokenProvider.getAuthorizationHeader(refreshToken); + if (authHeader) { + headers.append("Authorization", authHeader); + refreshToken = true; + } + headers.append("X-TFS-FedAuthRedirect", "Suppress"); + } + + // Execute the http request defined by the caller. + try { + response = await fetch(requestUrl, options); + } + catch (ex) { + console.warn(`Unhandled exception in fetch for ${requestUrl}: ${ex && ex.toString()}`); + const error = new Error("TF400893: Unable to contact the server. Please check your network connection and try again."); + error.name = "NetworkException"; + throw error; + } + + // If we recieved a 401 and have a token manager, we will refresh our token and try again. + if (response.status === 401 && !refreshToken && vssRequestOptions && vssRequestOptions.authTokenProvider) { + refreshToken = true; + continue; + } + + // eslint-disable-next-line no-constant-condition + } while (false); + + // Parse error details from requests that returned non 200-299 status codes. + if (!response.ok) { + + // Create a VssServerError and throw it as the result of the fetch request. + const error = new Error() as VssServerError; + error.name = "TFS.WebApi.Exception"; + error.status = response.status; + error.responseText = await response.text(); + + // Attempt to parse the response as a json object, for many of the failures + // the server will serialize details into the body. + if (error.responseText && error.responseText !== "") { + try { + error.serverError = JSON.parse(error.responseText); + if (error.serverError && error.serverError.message) { + error.message = error.serverError.message; + } + } + catch { /* empty */ } + } + + if (!error.message) { + error.message = response.status + ": " + response.statusText; + } + + throw error; + } + + return response; +} \ No newline at end of file diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/RestClientBase.ts b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/RestClientBase.ts new file mode 100644 index 00000000000..482430ff47a --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/RestClientBase.ts @@ -0,0 +1,185 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import { IVssRestClientOptions } from "./Context"; +import { issueRequest, IVssRequestOptions } from "./Fetch"; +import { deserializeVssJsonObject } from "./Util/Serialization"; +import { replaceRouteValues, Uri } from "./Util/Url"; + +/** +* Parameters for sending a WebApi request +*/ +export interface RestClientRequestParams { + + /** + * Route template that is used to form the request path. If routeTemplate is NOT specified, then locationId + * is used to lookup the template via an OPTIONS request. + */ + routeTemplate: string; + + /** + * The api version string to send in the request (e.g. "1.0" or "2.0-preview.2") + */ + apiVersion: string; + + /** + * Dictionary of route template replacement values + */ + routeValues?: { [key: string]: any; }; + + /** + * Data to post. In this case of a GET, this indicates query parameters. + * For other requests, this is the request body object (which will be serialized + * into a JSON string unless isRawData is set to true). + */ + body?: any; + + /** + * Query parameters to add to the url. In the case of a GET, query parameters can + * be supplied via 'data' or 'queryParams'. For other verbs such as POST, the + * data object specifies the POST body, so queryParams is needed to indicate + * parameters to add to the query string of the url (not included in the post body). + */ + queryParams?: { [key: string]: any; }; + + /** + * HTTP verb (GET by default if not specified) + */ + method?: string; + + /** + * The http response (Accept) type. This is "json" (corresponds to application/json Accept header) + * unless otherwise specified. Other possible values are "html", "text", "zip", or "binary" or their accept + * header equivalents (e.g. application/zip). + */ + httpResponseType?: string; + + /** + * Allows the caller to specify custom request headers. + */ + customHeaders?: { [headerName: string]: any; }; + + /** + * If true, indicates that the raw Response should be returned in the request's resulting promise + * rather than deserializing the response (the default). + */ + returnRawResponse?: boolean; + + /** + * If true, this indicates that no processing should be done on the 'data' object + * before it is sent in the request. *This is rarely needed*. One case is when posting + * an HTML5 File object. + */ + isRawData?: boolean; + + /** + * Current command for activity logging. This will override the RestClient's base option. + */ + command?: string; +} + +/** +* Base class that should be used (derived from) to make requests to VSS REST apis +*/ +export class RestClientBase { + + private _options: IVssRestClientOptions; + private _rootPath: Promise; + + constructor(options: IVssRestClientOptions) { + + this._options = options || {}; + + if (typeof this._options.rootPath === "string") { + this._rootPath = Promise.resolve(this._options.rootPath); + } + else { + this._rootPath = this._options.rootPath || Promise.resolve("/"); + } + } + + /** + * Gets the root path of the Service + * + * @returns Promise for the resolving the root path of the service. + */ + protected async getRootPath(): Promise { + return this._rootPath; + } + + /** + * Issue a request to a VSS REST endpoint. + * + * @param requestParams request options + * @returns Promise for the response + */ + protected async beginRequest(requestParams: RestClientRequestParams): Promise { + return this._rootPath.then((rootPath: string) => { + + let requestUrl = rootPath + replaceRouteValues(requestParams.routeTemplate, requestParams.routeValues || {}); + if (requestParams.queryParams) { + const uri = new Uri(requestUrl); + uri.addQueryParams(requestParams.queryParams); + requestUrl = uri.absoluteUri; + } + + return this._issueRequest(requestUrl, requestParams.apiVersion, requestParams); + }); + } + + /** + * Issue a request to a VSS REST endpoint at the specified location + * + * @param requestUrl Resolved URL of the request + * @param apiVersion API version + * @param requestParams Optional request parameters + */ + protected _issueRequest(requestUrl: string, apiVersion: string, requestParams: RestClientRequestParams): Promise { + + const fetchOptions: RequestInit = {}; + + fetchOptions.method = requestParams.method || "GET"; + fetchOptions.mode = "cors"; + + if (!requestParams.isRawData && requestParams.body && fetchOptions.method.toUpperCase() !== 'GET') { + fetchOptions.body = JSON.stringify(requestParams.body); + } + else { + fetchOptions.body = requestParams.body; + } + + const acceptType = requestParams.httpResponseType || "application/json"; + const acceptHeaderValue = `${acceptType};api-version=${apiVersion};excludeUrls=true;enumsAsNumbers=true;msDateFormat=true;noArrayWrap=true`; + + fetchOptions.headers = Object.assign({ + "Accept": acceptHeaderValue, + "Content-Type": requestParams.body && "application/json" + }, requestParams.customHeaders) as any /* lib.dom.d.ts does not have the correct type for Headers */; + + const vssRequestOptions = { + authTokenProvider: this._options.authTokenProvider, + sessionId: this._options.sessionId, + command: requestParams.command || this._options.command + } as IVssRequestOptions; + + const result = issueRequest(requestUrl, fetchOptions, vssRequestOptions); + return result.then((response: Response) => { + + if (requestParams.returnRawResponse) { + return response; + } + else if (acceptType.toLowerCase().indexOf("json") >= 0) { + // MSJSON date formats must be replaced in the raw text before JSON parsing + return response.text().then(deserializeVssJsonObject); + } + else if (acceptType.toLowerCase() === "text/plain") { + return response.text(); + } + else { + return response.arrayBuffer(); + } + }); + } +} \ No newline at end of file diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/Util/Serialization.ts b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/Util/Serialization.ts new file mode 100644 index 00000000000..8afe3226365 --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/Util/Serialization.ts @@ -0,0 +1,60 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* + * --------------------------------------------------------- + * Copyright(C) Microsoft Corporation. All rights reserved. + * --------------------------------------------------------- + */ + +const msAjaxDateRegEx = new RegExp('(^|[^\\\\])\\"\\\\/Date\\((-?[0-9]+)(?:[a-zA-Z]|(?:\\+|-)[0-9]{4})?\\)\\\\/\\"', 'g'); + +/** + * Handle the raw text of a JSON response which may contain MSJSON style dates and + * deserialize (JSON.parse) the data in a way that restores Date objects rather than + * strings. + * + * MSJSON style dates are in the form: + * + * "lastModified": "\/Date(1496158224000)\/" + * + * This format unnecessarily (but intentionally) escapes the "/" character. So the parsed + * JSON will have no trace of the escape character (\). + * + * @param text The raw JSON text + */ +export function deserializeVssJsonObject(text: string): T | null { + + function replaceMsJsonDates(object: any, parentObject: any, parentObjectKey: string) { + + if (parentObject && typeof object.__msjson_date__ === "number") { + parentObject[parentObjectKey] = new Date(object.__msjson_date__); + return; + } + + for (const key in object) { + const value = object[key]; + if (value !== null && typeof value === "object") { + replaceMsJsonDates(object[key], object, key); + } + } + } + + let deserializedData: T | null = null; + + if (text) { + + // Replace MSJSON dates with an object that we can easily identify after JSON.parse. + // This replaces the string value (like "\/Date(1496158224000)\/") with a JSON object that + // has an "__msjson_date__" key. + const replacedText = text.replace(msAjaxDateRegEx, "$1{\"__msjson_date__\":$2 }"); + + // Do the actual JSON deserialization + deserializedData = JSON.parse(replacedText); + + // Go through the parsed object and create actual Date objects for our replacements made above + if (replacedText !== text) { + replaceMsJsonDates(deserializedData, null, ""); + } + } + + return deserializedData; +} \ No newline at end of file diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/Util/Url.ts b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/Util/Url.ts new file mode 100644 index 00000000000..5c39c2aff73 --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/Util/Url.ts @@ -0,0 +1,773 @@ +/* + * --------------------------------------------------------- + * Copyright(C) Microsoft Corporation. All rights reserved. + * --------------------------------------------------------- + */ + +/** + * Key constants used by route parsing. + */ +const enum KeyCodes { + asterisk = 42, + endCurlyBrace = 125, + startCurlyBrace = 123 +} + +/** +* A single query parameter entry in a Uri +*/ +export interface IQueryParameter { + + /** + * Unencoded name of the query parameter + */ + name: string; + + /** + * Unencoded value of the query parameter + */ + value: string | null; +} + +/** +* Options for parsing a Uri string +*/ +export interface IUriParseOptions { + + /** + * If true, throw if the Uri is not absolute + */ + absoluteUriRequired?: boolean; +} + +/** + * Type of individual entry values that can be used in Uri.addQueryParams call + */ +export type QueryParameterEntryValueType = string | boolean | number | Date | undefined; + +/** + * Type of values supported by Uri.addQueryParams call + */ +export type QueryParameterValueType = + | QueryParameterEntryValueType + | Array + | { [key: string]: QueryParameterEntryValueType }; + +function prepareForComparison(value: string, upperCase: boolean): string { + return value ? (upperCase ? value.toLocaleUpperCase() : value) : ""; +} + +function stringEquals(str1: string, str2: string, ignoreCase: boolean): boolean { + if (str1 === str2) { + return true; + } + return prepareForComparison(str1, ignoreCase).localeCompare(prepareForComparison(str2, ignoreCase)) === 0; +} + +/** +* Class that represents a Uri and allows parsing/getting and setting of individual parts +*/ +export class Uri { + + /** + * The uri scheme such as http or https + */ + public scheme: string = ""; + + /** + * If true, do not emit the "//" separator after the scheme: + * Set to true for schemes like mailto (e.g. mailto:foo@bar) + */ + public noSchemeSeparator: boolean = false; + + /** + * The uri hostname (does not include port or scheme) + */ + public host: string = ""; + + /** + * The port number of the uri as supplied in the url. 0 if left out in the url (e.g. the default port for the scheme). + */ + public port: number = 0; + + /** + * The relative path of the uri + */ + public path: string = ""; + + /** + * The array of query parameters in the uri + */ + public queryParameters: IQueryParameter[] = []; + + /** + * The hash string of the uri + */ + public hashString: string = ""; + + /** + * Create a new Uri. + * + * @param uri Optional uri string to populate values with + * @param options Options for parsing the uri string + */ + constructor(uri?: string, options?: IUriParseOptions) { + if (uri) { + this._setFromUriString(uri, options); + } + } + + private _setFromUriString(uriString: string, options?: IUriParseOptions) { + let uri = uriString; + + // Parse out the hash string + const hashSplit = this._singleSplit(uri, "#"); + if (hashSplit) { + uri = hashSplit.part1; + this.hashString = this._decodeUriComponent(hashSplit.part2); + } + else { + this.hashString = ""; + } + + // Parse the query parameters + const querySplit = this._singleSplit(uri, "?"); + if (querySplit) { + uri = querySplit.part1; + this.queryString = querySplit.part2; + } + else { + this.queryParameters = []; + } + + this.scheme = ""; + this.host = ""; + this.port = 0; + this.path = ""; + + // Parse out the scheme components of the uri + this.noSchemeSeparator = false; + const schemeSplit = this._singleSplit(uri, ":"); + if (schemeSplit) { + this.scheme = schemeSplit.part1; + uri = schemeSplit.part2; + + if (uri.substr(0, 2) === "//") { + uri = uri.substr(2); + + // Parse out the path part of the uri + const pathSplit = this._singleSplit(uri, "/"); + if (pathSplit) { + uri = pathSplit.part1; + this.path = pathSplit.part2; + } + else { + this.path = ""; + } + + // Parse out the port number + const portSplit = this._singleSplit(uri, ":"); + if (portSplit) { + this.host = portSplit.part1; + this.port = parseInt(portSplit.part2); + + if (isNaN(this.port)) { + // Segment after : was not a port, consider it part of the path + this.host += ":"; + this.path = portSplit.part2 + "/" + this.path; + } + } + else { + this.host = uri; + } + } + else { + // No host for schemes like mailto: just use path + this.noSchemeSeparator = true; + this.path = uri; + } + } + else { + // Relative Url was given + this.path = uri; + } + + if (options && options.absoluteUriRequired && !this.scheme) { + throw new Error(`The uri string "${uriString}" does not represent a valid absolute uri.`); + } + } + + private _singleSplit(value: string, separator: string): { part1: string, part2: string } | undefined { + const matchIndex = value.indexOf(separator); + if (matchIndex >= 0) { + return { + part1: value.substr(0, matchIndex), + part2: value.substr(matchIndex + 1) + }; + } + else { + return undefined; + } + } + + private _decodeUriComponent(value: string): string { + if (value) { + // Replace "+" character with %20. + value = value.replace(/\+/g, "%20"); + value = decodeURIComponent(value); + } + return value; + } + + /** + * Get the absolute uri string for this Uri + */ + public get absoluteUri(): string { + let uri = ""; + + if (this.scheme) { + uri = encodeURI(decodeURI(this.scheme)) + ":"; + if (!this.noSchemeSeparator) { + uri += "//"; + } + } + + if (this.host) { + uri += encodeURI(decodeURI(this.host)); + + if (this.port) { + uri += ":" + this.port; + } + + if (!this.noSchemeSeparator || this.path) { + uri += "/"; + } + } + + if (this.path) { + let encodedPath: string; + if (this.noSchemeSeparator) { + // Only do simple encoding for schemes like mailto: or blob: where + // we can't determine host versus path + encodedPath = encodeURI(decodeURI(this.path)); + } + else { + const parts = this.path.split('/'); + encodedPath = parts.map(p => encodeURIComponent(decodeURIComponent(p))).join("/"); + } + + if (this.host) { + uri = combineUrlPaths(uri, encodedPath); + } + else { + uri = uri + encodedPath; + } + } + + const queryString = this.queryString; + if (queryString) { + uri += "?" + queryString; + } + + if (this.hashString) { + const params = this._splitStringIntoParams(this.hashString); + const encodedString = this._getParamsAsString(params); + uri += "#" + encodedString; + } + + return uri; + } + + /** + * Set the absolute uri string for this Uri. Replaces all existing values + */ + public set absoluteUri(uri: string) { + this._setFromUriString(uri || ""); + } + + /** + * Gets the effective port number, returning the default port number if omitted for the given scheme. + */ + public getEffectivePort(): number { + if (this.port) { + return this.port; + } + else { + if (stringEquals(this.scheme, "http", true)) { + return 80; + } + else if (stringEquals(this.scheme, "https", true)) { + return 443; + } + else { + return 0; + } + } + } + + /** + * Builds an encoded key/value pair string + * like query string or hash strings + */ + private _getParamsAsString(params: IQueryParameter[]): string { + if (params && params.length) { + return params.map((param) => { + if (param.value !== null) { + return encodeURIComponent(param.name) + "=" + encodeURIComponent(param.value); + } + else { + return encodeURIComponent(param.name); + } + }).join("&"); + } + else { + return ""; + } + } + + /** + * Get the query string for this Uri. + */ + public get queryString(): string { + return this._getParamsAsString(this.queryParameters); + } + + /** + * Set the query string for this Uri. Replaces existing value + */ + public set queryString(queryString: string) { + this.queryParameters = this._splitStringIntoParams(queryString); + } + + /** + * Coverts a key/value pair string into parameters array + * @param paramString String such as a=b&c=d + */ + private _splitStringIntoParams(paramString: string): IQueryParameter[] { + const params: IQueryParameter[] = []; + paramString.split('&').forEach((pair) => { + if (pair) { + const valueSplit = this._singleSplit(pair, "="); + if (valueSplit) { + params.push({ + name: this._decodeUriComponent(valueSplit.part1), + value: this._decodeUriComponent(valueSplit.part2) + }); + } + else { + params.push({ + name: this._decodeUriComponent(pair), + value: null + }); + } + } + }); + + return params; + } + + /** + * Get the value of the query parameter with the given key + * + * @param name Query parameter name + */ + public getQueryParam(name: string): string | null | undefined { + let value: string | undefined | null; + if (this.queryParameters) { + const matchingPairs = this.queryParameters.filter(p => stringEquals(p.name, name, true)); + if (matchingPairs.length > 0) { + value = matchingPairs[0].value; + } + } + return value; + } + + /** + * Adds a query string parameter to the current uri + * + * @param name The Query parameter name + * @param value The Query parameter value + * @param replaceExisting If true, replace all existing parameters with the same name + */ + public addQueryParam(name: string, value: string | null, replaceExisting?: boolean): void { + if (replaceExisting) { + this.removeQueryParam(name); + } + if (!this.queryParameters) { + this.queryParameters = []; + } + this.queryParameters.push({ name: name, value: value }); + } + + /** + * Adds query string parameters to the current uri + * + * @param parameters Query parameters to add + * @param replaceExisting If true, replace all existing parameters with the same name + * @param keyPrefix If specified, a value to prepend to all query parameter keys + */ + public addQueryParams(parameters: { [key: string]: QueryParameterValueType }, replaceExisting?: boolean, keyPrefix?: string): void { + for (const key in parameters) { + const value = parameters[key]; + if (value !== null && value !== undefined) { + const keyWithPrefix = (keyPrefix || "") + key; + if (value instanceof Date) { + this.addQueryParam(keyWithPrefix, value.toJSON(), replaceExisting); + } else if (Array.isArray(value)) { + value.forEach(v => this.addQueryParam(keyWithPrefix, "" + v, replaceExisting)); + } else if (typeof value === "object") { + this.addQueryParams(value, replaceExisting, keyWithPrefix + "."); + } else { + this.addQueryParam(keyWithPrefix, "" + value, replaceExisting); + } + } + } + } + + /** + * Removes a query string parameter + * + * @param name The Query parameter name + */ + public removeQueryParam(name: string): void { + if (this.queryParameters) { + this.queryParameters = this.queryParameters.filter(p => !stringEquals(p.name, name, true)); + } + } +} + +/** + * Take url segments and join them with a single slash character. Takes care of path segements that start and/or end + * with a slash to ensure that the resulting URL does not have double-slashes + * + * @param paths Path segments to concatenate + */ +export function combineUrlPaths(...paths: string[]): string { + + const segmentsToJoin: string[] = []; + + // Trim leading and trailing slash in each segment + for (let i = 0, last = paths.length - 1; i <= last; i++) { + let path = paths[i]; + if (path) { + if (path === "/" && (i === 0 || i === last)) { + // For a "/" segment at the beginning or end of the list, insert an empty entry to force + // a leading or trailing slash in the resulting URL + segmentsToJoin.push(""); + } + else { + if (i > 0 && path[0] === "/") { + // Trim leading slash in any segment except the first one + path = path.substr(1); + } + if (i < last && path[path.length - 1] === "/") { + // Trim trailing slash in any segment except the last one + path = path.substr(0, path.length - 1); + } + if (path) { + segmentsToJoin.push(path); + } + } + } + } + + if (segmentsToJoin.length === 1 && segmentsToJoin[0] === "") { + return "/"; + } + + // Join segments by slash + return segmentsToJoin.join("/"); +} + +/** + * Represents a route parsed by parseRoute + */ +export interface IParsedRoute { + + /** + * Array of the segements in the route + */ + segments: IParsedRouteSegment[]; +} + +/** + * And individual segment of the route (fixed text or a parameter) + */ +export interface IParsedRouteSegment { + + /** + * If present, the fixed text for this segement. Either text or paramName will be defined for a segment, never both. + */ + text?: string; + + /** + * If present, the name of the route value parameter to substitute for this segment. Either text or paramName will be defined for a segment, never both. + */ + paramName?: string; + + /** + * For parameters, whether or not this parameter is a wildcard (*) parameter, which means it allows multiple path segments (i.e. don't escape "/") + */ + isWildCardParam?: boolean; + + /** + * Whether the parameter is required in order for the URL to be valid. + */ + isRequiredParam?: boolean; +} + +/** + * Parse a route template into a structure that can be used to quickly do route replacements + * + * @param routeTemplate MVC route template string (like "/foo/{id}/{*params}") + */ +export function parseRouteTemplate(routeTemplate: string): IParsedRoute { + + const parsedRoute: IParsedRoute = { + segments: [] + }; + + let paramStartIndex = -1; + let segmentStartIndex = -1; + let segmentPrefix = ""; + + for (let charIndex = 0, routeTemplateLen = routeTemplate.length; charIndex < routeTemplateLen; charIndex++) { + const c = routeTemplate.charCodeAt(charIndex); + + if (paramStartIndex >= 0) { + if (c === KeyCodes.endCurlyBrace) { + + let paramName = routeTemplate.substring(paramStartIndex, charIndex); + let isWildCardParam = false; + + if (paramName.charCodeAt(0) === KeyCodes.asterisk) { + paramName = paramName.substr(1); + isWildCardParam = true; + } + + parsedRoute.segments.push({ + paramName, + isWildCardParam + }); + + paramStartIndex = -1; + } + } + else { + if (c === KeyCodes.startCurlyBrace && routeTemplate.charCodeAt(charIndex + 1) !== KeyCodes.startCurlyBrace) { + // Start of a parameter + if (segmentPrefix || segmentStartIndex >= 0) { + + // Store the previous segment + let segmentText = segmentPrefix; + if (segmentStartIndex >= 0) { + segmentText += routeTemplate.substring(segmentStartIndex, charIndex); + } + + if (segmentText) { + parsedRoute.segments.push({ + text: segmentText + }); + } + + // Reset the segment tracking info + segmentStartIndex = -1; + segmentPrefix = ""; + } + paramStartIndex = charIndex + 1; + } + else { + + // Handle double {{ or double }} as an escape sequence. This is rare. For simplicity we will + if ((c === KeyCodes.startCurlyBrace && routeTemplate.charCodeAt(charIndex + 1) === KeyCodes.startCurlyBrace) || + (c === KeyCodes.endCurlyBrace && routeTemplate.charCodeAt(charIndex + 1) === KeyCodes.endCurlyBrace)) { + + segmentPrefix = segmentPrefix + routeTemplate.substring(segmentStartIndex >= 0 ? segmentStartIndex : charIndex, charIndex + 1); + segmentStartIndex = -1; + charIndex++; + } + + if (segmentStartIndex < 0) { + segmentStartIndex = charIndex; + } + } + } + } + + // Store any pending segment + if (segmentStartIndex >= 0 || paramStartIndex >= 0) { + const segmentText = segmentPrefix + routeTemplate.substring(segmentStartIndex >= 0 ? segmentStartIndex : paramStartIndex); + if (segmentText) { + parsedRoute.segments.push({ + text: segmentText + }); + } + } + + // Mark any param as required if it has a text segment (other than just "/") after it + let required = false; + for (let i = parsedRoute.segments.length - 1; i >= 0; i--) { + const segment = parsedRoute.segments[i]; + if (segment.text && segment.text !== "/") { + required = true; + } + else if (required && segment.paramName) { + segment.isRequiredParam = true; + } + } + + return parsedRoute; +} + +/** + * Take a set of routes and route values and form a url using the best match. The best match + * is the route with the highest number of replacements (of the given routeValues dictionary). + * In the event of a tie (same number of replacements) the route that came first wins. + * + * @param routeCollection Array of parsed routes + * @param routeValues Replacement values + */ +export function routeUrl(routeCollection: IParsedRoute[], routeValues: { [name: string]: string }): string { + + const bestMatch = getBestRouteMatch(routeCollection, routeValues); + if (!bestMatch) { + return ""; + } + + const uri = new Uri(bestMatch.url); + for (const routeValueKey in routeValues) { + if (!bestMatch.matchedParameters[routeValueKey]) { + uri.addQueryParam(routeValueKey, routeValues[routeValueKey]); + } + } + + return uri.absoluteUri; +} + +/** + * Take a set of routes and find the best match. The best match is the route with the highest number of replacements + * (of the given routeValues dictionary). In the event of a tie (same number of replacements) the route that came first wins. + * + * @param routeCollection Array of parsed routes + * @param routeValues Replacement values + */ +export function getBestRouteMatch(routeCollection: IParsedRoute[], routeValues: { [name: string]: string }): IRouteMatchResult | undefined { + + let bestMatch: IRouteMatchResult | undefined; + const totalRouteValues = Object.keys(routeValues).length; + + for (const route of routeCollection) { + const match = replaceParsedRouteValues(route, routeValues, false); + if (match && (!bestMatch || match.matchedParametersCount > bestMatch.matchedParametersCount)) { + bestMatch = match; + + if (match.matchedParametersCount === totalRouteValues) { + // This route matched all route values. Return its url directly (no need to even add query params) + return bestMatch; + } + } + } + + return bestMatch; +} + +/** + * Result of a call to replace route values for a parsed route + */ +export interface IRouteMatchResult { + + /** + * Resulting URL from the template replacement. Does NOT include any query parameters that would be added from extra route values. + */ + url: string; + + /** + * Dictionary of the route value keys that were used as replacements + */ + matchedParameters: { [key: string]: boolean }; + + /** + * The number of parameter replacements made + */ + matchedParametersCount: number; +} + +/** + * Replace route values for a specific parsed route + * + * @param parsedRoute The route to evaluate + * @param routeValues Dictionary of route replacement parameters + * @param continueOnUnmatchedSegements If true, continue with replacements even after a miss. By default (false), stop replacements once a parameter is not present. + */ +export function replaceParsedRouteValues(parsedRoute: IParsedRoute, routeValues: { [name: string]: string | number | undefined }, continueOnUnmatchedSegements?: boolean): IRouteMatchResult | undefined { + + const urlParts: string[] = []; + const matchedParameters: { [key: string]: boolean } = {}; + let matchedParametersCount = 0; + + for (let segmentIndex = 0, l = parsedRoute.segments.length; segmentIndex < l; segmentIndex++) { + const segment = parsedRoute.segments[segmentIndex]; + + if (segment.text) { + let segmentText = segment.text; + if (continueOnUnmatchedSegements) { + // Make sure we don't have consecutive slash (/) characters in the case of missing segments + if (segmentIndex > 0 && segmentText.charAt(0) === "/") { + if (urlParts.length === 0) { + // First text segment after one or more missing parameter segments. Remove the leading slash. + segmentText = segmentText.substr(1); + } + } + } + + if (segmentText) { + urlParts.push(segmentText); + } + } + else { + const value = routeValues[segment.paramName!]; + if (!value && value !== 0) { + // The route value was not supplied + if (!continueOnUnmatchedSegements) { + if (segment.isRequiredParam) { + // This is a required parameter. Return undefined since this route was not a match. + return undefined; + } + else { + // This is an omitted optional parameter. Return what we've computed so far. + break; + } + } + else if (urlParts.length) { + // Unmatched segment being omitted. Remove any previously trailing slash + const lastSegment = urlParts[urlParts.length - 1]; + if (lastSegment[lastSegment.length - 1] === "/") { + urlParts[urlParts.length - 1] = lastSegment.substr(0, lastSegment.length - 1); + } + } + } + else { + urlParts.push(segment.isWildCardParam ? encodeURI("" + value) : encodeURIComponent("" + value)); + matchedParameters[segment.paramName!] = true; + matchedParametersCount++; + } + } + } + + return { + url: urlParts.join(""), + matchedParameters, + matchedParametersCount + }; +} + +/** + * Take an MVC route template (like "/foo/{id}/{*params}") and replace the templated parts with values from the given dictionary + * + * @param routeTemplate MVC route template (like "/foo/{id}/{*params}") + * @param routeValues Route value replacements + */ +export function replaceRouteValues(routeTemplate: string, routeValues: { [name: string]: string | number | undefined }): string { + const parsedRoute = parseRouteTemplate(routeTemplate); + return replaceParsedRouteValues(parsedRoute, routeValues, true)!.url; +} \ No newline at end of file diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/index.ts b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/index.ts new file mode 100644 index 00000000000..50da37ec3a3 --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Common/index.ts @@ -0,0 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +export * from "./Client"; +export * from "./CommonServices"; +export type * from "./Context.d.ts"; \ No newline at end of file diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Core/Core.ts b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Core/Core.ts new file mode 100644 index 00000000000..917d6587cc1 --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Core/Core.ts @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/** + * Represents a shallow reference to a TeamProject. + */ +export interface TeamProjectReference { + /** + * Project name. + */ + name: string; +} + diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Core/index.ts b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Core/index.ts new file mode 100644 index 00000000000..cba4ff7efaf --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/Core/index.ts @@ -0,0 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +export * from "./Core"; \ No newline at end of file diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/README.md b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/README.md new file mode 100644 index 00000000000..058a64ba925 --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/README.md @@ -0,0 +1,8 @@ +### Why is this here? + +The [azure-devops-extension-api](https://github.com/microsoft/azure-devops-extension-api/) is built as an AMD package, which is not compatible with +modern web packaging. It would be best if the package +[included ESM modules](https://github.com/microsoft/azure-devops-extension-api/issues/109). + +Since we only use a handful of calls to the API, we've included a pruned copy of the API sources as ES imports in this folder. +If the package is published as ES modules, we can import it instead. \ No newline at end of file diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/index.ts b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/index.ts new file mode 100644 index 00000000000..dff0f6a15a5 --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/azure-devops-extension-api/index.ts @@ -0,0 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +export * from "./Common/index"; \ No newline at end of file diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/main.tsx b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/main.tsx index 7ee4ee5d2f1..0e08ed49a0c 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/main.tsx +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/src/main.tsx @@ -1,14 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -/* eslint-disable @typescript-eslint/no-require-imports */ import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import App from '../../components/App.tsx'; import './index.css' -import * as SDK from "azure-devops-extension-sdk"; -import { getClient } from "azure-devops-extension-api"; -import { Build, Attachment, BuildRestClient } from "azure-devops-extension-api/Build"; +import { init, ready, getAccessToken, getConfiguration } from "azure-devops-extension-sdk"; +import { getClient } from "./azure-devops-extension-api"; +import { Build, Attachment, BuildRestClient } from "./azure-devops-extension-api/Build"; import { FluentProvider, webLightTheme } from '@fluentui/react-components'; import { createScoreSummary as createScoreSummary } from '../../components/Summary.ts'; import { ReportContextProvider } from '../../components/ReportContext.tsx'; @@ -35,7 +34,7 @@ const findReportAttachment = async (client: BuildRestClient, project: string, bu const getReportData = async (client: BuildRestClient, project: string, buildId: number) => { const att = await findReportAttachment(client, project, buildId); if (att) { - const accessToken = await SDK.getAccessToken(); + const accessToken = await getAccessToken(); const encodedToken = btoa(":" + accessToken); const headers = { headers: { Authorization: `Basic ${encodedToken}` } }; const response = await fetch(att, headers); @@ -49,16 +48,15 @@ const getReportData = async (client: BuildRestClient, project: string, buildId: } return dataset; } - -}; +}; const run = async () => { - await SDK.init(); - await SDK.ready(); + await init(); + await ready(); - const config = SDK.getConfiguration(); + const config = getConfiguration(); config.onBuildChanged(async (build: Build) => { try { @@ -81,13 +79,13 @@ const run = async () => { ); } catch (e) { - + if (e instanceof Error) { const err = e as Error; createRoot(document.getElementById('root')!).render( - + ); diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/vite.config.ts b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/vite.config.ts index 72f0f31bb1d..e9ec6c3132e 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/vite.config.ts +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/azure-devops-report/vite.config.ts @@ -3,20 +3,12 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; -import babel from 'vite-plugin-babel'; -import commonjs from 'vite-plugin-commonjs'; // https://vitejs.dev/config/ export default defineConfig({ + base: './', plugins: [ - babel({ - babelConfig: { - plugins: ["transform-amd-to-commonjs"], - } - }), react(), - commonjs(), ], - base: './', }) diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/build-if-out-of-date.js b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/build-if-out-of-date.js index 1f6bfcef308..8f9e43e2eeb 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/build-if-out-of-date.js +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/build-if-out-of-date.js @@ -40,6 +40,7 @@ if (!newestDistFile || newestDistFile.mtimeMs < Math.max(newestReportFile.mtimeM // Run the build exec("npm run build", (err, stdout, stderr) => { if (err) { + console.log(stdout); console.error(err); return; } diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ChatDetailsSection.tsx b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ChatDetailsSection.tsx index 0958e0b560d..ba41410093f 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ChatDetailsSection.tsx +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ChatDetailsSection.tsx @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + import { Table, TableHeader, TableRow, TableHeaderCell, TableBody, TableCell } from "@fluentui/react-components"; import { ChevronDown12Regular, ChevronRight12Regular, Warning16Regular, CheckmarkCircle16Regular, Copy16Regular } from "@fluentui/react-icons"; import { useState } from "react"; diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ConversationDetails.tsx b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ConversationDetails.tsx index 6a3a78d5ca3..325f66ee7e2 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ConversationDetails.tsx +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ConversationDetails.tsx @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + import { mergeClasses } from "@fluentui/react-components"; import { ChevronDown12Regular, ChevronRight12Regular } from "@fluentui/react-icons"; import { useState } from "react"; diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/DiagnosticsContent.tsx b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/DiagnosticsContent.tsx index 6b53f367e51..10fdc15ee3b 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/DiagnosticsContent.tsx +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/DiagnosticsContent.tsx @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + import { DismissCircle16Regular, Warning16Regular, Info16Regular, Copy16Regular } from "@fluentui/react-icons"; import { Table, TableHeader, TableRow, TableHeaderCell, TableBody, TableCell } from "@fluentui/react-components"; import { useStyles } from "./Styles"; diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/MetricDetailsSection.tsx b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/MetricDetailsSection.tsx index 6158f6c1ab2..b8ac0ccc194 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/MetricDetailsSection.tsx +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/MetricDetailsSection.tsx @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + import { ChevronDown12Regular, ChevronRight12Regular, DismissCircle16Regular } from "@fluentui/react-icons"; import { useState } from "react"; import type { MetricType } from "./MetricCard"; diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ReportContext.tsx b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ReportContext.tsx index f43ff59a078..64a1e4a3c20 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ReportContext.tsx +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ReportContext.tsx @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + import { useContext, createContext, useState } from "react"; import { ReverseTextIndex, ScoreNode, ScoreNodeType, ScoreSummary } from "./Summary"; diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ScenarioRunHistory.tsx b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ScenarioRunHistory.tsx index 771f2c9cbdb..9dfeeff5622 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ScenarioRunHistory.tsx +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ScenarioRunHistory.tsx @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + import { mergeClasses, Table, TableHeader, TableRow, TableHeaderCell, TableBody, TableCell, TableCellLayout } from "@fluentui/react-components"; import { ChevronDown12Regular, ChevronRight12Regular } from "@fluentui/react-icons"; import { useState } from "react"; diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ScoreDetail.tsx b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ScoreDetail.tsx index 78c8d457cb0..1147cce6669 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ScoreDetail.tsx +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ScoreDetail.tsx @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + import { useEffect, useRef, useState } from "react"; import { ChatDetailsSection } from "./ChatDetailsSection"; import { ConversationDetails } from "./ConversationDetails"; diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ScoreNodeHistory.tsx b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ScoreNodeHistory.tsx index 92f2079a8b7..6c27441964c 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ScoreNodeHistory.tsx +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/ScoreNodeHistory.tsx @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + import { Table, TableHeader, TableRow, TableHeaderCell, TableBody, TableCell, TableCellLayout, Button, mergeClasses } from "@fluentui/react-components"; import { useReportContext } from "./ReportContext"; import { useStyles } from "./Styles"; diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/Styles.ts b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/Styles.ts index 2a5cedb7906..7ddc980a372 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/Styles.ts +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/components/Styles.ts @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + import { makeStyles, tokens } from "@fluentui/react-components"; export const useStyles = makeStyles({ diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/package-lock.json b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/package-lock.json index 01d4afcc011..4f6ad9c840d 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/package-lock.json +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/package-lock.json @@ -12,7 +12,6 @@ "@fluentui/react-components": "^9.61.2", "@fluentui/react-icons": "^2.0.292", "@leeoniya/ufuzzy": "^1.0.18", - "azure-devops-extension-api": "^4.243.0", "azure-devops-extension-sdk": "^4.0.2", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -28,14 +27,12 @@ "eslint": "^9.9.0", "eslint-plugin-react-hooks": "^5.1.0-rc.0", "eslint-plugin-react-refresh": "^0.4.9", - "glob": "^11.0.0", + "glob": "^11.0.1", "globals": "^16.0.0", "tfx-cli": "^0.21.0", "typescript": "^5.5.3", "typescript-eslint": "^8.27.0", "vite": "^6.2.6", - "vite-plugin-babel": "^1.2.0", - "vite-plugin-commonjs": "^0.10.3", "vite-plugin-singlefile": "^2.0.2" } }, @@ -749,9 +746,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", - "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.0.tgz", + "integrity": "sha512-WhCn7Z7TauhBtmzhvKpoQs0Wwb/kBcy4CwpuI0/eEIr2Lx2auxmulAzLr91wVZJaz47iUZdkXOK7WlAfxGKCnA==", "dev": true, "dependencies": { "eslint-visitor-keys": "^3.4.3" @@ -949,24 +946,24 @@ } }, "node_modules/@fluentui/font-icons-mdl2": { - "version": "8.5.59", - "resolved": "https://registry.npmjs.org/@fluentui/font-icons-mdl2/-/font-icons-mdl2-8.5.59.tgz", - "integrity": "sha512-pAVjJqnqG9JxJTflzaYU85+Elt0/8NCgWlOBgINeBS/cM0rf4YC6P7phiqwPr9vvQwXrqPAcIWNXhaJFqmWRog==", + "version": "8.5.60", + "resolved": "https://registry.npmjs.org/@fluentui/font-icons-mdl2/-/font-icons-mdl2-8.5.60.tgz", + "integrity": "sha512-PpS+lJrgIOHjDa1IiV13OSpcBrwldXuN+cz24qfqolmLRZ0NNeiWnhcOwse9Q5jxXc9JpY9kdPt9ECYtunzzaw==", "dependencies": { "@fluentui/set-version": "^8.2.24", - "@fluentui/style-utilities": "^8.11.8", + "@fluentui/style-utilities": "^8.12.0", "@fluentui/utilities": "^8.15.20", "tslib": "^2.1.0" } }, "node_modules/@fluentui/foundation-legacy": { - "version": "8.4.25", - "resolved": "https://registry.npmjs.org/@fluentui/foundation-legacy/-/foundation-legacy-8.4.25.tgz", - "integrity": "sha512-EcynaaKhP1Fyy4DigroXMrzAvf34+a1/CvIMnS0FMBx55sOaP3viUghsMdkiH1dN5uwVh6XoGgRbgjhf4IDNnQ==", + "version": "8.4.26", + "resolved": "https://registry.npmjs.org/@fluentui/foundation-legacy/-/foundation-legacy-8.4.26.tgz", + "integrity": "sha512-kTOdBGk7xPK3usD4uQ77+2UrK9AsAp0CeAboEBSvV/1wO6uI86IWGN7iyvSaqnnZHRJ8k5VAMrE2lHRJsOveag==", "dependencies": { "@fluentui/merge-styles": "^8.6.14", "@fluentui/set-version": "^8.2.24", - "@fluentui/style-utilities": "^8.11.8", + "@fluentui/style-utilities": "^8.12.0", "@fluentui/utilities": "^8.15.20", "tslib": "^2.1.0" }, @@ -1009,20 +1006,20 @@ } }, "node_modules/@fluentui/react": { - "version": "8.122.14", - "resolved": "https://registry.npmjs.org/@fluentui/react/-/react-8.122.14.tgz", - "integrity": "sha512-uMazy9wqMOhQHnYYZ13rl42tx09t7FIhUp+LEBqmRyDXxM9523g+fhcXDn4tZR/NHC+3x03t1pR5y6UV6Y2y+A==", + "version": "8.122.15", + "resolved": "https://registry.npmjs.org/@fluentui/react/-/react-8.122.15.tgz", + "integrity": "sha512-uh0zZooMeGZj5F5fxLY8tjfALAHnJMFUBeiVEt5D6ZGVLr/bEUq9EDUBmyOYS/o5RpHuY55I4rT5+Ua/iLzKVw==", "dependencies": { "@fluentui/date-time-utilities": "^8.6.10", - "@fluentui/font-icons-mdl2": "^8.5.59", - "@fluentui/foundation-legacy": "^8.4.25", + "@fluentui/font-icons-mdl2": "^8.5.60", + "@fluentui/foundation-legacy": "^8.4.26", "@fluentui/merge-styles": "^8.6.14", - "@fluentui/react-focus": "^8.9.22", + "@fluentui/react-focus": "^8.9.23", "@fluentui/react-hooks": "^8.8.17", "@fluentui/react-portal-compat-context": "^9.0.13", "@fluentui/react-window-provider": "^2.2.29", "@fluentui/set-version": "^8.2.24", - "@fluentui/style-utilities": "^8.11.8", + "@fluentui/style-utilities": "^8.12.0", "@fluentui/theme": "^2.6.65", "@fluentui/utilities": "^8.15.20", "@microsoft/load-themed-styles": "^1.10.26", @@ -1497,14 +1494,14 @@ } }, "node_modules/@fluentui/react-focus": { - "version": "8.9.22", - "resolved": "https://registry.npmjs.org/@fluentui/react-focus/-/react-focus-8.9.22.tgz", - "integrity": "sha512-+qHv/s3b4G/mvMVXdfwVGXq+vLXyl2K3G+UTFB7csTtKHqfLQen+YOBrD0h7XTUyJq3mlpzydU10ZO6PKx6rfA==", + "version": "8.9.23", + "resolved": "https://registry.npmjs.org/@fluentui/react-focus/-/react-focus-8.9.23.tgz", + "integrity": "sha512-cBx6E/uM9T55uzwO27cV9iKwfv/o2oZNSTyAdYdJeACIocVeIoH8fFA0zRbWoxwL72cZ9nteO+pKHcb+np2P9w==", "dependencies": { "@fluentui/keyboard-key": "^0.4.23", "@fluentui/merge-styles": "^8.6.14", "@fluentui/set-version": "^8.2.24", - "@fluentui/style-utilities": "^8.11.8", + "@fluentui/style-utilities": "^8.12.0", "@fluentui/utilities": "^8.15.20", "tslib": "^2.1.0" }, @@ -1529,9 +1526,9 @@ } }, "node_modules/@fluentui/react-icons": { - "version": "2.0.294", - "resolved": "https://registry.npmjs.org/@fluentui/react-icons/-/react-icons-2.0.294.tgz", - "integrity": "sha512-BoR+wqa+HEn8bB8L+gN/BnxR2LaR2ri6MSRb8W/s9mxy5deodnHalQ1P66vBWlJyaozNcxNHR4COKb4RJvpmeA==", + "version": "2.0.297", + "resolved": "https://registry.npmjs.org/@fluentui/react-icons/-/react-icons-2.0.297.tgz", + "integrity": "sha512-nMAZqWDHKzlVgs9OaeE7FNp+RHHskC3NaKftyIQmnFhPb34YEuNGABsi00dDpwxT5H4ZedmeqqCnWz+XSz0G0A==", "dependencies": { "@griffel/react": "^1.0.0", "tslib": "^2.1.0" @@ -2519,9 +2516,9 @@ } }, "node_modules/@fluentui/style-utilities": { - "version": "8.11.8", - "resolved": "https://registry.npmjs.org/@fluentui/style-utilities/-/style-utilities-8.11.8.tgz", - "integrity": "sha512-DPYIVL99Xvm5h67+Reoc/z+EQtO+F/vu+ZmdHw1zgVLDmtKxKksRA53xBwFqL4YweoNkSTPKoJlJF1/NF5U8uw==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/@fluentui/style-utilities/-/style-utilities-8.12.0.tgz", + "integrity": "sha512-uBHPzZ1waIOj/lQG6Z8jjAilKnePqvFWJi2UHYAfezQM6HXoGpE0sUZWECYNPU+fL0/LTNF8FmfvYPFD0frVGw==", "dependencies": { "@fluentui/merge-styles": "^8.6.14", "@fluentui/set-version": "^8.2.24", @@ -2775,9 +2772,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.39.0.tgz", - "integrity": "sha512-lGVys55Qb00Wvh8DMAocp5kIcaNzEFTmGhfFd88LfaogYTRKrdxgtlO5H6S49v2Nd8R2C6wLOal0qv6/kCkOwA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", + "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", "cpu": [ "arm" ], @@ -2788,9 +2785,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.39.0.tgz", - "integrity": "sha512-It9+M1zE31KWfqh/0cJLrrsCPiF72PoJjIChLX+rEcujVRCb4NLQ5QzFkzIZW8Kn8FTbvGQBY5TkKBau3S8cCQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", + "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", "cpu": [ "arm64" ], @@ -2801,9 +2798,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.39.0.tgz", - "integrity": "sha512-lXQnhpFDOKDXiGxsU9/l8UEGGM65comrQuZ+lDcGUx+9YQ9dKpF3rSEGepyeR5AHZ0b5RgiligsBhWZfSSQh8Q==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", + "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", "cpu": [ "arm64" ], @@ -2814,9 +2811,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.39.0.tgz", - "integrity": "sha512-mKXpNZLvtEbgu6WCkNij7CGycdw9cJi2k9v0noMb++Vab12GZjFgUXD69ilAbBh034Zwn95c2PNSz9xM7KYEAQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", + "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", "cpu": [ "x64" ], @@ -2827,9 +2824,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.39.0.tgz", - "integrity": "sha512-jivRRlh2Lod/KvDZx2zUR+I4iBfHcu2V/BA2vasUtdtTN2Uk3jfcZczLa81ESHZHPHy4ih3T/W5rPFZ/hX7RtQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", + "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", "cpu": [ "arm64" ], @@ -2840,9 +2837,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.39.0.tgz", - "integrity": "sha512-8RXIWvYIRK9nO+bhVz8DwLBepcptw633gv/QT4015CpJ0Ht8punmoHU/DuEd3iw9Hr8UwUV+t+VNNuZIWYeY7Q==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", + "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", "cpu": [ "x64" ], @@ -2853,9 +2850,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.39.0.tgz", - "integrity": "sha512-mz5POx5Zu58f2xAG5RaRRhp3IZDK7zXGk5sdEDj4o96HeaXhlUwmLFzNlc4hCQi5sGdR12VDgEUqVSHer0lI9g==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", + "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", "cpu": [ "arm" ], @@ -2866,9 +2863,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.39.0.tgz", - "integrity": "sha512-+YDwhM6gUAyakl0CD+bMFpdmwIoRDzZYaTWV3SDRBGkMU/VpIBYXXEvkEcTagw/7VVkL2vA29zU4UVy1mP0/Yw==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", + "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", "cpu": [ "arm" ], @@ -2879,9 +2876,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.39.0.tgz", - "integrity": "sha512-EKf7iF7aK36eEChvlgxGnk7pdJfzfQbNvGV/+l98iiMwU23MwvmV0Ty3pJ0p5WQfm3JRHOytSIqD9LB7Bq7xdQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", + "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", "cpu": [ "arm64" ], @@ -2892,9 +2889,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.39.0.tgz", - "integrity": "sha512-vYanR6MtqC7Z2SNr8gzVnzUul09Wi1kZqJaek3KcIlI/wq5Xtq4ZPIZ0Mr/st/sv/NnaPwy/D4yXg5x0B3aUUA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", + "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", "cpu": [ "arm64" ], @@ -2905,9 +2902,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.39.0.tgz", - "integrity": "sha512-NMRUT40+h0FBa5fb+cpxtZoGAggRem16ocVKIv5gDB5uLDgBIwrIsXlGqYbLwW8YyO3WVTk1FkFDjMETYlDqiw==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", + "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", "cpu": [ "loong64" ], @@ -2918,9 +2915,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.39.0.tgz", - "integrity": "sha512-0pCNnmxgduJ3YRt+D+kJ6Ai/r+TaePu9ZLENl+ZDV/CdVczXl95CbIiwwswu4L+K7uOIGf6tMo2vm8uadRaICQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", + "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", "cpu": [ "ppc64" ], @@ -2931,9 +2928,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.39.0.tgz", - "integrity": "sha512-t7j5Zhr7S4bBtksT73bO6c3Qa2AV/HqiGlj9+KB3gNF5upcVkx+HLgxTm8DK4OkzsOYqbdqbLKwvGMhylJCPhQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", + "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", "cpu": [ "riscv64" ], @@ -2944,9 +2941,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.39.0.tgz", - "integrity": "sha512-m6cwI86IvQ7M93MQ2RF5SP8tUjD39Y7rjb1qjHgYh28uAPVU8+k/xYWvxRO3/tBN2pZkSMa5RjnPuUIbrwVxeA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", + "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", "cpu": [ "riscv64" ], @@ -2957,9 +2954,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.39.0.tgz", - "integrity": "sha512-iRDJd2ebMunnk2rsSBYlsptCyuINvxUfGwOUldjv5M4tpa93K8tFMeYGpNk2+Nxl+OBJnBzy2/JCscGeO507kA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", + "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", "cpu": [ "s390x" ], @@ -2970,9 +2967,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.39.0.tgz", - "integrity": "sha512-t9jqYw27R6Lx0XKfEFe5vUeEJ5pF3SGIM6gTfONSMb7DuG6z6wfj2yjcoZxHg129veTqU7+wOhY6GX8wmf90dA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", + "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", "cpu": [ "x64" ], @@ -2983,9 +2980,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.39.0.tgz", - "integrity": "sha512-ThFdkrFDP55AIsIZDKSBWEt/JcWlCzydbZHinZ0F/r1h83qbGeenCt/G/wG2O0reuENDD2tawfAj2s8VK7Bugg==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", + "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", "cpu": [ "x64" ], @@ -2996,9 +2993,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.39.0.tgz", - "integrity": "sha512-jDrLm6yUtbOg2TYB3sBF3acUnAwsIksEYjLeHL+TJv9jg+TmTwdyjnDex27jqEMakNKf3RwwPahDIt7QXCSqRQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", + "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", "cpu": [ "arm64" ], @@ -3009,9 +3006,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.39.0.tgz", - "integrity": "sha512-6w9uMuza+LbLCVoNKL5FSLE7yvYkq9laSd09bwS0tMjkwXrmib/4KmoJcrKhLWHvw19mwU+33ndC69T7weNNjQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", + "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", "cpu": [ "ia32" ], @@ -3022,9 +3019,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.39.0.tgz", - "integrity": "sha512-yAkUOkIKZlK5dl7u6dg897doBgLXmUHhIINM2c+sND3DZwnrdQkkSiDh7N75Ll4mM4dxSkYfXqU9fW3lLkMFug==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", + "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", "cpu": [ "x64" ], @@ -3035,9 +3032,9 @@ ] }, "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", + "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", "dependencies": { "tslib": "^2.8.0" } @@ -3132,9 +3129,9 @@ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==" }, "node_modules/@types/node": { - "version": "22.14.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz", - "integrity": "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==", + "version": "22.14.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", + "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", "dev": true, "dependencies": { "undici-types": "~6.21.0" @@ -3168,16 +3165,16 @@ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.1.tgz", - "integrity": "sha512-ba0rr4Wfvg23vERs3eB+P3lfj2E+2g3lhWcCVukUuhtcdUx5lSIFZlGFEBHKr+3zizDa/TvZTptdNHVZWAkSBg==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz", + "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.29.1", - "@typescript-eslint/type-utils": "8.29.1", - "@typescript-eslint/utils": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/type-utils": "8.30.1", + "@typescript-eslint/utils": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -3197,15 +3194,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.1.tgz", - "integrity": "sha512-zczrHVEqEaTwh12gWBIJWj8nx+ayDcCJs06yoNMY0kwjMWDM6+kppljY+BxWI06d2Ja+h4+WdufDcwMnnMEWmg==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz", + "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.29.1", - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/typescript-estree": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", "debug": "^4.3.4" }, "engines": { @@ -3221,13 +3218,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.1.tgz", - "integrity": "sha512-2nggXGX5F3YrsGN08pw4XpMLO1Rgtnn4AzTegC2MDesv6q3QaTU5yU7IbS1tf1IwCR0Hv/1EFygLn9ms6LIpDA==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", + "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1" + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3238,13 +3235,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.1.tgz", - "integrity": "sha512-DkDUSDwZVCYN71xA4wzySqqcZsHKic53A4BLqmrWFFpOpNSoxX233lwGu/2135ymTCR04PoKiEEEvN1gFYg4Tw==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz", + "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.29.1", - "@typescript-eslint/utils": "8.29.1", + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/utils": "8.30.1", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, @@ -3261,9 +3258,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.1.tgz", - "integrity": "sha512-VT7T1PuJF1hpYC3AGm2rCgJBjHL3nc+A/bhOp9sGMKfi5v0WufsX/sHCFBfNTx2F+zA6qBc/PD0/kLRLjdt8mQ==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz", + "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3274,13 +3271,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.1.tgz", - "integrity": "sha512-l1enRoSaUkQxOQnbi0KPUtqeZkSiFlqrx9/3ns2rEDhGKfTa+88RmXqedC1zmVTOWrLc2e6DEJrTA51C9iLH5g==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", + "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3336,15 +3333,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.29.1.tgz", - "integrity": "sha512-QAkFEbytSaB8wnmB+DflhUPz6CLbFWE2SnSCrRMEa+KnXIzDYbpsn++1HGvnfAsUY44doDXmvRkO5shlM/3UfA==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", + "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.29.1", - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/typescript-estree": "8.29.1" + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3359,12 +3356,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.1.tgz", - "integrity": "sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", + "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/types": "8.30.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -3381,16 +3378,16 @@ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==" }, "node_modules/@vitejs/plugin-react": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", - "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.0.tgz", + "integrity": "sha512-x/EztcTKVj+TDeANY1WjNeYsvZjZdfWRMP/KXi5Yn8BoTzpa13ZltaQqKfvWYbX8CE10GOHHdC5v86jY9x8i/g==", "dev": true, "dependencies": { - "@babel/core": "^7.26.0", + "@babel/core": "^7.26.10", "@babel/plugin-transform-react-jsx-self": "^7.25.9", "@babel/plugin-transform-react-jsx-source": "^7.25.9", "@types/babel__core": "^7.20.5", - "react-refresh": "^0.14.2" + "react-refresh": "^0.17.0" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -3666,17 +3663,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/azure-devops-extension-api": { - "version": "4.248.1", - "resolved": "https://registry.npmjs.org/azure-devops-extension-api/-/azure-devops-extension-api-4.248.1.tgz", - "integrity": "sha512-DyJdgQS1wP6m0p0cOADGCfYnYvJgM/vT+Qji1JSrQQLCUYkRmzwEicrbBdscgNGuhlk6X6SmDX7B6+iifP/IjQ==", - "dependencies": { - "whatwg-fetch": "~3.0.0" - }, - "peerDependencies": { - "azure-devops-extension-sdk": "^2 || ^3 || ^4" - } - }, "node_modules/azure-devops-extension-sdk": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/azure-devops-extension-sdk/-/azure-devops-extension-sdk-4.0.2.tgz", @@ -3921,9 +3907,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001712", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001712.tgz", - "integrity": "sha512-MBqPpGYYdQ7/hfKiet9SCI+nmN5/hp4ZzveOJubl5DTAMa5oggjAuoi0Z4onBpKPFI2ePGnQuQIzF3VxDjDJig==", + "version": "1.0.30001713", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001713.tgz", + "integrity": "sha512-wCIWIg+A4Xr7NfhTuHdX+/FKh3+Op3LBbSp2N5Pfx6T/LhdQy3GTyoTg48BReaW/MyMNZAkTadsBtai3ldWK0Q==", "dev": true, "funding": [ { @@ -4328,9 +4314,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.5.134", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.134.tgz", - "integrity": "sha512-zSwzrLg3jNP3bwsLqWHmS5z2nIOQ5ngMnfMZOWWtXnqqQkPVyOipxK98w+1beLw1TB+EImPNcG8wVP/cLVs2Og==", + "version": "1.5.137", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.137.tgz", + "integrity": "sha512-/QSJaU2JyIuTbbABAo/crOs+SuAZLS+fVVS10PVrIT9hrRkmZl8Hb0xPSkKRUUWHQtYzXHpQUW3Dy5hwMzGZkA==", "dev": true }, "node_modules/embla-carousel": { @@ -4458,12 +4444,6 @@ "node": ">= 0.4" } }, - "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", - "dev": true - }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -6145,15 +6125,6 @@ "yallist": "^3.0.2" } }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -7398,9 +7369,9 @@ } }, "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -7593,9 +7564,9 @@ } }, "node_modules/rollup": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.39.0.tgz", - "integrity": "sha512-thI8kNc02yNvnmJp8dr3fNWJ9tCONDhp6TV35X6HkKGGs9E6q7YWCHbe5vKiTa7TAiNcFEmXKj3X/pG2b3ci0g==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", + "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", "dev": true, "dependencies": { "@types/estree": "1.0.7" @@ -7608,26 +7579,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.39.0", - "@rollup/rollup-android-arm64": "4.39.0", - "@rollup/rollup-darwin-arm64": "4.39.0", - "@rollup/rollup-darwin-x64": "4.39.0", - "@rollup/rollup-freebsd-arm64": "4.39.0", - "@rollup/rollup-freebsd-x64": "4.39.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.39.0", - "@rollup/rollup-linux-arm-musleabihf": "4.39.0", - "@rollup/rollup-linux-arm64-gnu": "4.39.0", - "@rollup/rollup-linux-arm64-musl": "4.39.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.39.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.39.0", - "@rollup/rollup-linux-riscv64-gnu": "4.39.0", - "@rollup/rollup-linux-riscv64-musl": "4.39.0", - "@rollup/rollup-linux-s390x-gnu": "4.39.0", - "@rollup/rollup-linux-x64-gnu": "4.39.0", - "@rollup/rollup-linux-x64-musl": "4.39.0", - "@rollup/rollup-win32-arm64-msvc": "4.39.0", - "@rollup/rollup-win32-ia32-msvc": "4.39.0", - "@rollup/rollup-win32-x64-msvc": "4.39.0", + "@rollup/rollup-android-arm-eabi": "4.40.0", + "@rollup/rollup-android-arm64": "4.40.0", + "@rollup/rollup-darwin-arm64": "4.40.0", + "@rollup/rollup-darwin-x64": "4.40.0", + "@rollup/rollup-freebsd-arm64": "4.40.0", + "@rollup/rollup-freebsd-x64": "4.40.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", + "@rollup/rollup-linux-arm-musleabihf": "4.40.0", + "@rollup/rollup-linux-arm64-gnu": "4.40.0", + "@rollup/rollup-linux-arm64-musl": "4.40.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-musl": "4.40.0", + "@rollup/rollup-linux-s390x-gnu": "4.40.0", + "@rollup/rollup-linux-x64-gnu": "4.40.0", + "@rollup/rollup-linux-x64-musl": "4.40.0", + "@rollup/rollup-win32-arm64-msvc": "4.40.0", + "@rollup/rollup-win32-ia32-msvc": "4.40.0", + "@rollup/rollup-win32-x64-msvc": "4.40.0", "fsevents": "~2.3.2" } }, @@ -8523,14 +8494,14 @@ } }, "node_modules/typescript-eslint": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.29.1.tgz", - "integrity": "sha512-f8cDkvndhbQMPcysk6CUSGBWV+g1utqdn71P5YKwMumVMOG/5k7cHq0KyG4O52nB0oKS4aN2Tp5+wB4APJGC+w==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.30.1.tgz", + "integrity": "sha512-D7lC0kcehVH7Mb26MRQi64LMyRJsj3dToJxM1+JVTl53DQSV5/7oUGWQLcKl1C1KnoVHxMMU2FNQMffr7F3Row==", "dev": true, "dependencies": { - "@typescript-eslint/eslint-plugin": "8.29.1", - "@typescript-eslint/parser": "8.29.1", - "@typescript-eslint/utils": "8.29.1" + "@typescript-eslint/eslint-plugin": "8.30.1", + "@typescript-eslint/parser": "8.30.1", + "@typescript-eslint/utils": "8.30.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -8861,39 +8832,6 @@ } } }, - "node_modules/vite-plugin-babel": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/vite-plugin-babel/-/vite-plugin-babel-1.3.0.tgz", - "integrity": "sha512-C5WKX0UwvQKH8WD2GiyWUjI62UBfLbfUhiLexnIm4asLdENX5ymrRipFlBnGeVxoOaYgTL5dh5KW6YDGpWsR8A==", - "dev": true, - "peerDependencies": { - "@babel/core": "^7.0.0", - "vite": "^2.7.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" - } - }, - "node_modules/vite-plugin-commonjs": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/vite-plugin-commonjs/-/vite-plugin-commonjs-0.10.4.tgz", - "integrity": "sha512-eWQuvQKCcx0QYB5e5xfxBNjQKyrjEWZIR9UOkOV6JAgxVhtbZvCOF+FNC2ZijBJ3U3Px04ZMMyyMyFBVWIJ5+g==", - "dev": true, - "dependencies": { - "acorn": "^8.12.1", - "magic-string": "^0.30.11", - "vite-plugin-dynamic-import": "^1.6.0" - } - }, - "node_modules/vite-plugin-dynamic-import": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/vite-plugin-dynamic-import/-/vite-plugin-dynamic-import-1.6.0.tgz", - "integrity": "sha512-TM0sz70wfzTIo9YCxVFwS8OA9lNREsh+0vMHGSkWDTZ7bgd1Yjs5RV8EgB634l/91IsXJReg0xtmuQqP0mf+rg==", - "dev": true, - "dependencies": { - "acorn": "^8.12.1", - "es-module-lexer": "^1.5.4", - "fast-glob": "^3.3.2", - "magic-string": "^0.30.11" - } - }, "node_modules/vite-plugin-singlefile": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/vite-plugin-singlefile/-/vite-plugin-singlefile-2.2.0.tgz", @@ -8919,11 +8857,6 @@ "node": ">=0.6.0" } }, - "node_modules/whatwg-fetch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.1.tgz", - "integrity": "sha512-gPeS9Ef8bSigQwAZiSbQyyU/sBQP1Qo6r112A2shGw5B6+4YT1D8O0oXXwF3zgXXLrrfxbIeP8fM3JmsoWU4Vw==" - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/package.json b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/package.json index 522d1d49d55..520f5f94436 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/package.json +++ b/src/Libraries/Microsoft.Extensions.AI.Evaluation.Reporting/TypeScript/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "dev": "vite --config html-report/vite.config.ts", - "build": "tsc -b && vite build --config html-report/vite.config.ts", + "build": "tsc -b --verbose && vite build --config html-report/vite.config.ts", "buildtask": "pushd azure-devops-report\\tasks\\PublishAIEvaluationReport && npm install && npm build && popd" }, "dependencies": { @@ -13,7 +13,6 @@ "@fluentui/react-components": "^9.61.2", "@fluentui/react-icons": "^2.0.292", "@leeoniya/ufuzzy": "^1.0.18", - "azure-devops-extension-api": "^4.243.0", "azure-devops-extension-sdk": "^4.0.2", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -29,14 +28,12 @@ "eslint": "^9.9.0", "eslint-plugin-react-hooks": "^5.1.0-rc.0", "eslint-plugin-react-refresh": "^0.4.9", - "glob": "^11.0.0", + "glob": "^11.0.1", "globals": "^16.0.0", "tfx-cli": "^0.21.0", "typescript": "^5.5.3", "typescript-eslint": "^8.27.0", "vite": "^6.2.6", - "vite-plugin-babel": "^1.2.0", - "vite-plugin-commonjs": "^0.10.3", "vite-plugin-singlefile": "^2.0.2" } -} +} \ No newline at end of file