From 260e991b8ba1d1302125d78f98e6b1a6f7b447c7 Mon Sep 17 00:00:00 2001 From: phucvinh57 Date: Wed, 22 Nov 2023 00:50:22 +0700 Subject: [PATCH] feat(logger): integrate with discord webhook --- .env.example | 10 +- package.json | 2 + src/Server.ts | 5 +- src/configs/env.ts | 3 +- src/configs/index.ts | 2 +- src/configs/logger.ts | 43 -------- src/configs/logger/discord.ts | 62 ++++++++++++ src/types/log.d.ts | 7 ++ src/utils/logger.ts | 51 +++++++++- yarn.lock | 178 +++++++++++++++++++++++++++++++++- 10 files changed, 305 insertions(+), 58 deletions(-) delete mode 100644 src/configs/logger.ts create mode 100644 src/configs/logger/discord.ts create mode 100644 src/types/log.d.ts diff --git a/.env.example b/.env.example index eb35678..85c5f19 100644 --- a/.env.example +++ b/.env.example @@ -7,12 +7,4 @@ CORS_WHITE_LIST=http://localhost:3000,http://localhost:8080 COOKIE_SECRET= JWT_SECRET= -# environment variables for elasticsearch and kibana docker container -ELASTIC_PASSWORD=abcdef -KIBANA_PASSWORD=abcdef -STACK_VERSION=8.7.0 -CLUSTER_NAME=docker-cluster -LICENSE=basic -ES_PORT=9200 -KIBANA_PORT=5601 -MEM_LIMIT=2073741824 # in bytes \ No newline at end of file +DISCORD_WEBHOOK_URL= \ No newline at end of file diff --git a/package.json b/package.json index bb08037..524a6dc 100644 --- a/package.json +++ b/package.json @@ -33,10 +33,12 @@ "@prisma/client": "^5.1.1", "@sinclair/typebox": "^0.31.1", "bcrypt": "^5.1.0", + "discord.js": "^14.14.1", "dotenv": "^16.3.1", "envalid": "^7.3.1", "fastify": "^4.21.0", "jsonwebtoken": "^9.0.1", + "moment-timezone": "^0.5.43", "prisma": "^5.1.1" }, "devDependencies": { diff --git a/src/Server.ts b/src/Server.ts index 2bac469..7bdb544 100644 --- a/src/Server.ts +++ b/src/Server.ts @@ -1,11 +1,12 @@ import fastify, { FastifyInstance } from 'fastify'; import type { FastifyCookieOptions } from '@fastify/cookie'; -import { CORS_WHITE_LIST, envs, loggerConfig, swaggerConfig, swaggerUIConfig } from '@configs'; +import { CORS_WHITE_LIST, envs, swaggerConfig, swaggerUIConfig } from '@configs'; import { apiPlugin, authPlugin } from './routes'; import { customErrorHandler } from '@handlers'; +import { logger } from '@utils'; export function createServer(config: ServerConfig): FastifyInstance { - const app = fastify({ logger: loggerConfig[envs.NODE_ENV] }); + const app = fastify({ logger }); app.register(import('@fastify/sensible')); app.register(import('@fastify/helmet')); diff --git a/src/configs/env.ts b/src/configs/env.ts index 98608c5..733d124 100644 --- a/src/configs/env.ts +++ b/src/configs/env.ts @@ -10,7 +10,8 @@ export const envs = cleanEnv(process.env, { }), JWT_SECRET: str(), COOKIE_SECRET: str(), - CORS_WHITE_LIST: str() + CORS_WHITE_LIST: str(), + DISCORD_WEBHOOK_URL: str() }); export const CORS_WHITE_LIST = envs.CORS_WHITE_LIST.split(','); diff --git a/src/configs/index.ts b/src/configs/index.ts index 66d836d..73cd64d 100644 --- a/src/configs/index.ts +++ b/src/configs/index.ts @@ -3,5 +3,5 @@ */ export * from './env'; -export * from './logger'; export * from './swagger'; +export * from './logger/discord'; diff --git a/src/configs/logger.ts b/src/configs/logger.ts deleted file mode 100644 index 9e2de60..0000000 --- a/src/configs/logger.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { FastifyError } from 'fastify'; -import { PinoLoggerOptions } from 'fastify/types/logger'; - -const errorSerialize = (err: FastifyError) => { - const isInternalServerError = !err.statusCode || err.statusCode >= 500; - return { - type: err.name, - stack: isInternalServerError && err.stack ? err.stack : 'null', - message: err.message, - statusCode: err.statusCode - }; -}; - -export const loggerConfig: Record = { - development: { - transport: { - target: 'pino-pretty', - options: { - translateTime: 'dd/mm/yy HH:MM:ss', - ignore: 'pid,hostname' - } - }, - serializers: { err: errorSerialize } - }, - test: { - serializers: { err: errorSerialize } - }, - // In production, save log to files. - // Can write a plugin to use centralize logging services, if need - production: { - transport: { - targets: ['info', 'warn', 'error', 'fatal'].map((logLevel) => ({ - target: 'pino/file', - level: logLevel, - options: { - destination: process.cwd() + `/logs/${logLevel}.log`, - mkdir: true - } - })) - }, - serializers: { err: errorSerialize } - } -}; diff --git a/src/configs/logger/discord.ts b/src/configs/logger/discord.ts new file mode 100644 index 0000000..e20616c --- /dev/null +++ b/src/configs/logger/discord.ts @@ -0,0 +1,62 @@ +import { envs } from '@configs/env'; +import { WebhookClient } from 'discord.js'; +import moment from 'moment-timezone'; +import build from 'pino-abstract-transport'; + +export default function (options: DiscordLogOptions) { + const threadId = options.threadId; + + const webhookClient = new WebhookClient({ + url: options.webhookUrl + }); + + const fields = options.ignore ? options.ignore.split(',') : null; + const ignoreFields = fields && fields.length > 0 ? fields.map((field) => field.trim()) : null; + + return build(async function (source) { + for await (const obj of source) { + const level = obj.level; + let levelName: LogLevel; + if (level >= 60) levelName = 'FATAL'; + else if (level >= 50) levelName = 'ERROR'; + else if (level >= 40) levelName = 'WARN'; + else levelName = 'INFO'; + + if (ignoreFields) ignoreFields.forEach((field) => delete obj[field]); + const time = moment.tz('Asia/Ho_Chi_Minh').format('HH:mm:ss DD/MM/YYYY'); + const payload = JSON.stringify(obj, null, 2); + + const header = `🪲 [**${envs.NODE_ENV.toUpperCase()}**] [${levelName}] [${time}] 🪲\n`; + const beginBody = '```bash\n'; + const endBody = '\n```'; + if (payload.length > 1800) { + // Split message + const subMessages: string[] = []; + let i = 0; + while (i * 1800 < payload.length) { + subMessages.push(payload.substring(i * 1800, (i + 1) * 1800)); + i++; + } + for (const message of subMessages) { + try { + await webhookClient.send({ + threadId, + content: header + beginBody + message + endBody + }); + } catch (e) { + // Do nothing + } + } + } else { + webhookClient + .send({ + threadId, + content: header + beginBody + payload + endBody + }) + .catch(() => { + // Do nothing + }); + } + } + }); +} diff --git a/src/types/log.d.ts b/src/types/log.d.ts new file mode 100644 index 0000000..bc5fbcd --- /dev/null +++ b/src/types/log.d.ts @@ -0,0 +1,7 @@ +type LogLevel = 'INFO' | 'WARN' | 'DEBUG' | 'ERROR' | 'FATAL'; + +type DiscordLogOptions = { + webhookUrl: string; + threadId?: string; + ignore?: string; +}; diff --git a/src/utils/logger.ts b/src/utils/logger.ts index 7ad5ec1..eb55985 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -1,4 +1,53 @@ -import { envs, loggerConfig } from '@configs'; +import { FastifyError } from 'fastify'; +import { PinoLoggerOptions } from 'fastify/types/logger'; +import { envs } from '@configs'; import pino from 'pino'; +const errorSerialize = (err: FastifyError) => { + const isInternalServerError = !err.statusCode || err.statusCode === 500; + return { + type: err.name, + stack: isInternalServerError && err.stack ? err.stack : 'null', + message: err.message, + statusCode: err.statusCode + }; +}; + +const fileLogTargets = ['info', 'warn', 'error', 'fatal'].map((logLevel) => ({ + target: 'pino/file', + level: logLevel, + options: { + destination: process.cwd() + `/logs/${logLevel}.log`, + mkdir: true + } +})); +const pinoLogTarget = { + target: 'pino-pretty', + level: 'info', + options: { + translateTime: 'dd/mm/yy HH:MM:ss', + ignore: 'pid,hostname' + } +}; +const discordLogTarget = { + target: '../configs/logger/discord', + level: 'warn', + options: { + webhookUrl: envs.DISCORD_WEBHOOK_URL, + ignore: 'time, pid, hostname' + } as DiscordLogOptions +}; + +const loggerConfig: Record = { + development: { + transport: { targets: [pinoLogTarget, discordLogTarget] }, + serializers: { err: errorSerialize } + }, + production: { + transport: { targets: [discordLogTarget, ...fileLogTargets] }, + serializers: { err: errorSerialize } + }, + test: { serializers: { err: errorSerialize } } +}; + export const logger = pino(loggerConfig[envs.NODE_ENV]); diff --git a/yarn.lock b/yarn.lock index 2d898e7..3afdbec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -308,6 +308,71 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@discordjs/builders@^1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.7.0.tgz#e2478c7e55b0f4c40837edb8f102bce977323a37" + integrity sha512-GDtbKMkg433cOZur8Dv6c25EHxduNIBsxeHrsRoIM8+AwmEZ8r0tEpckx/sHwTLwQPOF3e2JWloZh9ofCaMfAw== + dependencies: + "@discordjs/formatters" "^0.3.3" + "@discordjs/util" "^1.0.2" + "@sapphire/shapeshift" "^3.9.3" + discord-api-types "0.37.61" + fast-deep-equal "^3.1.3" + ts-mixer "^6.0.3" + tslib "^2.6.2" + +"@discordjs/collection@1.5.3": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-1.5.3.tgz#5a1250159ebfff9efa4f963cfa7e97f1b291be18" + integrity sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ== + +"@discordjs/collection@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-2.0.0.tgz#409b80c74eb8486cc4ee6a9b83426aaff1380f8c" + integrity sha512-YTWIXLrf5FsrLMycpMM9Q6vnZoR/lN2AWX23/Cuo8uOOtS8eHB2dyQaaGnaF8aZPYnttf2bkLMcXn/j6JUOi3w== + +"@discordjs/formatters@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@discordjs/formatters/-/formatters-0.3.3.tgz#b16fdd79bb819680ab7e519193004e9dc124a749" + integrity sha512-wTcI1Q5cps1eSGhl6+6AzzZkBBlVrBdc9IUhJbijRgVjCNIIIZPgqnUj3ntFODsHrdbGU8BEG9XmDQmgEEYn3w== + dependencies: + discord-api-types "0.37.61" + +"@discordjs/rest@^2.1.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-2.2.0.tgz#f4ec00d3faff965c00a879b7e87bb4b6f4446966" + integrity sha512-nXm9wT8oqrYFRMEqTXQx9DUTeEtXUDMmnUKIhZn6O2EeDY9VCdwj23XCPq7fkqMPKdF7ldAfeVKyxxFdbZl59A== + dependencies: + "@discordjs/collection" "^2.0.0" + "@discordjs/util" "^1.0.2" + "@sapphire/async-queue" "^1.5.0" + "@sapphire/snowflake" "^3.5.1" + "@vladfrangu/async_event_emitter" "^2.2.2" + discord-api-types "0.37.61" + magic-bytes.js "^1.5.0" + tslib "^2.6.2" + undici "5.27.2" + +"@discordjs/util@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@discordjs/util/-/util-1.0.2.tgz#dc1896d764452b1bd9707eb9aa99ccfbb30bd1c0" + integrity sha512-IRNbimrmfb75GMNEjyznqM1tkI7HrZOf14njX7tCAAUetyZM1Pr8hX/EK2lxBCOgWDRmigbp24fD1hdMfQK5lw== + +"@discordjs/ws@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@discordjs/ws/-/ws-1.0.2.tgz#3933b12d4686aabf6a95dfe5fb6e744342a661d1" + integrity sha512-+XI82Rm2hKnFwAySXEep4A7Kfoowt6weO6381jgW+wVdTpMS/56qCvoXyFRY0slcv7c/U8My2PwIB2/wEaAh7Q== + dependencies: + "@discordjs/collection" "^2.0.0" + "@discordjs/rest" "^2.1.0" + "@discordjs/util" "^1.0.2" + "@sapphire/async-queue" "^1.5.0" + "@types/ws" "^8.5.9" + "@vladfrangu/async_event_emitter" "^2.2.2" + discord-api-types "0.37.61" + tslib "^2.6.2" + ws "^8.14.2" + "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -354,6 +419,11 @@ ajv-formats "^2.1.1" fast-uri "^2.0.0" +"@fastify/busboy@^2.0.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.0.tgz#0709e9f4cb252351c609c6e6d8d6779a8d25edff" + integrity sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA== + "@fastify/cookie@^9.0.4": version "9.0.4" resolved "https://registry.yarnpkg.com/@fastify/cookie/-/cookie-9.0.4.tgz#d98f527f2bae7e52bbf585699d70e203877f1e08" @@ -789,6 +859,24 @@ resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.1.1.tgz#9c26d209f93a563e048eab63b1976f222f1707d0" integrity sha512-NV/4nVNWFZSJCCIA3HIFJbbDKO/NARc9ej0tX5S9k2EVbkrFJC4Xt9b0u4rNZWL4V+F5LAjvta8vzEUw0rw+HA== +"@sapphire/async-queue@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.5.0.tgz#2f255a3f186635c4fb5a2381e375d3dfbc5312d8" + integrity sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA== + +"@sapphire/shapeshift@^3.9.3": + version "3.9.3" + resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.9.3.tgz#89d26713044bc21cc5e0845e61a8a328ca3c1a84" + integrity sha512-WzKJSwDYloSkHoBbE8rkRW8UNKJiSRJ/P8NqJ5iVq7U2Yr/kriIBx2hW+wj2Z5e5EnXL1hgYomgaFsdK6b+zqQ== + dependencies: + fast-deep-equal "^3.1.3" + lodash "^4.17.21" + +"@sapphire/snowflake@3.5.1", "@sapphire/snowflake@^3.5.1": + version "3.5.1" + resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.5.1.tgz#254521c188b49e8b2d4cc048b475fb2b38737fec" + integrity sha512-BxcYGzgEsdlG0dKAyOm0ehLGm2CafIrfQTZGWgkfKYbj+pNNsorZ7EotuZukc2MT70E0UbppVbtpBrqpzVzjNA== + "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" @@ -944,6 +1032,20 @@ resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== +"@types/ws@8.5.9": + version "8.5.9" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.9.tgz#384c489f99c83225a53f01ebc3eddf3b8e202a8c" + integrity sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg== + dependencies: + "@types/node" "*" + +"@types/ws@^8.5.9": + version "8.5.10" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787" + integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== + dependencies: + "@types/node" "*" + "@types/yargs-parser@*": version "21.0.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" @@ -1042,6 +1144,11 @@ "@typescript-eslint/types" "6.2.1" eslint-visitor-keys "^3.4.1" +"@vladfrangu/async_event_emitter@^2.2.2": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.2.tgz#84c5a3f8d648842cec5cc649b88df599af32ed88" + integrity sha512-HIzRG7sy88UZjBJamssEczH5q7t5+axva19UbZLO6u0ySbYPrwzWiXBcC0WuHyhKKoeCyneH+FvYzKQq/zTtkQ== + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -1665,6 +1772,31 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +discord-api-types@0.37.61: + version "0.37.61" + resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.61.tgz#9dd8e58c624237e6f1b23be2d29579af268b8c5b" + integrity sha512-o/dXNFfhBpYHpQFdT6FWzeO7pKc838QeeZ9d91CfVAtpr5XLK4B/zYxQbYgPdoMiTDvJfzcsLW5naXgmHGDNXw== + +discord.js@^14.14.1: + version "14.14.1" + resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.14.1.tgz#9a2bea23bba13819705ab87612837610abce9ee3" + integrity sha512-/hUVzkIerxKHyRKopJy5xejp4MYKDPTszAnpYxzVVv4qJYf+Tkt+jnT2N29PIPschicaEEpXwF2ARrTYHYwQ5w== + dependencies: + "@discordjs/builders" "^1.7.0" + "@discordjs/collection" "1.5.3" + "@discordjs/formatters" "^0.3.3" + "@discordjs/rest" "^2.1.0" + "@discordjs/util" "^1.0.2" + "@discordjs/ws" "^1.0.2" + "@sapphire/snowflake" "3.5.1" + "@types/ws" "8.5.9" + discord-api-types "0.37.61" + fast-deep-equal "3.1.3" + lodash.snakecase "4.1.1" + tslib "2.6.2" + undici "5.27.2" + ws "8.14.2" + doctrine@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" @@ -1948,7 +2080,7 @@ fast-decode-uri-component@^1.0.1: resolved "https://registry.yarnpkg.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz#46f8b6c22b30ff7a81357d4f59abfae938202543" integrity sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg== -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: +fast-deep-equal@3.1.3, fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -3062,6 +3194,11 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.snakecase@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" + integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== + lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -3091,6 +3228,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +magic-bytes.js@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/magic-bytes.js/-/magic-bytes.js-1.5.0.tgz#f5531ca53e1c8dab5692b8dcfb360f7ca6c6b6bc" + integrity sha512-wJkXvutRbNWcc37tt5j1HyOK1nosspdh3dj6LUYYAvF6JYNqs53IfRvK9oEpcwiDA1NdoIi64yAMfdivPeVAyw== + make-dir@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -3218,6 +3360,18 @@ mnemonist@0.39.5: dependencies: obliterator "^2.0.1" +moment-timezone@^0.5.43: + version "0.5.43" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.43.tgz#3dd7f3d0c67f78c23cd1906b9b2137a09b3c4790" + integrity sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ== + dependencies: + moment "^2.29.4" + +moment@^2.29.4: + version "2.29.4" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" + integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -4152,6 +4306,11 @@ ts-jest@^29.1.1: semver "^7.5.3" yargs-parser "^21.0.1" +ts-mixer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.3.tgz#69bd50f406ff39daa369885b16c77a6194c7cae6" + integrity sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ== + ts-node-dev@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-2.0.0.tgz#bdd53e17ab3b5d822ef519928dc6b4a7e0f13065" @@ -4223,6 +4382,11 @@ tslib@2.3.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== +tslib@2.6.2, tslib@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + tslib@^2.1.0: version "2.6.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.1.tgz#fd8c9a0ff42590b25703c0acb3de3d3f4ede0410" @@ -4263,6 +4427,13 @@ typescript@^5.1.6: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== +undici@5.27.2: + version "5.27.2" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.27.2.tgz#a270c563aea5b46cc0df2550523638c95c5d4411" + integrity sha512-iS857PdOEy/y3wlM3yRp+6SNQQ6xU0mmZcwRSriqk+et/cwWAtwmIGf6WkoDN2EK/AMdCO/dfXzIwi+rFMrjjQ== + dependencies: + "@fastify/busboy" "^2.0.0" + update-browserslist-db@^1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" @@ -4367,6 +4538,11 @@ write-file-atomic@^4.0.2: imurmurhash "^0.1.4" signal-exit "^3.0.7" +ws@8.14.2, ws@^8.14.2: + version "8.14.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" + integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== + xtend@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"