diff --git a/.oxlintrc.base.json b/.oxlintrc.base.json index 82f9f4ac5d50..6f2158f5c7db 100644 --- a/.oxlintrc.base.json +++ b/.oxlintrc.base.json @@ -147,7 +147,8 @@ "**/integrations/fs/vendored/**/*.ts", "**/integrations/tracing/knex/vendored/**/*.ts", "**/integrations/tracing/mongo/vendored/**/*.ts", - "**/integrations/tracing/connect/vendored/**/*.ts" + "**/integrations/tracing/connect/vendored/**/*.ts", + "**/integrations/tracing/tedious/vendored/**/*.ts" ], "rules": { "typescript/no-explicit-any": "off" diff --git a/packages/node/package.json b/packages/node/package.json index e8a57e00a78d..65fb9788eb43 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -78,7 +78,6 @@ "@opentelemetry/instrumentation-mysql": "0.60.0", "@opentelemetry/instrumentation-mysql2": "0.60.0", "@opentelemetry/instrumentation-pg": "0.66.0", - "@opentelemetry/instrumentation-tedious": "0.33.0", "@opentelemetry/sdk-trace-base": "^2.6.1", "@opentelemetry/semantic-conventions": "^1.40.0", "@prisma/instrumentation": "7.6.0", diff --git a/packages/node/src/integrations/tracing/tedious.ts b/packages/node/src/integrations/tracing/tedious/index.ts similarity index 96% rename from packages/node/src/integrations/tracing/tedious.ts rename to packages/node/src/integrations/tracing/tedious/index.ts index d9ae7d186410..30f32cd74f43 100644 --- a/packages/node/src/integrations/tracing/tedious.ts +++ b/packages/node/src/integrations/tracing/tedious/index.ts @@ -1,4 +1,4 @@ -import { TediousInstrumentation } from '@opentelemetry/instrumentation-tedious'; +import { TediousInstrumentation } from './vendored/instrumentation'; import type { IntegrationFn } from '@sentry/core'; import { defineIntegration, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, spanToJSON } from '@sentry/core'; import { generateInstrumentOnce, instrumentWhenWrapped } from '@sentry/node-core'; diff --git a/packages/node/src/integrations/tracing/tedious/vendored/instrumentation.ts b/packages/node/src/integrations/tracing/tedious/vendored/instrumentation.ts new file mode 100644 index 000000000000..894f8771bbe2 --- /dev/null +++ b/packages/node/src/integrations/tracing/tedious/vendored/instrumentation.ts @@ -0,0 +1,310 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * NOTICE from the Sentry authors: + * - Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/15ef7506553f631ea4181391e0c5725a56f0d082/packages/instrumentation-tedious + * - Upstream version: @opentelemetry/instrumentation-tedious@0.37.0 + * - Minor TypeScript strictness adjustments + */ +/* eslint-disable */ + +import * as api from '@opentelemetry/api'; +import { EventEmitter } from 'events'; +import { + InstrumentationBase, + InstrumentationNodeModuleDefinition, + isWrapped, + SemconvStability, + semconvStabilityFromStr, +} from '@opentelemetry/instrumentation'; +import { + ATTR_DB_COLLECTION_NAME, + ATTR_DB_NAMESPACE, + ATTR_DB_QUERY_TEXT, + ATTR_DB_SYSTEM_NAME, + ATTR_SERVER_ADDRESS, + ATTR_SERVER_PORT, + DB_SYSTEM_NAME_VALUE_MICROSOFT_SQL_SERVER, +} from '@opentelemetry/semantic-conventions'; +import { + DB_SYSTEM_VALUE_MSSQL, + ATTR_DB_NAME, + ATTR_DB_SQL_TABLE, + ATTR_DB_STATEMENT, + ATTR_DB_SYSTEM, + ATTR_DB_USER, + ATTR_NET_PEER_NAME, + ATTR_NET_PEER_PORT, +} from './semconv'; +import type * as tedious from './tedious-types'; +import { TediousInstrumentationConfig } from './types'; +import { getSpanName, once } from './utils'; +import { SDK_VERSION } from '@sentry/core'; + +const PACKAGE_NAME = '@sentry/instrumentation-tedious'; + +const CURRENT_DATABASE = Symbol('opentelemetry.instrumentation-tedious.current-database'); + +export const INJECTED_CTX = Symbol('opentelemetry.instrumentation-tedious.context-info-injected'); + +const PATCHED_METHODS = ['callProcedure', 'execSql', 'execSqlBatch', 'execBulkLoad', 'prepare', 'execute']; + +type UnknownFunction = (...args: any[]) => any; +type ApproxConnection = EventEmitter & { + [CURRENT_DATABASE]: string; + config: any; +}; +type ApproxRequest = EventEmitter & { + sqlTextOrProcedure: string | undefined; + callback: any; + table: string | undefined; + parametersByName: any; +}; + +function setDatabase(this: ApproxConnection, databaseName: string) { + Object.defineProperty(this, CURRENT_DATABASE, { + value: databaseName, + writable: true, + }); +} + +export class TediousInstrumentation extends InstrumentationBase { + static readonly COMPONENT = 'tedious'; + private _netSemconvStability!: SemconvStability; + private _dbSemconvStability!: SemconvStability; + + constructor(config: TediousInstrumentationConfig = {}) { + super(PACKAGE_NAME, SDK_VERSION, config); + this._setSemconvStabilityFromEnv(); + } + + // Used for testing. + private _setSemconvStabilityFromEnv() { + this._netSemconvStability = semconvStabilityFromStr('http', process.env.OTEL_SEMCONV_STABILITY_OPT_IN); + this._dbSemconvStability = semconvStabilityFromStr('database', process.env.OTEL_SEMCONV_STABILITY_OPT_IN); + } + + protected init() { + return [ + new InstrumentationNodeModuleDefinition( + TediousInstrumentation.COMPONENT, + ['>=1.11.0 <20'], + (moduleExports: typeof tedious) => { + const ConnectionPrototype: any = moduleExports.Connection.prototype; + for (const method of PATCHED_METHODS) { + if (isWrapped(ConnectionPrototype[method])) { + this._unwrap(ConnectionPrototype, method); + } + this._wrap(ConnectionPrototype, method, this._patchQuery(method, moduleExports) as any); + } + + if (isWrapped(ConnectionPrototype.connect)) { + this._unwrap(ConnectionPrototype, 'connect'); + } + this._wrap(ConnectionPrototype, 'connect', this._patchConnect); + + return moduleExports; + }, + (moduleExports: typeof tedious) => { + if (moduleExports === undefined) return; + const ConnectionPrototype: any = moduleExports.Connection.prototype; + for (const method of PATCHED_METHODS) { + this._unwrap(ConnectionPrototype, method); + } + this._unwrap(ConnectionPrototype, 'connect'); + }, + ), + ]; + } + + private _patchConnect(original: UnknownFunction): UnknownFunction { + return function patchedConnect(this: ApproxConnection) { + setDatabase.call(this, this.config?.options?.database); + + // remove the listener first in case it's already added + this.removeListener('databaseChange', setDatabase); + this.on('databaseChange', setDatabase); + + this.once('end', () => { + this.removeListener('databaseChange', setDatabase); + }); + return original.apply(this, arguments as unknown as any[]); + }; + } + + private _buildTraceparent(span: api.Span): string { + const sc = span.spanContext(); + return `00-${sc.traceId}-${sc.spanId}-0${Number(sc.traceFlags || api.TraceFlags.NONE).toString(16)}`; + } + + /** + * Fire a one-off `SET CONTEXT_INFO @opentelemetry_traceparent` on the same + * connection. Marks the request with INJECTED_CTX so our patch skips it. + */ + private _injectContextInfo(connection: any, tediousModule: typeof tedious, traceparent: string): Promise { + return new Promise(resolve => { + try { + const sql = 'set context_info @opentelemetry_traceparent'; + const req = new tediousModule.Request(sql, (_err: any) => { + resolve(); + }); + Object.defineProperty(req, INJECTED_CTX, { value: true }); + const buf = Buffer.from(traceparent, 'utf8'); + req.addParameter('opentelemetry_traceparent', (tediousModule as any).TYPES.VarBinary, buf, { + length: buf.length, + }); + + connection.execSql(req); + } catch { + resolve(); + } + }); + } + + private _shouldInjectFor(operation: string): boolean { + return ( + operation === 'execSql' || + operation === 'execSqlBatch' || + operation === 'callProcedure' || + operation === 'execute' + ); + } + + private _patchQuery(operation: string, tediousModule: typeof tedious) { + return (originalMethod: UnknownFunction): UnknownFunction => { + const thisPlugin = this; + + function patchedMethod(this: ApproxConnection, request: ApproxRequest) { + // Skip our own injected request + if ((request as any)?.[INJECTED_CTX]) { + return originalMethod.apply(this, arguments as unknown as any[]); + } + + if (!(request instanceof EventEmitter)) { + thisPlugin._diag.warn(`Unexpected invocation of patched ${operation} method. Span not recorded`); + return originalMethod.apply(this, arguments as unknown as any[]); + } + let procCount = 0; + let statementCount = 0; + const incrementStatementCount = () => statementCount++; + const incrementProcCount = () => procCount++; + const databaseName = this[CURRENT_DATABASE]; + const sql = (request => { + // Required for <11.0.9 + if (request.sqlTextOrProcedure === 'sp_prepare' && request.parametersByName?.stmt?.value) { + return request.parametersByName.stmt.value; + } + return request.sqlTextOrProcedure; + })(request); + + const attributes: api.Attributes = {}; + if (thisPlugin._dbSemconvStability & SemconvStability.OLD) { + attributes[ATTR_DB_SYSTEM] = DB_SYSTEM_VALUE_MSSQL; + attributes[ATTR_DB_NAME] = databaseName; + // >=4 uses `authentication` object; older versions just userName and password pair + attributes[ATTR_DB_USER] = this.config?.userName ?? this.config?.authentication?.options?.userName; + attributes[ATTR_DB_STATEMENT] = sql; + attributes[ATTR_DB_SQL_TABLE] = request.table; + } + if (thisPlugin._dbSemconvStability & SemconvStability.STABLE) { + // The OTel spec for "db.namespace" discusses handling for connection + // to MSSQL "named instances". This isn't currently supported. + // https://opentelemetry.io/docs/specs/semconv/database/sql-server/#:~:text=%5B1%5D%20db%2Enamespace + attributes[ATTR_DB_NAMESPACE] = databaseName; + attributes[ATTR_DB_SYSTEM_NAME] = DB_SYSTEM_NAME_VALUE_MICROSOFT_SQL_SERVER; + attributes[ATTR_DB_QUERY_TEXT] = sql; + attributes[ATTR_DB_COLLECTION_NAME] = request.table; + // See https://opentelemetry.io/docs/specs/semconv/database/sql-server/#spans + // TODO(3290): can `db.response.status_code` be added? + // TODO(3290): is `operation` correct for `db.operation.name` + // TODO(3290): can `db.query.summary` reliably be calculated? + // TODO(3290): `db.stored_procedure.name` + } + if (thisPlugin._netSemconvStability & SemconvStability.OLD) { + attributes[ATTR_NET_PEER_NAME] = this.config?.server; + attributes[ATTR_NET_PEER_PORT] = this.config?.options?.port; + } + if (thisPlugin._netSemconvStability & SemconvStability.STABLE) { + attributes[ATTR_SERVER_ADDRESS] = this.config?.server; + attributes[ATTR_SERVER_PORT] = this.config?.options?.port; + } + const span = thisPlugin.tracer.startSpan(getSpanName(operation, databaseName, sql, request.table), { + kind: api.SpanKind.CLIENT, + attributes, + }); + + const endSpan = once((err?: any) => { + request.removeListener('done', incrementStatementCount); + request.removeListener('doneInProc', incrementStatementCount); + request.removeListener('doneProc', incrementProcCount); + request.removeListener('error', endSpan); + this.removeListener('end', endSpan); + + span.setAttribute('tedious.procedure_count', procCount); + span.setAttribute('tedious.statement_count', statementCount); + if (err) { + span.setStatus({ + code: api.SpanStatusCode.ERROR, + message: err.message, + }); + // TODO(3290): set `error.type` attribute? + } + span.end(); + }); + + request.on('done', incrementStatementCount); + request.on('doneInProc', incrementStatementCount); + request.on('doneProc', incrementProcCount); + request.once('error', endSpan); + this.on('end', endSpan); + + if (typeof request.callback === 'function') { + thisPlugin._wrap(request, 'callback', thisPlugin._patchCallbackQuery(endSpan)); + } else { + thisPlugin._diag.error('Expected request.callback to be a function'); + } + + const runUserRequest = () => { + return api.context.with(api.trace.setSpan(api.context.active(), span), originalMethod, this, ...arguments); + }; + + const cfg = thisPlugin.getConfig(); + const shouldInject = cfg.enableTraceContextPropagation && thisPlugin._shouldInjectFor(operation); + + if (!shouldInject) return runUserRequest(); + + const traceparent = thisPlugin._buildTraceparent(span); + + void thisPlugin._injectContextInfo(this, tediousModule, traceparent).finally(runUserRequest); + } + + Object.defineProperty(patchedMethod, 'length', { + value: originalMethod.length, + writable: false, + }); + + return patchedMethod; + }; + } + + private _patchCallbackQuery(endSpan: Function) { + return (originalCallback: Function) => { + return function (this: any, err: Error | undefined | null, rowCount?: number, rows?: any) { + endSpan(err); + return originalCallback.apply(this, arguments); + }; + }; + } +} diff --git a/packages/node/src/integrations/tracing/tedious/vendored/semconv.ts b/packages/node/src/integrations/tracing/tedious/vendored/semconv.ts new file mode 100644 index 000000000000..e4c4ee0d30b8 --- /dev/null +++ b/packages/node/src/integrations/tracing/tedious/vendored/semconv.ts @@ -0,0 +1,113 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * NOTICE from the Sentry authors: + * - Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/15ef7506553f631ea4181391e0c5725a56f0d082/packages/instrumentation-tedious + * - Upstream version: @opentelemetry/instrumentation-tedious@0.37.0 + */ +/* eslint-disable */ + +/* + * This file contains a copy of unstable semantic convention definitions + * used by this package. + * @see https://github.com/open-telemetry/opentelemetry-js/tree/main/semantic-conventions#unstable-semconv + */ + +/** + * Deprecated, use `db.namespace` instead. + * + * @example customers + * @example main + * + * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`. + * + * @deprecated Replaced by `db.namespace`. + */ +export const ATTR_DB_NAME = 'db.name' as const; + +/** + * Deprecated, use `db.collection.name` instead. + * + * @example "mytable" + * + * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`. + * + * @deprecated Replaced by `db.collection.name`, but only if not extracting the value from `db.query.text`. + */ +export const ATTR_DB_SQL_TABLE = 'db.sql.table' as const; + +/** + * The database statement being executed. + * + * @example SELECT * FROM wuser_table + * @example SET mykey "WuValue" + * + * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`. + * + * @deprecated Replaced by `db.query.text`. + */ +export const ATTR_DB_STATEMENT = 'db.statement' as const; + +/** + * Deprecated, use `db.system.name` instead. + * + * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`. + * + * @deprecated Replaced by `db.system.name`. + */ +export const ATTR_DB_SYSTEM = 'db.system' as const; + +/** + * Deprecated, no replacement at this time. + * + * @example readonly_user + * @example reporting_user + * + * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`. + * + * @deprecated Removed, no replacement at this time. + */ +export const ATTR_DB_USER = 'db.user' as const; + +/** + * Deprecated, use `server.address` on client spans and `client.address` on server spans. + * + * @example example.com + * + * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`. + * + * @deprecated Replaced by `server.address` on client spans and `client.address` on server spans. + */ +export const ATTR_NET_PEER_NAME = 'net.peer.name' as const; + +/** + * Deprecated, use `server.port` on client spans and `client.port` on server spans. + * + * @example 8080 + * + * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`. + * + * @deprecated Replaced by `server.port` on client spans and `client.port` on server spans. + */ +export const ATTR_NET_PEER_PORT = 'net.peer.port' as const; + +/** + * Enum value "mssql" for attribute {@link ATTR_DB_SYSTEM}. + * + * Microsoft SQL Server + * + * @experimental This enum value is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`. + */ +export const DB_SYSTEM_VALUE_MSSQL = 'mssql' as const; diff --git a/packages/node/src/integrations/tracing/tedious/vendored/tedious-types.ts b/packages/node/src/integrations/tracing/tedious/vendored/tedious-types.ts new file mode 100644 index 000000000000..f51ca322e363 --- /dev/null +++ b/packages/node/src/integrations/tracing/tedious/vendored/tedious-types.ts @@ -0,0 +1,30 @@ +/* + * Simplified types inlined from tedious. + */ + +import { EventEmitter } from 'events'; + +export declare class Connection extends EventEmitter { + config: any; + connect(connectListener?: (err?: Error) => void): void; + execSql(request: Request): void; + [key: string]: any; +} + +export declare class Request extends EventEmitter { + sqlTextOrProcedure: string | undefined; + callback: any; + table: string | undefined; + parametersByName: any; + constructor( + sqlTextOrProcedure: string | undefined, + callback: (error: Error | null | undefined, rowCount?: number, rows?: any) => void, + ); + addParameter(name: string, type: any, value?: unknown, options?: any): void; + [key: string]: any; +} + +export declare const TYPES: { + VarBinary: any; + [key: string]: any; +}; diff --git a/packages/node/src/integrations/tracing/tedious/vendored/types.ts b/packages/node/src/integrations/tracing/tedious/vendored/types.ts new file mode 100644 index 000000000000..025ed74ce216 --- /dev/null +++ b/packages/node/src/integrations/tracing/tedious/vendored/types.ts @@ -0,0 +1,30 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * NOTICE from the Sentry authors: + * - Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/15ef7506553f631ea4181391e0c5725a56f0d082/packages/instrumentation-tedious + * - Upstream version: @opentelemetry/instrumentation-tedious@0.37.0 + */ +/* eslint-disable */ + +import { InstrumentationConfig } from '@opentelemetry/instrumentation'; +export interface TediousInstrumentationConfig extends InstrumentationConfig { + /** + * If true, injects the current DB span's W3C traceparent into SQL Server + * session state via `SET CONTEXT_INFO @opentelemetry_traceparent` (varbinary). + * Off by default to avoid the extra round-trip per request. + */ + enableTraceContextPropagation?: boolean; +} diff --git a/packages/node/src/integrations/tracing/tedious/vendored/utils.ts b/packages/node/src/integrations/tracing/tedious/vendored/utils.ts new file mode 100644 index 000000000000..e0e8d2c47361 --- /dev/null +++ b/packages/node/src/integrations/tracing/tedious/vendored/utils.ts @@ -0,0 +1,58 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * NOTICE from the Sentry authors: + * - Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/15ef7506553f631ea4181391e0c5725a56f0d082/packages/instrumentation-tedious + * - Upstream version: @opentelemetry/instrumentation-tedious@0.37.0 + */ +/* eslint-disable */ + +/** + * The span name SHOULD be set to a low cardinality value + * representing the statement executed on the database. + * + * @returns Operation executed on Tedious Connection. Does not map to SQL statement in any way. + */ +export function getSpanName( + operation: string, + db: string | undefined, + sql: string | undefined, + bulkLoadTable: string | undefined, +): string { + if (operation === 'execBulkLoad' && bulkLoadTable && db) { + return `${operation} ${bulkLoadTable} ${db}`; + } + if (operation === 'callProcedure') { + // `sql` refers to procedure name with `callProcedure` + if (db) { + return `${operation} ${sql} ${db}`; + } + return `${operation} ${sql}`; + } + // do not use `sql` in general case because of high-cardinality + if (db) { + return `${operation} ${db}`; + } + return `${operation}`; +} + +export const once = (fn: Function) => { + let called = false; + return (...args: unknown[]) => { + if (called) return; + called = true; + return fn(...args); + }; +}; diff --git a/yarn.lock b/yarn.lock index 7337d680cc8f..149ad5547345 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6166,15 +6166,6 @@ "@types/pg" "8.15.6" "@types/pg-pool" "2.0.7" -"@opentelemetry/instrumentation-tedious@0.33.0": - version "0.33.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.33.0.tgz#00f6698f8afae1b350bf0c463a59eeae3c8d25d7" - integrity sha512-Q6WQwAD01MMTub31GlejoiFACYNw26J426wyjvU7by7fDIr2nZXNW4vhTGs7i7F0TnXBO3xN688g1tdUgYwJ5w== - dependencies: - "@opentelemetry/instrumentation" "^0.214.0" - "@opentelemetry/semantic-conventions" "^1.33.0" - "@types/tedious" "^4.0.14" - "@opentelemetry/instrumentation@0.214.0", "@opentelemetry/instrumentation@^0.214.0": version "0.214.0" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.214.0.tgz#2649e8a29a8c4748bc583d35281c80632f046e25" @@ -9710,13 +9701,6 @@ resolved "https://registry.yarnpkg.com/@types/symlink-or-copy/-/symlink-or-copy-1.2.0.tgz#4151a81b4052c80bc2becbae09f3a9ec010a9c7a" integrity sha512-Lja2xYuuf2B3knEsga8ShbOdsfNOtzT73GyJmZyY7eGl2+ajOqrs8yM5ze0fsSoYwvA6bw7/Qr7OZ7PEEmYwWg== -"@types/tedious@^4.0.14": - version "4.0.14" - resolved "https://registry.yarnpkg.com/@types/tedious/-/tedious-4.0.14.tgz#868118e7a67808258c05158e9cad89ca58a2aec1" - integrity sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw== - dependencies: - "@types/node" "*" - "@types/tough-cookie@*": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.0.tgz#fef1904e4668b6e5ecee60c52cc6a078ffa6697d"