From f4e22fbf80388f3b37505ad0ca1c5b48821d30ae Mon Sep 17 00:00:00 2001 From: Dan Caddigan Date: Mon, 15 Feb 2021 01:25:33 -0500 Subject: [PATCH] feat(replication): adds read replica functionality --- examples/12-replica-db-connection/.env | 4 +- .../generated/binding.ts | 1 + .../generated/schema.graphql | 1 + .../12-replica-db-connection/src/server.ts | 14 +++- .../src/user.resolver.ts | 6 ++ .../src/user.service.ts | 13 ++- src/core/BaseService.ts | 44 ++++++++-- src/core/server.test.ts | 10 ++- src/core/server.ts | 20 +---- src/test/functional/server.test.ts | 81 ++++++++----------- src/test/server-vars.ts | 3 - src/test/test-server.ts | 1 - src/torm/createConnection.ts | 68 ++++------------ yarn.lock | 42 +++++----- 14 files changed, 147 insertions(+), 161 deletions(-) diff --git a/examples/12-replica-db-connection/.env b/examples/12-replica-db-connection/.env index 1a7bbaf5..932a143e 100644 --- a/examples/12-replica-db-connection/.env +++ b/examples/12-replica-db-connection/.env @@ -10,4 +10,6 @@ WARTHOG_DB_HOST=localhost WARTHOG_DB_PORT=5432 WARTHOG_DB_REPLICA_HOST=localhost WARTHOG_DB_REPLICA_PORT=5432 -WARTHOG_DB_CONNECT_REPLICA=true +WARTHOG_DB_REPLICA_DATABASE=warthog-example-12 +WARTHOG_DB_REPLICA_USERNAME=postgres +WARTHOG_DB_REPLICA_PASSWORD= diff --git a/examples/12-replica-db-connection/generated/binding.ts b/examples/12-replica-db-connection/generated/binding.ts index 2f36ce89..7588af16 100644 --- a/examples/12-replica-db-connection/generated/binding.ts +++ b/examples/12-replica-db-connection/generated/binding.ts @@ -7,6 +7,7 @@ import * as schema from './schema.graphql' export interface Query { users: >(args: { offset?: Int | null, limit?: Int | null, where?: UserWhereInput | null, orderBy?: UserOrderByInput | null }, info?: GraphQLResolveInfo | string, options?: Options) => Promise , + usersFromMaster: >(args?: {}, info?: GraphQLResolveInfo | string, options?: Options) => Promise , user: (args: { where: UserWhereUniqueInput }, info?: GraphQLResolveInfo | string, options?: Options) => Promise } diff --git a/examples/12-replica-db-connection/generated/schema.graphql b/examples/12-replica-db-connection/generated/schema.graphql index 28f77acf..eed1d058 100644 --- a/examples/12-replica-db-connection/generated/schema.graphql +++ b/examples/12-replica-db-connection/generated/schema.graphql @@ -79,6 +79,7 @@ type PageInfo { type Query { users(offset: Int, limit: Int = 50, where: UserWhereInput, orderBy: UserOrderByInput): [User!]! + usersFromMaster: [User!]! user(where: UserWhereUniqueInput!): User! } diff --git a/examples/12-replica-db-connection/src/server.ts b/examples/12-replica-db-connection/src/server.ts index 3ca418b0..2c235e89 100644 --- a/examples/12-replica-db-connection/src/server.ts +++ b/examples/12-replica-db-connection/src/server.ts @@ -1,4 +1,5 @@ import 'reflect-metadata'; +import { AdvancedConsoleLogger, Logger, QueryRunner } from 'typeorm'; import { BaseContext, Server } from '../../../src'; @@ -10,6 +11,16 @@ interface Context extends BaseContext { }; } +export class CustomLogger extends AdvancedConsoleLogger implements Logger { + logQuery(query: string, parameters?: any[], queryRunner?: QueryRunner) { + if (!queryRunner) { + return console.log(query); + } + + console.log(`[${(queryRunner as any).mode}] ${query}`); + } +} + export function getServer(AppOptions = {}, dbOptions = {}) { return new Server( { @@ -23,9 +34,8 @@ export function getServer(AppOptions = {}, dbOptions = {}) { } }; }, - connectDBReplica: true, ...AppOptions }, - dbOptions + { ...dbOptions, logger: new CustomLogger() } ); } diff --git a/examples/12-replica-db-connection/src/user.resolver.ts b/examples/12-replica-db-connection/src/user.resolver.ts index fa121ba4..032a7c07 100644 --- a/examples/12-replica-db-connection/src/user.resolver.ts +++ b/examples/12-replica-db-connection/src/user.resolver.ts @@ -22,6 +22,12 @@ export class UserResolver { return this.userService.find(where, orderBy, limit, offset); } + // This query will use the "master" server, even though it would typically use "slave" by default for doing a find + @Query(() => [User]) + async usersFromMaster(): Promise { + return this.userService.findFromMaster(); + } + @Query(() => User) async user(@Arg('where') where: UserWhereUniqueInput): Promise { return this.userService.findOne(where); diff --git a/examples/12-replica-db-connection/src/user.service.ts b/examples/12-replica-db-connection/src/user.service.ts index cbd0869a..9b6350f3 100644 --- a/examples/12-replica-db-connection/src/user.service.ts +++ b/examples/12-replica-db-connection/src/user.service.ts @@ -1,5 +1,5 @@ import { Service } from 'typedi'; -import { DeepPartial, Repository } from 'typeorm'; +import { Repository } from 'typeorm'; import { InjectRepository } from 'typeorm-typedi-extensions'; import { BaseService } from '../../../src'; @@ -12,11 +12,10 @@ export class UserService extends BaseService { super(User, repository); } - async create(data: DeepPartial, userId: string): Promise { - const newUser = await super.create(data, userId); - - // Perform some side effects - - return newUser; + // This query will use the "master" server, even though it would typically use "slave" by default for doing a find + async findFromMaster(): Promise { + return super.find(undefined, undefined, undefined, undefined, undefined, { + replicationMode: 'master' + }); } } diff --git a/src/core/BaseService.ts b/src/core/BaseService.ts index 12159b59..af7e1d73 100644 --- a/src/core/BaseService.ts +++ b/src/core/BaseService.ts @@ -5,6 +5,8 @@ import { DeepPartial, EntityManager, getRepository, + QueryBuilder, + ReplicationMode, Repository, SelectQueryBuilder } from 'typeorm'; @@ -34,6 +36,10 @@ export interface BaseOptions { manager?: EntityManager; // Allows consumers to pass in a TransactionManager } +export interface AdvancedFindOptions { + replicationMode?: ReplicationMode; +} + interface WhereFilterAttributes { [key: string]: string | number | null; } @@ -114,12 +120,13 @@ export class BaseService { orderBy?: string, limit?: number, offset?: number, - fields?: string[] + fields?: string[], + options: AdvancedFindOptions = {} ): Promise { // TODO: FEATURE - make the default limit configurable limit = limit ?? 20; debugStatement('find:buildQuery'); - const qb = this.buildFindQuery(where, orderBy, { limit, offset }, fields); + const qb = this.buildFindQuery(where, orderBy, { limit, offset }, fields, options); try { debugStatement('find:gettingMany'); const records = await qb.getMany(); @@ -129,6 +136,8 @@ export class BaseService { debugStatement('find:error'); logger.error('failed on getMany', e); throw e; + } finally { + this.cleanUpQueryBuilder(qb); } } @@ -137,7 +146,8 @@ export class BaseService { whereUserInput: any = {}, // V3: WhereExpression = {}, orderBy?: string | string[], _pageOptions: RelayPageOptionsInput = {}, - fields?: ConnectionInputFields + fields?: ConnectionInputFields, + options: AdvancedFindOptions = {} ): Promise> { // TODO: if the orderby items aren't included in `fields`, should we automatically include? @@ -176,7 +186,8 @@ export class BaseService { whereCombined, this.relayService.effectiveOrderStrings(sorts, relayPageOptions), { limit: limit + 1 }, // We ask for 1 too many so that we know if there is an additional page - requestedFields.selectFields + requestedFields.selectFields, + options ); let rawData; @@ -189,6 +200,8 @@ export class BaseService { rawData = await qb.getMany(); } + this.cleanUpQueryBuilder(qb); + // If we got the n+1 that we requested, pluck the last item off const returnData = rawData.length > limit ? rawData.slice(0, limit) : rawData; @@ -209,13 +222,19 @@ export class BaseService { where: WhereExpression = {}, orderBy?: string | string[], pageOptions?: LimitOffset, - fields?: string[] + fields?: string[], + options: AdvancedFindOptions = {} ): SelectQueryBuilder { try { const DEFAULT_LIMIT = 50; - let qb = this.manager.connection - .createQueryBuilder(this.entityClass, this.klass) - .setQueryRunner(this.manager.connection.createQueryRunner('slave')); + let qb = this.manager.connection.createQueryBuilder(this.entityClass, this.klass); + if (options.replicationMode) { + const queryRunner = this.manager.connection.createQueryRunner(options.replicationMode); + qb.setQueryRunner(queryRunner); + (qb as any).warthogQueryRunnerOverride = queryRunner; + } + + // if (!pageOptions) { pageOptions = { limit: DEFAULT_LIMIT @@ -476,6 +495,15 @@ export class BaseService { return { id: found.id }; } + // This is really ugly. Shouldn't be attaching to the querybuilder, but need to keep track of whether this + // instance of the queryBuilder was created with a custom query runner so that it can be cleaned up + cleanUpQueryBuilder(qb: QueryBuilder) { + // console.log(qb); + if ((qb as any).warthogQueryRunnerOverride) { + (qb as any).warthogQueryRunnerOverride.release(); + } + } + attrsToDBColumns = (attrs: string[]): string[] => { return attrs.map(this.attrToDBColumn); }; diff --git a/src/core/server.test.ts b/src/core/server.test.ts index 73d05936..c0fac121 100644 --- a/src/core/server.test.ts +++ b/src/core/server.test.ts @@ -50,8 +50,14 @@ describe('Server', () => { const customExpressApp: express.Application = express(); const appListenSpy = jest.spyOn(customExpressApp, 'listen'); server = buildServer( - { expressApp: customExpressApp, connectDBReplica: true }, - { WARTHOG_DB_CONNECT_REPLICA: 'true' } + { expressApp: customExpressApp }, + { + WARTHOG_DB_REPLICA_HOST: 'localhost', + WARTHOG_DB_REPLICA_DATABASE: 'warthog-test', + WARTHOG_DB_REPLICA_PORT: '5432', + WARTHOG_DB_REPLICA_USERNAME: 'postgres', + WARTHOG_DB_REPLICA_PASSWORD: '' + } ); await server.start(); const binding = await server.getBinding(); diff --git a/src/core/server.ts b/src/core/server.ts index e46954db..0542dc67 100644 --- a/src/core/server.ts +++ b/src/core/server.ts @@ -17,7 +17,7 @@ import { Connection, ConnectionOptions, useContainer as TypeORMUseContainer } fr import { logger, Logger } from '../core/logger'; import { getRemoteBinding } from '../gql'; import { DataLoaderMiddleware, healthCheckMiddleware } from '../middleware'; -import { createDBConnection, createReplicatedDBConnection } from '../torm'; +import { createDBConnection, WarthogDBConnectionOptions } from '../torm'; import { CodeGenerator } from './code-generator'; import { Config } from './config'; @@ -47,7 +47,6 @@ export interface ServerOptions { bodyParserConfig?: OptionsJson; onBeforeGraphQLMiddleware?: (app: express.Application) => void; onAfterGraphQLMiddleware?: (app: express.Application) => void; - connectDBReplica?: boolean; } export class Server { @@ -66,8 +65,7 @@ export class Server { constructor( private appOptions: ServerOptions, - private dbOptions: Partial = {}, - private dbReplicaOptions: Partial = {} + private dbOptions: Partial = {} ) { if (typeof this.appOptions.host !== 'undefined') { process.env.WARTHOG_APP_HOST = this.appOptions.host; @@ -93,11 +91,6 @@ export class Server { ? 'true' : 'false'; } - if (typeof this.appOptions.connectDBReplica !== 'undefined') { - process.env.WARTHOG_DB_CONNECT_REPLICA = this.appOptions.connectDBReplica ? 'true' : 'false'; - } else { - process.env.WARTHOG_DB_CONNECT_REPLICA = 'false'; - } // Ensure that Warthog, TypeORM and TypeGraphQL are all using the same typedi container this.container = this.appOptions.container || Container; @@ -132,13 +125,8 @@ export class Server { async establishDBConnection(): Promise { if (!this.connection) { debug('establishDBConnection:start'); - if (this.config.get('WARTHOG_DB_CONNECT_REPLICA')) { - this.connection = await createReplicatedDBConnection(this.dbOptions); - this.allConnections = [this.connection]; - } else { - this.connection = await createDBConnection(this.dbOptions); - this.allConnections = [this.connection]; - } + this.connection = await createDBConnection(this.dbOptions); + this.allConnections = [this.connection]; debug('establishDBConnection:end'); } diff --git a/src/test/functional/server.test.ts b/src/test/functional/server.test.ts index 6e4b9485..b8443956 100644 --- a/src/test/functional/server.test.ts +++ b/src/test/functional/server.test.ts @@ -30,13 +30,10 @@ let onBeforeCalled = false; let onAfterCalled = false; let kitchenSink: KitchenSink; -const beforeAllLogic = async (connectDBReplica = true) => { - // setTestServerEnvironmentVariables(); - +const beforeAllLogic = async () => { runKey = String(new Date().getTime()); // used to ensure test runs create unique data - const WARTHOG_DB_CONNECT_REPLICA = connectDBReplica ? 'true' : 'false'; - setTestServerEnvironmentVariables({ WARTHOG_DB_CONNECT_REPLICA }); + setTestServerEnvironmentVariables(); // build a custom express app with a dummy endpoint customExpressApp = buildCustomExpressApp(); @@ -54,8 +51,7 @@ const beforeAllLogic = async (connectDBReplica = true) => { onAfterGraphQLMiddleware: (app: express.Application) => { app; onAfterCalled = true; - }, - connectDBReplica + } }); await server.start(); @@ -358,7 +354,7 @@ const runTests = () => { ); expect(result.length).toEqual(30); - expect(result[0].dateOnlyField!.length).toBe(10); + expect((result[0].dateOnlyField as string).length).toBe(10); expect(result).toMatchSnapshot(); }); @@ -369,7 +365,7 @@ const runTests = () => { ); expect(result.length).toEqual(69); - expect(result[0].dateTimeField!.length).toBe(24); + expect((result[0].dateTimeField as string).length).toBe(24); expect(result).toMatchSnapshot(); }); @@ -443,19 +439,6 @@ const runTests = () => { expect(result).toMatchSnapshot(); }); - // - // - - // jsonField: { - // foo: 'bar', - // quia: 'autem' - // }, - // arrayOfInts: [1], - // arrayOfStrings: [] - - // - // - test('findOne: consequuntur-94489@a.com', async () => { expect.assertions(1); @@ -769,41 +752,43 @@ const runTests = () => { }); }); }); +}; - describe('server', () => { - xdescribe('no replica', () => { - // Make sure to clean up server - beforeAll(async done => { - await beforeAllLogic(false); - done(); - }); +// TODO: FIX THESE TESTS +describe('server', () => { + xdescribe('no replica', () => { + // Make sure to clean up server + beforeAll(async done => { + await beforeAllLogic(); + done(); + }); - runTests(); + runTests(); - // Make sure to clean up server - afterAll(async done => { - await server.stop(); - done(); - }); + // Make sure to clean up server + afterAll(async done => { + await server.stop(); + done(); }); + }); - describe('with replica', () => { - // Make sure to clean up server - beforeAll(async done => { - await beforeAllLogic(true); - done(); - }); + describe('with replica', () => { + // Make sure to clean up server + beforeAll(async done => { + // TODO: FIX THIS TEST + await beforeAllLogic(); + done(); + }); - runTests(); + runTests(); - // Make sure to clean up server - afterAll(async done => { - await server.stop(); - done(); - }); + // Make sure to clean up server + afterAll(async done => { + await server.stop(); + done(); }); }); -}; +}); async function createKitchenSink( binding: any, diff --git a/src/test/server-vars.ts b/src/test/server-vars.ts index e7739b17..fa862564 100644 --- a/src/test/server-vars.ts +++ b/src/test/server-vars.ts @@ -27,9 +27,6 @@ export function getStandardEnvironmentVariables(): StringMap { WARTHOG_DB_DATABASE: 'warthog-test', WARTHOG_DB_ENTITIES: 'src/test/modules/**/*.model.ts', WARTHOG_DB_HOST: 'localhost', - WARTHOG_DB_REPLICA_HOST: 'localhost', - WARTHOG_DB_REPLICA_PORT: '5432', - WARTHOG_DB_CONNECT_REPLICA: 'false', WARTHOG_DB_LOGGING: 'none', WARTHOG_DB_MIGRATIONS_DIR: './tmp/test/migrations', WARTHOG_DB_OVERRIDE: 'true', // Set so that we can do DB stuff outside of NODE_ENV=development diff --git a/src/test/test-server.ts b/src/test/test-server.ts index e5f115a1..ff0b69ec 100644 --- a/src/test/test-server.ts +++ b/src/test/test-server.ts @@ -22,7 +22,6 @@ export function getTestServer(options: ServerOptions = {}) { }, introspection: true, openPlayground: false, - connectDBReplica: true, ...options }); } diff --git a/src/torm/createConnection.ts b/src/torm/createConnection.ts index b194d582..e0744a50 100644 --- a/src/torm/createConnection.ts +++ b/src/torm/createConnection.ts @@ -2,12 +2,13 @@ import { Connection, ConnectionOptions, createConnection } from 'typeorm'; import { SnakeNamingStrategy } from './SnakeNamingStrategy'; import { logger } from '../core/logger'; +import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions'; -export const REPLICA_CONNECTION_NAME = 'replica'; +export type WarthogDBConnectionOptions = PostgresConnectionOptions; // TODO: Need to figure out a way for this and the generated ormconfig to be one and the same -export function getBaseConfig() { - return { +export function getBaseConfig(): WarthogDBConnectionOptions { + const config: PostgresConnectionOptions = { cli: { entitiesDir: process.env.WARTHOG_DB_ENTITIES_DIR, migrationsDir: process.env.WARTHOG_DB_MIGRATIONS_DIR, @@ -16,36 +17,20 @@ export function getBaseConfig() { database: process.env.WARTHOG_DB_DATABASE!, // eslint-disable-line @typescript-eslint/no-non-null-assertion entities: getDatabaseEntityPaths(), host: process.env.WARTHOG_DB_HOST!, // eslint-disable-line @typescript-eslint/no-non-null-assertion - logger: process.env.WARTHOG_DB_LOGGER, - logging: process.env.WARTHOG_DB_LOGGING!, // eslint-disable-line @typescript-eslint/no-non-null-assertion + logger: process.env.WARTHOG_DB_LOGGER as any, + logging: process.env.WARTHOG_DB_LOGGING! as any, // eslint-disable-line @typescript-eslint/no-non-null-assertion migrations: getDatabaseMigrationPaths(), namingStrategy: new SnakeNamingStrategy(), password: process.env.WARTHOG_DB_PASSWORD, port: parseInt(process.env.WARTHOG_DB_PORT || '', 10), subscribers: getDatabaseSubscriberPaths(), synchronize: process.env.WARTHOG_DB_SYNCHRONIZE === 'true', - type: process.env.WARTHOG_DB_CONNECTION!, // eslint-disable-line @typescript-eslint/no-non-null-assertion + type: 'postgres', username: process.env.WARTHOG_DB_USERNAME }; -} -export function getBaseReplicatedConfig() { - return { - cli: { - entitiesDir: process.env.WARTHOG_DB_ENTITIES_DIR, - migrationsDir: process.env.WARTHOG_DB_MIGRATIONS_DIR, - subscribersDir: process.env.WARTHOG_DB_SUBSCRIBERS_DIR - }, - entities: getDatabaseEntityPaths(), - logger: process.env.WARTHOG_DB_LOGGER, - logging: process.env.WARTHOG_DB_LOGGING!, // eslint-disable-line @typescript-eslint/no-non-null-assertion - migrations: getDatabaseMigrationPaths(), - namingStrategy: new SnakeNamingStrategy(), - subscribers: getDatabaseSubscriberPaths(), - synchronize: process.env.WARTHOG_DB_SYNCHRONIZE === 'true', - type: process.env.WARTHOG_DB_CONNECTION!, // eslint-disable-line @typescript-eslint/no-non-null-assertion - database: process.env.WARTHOG_DB_DATABASE!, // eslint-disable-line @typescript-eslint/no-non-null-assertion - replication: { + if (process.env.WARTHOG_DB_REPLICA_HOST) { + (config as any).replication = { master: { host: process.env.WARTHOG_DB_HOST!, // eslint-disable-line @typescript-eslint/no-non-null-assertion port: parseInt(process.env.WARTHOG_DB_PORT || '', 10), @@ -57,13 +42,15 @@ export function getBaseReplicatedConfig() { { host: process.env.WARTHOG_DB_REPLICA_HOST, port: parseInt(process.env.WARTHOG_DB_REPLICA_PORT || '', 10), - username: process.env.WARTHOG_DB_USERNAME, - password: process.env.WARTHOG_DB_PASSWORD, - database: process.env.WARTHOG_DB_DATABASE! // eslint-disable-line @typescript-eslint/no-non-null-assertion + username: process.env.WARTHOG_DB_REPLICA_USERNAME, + password: process.env.WARTHOG_DB_REPLICA_PASSWORD, + database: process.env.WARTHOG_DB_REPLICA_DATABASE! // eslint-disable-line @typescript-eslint/no-non-null-assertion } ] - } - }; + }; + } + + return config; } // Note: all DB options should be specified by environment variables @@ -85,29 +72,6 @@ export const createDBConnection = async ( return createConnection(config as any); // TODO: fix any. It is complaining about `type` }; -// Note: all DB options should be specified by environment variables -// Either using TYPEORM_ or WARTHOG_DB_ -export const createReplicatedDBConnection = async ( - dbOptions: Partial = {} -): Promise => { - const config = { - ...getBaseReplicatedConfig(), - ...dbOptions - }; - - if (!config.replication.master.database) { - throw new Error("createReplicatedDBConnection: 'database' is required on the master config"); - } - - if (!config.replication.slaves[0].database) { - throw new Error("createReplicatedDBConnection: 'database' is required on the slave config"); - } - - logger.debug('createReplicatedDBConnection', JSON.stringify(config)); - - return createConnection(config as any); // TODO: fix any. It is complaining about `type` -}; - function getDatabaseEntityPaths(): string[] { return process.env.WARTHOG_DB_ENTITIES ? process.env.WARTHOG_DB_ENTITIES.split(',') diff --git a/yarn.lock b/yarn.lock index 29b655e0..0bbf6106 100644 --- a/yarn.lock +++ b/yarn.lock @@ -678,7 +678,7 @@ "@sqltools/formatter@1.2.2": version "1.2.2" - resolved "https://indigoag.jfrog.io/indigoag/api/npm/npm/@sqltools/formatter/-/formatter-1.2.2.tgz#9390a8127c0dcba61ebd7fdcc748655e191bdd68" + resolved "https://registry.yarnpkg.com/@sqltools/formatter/-/formatter-1.2.2.tgz#9390a8127c0dcba61ebd7fdcc748655e191bdd68" integrity sha1-k5CoEnwNy6YevX/cx0hlXhkb3Wg= "@types/accepts@*", "@types/accepts@^1.3.5": @@ -1840,7 +1840,7 @@ balanced-match@^1.0.0: base64-js@^1.3.1: version "1.5.1" - resolved "https://indigoag.jfrog.io/indigoag/api/npm/npm/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha1-GxtEAWClv3rUC2UPCVljSBkDkwo= base@^0.11.1: @@ -2003,7 +2003,7 @@ buffer-writer@2.0.0: buffer@^5.5.0: version "5.7.1" - resolved "https://indigoag.jfrog.io/indigoag/api/npm/npm/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" integrity sha1-umLnwTEzBTWCGXFghRqPZI6Z7tA= dependencies: base64-js "^1.3.1" @@ -2198,7 +2198,7 @@ chalk@^3.0.0: chalk@^4.0.0, chalk@^4.1.0: version "4.1.0" - resolved "https://indigoag.jfrog.io/indigoag/api/npm/npm/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" integrity sha1-ThSHCmGNni7dl92DRf2dncMVZGo= dependencies: ansi-styles "^4.1.0" @@ -2304,7 +2304,7 @@ cli-cursor@^3.1.0: cli-highlight@^2.1.10: version "2.1.10" - resolved "https://indigoag.jfrog.io/indigoag/api/npm/npm/cli-highlight/-/cli-highlight-2.1.10.tgz#26a087da9209dce4fcb8cf5427dc97cd96ac173a" + resolved "https://registry.yarnpkg.com/cli-highlight/-/cli-highlight-2.1.10.tgz#26a087da9209dce4fcb8cf5427dc97cd96ac173a" integrity sha1-JqCH2pIJ3OT8uM9UJ9yXzZasFzo= dependencies: chalk "^4.0.0" @@ -2387,7 +2387,7 @@ cliui@^6.0.0: cliui@^7.0.2: version "7.0.4" - resolved "https://indigoag.jfrog.io/indigoag/api/npm/npm/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" integrity sha1-oCZe5lVHb8gHrqnfPfjfd4OAi08= dependencies: string-width "^4.2.0" @@ -3243,7 +3243,7 @@ es6-promisify@^5.0.0: escalade@^3.1.1: version "3.1.1" - resolved "https://indigoag.jfrog.io/indigoag/api/npm/npm/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha1-2M/ccACWXFoBdLSoLqpcBVJ0LkA= escape-html@~1.0.3: @@ -3985,7 +3985,7 @@ get-caller-file@^1.0.1: get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" - resolved "https://indigoag.jfrog.io/indigoag/api/npm/npm/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha1-T5RBKoLbMvNuOwuXQfipf+sDH34= get-own-enumerable-property-symbols@^3.0.0: @@ -4414,7 +4414,7 @@ has@^1.0.3: highlight.js@^10.0.0: version "10.6.0" - resolved "https://indigoag.jfrog.io/indigoag/api/npm/npm/highlight.js/-/highlight.js-10.6.0.tgz#0073aa71d566906965ba6e1b7be7b2682f5e18b6" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.6.0.tgz#0073aa71d566906965ba6e1b7be7b2682f5e18b6" integrity sha1-AHOqcdVmkGllum4be+eyaC9eGLY= hook-std@^2.0.0: @@ -4539,7 +4539,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: ieee754@^1.1.13: version "1.2.1" - resolved "https://indigoag.jfrog.io/indigoag/api/npm/npm/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha1-jrehCmP/8l0VpXsAFYbRd9Gw01I= iferr@^0.1.5: @@ -5514,7 +5514,7 @@ js-yaml@^3.13.1: js-yaml@^3.14.0: version "3.14.1" - resolved "https://indigoag.jfrog.io/indigoag/api/npm/npm/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha1-2ugS/bOCX6MGYJqHFzg8UMNqBTc= dependencies: argparse "^1.0.7" @@ -7580,7 +7580,7 @@ parse-json@^5.0.0: parse5-htmlparser2-tree-adapter@^6.0.0: version "6.0.1" - resolved "https://indigoag.jfrog.io/indigoag/api/npm/npm/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" integrity sha1-LN+a2CMyEUA3DU2/XT6Sx8jdxuY= dependencies: parse5 "^6.0.1" @@ -7592,12 +7592,12 @@ parse5@4.0.0: parse5@^5.1.1: version "5.1.1" - resolved "https://indigoag.jfrog.io/indigoag/api/npm/npm/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" integrity sha1-9o5OW6GFKsLK3AD0VV//bCq7YXg= parse5@^6.0.1: version "6.0.1" - resolved "https://indigoag.jfrog.io/indigoag/api/npm/npm/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha1-4aHAhcVps9wIMhGE8Zo5zCf3wws= parseurl@^1.3.2, parseurl@~1.3.3: @@ -9718,7 +9718,7 @@ tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: tslib@^1.13.0: version "1.14.1" - resolved "https://indigoag.jfrog.io/indigoag/api/npm/npm/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha1-zy04vcNKE0vK8QkcQfZhni9nLQA= tsutils@^3.17.1: @@ -9802,7 +9802,7 @@ typeorm-typedi-extensions@^0.2.3: typeorm@^0.2.31: version "0.2.31" - resolved "https://indigoag.jfrog.io/indigoag/api/npm/npm/typeorm/-/typeorm-0.2.31.tgz#82b8a1b233224f81c738f53b0380386ccf360917" + resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.31.tgz#82b8a1b233224f81c738f53b0380386ccf360917" integrity sha1-grihsjMiT4HHOPU7A4A4bM82CRc= dependencies: "@sqltools/formatter" "1.2.2" @@ -10194,7 +10194,7 @@ wrap-ansi@^6.2.0: wrap-ansi@^7.0.0: version "7.0.0" - resolved "https://indigoag.jfrog.io/indigoag/api/npm/npm/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha1-Z+FFz/UQpqaYS98RUpEdadLrnkM= dependencies: ansi-styles "^4.0.0" @@ -10257,7 +10257,7 @@ xml-name-validator@^3.0.0: xml2js@^0.4.23: version "0.4.23" - resolved "https://indigoag.jfrog.io/indigoag/api/npm/npm/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" integrity sha1-oMaVFnUkIesqx1juTUzPWIQ+rGY= dependencies: sax ">=0.6.0" @@ -10290,7 +10290,7 @@ y18n@^3.2.1: y18n@^5.0.5: version "5.0.5" - resolved "https://indigoag.jfrog.io/indigoag/api/npm/npm/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" integrity sha1-h2nsCNA7HqLfJQCs71YXQ7u5qxg= yallist@^2.1.2: @@ -10368,7 +10368,7 @@ yargs-parser@^18.1.2: yargs-parser@^20.2.2: version "20.2.4" - resolved "https://indigoag.jfrog.io/indigoag/api/npm/npm/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha1-tCiQ8UVmeW+Fro46JSkNIF8VSlQ= yargs-parser@^3.2.0: @@ -10481,7 +10481,7 @@ yargs@^15.3.1: yargs@^16.0.0, yargs@^16.0.3: version "16.2.0" - resolved "https://indigoag.jfrog.io/indigoag/api/npm/npm/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha1-HIK/D2tqZur85+8w43b0mhJHf2Y= dependencies: cliui "^7.0.2"