From 1f56ffa09f4f0deb4a33a8f1a0e7d272ebfb038e Mon Sep 17 00:00:00 2001 From: Kaung Zin Hein <83657429+Zen-cronic@users.noreply.github.com> Date: Mon, 11 Nov 2024 12:26:27 -0500 Subject: [PATCH] feat(node): Add `knex` integration (#13526) Implement Knex OTEL instrumentation in `packages/node`. This integration is not enabled by default. Signed-off-by: Kaung Zin Hein Co-authored-by: Abhijeet Prasad --- .../node-integration-tests/package.json | 1 + .../suites/tracing/knex/docker-compose.yml | 23 ++++ .../tracing/knex/scenario-withMysql2.js | 53 +++++++ .../tracing/knex/scenario-withPostgres.js | 53 +++++++ .../suites/tracing/knex/test.ts | 129 ++++++++++++++++++ packages/astro/src/index.server.ts | 1 + packages/aws-serverless/src/index.ts | 1 + packages/bun/src/index.ts | 1 + packages/google-cloud-serverless/src/index.ts | 1 + packages/node/package.json | 9 +- packages/node/src/index.ts | 1 + .../node/src/integrations/tracing/knex.ts | 47 +++++++ packages/remix/src/index.server.ts | 1 + packages/solidstart/src/server/index.ts | 1 + packages/sveltekit/src/server/index.ts | 1 + yarn.lock | 69 +++++++++- 16 files changed, 384 insertions(+), 8 deletions(-) create mode 100644 dev-packages/node-integration-tests/suites/tracing/knex/docker-compose.yml create mode 100644 dev-packages/node-integration-tests/suites/tracing/knex/scenario-withMysql2.js create mode 100644 dev-packages/node-integration-tests/suites/tracing/knex/scenario-withPostgres.js create mode 100644 dev-packages/node-integration-tests/suites/tracing/knex/test.ts create mode 100644 packages/node/src/integrations/tracing/knex.ts diff --git a/dev-packages/node-integration-tests/package.json b/dev-packages/node-integration-tests/package.json index 14c25b252f3f..e37952cf2381 100644 --- a/dev-packages/node-integration-tests/package.json +++ b/dev-packages/node-integration-tests/package.json @@ -50,6 +50,7 @@ "graphql": "^16.3.0", "http-terminator": "^3.2.0", "ioredis": "^5.4.1", + "knex": "^2.5.1", "kafkajs": "2.2.4", "lru-memoizer": "2.3.0", "mongodb": "^3.7.3", diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/knex/docker-compose.yml new file mode 100644 index 000000000000..50eb7bc94237 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/knex/docker-compose.yml @@ -0,0 +1,23 @@ +version: '3.9' + +services: + db_postgres: + image: postgres:13 + restart: always + container_name: integration-tests-knex-postgres + ports: + - '5445:5432' + environment: + POSTGRES_USER: test + POSTGRES_PASSWORD: test + POSTGRES_DB: tests + + db_mysql2: + image: mysql:8 + restart: always + container_name: integration-tests-knex-mysql2 + ports: + - '3307:3306' + environment: + MYSQL_ROOT_PASSWORD: docker + MYSQL_DATABASE: tests diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/scenario-withMysql2.js b/dev-packages/node-integration-tests/suites/tracing/knex/scenario-withMysql2.js new file mode 100644 index 000000000000..5d57e38d9318 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/knex/scenario-withMysql2.js @@ -0,0 +1,53 @@ +const { loggingTransport } = require('@sentry-internal/node-integration-tests'); +const Sentry = require('@sentry/node'); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0', + tracesSampleRate: 1.0, + transport: loggingTransport, + integrations: [Sentry.knexIntegration()], +}); + +// Stop the process from exiting before the transaction is sent +setInterval(() => {}, 1000); + +const knex = require('knex').default; + +const mysql2Client = knex({ + client: 'mysql2', + connection: { + host: 'localhost', + port: 3307, + user: 'root', + password: 'docker', + database: 'tests', + }, +}); + +async function run() { + await Sentry.startSpan( + { + name: 'Test Transaction', + op: 'transaction', + }, + async () => { + try { + await mysql2Client.schema.createTable('User', table => { + table.increments('id').notNullable().primary({ constraintName: 'User_pkey' }); + table.timestamp('createdAt', { precision: 3 }).notNullable().defaultTo(mysql2Client.fn.now(3)); + table.text('email').notNullable(); + table.text('name').notNullable(); + }); + + await mysql2Client('User').insert({ name: 'jane', email: 'jane@domain.com' }); + await mysql2Client('User').select('*'); + } finally { + await mysql2Client.destroy(); + } + }, + ); +} + +// eslint-disable-next-line @typescript-eslint/no-floating-promises +run(); diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/scenario-withPostgres.js b/dev-packages/node-integration-tests/suites/tracing/knex/scenario-withPostgres.js new file mode 100644 index 000000000000..a9f2d558a618 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/knex/scenario-withPostgres.js @@ -0,0 +1,53 @@ +const { loggingTransport } = require('@sentry-internal/node-integration-tests'); +const Sentry = require('@sentry/node'); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0', + tracesSampleRate: 1.0, + transport: loggingTransport, + integrations: [Sentry.knexIntegration()], +}); + +// Stop the process from exiting before the transaction is sent +setInterval(() => {}, 1000); + +const knex = require('knex').default; + +const pgClient = knex({ + client: 'pg', + connection: { + host: 'localhost', + port: 5445, + user: 'test', + password: 'test', + database: 'tests', + }, +}); + +async function run() { + await Sentry.startSpan( + { + name: 'Test Transaction', + op: 'transaction', + }, + async () => { + try { + await pgClient.schema.createTable('User', table => { + table.increments('id').notNullable().primary({ constraintName: 'User_pkey' }); + table.timestamp('createdAt', { precision: 3 }).notNullable().defaultTo(pgClient.fn.now(3)); + table.text('email').notNullable(); + table.text('name').notNullable(); + }); + + await pgClient('User').insert({ name: 'bob', email: 'bob@domain.com' }); + await pgClient('User').select('*'); + } finally { + await pgClient.destroy(); + } + }, + ); +} + +// eslint-disable-next-line @typescript-eslint/no-floating-promises +run(); diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/test.ts b/dev-packages/node-integration-tests/suites/tracing/knex/test.ts new file mode 100644 index 000000000000..3ededda4f162 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/knex/test.ts @@ -0,0 +1,129 @@ +import { createRunner } from '../../../utils/runner'; + +// When running docker compose, we need a larger timeout, as this takes some time... +jest.setTimeout(90000); + +describe('knex auto instrumentation', () => { + // Update this if another knex version is installed + const KNEX_VERSION = '2.5.1'; + + test('should auto-instrument `knex` package when using `pg` client', done => { + const EXPECTED_TRANSACTION = { + transaction: 'Test Transaction', + spans: expect.arrayContaining([ + expect.objectContaining({ + data: expect.objectContaining({ + 'knex.version': KNEX_VERSION, + 'db.system': 'postgresql', + 'db.name': 'tests', + 'sentry.origin': 'auto.db.otel.knex', + 'sentry.op': 'db', + 'net.peer.name': 'localhost', + 'net.peer.port': 5445, + }), + status: 'ok', + description: + 'create table "User" ("id" serial primary key, "createdAt" timestamptz(3) not null default CURRENT_TIMESTAMP(3), "email" text not null, "name" text not null)', + origin: 'auto.db.otel.knex', + }), + expect.objectContaining({ + data: expect.objectContaining({ + 'knex.version': KNEX_VERSION, + 'db.system': 'postgresql', + 'db.name': 'tests', + 'sentry.origin': 'auto.db.otel.knex', + 'sentry.op': 'db', + 'net.peer.name': 'localhost', + 'net.peer.port': 5445, + }), + status: 'ok', + // In the knex-otel spans, the placeholders (e.g., `$1`) are replaced by a `?`. + description: 'insert into "User" ("email", "name") values (?, ?)', + origin: 'auto.db.otel.knex', + }), + + expect.objectContaining({ + data: expect.objectContaining({ + 'knex.version': KNEX_VERSION, + 'db.operation': 'select', + 'db.sql.table': 'User', + 'db.system': 'postgresql', + 'db.name': 'tests', + 'db.statement': 'select * from "User"', + 'sentry.origin': 'auto.db.otel.knex', + 'sentry.op': 'db', + }), + status: 'ok', + description: 'select * from "User"', + origin: 'auto.db.otel.knex', + }), + ]), + }; + + createRunner(__dirname, 'scenario-withPostgres.js') + .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) + .expect({ transaction: EXPECTED_TRANSACTION }) + .start(done); + }); + + test('should auto-instrument `knex` package when using `mysql2` client', done => { + const EXPECTED_TRANSACTION = { + transaction: 'Test Transaction', + spans: expect.arrayContaining([ + expect.objectContaining({ + data: expect.objectContaining({ + 'knex.version': KNEX_VERSION, + 'db.system': 'mysql2', + 'db.name': 'tests', + 'db.user': 'root', + 'sentry.origin': 'auto.db.otel.knex', + 'sentry.op': 'db', + 'net.peer.name': 'localhost', + 'net.peer.port': 3307, + }), + status: 'ok', + description: + 'create table `User` (`id` int unsigned not null auto_increment primary key, `createdAt` timestamp(3) not null default CURRENT_TIMESTAMP(3), `email` text not null, `name` text not null)', + origin: 'auto.db.otel.knex', + }), + expect.objectContaining({ + data: expect.objectContaining({ + 'knex.version': KNEX_VERSION, + 'db.system': 'mysql2', + 'db.name': 'tests', + 'db.user': 'root', + 'sentry.origin': 'auto.db.otel.knex', + 'sentry.op': 'db', + 'net.peer.name': 'localhost', + 'net.peer.port': 3307, + }), + status: 'ok', + description: 'insert into `User` (`email`, `name`) values (?, ?)', + origin: 'auto.db.otel.knex', + }), + + expect.objectContaining({ + data: expect.objectContaining({ + 'knex.version': KNEX_VERSION, + 'db.operation': 'select', + 'db.sql.table': 'User', + 'db.system': 'mysql2', + 'db.name': 'tests', + 'db.statement': 'select * from `User`', + 'db.user': 'root', + 'sentry.origin': 'auto.db.otel.knex', + 'sentry.op': 'db', + }), + status: 'ok', + description: 'select * from `User`', + origin: 'auto.db.otel.knex', + }), + ]), + }; + + createRunner(__dirname, 'scenario-withMysql2.js') + .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port: 3306'] }) + .expect({ transaction: EXPECTED_TRANSACTION }) + .start(done); + }); +}); diff --git a/packages/astro/src/index.server.ts b/packages/astro/src/index.server.ts index b2f57937148a..e4c871ec74ea 100644 --- a/packages/astro/src/index.server.ts +++ b/packages/astro/src/index.server.ts @@ -69,6 +69,7 @@ export { isInitialized, kafkaIntegration, koaIntegration, + knexIntegration, lastEventId, linkedErrorsIntegration, localVariablesIntegration, diff --git a/packages/aws-serverless/src/index.ts b/packages/aws-serverless/src/index.ts index e6a21a498e2e..060dddd51787 100644 --- a/packages/aws-serverless/src/index.ts +++ b/packages/aws-serverless/src/index.ts @@ -92,6 +92,7 @@ export { fsIntegration, genericPoolIntegration, graphqlIntegration, + knexIntegration, kafkaIntegration, lruMemoizerIntegration, mongoIntegration, diff --git a/packages/bun/src/index.ts b/packages/bun/src/index.ts index 246f6ca9049c..5688d1007769 100644 --- a/packages/bun/src/index.ts +++ b/packages/bun/src/index.ts @@ -113,6 +113,7 @@ export { setupConnectErrorHandler, genericPoolIntegration, graphqlIntegration, + knexIntegration, kafkaIntegration, lruMemoizerIntegration, mongoIntegration, diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index 5efe98ac5474..5e1c2bba5bc1 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -92,6 +92,7 @@ export { fastifyIntegration, genericPoolIntegration, graphqlIntegration, + knexIntegration, kafkaIntegration, lruMemoizerIntegration, mongoIntegration, diff --git a/packages/node/package.json b/packages/node/package.json index 8de05630c1b5..6e7a2a1660e1 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -9,9 +9,7 @@ "engines": { "node": ">=14.18" }, - "files": [ - "/build" - ], + "files": ["/build"], "main": "build/cjs/index.js", "module": "build/esm/index.js", "types": "build/types/index.d.ts", @@ -56,9 +54,7 @@ }, "typesVersions": { "<4.9": { - "build/types/index.d.ts": [ - "build/types-ts3.8/index.d.ts" - ] + "build/types/index.d.ts": ["build/types-ts3.8/index.d.ts"] } }, "publishConfig": { @@ -81,6 +77,7 @@ "@opentelemetry/instrumentation-http": "0.53.0", "@opentelemetry/instrumentation-ioredis": "0.43.0", "@opentelemetry/instrumentation-kafkajs": "0.4.0", + "@opentelemetry/instrumentation-knex": "0.41.0", "@opentelemetry/instrumentation-koa": "0.43.0", "@opentelemetry/instrumentation-lru-memoizer": "0.40.0", "@opentelemetry/instrumentation-mongodb": "0.48.0", diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 97694bee7c70..6ab536034894 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -28,6 +28,7 @@ export { hapiIntegration, setupHapiErrorHandler } from './integrations/tracing/h export { koaIntegration, setupKoaErrorHandler } from './integrations/tracing/koa'; export { connectIntegration, setupConnectErrorHandler } from './integrations/tracing/connect'; export { spotlightIntegration } from './integrations/spotlight'; +export { knexIntegration } from './integrations/tracing/knex'; export { tediousIntegration } from './integrations/tracing/tedious'; export { genericPoolIntegration } from './integrations/tracing/genericPool'; export { dataloaderIntegration } from './integrations/tracing/dataloader'; diff --git a/packages/node/src/integrations/tracing/knex.ts b/packages/node/src/integrations/tracing/knex.ts new file mode 100644 index 000000000000..55457680e101 --- /dev/null +++ b/packages/node/src/integrations/tracing/knex.ts @@ -0,0 +1,47 @@ +import { KnexInstrumentation } from '@opentelemetry/instrumentation-knex'; +import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, defineIntegration, spanToJSON } from '@sentry/core'; +import type { IntegrationFn } from '@sentry/types'; +import { generateInstrumentOnce } from '../../otel/instrument'; + +const INTEGRATION_NAME = 'Knex'; + +export const instrumentKnex = generateInstrumentOnce( + INTEGRATION_NAME, + () => new KnexInstrumentation({ requireParentSpan: true }), +); + +const _knexIntegration = (() => { + return { + name: INTEGRATION_NAME, + setupOnce() { + instrumentKnex(); + }, + + setup(client) { + client.on('spanStart', span => { + const { data } = spanToJSON(span); + // knex.version is always set in the span data + // https://github.com/open-telemetry/opentelemetry-js-contrib/blob/0309caeafc44ac9cb13a3345b790b01b76d0497d/plugins/node/opentelemetry-instrumentation-knex/src/instrumentation.ts#L138 + if (data && 'knex.version' in data) { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.knex'); + } + }); + }, + }; +}) satisfies IntegrationFn; + +/** + * Knex integration + * + * Capture tracing data for [Knex](https://knexjs.org/). + * + * @example + * ```javascript + * import * as Sentry from '@sentry/node'; + * + * Sentry.init({ + * integrations: [Sentry.knexIntegration()], + * }); + * ``` + */ +export const knexIntegration = defineIntegration(_knexIntegration); diff --git a/packages/remix/src/index.server.ts b/packages/remix/src/index.server.ts index d0ee1082426d..1291a3fb8767 100644 --- a/packages/remix/src/index.server.ts +++ b/packages/remix/src/index.server.ts @@ -68,6 +68,7 @@ export { inboundFiltersIntegration, initOpenTelemetry, isInitialized, + knexIntegration, kafkaIntegration, koaIntegration, lastEventId, diff --git a/packages/solidstart/src/server/index.ts b/packages/solidstart/src/server/index.ts index 21705110361b..54e0e8cf68ab 100644 --- a/packages/solidstart/src/server/index.ts +++ b/packages/solidstart/src/server/index.ts @@ -59,6 +59,7 @@ export { inboundFiltersIntegration, initOpenTelemetry, isInitialized, + knexIntegration, kafkaIntegration, koaIntegration, lastEventId, diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts index bf317b964191..05f105c252a8 100644 --- a/packages/sveltekit/src/server/index.ts +++ b/packages/sveltekit/src/server/index.ts @@ -61,6 +61,7 @@ export { inboundFiltersIntegration, initOpenTelemetry, isInitialized, + knexIntegration, kafkaIntegration, koaIntegration, lastEventId, diff --git a/yarn.lock b/yarn.lock index f5a01a5a820c..da1c9aa37efc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7603,6 +7603,14 @@ "@opentelemetry/instrumentation" "^0.54.0" "@opentelemetry/semantic-conventions" "^1.27.0" +"@opentelemetry/instrumentation-knex@0.41.0": + version "0.41.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.41.0.tgz#74d611489e823003a825097bac019c6c2ad061a5" + integrity sha512-OhI1SlLv5qnsnm2dOVrian/x3431P75GngSpnR7c4fcVFv7prXGYu29Z6ILRWJf/NJt6fkbySmwdfUUnFnHCTg== + dependencies: + "@opentelemetry/instrumentation" "^0.54.0" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@opentelemetry/instrumentation-koa@0.43.0": version "0.43.0" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.43.0.tgz#963fd192a1b5f6cbae5dabf4ec82e3105cbb23b1" @@ -14464,6 +14472,11 @@ colord@^2.9.3: resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== +colorette@2.0.19: + version "2.0.19" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" + integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== + colorette@^2.0.10: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" @@ -14511,7 +14524,7 @@ commander@7.2.0, commander@^7.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== -commander@^10.0.1: +commander@^10.0.0, commander@^10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== @@ -17885,7 +17898,7 @@ esm-env@^1.0.0: resolved "https://registry.yarnpkg.com/esm-env/-/esm-env-1.0.0.tgz#b124b40b180711690a4cb9b00d16573391950413" integrity sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA== -esm@^3.2.4: +esm@^3.2.25, esm@^3.2.4: version "3.2.25" resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== @@ -19196,6 +19209,11 @@ get-value@^2.0.3, get-value@^2.0.6: resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= +getopts@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.3.0.tgz#71e5593284807e03e2427449d4f6712a268666f4" + integrity sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA== + giget@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/giget/-/giget-1.2.3.tgz#ef6845d1140e89adad595f7f3bb60aa31c672cb6" @@ -20848,6 +20866,11 @@ interpret@^1.0.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== +interpret@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" + integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== + invariant@^2.2.1, invariant@^2.2.2: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -22515,6 +22538,26 @@ klona@^2.0.4, klona@^2.0.5, klona@^2.0.6: resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.6.tgz#85bffbf819c03b2f53270412420a4555ef882e22" integrity sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA== +knex@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/knex/-/knex-2.5.1.tgz#a6c6b449866cf4229f070c17411f23871ba52ef9" + integrity sha512-z78DgGKUr4SE/6cm7ku+jHvFT0X97aERh/f0MUKAKgFnwCYBEW4TFBqtHWFYiJFid7fMrtpZ/gxJthvz5mEByA== + dependencies: + colorette "2.0.19" + commander "^10.0.0" + debug "4.3.4" + escalade "^3.1.1" + esm "^3.2.25" + get-package-type "^0.1.0" + getopts "2.3.0" + interpret "^2.2.0" + lodash "^4.17.21" + pg-connection-string "2.6.1" + rechoir "^0.8.0" + resolve-from "^5.0.0" + tarn "^3.0.2" + tildify "2.0.0" + knitwork@^1.0.0, knitwork@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/knitwork/-/knitwork-1.1.0.tgz#d8c9feafadd7ee744ff64340b216a52c7199c417" @@ -26821,6 +26864,11 @@ periscopic@^3.1.0: estree-walker "^3.0.0" is-reference "^3.0.0" +pg-connection-string@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.6.1.tgz#78c23c21a35dd116f48e12e23c0965e8d9e2cbfb" + integrity sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg== + pg-connection-string@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.5.0.tgz#538cadd0f7e603fc09a12590f3b8a452c2c0cf34" @@ -28762,6 +28810,13 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== + dependencies: + resolve "^1.20.0" + redent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" @@ -31476,6 +31531,11 @@ tar@^6.2.0: mkdirp "^1.0.3" yallist "^4.0.0" +tarn@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/tarn/-/tarn-3.0.2.tgz#73b6140fbb881b71559c4f8bfde3d9a4b3d27693" + integrity sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ== + tedious@^18.6.1: version "18.6.1" resolved "https://registry.yarnpkg.com/tedious/-/tedious-18.6.1.tgz#1c4a3f06c891be67a032117e2e25193286d44496" @@ -31692,6 +31752,11 @@ thunky@^1.0.2: resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== +tildify@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tildify/-/tildify-2.0.0.tgz#f205f3674d677ce698b7067a99e949ce03b4754a" + integrity sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw== + tiny-glob@0.2.9, tiny-glob@^0.2.9: version "0.2.9" resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.9.tgz#2212d441ac17928033b110f8b3640683129d31e2"