From f4c7badd59f9415061318fd5e9ba89b8ddf7f329 Mon Sep 17 00:00:00 2001 From: AnDQK Date: Tue, 4 Jul 2023 17:28:36 +0700 Subject: [PATCH 01/20] feat: move statistic feature from v1 to v2 (not tested yet) --- ci/config.json.ci | 9 + config.json | 9 + ..._create_table_daily_stats_account_stats.ts | 26 ++ src/common/constant.ts | 12 + src/common/utils/request.ts | 19 + src/models/account.ts | 2 + src/models/account_statistics.ts | 45 +++ src/models/daily_statistics.ts | 41 ++ src/models/event.ts | 4 + src/models/event_attribute.ts | 16 +- src/models/index.ts | 2 + .../statistics/account_statistics.service.ts | 375 ++++++++++++++++++ .../statistics/daily_statistics.service.ts | 142 +++++++ .../dashboard_statistics.service.ts | 2 +- 14 files changed, 700 insertions(+), 4 deletions(-) create mode 100644 migrations/20230704102018_create_table_daily_stats_account_stats.ts create mode 100644 src/models/account_statistics.ts create mode 100644 src/models/daily_statistics.ts create mode 100644 src/services/statistics/account_statistics.service.ts create mode 100644 src/services/statistics/daily_statistics.service.ts diff --git a/ci/config.json.ci b/ci/config.json.ci index 60126927d..3a4fe8bcd 100644 --- a/ci/config.json.ci +++ b/ci/config.json.ci @@ -154,5 +154,14 @@ "httpBatchRequest": { "dispatchMilisecond": 1000, "batchSizeLimit": 10 + }, + "dailyStatistics": { + "recordsPerCall": 100, + "jobPattern": "0 0 0 * * ?" + }, + "accountStatistics": { + "numberOfTopRecords": 10, + "recordsPerCall": 100, + "jobPattern": "0 0 0 * * ?" } } diff --git a/config.json b/config.json index 8655946a9..080f2ecd1 100644 --- a/config.json +++ b/config.json @@ -154,5 +154,14 @@ "httpBatchRequest": { "dispatchMilisecond": 1000, "batchSizeLimit": 10 + }, + "dailyStatistics": { + "recordsPerCall": 100, + "jobPattern": "0 0 0 * * ?" + }, + "accountStatistics": { + "numberOfTopRecords": 10, + "recordsPerCall": 100, + "jobPattern": "0 0 0 * * ?" } } diff --git a/migrations/20230704102018_create_table_daily_stats_account_stats.ts b/migrations/20230704102018_create_table_daily_stats_account_stats.ts new file mode 100644 index 000000000..20c24a761 --- /dev/null +++ b/migrations/20230704102018_create_table_daily_stats_account_stats.ts @@ -0,0 +1,26 @@ +import { Knex } from 'knex'; + +export async function up(knex: Knex): Promise { + await knex.schema.createTable('daily_statistics', (table: any) => { + table.increments(); + table.bigint('daily_txs').index().notNullable(); + table.bigint('daily_active_addresses').index().notNullable(); + table.bigint('unique_addresses').index().notNullable(); + table.timestamp('date').unique().notNullable(); + }); + await knex.schema.createTable('account_statistics', (table: any) => { + table.increments(); + table.string('address').index().notNullable(); + table.bigint('amount_sent').index().notNullable(); + table.bigint('amount_received').index().notNullable(); + table.bigint('tx_sent').index().notNullable(); + table.bigint('gas_used').index().notNullable(); + table.timestamp('date').index().notNullable(); + table.unique(['address', 'date']); + }); +} + +export async function down(knex: Knex): Promise { + await knex.schema.dropTable('account_statistics'); + await knex.schema.dropTable('daily_statistics'); +} diff --git a/src/common/constant.ts b/src/common/constant.ts index 02a5ec8d9..fa625eb41 100644 --- a/src/common/constant.ts +++ b/src/common/constant.ts @@ -1,6 +1,7 @@ export const REDIS_KEY = { IBC_DENOM: 'ibc_denom', DASHBOARD_STATISTICS: 'dashboard_statistics', + TOP_ACCOUNTS: 'top_accounts', }; export const URL_TYPE_CONSTANTS = { @@ -62,6 +63,9 @@ export const BULL_JOB_NAME = { 'job:check-need-create-event-attr-partition', JOB_CREATE_EVENT_ATTR_PARTITION: 'job:create-event-attr-partition', CRAWL_GENESIS_FEEGRANT: 'crawl:genesis-feegrant', + CRAWL_DAILY_STATISTICS: 'crawl:daily-statistics', + CRAWL_ACCOUNT_STATISTICS: 'crawl:account-statistics', + HANDLE_TOP_ACCOUNTS: 'handle:top-accounts', }; export const SERVICE = { @@ -203,6 +207,14 @@ export const SERVICE = { path: 'v1.CreateEventAttrPartition', }, }, + DailyStatisticsService: { + key: 'DailyStatisticsService', + name: 'v1.DailyStatisticsService', + }, + AccountStatisticsService: { + key: 'AccountStatisticsService', + name: 'v1.AccountStatisticsService', + }, }, }; diff --git a/src/common/utils/request.ts b/src/common/utils/request.ts index 854947411..734a2465c 100644 --- a/src/common/utils/request.ts +++ b/src/common/utils/request.ts @@ -8,3 +8,22 @@ export interface IProposalIdParam { export interface ITxIdsParam { txIds: number[]; } + +export interface IDailyStatsParam { + offset: number; + txIds: number[]; + addresses: string[]; +} + +export interface IAccountStatsParam { + offset: number; + accountStats: IAccountStats[]; +} + +export interface IAccountStats { + address: string; + amount_sent: string; + amount_received: string; + tx_sent: number; + gas_used: string; +} diff --git a/src/models/account.ts b/src/models/account.ts index 5ebacd8a0..4187c9d8c 100644 --- a/src/models/account.ts +++ b/src/models/account.ts @@ -14,6 +14,8 @@ export interface IBalance extends ICoin { } export class Account extends BaseModel { + [relation: string]: any; + id!: number; address!: string; diff --git a/src/models/account_statistics.ts b/src/models/account_statistics.ts new file mode 100644 index 000000000..1aab5ab27 --- /dev/null +++ b/src/models/account_statistics.ts @@ -0,0 +1,45 @@ +import BaseModel from './base'; + +export class AccountStatistics extends BaseModel { + address!: string; + + amount_sent!: string; + + amount_received!: string; + + tx_sent!: number; + + gas_used!: string; + + date!: Date; + + static get tableName() { + return 'account_statistics'; + } + + static get jsonSchema() { + return { + type: 'object', + required: [ + 'address', + 'amount_sent', + 'amount_received', + 'tx_sent', + 'gas_used', + 'date', + ], + properties: { + address: { type: 'string' }, + amount_sent: { type: 'string' }, + amount_received: { type: 'string' }, + tx_sent: { type: 'number' }, + gas_used: { type: 'string' }, + date: { type: 'string', format: 'date-time' }, + }, + }; + } + + static get relationMappings() { + return {}; + } +} diff --git a/src/models/daily_statistics.ts b/src/models/daily_statistics.ts new file mode 100644 index 000000000..2bda2cf2f --- /dev/null +++ b/src/models/daily_statistics.ts @@ -0,0 +1,41 @@ +import BaseModel from './base'; + +export class DailyStatistics extends BaseModel { + daily_txs!: number; + + daily_active_addresses!: number; + + unique_addresses!: number; + + unique_addresses_increase!: number; + + date!: Date; + + static get tableName() { + return 'daily_statistics'; + } + + static get jsonSchema() { + return { + type: 'object', + required: [ + 'daily_txs', + 'daily_active_addresses', + 'unique_addresses', + 'unique_addresses_increase', + 'date', + ], + properties: { + daily_txs: { type: 'number' }, + daily_active_addresses: { type: 'number' }, + unique_addresses: { type: 'number' }, + unique_addresses_increase: { type: 'number' }, + date: { type: 'string', format: 'date-time' }, + }, + }; + } + + static get relationMappings() { + return {}; + } +} diff --git a/src/models/event.ts b/src/models/event.ts index c840b24d2..42b94824e 100644 --- a/src/models/event.ts +++ b/src/models/event.ts @@ -89,5 +89,9 @@ export class Event extends BaseModel { REVOKE_FEEGRANT: 'revoke_feegrant', USE_FEEGRANT: 'use_feegrant', SET_FEEGRANT: 'set_feegrant', + COIN_SPENT: 'coin_spent', + COIN_RECEIVED: 'coin_received', + TX: 'tx', + TRANSFER: 'transfer', }; } diff --git a/src/models/event_attribute.ts b/src/models/event_attribute.ts index 49200ac98..ab6aa6ad3 100644 --- a/src/models/event_attribute.ts +++ b/src/models/event_attribute.ts @@ -61,13 +61,10 @@ export class EventAttribute extends BaseModel { REDELEGATION_RESPONSES: 'redelegation_responses', UNBONDING_RESPONSES: 'unbonding_responses', ACTION: 'action', - TRANSFER: 'transfer', SENDER: 'sender', RECEIVER: 'receiver', SPENDER: 'spender', RECIPIENT: 'recipient', - COIN_RECEIVED: 'coin_received', - COIN_SPENT: 'coin_spent', WITHDRAW_REWARDS: 'withdraw_rewards', AMOUNT: 'amount', VALIDATOR: 'validator', @@ -90,5 +87,18 @@ export class EventAttribute extends BaseModel { GRANTER: 'granter', GRANTEE: 'grantee', FROM: 'from', + FEE: 'fee', + FEE_PAYER: 'fee_payer', + }; + + static ATTRIBUTE_COMPOSITE_KEY = { + COIN_SPENT_SPENDER: 'coin_spent.spender', + COIN_RECEIVED_RECEIVER: 'coin_received.receiver', + COIN_SPENT_AMOUNT: 'coin_spent.amount', + COIN_RECEIVED_AMOUNT: 'coin_received.amount', + USE_FEEGRANT_GRANTER: 'use_feegrant.granter', + USE_FEEGRANT_GRANTEE: 'use_feegrant.grantee', + TX_FEE: 'tx.fee', + TX_FEE_PAYER: 'tx.fee_payer', }; } diff --git a/src/models/index.ts b/src/models/index.ts index 94a7bbe15..0d2256103 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -22,3 +22,5 @@ export * from './delegator'; export * from './code_id_verification'; export * from './feegrant'; export * from './feegrant_history'; +export * from './daily_statistics'; +export * from './account_statistics'; diff --git a/src/services/statistics/account_statistics.service.ts b/src/services/statistics/account_statistics.service.ts new file mode 100644 index 000000000..57461f07c --- /dev/null +++ b/src/services/statistics/account_statistics.service.ts @@ -0,0 +1,375 @@ +/* eslint-disable no-case-declarations */ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { Service } from '@ourparentcenter/moleculer-decorators-extended'; +import { ServiceBroker } from 'moleculer'; +import { parseCoins } from '@cosmjs/proto-signing'; +import { + AccountStatistics, + Event, + EventAttribute, + Transaction, +} from '../../models'; +import { + BULL_JOB_NAME, + IAccountStatsParam, + REDIS_KEY, + SERVICE, +} from '../../common'; +import config from '../../../config.json' assert { type: 'json' }; +import BullableService, { QueueHandler } from '../../base/bullable.service'; + +@Service({ + name: SERVICE.V1.AccountStatisticsService.key, + version: 1, +}) +export default class AccountStatisticsService extends BullableService { + public constructor(public broker: ServiceBroker) { + super(broker); + } + + @QueueHandler({ + queueName: BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, + jobName: BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, + // prefix: `horoscope-v2-${config.chainId}`, + }) + public async handleJob(_payload: IAccountStatsParam): Promise { + try { + const { accountStats } = _payload; + + const syncDate = new Date(); + const endTime = syncDate.setUTCHours(0, 0, 0, 0); + syncDate.setDate(syncDate.getDate() - 1); + const startTime = syncDate.setUTCHours(0, 0, 0, 0); + this.logger.info( + `Get account statistic events at page ${ + _payload.offset + } for day ${new Date(startTime)}` + ); + const dailyEvents = await Transaction.query() + .joinRelated('events[attributes]') + .select( + 'id', + 'gas_used', + 'events:attributes.event_id', + 'events:attributes.composite_key', + 'events:attributes.value' + ) + .where('timestamp', '>=', startTime) + .andWhere('timestamp', '<', endTime) + .andWhere((builder) => + builder.whereIn('events.type', [ + Event.EVENT_TYPE.COIN_SPENT, + Event.EVENT_TYPE.COIN_RECEIVED, + Event.EVENT_TYPE.USE_FEEGRANT, + ]) + ) + .orWhereIn('events:attributes.key', [ + EventAttribute.ATTRIBUTE_KEY.FEE, + EventAttribute.ATTRIBUTE_KEY.FEE_PAYER, + ]) + .limit(config.accountStatistics.recordsPerCall) + .offset(_payload.offset * config.accountStatistics.recordsPerCall); + + if (dailyEvents.length > 0) { + dailyEvents + .filter((event) => event.value.startsWith('aura')) + .forEach((event) => { + if (!accountStats.find((acc) => acc.address === event.value)) { + accountStats.push({ + address: event.value, + amount_sent: '0', + amount_received: '0', + tx_sent: 0, + gas_used: '0', + }); + } + }); + + dailyEvents + .filter((event) => !event.value.startsWith('aura')) + .forEach((event) => { + switch (event.composite_key) { + case EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_AMOUNT: + const addrSpent = dailyEvents.find( + (dEvent) => + dEvent.event_id === event.event_id && + dEvent.composite_key === + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_SPENDER + )?.value; + + const indexSpent = accountStats.indexOf( + accountStats.find((acc) => acc.address === addrSpent)! + ); + + accountStats[indexSpent].amount_sent = ( + BigInt(accountStats[indexSpent].amount_sent) + + BigInt(parseCoins(event.value)[0].amount) + ).toString(); + break; + case EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_AMOUNT: + const addrReceived = dailyEvents.find( + (dEvent) => + dEvent.event_id === event.event_id && + dEvent.composite_key === + EventAttribute.ATTRIBUTE_COMPOSITE_KEY + .COIN_RECEIVED_RECEIVER + )?.value; + + const indexReceived = accountStats.indexOf( + accountStats.find((acc) => acc.address === addrReceived)! + ); + + accountStats[indexReceived].amount_received = ( + BigInt(accountStats[indexReceived].amount_received) + + BigInt(parseCoins(event.value)[0].amount) + ).toString(); + break; + default: + break; + } + }); + + dailyEvents + .filter( + (event) => + event.composite_key === + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.TX_FEE_PAYER + ) + .forEach((event) => { + let addr = event.value; + + const feeGrant = dailyEvents.find( + (dEvent) => + dEvent.event_id === event.event_id && + dEvent.composite_key === + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.USE_FEEGRANT_GRANTEE + ); + + if (feeGrant) { + addr = feeGrant.value; + } + + const index = accountStats.indexOf( + accountStats.find((acc) => acc.address === addr)! + ); + accountStats[index].tx_sent += 1; + accountStats[index].gas_used = ( + BigInt(accountStats[index].gas_used) + BigInt(event.gas_used) + ).toString(); + }); + const offset = _payload.offset + 1; + + await this.createJob( + BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, + BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, + { + offset, + accountStats, + }, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + } + ); + } else { + const dailyAccountStats = accountStats.map((acc) => + AccountStatistics.fromJson({ + ...acc, + date: new Date(startTime).toISOString(), + }) + ); + + await AccountStatistics.query() + .insert(dailyAccountStats) + .catch((error) => { + this.logger.error( + 'Error insert new daily account statistic records' + ); + this.logger.error(error); + }); + + await this.createJob( + BULL_JOB_NAME.HANDLE_TOP_ACCOUNTS, + BULL_JOB_NAME.HANDLE_TOP_ACCOUNTS, + {}, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + } + ); + } + } catch (error) { + this.logger.error(error); + } + } + + @QueueHandler({ + queueName: BULL_JOB_NAME.HANDLE_TOP_ACCOUNTS, + jobName: BULL_JOB_NAME.HANDLE_TOP_ACCOUNTS, + // prefix: `horoscope-v2-${config.chainId}`, + }) + public async handleTopAccounts(_payload: object): Promise { + try { + const syncDate = new Date(); + const now = syncDate.setUTCHours(0, 0, 0, 0); + syncDate.setDate(syncDate.getDate() - 3); + const threeDays = syncDate.setUTCHours(0, 0, 0, 0); + syncDate.setDate(syncDate.getDate() - 15); + const fifteenDays = syncDate.setUTCHours(0, 0, 0, 0); + syncDate.setDate(syncDate.getDate() - 30); + const thirtyDays = syncDate.setUTCHours(0, 0, 0, 0); + + const [threeDayStat, fifteenDayStat, thirtyDayStat] = await Promise.all([ + AccountStatistics.query() + .select('address') + .sum('amount_sent') + .sum('amount_received') + .sum('tx_sent') + .sum('gas_used') + .where('date', '>=', threeDays) + .andWhere('date', '<', now) + .groupBy('address'), + AccountStatistics.query() + .select('address') + .sum('amount_sent') + .sum('amount_received') + .sum('tx_sent') + .sum('gas_used') + .where('date', '>=', fifteenDays) + .andWhere('date', '<', now) + .groupBy('address'), + AccountStatistics.query() + .select('address') + .sum('amount_sent') + .sum('amount_received') + .sum('tx_sent') + .sum('gas_used') + .where('date', '>=', thirtyDays) + .andWhere('date', '<', now) + .groupBy('address'), + ]); + + let topAccounts = await this.broker.cacher?.get(REDIS_KEY.TOP_ACCOUNTS); + topAccounts = { + three_days: this.calculateTop(threeDayStat), + fifteen_days: this.calculateTop(fifteenDayStat), + thirty_days: this.calculateTop(thirtyDayStat), + }; + + this.logger.info(`Update top accounts for day ${now}`); + await this.broker.cacher?.set(REDIS_KEY.TOP_ACCOUNTS, topAccounts); + } catch (error) { + this.logger.error(error); + } + } + + private calculateTop(dayStat: AccountStatistics[]) { + const newTopStat = { + top_amount_sent: [] as any[], + top_amount_received: [] as any[], + top_tx_sent: [] as any[], + top_gas_used: [] as any[], + }; + dayStat.forEach((stat) => { + newTopStat.top_amount_sent.push({ + address: stat.address, + amount: stat.amount_sent, + percentage: Number( + (BigInt(stat.amount_sent) * BigInt(100)) / + BigInt( + dayStat + .map((statistic) => statistic.amount_sent) + .reduce((a: string, b: string) => + (BigInt(a) + BigInt(b)).toString() + ) + ) + ), + }); + newTopStat.top_amount_received.push({ + address: stat.address, + amount: stat.amount_received, + percentage: Number( + (BigInt(stat.amount_received) * BigInt(100)) / + BigInt( + dayStat + .map((statistic) => statistic.amount_received) + .reduce((a: string, b: string) => + (BigInt(a) + BigInt(b)).toString() + ) + ) + ), + }); + newTopStat.top_tx_sent.push({ + address: stat.address, + amount: stat.tx_sent, + percentage: + (stat.tx_sent * 100) / + dayStat + .map((statistic) => statistic.tx_sent) + .reduce((a: number, b: number) => a + b), + }); + newTopStat.top_gas_used.push({ + address: stat.address, + amount: stat.gas_used, + percentage: Number( + (BigInt(stat.gas_used) * BigInt(100)) / + BigInt( + dayStat + .map((statistic) => statistic.gas_used) + .reduce((a: string, b: string) => + (BigInt(a) + BigInt(b)).toString() + ) + ) + ), + }); + }); + newTopStat.top_amount_sent = newTopStat.top_amount_sent + .sort((a, b) => a.percentage - b.percentage) + .filter( + (_, index) => index < config.accountStatistics.numberOfTopRecords + ); + newTopStat.top_amount_received = newTopStat.top_amount_received + .sort((a, b) => a.percentage - b.percentage) + .filter( + (_, index) => index < config.accountStatistics.numberOfTopRecords + ); + newTopStat.top_tx_sent = newTopStat.top_tx_sent + .sort((a, b) => a.percentage - b.percentage) + .filter( + (_, index) => index < config.accountStatistics.numberOfTopRecords + ); + newTopStat.top_gas_used = newTopStat.top_gas_used + .sort((a, b) => a.percentage - b.percentage) + .filter( + (_, index) => index < config.accountStatistics.numberOfTopRecords + ); + + return newTopStat; + } + + public async _start() { + this.createJob( + BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, + BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, + { + id: 0, + accountStats: [], + }, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + repeat: { + pattern: config.accountStatistics.jobPattern, + }, + } + ); + + return super._start(); + } +} diff --git a/src/services/statistics/daily_statistics.service.ts b/src/services/statistics/daily_statistics.service.ts new file mode 100644 index 000000000..16dcc6060 --- /dev/null +++ b/src/services/statistics/daily_statistics.service.ts @@ -0,0 +1,142 @@ +import { Service } from '@ourparentcenter/moleculer-decorators-extended'; +import { ServiceBroker } from 'moleculer'; +import { fromBech32 } from '@cosmjs/encoding'; +import { Account, DailyStatistics, Transaction } from '../../models'; +import { BULL_JOB_NAME, IDailyStatsParam, SERVICE } from '../../common'; +import config from '../../../config.json' assert { type: 'json' }; +import BullableService, { QueueHandler } from '../../base/bullable.service'; + +@Service({ + name: SERVICE.V1.DailyStatisticsService.key, + version: 1, +}) +export default class DailyStatisticsService extends BullableService { + public constructor(public broker: ServiceBroker) { + super(broker); + } + + @QueueHandler({ + queueName: BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, + jobName: BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, + // prefix: `horoscope-v2-${config.chainId}`, + }) + public async handleJob(_payload: IDailyStatsParam): Promise { + try { + const syncDate = new Date(); + const endTime = syncDate.setUTCHours(0, 0, 0, 0); + syncDate.setDate(syncDate.getDate() - 1); + const startTime = syncDate.setUTCHours(0, 0, 0, 0); + this.logger.info( + `Get daily statistic events at page ${ + _payload.offset + } for day ${new Date(startTime)}` + ); + + const dailyEvents = await Transaction.query() + .joinRelated('events[attributes]') + .select('id', 'code', 'events:attributes.value') + .where('timestamp', '>=', startTime) + .andWhere('timestamp', '<', endTime) + .andWhere( + 'events:attributes.value', + 'like', + `${config.networkPrefixAddress}%` + ) + .orderBy('id') + .limit(config.dailyStatistics.recordsPerCall) + .offset(_payload.offset * config.dailyStatistics.recordsPerCall); + + if (dailyEvents.length > 0) { + const activeAddrs = Array.from( + new Set( + _payload.addresses.concat( + dailyEvents + .filter((event) => fromBech32(event.value).data.length === 20) + .map((event) => event.value) + ) + ) + ); + const dailyTxs = Array.from( + new Set( + _payload.txIds.concat( + Array.from( + new Set( + dailyEvents + .filter((event) => event.code === 0) + .map((event) => event.id) + ) + ) + ) + ) + ); + const offset = _payload.offset + 1; + + await this.createJob( + BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, + BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, + { + offset, + txIds: dailyTxs, + addresses: activeAddrs, + }, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + } + ); + } else { + syncDate.setDate(syncDate.getDate() - 1); + const previousDay = syncDate.setUTCHours(0, 0, 0, 0); + + const [uniqueAddrs, prevDailyStat] = await Promise.all([ + Account.query().count('id'), + DailyStatistics.query().findOne('date', new Date(previousDay)), + ]); + + const dailyStat = DailyStatistics.fromJson({ + daily_txs: _payload.txIds.length, + daily_active_addresses: _payload.addresses.length, + unique_addresses: Number(uniqueAddrs[0].count), + unique_addresses_increase: prevDailyStat + ? uniqueAddrs[0].count - prevDailyStat.unique_addresses + : 0, + date: new Date(startTime).toISOString(), + }); + + await DailyStatistics.query() + .insert(dailyStat) + .catch((error) => { + this.logger.error('Error insert new daily statistic record'); + this.logger.error(error); + }); + } + } catch (error) { + this.logger.error(error); + } + } + + public async _start() { + this.createJob( + BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, + BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, + { + offset: 0, + txIds: [], + addresses: [], + }, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + repeat: { + pattern: config.dailyStatistics.jobPattern, + }, + } + ); + + return super._start(); + } +} diff --git a/src/services/statistics/dashboard_statistics.service.ts b/src/services/statistics/dashboard_statistics.service.ts index 7727909be..c6f93e9d4 100644 --- a/src/services/statistics/dashboard_statistics.service.ts +++ b/src/services/statistics/dashboard_statistics.service.ts @@ -101,7 +101,7 @@ export default class DashboardStatisticsService extends BullableService { public async _start() { this.createJob( BULL_JOB_NAME.HANDLE_DASHBOARD_STATISTICS, - 'crawl', + BULL_JOB_NAME.HANDLE_DASHBOARD_STATISTICS, {}, { removeOnComplete: true, From 6c004b07cd33c52e7314b690b2021a7156a953a5 Mon Sep 17 00:00:00 2001 From: AnDQK Date: Wed, 5 Jul 2023 13:40:51 +0700 Subject: [PATCH 02/20] feat: change job params so it can aggregate data at a specific date --- src/common/utils/request.ts | 2 ++ src/services/statistics/account_statistics.service.ts | 5 +++-- src/services/statistics/daily_statistics.service.ts | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/common/utils/request.ts b/src/common/utils/request.ts index 734a2465c..7ec8e0610 100644 --- a/src/common/utils/request.ts +++ b/src/common/utils/request.ts @@ -10,12 +10,14 @@ export interface ITxIdsParam { } export interface IDailyStatsParam { + date: string; offset: number; txIds: number[]; addresses: string[]; } export interface IAccountStatsParam { + date: string; offset: number; accountStats: IAccountStats[]; } diff --git a/src/services/statistics/account_statistics.service.ts b/src/services/statistics/account_statistics.service.ts index 57461f07c..8b8e986a7 100644 --- a/src/services/statistics/account_statistics.service.ts +++ b/src/services/statistics/account_statistics.service.ts @@ -36,7 +36,7 @@ export default class AccountStatisticsService extends BullableService { try { const { accountStats } = _payload; - const syncDate = new Date(); + const syncDate = _payload.date ? new Date(_payload.date) : new Date(); const endTime = syncDate.setUTCHours(0, 0, 0, 0); syncDate.setDate(syncDate.getDate() - 1); const startTime = syncDate.setUTCHours(0, 0, 0, 0); @@ -356,7 +356,8 @@ export default class AccountStatisticsService extends BullableService { BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, { - id: 0, + date: null, + offset: 0, accountStats: [], }, { diff --git a/src/services/statistics/daily_statistics.service.ts b/src/services/statistics/daily_statistics.service.ts index 16dcc6060..327529a67 100644 --- a/src/services/statistics/daily_statistics.service.ts +++ b/src/services/statistics/daily_statistics.service.ts @@ -22,7 +22,7 @@ export default class DailyStatisticsService extends BullableService { }) public async handleJob(_payload: IDailyStatsParam): Promise { try { - const syncDate = new Date(); + const syncDate = _payload.date ? new Date(_payload.date) : new Date(); const endTime = syncDate.setUTCHours(0, 0, 0, 0); syncDate.setDate(syncDate.getDate() - 1); const startTime = syncDate.setUTCHours(0, 0, 0, 0); @@ -123,6 +123,7 @@ export default class DailyStatisticsService extends BullableService { BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, { offset: 0, + date: null, txIds: [], addresses: [], }, From 98fcc9b547f17faa604afd205269460df5086989 Mon Sep 17 00:00:00 2001 From: AnDQK Date: Thu, 6 Jul 2023 10:38:05 +0700 Subject: [PATCH 03/20] fix: query fields from exact table --- config.json | 13 +++------- ..._create_table_daily_stats_account_stats.ts | 1 + .../statistics/daily_statistics.service.ts | 26 ++++++++++++------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/config.json b/config.json index 50188e5f3..2faa23f11 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,5 @@ { - "chainId": "aura-testnet", + "chainId": "euphoria-2", "networkPrefixAddress": "aura", "consensusPrefixAddress": "valcons", "validatorPrefixAddress": "valoper", @@ -132,13 +132,8 @@ "event_attribute", "view_event_attribute_value_index" ], - "queryNeedWhereRelation": [ - "event_attributes", - "event_attribute_index" - ], - "queryNeedWhereCondition": [ - "block_height" - ], + "queryNeedWhereRelation": ["event_attributes", "event_attribute_index"], + "queryNeedWhereCondition": ["block_height"], "queryHeightRangeLimit": 10000 }, "crawlValidatorImage": { @@ -170,4 +165,4 @@ "recordsPerCall": 100, "jobPattern": "0 0 0 * * ?" } -} \ No newline at end of file +} diff --git a/migrations/20230704102018_create_table_daily_stats_account_stats.ts b/migrations/20230704102018_create_table_daily_stats_account_stats.ts index 20c24a761..6ace46373 100644 --- a/migrations/20230704102018_create_table_daily_stats_account_stats.ts +++ b/migrations/20230704102018_create_table_daily_stats_account_stats.ts @@ -6,6 +6,7 @@ export async function up(knex: Knex): Promise { table.bigint('daily_txs').index().notNullable(); table.bigint('daily_active_addresses').index().notNullable(); table.bigint('unique_addresses').index().notNullable(); + table.bigint('unique_addresses_increase').index().notNullable(); table.timestamp('date').unique().notNullable(); }); await knex.schema.createTable('account_statistics', (table: any) => { diff --git a/src/services/statistics/daily_statistics.service.ts b/src/services/statistics/daily_statistics.service.ts index 327529a67..fdfa6c3f5 100644 --- a/src/services/statistics/daily_statistics.service.ts +++ b/src/services/statistics/daily_statistics.service.ts @@ -1,10 +1,10 @@ import { Service } from '@ourparentcenter/moleculer-decorators-extended'; import { ServiceBroker } from 'moleculer'; -import { fromBech32 } from '@cosmjs/encoding'; import { Account, DailyStatistics, Transaction } from '../../models'; import { BULL_JOB_NAME, IDailyStatsParam, SERVICE } from '../../common'; import config from '../../../config.json' assert { type: 'json' }; import BullableService, { QueueHandler } from '../../base/bullable.service'; +import Utils from '../../common/utils/utils'; @Service({ name: SERVICE.V1.DailyStatisticsService.key, @@ -33,16 +33,16 @@ export default class DailyStatisticsService extends BullableService { ); const dailyEvents = await Transaction.query() - .joinRelated('events[attributes]') - .select('id', 'code', 'events:attributes.value') - .where('timestamp', '>=', startTime) - .andWhere('timestamp', '<', endTime) + .joinRelated('events.[attributes]') + .select('transaction.id', 'transaction.code', 'events:attributes.value') + .where('transaction.timestamp', '>=', new Date(startTime)) + .andWhere('transaction.timestamp', '<', new Date(endTime)) .andWhere( 'events:attributes.value', 'like', `${config.networkPrefixAddress}%` ) - .orderBy('id') + .orderBy('transaction.id') .limit(config.dailyStatistics.recordsPerCall) .offset(_payload.offset * config.dailyStatistics.recordsPerCall); @@ -51,7 +51,13 @@ export default class DailyStatisticsService extends BullableService { new Set( _payload.addresses.concat( dailyEvents - .filter((event) => fromBech32(event.value).data.length === 20) + .filter((event) => + Utils.isValidAccountAddress( + event.value, + config.networkPrefixAddress, + 20 + ) + ) .map((event) => event.value) ) ) @@ -132,9 +138,9 @@ export default class DailyStatisticsService extends BullableService { removeOnFail: { count: 3, }, - repeat: { - pattern: config.dailyStatistics.jobPattern, - }, + // repeat: { + // pattern: config.dailyStatistics.jobPattern, + // }, } ); From cc6b16d2d743c0631b0af18dccebd38cff3fcff3 Mon Sep 17 00:00:00 2001 From: AnDQK Date: Thu, 6 Jul 2023 10:38:56 +0700 Subject: [PATCH 04/20] fix: query exact fields --- config.json | 2 +- .../statistics/account_statistics.service.ts | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/config.json b/config.json index 2faa23f11..136b22288 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,5 @@ { - "chainId": "euphoria-2", + "chainId": "aura-testnet", "networkPrefixAddress": "aura", "consensusPrefixAddress": "valcons", "validatorPrefixAddress": "valoper", diff --git a/src/services/statistics/account_statistics.service.ts b/src/services/statistics/account_statistics.service.ts index 8b8e986a7..8aa4fb759 100644 --- a/src/services/statistics/account_statistics.service.ts +++ b/src/services/statistics/account_statistics.service.ts @@ -46,16 +46,16 @@ export default class AccountStatisticsService extends BullableService { } for day ${new Date(startTime)}` ); const dailyEvents = await Transaction.query() - .joinRelated('events[attributes]') + .joinRelated('events.[attributes]') .select( - 'id', - 'gas_used', + 'transaction.id', + 'transaction.gas_used', 'events:attributes.event_id', 'events:attributes.composite_key', 'events:attributes.value' ) - .where('timestamp', '>=', startTime) - .andWhere('timestamp', '<', endTime) + .where('transaction.timestamp', '>=', new Date(startTime)) + .andWhere('transaction.timestamp', '<', new Date(endTime)) .andWhere((builder) => builder.whereIn('events.type', [ Event.EVENT_TYPE.COIN_SPENT, @@ -365,9 +365,9 @@ export default class AccountStatisticsService extends BullableService { removeOnFail: { count: 3, }, - repeat: { - pattern: config.accountStatistics.jobPattern, - }, + // repeat: { + // pattern: config.accountStatistics.jobPattern, + // }, } ); From 68b9102e4e7af4a0d934c3c11529b94ddd28a3ac Mon Sep 17 00:00:00 2001 From: AnDQK Date: Thu, 6 Jul 2023 16:15:04 +0700 Subject: [PATCH 05/20] feat: add logger when done --- src/services/statistics/account_statistics.service.ts | 3 +++ src/services/statistics/daily_statistics.service.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/services/statistics/account_statistics.service.ts b/src/services/statistics/account_statistics.service.ts index 8aa4fb759..9ec5a43b8 100644 --- a/src/services/statistics/account_statistics.service.ts +++ b/src/services/statistics/account_statistics.service.ts @@ -181,6 +181,9 @@ export default class AccountStatisticsService extends BullableService { }) ); + this.logger.info( + `Insert new daily statistic for date ${new Date(startTime)}` + ); await AccountStatistics.query() .insert(dailyAccountStats) .catch((error) => { diff --git a/src/services/statistics/daily_statistics.service.ts b/src/services/statistics/daily_statistics.service.ts index fdfa6c3f5..c2a49ed80 100644 --- a/src/services/statistics/daily_statistics.service.ts +++ b/src/services/statistics/daily_statistics.service.ts @@ -111,6 +111,9 @@ export default class DailyStatisticsService extends BullableService { date: new Date(startTime).toISOString(), }); + this.logger.info( + `Insert new daily statistic for date ${new Date(startTime)}` + ); await DailyStatistics.query() .insert(dailyStat) .catch((error) => { From 702f7c51294bd886f23d6ecaebad3df456efc4d3 Mon Sep 17 00:00:00 2001 From: AnDQK Date: Thu, 6 Jul 2023 19:31:34 +0700 Subject: [PATCH 06/20] feat: finish statistic jobs (not tested) --- src/common/utils/request.ts | 2 +- .../statistics/account_statistics.service.ts | 221 +++++++++++------- .../statistics/daily_statistics.service.ts | 9 +- 3 files changed, 137 insertions(+), 95 deletions(-) diff --git a/src/common/utils/request.ts b/src/common/utils/request.ts index 7ec8e0610..2b8103f6e 100644 --- a/src/common/utils/request.ts +++ b/src/common/utils/request.ts @@ -17,8 +17,8 @@ export interface IDailyStatsParam { } export interface IAccountStatsParam { + id: number; date: string; - offset: number; accountStats: IAccountStats[]; } diff --git a/src/services/statistics/account_statistics.service.ts b/src/services/statistics/account_statistics.service.ts index 9ec5a43b8..26692f63d 100644 --- a/src/services/statistics/account_statistics.service.ts +++ b/src/services/statistics/account_statistics.service.ts @@ -3,6 +3,7 @@ import { Service } from '@ourparentcenter/moleculer-decorators-extended'; import { ServiceBroker } from 'moleculer'; import { parseCoins } from '@cosmjs/proto-signing'; +import BigNumber from 'bignumber.js'; import { AccountStatistics, Event, @@ -17,6 +18,7 @@ import { } from '../../common'; import config from '../../../config.json' assert { type: 'json' }; import BullableService, { QueueHandler } from '../../base/bullable.service'; +import Utils from '../../common/utils/utils'; @Service({ name: SERVICE.V1.AccountStatisticsService.key, @@ -40,15 +42,26 @@ export default class AccountStatisticsService extends BullableService { const endTime = syncDate.setUTCHours(0, 0, 0, 0); syncDate.setDate(syncDate.getDate() - 1); const startTime = syncDate.setUTCHours(0, 0, 0, 0); + + const startTxId = !_payload.id + ? ( + await Transaction.query() + .select('id') + .where('transaction.timestamp', '>=', new Date(startTime)) + .limit(1) + .orderBy('id') + )[0].id + : _payload.id; + const nextId = startTxId + 50; this.logger.info( - `Get account statistic events at page ${ - _payload.offset - } for day ${new Date(startTime)}` + `Get account statistic events from id ${startTxId} for day ${new Date( + startTime + )}` ); + const dailyEvents = await Transaction.query() .joinRelated('events.[attributes]') .select( - 'transaction.id', 'transaction.gas_used', 'events:attributes.event_id', 'events:attributes.composite_key', @@ -56,23 +69,36 @@ export default class AccountStatisticsService extends BullableService { ) .where('transaction.timestamp', '>=', new Date(startTime)) .andWhere('transaction.timestamp', '<', new Date(endTime)) + .andWhere('transaction.id', '>=', startTxId) + .andWhere('transaction.id', '<', nextId) .andWhere((builder) => - builder.whereIn('events.type', [ - Event.EVENT_TYPE.COIN_SPENT, - Event.EVENT_TYPE.COIN_RECEIVED, - Event.EVENT_TYPE.USE_FEEGRANT, - ]) - ) - .orWhereIn('events:attributes.key', [ - EventAttribute.ATTRIBUTE_KEY.FEE, - EventAttribute.ATTRIBUTE_KEY.FEE_PAYER, - ]) - .limit(config.accountStatistics.recordsPerCall) - .offset(_payload.offset * config.accountStatistics.recordsPerCall); + builder + .whereIn('events.type', [ + Event.EVENT_TYPE.COIN_SPENT, + Event.EVENT_TYPE.COIN_RECEIVED, + Event.EVENT_TYPE.USE_FEEGRANT, + ]) + .orWhereIn('events:attributes.key', [ + EventAttribute.ATTRIBUTE_KEY.FEE, + EventAttribute.ATTRIBUTE_KEY.FEE_PAYER, + ]) + ); if (dailyEvents.length > 0) { dailyEvents - .filter((event) => event.value.startsWith('aura')) + .filter( + (event) => + Utils.isValidAccountAddress( + event.value, + config.networkPrefixAddress, + 20 + ) || + Utils.isValidAccountAddress( + event.value, + config.networkPrefixAddress, + 32 + ) + ) .forEach((event) => { if (!accountStats.find((acc) => acc.address === event.value)) { accountStats.push({ @@ -86,7 +112,14 @@ export default class AccountStatisticsService extends BullableService { }); dailyEvents - .filter((event) => !event.value.startsWith('aura')) + .filter( + (event) => + !Utils.isValidAccountAddress( + event.value, + config.networkPrefixAddress, + 20 + ) + ) .forEach((event) => { switch (event.composite_key) { case EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_AMOUNT: @@ -157,13 +190,13 @@ export default class AccountStatisticsService extends BullableService { BigInt(accountStats[index].gas_used) + BigInt(event.gas_used) ).toString(); }); - const offset = _payload.offset + 1; await this.createJob( BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, { - offset, + id: nextId, + date: null, accountStats, }, { @@ -184,14 +217,16 @@ export default class AccountStatisticsService extends BullableService { this.logger.info( `Insert new daily statistic for date ${new Date(startTime)}` ); - await AccountStatistics.query() - .insert(dailyAccountStats) - .catch((error) => { - this.logger.error( - 'Error insert new daily account statistic records' - ); - this.logger.error(error); - }); + if (dailyAccountStats.length > 0) { + await AccountStatistics.query() + .insert(dailyAccountStats) + .catch((error) => { + this.logger.error( + 'Error insert new daily account statistic records' + ); + this.logger.error(error); + }); + } await this.createJob( BULL_JOB_NAME.HANDLE_TOP_ACCOUNTS, @@ -229,30 +264,30 @@ export default class AccountStatisticsService extends BullableService { const [threeDayStat, fifteenDayStat, thirtyDayStat] = await Promise.all([ AccountStatistics.query() .select('address') - .sum('amount_sent') - .sum('amount_received') - .sum('tx_sent') - .sum('gas_used') - .where('date', '>=', threeDays) - .andWhere('date', '<', now) + .sum('amount_sent as amount_sent') + .sum('amount_received as amount_received') + .sum('tx_sent as tx_sent') + .sum('gas_used as gas_used') + .where('date', '>=', new Date(threeDays)) + .andWhere('date', '<', new Date(now)) .groupBy('address'), AccountStatistics.query() .select('address') - .sum('amount_sent') - .sum('amount_received') - .sum('tx_sent') - .sum('gas_used') - .where('date', '>=', fifteenDays) - .andWhere('date', '<', now) + .sum('amount_sent as amount_sent') + .sum('amount_received as amount_received') + .sum('tx_sent as tx_sent') + .sum('gas_used as gas_used') + .where('date', '>=', new Date(fifteenDays)) + .andWhere('date', '<', new Date(now)) .groupBy('address'), AccountStatistics.query() .select('address') - .sum('amount_sent') - .sum('amount_received') - .sum('tx_sent') - .sum('gas_used') - .where('date', '>=', thirtyDays) - .andWhere('date', '<', now) + .sum('amount_sent as amount_sent') + .sum('amount_received as amount_received') + .sum('tx_sent as tx_sent') + .sum('gas_used as gas_used') + .where('date', '>=', new Date(thirtyDays)) + .andWhere('date', '<', new Date(now)) .groupBy('address'), ]); @@ -263,7 +298,7 @@ export default class AccountStatisticsService extends BullableService { thirty_days: this.calculateTop(thirtyDayStat), }; - this.logger.info(`Update top accounts for day ${now}`); + this.logger.info(`Update top accounts for day ${new Date(now)}`); await this.broker.cacher?.set(REDIS_KEY.TOP_ACCOUNTS, topAccounts); } catch (error) { this.logger.error(error); @@ -277,76 +312,71 @@ export default class AccountStatisticsService extends BullableService { top_tx_sent: [] as any[], top_gas_used: [] as any[], }; + + const dayStatAmountSent = dayStat + .map((statistic) => statistic.amount_sent) + .reduce((a: string, b: string) => (BigInt(a) + BigInt(b)).toString()); + const dayStatAmountReceived = dayStat + .map((statistic) => statistic.amount_received) + .reduce((a: string, b: string) => (BigInt(a) + BigInt(b)).toString()); + const dayStatTxSent = dayStat + .map((statistic) => statistic.tx_sent) + .reduce((a: number, b: number) => Number(BigInt(a) + BigInt(b))); + const dayStatGasUsed = dayStat + .map((statistic) => statistic.gas_used) + .reduce((a: string, b: string) => (BigInt(a) + BigInt(b)).toString()); + dayStat.forEach((stat) => { newTopStat.top_amount_sent.push({ address: stat.address, amount: stat.amount_sent, percentage: Number( - (BigInt(stat.amount_sent) * BigInt(100)) / - BigInt( - dayStat - .map((statistic) => statistic.amount_sent) - .reduce((a: string, b: string) => - (BigInt(a) + BigInt(b)).toString() - ) - ) + BigNumber(stat.amount_sent) + .multipliedBy(100) + .dividedBy(BigNumber(dayStatAmountSent)) ), }); newTopStat.top_amount_received.push({ address: stat.address, amount: stat.amount_received, percentage: Number( - (BigInt(stat.amount_received) * BigInt(100)) / - BigInt( - dayStat - .map((statistic) => statistic.amount_received) - .reduce((a: string, b: string) => - (BigInt(a) + BigInt(b)).toString() - ) - ) + BigNumber(stat.amount_received) + .multipliedBy(100) + .dividedBy(BigNumber(dayStatAmountReceived)) ), }); newTopStat.top_tx_sent.push({ address: stat.address, amount: stat.tx_sent, - percentage: - (stat.tx_sent * 100) / - dayStat - .map((statistic) => statistic.tx_sent) - .reduce((a: number, b: number) => a + b), + percentage: (stat.tx_sent * 100) / dayStatTxSent, }); newTopStat.top_gas_used.push({ address: stat.address, amount: stat.gas_used, percentage: Number( - (BigInt(stat.gas_used) * BigInt(100)) / - BigInt( - dayStat - .map((statistic) => statistic.gas_used) - .reduce((a: string, b: string) => - (BigInt(a) + BigInt(b)).toString() - ) - ) + BigNumber(stat.gas_used) + .multipliedBy(100) + .dividedBy(BigNumber(dayStatGasUsed)) ), }); }); newTopStat.top_amount_sent = newTopStat.top_amount_sent - .sort((a, b) => a.percentage - b.percentage) + .sort((a, b) => b.percentage - a.percentage) .filter( (_, index) => index < config.accountStatistics.numberOfTopRecords ); newTopStat.top_amount_received = newTopStat.top_amount_received - .sort((a, b) => a.percentage - b.percentage) + .sort((a, b) => b.percentage - a.percentage) .filter( (_, index) => index < config.accountStatistics.numberOfTopRecords ); newTopStat.top_tx_sent = newTopStat.top_tx_sent - .sort((a, b) => a.percentage - b.percentage) + .sort((a, b) => b.percentage - a.percentage) .filter( (_, index) => index < config.accountStatistics.numberOfTopRecords ); newTopStat.top_gas_used = newTopStat.top_gas_used - .sort((a, b) => a.percentage - b.percentage) + .sort((a, b) => b.percentage - a.percentage) .filter( (_, index) => index < config.accountStatistics.numberOfTopRecords ); @@ -355,22 +385,33 @@ export default class AccountStatisticsService extends BullableService { } public async _start() { + // this.createJob( + // BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, + // BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, + // { + // id: null, + // date: null, + // accountStats: [], + // }, + // { + // removeOnComplete: true, + // removeOnFail: { + // count: 3, + // }, + // repeat: { + // pattern: config.accountStatistics.jobPattern, + // }, + // } + // ); this.createJob( - BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, - BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, - { - date: null, - offset: 0, - accountStats: [], - }, + BULL_JOB_NAME.HANDLE_TOP_ACCOUNTS, + BULL_JOB_NAME.HANDLE_TOP_ACCOUNTS, + {}, { removeOnComplete: true, removeOnFail: { count: 3, }, - // repeat: { - // pattern: config.accountStatistics.jobPattern, - // }, } ); diff --git a/src/services/statistics/daily_statistics.service.ts b/src/services/statistics/daily_statistics.service.ts index c2a49ed80..89e5d7a43 100644 --- a/src/services/statistics/daily_statistics.service.ts +++ b/src/services/statistics/daily_statistics.service.ts @@ -81,6 +81,7 @@ export default class DailyStatisticsService extends BullableService { BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, { + date: null, offset, txIds: dailyTxs, addresses: activeAddrs, @@ -131,8 +132,8 @@ export default class DailyStatisticsService extends BullableService { BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, { - offset: 0, date: null, + offset: 0, txIds: [], addresses: [], }, @@ -141,9 +142,9 @@ export default class DailyStatisticsService extends BullableService { removeOnFail: { count: 3, }, - // repeat: { - // pattern: config.dailyStatistics.jobPattern, - // }, + repeat: { + pattern: config.dailyStatistics.jobPattern, + }, } ); From 386bd92825782e96caa096acd331ed38da02766e Mon Sep 17 00:00:00 2001 From: AnDQK Date: Fri, 7 Jul 2023 11:08:42 +0700 Subject: [PATCH 07/20] feat: create a separate interval job that gets the current date and create statistics jobs with date --- ci/config.json.ci | 7 +- config.json | 7 +- src/common/constant.ts | 13 ++++ src/common/utils/request.ts | 4 ++ .../api-gateways/api_gateway.service.ts | 6 +- .../api-gateways/statistics.service.ts | 50 ++++++++++++++ .../statistics/account_statistics.service.ts | 68 +++++++++---------- .../statistics/daily_statistics.service.ts | 65 +++++++++++------- .../statistics/daily_stats_jobs.service.ts | 59 ++++++++++++++++ .../dashboard_statistics.service.ts | 28 ++++---- 10 files changed, 227 insertions(+), 80 deletions(-) create mode 100644 src/services/api-gateways/statistics.service.ts create mode 100644 src/services/statistics/daily_stats_jobs.service.ts diff --git a/ci/config.json.ci b/ci/config.json.ci index 4d5f1f689..85b651fba 100644 --- a/ci/config.json.ci +++ b/ci/config.json.ci @@ -157,12 +157,13 @@ "batchSizeLimit": 10 }, "dailyStatistics": { - "recordsPerCall": 100, - "jobPattern": "0 0 0 * * ?" + "recordsPerCall": 100 }, "accountStatistics": { "numberOfTopRecords": 10, - "recordsPerCall": 100, + "recordsPerCall": 100 + }, + "dailyStatsJobs": { "jobPattern": "0 0 0 * * ?" } } diff --git a/config.json b/config.json index 136b22288..f6486dbcc 100644 --- a/config.json +++ b/config.json @@ -157,12 +157,13 @@ "batchSizeLimit": 10 }, "dailyStatistics": { - "recordsPerCall": 100, - "jobPattern": "0 0 0 * * ?" + "recordsPerCall": 100 }, "accountStatistics": { "numberOfTopRecords": 10, - "recordsPerCall": 100, + "recordsPerCall": 100 + }, + "dailyStatsJobs": { "jobPattern": "0 0 0 * * ?" } } diff --git a/src/common/constant.ts b/src/common/constant.ts index fa625eb41..9bfdec5d2 100644 --- a/src/common/constant.ts +++ b/src/common/constant.ts @@ -66,6 +66,7 @@ export const BULL_JOB_NAME = { CRAWL_DAILY_STATISTICS: 'crawl:daily-statistics', CRAWL_ACCOUNT_STATISTICS: 'crawl:account-statistics', HANDLE_TOP_ACCOUNTS: 'handle:top-accounts', + HANDLE_DAILY_STATS_JOBS: 'handle:daily-stats-jobs', }; export const SERVICE = { @@ -210,10 +211,22 @@ export const SERVICE = { DailyStatisticsService: { key: 'DailyStatisticsService', name: 'v1.DailyStatisticsService', + CreateSpecificDateJob: { + key: 'CreateSpecificDateJob', + path: 'v1.DailyStatisticsService.CreateSpecificDateJob', + }, }, AccountStatisticsService: { key: 'AccountStatisticsService', name: 'v1.AccountStatisticsService', + CreateSpecificDateJob: { + key: 'CreateSpecificDateJob', + path: 'v1.AccountStatisticsService.CreateSpecificDateJob', + }, + }, + DailyStatsJobsService: { + key: 'DailyStatsJobsService', + name: 'v1.DailyStatsJobsService', }, }, }; diff --git a/src/common/utils/request.ts b/src/common/utils/request.ts index 2b8103f6e..9f38da4ec 100644 --- a/src/common/utils/request.ts +++ b/src/common/utils/request.ts @@ -29,3 +29,7 @@ export interface IAccountStats { tx_sent: number; gas_used: string; } + +export interface ICreateSpecificDateJob { + date: string; +} diff --git a/src/services/api-gateways/api_gateway.service.ts b/src/services/api-gateways/api_gateway.service.ts index 611d16feb..0125353f6 100644 --- a/src/services/api-gateways/api_gateway.service.ts +++ b/src/services/api-gateways/api_gateway.service.ts @@ -21,7 +21,11 @@ import { bullBoardMixin } from '../../mixins/bullBoard/bullBoard.mixin'; path: '/api', autoAliases: true, // allow generate rest info (GET/PUT/POST...) in the services mappingPolicy: 'restrict', // allow action called with exact method - whitelist: ['v1.dashboard-statistics.*', 'v1.graphiql.*'], + whitelist: [ + 'v1.dashboard-statistics.*', + 'v1.graphiql.*', + 'v1.statistics.*', + ], }, ], // empty cors object will have moleculer to generate handler for preflight request and CORS header which allow all origin diff --git a/src/services/api-gateways/statistics.service.ts b/src/services/api-gateways/statistics.service.ts new file mode 100644 index 000000000..aebe9fd1c --- /dev/null +++ b/src/services/api-gateways/statistics.service.ts @@ -0,0 +1,50 @@ +import { + Action, + Get, + Service, +} from '@ourparentcenter/moleculer-decorators-extended'; +import { Context, ServiceBroker } from 'moleculer'; +import { REDIS_KEY } from '../../common'; +import BaseService from '../../base/base.service'; +import networks from '../../../network.json' assert { type: 'json' }; + +@Service({ + name: 'statistics', + version: 1, +}) +export default class StatisticsService extends BaseService { + public constructor(public broker: ServiceBroker) { + super(broker); + } + + @Get('/top-accounts', { + name: 'getTopAccountsByChainId', + params: { + chainid: { + type: 'string', + optional: false, + enum: networks.map((network) => network.chainId), + }, + }, + }) + async getTopAccountsByChainId( + ctx: Context<{ chainid: string }, Record> + ) { + const selectedChain = networks.find( + (network) => network.chainId === ctx.params.chainid + ); + + return this.broker.call( + `v1.statistics.getTopAccounts@${selectedChain?.moleculerNamespace}` + ); + } + + @Action({ + name: 'getTopAccounts', + params: {}, + }) + async getTopAccounts() { + const result = await this.broker.cacher?.get(REDIS_KEY.TOP_ACCOUNTS); + return result; + } +} diff --git a/src/services/statistics/account_statistics.service.ts b/src/services/statistics/account_statistics.service.ts index 26692f63d..3c822b3e3 100644 --- a/src/services/statistics/account_statistics.service.ts +++ b/src/services/statistics/account_statistics.service.ts @@ -1,7 +1,10 @@ /* eslint-disable no-case-declarations */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import { Service } from '@ourparentcenter/moleculer-decorators-extended'; -import { ServiceBroker } from 'moleculer'; +import { + Action, + Service, +} from '@ourparentcenter/moleculer-decorators-extended'; +import { Context, ServiceBroker } from 'moleculer'; import { parseCoins } from '@cosmjs/proto-signing'; import BigNumber from 'bignumber.js'; import { @@ -13,6 +16,7 @@ import { import { BULL_JOB_NAME, IAccountStatsParam, + ICreateSpecificDateJob, REDIS_KEY, SERVICE, } from '../../common'; @@ -29,6 +33,32 @@ export default class AccountStatisticsService extends BullableService { super(broker); } + @Action({ + name: SERVICE.V1.AccountStatisticsService.CreateSpecificDateJob.key, + params: { + date: 'string', + }, + }) + public async actionCreateSpecificDateJob( + ctx: Context + ) { + await this.createJob( + BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, + BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, + { + id: null, + date: ctx.params.date, + accountStats: [], + }, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + } + ); + } + @QueueHandler({ queueName: BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, jobName: BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, @@ -38,7 +68,7 @@ export default class AccountStatisticsService extends BullableService { try { const { accountStats } = _payload; - const syncDate = _payload.date ? new Date(_payload.date) : new Date(); + const syncDate = new Date(_payload.date); const endTime = syncDate.setUTCHours(0, 0, 0, 0); syncDate.setDate(syncDate.getDate() - 1); const startTime = syncDate.setUTCHours(0, 0, 0, 0); @@ -196,7 +226,7 @@ export default class AccountStatisticsService extends BullableService { BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, { id: nextId, - date: null, + date: _payload.date, accountStats, }, { @@ -385,36 +415,6 @@ export default class AccountStatisticsService extends BullableService { } public async _start() { - // this.createJob( - // BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, - // BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, - // { - // id: null, - // date: null, - // accountStats: [], - // }, - // { - // removeOnComplete: true, - // removeOnFail: { - // count: 3, - // }, - // repeat: { - // pattern: config.accountStatistics.jobPattern, - // }, - // } - // ); - this.createJob( - BULL_JOB_NAME.HANDLE_TOP_ACCOUNTS, - BULL_JOB_NAME.HANDLE_TOP_ACCOUNTS, - {}, - { - removeOnComplete: true, - removeOnFail: { - count: 3, - }, - } - ); - return super._start(); } } diff --git a/src/services/statistics/daily_statistics.service.ts b/src/services/statistics/daily_statistics.service.ts index 89e5d7a43..ba6ef29a6 100644 --- a/src/services/statistics/daily_statistics.service.ts +++ b/src/services/statistics/daily_statistics.service.ts @@ -1,7 +1,15 @@ -import { Service } from '@ourparentcenter/moleculer-decorators-extended'; -import { ServiceBroker } from 'moleculer'; +import { + Action, + Service, +} from '@ourparentcenter/moleculer-decorators-extended'; +import { Context, ServiceBroker } from 'moleculer'; import { Account, DailyStatistics, Transaction } from '../../models'; -import { BULL_JOB_NAME, IDailyStatsParam, SERVICE } from '../../common'; +import { + BULL_JOB_NAME, + ICreateSpecificDateJob, + IDailyStatsParam, + SERVICE, +} from '../../common'; import config from '../../../config.json' assert { type: 'json' }; import BullableService, { QueueHandler } from '../../base/bullable.service'; import Utils from '../../common/utils/utils'; @@ -15,6 +23,33 @@ export default class DailyStatisticsService extends BullableService { super(broker); } + @Action({ + name: SERVICE.V1.DailyStatisticsService.CreateSpecificDateJob.key, + params: { + date: 'string', + }, + }) + public async actionCreateSpecificDateJob( + ctx: Context + ) { + await this.createJob( + BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, + BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, + { + date: ctx.params.date, + offset: 0, + txIds: [], + addresses: [], + }, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + } + ); + } + @QueueHandler({ queueName: BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, jobName: BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, @@ -22,7 +57,7 @@ export default class DailyStatisticsService extends BullableService { }) public async handleJob(_payload: IDailyStatsParam): Promise { try { - const syncDate = _payload.date ? new Date(_payload.date) : new Date(); + const syncDate = new Date(_payload.date); const endTime = syncDate.setUTCHours(0, 0, 0, 0); syncDate.setDate(syncDate.getDate() - 1); const startTime = syncDate.setUTCHours(0, 0, 0, 0); @@ -81,7 +116,7 @@ export default class DailyStatisticsService extends BullableService { BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, { - date: null, + date: _payload.date, offset, txIds: dailyTxs, addresses: activeAddrs, @@ -128,26 +163,6 @@ export default class DailyStatisticsService extends BullableService { } public async _start() { - this.createJob( - BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, - BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, - { - date: null, - offset: 0, - txIds: [], - addresses: [], - }, - { - removeOnComplete: true, - removeOnFail: { - count: 3, - }, - repeat: { - pattern: config.dailyStatistics.jobPattern, - }, - } - ); - return super._start(); } } diff --git a/src/services/statistics/daily_stats_jobs.service.ts b/src/services/statistics/daily_stats_jobs.service.ts new file mode 100644 index 000000000..668b53fc5 --- /dev/null +++ b/src/services/statistics/daily_stats_jobs.service.ts @@ -0,0 +1,59 @@ +import { Service } from '@ourparentcenter/moleculer-decorators-extended'; +import { ServiceBroker } from 'moleculer'; +import { BULL_JOB_NAME, SERVICE } from '../../common'; +import config from '../../../config.json' assert { type: 'json' }; +import BullableService, { QueueHandler } from '../../base/bullable.service'; + +@Service({ + name: SERVICE.V1.DailyStatsJobsService.key, + version: 1, +}) +export default class DailyStatsJobsService extends BullableService { + public constructor(public broker: ServiceBroker) { + super(broker); + } + + @QueueHandler({ + queueName: BULL_JOB_NAME.HANDLE_DAILY_STATS_JOBS, + jobName: BULL_JOB_NAME.HANDLE_DAILY_STATS_JOBS, + // prefix: `horoscope-v2-${config.chainId}`, + }) + public async handleJob(_payload: object): Promise { + const date = new Date().toString(); + + await Promise.all([ + this.broker.call( + SERVICE.V1.DailyStatisticsService.CreateSpecificDateJob.path, + { date } + ), + this.broker.call( + SERVICE.V1.AccountStatisticsService.CreateSpecificDateJob.path, + { date } + ), + ]); + } + + public async _start() { + await this.broker.waitForServices([ + SERVICE.V1.DailyStatisticsService.name, + SERVICE.V1.AccountStatisticsService.name, + ]); + + this.createJob( + BULL_JOB_NAME.HANDLE_DAILY_STATS_JOBS, + BULL_JOB_NAME.HANDLE_DAILY_STATS_JOBS, + {}, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + repeat: { + pattern: config.dailyStatsJobs.jobPattern, + }, + } + ); + + return super._start(); + } +} diff --git a/src/services/statistics/dashboard_statistics.service.ts b/src/services/statistics/dashboard_statistics.service.ts index c6f93e9d4..f9a631da6 100644 --- a/src/services/statistics/dashboard_statistics.service.ts +++ b/src/services/statistics/dashboard_statistics.service.ts @@ -99,20 +99,20 @@ export default class DashboardStatisticsService extends BullableService { } public async _start() { - this.createJob( - BULL_JOB_NAME.HANDLE_DASHBOARD_STATISTICS, - BULL_JOB_NAME.HANDLE_DASHBOARD_STATISTICS, - {}, - { - removeOnComplete: true, - removeOnFail: { - count: 3, - }, - repeat: { - every: config.dashboardStatistics.millisecondCrawl, - }, - } - ); + // this.createJob( + // BULL_JOB_NAME.HANDLE_DASHBOARD_STATISTICS, + // BULL_JOB_NAME.HANDLE_DASHBOARD_STATISTICS, + // {}, + // { + // removeOnComplete: true, + // removeOnFail: { + // count: 3, + // }, + // repeat: { + // every: config.dashboardStatistics.millisecondCrawl, + // }, + // } + // ); return super._start(); } From 05a2b56a1dfb0b7bc753d2fa24372840513a86e7 Mon Sep 17 00:00:00 2001 From: AnDQK Date: Thu, 20 Jul 2023 16:56:38 +0700 Subject: [PATCH 08/20] feat: update logic using dayjs and lodash lib --- ci/config.json.ci | 2 +- config.json | 2 +- package.json | 1 + src/common/utils/request.ts | 10 +- .../api-gateways/cross_chains.service.ts | 37 + .../dashboard_statistics.service.ts | 20 +- .../api-gateways/statistics.service.ts | 22 +- .../statistics/account_statistics.service.ts | 230 +-- .../statistics/daily_statistics.service.ts | 29 +- yarn.lock | 1551 +++++++++-------- 10 files changed, 976 insertions(+), 928 deletions(-) create mode 100644 src/services/api-gateways/cross_chains.service.ts diff --git a/ci/config.json.ci b/ci/config.json.ci index 85b651fba..2a00d6292 100644 --- a/ci/config.json.ci +++ b/ci/config.json.ci @@ -161,7 +161,7 @@ }, "accountStatistics": { "numberOfTopRecords": 10, - "recordsPerCall": 100 + "dayRange": [3, 15, 30] }, "dailyStatsJobs": { "jobPattern": "0 0 0 * * ?" diff --git a/config.json b/config.json index f6486dbcc..4081e616f 100644 --- a/config.json +++ b/config.json @@ -161,7 +161,7 @@ }, "accountStatistics": { "numberOfTopRecords": 10, - "recordsPerCall": 100 + "dayRange": [3, 15, 30] }, "dailyStatsJobs": { "jobPattern": "0 0 0 * * ?" diff --git a/package.json b/package.json index 968b55113..a97b72840 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "bull": "^4.10.2", "bullmq": "^3.13.3", "cosmjs-types": "^0.6.1", + "dayjs": "^1.11.9", "dotenv": "^16.0.3", "file-type": "^18.4.0", "graphql": "^16.6.0", diff --git a/src/common/utils/request.ts b/src/common/utils/request.ts index 9f38da4ec..cf94862a7 100644 --- a/src/common/utils/request.ts +++ b/src/common/utils/request.ts @@ -19,15 +19,7 @@ export interface IDailyStatsParam { export interface IAccountStatsParam { id: number; date: string; - accountStats: IAccountStats[]; -} - -export interface IAccountStats { - address: string; - amount_sent: string; - amount_received: string; - tx_sent: number; - gas_used: string; + accountStats: any; } export interface ICreateSpecificDateJob { diff --git a/src/services/api-gateways/cross_chains.service.ts b/src/services/api-gateways/cross_chains.service.ts new file mode 100644 index 000000000..9c9d673af --- /dev/null +++ b/src/services/api-gateways/cross_chains.service.ts @@ -0,0 +1,37 @@ +import { + Action, + Service, +} from '@ourparentcenter/moleculer-decorators-extended'; +import { ServiceBroker } from 'moleculer'; +import { REDIS_KEY } from '../../common'; +import BaseService from '../../base/base.service'; + +@Service({ + name: 'cross-chains', + version: 1, +}) +export default class CrossChainsService extends BaseService { + public constructor(public broker: ServiceBroker) { + super(broker); + } + + @Action({ + name: 'getDashboardStatistics', + params: {}, + }) + async getDashboardStatistics() { + const result = await this.broker.cacher?.get( + REDIS_KEY.DASHBOARD_STATISTICS + ); + return result; + } + + @Action({ + name: 'getTopAccounts', + params: {}, + }) + async getTopAccounts() { + const result = await this.broker.cacher?.get(REDIS_KEY.TOP_ACCOUNTS); + return result; + } +} diff --git a/src/services/api-gateways/dashboard_statistics.service.ts b/src/services/api-gateways/dashboard_statistics.service.ts index 34e96d2fc..182612c2e 100644 --- a/src/services/api-gateways/dashboard_statistics.service.ts +++ b/src/services/api-gateways/dashboard_statistics.service.ts @@ -1,10 +1,5 @@ -import { - Action, - Get, - Service, -} from '@ourparentcenter/moleculer-decorators-extended'; +import { Get, Service } from '@ourparentcenter/moleculer-decorators-extended'; import { Context, ServiceBroker } from 'moleculer'; -import { REDIS_KEY } from '../../common'; import BaseService from '../../base/base.service'; import networks from '../../../network.json' assert { type: 'json' }; @@ -35,18 +30,7 @@ export default class DashboardStatisticsService extends BaseService { ); return this.broker.call( - `v1.dashboard-statistics.getDashboardStatistics@${selectedChain?.moleculerNamespace}` + `v1.cross-chains.getDashboardStatistics@${selectedChain?.moleculerNamespace}` ); } - - @Action({ - name: 'getDashboardStatistics', - params: {}, - }) - async getDashboardStatistics() { - const result = await this.broker.cacher?.get( - REDIS_KEY.DASHBOARD_STATISTICS - ); - return result; - } } diff --git a/src/services/api-gateways/statistics.service.ts b/src/services/api-gateways/statistics.service.ts index aebe9fd1c..1af91a3ee 100644 --- a/src/services/api-gateways/statistics.service.ts +++ b/src/services/api-gateways/statistics.service.ts @@ -1,10 +1,5 @@ -import { - Action, - Get, - Service, -} from '@ourparentcenter/moleculer-decorators-extended'; +import { Get, Service } from '@ourparentcenter/moleculer-decorators-extended'; import { Context, ServiceBroker } from 'moleculer'; -import { REDIS_KEY } from '../../common'; import BaseService from '../../base/base.service'; import networks from '../../../network.json' assert { type: 'json' }; @@ -26,6 +21,10 @@ export default class StatisticsService extends BaseService { enum: networks.map((network) => network.chainId), }, }, + cache: { + keys: ['chainid'], + ttl: 3600, + }, }) async getTopAccountsByChainId( ctx: Context<{ chainid: string }, Record> @@ -35,16 +34,7 @@ export default class StatisticsService extends BaseService { ); return this.broker.call( - `v1.statistics.getTopAccounts@${selectedChain?.moleculerNamespace}` + `v1.cross-chains.getTopAccounts@${selectedChain?.moleculerNamespace}` ); } - - @Action({ - name: 'getTopAccounts', - params: {}, - }) - async getTopAccounts() { - const result = await this.broker.cacher?.get(REDIS_KEY.TOP_ACCOUNTS); - return result; - } } diff --git a/src/services/statistics/account_statistics.service.ts b/src/services/statistics/account_statistics.service.ts index 3c822b3e3..cfe3a0e73 100644 --- a/src/services/statistics/account_statistics.service.ts +++ b/src/services/statistics/account_statistics.service.ts @@ -7,6 +7,9 @@ import { import { Context, ServiceBroker } from 'moleculer'; import { parseCoins } from '@cosmjs/proto-signing'; import BigNumber from 'bignumber.js'; +import dayjs from 'dayjs'; +import utc from 'dayjs/plugin/utc'; +import _ from 'lodash'; import { AccountStatistics, Event, @@ -48,7 +51,7 @@ export default class AccountStatisticsService extends BullableService { { id: null, date: ctx.params.date, - accountStats: [], + accountStats: {}, }, { removeOnComplete: true, @@ -66,18 +69,20 @@ export default class AccountStatisticsService extends BullableService { }) public async handleJob(_payload: IAccountStatsParam): Promise { try { - const { accountStats } = _payload; + const { accountStats, date } = _payload; - const syncDate = new Date(_payload.date); - const endTime = syncDate.setUTCHours(0, 0, 0, 0); - syncDate.setDate(syncDate.getDate() - 1); - const startTime = syncDate.setUTCHours(0, 0, 0, 0); + const startTime = dayjs + .utc(date) + .subtract(1, 'day') + .startOf('day') + .toDate(); + const endTime = dayjs.utc(date).startOf('day').toDate(); const startTxId = !_payload.id ? ( await Transaction.query() .select('id') - .where('transaction.timestamp', '>=', new Date(startTime)) + .where('transaction.timestamp', '>=', startTime) .limit(1) .orderBy('id') )[0].id @@ -97,17 +102,19 @@ export default class AccountStatisticsService extends BullableService { 'events:attributes.composite_key', 'events:attributes.value' ) - .where('transaction.timestamp', '>=', new Date(startTime)) - .andWhere('transaction.timestamp', '<', new Date(endTime)) + .where('transaction.timestamp', '>=', startTime) + .andWhere('transaction.timestamp', '<', endTime) .andWhere('transaction.id', '>=', startTxId) .andWhere('transaction.id', '<', nextId) .andWhere((builder) => builder + // Get the address that actually spent or received token .whereIn('events.type', [ Event.EVENT_TYPE.COIN_SPENT, Event.EVENT_TYPE.COIN_RECEIVED, Event.EVENT_TYPE.USE_FEEGRANT, ]) + // If fee_grant is involved, then needs these to track to the granters .orWhereIn('events:attributes.key', [ EventAttribute.ATTRIBUTE_KEY.FEE, EventAttribute.ATTRIBUTE_KEY.FEE_PAYER, @@ -130,14 +137,13 @@ export default class AccountStatisticsService extends BullableService { ) ) .forEach((event) => { - if (!accountStats.find((acc) => acc.address === event.value)) { - accountStats.push({ - address: event.value, + if (!accountStats[event.value]) { + accountStats[event.value] = { amount_sent: '0', amount_received: '0', tx_sent: 0, gas_used: '0', - }); + }; } }); @@ -160,12 +166,8 @@ export default class AccountStatisticsService extends BullableService { EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_SPENDER )?.value; - const indexSpent = accountStats.indexOf( - accountStats.find((acc) => acc.address === addrSpent)! - ); - - accountStats[indexSpent].amount_sent = ( - BigInt(accountStats[indexSpent].amount_sent) + + accountStats[addrSpent].amount_sent = ( + BigInt(accountStats[addrSpent].amount_sent) + BigInt(parseCoins(event.value)[0].amount) ).toString(); break; @@ -178,12 +180,8 @@ export default class AccountStatisticsService extends BullableService { .COIN_RECEIVED_RECEIVER )?.value; - const indexReceived = accountStats.indexOf( - accountStats.find((acc) => acc.address === addrReceived)! - ); - - accountStats[indexReceived].amount_received = ( - BigInt(accountStats[indexReceived].amount_received) + + accountStats[addrReceived].amount_received = ( + BigInt(accountStats[addrReceived].amount_received) + BigInt(parseCoins(event.value)[0].amount) ).toString(); break; @@ -212,12 +210,9 @@ export default class AccountStatisticsService extends BullableService { addr = feeGrant.value; } - const index = accountStats.indexOf( - accountStats.find((acc) => acc.address === addr)! - ); - accountStats[index].tx_sent += 1; - accountStats[index].gas_used = ( - BigInt(accountStats[index].gas_used) + BigInt(event.gas_used) + accountStats[addr].tx_sent += 1; + accountStats[addr].gas_used = ( + BigInt(accountStats[addr].gas_used) + BigInt(event.gas_used) ).toString(); }); @@ -237,16 +232,15 @@ export default class AccountStatisticsService extends BullableService { } ); } else { - const dailyAccountStats = accountStats.map((acc) => + const dailyAccountStats = Object.keys(accountStats).map((acc) => AccountStatistics.fromJson({ - ...acc, - date: new Date(startTime).toISOString(), + address: acc, + ...accountStats[acc], + date: startTime.toISOString(), }) ); - this.logger.info( - `Insert new daily statistic for date ${new Date(startTime)}` - ); + this.logger.info(`Insert new daily statistic for date ${startTime}`); if (dailyAccountStats.length > 0) { await AccountStatistics.query() .insert(dailyAccountStats) @@ -282,47 +276,16 @@ export default class AccountStatisticsService extends BullableService { }) public async handleTopAccounts(_payload: object): Promise { try { - const syncDate = new Date(); - const now = syncDate.setUTCHours(0, 0, 0, 0); - syncDate.setDate(syncDate.getDate() - 3); - const threeDays = syncDate.setUTCHours(0, 0, 0, 0); - syncDate.setDate(syncDate.getDate() - 15); - const fifteenDays = syncDate.setUTCHours(0, 0, 0, 0); - syncDate.setDate(syncDate.getDate() - 30); - const thirtyDays = syncDate.setUTCHours(0, 0, 0, 0); + const now = dayjs.utc().startOf('day').toDate(); + const { dayRange } = config.accountStatistics; const [threeDayStat, fifteenDayStat, thirtyDayStat] = await Promise.all([ - AccountStatistics.query() - .select('address') - .sum('amount_sent as amount_sent') - .sum('amount_received as amount_received') - .sum('tx_sent as tx_sent') - .sum('gas_used as gas_used') - .where('date', '>=', new Date(threeDays)) - .andWhere('date', '<', new Date(now)) - .groupBy('address'), - AccountStatistics.query() - .select('address') - .sum('amount_sent as amount_sent') - .sum('amount_received as amount_received') - .sum('tx_sent as tx_sent') - .sum('gas_used as gas_used') - .where('date', '>=', new Date(fifteenDays)) - .andWhere('date', '<', new Date(now)) - .groupBy('address'), - AccountStatistics.query() - .select('address') - .sum('amount_sent as amount_sent') - .sum('amount_received as amount_received') - .sum('tx_sent as tx_sent') - .sum('gas_used as gas_used') - .where('date', '>=', new Date(thirtyDays)) - .andWhere('date', '<', new Date(now)) - .groupBy('address'), + this.getStatsFromSpecificDaysAgo(dayRange[0], now), + this.getStatsFromSpecificDaysAgo(dayRange[1], now), + this.getStatsFromSpecificDaysAgo(dayRange[2], now), ]); - let topAccounts = await this.broker.cacher?.get(REDIS_KEY.TOP_ACCOUNTS); - topAccounts = { + const topAccounts = { three_days: this.calculateTop(threeDayStat), fifteen_days: this.calculateTop(fifteenDayStat), thirty_days: this.calculateTop(thirtyDayStat), @@ -335,29 +298,63 @@ export default class AccountStatisticsService extends BullableService { } } + private async getStatsFromSpecificDaysAgo( + daysAgo: number, + endTime: Date + ): Promise { + const startTime = dayjs + .utc() + .subtract(daysAgo, 'day') + .startOf('day') + .toDate(); + + const result = await AccountStatistics.query() + .select('address') + .sum('amount_sent as amount_sent') + .sum('amount_received as amount_received') + .sum('tx_sent as tx_sent') + .sum('gas_used as gas_used') + .where('date', '>=', startTime) + .andWhere('date', '<', endTime) + .groupBy('address'); + + return result; + } + private calculateTop(dayStat: AccountStatistics[]) { - const newTopStat = { - top_amount_sent: [] as any[], - top_amount_received: [] as any[], - top_tx_sent: [] as any[], - top_gas_used: [] as any[], - }; + let topAmountSent: any[] = []; + let topAmountReceived: any[] = []; + let topTxSent: any[] = []; + let topGasUsed: any[] = []; const dayStatAmountSent = dayStat - .map((statistic) => statistic.amount_sent) - .reduce((a: string, b: string) => (BigInt(a) + BigInt(b)).toString()); + .reduce( + (init: bigint, accStat: AccountStatistics) => + init + BigInt(accStat.amount_sent), + BigInt(0) + ) + .toString(); const dayStatAmountReceived = dayStat - .map((statistic) => statistic.amount_received) - .reduce((a: string, b: string) => (BigInt(a) + BigInt(b)).toString()); - const dayStatTxSent = dayStat - .map((statistic) => statistic.tx_sent) - .reduce((a: number, b: number) => Number(BigInt(a) + BigInt(b))); + .reduce( + (init: bigint, accStat: AccountStatistics) => + init + BigInt(accStat.amount_received), + BigInt(0) + ) + .toString(); + const dayStatTxSent = dayStat.reduce( + (init: number, accStat: AccountStatistics) => init + accStat.tx_sent, + 0 + ); const dayStatGasUsed = dayStat - .map((statistic) => statistic.gas_used) - .reduce((a: string, b: string) => (BigInt(a) + BigInt(b)).toString()); + .reduce( + (init: bigint, accStat: AccountStatistics) => + init + BigInt(accStat.gas_used), + BigInt(0) + ) + .toString(); dayStat.forEach((stat) => { - newTopStat.top_amount_sent.push({ + topAmountSent.push({ address: stat.address, amount: stat.amount_sent, percentage: Number( @@ -366,7 +363,7 @@ export default class AccountStatisticsService extends BullableService { .dividedBy(BigNumber(dayStatAmountSent)) ), }); - newTopStat.top_amount_received.push({ + topAmountReceived.push({ address: stat.address, amount: stat.amount_received, percentage: Number( @@ -375,12 +372,12 @@ export default class AccountStatisticsService extends BullableService { .dividedBy(BigNumber(dayStatAmountReceived)) ), }); - newTopStat.top_tx_sent.push({ + topTxSent.push({ address: stat.address, amount: stat.tx_sent, percentage: (stat.tx_sent * 100) / dayStatTxSent, }); - newTopStat.top_gas_used.push({ + topGasUsed.push({ address: stat.address, amount: stat.gas_used, percentage: Number( @@ -390,31 +387,36 @@ export default class AccountStatisticsService extends BullableService { ), }); }); - newTopStat.top_amount_sent = newTopStat.top_amount_sent - .sort((a, b) => b.percentage - a.percentage) - .filter( - (_, index) => index < config.accountStatistics.numberOfTopRecords - ); - newTopStat.top_amount_received = newTopStat.top_amount_received - .sort((a, b) => b.percentage - a.percentage) - .filter( - (_, index) => index < config.accountStatistics.numberOfTopRecords - ); - newTopStat.top_tx_sent = newTopStat.top_tx_sent - .sort((a, b) => b.percentage - a.percentage) - .filter( - (_, index) => index < config.accountStatistics.numberOfTopRecords - ); - newTopStat.top_gas_used = newTopStat.top_gas_used - .sort((a, b) => b.percentage - a.percentage) - .filter( - (_, index) => index < config.accountStatistics.numberOfTopRecords - ); - return newTopStat; + topAmountSent = _.orderBy(topAmountSent, 'percentage', 'desc').slice( + 0, + config.accountStatistics.numberOfTopRecords + ); + topAmountReceived = _.orderBy( + topAmountReceived, + 'percentage', + 'desc' + ).slice(0, config.accountStatistics.numberOfTopRecords); + topTxSent = _.orderBy(topTxSent, 'percentage', 'desc').slice( + 0, + config.accountStatistics.numberOfTopRecords + ); + topGasUsed = _.orderBy(topGasUsed, 'percentage', 'desc').slice( + 0, + config.accountStatistics.numberOfTopRecords + ); + + return { + top_amount_sent: topAmountSent, + top_amount_received: topAmountReceived, + top_tx_sent: topTxSent, + top_gas_used: topGasUsed, + }; } public async _start() { + dayjs.extend(utc); + return super._start(); } } diff --git a/src/services/statistics/daily_statistics.service.ts b/src/services/statistics/daily_statistics.service.ts index ba6ef29a6..971d3f4c9 100644 --- a/src/services/statistics/daily_statistics.service.ts +++ b/src/services/statistics/daily_statistics.service.ts @@ -3,6 +3,8 @@ import { Service, } from '@ourparentcenter/moleculer-decorators-extended'; import { Context, ServiceBroker } from 'moleculer'; +import dayjs from 'dayjs'; +import utc from 'dayjs/plugin/utc'; import { Account, DailyStatistics, Transaction } from '../../models'; import { BULL_JOB_NAME, @@ -57,10 +59,12 @@ export default class DailyStatisticsService extends BullableService { }) public async handleJob(_payload: IDailyStatsParam): Promise { try { - const syncDate = new Date(_payload.date); - const endTime = syncDate.setUTCHours(0, 0, 0, 0); - syncDate.setDate(syncDate.getDate() - 1); - const startTime = syncDate.setUTCHours(0, 0, 0, 0); + const startTime = dayjs + .utc(_payload.date) + .subtract(1, 'day') + .startOf('day') + .toDate(); + const endTime = dayjs.utc(_payload.date).startOf('day').toDate(); this.logger.info( `Get daily statistic events at page ${ _payload.offset @@ -70,8 +74,8 @@ export default class DailyStatisticsService extends BullableService { const dailyEvents = await Transaction.query() .joinRelated('events.[attributes]') .select('transaction.id', 'transaction.code', 'events:attributes.value') - .where('transaction.timestamp', '>=', new Date(startTime)) - .andWhere('transaction.timestamp', '<', new Date(endTime)) + .where('transaction.timestamp', '>=', startTime) + .andWhere('transaction.timestamp', '<', endTime) .andWhere( 'events:attributes.value', 'like', @@ -129,12 +133,9 @@ export default class DailyStatisticsService extends BullableService { } ); } else { - syncDate.setDate(syncDate.getDate() - 1); - const previousDay = syncDate.setUTCHours(0, 0, 0, 0); - const [uniqueAddrs, prevDailyStat] = await Promise.all([ Account.query().count('id'), - DailyStatistics.query().findOne('date', new Date(previousDay)), + DailyStatistics.query().findOne('date', startTime), ]); const dailyStat = DailyStatistics.fromJson({ @@ -144,12 +145,10 @@ export default class DailyStatisticsService extends BullableService { unique_addresses_increase: prevDailyStat ? uniqueAddrs[0].count - prevDailyStat.unique_addresses : 0, - date: new Date(startTime).toISOString(), + date: startTime.toISOString(), }); - this.logger.info( - `Insert new daily statistic for date ${new Date(startTime)}` - ); + this.logger.info(`Insert new daily statistic for date ${startTime}`); await DailyStatistics.query() .insert(dailyStat) .catch((error) => { @@ -163,6 +162,8 @@ export default class DailyStatisticsService extends BullableService { } public async _start() { + dayjs.extend(utc); + return super._start(); } } diff --git a/yarn.lock b/yarn.lock index 5688a451e..4f62687f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + "@ampproject/remapping@^2.1.0", "@ampproject/remapping@^2.2.0": version "2.2.1" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" @@ -16,9 +21,9 @@ integrity sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg== "@aura-nw/aurajs@^0.1.3-alpha": - version "0.1.3-alpha" - resolved "https://npm.pkg.github.com/download/@aura-nw/aurajs/0.1.3-alpha/89ae79c11848c0530c0cd46e94f7f1149786a5b1#89ae79c11848c0530c0cd46e94f7f1149786a5b1" - integrity sha512-BMsaxIVNK58ugn3kqnxoomnTAPh5D58b61dWDCcNV0SoRWBAbcbb/1Y82fDXznOR1S1nJc+H3hXw1B2HA1v/Fw== + version "0.1.3-beta-smart-account" + resolved "https://npm.pkg.github.com/download/@aura-nw/aurajs/0.1.3-beta-smart-account/6e88fd3bcd73d81fc73e1c9f7ea4fdf18be9b5e2#6e88fd3bcd73d81fc73e1c9f7ea4fdf18be9b5e2" + integrity sha512-C0fLBowtAG671SQLH3Z0FDcoQo/7yZRbJ2m9qJfB6EGUGgSWr3bIlTFC2dxEgiUkLamiKymgxMgNJZ4/gMlOIw== dependencies: "@babel/runtime" "^7.19.4" "@cosmjs/amino" "0.29.4" @@ -36,10 +41,10 @@ dependencies: "@babel/highlight" "^7.22.5" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.4", "@babel/compat-data@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.5.tgz#b1f6c86a02d85d2dd3368a2b67c09add8cd0c255" - integrity sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.4", "@babel/compat-data@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" + integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== "@babel/core@7.18.10": version "7.18.10" @@ -84,25 +89,25 @@ semver "^6.3.0" "@babel/core@^7.11.6", "@babel/core@^7.12.3": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.5.tgz#d67d9747ecf26ee7ecd3ebae1ee22225fe902a89" - integrity sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg== + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.9.tgz#bd96492c68822198f33e8a256061da3cf391f58f" + integrity sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w== dependencies: "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.22.5" - "@babel/generator" "^7.22.5" - "@babel/helper-compilation-targets" "^7.22.5" - "@babel/helper-module-transforms" "^7.22.5" - "@babel/helpers" "^7.22.5" - "@babel/parser" "^7.22.5" + "@babel/generator" "^7.22.9" + "@babel/helper-compilation-targets" "^7.22.9" + "@babel/helper-module-transforms" "^7.22.9" + "@babel/helpers" "^7.22.6" + "@babel/parser" "^7.22.7" "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.5" + "@babel/traverse" "^7.22.8" "@babel/types" "^7.22.5" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.2" - semver "^6.3.0" + semver "^6.3.1" "@babel/generator@7.18.12": version "7.18.12" @@ -123,10 +128,10 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/generator@^7.18.10", "@babel/generator@^7.21.4", "@babel/generator@^7.22.5", "@babel/generator@^7.7.2": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.5.tgz#1e7bf768688acfb05cf30b2369ef855e82d984f7" - integrity sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA== +"@babel/generator@^7.18.10", "@babel/generator@^7.21.4", "@babel/generator@^7.22.7", "@babel/generator@^7.22.9", "@babel/generator@^7.7.2": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.9.tgz#572ecfa7a31002fa1de2a9d91621fd895da8493d" + integrity sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw== dependencies: "@babel/types" "^7.22.5" "@jridgewell/gen-mapping" "^0.3.2" @@ -147,40 +152,40 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.4", "@babel/helper-compilation-targets@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.5.tgz#fc7319fc54c5e2fa14b2909cf3c5fd3046813e02" - integrity sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.4", "@babel/helper-compilation-targets@^7.22.5", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz#f9d0a7aaaa7cd32a3f31c9316a69f5a9bcacb892" + integrity sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw== dependencies: - "@babel/compat-data" "^7.22.5" + "@babel/compat-data" "^7.22.9" "@babel/helper-validator-option" "^7.22.5" - browserslist "^4.21.3" + browserslist "^4.21.9" lru-cache "^5.1.1" - semver "^6.3.0" + semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0", "@babel/helper-create-class-features-plugin@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.5.tgz#2192a1970ece4685fbff85b48da2c32fcb130b7c" - integrity sha512-xkb58MyOYIslxu3gKmVXmjTtUPvBU4odYzbiIQbWwLKIHCsx6UGZGX6F1IznMFVnDdirseUZopzN+ZRt8Xb33Q== +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0", "@babel/helper-create-class-features-plugin@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.9.tgz#c36ea240bb3348f942f08b0fbe28d6d979fab236" + integrity sha512-Pwyi89uO4YrGKxL/eNJ8lfEH55DnRloGPOseaA8NFNL6jAUnn+KccaISiFazCj5IolPPDjGSdzQzXVzODVRqUQ== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-function-name" "^7.22.5" "@babel/helper-member-expression-to-functions" "^7.22.5" "@babel/helper-optimise-call-expression" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.9" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.5" - semver "^6.3.0" + "@babel/helper-split-export-declaration" "^7.22.6" + semver "^6.3.1" "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.5.tgz#bb2bf0debfe39b831986a4efbf4066586819c6e4" - integrity sha512-1VpEFOIbMRaXyDeUwUfmTIxExLwQ+zkW+Bh5zXpApA3oQedBx9v/updixWxnx/bZpKw7u8VxWjb/qWpIcmPq8A== + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz#9d8e61a8d9366fe66198f57c40565663de0825f6" + integrity sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" regexpu-core "^5.3.1" - semver "^6.3.0" + semver "^6.3.1" "@babel/helper-define-polyfill-provider@^0.3.2", "@babel/helper-define-polyfill-provider@^0.3.3": version "0.3.3" @@ -228,19 +233,16 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-module-transforms@^7.18.9", "@babel/helper-module-transforms@^7.21.2", "@babel/helper-module-transforms@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz#0f65daa0716961b6e96b164034e737f60a80d2ef" - integrity sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw== +"@babel/helper-module-transforms@^7.18.9", "@babel/helper-module-transforms@^7.21.2", "@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz#92dfcb1fbbb2bc62529024f72d942a8c97142129" + integrity sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ== dependencies: "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-module-imports" "^7.22.5" "@babel/helper-simple-access" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" "@babel/helper-validator-identifier" "^7.22.5" - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.5" - "@babel/types" "^7.22.5" "@babel/helper-optimise-call-expression@^7.22.5": version "7.22.5" @@ -255,26 +257,22 @@ integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== "@babel/helper-remap-async-to-generator@^7.18.9", "@babel/helper-remap-async-to-generator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.5.tgz#14a38141a7bf2165ad38da61d61cf27b43015da2" - integrity sha512-cU0Sq1Rf4Z55fgz7haOakIyM7+x/uCFwXpLPaeRzfoUtAEAuUZjZvFPjL/rk5rW693dIgn2hng1W7xbT7lWT4g== + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz#53a25b7484e722d7efb9c350c75c032d4628de82" + integrity sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-wrap-function" "^7.22.5" - "@babel/types" "^7.22.5" + "@babel/helper-wrap-function" "^7.22.9" -"@babel/helper-replace-supers@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.5.tgz#71bc5fb348856dea9fdc4eafd7e2e49f585145dc" - integrity sha512-aLdNM5I3kdI/V9xGNyKSF3X/gTyMUBohTZ+/3QdQKAA9vxIiy12E+8E2HoOP1/DjeqU+g6as35QHJNMDDYpuCg== +"@babel/helper-replace-supers@^7.22.5", "@babel/helper-replace-supers@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz#cbdc27d6d8d18cd22c81ae4293765a5d9afd0779" + integrity sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg== dependencies: "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-member-expression-to-functions" "^7.22.5" "@babel/helper-optimise-call-expression" "^7.22.5" - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.5" - "@babel/types" "^7.22.5" "@babel/helper-simple-access@^7.22.5": version "7.22.5" @@ -290,10 +288,10 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-split-export-declaration@^7.18.6", "@babel/helper-split-export-declaration@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz#88cf11050edb95ed08d596f7a044462189127a08" - integrity sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ== +"@babel/helper-split-export-declaration@^7.18.6", "@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== dependencies: "@babel/types" "^7.22.5" @@ -312,23 +310,22 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== -"@babel/helper-wrap-function@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.5.tgz#44d205af19ed8d872b4eefb0d2fa65f45eb34f06" - integrity sha512-bYqLIBSEshYcYQyfks8ewYA8S30yaGSeRslcvKMvoUk6HHPySbxHq9YRi6ghhzEU+yhQv9bP/jXnygkStOcqZw== +"@babel/helper-wrap-function@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.9.tgz#189937248c45b0182c1dcf32f3444ca153944cb9" + integrity sha512-sZ+QzfauuUEfxSEjKFmi3qDSHgLsTPK/pEpoD/qonZKOtTPTLbf59oabPQ4rKekt9lFcj/hTZaOhWwFYrgjk+Q== dependencies: "@babel/helper-function-name" "^7.22.5" "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.5" "@babel/types" "^7.22.5" -"@babel/helpers@^7.18.9", "@babel/helpers@^7.21.0", "@babel/helpers@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.5.tgz#74bb4373eb390d1ceed74a15ef97767e63120820" - integrity sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q== +"@babel/helpers@^7.18.9", "@babel/helpers@^7.21.0", "@babel/helpers@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.6.tgz#8e61d3395a4f0c5a8060f309fb008200969b5ecd" + integrity sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA== dependencies: "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.5" + "@babel/traverse" "^7.22.6" "@babel/types" "^7.22.5" "@babel/highlight@^7.22.5": @@ -345,10 +342,10 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9" integrity sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ== -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.18.11", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4", "@babel/parser@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.5.tgz#721fd042f3ce1896238cf1b341c77eb7dee7dbea" - integrity sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.18.11", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4", "@babel/parser@^7.22.5", "@babel/parser@^7.22.7": + version "7.22.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae" + integrity sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.22.5" @@ -685,18 +682,18 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-classes@^7.18.9", "@babel/plugin-transform-classes@^7.21.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.5.tgz#635d4e98da741fad814984639f4c0149eb0135e1" - integrity sha512-2edQhLfibpWpsVBx2n/GKOz6JdGQvLruZQfGr9l1qes2KQaWswjBzhQF7UDUZMNaMMQeYnQzxwOMPsbYF7wqPQ== + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz#e04d7d804ed5b8501311293d1a0e6d43e94c3363" + integrity sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.6" "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-function-name" "^7.22.5" "@babel/helper-optimise-call-expression" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-replace-supers" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.18.9", "@babel/plugin-transform-computed-properties@^7.20.7": @@ -826,9 +823,9 @@ "@babel/helper-replace-supers" "^7.22.5" "@babel/plugin-transform-optional-chaining@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.5.tgz#1003762b9c14295501beb41be72426736bedd1e0" - integrity sha512-AconbMKOMkyG+xCng2JogMCDcqW8wedQAqpVIL4cOSescZ7+iW8utC6YDZLMCSUIReEA733gzRSaOSXMAt/4WQ== + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.6.tgz#4bacfe37001fe1901117672875e931d439811564" + integrity sha512-Vd5HiWml0mDVtcLHIoEU5sw6HOUW/Zk0acLs/SAeuLzkGNOPc9DB4nkUajemhCmTIz3eiaKREZn2hQQqF79YTg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" @@ -924,12 +921,12 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-typescript@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.5.tgz#5c0f7adfc1b5f38c4dbc8f79b1f0f8074134bd7d" - integrity sha512-SMubA9S7Cb5sGSFFUlqxyClTA9zWJ8qGQrppNUm05LtFuN1ELRFNndkix4zUJrC9F+YivWwa1dHMSyo0e0N9dA== + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.9.tgz#91e08ad1eb1028ecc62662a842e93ecfbf3c7234" + integrity sha512-BnVR1CpKiuD0iobHPaM1iLvcwPYN2uVFAqoLVSpEDKWuOikoCv5HbKLxclhKYUXlWkX86DoZGtqI4XhbOsyrMg== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.9" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-typescript" "^7.22.5" @@ -1138,9 +1135,9 @@ integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== "@babel/runtime@^7.11.2", "@babel/runtime@^7.18.9", "@babel/runtime@^7.19.0", "@babel/runtime@^7.19.4", "@babel/runtime@^7.21.0", "@babel/runtime@^7.8.4": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.5.tgz#8564dd588182ce0047d55d7a75e93921107b57ec" - integrity sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA== + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438" + integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ== dependencies: regenerator-runtime "^0.13.11" @@ -1185,18 +1182,18 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.18.10", "@babel/traverse@^7.21.4", "@babel/traverse@^7.22.5", "@babel/traverse@^7.7.2": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.5.tgz#44bd276690db6f4940fdb84e1cb4abd2f729ccd1" - integrity sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ== +"@babel/traverse@^7.18.10", "@babel/traverse@^7.21.4", "@babel/traverse@^7.22.6", "@babel/traverse@^7.22.8": + version "7.22.8" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.8.tgz#4d4451d31bc34efeae01eac222b514a77aa4000e" + integrity sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw== dependencies: "@babel/code-frame" "^7.22.5" - "@babel/generator" "^7.22.5" + "@babel/generator" "^7.22.7" "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-function-name" "^7.22.5" "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.5" - "@babel/parser" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.22.7" "@babel/types" "^7.22.5" debug "^4.1.0" globals "^11.1.0" @@ -1233,38 +1230,38 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@bull-board/api@5.2.1", "@bull-board/api@^5.1.1": - version "5.2.1" - resolved "https://registry.yarnpkg.com/@bull-board/api/-/api-5.2.1.tgz#cda18d20c0b2a1ddc79d96f2313bb9043233d961" - integrity sha512-+WRK5pkjly8bVgedBY4f0zzuywFb9bbd2ABv8unvV5F9dWKc1+8W76xAycfvMQNshbWCjAJISWsc5Y/13xcT3Q== +"@bull-board/api@5.6.1", "@bull-board/api@^5.1.1": + version "5.6.1" + resolved "https://registry.yarnpkg.com/@bull-board/api/-/api-5.6.1.tgz#2dd149620110748cc5b64e8908a1f77737c7fceb" + integrity sha512-iq+VpvCt7v2kYCtapDDmgHFyTyHiCbJkSeYbgIDtMIspXfsKxz0ZWKsqaY/VOCRE26jAX2asK17S2XSrANEU0A== dependencies: redis-info "^3.0.8" "@bull-board/express@^5.1.1": - version "5.2.1" - resolved "https://registry.yarnpkg.com/@bull-board/express/-/express-5.2.1.tgz#90ffcc5bb6c3ef4db340c89f4d1b52c4c0d8e479" - integrity sha512-fW6FHka4EvQAqydGlDky6FYcf98HT2vjXWn/qhVbt+X7JoFB0WkMbCthoQy96Y7ac1+Cd8PbvTHYUktSArfSPg== + version "5.6.1" + resolved "https://registry.yarnpkg.com/@bull-board/express/-/express-5.6.1.tgz#616f81c16c159f5d162d0a6c27a423d63a6cdeb7" + integrity sha512-/K5lRN40c0mcu6ye+oMWrBb+luJaz4mbGOqrOoYZif5FJ0jOoyYy+WFc9LlMdSUarLFVX0dbOYV5RmYrUxZJkg== dependencies: - "@bull-board/api" "5.2.1" - "@bull-board/ui" "5.2.1" + "@bull-board/api" "5.6.1" + "@bull-board/ui" "5.6.1" ejs "3.1.7" express "4.17.3" -"@bull-board/ui@5.2.1": - version "5.2.1" - resolved "https://registry.yarnpkg.com/@bull-board/ui/-/ui-5.2.1.tgz#40bc4e4553d0e9bedb95c1292c8cf4e1fc56e207" - integrity sha512-G5gazsvCXm8J2cVS4QEKctGLoXhh9GOFs8fooXEWGd5AAnJZrjGcvI3ICAa4lx8taxlLG0tyF9y3R39iwzccuw== +"@bull-board/ui@5.6.1": + version "5.6.1" + resolved "https://registry.yarnpkg.com/@bull-board/ui/-/ui-5.6.1.tgz#da062505d5e3d7e0c485625abaffdf23c7f841bb" + integrity sha512-cy4fEXxOBHR5+3Ez3bfax+Cd2TUTPyuSYRyQWL0WIwd3bivn/3zov9IThcnHu1YmJ8rjmUNoMHz7JIDXwUg8zg== dependencies: - "@bull-board/api" "5.2.1" + "@bull-board/api" "5.6.1" "@commitlint/cli@^17.4.1": - version "17.6.5" - resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-17.6.5.tgz#3a8abd6499f9d4aeafe3bf9201338ccb868a14b9" - integrity sha512-3PQrWr/uo6lzF5k7n5QuosCYnzaxP9qGBp3jhWP0Vmsa7XA6wrl9ccPqfQyXpSbQE3zBROVO3TDqgPKe4tfmLQ== + version "17.6.7" + resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-17.6.7.tgz#1d352a6cfdb6b6a6ae49a959e6c13dcef1b63782" + integrity sha512-nzZmfO5KIOupYppn1MsnYX/80I+KDlxiwkks3CJT0XT+t34UgqGi3eSyEuzgcIjPlORk5/GMaAEiys78iLfGMg== dependencies: "@commitlint/format" "^17.4.4" - "@commitlint/lint" "^17.6.5" - "@commitlint/load" "^17.5.0" + "@commitlint/lint" "^17.6.7" + "@commitlint/load" "^17.6.7" "@commitlint/read" "^17.5.1" "@commitlint/types" "^17.4.4" execa "^5.0.0" @@ -1274,24 +1271,24 @@ yargs "^17.0.0" "@commitlint/config-conventional@^17.3.0": - version "17.6.5" - resolved "https://registry.yarnpkg.com/@commitlint/config-conventional/-/config-conventional-17.6.5.tgz#a8ec286e634a071329fe45dc4955032c2176aeb5" - integrity sha512-Xl9H9KLl86NZm5CYNTNF9dcz1xelE/EbvhWIWcYxG/rn3UWYWdWmmnX2q6ZduNdLFSGbOxzUpIx61j5zxbeXxg== + version "17.6.7" + resolved "https://registry.yarnpkg.com/@commitlint/config-conventional/-/config-conventional-17.6.7.tgz#8469d977def36148615e9516b1a521e38ca27ddd" + integrity sha512-4oTpEUC0HRM54QRHBPMOJW1pETp7usxXn9RuNYNWHcmu8wi1mpws95hvS20u2n6HtIkTn0jfn7vHioCm4AGUTw== dependencies: conventional-changelog-conventionalcommits "^5.0.0" -"@commitlint/config-validator@^17.4.4": - version "17.4.4" - resolved "https://registry.yarnpkg.com/@commitlint/config-validator/-/config-validator-17.4.4.tgz#d0742705719559a101d2ee49c0c514044af6d64d" - integrity sha512-bi0+TstqMiqoBAQDvdEP4AFh0GaKyLFlPPEObgI29utoKEYoPQTvF0EYqIwYYLEoJYhj5GfMIhPHJkTJhagfeg== +"@commitlint/config-validator@^17.6.7": + version "17.6.7" + resolved "https://registry.yarnpkg.com/@commitlint/config-validator/-/config-validator-17.6.7.tgz#c664d42a1ecf5040a3bb0843845150f55734df41" + integrity sha512-vJSncmnzwMvpr3lIcm0I8YVVDJTzyjy7NZAeXbTXy+MPUdAr9pKyyg7Tx/ebOQ9kqzE6O9WT6jg2164br5UdsQ== dependencies: "@commitlint/types" "^17.4.4" ajv "^8.11.0" -"@commitlint/ensure@^17.4.4": - version "17.4.4" - resolved "https://registry.yarnpkg.com/@commitlint/ensure/-/ensure-17.4.4.tgz#a36e7719bdb9c2b86c8b8c2e852b463a7bfda5fa" - integrity sha512-AHsFCNh8hbhJiuZ2qHv/m59W/GRE9UeOXbkOqxYMNNg9pJ7qELnFcwj5oYpa6vzTSHtPGKf3C2yUFNy1GGHq6g== +"@commitlint/ensure@^17.6.7": + version "17.6.7" + resolved "https://registry.yarnpkg.com/@commitlint/ensure/-/ensure-17.6.7.tgz#77a77a0c05e6a1c34589f59e82e6cb937101fc4b" + integrity sha512-mfDJOd1/O/eIb/h4qwXzUxkmskXDL9vNPnZ4AKYKiZALz4vHzwMxBSYtyL2mUIDeU9DRSpEUins8SeKtFkYHSw== dependencies: "@commitlint/types" "^17.4.4" lodash.camelcase "^4.3.0" @@ -1313,32 +1310,32 @@ "@commitlint/types" "^17.4.4" chalk "^4.1.0" -"@commitlint/is-ignored@^17.6.5": - version "17.6.5" - resolved "https://registry.yarnpkg.com/@commitlint/is-ignored/-/is-ignored-17.6.5.tgz#cea24cd2031fe7d242590b91fab3352750887194" - integrity sha512-CQvAPt9gX7cuUbMrIaIMKczfWJqqr6m8IlJs0F2zYwyyMTQ87QMHIj5jJ5HhOaOkaj6dvTMVGx8Dd1I4xgUuoQ== +"@commitlint/is-ignored@^17.6.7": + version "17.6.7" + resolved "https://registry.yarnpkg.com/@commitlint/is-ignored/-/is-ignored-17.6.7.tgz#711897f19180f1121ecf302a3c5496f9a920a59e" + integrity sha512-vqyNRqtbq72P2JadaoWiuoLtXIs9SaAWDqdtef6G2zsoXqKFc7vqj1f+thzVgosXG3X/5K9jNp+iYijmvOfc/g== dependencies: "@commitlint/types" "^17.4.4" - semver "7.5.0" + semver "7.5.2" -"@commitlint/lint@^17.6.5": - version "17.6.5" - resolved "https://registry.yarnpkg.com/@commitlint/lint/-/lint-17.6.5.tgz#dfa437f14430c9874d6b1a3ba8a2d44b79780c02" - integrity sha512-BSJMwkE4LWXrOsiP9KoHG+/heSDfvOL/Nd16+ojTS/DX8HZr8dNl8l3TfVr/d/9maWD8fSegRGtBtsyGuugFrw== +"@commitlint/lint@^17.6.7": + version "17.6.7" + resolved "https://registry.yarnpkg.com/@commitlint/lint/-/lint-17.6.7.tgz#fb49c2722749e3ef83e2b41258fc32531068a13b" + integrity sha512-TW+AozfuOFMrHn+jdwtz0IWu8REKFp0eryOvoBp2r8IXNc4KihKB1spAiUB6SFyHD6hVVeolz12aHnJ3Mb+xVQ== dependencies: - "@commitlint/is-ignored" "^17.6.5" - "@commitlint/parse" "^17.6.5" - "@commitlint/rules" "^17.6.5" + "@commitlint/is-ignored" "^17.6.7" + "@commitlint/parse" "^17.6.7" + "@commitlint/rules" "^17.6.7" "@commitlint/types" "^17.4.4" -"@commitlint/load@^17.5.0": - version "17.5.0" - resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-17.5.0.tgz#be45dbbb50aaf5eb7e8e940e1e0d6171d1426bab" - integrity sha512-l+4W8Sx4CD5rYFsrhHH8HP01/8jEP7kKf33Xlx2Uk2out/UKoKPYMOIRcDH5ppT8UXLMV+x6Wm5osdRKKgaD1Q== +"@commitlint/load@^17.6.7": + version "17.6.7" + resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-17.6.7.tgz#c63b18ca8942a8fc095ec7a7ff7aa52f3854f6ba" + integrity sha512-QZ2rJTbX55BQdYrCm/p6+hh/pFBgC9nTJxfsrK6xRPe2thiQzHN0AQDBqBwAirn6gIkHrjIbCbtAE6kiDYLjrw== dependencies: - "@commitlint/config-validator" "^17.4.4" + "@commitlint/config-validator" "^17.6.7" "@commitlint/execute-rule" "^17.4.0" - "@commitlint/resolve-extends" "^17.4.4" + "@commitlint/resolve-extends" "^17.6.7" "@commitlint/types" "^17.4.4" "@types/node" "*" chalk "^4.1.0" @@ -1356,10 +1353,10 @@ resolved "https://registry.yarnpkg.com/@commitlint/message/-/message-17.4.2.tgz#f4753a79701ad6db6db21f69076e34de6580e22c" integrity sha512-3XMNbzB+3bhKA1hSAWPCQA3lNxR4zaeQAQcHj0Hx5sVdO6ryXtgUBGGv+1ZCLMgAPRixuc6en+iNAzZ4NzAa8Q== -"@commitlint/parse@^17.6.5": - version "17.6.5" - resolved "https://registry.yarnpkg.com/@commitlint/parse/-/parse-17.6.5.tgz#7b84b328a6a94ca08ab7c98c491d9d3dab68f09d" - integrity sha512-0zle3bcn1Hevw5Jqpz/FzEWNo2KIzUbc1XyGg6WrWEoa6GH3A1pbqNF6MvE6rjuy6OY23c8stWnb4ETRZyN+Yw== +"@commitlint/parse@^17.6.7": + version "17.6.7" + resolved "https://registry.yarnpkg.com/@commitlint/parse/-/parse-17.6.7.tgz#b87c61213653d670f956faafe7783aef9ef13020" + integrity sha512-ibO03BgEns+JJpohpBZYD49mCdSNMg6fTv7vA5yqzEFWkBQk5NWhEBw2yG+Z1UClStIRkMkAYyI2HzoQG9tCQQ== dependencies: "@commitlint/types" "^17.4.4" conventional-changelog-angular "^5.0.11" @@ -1376,24 +1373,24 @@ git-raw-commits "^2.0.11" minimist "^1.2.6" -"@commitlint/resolve-extends@^17.4.4": - version "17.4.4" - resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-17.4.4.tgz#8f931467dea8c43b9fe38373e303f7c220de6fdc" - integrity sha512-znXr1S0Rr8adInptHw0JeLgumS11lWbk5xAWFVno+HUFVN45875kUtqjrI6AppmD3JI+4s0uZlqqlkepjJd99A== +"@commitlint/resolve-extends@^17.6.7": + version "17.6.7" + resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-17.6.7.tgz#9c53a4601c96ab2dd20b90fb35c988639307735d" + integrity sha512-PfeoAwLHtbOaC9bGn/FADN156CqkFz6ZKiVDMjuC2N5N0740Ke56rKU7Wxdwya8R8xzLK9vZzHgNbuGhaOVKIg== dependencies: - "@commitlint/config-validator" "^17.4.4" + "@commitlint/config-validator" "^17.6.7" "@commitlint/types" "^17.4.4" import-fresh "^3.0.0" lodash.mergewith "^4.6.2" resolve-from "^5.0.0" resolve-global "^1.0.0" -"@commitlint/rules@^17.6.5": - version "17.6.5" - resolved "https://registry.yarnpkg.com/@commitlint/rules/-/rules-17.6.5.tgz#fabcacdde923e26ac5ef90d4b3f8fc05526bbaa1" - integrity sha512-uTB3zSmnPyW2qQQH+Dbq2rekjlWRtyrjDo4aLFe63uteandgkI+cc0NhhbBAzcXShzVk0qqp8SlkQMu0mgHg/A== +"@commitlint/rules@^17.6.7": + version "17.6.7" + resolved "https://registry.yarnpkg.com/@commitlint/rules/-/rules-17.6.7.tgz#2dbf52e82b5bcb1c74445637c6a9974571ab54b6" + integrity sha512-x/SDwDTN3w3Gr5xkhrIORu96rlKCc8ZLYEMXRqi9+MB33st2mKcGvKa5uJuigHlbl3xm75bAAubATrodVrjguQ== dependencies: - "@commitlint/ensure" "^17.4.4" + "@commitlint/ensure" "^17.6.7" "@commitlint/message" "^17.4.2" "@commitlint/to-lines" "^17.4.0" "@commitlint/types" "^17.4.4" @@ -1768,14 +1765,14 @@ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.1.tgz#cdd35dce4fa1a89a4fd42b1599eb35b3af408884" integrity sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ== -"@eslint/eslintrc@^2.0.3": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.3.tgz#4910db5505f4d503f27774bf356e3704818a0331" - integrity sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ== +"@eslint/eslintrc@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.0.tgz#82256f164cc9e0b59669efc19d57f8092706841d" + integrity sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.5.2" + espree "^9.6.0" globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" @@ -1783,10 +1780,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.42.0": - version "8.42.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.42.0.tgz#484a1d638de2911e6f5a30c12f49c7e4a3270fb6" - integrity sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw== +"@eslint/js@8.44.0": + version "8.44.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.44.0.tgz#961a5903c74139390478bdc808bcde3fc45ab7af" + integrity sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw== "@fastify/busboy@^1.0.0": version "1.2.1" @@ -1860,28 +1857,28 @@ resolved "https://registry.yarnpkg.com/@jest-decorated/shared/-/shared-0.1.7.tgz#fe2a25c9fc6e79ecfa79a75b4473c0adf4122877" integrity sha512-zmr/uMl7xqIe5jjSiaM3vitcDYUnVC3rGlG7Z2lKI4nvGmCiGv7g9I9U3a48G4X8PQEf1aZP0bLW4SiJMrhDGA== -"@jest/console@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.5.0.tgz#593a6c5c0d3f75689835f1b3b4688c4f8544cb57" - integrity sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ== +"@jest/console@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.6.1.tgz#b48ba7b9c34b51483e6d590f46e5837f1ab5f639" + integrity sha512-Aj772AYgwTSr5w8qnyoJ0eDYvN6bMsH3ORH1ivMotrInHLKdUz6BDlaEXHdM6kODaBIkNIyQGzsMvRdOv7VG7Q== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.1" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^29.5.0" - jest-util "^29.5.0" + jest-message-util "^29.6.1" + jest-util "^29.6.1" slash "^3.0.0" -"@jest/core@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.5.0.tgz#76674b96904484e8214614d17261cc491e5f1f03" - integrity sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ== - dependencies: - "@jest/console" "^29.5.0" - "@jest/reporters" "^29.5.0" - "@jest/test-result" "^29.5.0" - "@jest/transform" "^29.5.0" - "@jest/types" "^29.5.0" +"@jest/core@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.6.1.tgz#fac0d9ddf320490c93356ba201451825231e95f6" + integrity sha512-CcowHypRSm5oYQ1obz1wfvkjZZ2qoQlrKKvlfPwh5jUXVU12TWr2qMeH8chLMuTFzHh5a1g2yaqlqDICbr+ukQ== + dependencies: + "@jest/console" "^29.6.1" + "@jest/reporters" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" @@ -1889,81 +1886,81 @@ exit "^0.1.2" graceful-fs "^4.2.9" jest-changed-files "^29.5.0" - jest-config "^29.5.0" - jest-haste-map "^29.5.0" - jest-message-util "^29.5.0" + jest-config "^29.6.1" + jest-haste-map "^29.6.1" + jest-message-util "^29.6.1" jest-regex-util "^29.4.3" - jest-resolve "^29.5.0" - jest-resolve-dependencies "^29.5.0" - jest-runner "^29.5.0" - jest-runtime "^29.5.0" - jest-snapshot "^29.5.0" - jest-util "^29.5.0" - jest-validate "^29.5.0" - jest-watcher "^29.5.0" + jest-resolve "^29.6.1" + jest-resolve-dependencies "^29.6.1" + jest-runner "^29.6.1" + jest-runtime "^29.6.1" + jest-snapshot "^29.6.1" + jest-util "^29.6.1" + jest-validate "^29.6.1" + jest-watcher "^29.6.1" micromatch "^4.0.4" - pretty-format "^29.5.0" + pretty-format "^29.6.1" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.5.0.tgz#9152d56317c1fdb1af389c46640ba74ef0bb4c65" - integrity sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ== +"@jest/environment@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.6.1.tgz#ee358fff2f68168394b4a50f18c68278a21fe82f" + integrity sha512-RMMXx4ws+Gbvw3DfLSuo2cfQlK7IwGbpuEWXCqyYDcqYTI+9Ju3a5hDnXaxjNsa6uKh9PQF2v+qg+RLe63tz5A== dependencies: - "@jest/fake-timers" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/fake-timers" "^29.6.1" + "@jest/types" "^29.6.1" "@types/node" "*" - jest-mock "^29.5.0" + jest-mock "^29.6.1" -"@jest/expect-utils@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.5.0.tgz#f74fad6b6e20f924582dc8ecbf2cb800fe43a036" - integrity sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg== +"@jest/expect-utils@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.6.1.tgz#ab83b27a15cdd203fe5f68230ea22767d5c3acc5" + integrity sha512-o319vIf5pEMx0LmzSxxkYYxo4wrRLKHq9dP1yJU7FoPTB0LfAKSz8SWD6D/6U3v/O52t9cF5t+MeJiRsfk7zMw== dependencies: jest-get-type "^29.4.3" -"@jest/expect@^29.0.1", "@jest/expect@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.5.0.tgz#80952f5316b23c483fbca4363ce822af79c38fba" - integrity sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g== +"@jest/expect@^29.0.1", "@jest/expect@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.6.1.tgz#fef18265188f6a97601f1ea0a2912d81a85b4657" + integrity sha512-N5xlPrAYaRNyFgVf2s9Uyyvr795jnB6rObuPx4QFvNJz8aAjpZUDfO4bh5G/xuplMID8PrnuF1+SfSyDxhsgYg== dependencies: - expect "^29.5.0" - jest-snapshot "^29.5.0" + expect "^29.6.1" + jest-snapshot "^29.6.1" -"@jest/fake-timers@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.5.0.tgz#d4d09ec3286b3d90c60bdcd66ed28d35f1b4dc2c" - integrity sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg== +"@jest/fake-timers@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.6.1.tgz#c773efddbc61e1d2efcccac008139f621de57c69" + integrity sha512-RdgHgbXyosCDMVYmj7lLpUwXA4c69vcNzhrt69dJJdf8azUrpRh3ckFCaTPNjsEeRi27Cig0oKDGxy5j7hOgHg== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.1" "@sinonjs/fake-timers" "^10.0.2" "@types/node" "*" - jest-message-util "^29.5.0" - jest-mock "^29.5.0" - jest-util "^29.5.0" + jest-message-util "^29.6.1" + jest-mock "^29.6.1" + jest-util "^29.6.1" -"@jest/globals@^29.3.1", "@jest/globals@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.5.0.tgz#6166c0bfc374c58268677539d0c181f9c1833298" - integrity sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ== +"@jest/globals@^29.3.1", "@jest/globals@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.6.1.tgz#c8a8923e05efd757308082cc22893d82b8aa138f" + integrity sha512-2VjpaGy78JY9n9370H8zGRCFbYVWwjY6RdDMhoJHa1sYfwe6XM/azGN0SjY8kk7BOZApIejQ1BFPyH7FPG0w3A== dependencies: - "@jest/environment" "^29.5.0" - "@jest/expect" "^29.5.0" - "@jest/types" "^29.5.0" - jest-mock "^29.5.0" + "@jest/environment" "^29.6.1" + "@jest/expect" "^29.6.1" + "@jest/types" "^29.6.1" + jest-mock "^29.6.1" -"@jest/reporters@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.5.0.tgz#985dfd91290cd78ddae4914ba7921bcbabe8ac9b" - integrity sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA== +"@jest/reporters@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.6.1.tgz#3325a89c9ead3cf97ad93df3a427549d16179863" + integrity sha512-9zuaI9QKr9JnoZtFQlw4GREQbxgmNYXU6QuWtmuODvk5nvPUeBYapVR/VYMyi2WSx3jXTLJTJji8rN6+Cm4+FA== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.5.0" - "@jest/test-result" "^29.5.0" - "@jest/transform" "^29.5.0" - "@jest/types" "^29.5.0" - "@jridgewell/trace-mapping" "^0.3.15" + "@jest/console" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" + "@jridgewell/trace-mapping" "^0.3.18" "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" @@ -1975,9 +1972,9 @@ istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-message-util "^29.5.0" - jest-util "^29.5.0" - jest-worker "^29.5.0" + jest-message-util "^29.6.1" + jest-util "^29.6.1" + jest-worker "^29.6.1" slash "^3.0.0" string-length "^4.0.1" strip-ansi "^6.0.0" @@ -1990,40 +1987,40 @@ dependencies: "@sinclair/typebox" "^0.24.1" -"@jest/schemas@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" - integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== +"@jest/schemas@^29.6.0": + version "29.6.0" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.0.tgz#0f4cb2c8e3dca80c135507ba5635a4fd755b0040" + integrity sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ== dependencies: - "@sinclair/typebox" "^0.25.16" + "@sinclair/typebox" "^0.27.8" -"@jest/source-map@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.4.3.tgz#ff8d05cbfff875d4a791ab679b4333df47951d20" - integrity sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w== +"@jest/source-map@^29.6.0": + version "29.6.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.0.tgz#bd34a05b5737cb1a99d43e1957020ac8e5b9ddb1" + integrity sha512-oA+I2SHHQGxDCZpbrsCQSoMLb3Bz547JnM+jUr9qEbuw0vQlWZfpPS7CO9J7XiwKicEz9OFn/IYoLkkiUD7bzA== dependencies: - "@jridgewell/trace-mapping" "^0.3.15" + "@jridgewell/trace-mapping" "^0.3.18" callsites "^3.0.0" graceful-fs "^4.2.9" -"@jest/test-result@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.5.0.tgz#7c856a6ca84f45cc36926a4e9c6b57f1973f1408" - integrity sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ== +"@jest/test-result@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.6.1.tgz#850e565a3f58ee8ca6ec424db00cb0f2d83c36ba" + integrity sha512-Ynr13ZRcpX6INak0TPUukU8GWRfm/vAytE3JbJNGAvINySWYdfE7dGZMbk36oVuK4CigpbhMn8eg1dixZ7ZJOw== dependencies: - "@jest/console" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/console" "^29.6.1" + "@jest/types" "^29.6.1" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz#34d7d82d3081abd523dbddc038a3ddcb9f6d3cc4" - integrity sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ== +"@jest/test-sequencer@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.6.1.tgz#e3e582ee074dd24ea9687d7d1aaf05ee3a9b068e" + integrity sha512-oBkC36PCDf/wb6dWeQIhaviU0l5u6VCsXa119yqdUosYAt7/FbQU2M2UoziO3igj/HBDEgp57ONQ3fm0v9uyyg== dependencies: - "@jest/test-result" "^29.5.0" + "@jest/test-result" "^29.6.1" graceful-fs "^4.2.9" - jest-haste-map "^29.5.0" + jest-haste-map "^29.6.1" slash "^3.0.0" "@jest/transform@28.1.3": @@ -2047,22 +2044,22 @@ slash "^3.0.0" write-file-atomic "^4.0.1" -"@jest/transform@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.5.0.tgz#cf9c872d0965f0cbd32f1458aa44a2b1988b00f9" - integrity sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw== +"@jest/transform@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.6.1.tgz#acb5606019a197cb99beda3c05404b851f441c92" + integrity sha512-URnTneIU3ZjRSaf906cvf6Hpox3hIeJXRnz3VDSw5/X93gR8ycdfSIEy19FlVx8NFmpN7fe3Gb1xF+NjXaQLWg== dependencies: "@babel/core" "^7.11.6" - "@jest/types" "^29.5.0" - "@jridgewell/trace-mapping" "^0.3.15" + "@jest/types" "^29.6.1" + "@jridgewell/trace-mapping" "^0.3.18" babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" convert-source-map "^2.0.0" fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.9" - jest-haste-map "^29.5.0" + jest-haste-map "^29.6.1" jest-regex-util "^29.4.3" - jest-util "^29.5.0" + jest-util "^29.6.1" micromatch "^4.0.4" pirates "^4.0.4" slash "^3.0.0" @@ -2080,12 +2077,12 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jest/types@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593" - integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog== +"@jest/types@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.1.tgz#ae79080278acff0a6af5eb49d063385aaa897bf2" + integrity sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw== dependencies: - "@jest/schemas" "^29.4.3" + "@jest/schemas" "^29.6.0" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" @@ -2134,7 +2131,7 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": version "0.3.18" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== @@ -2429,10 +2426,10 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" integrity sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA== -"@sinclair/typebox@^0.25.16": - version "0.25.24" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" - integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== "@sinonjs/commons@^3.0.0": version "3.0.0" @@ -2442,9 +2439,9 @@ type-detect "4.0.8" "@sinonjs/fake-timers@^10.0.2": - version "10.2.0" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.2.0.tgz#b3e322a34c5f26e3184e7f6115695f299c1b1194" - integrity sha512-OPwQlEdg40HAj5KNF8WW6q2KG4Z+cBCZb3m4ninfTZKaBmbIJodviQsDBoYMPHkOyJJMHnOJo5j2+LKDOhOACg== + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== dependencies: "@sinonjs/commons" "^3.0.0" @@ -2568,9 +2565,9 @@ "@types/jest" "*" "@types/jest@*": - version "29.5.2" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.2.tgz#86b4afc86e3a8f3005b297ed8a72494f89e6395b" - integrity sha512-mSoZVJF5YzGVCk+FsDxzDuH7s+SCkzrgKZzf0Z0T2WudhBUPoF6ktoTPC4R0ZoCPCV5xUvuU6ias5NvxcBcMMg== + version "29.5.3" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.3.tgz#7a35dc0044ffb8b56325c6802a4781a626b05777" + integrity sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA== dependencies: expect "^29.0.0" pretty-format "^29.0.0" @@ -2613,14 +2610,14 @@ "@types/node" "*" "@types/node@*", "@types/node@>=13.7.0": - version "20.3.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.1.tgz#e8a83f1aa8b649377bb1fb5d7bac5cb90e784dfe" - integrity sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg== + version "20.4.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.2.tgz#129cc9ae69f93824f92fac653eebfb4812ab4af9" + integrity sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw== "@types/node@^18.11.11": - version "18.16.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.18.tgz#85da09bafb66d4bc14f7c899185336d0c1736390" - integrity sha512-/aNaQZD0+iSBAGnvvN2Cx92HqE5sZCPZtx2TsK+4nvV23fFe09jVDvpArXr2j9DnYlzuU9WuoykDDc6wqvpNcw== + version "18.16.19" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.19.tgz#cb03fca8910fdeb7595b755126a8a78144714eea" + integrity sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2685,87 +2682,87 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^5.40.1": - version "5.59.11" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.11.tgz#8d466aa21abea4c3f37129997b198d141f09e76f" - integrity sha512-XxuOfTkCUiOSyBWIvHlUraLw/JT/6Io1365RO6ZuI88STKMavJZPNMU0lFcUTeQXEhHiv64CbxYxBNoDVSmghg== + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" + integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== dependencies: "@eslint-community/regexpp" "^4.4.0" - "@typescript-eslint/scope-manager" "5.59.11" - "@typescript-eslint/type-utils" "5.59.11" - "@typescript-eslint/utils" "5.59.11" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/type-utils" "5.62.0" + "@typescript-eslint/utils" "5.62.0" debug "^4.3.4" - grapheme-splitter "^1.0.4" + graphemer "^1.4.0" ignore "^5.2.0" natural-compare-lite "^1.4.0" semver "^7.3.7" tsutils "^3.21.0" "@typescript-eslint/parser@^5.40.1": - version "5.59.11" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.11.tgz#af7d4b7110e3068ce0b97550736de455e4250103" - integrity sha512-s9ZF3M+Nym6CAZEkJJeO2TFHHDsKAM3ecNkLuH4i4s8/RCPnF5JRip2GyviYkeEAcwGMJxkqG9h2dAsnA1nZpA== + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" + integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== dependencies: - "@typescript-eslint/scope-manager" "5.59.11" - "@typescript-eslint/types" "5.59.11" - "@typescript-eslint/typescript-estree" "5.59.11" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.59.11": - version "5.59.11" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.11.tgz#5d131a67a19189c42598af9fb2ea1165252001ce" - integrity sha512-dHFOsxoLFtrIcSj5h0QoBT/89hxQONwmn3FOQ0GOQcLOOXm+MIrS8zEAhs4tWl5MraxCY3ZJpaXQQdFMc2Tu+Q== +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== dependencies: - "@typescript-eslint/types" "5.59.11" - "@typescript-eslint/visitor-keys" "5.59.11" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" -"@typescript-eslint/type-utils@5.59.11": - version "5.59.11" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.11.tgz#5eb67121808a84cb57d65a15f48f5bdda25f2346" - integrity sha512-LZqVY8hMiVRF2a7/swmkStMYSoXMFlzL6sXV6U/2gL5cwnLWQgLEG8tjWPpaE4rMIdZ6VKWwcffPlo1jPfk43g== +"@typescript-eslint/type-utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" + integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== dependencies: - "@typescript-eslint/typescript-estree" "5.59.11" - "@typescript-eslint/utils" "5.59.11" + "@typescript-eslint/typescript-estree" "5.62.0" + "@typescript-eslint/utils" "5.62.0" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.59.11": - version "5.59.11" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.11.tgz#1a9018fe3c565ba6969561f2a49f330cf1fe8db1" - integrity sha512-epoN6R6tkvBYSc+cllrz+c2sOFWkbisJZWkOE+y3xHtvYaOE6Wk6B8e114McRJwFRjGvYdJwLXQH5c9osME/AA== +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== -"@typescript-eslint/typescript-estree@5.59.11": - version "5.59.11" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.11.tgz#b2caaa31725e17c33970c1197bcd54e3c5f42b9f" - integrity sha512-YupOpot5hJO0maupJXixi6l5ETdrITxeo5eBOeuV7RSKgYdU3G5cxO49/9WRnJq9EMrB7AuTSLH/bqOsXi7wPA== +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== dependencies: - "@typescript-eslint/types" "5.59.11" - "@typescript-eslint/visitor-keys" "5.59.11" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.59.11": - version "5.59.11" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.11.tgz#9dbff49dc80bfdd9289f9f33548f2e8db3c59ba1" - integrity sha512-didu2rHSOMUdJThLk4aZ1Or8IcO3HzCw/ZvEjTTIfjIrcdd5cvSIwwDy2AOlE7htSNp7QIZ10fLMyRCveesMLg== +"@typescript-eslint/utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.59.11" - "@typescript-eslint/types" "5.59.11" - "@typescript-eslint/typescript-estree" "5.59.11" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" eslint-scope "^5.1.1" semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.59.11": - version "5.59.11" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.11.tgz#dca561ddad169dc27d62396d64f45b2d2c3ecc56" - integrity sha512-KGYniTGG3AMTuKF9QBD7EIrvufkB6O6uX3knP73xbKLMpH+QRPcgnCxjWXSHjMRuOxFLovljqQgQpR0c7GvjoA== +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== dependencies: - "@typescript-eslint/types" "5.59.11" + "@typescript-eslint/types" "5.62.0" eslint-visitor-keys "^3.3.0" JSONStream@^1.0.4: @@ -2794,10 +2791,10 @@ acorn-walk@^8.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^8.4.1, acorn@^8.8.0: - version "8.8.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== +acorn@^8.4.1, acorn@^8.9.0: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== aggregate-error@^3.0.0: version "3.1.0" @@ -2999,6 +2996,18 @@ array.prototype.flatmap@^1.3.1: es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" +arraybuffer.prototype.slice@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz#9b5ea3868a6eebc30273da577eb888381c0044bb" + integrity sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + get-intrinsic "^1.2.1" + is-array-buffer "^3.0.2" + is-shared-array-buffer "^1.0.2" + arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -3044,9 +3053,9 @@ available-typed-arrays@^1.0.5: integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== aws-sdk@^2.1377.0: - version "2.1396.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1396.0.tgz#14dc92f874aca039a329d5a96f9f894fd9159252" - integrity sha512-5tAzB4pO9mfwb4XbDIv7wj4IsxaLI+KEAUZ8CR80sh2OdsP9AVGtMGH61dH6DQbHxCiwtLyQuoy7gZEuXv2ldQ== + version "2.1418.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1418.0.tgz#3c04e3484b9c826492b1d1d10f4ba19f4860e2a0" + integrity sha512-6WDMJQAWKwVt+44+61c/SAXKpUSwToqBMeaqizhEe3GN8TWfxMc9RfCnsYIIwS+L+5hedmKC5oc6Fg2ujs8KUQ== dependencies: buffer "4.9.2" events "1.1.1" @@ -3093,12 +3102,12 @@ axios@^1.3.4: form-data "^4.0.0" proxy-from-env "^1.1.0" -babel-jest@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.5.0.tgz#3fe3ddb109198e78b1c88f9ebdecd5e4fc2f50a5" - integrity sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q== +babel-jest@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.6.1.tgz#a7141ad1ed5ec50238f3cd36127636823111233a" + integrity sha512-qu+3bdPEQC6KZSPz+4Fyjbga5OODNcp49j6GKzG1EKbkfyJBxEYGVUmVGpwCSeGouG52R4EgYMLb6p9YeEEQ4A== dependencies: - "@jest/transform" "^29.5.0" + "@jest/transform" "^29.6.1" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" babel-preset-jest "^29.5.0" @@ -3302,13 +3311,13 @@ brorand@^1.1.0: resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== -browserslist@^4.21.3, browserslist@^4.21.5: - version "4.21.8" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.8.tgz#db2498e1f4b80ed199c076248a094935860b6017" - integrity sha512-j+7xYe+v+q2Id9qbBeCI8WX5NmZSRe8es1+0xntD/+gaWXznP8tFEkv5IgSaHf5dS1YwVMbX/4W6m937mj+wQw== +browserslist@^4.21.9: + version "4.21.9" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.9.tgz#e11bdd3c313d7e2a9e87e8b4b0c7872b13897635" + integrity sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg== dependencies: - caniuse-lite "^1.0.30001502" - electron-to-chromium "^1.4.428" + caniuse-lite "^1.0.30001503" + electron-to-chromium "^1.4.431" node-releases "^2.0.12" update-browserslist-db "^1.0.11" @@ -3368,9 +3377,9 @@ bull@^4.10.2, bull@^4.7.0: uuid "^8.3.0" bullmq@^3.13.3: - version "3.15.5" - resolved "https://registry.yarnpkg.com/bullmq/-/bullmq-3.15.5.tgz#b8e80f47cd167a0ce77e34e47404adcb98447813" - integrity sha512-NotMUfU5wBAjZQgcl/F5UMUIs1DiXDRmZBN5BPuSQ841w7mYlaghTMGSy0H+kAwFa7388e7/5COTiX8EUxFo5w== + version "3.15.8" + resolved "https://registry.yarnpkg.com/bullmq/-/bullmq-3.15.8.tgz#e8ec5b46b0b7d7ce57e509280d03745109411e05" + integrity sha512-k3uimHGhl5svqD7SEak+iI6c5DxeLOaOXzCufI9Ic0ST3nJr69v71TGR4cXCTXdgCff3tLec5HgoBnfyWjgn5A== dependencies: cron-parser "^4.6.0" glob "^8.0.3" @@ -3428,10 +3437,10 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001502: - version "1.0.30001502" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001502.tgz#f7e4a76eb1d2d585340f773767be1fefc118dca8" - integrity sha512-AZ+9tFXw1sS0o0jcpJQIXvFTOB/xGiQ4OQ2t98QX3NDn2EZTSRBC801gxrsGgViuq2ak/NLkNgSNEPtCr5lfKg== +caniuse-lite@^1.0.30001503: + version "1.0.30001517" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz#90fabae294215c3495807eb24fc809e11dc2f0a8" + integrity sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA== case@1.6.3: version "1.6.3" @@ -3599,9 +3608,9 @@ co@^4.6.0: integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== collect-v8-coverage@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" - integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== + version "1.0.2" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" + integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== color-convert@^1.9.0: version "1.9.3" @@ -3654,7 +3663,7 @@ commander@^10.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== -commander@^9.1.0, commander@^9.4.1: +commander@^9.4.1: version "9.5.0" resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== @@ -3739,11 +3748,11 @@ cookie@0.4.2: integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== core-js-compat@^3.21.0, core-js-compat@^3.22.1, core-js-compat@^3.25.1: - version "3.31.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.31.0.tgz#4030847c0766cc0e803dcdfb30055d7ef2064bf1" - integrity sha512-hM7YCu1cU6Opx7MXNu0NuumM0ezNeAeRKadixyiQELWY3vT3De9S4J5ZBMraWV2vZnrE1Cirl0GtFtDtMUXzPw== + version "3.31.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.31.1.tgz#5084ad1a46858df50ff89ace152441a63ba7aae0" + integrity sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA== dependencies: - browserslist "^4.21.5" + browserslist "^4.21.9" core-util-is@1.0.2: version "1.0.2" @@ -3842,6 +3851,11 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +dayjs@^1.11.9: + version "1.11.9" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.9.tgz#9ca491933fadd0a60a2c19f6c237c03517d71d1a" + integrity sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA== + db-errors@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/db-errors/-/db-errors-0.2.3.tgz#a6a38952e00b20e790f2695a6446b3c65497ffa2" @@ -3995,9 +4009,9 @@ dot-prop@^5.1.0: is-obj "^2.0.0" dotenv@^16.0.3: - version "16.1.4" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.1.4.tgz#67ac1a10cd9c25f5ba604e4e08bc77c0ebe0ca8c" - integrity sha512-m55RtE8AsPeJBpOIFKihEmqUcoVncQIwo7x9U8ZwLEZw9ZpXboz2c+rvog+jUaJvVrZ5kBOeYQBX5+8Aa/OZQw== + version "16.3.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" + integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== dotty@0.1.2: version "0.1.2" @@ -4029,10 +4043,10 @@ ejs@3.1.7: dependencies: jake "^10.8.5" -electron-to-chromium@^1.4.428: - version "1.4.428" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.428.tgz#c31fc88e854f49d8305cdabf6ec934ff1588a902" - integrity sha512-L7uUknyY286of0AYC8CKfgWstD0Smk2DvHDi9F0GWQhSH90Bzi7iDrmCbZKz75tYJxeGSAc7TYeKpmbjMDoh1w== +electron-to-chromium@^1.4.431: + version "1.4.466" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.466.tgz#17193d70f203da3d52a89c653b8d89f47a51d79d" + integrity sha512-TSkRvbXRXD8BwhcGlZXDsbI2lRoP8dvqR7LQnqQNk9KxXBc4tG8O+rTuXgTyIpEdiqSGKEBSqrxdqEntnjNncA== elliptic@^6.5.4: version "6.5.4" @@ -4075,17 +4089,18 @@ error-ex@^1.3.1: is-arrayish "^0.2.1" es-abstract@^1.19.0, es-abstract@^1.20.4: - version "1.21.2" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" - integrity sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg== + version "1.22.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.1.tgz#8b4e5fc5cefd7f1660f0f8e1a52900dfbc9d9ccc" + integrity sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw== dependencies: array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.1" available-typed-arrays "^1.0.5" call-bind "^1.0.2" es-set-tostringtag "^2.0.1" es-to-primitive "^1.2.1" function.prototype.name "^1.1.5" - get-intrinsic "^1.2.0" + get-intrinsic "^1.2.1" get-symbol-description "^1.0.0" globalthis "^1.0.3" gopd "^1.0.1" @@ -4105,14 +4120,18 @@ es-abstract@^1.19.0, es-abstract@^1.20.4: object-inspect "^1.12.3" object-keys "^1.1.1" object.assign "^4.1.4" - regexp.prototype.flags "^1.4.3" + regexp.prototype.flags "^1.5.0" + safe-array-concat "^1.0.0" safe-regex-test "^1.0.0" string.prototype.trim "^1.2.7" string.prototype.trimend "^1.0.6" string.prototype.trimstart "^1.0.6" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" typed-array-length "^1.0.4" unbox-primitive "^1.0.2" - which-typed-array "^1.1.9" + which-typed-array "^1.1.10" es-set-tostringtag@^2.0.1: version "2.0.1" @@ -4243,9 +4262,9 @@ eslint-config-airbnb-base@^15.0.0: semver "^6.3.0" eslint-config-airbnb-typescript@^17.0.0: - version "17.0.0" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.0.0.tgz#360dbcf810b26bbcf2ff716198465775f1c49a07" - integrity sha512-elNiuzD0kPAPTXjFWg+lE24nMdHMtuxgYoD30OyMD6yrW1AhFZPAg27VX7d3tzOErw+dgJTNWfRSDqEcXb4V0g== + version "17.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.1.0.tgz#fda960eee4a510f092a9a1c139035ac588937ddc" + integrity sha512-GPxI5URre6dDpJ0CtcthSZVBAfI+Uw7un5OYNVxP2EYi3H81Jw701yFP7AU+/vCE7xBtFmjge7kfhhk4+RAiig== dependencies: eslint-config-airbnb-base "^15.0.0" @@ -4307,9 +4326,9 @@ eslint-scope@^5.1.1: estraverse "^4.1.1" eslint-scope@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" - integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== + version "7.2.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.1.tgz#936821d3462675f25a18ac5fd88a67cc15b393bd" + integrity sha512-CvefSOsDdaYYvxChovdrPo/ZGt8d5lrJWleAc1diXRKhHGiTYEI26cvo8Kle/wGnsizoCJjK73FMg1/IkIwiNA== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" @@ -4320,14 +4339,14 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA== eslint@^8.25.0: - version "8.42.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.42.0.tgz#7bebdc3a55f9ed7167251fe7259f75219cade291" - integrity sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A== + version "8.45.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.45.0.tgz#bab660f90d18e1364352c0a6b7c6db8edb458b78" + integrity sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.4.0" - "@eslint/eslintrc" "^2.0.3" - "@eslint/js" "8.42.0" + "@eslint/eslintrc" "^2.1.0" + "@eslint/js" "8.44.0" "@humanwhocodes/config-array" "^0.11.10" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" @@ -4339,7 +4358,7 @@ eslint@^8.25.0: escape-string-regexp "^4.0.0" eslint-scope "^7.2.0" eslint-visitor-keys "^3.4.1" - espree "^9.5.2" + espree "^9.6.0" esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -4349,7 +4368,6 @@ eslint@^8.25.0: globals "^13.19.0" graphemer "^1.4.0" ignore "^5.2.0" - import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" is-path-inside "^3.0.3" @@ -4359,9 +4377,8 @@ eslint@^8.25.0: lodash.merge "^4.6.2" minimatch "^3.1.2" natural-compare "^1.4.0" - optionator "^0.9.1" + optionator "^0.9.3" strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" text-table "^0.2.0" esm@^3.2.25: @@ -4369,12 +4386,12 @@ esm@^3.2.25: resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== -espree@^9.5.2: - version "9.5.2" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.2.tgz#e994e7dc33a082a7a82dceaf12883a829353215b" - integrity sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw== +espree@^9.6.0: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== dependencies: - acorn "^8.8.0" + acorn "^8.9.0" acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" @@ -4475,16 +4492,17 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== -expect@^29.0.0, expect@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.5.0.tgz#68c0509156cb2a0adb8865d413b137eeaae682f7" - integrity sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg== +expect@^29.0.0, expect@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.6.1.tgz#64dd1c8f75e2c0b209418f2b8d36a07921adfdf1" + integrity sha512-XEdDLonERCU1n9uR56/Stx9OqojaLAQtZf9PrCHH9Hl8YXiEIka3H4NXJ3NOIBmQJTg7+j7buh34PMHfJujc8g== dependencies: - "@jest/expect-utils" "^29.5.0" + "@jest/expect-utils" "^29.6.1" + "@types/node" "*" jest-get-type "^29.4.3" - jest-matcher-utils "^29.5.0" - jest-message-util "^29.5.0" - jest-util "^29.5.0" + jest-matcher-utils "^29.6.1" + jest-message-util "^29.6.1" + jest-util "^29.6.1" express@4.17.3: version "4.17.3" @@ -4573,9 +4591,9 @@ fast-diff@^1.1.2: integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== fast-glob@^3.2.9: - version "3.2.12" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" - integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== + version "3.3.0" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.0.tgz#7c40cb491e1e2ed5664749e87bfb516dbe8727c0" + integrity sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -4593,7 +4611,7 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== -fastest-validator@^1.16.0: +fastest-validator@^1.17.0: version "1.17.0" resolved "https://registry.yarnpkg.com/fastest-validator/-/fastest-validator-1.17.0.tgz#0c032e9c42c40a237d24b20be187f732ebdcc062" integrity sha512-37U/JDP72QSFqcvNnO81f0Aeu9og+5I3mc55b2v2RbV0S2I7KvQEdBtrFeIvaYVgam1bDUgy9F9AK9HolByogA== @@ -4803,7 +4821,7 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== @@ -4897,15 +4915,15 @@ glob@8.0.3: once "^1.3.0" glob@^10.0.0: - version "10.2.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.2.7.tgz#9dd2828cd5bc7bd861e7738d91e7113dda41d7d8" - integrity sha512-jTKehsravOJo8IJxUGfZILnkvVJM/MOfHRs8QcXolVef2zNI9Tqyy5+SeuOAZd3upViEZQLyFpQhYiHLrMUNmA== + version "10.3.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.3.tgz#8360a4ffdd6ed90df84aa8d52f21f452e86a123b" + integrity sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw== dependencies: foreground-child "^3.1.0" jackspeak "^2.0.3" minimatch "^9.0.1" - minipass "^5.0.0 || ^6.0.2" - path-scurry "^1.7.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-scurry "^1.10.1" glob@^7.0.0, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7, glob@^7.2.0: version "7.2.3" @@ -4980,11 +4998,6 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -grapheme-splitter@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" - integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== - graphemer@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" @@ -4998,9 +5011,9 @@ graphql-tag@^2.12.6: tslib "^2.1.0" graphql@^16.6.0: - version "16.6.0" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.6.0.tgz#c2dcffa4649db149f6282af726c8c83f1c7c5fdb" - integrity sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw== + version "16.7.1" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.7.1.tgz#11475b74a7bff2aefd4691df52a0eca0abd9b642" + integrity sha512-DRYR9tf+UGU0KOsMcKAlXeFfX89UiiIZ0dRU3mR0yJfu6OjZqUcp68NnFLnqQU5RexygFoDy1EW+ccOYcPfmHg== har-schema@^2.0.0: version "2.0.0" @@ -5343,7 +5356,7 @@ ipaddr.js@1.9.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -ipaddr.js@^2.0.1: +ipaddr.js@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.1.0.tgz#2119bc447ff8c257753b196fc5f1ce08a4cdf39f" integrity sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ== @@ -5532,15 +5545,11 @@ is-text-path@^1.0.1: text-extensions "^1.0.0" is-typed-array@^1.1.10, is-typed-array@^1.1.3, is-typed-array@^1.1.9: - version "1.1.10" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" - integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== + version "1.1.12" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" + which-typed-array "^1.1.11" is-typedarray@~1.0.0: version "1.0.0" @@ -5564,6 +5573,11 @@ isarray@^1.0.0, isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -5648,87 +5662,87 @@ jest-changed-files@^29.5.0: execa "^5.0.0" p-limit "^3.1.0" -jest-circus@^29.0.1, jest-circus@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.5.0.tgz#b5926989449e75bff0d59944bae083c9d7fb7317" - integrity sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA== +jest-circus@^29.0.1, jest-circus@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.6.1.tgz#861dab37e71a89907d1c0fabc54a0019738ed824" + integrity sha512-tPbYLEiBU4MYAL2XoZme/bgfUeotpDBd81lgHLCbDZZFaGmECk0b+/xejPFtmiBP87GgP/y4jplcRpbH+fgCzQ== dependencies: - "@jest/environment" "^29.5.0" - "@jest/expect" "^29.5.0" - "@jest/test-result" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/environment" "^29.6.1" + "@jest/expect" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/types" "^29.6.1" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" dedent "^0.7.0" is-generator-fn "^2.0.0" - jest-each "^29.5.0" - jest-matcher-utils "^29.5.0" - jest-message-util "^29.5.0" - jest-runtime "^29.5.0" - jest-snapshot "^29.5.0" - jest-util "^29.5.0" + jest-each "^29.6.1" + jest-matcher-utils "^29.6.1" + jest-message-util "^29.6.1" + jest-runtime "^29.6.1" + jest-snapshot "^29.6.1" + jest-util "^29.6.1" p-limit "^3.1.0" - pretty-format "^29.5.0" + pretty-format "^29.6.1" pure-rand "^6.0.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-cli@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.5.0.tgz#b34c20a6d35968f3ee47a7437ff8e53e086b4a67" - integrity sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw== +jest-cli@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.6.1.tgz#99d9afa7449538221c71f358f0fdd3e9c6e89f72" + integrity sha512-607dSgTA4ODIN6go9w6xY3EYkyPFGicx51a69H7yfvt7lN53xNswEVLovq+E77VsTRi5fWprLH0yl4DJgE8Ing== dependencies: - "@jest/core" "^29.5.0" - "@jest/test-result" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/core" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/types" "^29.6.1" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^29.5.0" - jest-util "^29.5.0" - jest-validate "^29.5.0" + jest-config "^29.6.1" + jest-util "^29.6.1" + jest-validate "^29.6.1" prompts "^2.0.1" yargs "^17.3.1" -jest-config@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.5.0.tgz#3cc972faec8c8aaea9ae158c694541b79f3748da" - integrity sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA== +jest-config@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.6.1.tgz#d785344509065d53a238224c6cdc0ed8e2f2f0dd" + integrity sha512-XdjYV2fy2xYixUiV2Wc54t3Z4oxYPAELUzWnV6+mcbq0rh742X2p52pii5A3oeRzYjLnQxCsZmp0qpI6klE2cQ== dependencies: "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.5.0" - "@jest/types" "^29.5.0" - babel-jest "^29.5.0" + "@jest/test-sequencer" "^29.6.1" + "@jest/types" "^29.6.1" + babel-jest "^29.6.1" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.3" graceful-fs "^4.2.9" - jest-circus "^29.5.0" - jest-environment-node "^29.5.0" + jest-circus "^29.6.1" + jest-environment-node "^29.6.1" jest-get-type "^29.4.3" jest-regex-util "^29.4.3" - jest-resolve "^29.5.0" - jest-runner "^29.5.0" - jest-util "^29.5.0" - jest-validate "^29.5.0" + jest-resolve "^29.6.1" + jest-runner "^29.6.1" + jest-util "^29.6.1" + jest-validate "^29.6.1" micromatch "^4.0.4" parse-json "^5.2.0" - pretty-format "^29.5.0" + pretty-format "^29.6.1" slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.5.0.tgz#e0d83a58eb5451dcc1fa61b1c3ee4e8f5a290d63" - integrity sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw== +jest-diff@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.6.1.tgz#13df6db0a89ee6ad93c747c75c85c70ba941e545" + integrity sha512-FsNCvinvl8oVxpNLttNQX7FAq7vR+gMDGj90tiP7siWw1UdakWUGqrylpsYrpvj908IYckm5Y0Q7azNAozU1Kg== dependencies: chalk "^4.0.0" diff-sequences "^29.4.3" jest-get-type "^29.4.3" - pretty-format "^29.5.0" + pretty-format "^29.6.1" jest-docblock@^29.4.3: version "29.4.3" @@ -5737,28 +5751,28 @@ jest-docblock@^29.4.3: dependencies: detect-newline "^3.0.0" -jest-each@^29.0.1, jest-each@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.5.0.tgz#fc6e7014f83eac68e22b7195598de8554c2e5c06" - integrity sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA== +jest-each@^29.0.1, jest-each@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.6.1.tgz#975058e5b8f55c6780beab8b6ab214921815c89c" + integrity sha512-n5eoj5eiTHpKQCAVcNTT7DRqeUmJ01hsAL0Q1SMiBHcBcvTKDELixQOGMCpqhbIuTcfC4kMfSnpmDqRgRJcLNQ== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.1" chalk "^4.0.0" jest-get-type "^29.4.3" - jest-util "^29.5.0" - pretty-format "^29.5.0" + jest-util "^29.6.1" + pretty-format "^29.6.1" -jest-environment-node@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.5.0.tgz#f17219d0f0cc0e68e0727c58b792c040e332c967" - integrity sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw== +jest-environment-node@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.6.1.tgz#08a122dece39e58bc388da815a2166c58b4abec6" + integrity sha512-ZNIfAiE+foBog24W+2caIldl4Irh8Lx1PUhg/GZ0odM1d/h2qORAsejiFc7zb+SEmYPn1yDZzEDSU5PmDkmVLQ== dependencies: - "@jest/environment" "^29.5.0" - "@jest/fake-timers" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/environment" "^29.6.1" + "@jest/fake-timers" "^29.6.1" + "@jest/types" "^29.6.1" "@types/node" "*" - jest-mock "^29.5.0" - jest-util "^29.5.0" + jest-mock "^29.6.1" + jest-util "^29.6.1" jest-get-type@^29.4.3: version "29.4.3" @@ -5784,32 +5798,32 @@ jest-haste-map@^28.1.3: optionalDependencies: fsevents "^2.3.2" -jest-haste-map@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.5.0.tgz#69bd67dc9012d6e2723f20a945099e972b2e94de" - integrity sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA== +jest-haste-map@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.6.1.tgz#62655c7a1c1b349a3206441330fb2dbdb4b63803" + integrity sha512-0m7f9PZXxOCk1gRACiVgX85knUKPKLPg4oRCjLoqIm9brTHXaorMA0JpmtmVkQiT8nmXyIVoZd/nnH1cfC33ig== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.1" "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.9" jest-regex-util "^29.4.3" - jest-util "^29.5.0" - jest-worker "^29.5.0" + jest-util "^29.6.1" + jest-worker "^29.6.1" micromatch "^4.0.4" walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-leak-detector@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz#cf4bdea9615c72bac4a3a7ba7e7930f9c0610c8c" - integrity sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow== +jest-leak-detector@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.6.1.tgz#66a902c81318e66e694df7d096a95466cb962f8e" + integrity sha512-OrxMNyZirpOEwkF3UHnIkAiZbtkBWiye+hhBweCHkVbCgyEy71Mwbb5zgeTNYWJBi1qgDVfPC1IwO9dVEeTLwQ== dependencies: jest-get-type "^29.4.3" - pretty-format "^29.5.0" + pretty-format "^29.6.1" jest-light-runner@^0.4.1: version "0.4.1" @@ -5824,39 +5838,39 @@ jest-light-runner@^0.4.1: piscina "^3.1.0" supports-color "^9.2.1" -jest-matcher-utils@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz#d957af7f8c0692c5453666705621ad4abc2c59c5" - integrity sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw== +jest-matcher-utils@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.6.1.tgz#6c60075d84655d6300c5d5128f46531848160b53" + integrity sha512-SLaztw9d2mfQQKHmJXKM0HCbl2PPVld/t9Xa6P9sgiExijviSp7TnZZpw2Fpt+OI3nwUO/slJbOfzfUMKKC5QA== dependencies: chalk "^4.0.0" - jest-diff "^29.5.0" + jest-diff "^29.6.1" jest-get-type "^29.4.3" - pretty-format "^29.5.0" + pretty-format "^29.6.1" -jest-message-util@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.5.0.tgz#1f776cac3aca332ab8dd2e3b41625435085c900e" - integrity sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA== +jest-message-util@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.6.1.tgz#d0b21d87f117e1b9e165e24f245befd2ff34ff8d" + integrity sha512-KoAW2zAmNSd3Gk88uJ56qXUWbFk787QKmjjJVOjtGFmmGSZgDBrlIL4AfQw1xyMYPNVD7dNInfIbur9B2rd/wQ== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.1" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^29.5.0" + pretty-format "^29.6.1" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^29.0.1, jest-mock@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.5.0.tgz#26e2172bcc71d8b0195081ff1f146ac7e1518aed" - integrity sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw== +jest-mock@^29.0.1, jest-mock@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.6.1.tgz#049ee26aea8cbf54c764af649070910607316517" + integrity sha512-brovyV9HBkjXAEdRooaTQK42n8usKoSRR3gihzUpYeV/vwqgSoNfrksO7UfSACnPmxasO/8TmHM3w9Hp3G1dgw== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.1" "@types/node" "*" - jest-util "^29.5.0" + jest-util "^29.6.1" jest-pnp-resolver@^1.2.2: version "1.2.3" @@ -5873,112 +5887,110 @@ jest-regex-util@^29.4.3: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.3.tgz#a42616141e0cae052cfa32c169945d00c0aa0bb8" integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg== -jest-resolve-dependencies@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz#f0ea29955996f49788bf70996052aa98e7befee4" - integrity sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg== +jest-resolve-dependencies@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.1.tgz#b85b06670f987a62515bbf625d54a499e3d708f5" + integrity sha512-BbFvxLXtcldaFOhNMXmHRWx1nXQO5LoXiKSGQcA1LxxirYceZT6ch8KTE1bK3X31TNG/JbkI7OkS/ABexVahiw== dependencies: jest-regex-util "^29.4.3" - jest-snapshot "^29.5.0" + jest-snapshot "^29.6.1" -jest-resolve@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.5.0.tgz#b053cc95ad1d5f6327f0ac8aae9f98795475ecdc" - integrity sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w== +jest-resolve@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.6.1.tgz#4c3324b993a85e300add2f8609f51b80ddea39ee" + integrity sha512-AeRkyS8g37UyJiP9w3mmI/VXU/q8l/IH52vj/cDAyScDcemRbSBhfX/NMYIGilQgSVwsjxrCHf3XJu4f+lxCMg== dependencies: chalk "^4.0.0" graceful-fs "^4.2.9" - jest-haste-map "^29.5.0" + jest-haste-map "^29.6.1" jest-pnp-resolver "^1.2.2" - jest-util "^29.5.0" - jest-validate "^29.5.0" + jest-util "^29.6.1" + jest-validate "^29.6.1" resolve "^1.20.0" resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.5.0.tgz#6a57c282eb0ef749778d444c1d758c6a7693b6f8" - integrity sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ== - dependencies: - "@jest/console" "^29.5.0" - "@jest/environment" "^29.5.0" - "@jest/test-result" "^29.5.0" - "@jest/transform" "^29.5.0" - "@jest/types" "^29.5.0" +jest-runner@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.6.1.tgz#54557087e7972d345540d622ab5bfc3d8f34688c" + integrity sha512-tw0wb2Q9yhjAQ2w8rHRDxteryyIck7gIzQE4Reu3JuOBpGp96xWgF0nY8MDdejzrLCZKDcp8JlZrBN/EtkQvPQ== + dependencies: + "@jest/console" "^29.6.1" + "@jest/environment" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" "@types/node" "*" chalk "^4.0.0" emittery "^0.13.1" graceful-fs "^4.2.9" jest-docblock "^29.4.3" - jest-environment-node "^29.5.0" - jest-haste-map "^29.5.0" - jest-leak-detector "^29.5.0" - jest-message-util "^29.5.0" - jest-resolve "^29.5.0" - jest-runtime "^29.5.0" - jest-util "^29.5.0" - jest-watcher "^29.5.0" - jest-worker "^29.5.0" + jest-environment-node "^29.6.1" + jest-haste-map "^29.6.1" + jest-leak-detector "^29.6.1" + jest-message-util "^29.6.1" + jest-resolve "^29.6.1" + jest-runtime "^29.6.1" + jest-util "^29.6.1" + jest-watcher "^29.6.1" + jest-worker "^29.6.1" p-limit "^3.1.0" source-map-support "0.5.13" -jest-runtime@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.5.0.tgz#c83f943ee0c1da7eb91fa181b0811ebd59b03420" - integrity sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw== - dependencies: - "@jest/environment" "^29.5.0" - "@jest/fake-timers" "^29.5.0" - "@jest/globals" "^29.5.0" - "@jest/source-map" "^29.4.3" - "@jest/test-result" "^29.5.0" - "@jest/transform" "^29.5.0" - "@jest/types" "^29.5.0" +jest-runtime@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.6.1.tgz#8a0fc9274ef277f3d70ba19d238e64334958a0dc" + integrity sha512-D6/AYOA+Lhs5e5il8+5pSLemjtJezUr+8zx+Sn8xlmOux3XOqx4d8l/2udBea8CRPqqrzhsKUsN/gBDE/IcaPQ== + dependencies: + "@jest/environment" "^29.6.1" + "@jest/fake-timers" "^29.6.1" + "@jest/globals" "^29.6.1" + "@jest/source-map" "^29.6.0" + "@jest/test-result" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" "@types/node" "*" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" glob "^7.1.3" graceful-fs "^4.2.9" - jest-haste-map "^29.5.0" - jest-message-util "^29.5.0" - jest-mock "^29.5.0" + jest-haste-map "^29.6.1" + jest-message-util "^29.6.1" + jest-mock "^29.6.1" jest-regex-util "^29.4.3" - jest-resolve "^29.5.0" - jest-snapshot "^29.5.0" - jest-util "^29.5.0" + jest-resolve "^29.6.1" + jest-snapshot "^29.6.1" + jest-util "^29.6.1" slash "^3.0.0" strip-bom "^4.0.0" -jest-snapshot@^29.0.1, jest-snapshot@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.5.0.tgz#c9c1ce0331e5b63cd444e2f95a55a73b84b1e8ce" - integrity sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g== +jest-snapshot@^29.0.1, jest-snapshot@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.6.1.tgz#0d083cb7de716d5d5cdbe80d598ed2fbafac0239" + integrity sha512-G4UQE1QQ6OaCgfY+A0uR1W2AY0tGXUPQpoUClhWHq1Xdnx1H6JOrC2nH5lqnOEqaDgbHFgIwZ7bNq24HpB180A== dependencies: "@babel/core" "^7.11.6" "@babel/generator" "^7.7.2" "@babel/plugin-syntax-jsx" "^7.7.2" "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" "@babel/types" "^7.3.3" - "@jest/expect-utils" "^29.5.0" - "@jest/transform" "^29.5.0" - "@jest/types" "^29.5.0" - "@types/babel__traverse" "^7.0.6" + "@jest/expect-utils" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" "@types/prettier" "^2.1.5" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^29.5.0" + expect "^29.6.1" graceful-fs "^4.2.9" - jest-diff "^29.5.0" + jest-diff "^29.6.1" jest-get-type "^29.4.3" - jest-matcher-utils "^29.5.0" - jest-message-util "^29.5.0" - jest-util "^29.5.0" + jest-matcher-utils "^29.6.1" + jest-message-util "^29.6.1" + jest-util "^29.6.1" natural-compare "^1.4.0" - pretty-format "^29.5.0" - semver "^7.3.5" + pretty-format "^29.6.1" + semver "^7.5.3" jest-util@^28.1.3: version "28.1.3" @@ -5992,42 +6004,42 @@ jest-util@^28.1.3: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-util@^29.0.0, jest-util@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" - integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== +jest-util@^29.0.0, jest-util@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.6.1.tgz#c9e29a87a6edbf1e39e6dee2b4689b8a146679cb" + integrity sha512-NRFCcjc+/uO3ijUVyNOQJluf8PtGCe/W6cix36+M3cTFgiYqFOOW5MgN4JOOcvbUhcKTYVd1CvHz/LWi8d16Mg== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.1" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.5.0.tgz#8e5a8f36178d40e47138dc00866a5f3bd9916ffc" - integrity sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ== +jest-validate@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.6.1.tgz#765e684af6e2c86dce950aebefbbcd4546d69f7b" + integrity sha512-r3Ds69/0KCN4vx4sYAbGL1EVpZ7MSS0vLmd3gV78O+NAx3PDQQukRU5hNHPXlyqCgFY8XUk7EuTMLugh0KzahA== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.1" camelcase "^6.2.0" chalk "^4.0.0" jest-get-type "^29.4.3" leven "^3.1.0" - pretty-format "^29.5.0" + pretty-format "^29.6.1" -jest-watcher@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.5.0.tgz#cf7f0f949828ba65ddbbb45c743a382a4d911363" - integrity sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA== +jest-watcher@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.6.1.tgz#7c0c43ddd52418af134c551c92c9ea31e5ec942e" + integrity sha512-d4wpjWTS7HEZPaaj8m36QiaP856JthRZkrgcIY/7ISoUWPIillrXM23WPboZVLbiwZBt4/qn2Jke84Sla6JhFA== dependencies: - "@jest/test-result" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/test-result" "^29.6.1" + "@jest/types" "^29.6.1" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" emittery "^0.13.1" - jest-util "^29.5.0" + jest-util "^29.6.1" string-length "^4.0.1" jest-worker@^28.1.3: @@ -6039,25 +6051,25 @@ jest-worker@^28.1.3: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.5.0.tgz#bdaefb06811bd3384d93f009755014d8acb4615d" - integrity sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA== +jest-worker@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.6.1.tgz#64b015f0e985ef3a8ad049b61fe92b3db74a5319" + integrity sha512-U+Wrbca7S8ZAxAe9L6nb6g8kPdia5hj32Puu5iOqBCMTMWFHXuK6dOV2IFrpedbTV8fjMFLdWNttQTBL6u2MRA== dependencies: "@types/node" "*" - jest-util "^29.5.0" + jest-util "^29.6.1" merge-stream "^2.0.0" supports-color "^8.0.0" jest@^29.3.1: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.5.0.tgz#f75157622f5ce7ad53028f2f8888ab53e1f1f24e" - integrity sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ== + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.6.1.tgz#74be1cb719c3abe439f2d94aeb18e6540a5b02ad" + integrity sha512-Nirw5B4nn69rVUZtemCQhwxOBhm0nsp3hmtF4rzCeWD7BkjAXRIji7xWQfnTNbz9g0aVsBX6aZK3n+23LM6uDw== dependencies: - "@jest/core" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/core" "^29.6.1" + "@jest/types" "^29.6.1" import-local "^3.0.2" - jest-cli "^29.5.0" + jest-cli "^29.6.1" jmespath@0.16.0: version "0.16.0" @@ -6181,12 +6193,12 @@ kleur@^4.1.4, kleur@^4.1.5: integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== knex@^2.3.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/knex/-/knex-2.4.2.tgz#a34a289d38406dc19a0447a78eeaf2d16ebedd61" - integrity sha512-tMI1M7a+xwHhPxjbl/H9K1kHX+VncEYcvCx5K00M16bWvpYPKAZd6QrCu68PtHAdIZNQPWZn0GVhqVBEthGWCg== + 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 "^9.1.0" + commander "^10.0.0" debug "4.3.4" escalade "^3.1.1" esm "^3.2.25" @@ -6194,7 +6206,7 @@ knex@^2.3.0: getopts "2.3.0" interpret "^2.2.0" lodash "^4.17.21" - pg-connection-string "2.5.0" + pg-connection-string "2.6.1" rechoir "^0.8.0" resolve-from "^5.0.0" tarn "^3.0.2" @@ -6258,9 +6270,9 @@ lines-and-columns@^1.1.6: integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== lint-staged@^13.1.0: - version "13.2.2" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-13.2.2.tgz#5e711d3139c234f73402177be2f8dd312e6508ca" - integrity sha512-71gSwXKy649VrSU09s10uAT0rWCcY3aewhMaHyl2N84oBk4Xs9HgxvUp3AYu+bNsK4NrOYYxvSgg7FyGJ+jGcA== + version "13.2.3" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-13.2.3.tgz#f899aad6c093473467e9c9e316e3c2d8a28f87a7" + integrity sha512-zVVEXLuQIhr1Y7R7YAWx4TZLdvuzk7DnmrsTNL0fax6Z3jrpFcas+vKbzxhhvp6TA55m1SQuWkpzI1qbfDZbAg== dependencies: chalk "5.2.0" cli-truncate "^3.1.0" @@ -6438,10 +6450,10 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lru-cache@^9.1.1: - version "9.1.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.1.2.tgz#255fdbc14b75589d6d0e73644ca167a8db506835" - integrity sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ== +"lru-cache@^9.1.1 || ^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.0.tgz#b9e2a6a72a129d81ab317202d93c7691df727e61" + integrity sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw== lru-queue@0.1, lru-queue@^0.1.0: version "0.1.0" @@ -6635,9 +6647,9 @@ minimatch@^5.0.1: brace-expansion "^2.0.1" minimatch@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.1.tgz#8a555f541cf976c622daf078bb28f29fb927c253" - integrity sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w== + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== dependencies: brace-expansion "^2.0.1" @@ -6660,10 +6672,10 @@ minimist@1.2.8, minimist@^1.2.0, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -"minipass@^5.0.0 || ^6.0.2": - version "6.0.2" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-6.0.2.tgz#542844b6c4ce95b202c0995b0a471f1229de4c81" - integrity sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w== +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": + version "7.0.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.2.tgz#58a82b7d81c7010da5bd4b2c0c85ac4b4ec5131e" + integrity sha512-eL79dXrE1q9dBbDCLg7xfn/vl7MS4F1gvJAgjJrQli/jbQWdUttuVawphqpffoIYfRdq78LHx6GP4bU/EQ2ATA== mkdirp@1.0.4, mkdirp@^1.0.4: version "1.0.4" @@ -6691,9 +6703,9 @@ moleculer-bull@^0.3.1: lodash "^4.17.21" moleculer-db@^0.8.21: - version "0.8.23" - resolved "https://registry.yarnpkg.com/moleculer-db/-/moleculer-db-0.8.23.tgz#f38aa4597917d40e2c7550150544e095c8af17c0" - integrity sha512-jjdRmdLBJGF8ZXDVcaS65dfXPffz4dKU1Hmw/z7J4kF/a2mYvg92/k8LNlX5j3uGYPMXFiSl3IDVXMERTmWWsA== + version "0.8.24" + resolved "https://registry.yarnpkg.com/moleculer-db/-/moleculer-db-0.8.24.tgz#9b6a39056c41f3a116d56767c6d4dc8c407067ad" + integrity sha512-ABJOTPTJFXta0w/nn5zY/o3cbkqUGFrfdPeY+VaurOt/wzVuCZ89wme+sgSRamnwhPbfmqUrDQG8MGy7BG0uTQ== dependencies: "@seald-io/nedb" "^3.0.0" bluebird "^3.7.2" @@ -6718,9 +6730,9 @@ moleculer-repl@^0.7.3: yargs-parser "^21.1.1" moleculer-web@^0.10.5: - version "0.10.5" - resolved "https://registry.yarnpkg.com/moleculer-web/-/moleculer-web-0.10.5.tgz#76be7bf8f7200aff45c6ca61bb96feba6f412b44" - integrity sha512-b4LFl67ESo8tmYtfuu91r8nRyLV158URAbf21dsNvf4hiVJW0u0BFwkkQF7Bn1Y+KRYCnAXNjGt4B+q5+cKnIw== + version "0.10.6" + resolved "https://registry.yarnpkg.com/moleculer-web/-/moleculer-web-0.10.6.tgz#a8bc99b7f153b882018aec9fa553496a86b864f5" + integrity sha512-MGNIH6mXLU2Wj63bAgoVzdhMKXALp99F5UHuiBgS2ywakdWEUl/q7GlMblvscioCCkXuUWezId85J0yioYxedg== dependencies: "@fastify/busboy" "^1.0.0" body-parser "^1.19.0" @@ -6731,19 +6743,19 @@ moleculer-web@^0.10.5: kleur "^4.1.4" lodash "^4.17.21" path-to-regexp "^3.1.0" - qs "^6.10.1" + qs "^6.11.0" serve-static "^1.14.1" moleculer@^0.14.16, moleculer@^0.14.27: - version "0.14.29" - resolved "https://registry.yarnpkg.com/moleculer/-/moleculer-0.14.29.tgz#5ac1fe45c6f2492ddb4601002a46ed7b14e6aa87" - integrity sha512-19SbGgZGL6tHpgQAiEg8ZTkPR3IagJ/ruZr3038fZchTUHZ0wMZSrh62Or3iUJBMEOFqZ5S5nHpKtV40jpoeew== + version "0.14.30" + resolved "https://registry.yarnpkg.com/moleculer/-/moleculer-0.14.30.tgz#ab735b80b05f5d5b2450bc0c5bf10b891cc719f8" + integrity sha512-XxM2oYPofd5FhHTnu8+gzEcEMy92rHw2ey6f4bd+aMCWNFQpueg2eKURZPn5rSpif7mS2AwKMPGGgEqoRnT7+A== dependencies: args "^5.0.3" eventemitter2 "^6.4.9" - fastest-validator "^1.16.0" + fastest-validator "^1.17.0" glob "^7.2.0" - ipaddr.js "^2.0.1" + ipaddr.js "^2.1.0" kleur "^4.1.5" lodash "^4.17.21" lru-cache "^6.0.0" @@ -6854,9 +6866,9 @@ node-addon-api@^3.0.0: integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== node-fetch@^2.6.7: - version "2.6.11" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.11.tgz#cde7fc71deef3131ef80a738919f999e6edfff25" - integrity sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w== + version "2.6.12" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.12.tgz#02eb8e22074018e3d5a83016649d04df0e348fba" + integrity sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g== dependencies: whatwg-url "^5.0.0" @@ -6876,9 +6888,9 @@ node-int64@^0.4.0: integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== node-releases@^2.0.12: - version "2.0.12" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.12.tgz#35627cc224a23bfb06fb3380f2b3afaaa7eb1039" - integrity sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ== + version "2.0.13" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" + integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== normalize-package-data@^2.5.0: version "2.5.0" @@ -6968,9 +6980,9 @@ object.values@^1.1.6: es-abstract "^1.20.4" objection@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/objection/-/objection-3.0.1.tgz#f67dc698187d10524e5d1b5d37a54e5bba49a42a" - integrity sha512-rqNnyQE+C55UHjdpTOJEKQHJGZ/BGtBBtgxdUpKG4DQXRUmqxfmgS/MhPWxB9Pw0mLSVLEltr6soD4c0Sddy0Q== + version "3.0.4" + resolved "https://registry.yarnpkg.com/objection/-/objection-3.0.4.tgz#d2f60cec49f7f917c4b97260add9e2c2cdb52c0b" + integrity sha512-0XaStHtOBcux4nlffUj8gOpxUsAPE+sLDZPml6n79WQhInYLBvjctU/uv2kEUPPybAJK8YL6ETkLtft9UOj7nQ== dependencies: ajv "^8.6.2" db-errors "^0.2.3" @@ -7017,17 +7029,17 @@ onetime@^6.0.0: dependencies: mimic-fn "^4.0.0" -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" deep-is "^0.1.3" fast-levenshtein "^2.0.6" levn "^0.4.1" prelude-ls "^1.2.1" type-check "^0.4.0" - word-wrap "^1.2.3" ora@^5.4.1: version "5.4.1" @@ -7161,13 +7173,13 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-scurry@^1.7.0: - version "1.9.2" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.9.2.tgz#90f9d296ac5e37e608028e28a447b11d385b3f63" - integrity sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg== +path-scurry@^1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698" + integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== dependencies: - lru-cache "^9.1.1" - minipass "^5.0.0 || ^6.0.2" + lru-cache "^9.1.1 || ^10.0.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" path-to-regexp@0.1.7: version "0.1.7" @@ -7194,20 +7206,15 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== -pg-cloudflare@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pg-cloudflare/-/pg-cloudflare-1.1.0.tgz#833d70870d610d14bf9df7afb40e1cba310c17a0" - integrity sha512-tGM8/s6frwuAIyRcJ6nWcIvd3+3NmUKIs6OjviIm1HPPFEt5MzQDOTBQyhPWg/m0kCl95M6gA1JaIXtS8KovOA== - -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" - integrity sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ== +pg-cloudflare@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz#e6d5833015b170e23ae819e8c5d7eaedb472ca98" + integrity sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q== -pg-connection-string@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.6.0.tgz#12a36cc4627df19c25cc1b9b736cc39ee1f73ae8" - integrity sha512-x14ibktcwlHKoHxx9X3uTVW9zIGR41ZB6QNhHb21OPNdCCO3NaRnpJuwKIQSR4u+Yqjx4HCvy7Hh7VSy1U4dGg== +pg-connection-string@2.6.1, 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-hstore@^2.3.4: version "2.3.4" @@ -7221,10 +7228,10 @@ pg-int8@1.0.1: resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== -pg-pool@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.6.0.tgz#3190df3e4747a0d23e5e9e8045bcd99bda0a712e" - integrity sha512-clFRf2ksqd+F497kWFyM21tMjeikn60oGDmqMT8UBrynEwVEX/5R5xd2sdvdo1cZCFlguORNpVuqxIj+aK4cfQ== +pg-pool@^3.6.1: + version "3.6.1" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.6.1.tgz#5a902eda79a8d7e3c928b77abf776b3cb7d351f7" + integrity sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og== pg-protocol@^1.6.0: version "1.6.0" @@ -7243,19 +7250,19 @@ pg-types@^2.1.0: postgres-interval "^1.1.0" pg@^8.8.0: - version "8.11.0" - resolved "https://registry.yarnpkg.com/pg/-/pg-8.11.0.tgz#a37e534e94b57a7ed811e926f23a7c56385f55d9" - integrity sha512-meLUVPn2TWgJyLmy7el3fQQVwft4gU5NGyvV0XbD41iU9Jbg8lCH4zexhIkihDzVHJStlt6r088G6/fWeNjhXA== + version "8.11.1" + resolved "https://registry.yarnpkg.com/pg/-/pg-8.11.1.tgz#297e0eb240306b1e9e4f55af8a3bae76ae4810b1" + integrity sha512-utdq2obft07MxaDg0zBJI+l/M3mBRfIpEN3iSemsz0G5F2/VXx+XzqF4oxrbIZXQxt2AZzIUzyVg/YM6xOP/WQ== dependencies: buffer-writer "2.0.0" packet-reader "1.0.0" - pg-connection-string "^2.6.0" - pg-pool "^3.6.0" + pg-connection-string "^2.6.1" + pg-pool "^3.6.1" pg-protocol "^1.6.0" pg-types "^2.1.0" pgpass "1.x" optionalDependencies: - pg-cloudflare "^1.1.0" + pg-cloudflare "^1.1.1" pgpass@1.x: version "1.0.5" @@ -7280,9 +7287,9 @@ pidtree@^0.6.0: integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== pirates@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" - integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== piscina@^3.1.0: version "3.2.0" @@ -7346,12 +7353,12 @@ pretty-bytes@^5.6.0: resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== -pretty-format@^29.0.0, pretty-format@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.5.0.tgz#283134e74f70e2e3e7229336de0e4fce94ccde5a" - integrity sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw== +pretty-format@^29.0.0, pretty-format@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.6.1.tgz#ec838c288850b7c4f9090b867c2d4f4edbfb0f3e" + integrity sha512-7jRj+yXO0W7e4/tSJKoR7HRIHLPPjtNaUGG2xxKQnGvPNRkgWcQ0AZX6P4KBRJN4FcTBWb3sa7DVUJmocYuoog== dependencies: - "@jest/schemas" "^29.4.3" + "@jest/schemas" "^29.6.0" ansi-styles "^5.0.0" react-is "^18.0.0" @@ -7388,9 +7395,9 @@ protobufjs@^6.11.2, protobufjs@^6.8.8, protobufjs@~6.11.2, protobufjs@~6.11.3: long "^4.0.0" protobufjs@^7.1.2: - version "7.2.3" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.3.tgz#01af019e40d9c6133c49acbb3ff9e30f4f0f70b2" - integrity sha512-TtpvOqwB5Gdz/PQmOjgsrGH1nHjAQVCN7JG4A6r1sXRWESL5rNMAiRcBQlCAdKxZcAbstExQePYG8xof/JVRgg== + version "7.2.4" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.4.tgz#3fc1ec0cdc89dd91aef9ba6037ba07408485c3ae" + integrity sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" @@ -7455,7 +7462,7 @@ qs@6.9.7: resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe" integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw== -qs@^6.10.1: +qs@^6.11.0: version "6.11.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== @@ -7647,7 +7654,7 @@ regenerator-transform@^0.15.1: dependencies: "@babel/runtime" "^7.8.4" -regexp.prototype.flags@^1.4.3: +regexp.prototype.flags@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== @@ -7838,6 +7845,16 @@ rxjs@^7.8.0: dependencies: tslib "^2.1.0" +safe-array-concat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.0.tgz#2064223cba3c08d2ee05148eedbc563cd6d84060" + integrity sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + has-symbols "^1.0.3" + isarray "^2.0.5" + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -7873,29 +7890,29 @@ sax@>=0.6.0: integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== "semver@2 || 3 || 4 || 5": - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@7.5.0: - version "7.5.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" - integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== +semver@7.5.2: + version "7.5.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.2.tgz#5b851e66d1be07c1cdaf37dfc856f543325a2beb" + integrity sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ== dependencies: lru-cache "^6.0.0" -semver@7.x, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: - version "7.5.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.1.tgz#c90c4d631cf74720e46b21c1d37ea07edfab91ec" - integrity sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw== +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.2, semver@^7.3.4, semver@^7.3.7, semver@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - send@0.17.2: version "0.17.2" resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" @@ -8287,7 +8304,7 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -8327,9 +8344,9 @@ supports-color@^8.0.0: has-flag "^4.0.0" supports-color@^9.2.1: - version "9.3.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.3.1.tgz#34e4ad3c71c9a39dae3254ecc46c9b74e89e15a6" - integrity sha512-knBY82pjmnIzK3NifMo3RxEIRD9E0kIzV4BKcyTZ9+9kWgLMxd4PrsTSMoFQUabgRBbF8KOLRDCyKgNV+iK44Q== + version "9.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.4.0.tgz#17bfcf686288f531db3dea3215510621ccb55954" + integrity sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw== supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" @@ -8486,9 +8503,9 @@ trim-newlines@^3.0.0: integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== ts-jest@^29.0.3: - version "29.1.0" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.0.tgz#4a9db4104a49b76d2b368ea775b6c9535c603891" - integrity sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA== + version "29.1.1" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.1.tgz#f58fe62c63caf7bfcc5cc6472082f79180f0815b" + integrity sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA== dependencies: bs-logger "0.x" fast-json-stable-stringify "2.x" @@ -8496,7 +8513,7 @@ ts-jest@^29.0.3: json5 "^2.2.3" lodash.memoize "4.x" make-error "1.x" - semver "7.x" + semver "^7.5.3" yargs-parser "^21.0.1" ts-node@^10.8.1, ts-node@^10.9.1: @@ -8543,9 +8560,9 @@ tslib@^1.8.1, tslib@^1.9.0: integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.0.0, tslib@^2.1.0: - version "2.5.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" - integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== + version "2.6.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3" + integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA== tsutils@^3.21.0: version "3.21.0" @@ -8626,6 +8643,36 @@ type@^2.7.2: resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== +typed-array-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" + integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-typed-array "^1.1.10" + +typed-array-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" + integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + typed-array-length@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" @@ -8636,9 +8683,9 @@ typed-array-length@^1.0.4: is-typed-array "^1.1.9" "typescript@^4.6.4 || ^5.0.0": - version "5.1.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.3.tgz#8d84219244a6b40b6fb2b33cc1c062f715b9e826" - integrity sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw== + version "5.1.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" + integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== typescript@^4.8.4: version "4.9.5" @@ -8850,17 +8897,16 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" -which-typed-array@^1.1.2, which-typed-array@^1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" - integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== +which-typed-array@^1.1.10, which-typed-array@^1.1.11, which-typed-array@^1.1.2: + version "1.1.11" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.11.tgz#99d691f23c72aab6768680805a271b69761ed61a" + integrity sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew== dependencies: available-typed-arrays "^1.0.5" call-bind "^1.0.2" for-each "^0.3.3" gopd "^1.0.1" has-tostringtag "^1.0.0" - is-typed-array "^1.1.10" which@^2.0.1: version "2.0.2" @@ -8869,11 +8915,6 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -word-wrap@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" From 763689e53084cdde5372c5de455334af280beaa1 Mon Sep 17 00:00:00 2001 From: AnDQK Date: Mon, 24 Jul 2023 10:17:50 +0700 Subject: [PATCH 09/20] feat: update cron jobs logic, move api actions to its service folder --- src/common/utils/request.ts | 6 +- src/models/event_attribute.ts | 9 ++ .../api-gateways/api_gateway.service.ts | 6 +- .../dashboard_statistics.service.ts | 36 ----- .../api-gateways/statistics.service.ts | 28 +++- .../statistics/account_statistics.service.ts | 128 ++++++++++-------- .../api_statistics.service.ts} | 4 +- .../statistics/daily_statistics.service.ts | 74 +++++----- .../dashboard_statistics.service.ts | 28 ++-- 9 files changed, 169 insertions(+), 150 deletions(-) delete mode 100644 src/services/api-gateways/dashboard_statistics.service.ts rename src/services/{api-gateways/cross_chains.service.ts => statistics/api_statistics.service.ts} (89%) diff --git a/src/common/utils/request.ts b/src/common/utils/request.ts index cf94862a7..a688dd2a5 100644 --- a/src/common/utils/request.ts +++ b/src/common/utils/request.ts @@ -10,14 +10,16 @@ export interface ITxIdsParam { } export interface IDailyStatsParam { + startId: number; + endId: number; date: string; offset: number; - txIds: number[]; addresses: string[]; } export interface IAccountStatsParam { - id: number; + startId: number; + endId: number; date: string; accountStats: any; } diff --git a/src/models/event_attribute.ts b/src/models/event_attribute.ts index ab6aa6ad3..2fb47b872 100644 --- a/src/models/event_attribute.ts +++ b/src/models/event_attribute.ts @@ -2,6 +2,7 @@ import { Model } from 'objection'; import BaseModel from './base'; // eslint-disable-next-line import/no-cycle import { Event } from './event'; +import { Transaction } from './transaction'; export class EventAttribute extends BaseModel { event_id!: string; @@ -52,6 +53,14 @@ export class EventAttribute extends BaseModel { to: 'event.id', }, }, + transaction: { + relation: Model.BelongsToOneRelation, + modelClass: Transaction, + join: { + from: 'event_attribute.tx_id', + to: 'transaction.id', + }, + }, }; } diff --git a/src/services/api-gateways/api_gateway.service.ts b/src/services/api-gateways/api_gateway.service.ts index 0125353f6..2509760df 100644 --- a/src/services/api-gateways/api_gateway.service.ts +++ b/src/services/api-gateways/api_gateway.service.ts @@ -21,11 +21,7 @@ import { bullBoardMixin } from '../../mixins/bullBoard/bullBoard.mixin'; path: '/api', autoAliases: true, // allow generate rest info (GET/PUT/POST...) in the services mappingPolicy: 'restrict', // allow action called with exact method - whitelist: [ - 'v1.dashboard-statistics.*', - 'v1.graphiql.*', - 'v1.statistics.*', - ], + whitelist: ['v1.graphiql.*', 'v1.statistics.*'], }, ], // empty cors object will have moleculer to generate handler for preflight request and CORS header which allow all origin diff --git a/src/services/api-gateways/dashboard_statistics.service.ts b/src/services/api-gateways/dashboard_statistics.service.ts deleted file mode 100644 index 182612c2e..000000000 --- a/src/services/api-gateways/dashboard_statistics.service.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Get, Service } from '@ourparentcenter/moleculer-decorators-extended'; -import { Context, ServiceBroker } from 'moleculer'; -import BaseService from '../../base/base.service'; -import networks from '../../../network.json' assert { type: 'json' }; - -@Service({ - name: 'dashboard-statistics', - version: 1, -}) -export default class DashboardStatisticsService extends BaseService { - public constructor(public broker: ServiceBroker) { - super(broker); - } - - @Get('/', { - name: 'getDashboardStatisticsByChainId', - params: { - chainid: { - type: 'string', - optional: false, - enum: networks.map((network) => network.chainId), - }, - }, - }) - async getDashboardStatisticsByChainId( - ctx: Context<{ chainid: string }, Record> - ) { - const selectedChain = networks.find( - (network) => network.chainId === ctx.params.chainid - ); - - return this.broker.call( - `v1.cross-chains.getDashboardStatistics@${selectedChain?.moleculerNamespace}` - ); - } -} diff --git a/src/services/api-gateways/statistics.service.ts b/src/services/api-gateways/statistics.service.ts index 1af91a3ee..dbb527ad3 100644 --- a/src/services/api-gateways/statistics.service.ts +++ b/src/services/api-gateways/statistics.service.ts @@ -12,6 +12,28 @@ export default class StatisticsService extends BaseService { super(broker); } + @Get('/dashboard', { + name: 'getDashboardStatisticsByChainId', + params: { + chainid: { + type: 'string', + optional: false, + enum: networks.map((network) => network.chainId), + }, + }, + }) + async getDashboardStatisticsByChainId( + ctx: Context<{ chainid: string }, Record> + ) { + const selectedChain = networks.find( + (network) => network.chainId === ctx.params.chainid + ); + + return this.broker.call( + `v1.api-statistics.getDashboardStatistics@${selectedChain?.moleculerNamespace}` + ); + } + @Get('/top-accounts', { name: 'getTopAccountsByChainId', params: { @@ -21,10 +43,6 @@ export default class StatisticsService extends BaseService { enum: networks.map((network) => network.chainId), }, }, - cache: { - keys: ['chainid'], - ttl: 3600, - }, }) async getTopAccountsByChainId( ctx: Context<{ chainid: string }, Record> @@ -34,7 +52,7 @@ export default class StatisticsService extends BaseService { ); return this.broker.call( - `v1.cross-chains.getTopAccounts@${selectedChain?.moleculerNamespace}` + `v1.api-statistics.getTopAccounts@${selectedChain?.moleculerNamespace}` ); } } diff --git a/src/services/statistics/account_statistics.service.ts b/src/services/statistics/account_statistics.service.ts index cfe3a0e73..1ba80fcfd 100644 --- a/src/services/statistics/account_statistics.service.ts +++ b/src/services/statistics/account_statistics.service.ts @@ -10,12 +10,7 @@ import BigNumber from 'bignumber.js'; import dayjs from 'dayjs'; import utc from 'dayjs/plugin/utc'; import _ from 'lodash'; -import { - AccountStatistics, - Event, - EventAttribute, - Transaction, -} from '../../models'; +import { AccountStatistics, EventAttribute, Transaction } from '../../models'; import { BULL_JOB_NAME, IAccountStatsParam, @@ -49,7 +44,8 @@ export default class AccountStatisticsService extends BullableService { BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, { - id: null, + startId: null, + endId: null, date: ctx.params.date, accountStats: {}, }, @@ -78,51 +74,73 @@ export default class AccountStatisticsService extends BullableService { .toDate(); const endTime = dayjs.utc(date).startOf('day').toDate(); - const startTxId = !_payload.id - ? ( - await Transaction.query() - .select('id') - .where('transaction.timestamp', '>=', startTime) - .limit(1) - .orderBy('id') - )[0].id - : _payload.id; - const nextId = startTxId + 50; + let startTxId = _payload.startId; + // The end tx's ID needs to plus 1 since the query will select record with _lt not _lte + let nextId = Math.min(_payload.endId + 1, startTxId + 50); + let endTxId = _payload.endId; + // If job runs for the 1st time, then needs to query the start and end tx id of the day as the range + if (!_payload.startId && !_payload.endId) { + const startTx = await Transaction.query() + .select('id') + .where('transaction.timestamp', '>=', startTime) + .limit(1) + .orderBy('id'); + const endTx = await Transaction.query() + .select('id') + .where('transaction.timestamp', '<', endTime) + .limit(1) + .orderBy('id', 'desc'); + + startTxId = startTx[0].id; + // The end tx's ID needs to plus 1 since the query will select record with _lt not _lte + nextId = Math.min(endTx[0].id + 1, startTxId + 50); + endTxId = endTx[0].id; + } this.logger.info( `Get account statistic events from id ${startTxId} for day ${new Date( startTime )}` ); - const dailyEvents = await Transaction.query() - .joinRelated('events.[attributes]') - .select( - 'transaction.gas_used', - 'events:attributes.event_id', - 'events:attributes.composite_key', - 'events:attributes.value' - ) - .where('transaction.timestamp', '>=', startTime) - .andWhere('transaction.timestamp', '<', endTime) - .andWhere('transaction.id', '>=', startTxId) - .andWhere('transaction.id', '<', nextId) - .andWhere((builder) => - builder - // Get the address that actually spent or received token - .whereIn('events.type', [ - Event.EVENT_TYPE.COIN_SPENT, - Event.EVENT_TYPE.COIN_RECEIVED, - Event.EVENT_TYPE.USE_FEEGRANT, - ]) - // If fee_grant is involved, then needs these to track to the granters - .orWhereIn('events:attributes.key', [ - EventAttribute.ATTRIBUTE_KEY.FEE, - EventAttribute.ATTRIBUTE_KEY.FEE_PAYER, - ]) - ); + const [dailyEvents, feeEvents]: [EventAttribute[], any[]] = + await Promise.all([ + EventAttribute.query() + .select('event_id', 'composite_key', 'value') + .where('tx_id', '>=', startTxId) + .andWhere('tx_id', '<', nextId) + .andWhere((builder) => + builder + // Get the address that actually spent or received token + .whereIn('composite_key', [ + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_SPENDER, + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_AMOUNT, + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_AMOUNT, + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_RECEIVER, + ]) + ), + EventAttribute.query() + .joinRelated('transaction') + .select( + 'event_attribute.tx_id', + 'event_attribute.composite_key', + 'event_attribute.value', + 'transaction.gas_used' + ) + .where('tx_id', '>=', startTxId) + .andWhere('tx_id', '<', nextId) + .andWhere((builder) => + builder + // If fee_grant is involved, then needs to track to the granters and grantees + .whereIn('composite_key', [ + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.TX_FEE_PAYER, + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.USE_FEEGRANT_GRANTEE, + ]) + ), + ]); - if (dailyEvents.length > 0) { + if (dailyEvents.length > 0 || feeEvents.length > 0) { dailyEvents + .concat(feeEvents) .filter( (event) => Utils.isValidAccountAddress( @@ -150,11 +168,10 @@ export default class AccountStatisticsService extends BullableService { dailyEvents .filter( (event) => - !Utils.isValidAccountAddress( - event.value, - config.networkPrefixAddress, - 20 - ) + event.composite_key === + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_AMOUNT || + event.composite_key === + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_AMOUNT ) .forEach((event) => { switch (event.composite_key) { @@ -164,7 +181,7 @@ export default class AccountStatisticsService extends BullableService { dEvent.event_id === event.event_id && dEvent.composite_key === EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_SPENDER - )?.value; + )!.value; accountStats[addrSpent].amount_sent = ( BigInt(accountStats[addrSpent].amount_sent) + @@ -178,7 +195,7 @@ export default class AccountStatisticsService extends BullableService { dEvent.composite_key === EventAttribute.ATTRIBUTE_COMPOSITE_KEY .COIN_RECEIVED_RECEIVER - )?.value; + )!.value; accountStats[addrReceived].amount_received = ( BigInt(accountStats[addrReceived].amount_received) + @@ -190,7 +207,7 @@ export default class AccountStatisticsService extends BullableService { } }); - dailyEvents + feeEvents .filter( (event) => event.composite_key === @@ -199,9 +216,9 @@ export default class AccountStatisticsService extends BullableService { .forEach((event) => { let addr = event.value; - const feeGrant = dailyEvents.find( + const feeGrant = feeEvents.find( (dEvent) => - dEvent.event_id === event.event_id && + dEvent.tx_id === event.tx_id && dEvent.composite_key === EventAttribute.ATTRIBUTE_COMPOSITE_KEY.USE_FEEGRANT_GRANTEE ); @@ -220,7 +237,8 @@ export default class AccountStatisticsService extends BullableService { BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, { - id: nextId, + startId: nextId, + endId: endTxId, date: _payload.date, accountStats, }, diff --git a/src/services/api-gateways/cross_chains.service.ts b/src/services/statistics/api_statistics.service.ts similarity index 89% rename from src/services/api-gateways/cross_chains.service.ts rename to src/services/statistics/api_statistics.service.ts index 9c9d673af..0b3bfe3d9 100644 --- a/src/services/api-gateways/cross_chains.service.ts +++ b/src/services/statistics/api_statistics.service.ts @@ -7,10 +7,10 @@ import { REDIS_KEY } from '../../common'; import BaseService from '../../base/base.service'; @Service({ - name: 'cross-chains', + name: 'api-statistics', version: 1, }) -export default class CrossChainsService extends BaseService { +export default class ApiStatisticsService extends BaseService { public constructor(public broker: ServiceBroker) { super(broker); } diff --git a/src/services/statistics/daily_statistics.service.ts b/src/services/statistics/daily_statistics.service.ts index 971d3f4c9..8d7046ab6 100644 --- a/src/services/statistics/daily_statistics.service.ts +++ b/src/services/statistics/daily_statistics.service.ts @@ -5,7 +5,12 @@ import { import { Context, ServiceBroker } from 'moleculer'; import dayjs from 'dayjs'; import utc from 'dayjs/plugin/utc'; -import { Account, DailyStatistics, Transaction } from '../../models'; +import { + Account, + DailyStatistics, + EventAttribute, + Transaction, +} from '../../models'; import { BULL_JOB_NAME, ICreateSpecificDateJob, @@ -38,9 +43,10 @@ export default class DailyStatisticsService extends BullableService { BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, { + startId: null, + endId: null, date: ctx.params.date, offset: 0, - txIds: [], addresses: [], }, { @@ -71,25 +77,38 @@ export default class DailyStatisticsService extends BullableService { } for day ${new Date(startTime)}` ); - const dailyEvents = await Transaction.query() - .joinRelated('events.[attributes]') - .select('transaction.id', 'transaction.code', 'events:attributes.value') - .where('transaction.timestamp', '>=', startTime) - .andWhere('transaction.timestamp', '<', endTime) - .andWhere( - 'events:attributes.value', - 'like', - `${config.networkPrefixAddress}%` - ) - .orderBy('transaction.id') + let startTxId = _payload.startId; + let endTxId = _payload.endId; + // If job runs for the 1st time, then needs to query the start and end tx id of the day as the range + if (!_payload.startId && !_payload.endId) { + const startTx = await Transaction.query() + .select('id') + .where('transaction.timestamp', '>=', startTime) + .limit(1) + .orderBy('id'); + const endTx = await Transaction.query() + .select('id') + .where('transaction.timestamp', '<', endTime) + .limit(1) + .orderBy('id', 'desc'); + + startTxId = startTx[0].id; + endTxId = endTx[0].id; + } + + const dailyAddresses = await EventAttribute.query() + .distinct('value') + .where('tx_id', '>=', startTxId) + .andWhere('tx_id', '<=', endTxId) + .andWhere('value', 'like', `${config.networkPrefixAddress}%`) .limit(config.dailyStatistics.recordsPerCall) .offset(_payload.offset * config.dailyStatistics.recordsPerCall); - if (dailyEvents.length > 0) { + if (dailyAddresses.length > 0) { const activeAddrs = Array.from( new Set( _payload.addresses.concat( - dailyEvents + dailyAddresses .filter((event) => Utils.isValidAccountAddress( event.value, @@ -101,28 +120,16 @@ export default class DailyStatisticsService extends BullableService { ) ) ); - const dailyTxs = Array.from( - new Set( - _payload.txIds.concat( - Array.from( - new Set( - dailyEvents - .filter((event) => event.code === 0) - .map((event) => event.id) - ) - ) - ) - ) - ); const offset = _payload.offset + 1; await this.createJob( BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, { + startId: startTxId, + endId: endTxId, date: _payload.date, offset, - txIds: dailyTxs, addresses: activeAddrs, }, { @@ -133,13 +140,18 @@ export default class DailyStatisticsService extends BullableService { } ); } else { - const [uniqueAddrs, prevDailyStat] = await Promise.all([ + const [dailyTxs, uniqueAddrs, prevDailyStat] = await Promise.all([ + Transaction.query() + .count('id') + .where('timestamp', '>=', startTime) + .andWhere('timestamp', '<', endTime) + .andWhere('code', 0), Account.query().count('id'), DailyStatistics.query().findOne('date', startTime), ]); const dailyStat = DailyStatistics.fromJson({ - daily_txs: _payload.txIds.length, + daily_txs: dailyTxs[0].count, daily_active_addresses: _payload.addresses.length, unique_addresses: Number(uniqueAddrs[0].count), unique_addresses_increase: prevDailyStat diff --git a/src/services/statistics/dashboard_statistics.service.ts b/src/services/statistics/dashboard_statistics.service.ts index f9a631da6..c6f93e9d4 100644 --- a/src/services/statistics/dashboard_statistics.service.ts +++ b/src/services/statistics/dashboard_statistics.service.ts @@ -99,20 +99,20 @@ export default class DashboardStatisticsService extends BullableService { } public async _start() { - // this.createJob( - // BULL_JOB_NAME.HANDLE_DASHBOARD_STATISTICS, - // BULL_JOB_NAME.HANDLE_DASHBOARD_STATISTICS, - // {}, - // { - // removeOnComplete: true, - // removeOnFail: { - // count: 3, - // }, - // repeat: { - // every: config.dashboardStatistics.millisecondCrawl, - // }, - // } - // ); + this.createJob( + BULL_JOB_NAME.HANDLE_DASHBOARD_STATISTICS, + BULL_JOB_NAME.HANDLE_DASHBOARD_STATISTICS, + {}, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + repeat: { + every: config.dashboardStatistics.millisecondCrawl, + }, + } + ); return super._start(); } From 194b3729f77638bffa3849ebe835d5ac33bb6d0f Mon Sep 17 00:00:00 2001 From: AnDQK Date: Wed, 26 Jul 2023 14:15:35 +0700 Subject: [PATCH 10/20] fix: query event from db and group it based on event_id and tx_id --- .../statistics/account_statistics.service.ts | 477 +++++++++--------- .../statistics/daily_statistics.service.ts | 185 ++++--- .../dashboard_statistics.service.ts | 114 ++--- 3 files changed, 385 insertions(+), 391 deletions(-) diff --git a/src/services/statistics/account_statistics.service.ts b/src/services/statistics/account_statistics.service.ts index 1ba80fcfd..2884076b2 100644 --- a/src/services/statistics/account_statistics.service.ts +++ b/src/services/statistics/account_statistics.service.ts @@ -20,13 +20,15 @@ import { } from '../../common'; import config from '../../../config.json' assert { type: 'json' }; import BullableService, { QueueHandler } from '../../base/bullable.service'; -import Utils from '../../common/utils/utils'; +import knex from '../../common/utils/db_connection'; @Service({ name: SERVICE.V1.AccountStatisticsService.key, version: 1, }) export default class AccountStatisticsService extends BullableService { + private accountStats: any; + public constructor(public broker: ServiceBroker) { super(broker); } @@ -64,226 +66,87 @@ export default class AccountStatisticsService extends BullableService { // prefix: `horoscope-v2-${config.chainId}`, }) public async handleJob(_payload: IAccountStatsParam): Promise { - try { - const { accountStats, date } = _payload; - - const startTime = dayjs - .utc(date) - .subtract(1, 'day') - .startOf('day') - .toDate(); - const endTime = dayjs.utc(date).startOf('day').toDate(); - - let startTxId = _payload.startId; + const { date } = _payload; + this.accountStats = _payload.accountStats; + + const endTime = dayjs.utc(date).startOf('day').toDate(); + const startTime = dayjs.utc(endTime).subtract(1, 'day').toDate(); + + let startTxId = _payload.startId; + // The end tx's ID needs to plus 1 since the query will select record with _lt not _lte + let nextId = Math.min(_payload.endId + 1, startTxId + 50); + let endTxId = _payload.endId; + // If job runs for the 1st time, then needs to query the start and end tx id of the day as the range + if (!_payload.startId && !_payload.endId) { + const startTx = await Transaction.query() + .select('id') + .where('transaction.timestamp', '>=', startTime) + .limit(1) + .orderBy('id'); + const endTx = await Transaction.query() + .select('id') + .where('transaction.timestamp', '<', endTime) + .limit(1) + .orderBy('id', 'desc'); + + startTxId = startTx[0].id; // The end tx's ID needs to plus 1 since the query will select record with _lt not _lte - let nextId = Math.min(_payload.endId + 1, startTxId + 50); - let endTxId = _payload.endId; - // If job runs for the 1st time, then needs to query the start and end tx id of the day as the range - if (!_payload.startId && !_payload.endId) { - const startTx = await Transaction.query() - .select('id') - .where('transaction.timestamp', '>=', startTime) - .limit(1) - .orderBy('id'); - const endTx = await Transaction.query() - .select('id') - .where('transaction.timestamp', '<', endTime) - .limit(1) - .orderBy('id', 'desc'); - - startTxId = startTx[0].id; - // The end tx's ID needs to plus 1 since the query will select record with _lt not _lte - nextId = Math.min(endTx[0].id + 1, startTxId + 50); - endTxId = endTx[0].id; - } - this.logger.info( - `Get account statistic events from id ${startTxId} for day ${new Date( - startTime - )}` - ); + nextId = Math.min(endTx[0].id + 1, startTxId + 50); + endTxId = endTx[0].id; + } + this.logger.info( + `Get account statistic events from id ${startTxId} for day ${new Date( + startTime + )}` + ); - const [dailyEvents, feeEvents]: [EventAttribute[], any[]] = - await Promise.all([ - EventAttribute.query() - .select('event_id', 'composite_key', 'value') - .where('tx_id', '>=', startTxId) - .andWhere('tx_id', '<', nextId) - .andWhere((builder) => - builder - // Get the address that actually spent or received token - .whereIn('composite_key', [ - EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_SPENDER, - EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_AMOUNT, - EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_AMOUNT, - EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_RECEIVER, - ]) - ), - EventAttribute.query() - .joinRelated('transaction') - .select( - 'event_attribute.tx_id', - 'event_attribute.composite_key', - 'event_attribute.value', - 'transaction.gas_used' - ) - .where('tx_id', '>=', startTxId) - .andWhere('tx_id', '<', nextId) - .andWhere((builder) => - builder - // If fee_grant is involved, then needs to track to the granters and grantees - .whereIn('composite_key', [ - EventAttribute.ATTRIBUTE_COMPOSITE_KEY.TX_FEE_PAYER, - EventAttribute.ATTRIBUTE_COMPOSITE_KEY.USE_FEEGRANT_GRANTEE, - ]) - ), - ]); - - if (dailyEvents.length > 0 || feeEvents.length > 0) { - dailyEvents - .concat(feeEvents) - .filter( - (event) => - Utils.isValidAccountAddress( - event.value, - config.networkPrefixAddress, - 20 - ) || - Utils.isValidAccountAddress( - event.value, - config.networkPrefixAddress, - 32 - ) - ) - .forEach((event) => { - if (!accountStats[event.value]) { - accountStats[event.value] = { - amount_sent: '0', - amount_received: '0', - tx_sent: 0, - gas_used: '0', - }; - } - }); - - dailyEvents - .filter( - (event) => - event.composite_key === - EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_AMOUNT || - event.composite_key === - EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_AMOUNT - ) - .forEach((event) => { - switch (event.composite_key) { - case EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_AMOUNT: - const addrSpent = dailyEvents.find( - (dEvent) => - dEvent.event_id === event.event_id && - dEvent.composite_key === - EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_SPENDER - )!.value; - - accountStats[addrSpent].amount_sent = ( - BigInt(accountStats[addrSpent].amount_sent) + - BigInt(parseCoins(event.value)[0].amount) - ).toString(); - break; - case EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_AMOUNT: - const addrReceived = dailyEvents.find( - (dEvent) => - dEvent.event_id === event.event_id && - dEvent.composite_key === - EventAttribute.ATTRIBUTE_COMPOSITE_KEY - .COIN_RECEIVED_RECEIVER - )!.value; - - accountStats[addrReceived].amount_received = ( - BigInt(accountStats[addrReceived].amount_received) + - BigInt(parseCoins(event.value)[0].amount) - ).toString(); - break; - default: - break; - } - }); - - feeEvents - .filter( - (event) => - event.composite_key === - EventAttribute.ATTRIBUTE_COMPOSITE_KEY.TX_FEE_PAYER - ) - .forEach((event) => { - let addr = event.value; - - const feeGrant = feeEvents.find( - (dEvent) => - dEvent.tx_id === event.tx_id && - dEvent.composite_key === - EventAttribute.ATTRIBUTE_COMPOSITE_KEY.USE_FEEGRANT_GRANTEE - ); - - if (feeGrant) { - addr = feeGrant.value; - } - - accountStats[addr].tx_sent += 1; - accountStats[addr].gas_used = ( - BigInt(accountStats[addr].gas_used) + BigInt(event.gas_used) - ).toString(); - }); - - await this.createJob( - BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, - BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, - { - startId: nextId, - endId: endTxId, - date: _payload.date, - accountStats, + const [spendReceiveDone, gasTxSentDone] = await Promise.all([ + this.calculateSpendReceive(startTxId, nextId), + this.calculateGasUsedTxSent(startTxId, nextId), + ]); + + if (!spendReceiveDone || !gasTxSentDone) { + await this.createJob( + BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, + BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, + { + startId: nextId, + endId: endTxId, + date: _payload.date, + accountStats: this.accountStats, + }, + { + removeOnComplete: true, + removeOnFail: { + count: 3, }, - { - removeOnComplete: true, - removeOnFail: { - count: 3, - }, - } - ); - } else { - const dailyAccountStats = Object.keys(accountStats).map((acc) => - AccountStatistics.fromJson({ - address: acc, - ...accountStats[acc], - date: startTime.toISOString(), - }) - ); - - this.logger.info(`Insert new daily statistic for date ${startTime}`); - if (dailyAccountStats.length > 0) { - await AccountStatistics.query() - .insert(dailyAccountStats) - .catch((error) => { - this.logger.error( - 'Error insert new daily account statistic records' - ); - this.logger.error(error); - }); } + ); + } else { + const dailyAccountStats = Object.keys(this.accountStats).map((acc) => + AccountStatistics.fromJson({ + address: acc, + ...this.accountStats[acc], + date: startTime.toISOString(), + }) + ); - await this.createJob( - BULL_JOB_NAME.HANDLE_TOP_ACCOUNTS, - BULL_JOB_NAME.HANDLE_TOP_ACCOUNTS, - {}, - { - removeOnComplete: true, - removeOnFail: { - count: 3, - }, - } - ); + this.logger.info(`Insert new account statistics for date ${startTime}`); + if (dailyAccountStats.length > 0) { + await AccountStatistics.query().insert(dailyAccountStats); } - } catch (error) { - this.logger.error(error); + + await this.createJob( + BULL_JOB_NAME.HANDLE_TOP_ACCOUNTS, + BULL_JOB_NAME.HANDLE_TOP_ACCOUNTS, + {}, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + } + ); } } @@ -293,27 +156,171 @@ export default class AccountStatisticsService extends BullableService { // prefix: `horoscope-v2-${config.chainId}`, }) public async handleTopAccounts(_payload: object): Promise { - try { - const now = dayjs.utc().startOf('day').toDate(); - - const { dayRange } = config.accountStatistics; - const [threeDayStat, fifteenDayStat, thirtyDayStat] = await Promise.all([ - this.getStatsFromSpecificDaysAgo(dayRange[0], now), - this.getStatsFromSpecificDaysAgo(dayRange[1], now), - this.getStatsFromSpecificDaysAgo(dayRange[2], now), - ]); - - const topAccounts = { - three_days: this.calculateTop(threeDayStat), - fifteen_days: this.calculateTop(fifteenDayStat), - thirty_days: this.calculateTop(thirtyDayStat), - }; - - this.logger.info(`Update top accounts for day ${new Date(now)}`); - await this.broker.cacher?.set(REDIS_KEY.TOP_ACCOUNTS, topAccounts); - } catch (error) { - this.logger.error(error); + const now = dayjs.utc().startOf('day').toDate(); + + const { dayRange } = config.accountStatistics; + const [threeDayStat, fifteenDayStat, thirtyDayStat] = await Promise.all([ + this.getStatsFromSpecificDaysAgo(dayRange[0], now), + this.getStatsFromSpecificDaysAgo(dayRange[1], now), + this.getStatsFromSpecificDaysAgo(dayRange[2], now), + ]); + + const topAccounts = { + three_days: this.calculateTop(threeDayStat), + fifteen_days: this.calculateTop(fifteenDayStat), + thirty_days: this.calculateTop(thirtyDayStat), + }; + + this.logger.info(`Update top accounts for day ${new Date(now)}`); + await this.broker.cacher?.set(REDIS_KEY.TOP_ACCOUNTS, topAccounts); + } + + private async calculateSpendReceive( + startTxId: number, + nextId: number + ): Promise { + const dailyEvents: any[] = await EventAttribute.query() + .select(knex.raw('jsonb_agg(jsonb_build_object(composite_key, value))')) + .where('tx_id', '>=', startTxId) + .andWhere('tx_id', '<', nextId) + .andWhere((builder) => + builder + // Get the address that actually spent or received token + .whereIn('composite_key', [ + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_SPENDER, + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_AMOUNT, + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_AMOUNT, + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_RECEIVER, + ]) + ) + .groupBy('event_id'); + + if (dailyEvents.length > 0) { + dailyEvents + .map((event) => Object.assign({}, ...event.jsonb_agg)) + .map( + (event) => + event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_SPENDER] ?? + event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_RECEIVER] + ) + .forEach((address) => { + if (!this.accountStats[address]) { + this.accountStats[address] = { + amount_sent: '0', + amount_received: '0', + tx_sent: 0, + gas_used: '0', + }; + } + }); + + dailyEvents + .map((event) => Object.assign({}, ...event.jsonb_agg)) + .forEach((event) => { + if ( + event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_SPENDER] + ) { + const addrSpent = + event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_SPENDER]; + const amountSpent = + event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_AMOUNT]; + + this.accountStats[addrSpent].amount_sent = ( + BigInt(this.accountStats[addrSpent].amount_sent) + + BigInt(parseCoins(amountSpent)[0].amount) + ).toString(); + } else if ( + event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_RECEIVER] + ) { + const addrReceived = + event[ + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_RECEIVER + ]; + const amountReceived = + event[ + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_AMOUNT + ]; + + this.accountStats[addrReceived].amount_received = ( + BigInt(this.accountStats[addrReceived].amount_received) + + BigInt(parseCoins(amountReceived)[0].amount) + ).toString(); + } + }); + + return false; + } + + return true; + } + + private async calculateGasUsedTxSent( + startTxId: number, + nextId: number + ): Promise { + const feeEvents: any[] = await EventAttribute.query() + .joinRelated('transaction') + .select( + knex.raw('jsonb_agg(jsonb_build_object(composite_key, value))'), + 'gas_used' + ) + .where('tx_id', '>=', startTxId) + .andWhere('tx_id', '<', nextId) + .andWhere((builder) => + builder + // If fee_grant is involved, then needs to track to the granters and grantees + .whereIn('composite_key', [ + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.TX_FEE_PAYER, + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.USE_FEEGRANT_GRANTEE, + ]) + ) + .groupBy('tx_id', 'gas_used'); + + if (feeEvents.length > 0) { + feeEvents + .map((event) => Object.assign({}, ...event.jsonb_agg)) + .map( + (event) => + event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.TX_FEE_PAYER] ?? + event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.USE_FEEGRANT_GRANTEE] + ) + .forEach((address) => { + if (!this.accountStats[address]) { + this.accountStats[address] = { + amount_sent: '0', + amount_received: '0', + tx_sent: 0, + gas_used: '0', + }; + } + }); + + feeEvents + .map((event) => + Object.assign({ gas_used: event.gas_used }, ...event.jsonb_agg) + ) + .forEach((event) => { + let addr = event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.TX_FEE_PAYER]; + + if ( + event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.USE_FEEGRANT_GRANTEE] + ) { + addr = + event[ + EventAttribute.ATTRIBUTE_COMPOSITE_KEY.USE_FEEGRANT_GRANTEE + ]; + } + + this.accountStats[addr].tx_sent += 1; + this.accountStats[addr].gas_used = ( + BigInt(this.accountStats[addr].gas_used) + BigInt(event.gas_used) + ).toString(); + }); + + return false; } + + return true; } private async getStatsFromSpecificDaysAgo( diff --git a/src/services/statistics/daily_statistics.service.ts b/src/services/statistics/daily_statistics.service.ts index 8d7046ab6..e4e71fdce 100644 --- a/src/services/statistics/daily_statistics.service.ts +++ b/src/services/statistics/daily_statistics.service.ts @@ -64,112 +64,105 @@ export default class DailyStatisticsService extends BullableService { // prefix: `horoscope-v2-${config.chainId}`, }) public async handleJob(_payload: IDailyStatsParam): Promise { - try { - const startTime = dayjs - .utc(_payload.date) - .subtract(1, 'day') - .startOf('day') - .toDate(); - const endTime = dayjs.utc(_payload.date).startOf('day').toDate(); - this.logger.info( - `Get daily statistic events at page ${ - _payload.offset - } for day ${new Date(startTime)}` - ); + const endTime = dayjs.utc(_payload.date).startOf('day').toDate(); + const startTime = dayjs.utc(endTime).subtract(1, 'day').toDate(); + this.logger.info( + `Get daily statistic events at page ${_payload.offset} for day ${new Date( + startTime + )}` + ); - let startTxId = _payload.startId; - let endTxId = _payload.endId; - // If job runs for the 1st time, then needs to query the start and end tx id of the day as the range - if (!_payload.startId && !_payload.endId) { - const startTx = await Transaction.query() - .select('id') - .where('transaction.timestamp', '>=', startTime) - .limit(1) - .orderBy('id'); - const endTx = await Transaction.query() - .select('id') - .where('transaction.timestamp', '<', endTime) - .limit(1) - .orderBy('id', 'desc'); + let startTxId = _payload.startId; + let endTxId = _payload.endId; + // If job runs for the 1st time, then needs to query the start and end tx id of the day as the range + if (!_payload.startId && !_payload.endId) { + const startTx = await Transaction.query() + .select('id') + .where('transaction.timestamp', '>=', startTime) + .limit(1) + .orderBy('id'); + const endTx = await Transaction.query() + .select('id') + .where('transaction.timestamp', '<', endTime) + .limit(1) + .orderBy('id', 'desc'); - startTxId = startTx[0].id; - endTxId = endTx[0].id; - } + startTxId = startTx[0].id; + endTxId = endTx[0].id; + } - const dailyAddresses = await EventAttribute.query() - .distinct('value') - .where('tx_id', '>=', startTxId) - .andWhere('tx_id', '<=', endTxId) - .andWhere('value', 'like', `${config.networkPrefixAddress}%`) - .limit(config.dailyStatistics.recordsPerCall) - .offset(_payload.offset * config.dailyStatistics.recordsPerCall); + const dailyAddresses = await EventAttribute.query() + .distinct('value') + .where('tx_id', '>=', startTxId) + .andWhere('tx_id', '<=', endTxId) + .andWhere('value', 'like', `${config.networkPrefixAddress}%`) + .limit(config.dailyStatistics.recordsPerCall) + .offset(_payload.offset * config.dailyStatistics.recordsPerCall); - if (dailyAddresses.length > 0) { - const activeAddrs = Array.from( - new Set( - _payload.addresses.concat( - dailyAddresses - .filter((event) => - Utils.isValidAccountAddress( - event.value, - config.networkPrefixAddress, - 20 - ) + // TODO: Need to re-define if it just count normal addresses only or also the contract addresses + if (dailyAddresses.length > 0) { + const activeAddrs = Array.from( + new Set( + _payload.addresses.concat( + dailyAddresses + .filter((event) => + Utils.isValidAccountAddress( + event.value, + config.networkPrefixAddress, + 20 ) - .map((event) => event.value) - ) + ) + .map((event) => event.value) ) - ); - const offset = _payload.offset + 1; + ) + ); + const offset = _payload.offset + 1; - await this.createJob( - BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, - BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, - { - startId: startTxId, - endId: endTxId, - date: _payload.date, - offset, - addresses: activeAddrs, + await this.createJob( + BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, + BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, + { + startId: startTxId, + endId: endTxId, + date: _payload.date, + offset, + addresses: activeAddrs, + }, + { + removeOnComplete: true, + removeOnFail: { + count: 3, }, - { - removeOnComplete: true, - removeOnFail: { - count: 3, - }, - } - ); - } else { - const [dailyTxs, uniqueAddrs, prevDailyStat] = await Promise.all([ - Transaction.query() - .count('id') - .where('timestamp', '>=', startTime) - .andWhere('timestamp', '<', endTime) - .andWhere('code', 0), - Account.query().count('id'), - DailyStatistics.query().findOne('date', startTime), - ]); + } + ); + } else { + const [dailyTxs, uniqueAddrs, prevDailyStat] = await Promise.all([ + Transaction.query() + .count('id') + .where('timestamp', '>=', startTime) + .andWhere('timestamp', '<', endTime) + .andWhere('code', 0), + Account.query().count('id'), + DailyStatistics.query().findOne('date', startTime), + ]); - const dailyStat = DailyStatistics.fromJson({ - daily_txs: dailyTxs[0].count, - daily_active_addresses: _payload.addresses.length, - unique_addresses: Number(uniqueAddrs[0].count), - unique_addresses_increase: prevDailyStat - ? uniqueAddrs[0].count - prevDailyStat.unique_addresses - : 0, - date: startTime.toISOString(), - }); + const dailyStat = DailyStatistics.fromJson({ + daily_txs: dailyTxs[0].count, + daily_active_addresses: _payload.addresses.length, + unique_addresses: Number(uniqueAddrs[0].count), + unique_addresses_increase: prevDailyStat + ? uniqueAddrs[0].count - prevDailyStat.unique_addresses + : 0, + date: startTime.toISOString(), + }); - this.logger.info(`Insert new daily statistic for date ${startTime}`); - await DailyStatistics.query() - .insert(dailyStat) - .catch((error) => { - this.logger.error('Error insert new daily statistic record'); - this.logger.error(error); - }); - } - } catch (error) { - this.logger.error(error); + this.logger.info(`Insert new daily statistic for date ${startTime}`); + await DailyStatistics.query() + .insert(dailyStat) + .catch((error) => { + this.logger.error('Error insert new daily statistic record'); + this.logger.error(error); + }); } } diff --git a/src/services/statistics/dashboard_statistics.service.ts b/src/services/statistics/dashboard_statistics.service.ts index c6f93e9d4..b998d3d4e 100644 --- a/src/services/statistics/dashboard_statistics.service.ts +++ b/src/services/statistics/dashboard_statistics.service.ts @@ -32,70 +32,64 @@ export default class DashboardStatisticsService extends BullableService { // prefix: `horoscope-v2-${config.chainId}`, }) public async handleJob(_payload: object): Promise { - try { - this.logger.info('Update AuraScan dashboard statistics'); - this._lcdClient = await getLcdClient(); + this.logger.info('Update AuraScan dashboard statistics'); + this._lcdClient = await getLcdClient(); - const [totalBlocks, totalTxs, totalValidators] = await Promise.all([ - BlockCheckpoint.query().findOne('job_name', BULL_JOB_NAME.CRAWL_BLOCK), - Transaction.query().count('id'), - Validator.query(), - ]); + const [totalBlocks, totalTxs, totalValidators] = await Promise.all([ + BlockCheckpoint.query().findOne('job_name', BULL_JOB_NAME.CRAWL_BLOCK), + Transaction.query().count('id'), + Validator.query(), + ]); - const [communityPool, inflation, distribution, supply] = - await Promise.all([ - this._lcdClient.auranw.cosmos.distribution.v1beta1.communityPool(), - this._lcdClient.auranw.cosmos.mint.v1beta1.inflation(), - this._lcdClient.auranw.cosmos.distribution.v1beta1.params(), - this._lcdClient.auranw.cosmos.bank.v1beta1.supplyOf({ - denom: config.networkDenom, - }), - ]); - let bondedTokens = BigInt(0); - totalValidators - .filter( - (val) => - val.status === Validator.STATUS.BONDED && val.jailed === false - ) - .forEach((val) => { - bondedTokens += BigInt(val.tokens); - }); - const totalAura = supply.amount.amount; + const [communityPool, inflation, distribution, supply] = await Promise.all([ + this._lcdClient.auranw.cosmos.distribution.v1beta1.communityPool(), + this._lcdClient.auranw.cosmos.mint.v1beta1.inflation(), + this._lcdClient.auranw.cosmos.distribution.v1beta1.params(), + this._lcdClient.auranw.cosmos.bank.v1beta1.supplyOf({ + denom: config.networkDenom, + }), + ]); + let bondedTokens = BigInt(0); + totalValidators + .filter( + (val) => val.status === Validator.STATUS.BONDED && val.jailed === false + ) + .forEach((val) => { + bondedTokens += BigInt(val.tokens); + }); + const totalAura = supply.amount.amount; - const dashboardStatistics = { - total_blocks: totalBlocks?.height, - community_pool: communityPool.pool.find( - (pool: DecCoinSDKType) => pool.denom === config.networkDenom - ).amount, - total_transactions: Number(totalTxs[0].count), - total_validators: totalValidators.length, - total_active_validators: totalValidators.filter( - (val) => val.status === Validator.STATUS.BONDED - ).length, - total_inactive_validators: totalValidators.filter( - (val) => val.status === Validator.STATUS.UNBONDED - ).length, - bonded_tokens: bondedTokens.toString(), - inflation: inflation.inflation, - total_aura: totalAura, - staking_apr: Number( - BigNumber(inflation.inflation) - .multipliedBy( - BigNumber(1 - Number(distribution.params.community_tax)) - ) - .multipliedBy(BigNumber(totalAura)) - .dividedBy(BigNumber(bondedTokens.toString())) - .multipliedBy(100) - ), - }; + const dashboardStatistics = { + total_blocks: totalBlocks?.height, + community_pool: communityPool.pool.find( + (pool: DecCoinSDKType) => pool.denom === config.networkDenom + ).amount, + total_transactions: Number(totalTxs[0].count), + total_validators: totalValidators.length, + total_active_validators: totalValidators.filter( + (val) => val.status === Validator.STATUS.BONDED + ).length, + total_inactive_validators: totalValidators.filter( + (val) => val.status === Validator.STATUS.UNBONDED + ).length, + bonded_tokens: bondedTokens.toString(), + inflation: inflation.inflation, + total_aura: totalAura, + staking_apr: Number( + BigNumber(inflation.inflation) + .multipliedBy( + BigNumber(1 - Number(distribution.params.community_tax)) + ) + .multipliedBy(BigNumber(totalAura)) + .dividedBy(BigNumber(bondedTokens.toString())) + .multipliedBy(100) + ), + }; - await this.broker.cacher?.set( - REDIS_KEY.DASHBOARD_STATISTICS, - dashboardStatistics - ); - } catch (error) { - this.logger.error(error); - } + await this.broker.cacher?.set( + REDIS_KEY.DASHBOARD_STATISTICS, + dashboardStatistics + ); } public async _start() { From 1eb180ebcc0623edb22a3a10850a59d086e3cbe6 Mon Sep 17 00:00:00 2001 From: AnDQK Date: Wed, 26 Jul 2023 17:40:03 +0700 Subject: [PATCH 11/20] fix: just use a single job to query all daily data --- ..._create_table_daily_stats_account_stats.ts | 1 - src/common/utils/request.ts | 13 +- src/models/account_statistics.ts | 11 ++ src/models/daily_statistics.ts | 4 - .../statistics/account_statistics.service.ts | 183 +++++++----------- .../statistics/daily_statistics.service.ts | 136 +++++-------- 6 files changed, 134 insertions(+), 214 deletions(-) diff --git a/migrations/20230704102018_create_table_daily_stats_account_stats.ts b/migrations/20230704102018_create_table_daily_stats_account_stats.ts index 6ace46373..20c24a761 100644 --- a/migrations/20230704102018_create_table_daily_stats_account_stats.ts +++ b/migrations/20230704102018_create_table_daily_stats_account_stats.ts @@ -6,7 +6,6 @@ export async function up(knex: Knex): Promise { table.bigint('daily_txs').index().notNullable(); table.bigint('daily_active_addresses').index().notNullable(); table.bigint('unique_addresses').index().notNullable(); - table.bigint('unique_addresses_increase').index().notNullable(); table.timestamp('date').unique().notNullable(); }); await knex.schema.createTable('account_statistics', (table: any) => { diff --git a/src/common/utils/request.ts b/src/common/utils/request.ts index a688dd2a5..8be6126f8 100644 --- a/src/common/utils/request.ts +++ b/src/common/utils/request.ts @@ -9,19 +9,8 @@ export interface ITxIdsParam { txIds: number[]; } -export interface IDailyStatsParam { - startId: number; - endId: number; +export interface IStatisticsParam { date: string; - offset: number; - addresses: string[]; -} - -export interface IAccountStatsParam { - startId: number; - endId: number; - date: string; - accountStats: any; } export interface ICreateSpecificDateJob { diff --git a/src/models/account_statistics.ts b/src/models/account_statistics.ts index 1aab5ab27..d097e61ee 100644 --- a/src/models/account_statistics.ts +++ b/src/models/account_statistics.ts @@ -42,4 +42,15 @@ export class AccountStatistics extends BaseModel { static get relationMappings() { return {}; } + + static newAccountStat(address: string, date: string) { + return AccountStatistics.fromJson({ + address, + amount_sent: '0', + amount_received: '0', + tx_sent: 0, + gas_used: '0', + date, + }); + } } diff --git a/src/models/daily_statistics.ts b/src/models/daily_statistics.ts index 2bda2cf2f..e9991a281 100644 --- a/src/models/daily_statistics.ts +++ b/src/models/daily_statistics.ts @@ -7,8 +7,6 @@ export class DailyStatistics extends BaseModel { unique_addresses!: number; - unique_addresses_increase!: number; - date!: Date; static get tableName() { @@ -22,14 +20,12 @@ export class DailyStatistics extends BaseModel { 'daily_txs', 'daily_active_addresses', 'unique_addresses', - 'unique_addresses_increase', 'date', ], properties: { daily_txs: { type: 'number' }, daily_active_addresses: { type: 'number' }, unique_addresses: { type: 'number' }, - unique_addresses_increase: { type: 'number' }, date: { type: 'string', format: 'date-time' }, }, }; diff --git a/src/services/statistics/account_statistics.service.ts b/src/services/statistics/account_statistics.service.ts index 2884076b2..830adc835 100644 --- a/src/services/statistics/account_statistics.service.ts +++ b/src/services/statistics/account_statistics.service.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-param-reassign */ /* eslint-disable no-case-declarations */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { @@ -13,7 +14,7 @@ import _ from 'lodash'; import { AccountStatistics, EventAttribute, Transaction } from '../../models'; import { BULL_JOB_NAME, - IAccountStatsParam, + IStatisticsParam, ICreateSpecificDateJob, REDIS_KEY, SERVICE, @@ -27,8 +28,6 @@ import knex from '../../common/utils/db_connection'; version: 1, }) export default class AccountStatisticsService extends BullableService { - private accountStats: any; - public constructor(public broker: ServiceBroker) { super(broker); } @@ -65,89 +64,63 @@ export default class AccountStatisticsService extends BullableService { jobName: BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, // prefix: `horoscope-v2-${config.chainId}`, }) - public async handleJob(_payload: IAccountStatsParam): Promise { + public async handleJob(_payload: IStatisticsParam): Promise { const { date } = _payload; - this.accountStats = _payload.accountStats; + const accountStats: any = {}; const endTime = dayjs.utc(date).startOf('day').toDate(); const startTime = dayjs.utc(endTime).subtract(1, 'day').toDate(); - let startTxId = _payload.startId; - // The end tx's ID needs to plus 1 since the query will select record with _lt not _lte - let nextId = Math.min(_payload.endId + 1, startTxId + 50); - let endTxId = _payload.endId; - // If job runs for the 1st time, then needs to query the start and end tx id of the day as the range - if (!_payload.startId && !_payload.endId) { - const startTx = await Transaction.query() - .select('id') - .where('transaction.timestamp', '>=', startTime) - .limit(1) - .orderBy('id'); - const endTx = await Transaction.query() - .select('id') - .where('transaction.timestamp', '<', endTime) - .limit(1) - .orderBy('id', 'desc'); - - startTxId = startTx[0].id; - // The end tx's ID needs to plus 1 since the query will select record with _lt not _lte - nextId = Math.min(endTx[0].id + 1, startTxId + 50); - endTxId = endTx[0].id; - } + const startTx = await Transaction.query() + .select('id') + .where('transaction.timestamp', '>=', startTime) + .limit(1) + .orderBy('id'); + const endTx = await Transaction.query() + .select('id') + .where('transaction.timestamp', '<', endTime) + .limit(1) + .orderBy('id', 'desc'); + this.logger.info( - `Get account statistic events from id ${startTxId} for day ${new Date( - startTime - )}` + `Get account statistic events for day ${new Date(startTime)}` ); - const [spendReceiveDone, gasTxSentDone] = await Promise.all([ - this.calculateSpendReceive(startTxId, nextId), - this.calculateGasUsedTxSent(startTxId, nextId), + await Promise.all([ + this.calculateSpendReceive( + startTx[0].id, + endTx[0].id, + accountStats, + startTime.toISOString() + ), + this.calculateGasUsedTxSent( + startTx[0].id, + endTx[0].id, + accountStats, + startTime.toISOString() + ), ]); - if (!spendReceiveDone || !gasTxSentDone) { - await this.createJob( - BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, - BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, - { - startId: nextId, - endId: endTxId, - date: _payload.date, - accountStats: this.accountStats, - }, - { - removeOnComplete: true, - removeOnFail: { - count: 3, - }, - } - ); - } else { - const dailyAccountStats = Object.keys(this.accountStats).map((acc) => - AccountStatistics.fromJson({ - address: acc, - ...this.accountStats[acc], - date: startTime.toISOString(), - }) - ); - - this.logger.info(`Insert new account statistics for date ${startTime}`); - if (dailyAccountStats.length > 0) { - await AccountStatistics.query().insert(dailyAccountStats); - } + const dailyAccountStats = Object.keys(accountStats).map( + (acc) => accountStats[acc] + ); - await this.createJob( - BULL_JOB_NAME.HANDLE_TOP_ACCOUNTS, - BULL_JOB_NAME.HANDLE_TOP_ACCOUNTS, - {}, - { - removeOnComplete: true, - removeOnFail: { - count: 3, - }, - } - ); + this.logger.info(`Insert new account statistics for date ${startTime}`); + if (dailyAccountStats.length > 0) { + await AccountStatistics.query().insert(dailyAccountStats); } + + await this.createJob( + BULL_JOB_NAME.HANDLE_TOP_ACCOUNTS, + BULL_JOB_NAME.HANDLE_TOP_ACCOUNTS, + {}, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + } + ); } @QueueHandler({ @@ -177,12 +150,14 @@ export default class AccountStatisticsService extends BullableService { private async calculateSpendReceive( startTxId: number, - nextId: number - ): Promise { + endTxId: number, + accountStats: any, + date: string + ) { const dailyEvents: any[] = await EventAttribute.query() .select(knex.raw('jsonb_agg(jsonb_build_object(composite_key, value))')) .where('tx_id', '>=', startTxId) - .andWhere('tx_id', '<', nextId) + .andWhere('tx_id', '<', endTxId) .andWhere((builder) => builder // Get the address that actually spent or received token @@ -204,13 +179,11 @@ export default class AccountStatisticsService extends BullableService { event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_RECEIVER] ) .forEach((address) => { - if (!this.accountStats[address]) { - this.accountStats[address] = { - amount_sent: '0', - amount_received: '0', - tx_sent: 0, - gas_used: '0', - }; + if (!accountStats[address]) { + accountStats[address] = AccountStatistics.newAccountStat( + address, + date + ); } }); @@ -225,8 +198,8 @@ export default class AccountStatisticsService extends BullableService { const amountSpent = event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_AMOUNT]; - this.accountStats[addrSpent].amount_sent = ( - BigInt(this.accountStats[addrSpent].amount_sent) + + accountStats[addrSpent].amount_sent = ( + BigInt(accountStats[addrSpent].amount_sent) + BigInt(parseCoins(amountSpent)[0].amount) ).toString(); } else if ( @@ -241,23 +214,21 @@ export default class AccountStatisticsService extends BullableService { EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_AMOUNT ]; - this.accountStats[addrReceived].amount_received = ( - BigInt(this.accountStats[addrReceived].amount_received) + + accountStats[addrReceived].amount_received = ( + BigInt(accountStats[addrReceived].amount_received) + BigInt(parseCoins(amountReceived)[0].amount) ).toString(); } }); - - return false; } - - return true; } private async calculateGasUsedTxSent( startTxId: number, - nextId: number - ): Promise { + endTxId: number, + accountStats: any, + date: string + ) { const feeEvents: any[] = await EventAttribute.query() .joinRelated('transaction') .select( @@ -265,7 +236,7 @@ export default class AccountStatisticsService extends BullableService { 'gas_used' ) .where('tx_id', '>=', startTxId) - .andWhere('tx_id', '<', nextId) + .andWhere('tx_id', '<', endTxId) .andWhere((builder) => builder // If fee_grant is involved, then needs to track to the granters and grantees @@ -285,13 +256,11 @@ export default class AccountStatisticsService extends BullableService { event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.USE_FEEGRANT_GRANTEE] ) .forEach((address) => { - if (!this.accountStats[address]) { - this.accountStats[address] = { - amount_sent: '0', - amount_received: '0', - tx_sent: 0, - gas_used: '0', - }; + if (!accountStats[address]) { + accountStats[address] = AccountStatistics.newAccountStat( + address, + date + ); } }); @@ -311,16 +280,12 @@ export default class AccountStatisticsService extends BullableService { ]; } - this.accountStats[addr].tx_sent += 1; - this.accountStats[addr].gas_used = ( - BigInt(this.accountStats[addr].gas_used) + BigInt(event.gas_used) + accountStats[addr].tx_sent += 1; + accountStats[addr].gas_used = ( + BigInt(accountStats[addr].gas_used) + BigInt(event.gas_used) ).toString(); }); - - return false; } - - return true; } private async getStatsFromSpecificDaysAgo( diff --git a/src/services/statistics/daily_statistics.service.ts b/src/services/statistics/daily_statistics.service.ts index e4e71fdce..49cd70df1 100644 --- a/src/services/statistics/daily_statistics.service.ts +++ b/src/services/statistics/daily_statistics.service.ts @@ -14,7 +14,7 @@ import { import { BULL_JOB_NAME, ICreateSpecificDateJob, - IDailyStatsParam, + IStatisticsParam, SERVICE, } from '../../common'; import config from '../../../config.json' assert { type: 'json' }; @@ -63,107 +63,67 @@ export default class DailyStatisticsService extends BullableService { jobName: BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, // prefix: `horoscope-v2-${config.chainId}`, }) - public async handleJob(_payload: IDailyStatsParam): Promise { + public async handleJob(_payload: IStatisticsParam): Promise { const endTime = dayjs.utc(_payload.date).startOf('day').toDate(); const startTime = dayjs.utc(endTime).subtract(1, 'day').toDate(); this.logger.info( - `Get daily statistic events at page ${_payload.offset} for day ${new Date( - startTime - )}` + `Get daily statistic events for day ${new Date(startTime)}` ); - let startTxId = _payload.startId; - let endTxId = _payload.endId; - // If job runs for the 1st time, then needs to query the start and end tx id of the day as the range - if (!_payload.startId && !_payload.endId) { - const startTx = await Transaction.query() - .select('id') - .where('transaction.timestamp', '>=', startTime) - .limit(1) - .orderBy('id'); - const endTx = await Transaction.query() - .select('id') - .where('transaction.timestamp', '<', endTime) - .limit(1) - .orderBy('id', 'desc'); - - startTxId = startTx[0].id; - endTxId = endTx[0].id; - } + const startTx = await Transaction.query() + .select('id') + .where('transaction.timestamp', '>=', startTime) + .limit(1) + .orderBy('id'); + const endTx = await Transaction.query() + .select('id') + .where('transaction.timestamp', '<', endTime) + .limit(1) + .orderBy('id', 'desc'); const dailyAddresses = await EventAttribute.query() .distinct('value') - .where('tx_id', '>=', startTxId) - .andWhere('tx_id', '<=', endTxId) - .andWhere('value', 'like', `${config.networkPrefixAddress}%`) - .limit(config.dailyStatistics.recordsPerCall) - .offset(_payload.offset * config.dailyStatistics.recordsPerCall); + .where('tx_id', '>=', startTx[0].id) + .andWhere('tx_id', '<=', endTx[0].id) + .andWhere('value', 'like', `${config.networkPrefixAddress}%`); // TODO: Need to re-define if it just count normal addresses only or also the contract addresses - if (dailyAddresses.length > 0) { - const activeAddrs = Array.from( - new Set( - _payload.addresses.concat( - dailyAddresses - .filter((event) => - Utils.isValidAccountAddress( - event.value, - config.networkPrefixAddress, - 20 - ) - ) - .map((event) => event.value) + const activeAddrs = Array.from( + new Set( + dailyAddresses + .filter((event) => + Utils.isValidAccountAddress( + event.value, + config.networkPrefixAddress, + 20 + ) ) - ) - ); - const offset = _payload.offset + 1; + .map((event) => event.value) + ) + ); + const [dailyTxs, uniqueAddrs] = await Promise.all([ + Transaction.query() + .count('id') + .where('timestamp', '>=', startTime) + .andWhere('timestamp', '<', endTime) + .andWhere('code', 0), + Account.query().count('id'), + ]); - await this.createJob( - BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, - BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, - { - startId: startTxId, - endId: endTxId, - date: _payload.date, - offset, - addresses: activeAddrs, - }, - { - removeOnComplete: true, - removeOnFail: { - count: 3, - }, - } - ); - } else { - const [dailyTxs, uniqueAddrs, prevDailyStat] = await Promise.all([ - Transaction.query() - .count('id') - .where('timestamp', '>=', startTime) - .andWhere('timestamp', '<', endTime) - .andWhere('code', 0), - Account.query().count('id'), - DailyStatistics.query().findOne('date', startTime), - ]); + const dailyStat = DailyStatistics.fromJson({ + daily_txs: dailyTxs[0].count, + daily_active_addresses: activeAddrs.length, + unique_addresses: Number(uniqueAddrs[0].count), + date: startTime.toISOString(), + }); - const dailyStat = DailyStatistics.fromJson({ - daily_txs: dailyTxs[0].count, - daily_active_addresses: _payload.addresses.length, - unique_addresses: Number(uniqueAddrs[0].count), - unique_addresses_increase: prevDailyStat - ? uniqueAddrs[0].count - prevDailyStat.unique_addresses - : 0, - date: startTime.toISOString(), + this.logger.info(`Insert new daily statistic for date ${startTime}`); + await DailyStatistics.query() + .insert(dailyStat) + .catch((error) => { + this.logger.error('Error insert new daily statistic record'); + this.logger.error(error); }); - - this.logger.info(`Insert new daily statistic for date ${startTime}`); - await DailyStatistics.query() - .insert(dailyStat) - .catch((error) => { - this.logger.error('Error insert new daily statistic record'); - this.logger.error(error); - }); - } } public async _start() { From 3d3d5aaadc9ca3bfd29726a7f12db7f0b60746c5 Mon Sep 17 00:00:00 2001 From: AnDQK Date: Thu, 27 Jul 2023 08:41:08 +0700 Subject: [PATCH 12/20] fix: move all queries in daily_stats job into a single Promise all --- .../statistics/daily_statistics.service.ts | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/services/statistics/daily_statistics.service.ts b/src/services/statistics/daily_statistics.service.ts index 49cd70df1..9bfcf9319 100644 --- a/src/services/statistics/daily_statistics.service.ts +++ b/src/services/statistics/daily_statistics.service.ts @@ -81,12 +81,19 @@ export default class DailyStatisticsService extends BullableService { .limit(1) .orderBy('id', 'desc'); - const dailyAddresses = await EventAttribute.query() - .distinct('value') - .where('tx_id', '>=', startTx[0].id) - .andWhere('tx_id', '<=', endTx[0].id) - .andWhere('value', 'like', `${config.networkPrefixAddress}%`); - + const [dailyTxs, dailyAddresses, uniqueAddrs] = await Promise.all([ + Transaction.query() + .count('id') + .where('timestamp', '>=', startTime) + .andWhere('timestamp', '<', endTime) + .andWhere('code', 0), + EventAttribute.query() + .distinct('value') + .where('tx_id', '>=', startTx[0].id) + .andWhere('tx_id', '<=', endTx[0].id) + .andWhere('value', 'like', `${config.networkPrefixAddress}%`), + Account.query().count('id'), + ]); // TODO: Need to re-define if it just count normal addresses only or also the contract addresses const activeAddrs = Array.from( new Set( @@ -101,14 +108,6 @@ export default class DailyStatisticsService extends BullableService { .map((event) => event.value) ) ); - const [dailyTxs, uniqueAddrs] = await Promise.all([ - Transaction.query() - .count('id') - .where('timestamp', '>=', startTime) - .andWhere('timestamp', '<', endTime) - .andWhere('code', 0), - Account.query().count('id'), - ]); const dailyStat = DailyStatistics.fromJson({ daily_txs: dailyTxs[0].count, From 0f77be5b79e7c90c18788411f496b345b519a5c5 Mon Sep 17 00:00:00 2001 From: AnDQK Date: Thu, 27 Jul 2023 09:30:18 +0700 Subject: [PATCH 13/20] fix: move dayjs.extend to after import statements --- src/services/statistics/account_statistics.service.ts | 4 ++-- src/services/statistics/daily_statistics.service.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services/statistics/account_statistics.service.ts b/src/services/statistics/account_statistics.service.ts index 830adc835..ff9c7587f 100644 --- a/src/services/statistics/account_statistics.service.ts +++ b/src/services/statistics/account_statistics.service.ts @@ -23,6 +23,8 @@ import config from '../../../config.json' assert { type: 'json' }; import BullableService, { QueueHandler } from '../../base/bullable.service'; import knex from '../../common/utils/db_connection'; +dayjs.extend(utc); + @Service({ name: SERVICE.V1.AccountStatisticsService.key, version: 1, @@ -405,8 +407,6 @@ export default class AccountStatisticsService extends BullableService { } public async _start() { - dayjs.extend(utc); - return super._start(); } } diff --git a/src/services/statistics/daily_statistics.service.ts b/src/services/statistics/daily_statistics.service.ts index 9bfcf9319..0787e0ca3 100644 --- a/src/services/statistics/daily_statistics.service.ts +++ b/src/services/statistics/daily_statistics.service.ts @@ -21,6 +21,8 @@ import config from '../../../config.json' assert { type: 'json' }; import BullableService, { QueueHandler } from '../../base/bullable.service'; import Utils from '../../common/utils/utils'; +dayjs.extend(utc); + @Service({ name: SERVICE.V1.DailyStatisticsService.key, version: 1, @@ -126,8 +128,6 @@ export default class DailyStatisticsService extends BullableService { } public async _start() { - dayjs.extend(utc); - return super._start(); } } From 09cb61e83b4bf19262584f01f50824d1b3343e1d Mon Sep 17 00:00:00 2001 From: AnDQK Date: Thu, 27 Jul 2023 09:38:42 +0700 Subject: [PATCH 14/20] fix: remove _start --- src/services/statistics/account_statistics.service.ts | 4 ---- src/services/statistics/daily_statistics.service.ts | 4 ---- 2 files changed, 8 deletions(-) diff --git a/src/services/statistics/account_statistics.service.ts b/src/services/statistics/account_statistics.service.ts index ff9c7587f..398a69675 100644 --- a/src/services/statistics/account_statistics.service.ts +++ b/src/services/statistics/account_statistics.service.ts @@ -405,8 +405,4 @@ export default class AccountStatisticsService extends BullableService { top_gas_used: topGasUsed, }; } - - public async _start() { - return super._start(); - } } diff --git a/src/services/statistics/daily_statistics.service.ts b/src/services/statistics/daily_statistics.service.ts index 0787e0ca3..593e98a55 100644 --- a/src/services/statistics/daily_statistics.service.ts +++ b/src/services/statistics/daily_statistics.service.ts @@ -126,8 +126,4 @@ export default class DailyStatisticsService extends BullableService { this.logger.error(error); }); } - - public async _start() { - return super._start(); - } } From 1bf967e389f09834dc8e00ac536f016e97aa176b Mon Sep 17 00:00:00 2001 From: AnDQK Date: Thu, 27 Jul 2023 10:12:56 +0700 Subject: [PATCH 15/20] fix: only count native token in account_stats job --- .../statistics/account_statistics.service.ts | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/services/statistics/account_statistics.service.ts b/src/services/statistics/account_statistics.service.ts index 398a69675..5cc025dc5 100644 --- a/src/services/statistics/account_statistics.service.ts +++ b/src/services/statistics/account_statistics.service.ts @@ -197,13 +197,16 @@ export default class AccountStatisticsService extends BullableService { ) { const addrSpent = event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_SPENDER]; - const amountSpent = - event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_AMOUNT]; - - accountStats[addrSpent].amount_sent = ( - BigInt(accountStats[addrSpent].amount_sent) + - BigInt(parseCoins(amountSpent)[0].amount) - ).toString(); + const amountSpent = parseCoins( + event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_SPENT_AMOUNT] + )[0]; + + if (amountSpent.denom === config.networkDenom) { + accountStats[addrSpent].amount_sent = ( + BigInt(accountStats[addrSpent].amount_sent) + + BigInt(amountSpent.amount) + ).toString(); + } } else if ( event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_RECEIVER] ) { @@ -211,15 +214,16 @@ export default class AccountStatisticsService extends BullableService { event[ EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_RECEIVER ]; - const amountReceived = - event[ - EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_AMOUNT - ]; - - accountStats[addrReceived].amount_received = ( - BigInt(accountStats[addrReceived].amount_received) + - BigInt(parseCoins(amountReceived)[0].amount) - ).toString(); + const amountReceived = parseCoins( + event[EventAttribute.ATTRIBUTE_COMPOSITE_KEY.COIN_RECEIVED_AMOUNT] + )[0]; + + if (amountReceived.denom === config.networkDenom) { + accountStats[addrReceived].amount_received = ( + BigInt(accountStats[addrReceived].amount_received) + + BigInt(amountReceived.amount) + ).toString(); + } } }); } From 2db7688f7d3955cd3e4bd45d4703b1cd37e32509 Mon Sep 17 00:00:00 2001 From: AnDQK Date: Thu, 27 Jul 2023 14:30:06 +0700 Subject: [PATCH 16/20] feat: add api to sync stats from prev dates --- .../api-gateways/api_gateway.service.ts | 12 +++- .../api-gateways/statistics.service.ts | 45 +++++++++++++- .../statistics/account_statistics.service.ts | 7 ++- .../statistics/api_statistics.service.ts | 58 ++++++++++++++++++- .../statistics/daily_statistics.service.ts | 6 +- 5 files changed, 117 insertions(+), 11 deletions(-) diff --git a/src/services/api-gateways/api_gateway.service.ts b/src/services/api-gateways/api_gateway.service.ts index 39176fa68..d7e4b89b6 100644 --- a/src/services/api-gateways/api_gateway.service.ts +++ b/src/services/api-gateways/api_gateway.service.ts @@ -21,7 +21,17 @@ import { bullBoardMixin } from '../../mixins/bullBoard/bullBoard.mixin'; path: '/api', autoAliases: true, // allow generate rest info (GET/PUT/POST...) in the services mappingPolicy: 'restrict', // allow action called with exact method - whitelist: ['v2.graphiql.*', 'v2.statistics.*'], + whitelist: [ + 'v2.graphiql.*', + 'v2.statistics.getDashboardStatisticsByChainId', + 'v2.statistics.getTopAccountsByChainId', + ], + }, + { + path: '/admin', + autoAliases: true, // allow generate rest info (GET/PUT/POST...) in the services + mappingPolicy: 'restrict', // allow action called with exact method + whitelist: ['v2.statistics.syncPrevDateStatsByChainId'], }, ], // empty cors object will have moleculer to generate handler for preflight request and CORS header which allow all origin diff --git a/src/services/api-gateways/statistics.service.ts b/src/services/api-gateways/statistics.service.ts index ee8c828d3..ee0ee08f9 100644 --- a/src/services/api-gateways/statistics.service.ts +++ b/src/services/api-gateways/statistics.service.ts @@ -1,4 +1,8 @@ -import { Get, Service } from '@ourparentcenter/moleculer-decorators-extended'; +import { + Get, + Post, + Service, +} from '@ourparentcenter/moleculer-decorators-extended'; import { Context, ServiceBroker } from 'moleculer'; import BaseService from '../../base/base.service'; import networks from '../../../network.json' assert { type: 'json' }; @@ -55,4 +59,43 @@ export default class StatisticsService extends BaseService { `v2.api-statistics.getTopAccounts@${selectedChain?.moleculerNamespace}` ); } + + @Post('/sync-prev-date-stats', { + name: 'syncPrevDateStatsByChainId', + params: { + chainid: { + type: 'string', + optional: false, + enum: networks.map((network) => network.chainId), + }, + startDate: { + type: 'string', + optional: false, + default: '2023-01-01', + }, + endDate: { + type: 'string', + optional: true, + default: '2023-01-02', + }, + }, + }) + async syncPrevDateStatsByChainId( + ctx: Context< + { chainid: string; startDate: string; endDate: string }, + Record + > + ) { + const selectedChain = networks.find( + (network) => network.chainId === ctx.params.chainid + ); + + return this.broker.call( + `v2.api-statistics.syncPrevDateStats@${selectedChain?.moleculerNamespace}`, + { + startDate: ctx.params.startDate, + endDate: ctx.params.endDate, + } + ); + } } diff --git a/src/services/statistics/account_statistics.service.ts b/src/services/statistics/account_statistics.service.ts index 5cc025dc5..e40d8acd7 100644 --- a/src/services/statistics/account_statistics.service.ts +++ b/src/services/statistics/account_statistics.service.ts @@ -47,16 +47,15 @@ export default class AccountStatisticsService extends BullableService { BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, BULL_JOB_NAME.CRAWL_ACCOUNT_STATISTICS, { - startId: null, - endId: null, date: ctx.params.date, - accountStats: {}, }, { removeOnComplete: true, removeOnFail: { count: 3, }, + attempts: config.jobRetryAttempt, + backoff: config.jobRetryBackoff, } ); } @@ -121,6 +120,8 @@ export default class AccountStatisticsService extends BullableService { removeOnFail: { count: 3, }, + attempts: config.jobRetryAttempt, + backoff: config.jobRetryBackoff, } ); } diff --git a/src/services/statistics/api_statistics.service.ts b/src/services/statistics/api_statistics.service.ts index 77218d336..74f7da015 100644 --- a/src/services/statistics/api_statistics.service.ts +++ b/src/services/statistics/api_statistics.service.ts @@ -1,10 +1,16 @@ +/* eslint-disable no-await-in-loop */ import { Action, Service, } from '@ourparentcenter/moleculer-decorators-extended'; -import { ServiceBroker } from 'moleculer'; -import { REDIS_KEY } from '../../common'; +import dayjs from 'dayjs'; +import utc from 'dayjs/plugin/utc'; +import { Context, ServiceBroker } from 'moleculer'; +import { REDIS_KEY, SERVICE } from '../../common'; import BaseService from '../../base/base.service'; +import { ErrorCode, ErrorMessage } from '../../common/types/errors'; + +dayjs.extend(utc); @Service({ name: 'api-statistics', @@ -34,4 +40,52 @@ export default class ApiStatisticsService extends BaseService { const result = await this.broker.cacher?.get(REDIS_KEY.TOP_ACCOUNTS); return result; } + + @Action({ + name: 'syncPrevDateStats', + params: { + startDate: { + type: 'string', + }, + endDate: { + type: 'string', + }, + }, + }) + async syncPrevDateStats( + ctx: Context< + { startDate: string; endDate: string }, + Record + > + ) { + // Since each stats job query data of the prev date, + // so the start and end date needs to change to the following date + const startTime = dayjs.utc(ctx.params.startDate).add(1, 'day').toDate(); + const endTime = dayjs.utc(ctx.params.endDate).add(1, 'day').toDate(); + this.logger.info(ctx.params.startDate); + this.logger.info(ctx.params.endDate); + this.logger.info(startTime); + this.logger.info(endTime); + for ( + let date = startTime; + date <= endTime; + date.setDate(date.getDate() + 1) + ) { + await Promise.all([ + this.broker.call( + SERVICE.V1.DailyStatisticsService.CreateSpecificDateJob.path, + { date: date.toString() } + ), + this.broker.call( + SERVICE.V1.AccountStatisticsService.CreateSpecificDateJob.path, + { date: date.toString() } + ), + ]); + } + + return { + code: ErrorCode.SUCCESSFUL, + message: ErrorMessage.SUCCESSFUL, + }; + } } diff --git a/src/services/statistics/daily_statistics.service.ts b/src/services/statistics/daily_statistics.service.ts index 593e98a55..775d93a5f 100644 --- a/src/services/statistics/daily_statistics.service.ts +++ b/src/services/statistics/daily_statistics.service.ts @@ -45,17 +45,15 @@ export default class DailyStatisticsService extends BullableService { BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, BULL_JOB_NAME.CRAWL_DAILY_STATISTICS, { - startId: null, - endId: null, date: ctx.params.date, - offset: 0, - addresses: [], }, { removeOnComplete: true, removeOnFail: { count: 3, }, + attempts: config.jobRetryAttempt, + backoff: config.jobRetryBackoff, } ); } From b535681a19f628b4bd0e524f9c7b3dde1e4abe35 Mon Sep 17 00:00:00 2001 From: AnDQK Date: Thu, 27 Jul 2023 15:04:09 +0700 Subject: [PATCH 17/20] fix: support case when user just pass startDate to api --- src/services/api-gateways/statistics.service.ts | 4 +--- src/services/statistics/api_statistics.service.ts | 10 +++++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/services/api-gateways/statistics.service.ts b/src/services/api-gateways/statistics.service.ts index ee0ee08f9..aae362274 100644 --- a/src/services/api-gateways/statistics.service.ts +++ b/src/services/api-gateways/statistics.service.ts @@ -71,12 +71,10 @@ export default class StatisticsService extends BaseService { startDate: { type: 'string', optional: false, - default: '2023-01-01', }, endDate: { type: 'string', optional: true, - default: '2023-01-02', }, }, }) @@ -94,7 +92,7 @@ export default class StatisticsService extends BaseService { `v2.api-statistics.syncPrevDateStats@${selectedChain?.moleculerNamespace}`, { startDate: ctx.params.startDate, - endDate: ctx.params.endDate, + endDate: ctx.params.endDate ?? null, } ); } diff --git a/src/services/statistics/api_statistics.service.ts b/src/services/statistics/api_statistics.service.ts index 74f7da015..e58abfaea 100644 --- a/src/services/statistics/api_statistics.service.ts +++ b/src/services/statistics/api_statistics.service.ts @@ -49,6 +49,7 @@ export default class ApiStatisticsService extends BaseService { }, endDate: { type: 'string', + optional: true, }, }, }) @@ -61,11 +62,10 @@ export default class ApiStatisticsService extends BaseService { // Since each stats job query data of the prev date, // so the start and end date needs to change to the following date const startTime = dayjs.utc(ctx.params.startDate).add(1, 'day').toDate(); - const endTime = dayjs.utc(ctx.params.endDate).add(1, 'day').toDate(); - this.logger.info(ctx.params.startDate); - this.logger.info(ctx.params.endDate); - this.logger.info(startTime); - this.logger.info(endTime); + const endTime = ctx.params.endDate + ? dayjs.utc(ctx.params.endDate).add(1, 'day').toDate() + : dayjs.utc(ctx.params.startDate).add(1, 'day').toDate(); + for ( let date = startTime; date <= endTime; From ac023df434dc65984223e8c552385294eb021f9c Mon Sep 17 00:00:00 2001 From: Phan Anh Tuan Date: Thu, 27 Jul 2023 16:42:55 +0700 Subject: [PATCH 18/20] fix: fix whitelist graphql --- src/services/api-gateways/api_gateway.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/api-gateways/api_gateway.service.ts b/src/services/api-gateways/api_gateway.service.ts index d7e4b89b6..92c51c8f9 100644 --- a/src/services/api-gateways/api_gateway.service.ts +++ b/src/services/api-gateways/api_gateway.service.ts @@ -22,7 +22,7 @@ import { bullBoardMixin } from '../../mixins/bullBoard/bullBoard.mixin'; autoAliases: true, // allow generate rest info (GET/PUT/POST...) in the services mappingPolicy: 'restrict', // allow action called with exact method whitelist: [ - 'v2.graphiql.*', + 'v2.graphql.*', 'v2.statistics.getDashboardStatisticsByChainId', 'v2.statistics.getTopAccountsByChainId', ], From c961d2ac1b2833eca2764bd0534ac2c5edfc5dc0 Mon Sep 17 00:00:00 2001 From: Phan Anh Tuan Date: Thu, 17 Aug 2023 09:40:08 +0700 Subject: [PATCH 19/20] fix: update column in account_statistic table, update bignum top_tx_sent --- ...6_modify_column_table_account_statistic.ts | 21 +++++++++++++++++++ .../statistics/account_statistics.service.ts | 17 ++++++++++----- 2 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 migrations/20230817015916_modify_column_table_account_statistic.ts diff --git a/migrations/20230817015916_modify_column_table_account_statistic.ts b/migrations/20230817015916_modify_column_table_account_statistic.ts new file mode 100644 index 000000000..c346b7bf7 --- /dev/null +++ b/migrations/20230817015916_modify_column_table_account_statistic.ts @@ -0,0 +1,21 @@ +import { Knex } from 'knex'; + +export async function up(knex: Knex): Promise { + await knex.schema.alterTable('account_statistics', (table) => { + table.dropIndex('amount_sent'); + table.dropIndex('amount_received'); + table.dropIndex('tx_sent'); + table.dropIndex('gas_used'); + table.integer('tx_sent').alter(); + }); +} + +export async function down(knex: Knex): Promise { + await knex.schema.alterTable('account_statistics', (table) => { + table.index('amount_sent'); + table.index('amount_received'); + table.index('tx_sent'); + table.index('gas_used'); + table.bigint('tx_sent').alter(); + }); +} diff --git a/src/services/statistics/account_statistics.service.ts b/src/services/statistics/account_statistics.service.ts index e40d8acd7..dd97d64bf 100644 --- a/src/services/statistics/account_statistics.service.ts +++ b/src/services/statistics/account_statistics.service.ts @@ -338,10 +338,13 @@ export default class AccountStatisticsService extends BullableService { BigInt(0) ) .toString(); - const dayStatTxSent = dayStat.reduce( - (init: number, accStat: AccountStatistics) => init + accStat.tx_sent, - 0 - ); + const dayStatTxSent = dayStat + .reduce( + (init: bigint, accStat: AccountStatistics) => + init + BigInt(accStat.tx_sent), + BigInt(0) + ) + .toString(); const dayStatGasUsed = dayStat .reduce( (init: bigint, accStat: AccountStatistics) => @@ -372,7 +375,11 @@ export default class AccountStatisticsService extends BullableService { topTxSent.push({ address: stat.address, amount: stat.tx_sent, - percentage: (stat.tx_sent * 100) / dayStatTxSent, + percentage: Number( + BigNumber(stat.tx_sent) + .multipliedBy(100) + .dividedBy(BigNumber(dayStatTxSent)) + ), }); topGasUsed.push({ address: stat.address, From ef0f7dd3d42949eb16278c51b793631429c3f41c Mon Sep 17 00:00:00 2001 From: Phan Anh Tuan Date: Fri, 18 Aug 2023 14:45:32 +0700 Subject: [PATCH 20/20] fix: remove drop index in modify account statistic table --- ...0230817015916_modify_column_table_account_statistic.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/migrations/20230817015916_modify_column_table_account_statistic.ts b/migrations/20230817015916_modify_column_table_account_statistic.ts index c346b7bf7..0912a4d56 100644 --- a/migrations/20230817015916_modify_column_table_account_statistic.ts +++ b/migrations/20230817015916_modify_column_table_account_statistic.ts @@ -2,20 +2,12 @@ import { Knex } from 'knex'; export async function up(knex: Knex): Promise { await knex.schema.alterTable('account_statistics', (table) => { - table.dropIndex('amount_sent'); - table.dropIndex('amount_received'); - table.dropIndex('tx_sent'); - table.dropIndex('gas_used'); table.integer('tx_sent').alter(); }); } export async function down(knex: Knex): Promise { await knex.schema.alterTable('account_statistics', (table) => { - table.index('amount_sent'); - table.index('amount_received'); - table.index('tx_sent'); - table.index('gas_used'); table.bigint('tx_sent').alter(); }); }