@tradejs/cli 3.1.10 → 3.1.11-beta.216
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +131 -28
- package/dist/lib/runEnvironment.js +18 -2
- package/dist/lib/runtimeEvidenceDeployment.js +9 -5
- package/dist/lib/runtimeRedis.js +13 -0
- package/dist/scripts/backtest.js +6 -3
- package/dist/scripts/replay.js +43 -12
- package/dist/scripts/replayRunner.js +43 -12
- package/dist/scripts/runtimeEvidence.js +4 -2
- package/dist/scripts/signals.js +113 -26
- package/dist/scripts/signalsDaemon.js +113 -26
- package/package.json +7 -7
|
@@ -46,7 +46,7 @@ var import_cli2 = require("@tradejs/node/cli");
|
|
|
46
46
|
var import_async3 = require("@tradejs/core/async");
|
|
47
47
|
var import_strategies5 = require("@tradejs/core/strategies");
|
|
48
48
|
var import_constants3 = require("@tradejs/core/constants");
|
|
49
|
-
var
|
|
49
|
+
var import_time3 = require("@tradejs/core/time");
|
|
50
50
|
var import_logger4 = require("@tradejs/infra/logger");
|
|
51
51
|
var import_runtimeHeartbeats = require("@tradejs/infra/runtimeHeartbeats");
|
|
52
52
|
var import_runtimeStrategies2 = require("@tradejs/node/runtimeStrategies");
|
|
@@ -63,6 +63,38 @@ var getClosedCandlesForInterval = (candles, currentTimestamp, intervalMs) => {
|
|
|
63
63
|
};
|
|
64
64
|
var alignSymbolWithBtcReference = (symbolCandles, btcCandles) => (0, import_indicators.alignSortedCandlesByTimestamp)(symbolCandles, btcCandles);
|
|
65
65
|
|
|
66
|
+
// src/lib/runtimeRedis.ts
|
|
67
|
+
var import_redis = require("@tradejs/infra/redis");
|
|
68
|
+
var import_time = require("@tradejs/core/time");
|
|
69
|
+
var import_runtimeStrategyConfigs = require("@tradejs/infra/runtimeStrategyConfigs");
|
|
70
|
+
var isRuntimeTradeRecord = (value) => {
|
|
71
|
+
if (!value || typeof value !== "object") {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
const record = value;
|
|
75
|
+
return typeof record.orderId === "string" && typeof record.strategy === "string" && typeof record.symbol === "string" && typeof record.entryTimestamp === "number" && typeof record.entryPrice === "number" && typeof record.qty === "number" && (record.direction === "LONG" || record.direction === "SHORT");
|
|
76
|
+
};
|
|
77
|
+
var loadRuntimeActiveTradeOrderIds = async (userName) => {
|
|
78
|
+
const keys = await (0, import_redis.getKeys)(import_redis.redisKeys.runtimeActiveTrades(userName));
|
|
79
|
+
const refs = await Promise.all(keys.map((key) => (0, import_redis.getData)(key, null)));
|
|
80
|
+
return new Set(
|
|
81
|
+
refs.map(
|
|
82
|
+
(ref) => ref && typeof ref === "object" && "orderId" in ref && typeof ref.orderId === "string" && ref.orderId.trim() ? ref.orderId.trim() : null
|
|
83
|
+
).filter((orderId) => orderId != null)
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
var loadRuntimeActiveTrades = async (userName) => {
|
|
87
|
+
const orderIds = await loadRuntimeActiveTradeOrderIds(userName);
|
|
88
|
+
const trades = await Promise.all(
|
|
89
|
+
[...orderIds].map(
|
|
90
|
+
(orderId) => (0, import_redis.getData)(import_redis.redisKeys.runtimeTrade(userName, orderId), null)
|
|
91
|
+
)
|
|
92
|
+
);
|
|
93
|
+
return trades.filter(
|
|
94
|
+
(trade) => isRuntimeTradeRecord(trade) && trade.status === "active"
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
|
|
66
98
|
// src/lib/runFormatting.ts
|
|
67
99
|
var import_date_fns = require("date-fns");
|
|
68
100
|
var ListIt = require("list-it");
|
|
@@ -3744,7 +3776,7 @@ var import_logger = require("@tradejs/infra/logger");
|
|
|
3744
3776
|
|
|
3745
3777
|
// src/lib/runtimeSignalsStorage.ts
|
|
3746
3778
|
var import_constants2 = require("@tradejs/core/constants");
|
|
3747
|
-
var
|
|
3779
|
+
var import_time2 = require("@tradejs/core/time");
|
|
3748
3780
|
var SECONDS_PER_DAY = 86400;
|
|
3749
3781
|
var RUNTIME_SIGNAL_RETENTION_DAYS_ENV = "RUNTIME_SIGNAL_RETENTION_DAYS";
|
|
3750
3782
|
var DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS = import_constants2.TTL_3D;
|
|
@@ -3893,7 +3925,7 @@ var logStrategySkipStats = (runtimeStrategies, strategyStats) => {
|
|
|
3893
3925
|
};
|
|
3894
3926
|
|
|
3895
3927
|
// src/lib/signals/evaluations.ts
|
|
3896
|
-
var
|
|
3928
|
+
var import_redis2 = require("@tradejs/infra/redis");
|
|
3897
3929
|
|
|
3898
3930
|
// src/lib/runtimeLineage.ts
|
|
3899
3931
|
var import_node_child_process = require("child_process");
|
|
@@ -4245,7 +4277,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
|
|
|
4245
4277
|
if (evaluations.length === 0) return;
|
|
4246
4278
|
const evaluationsByDay = /* @__PURE__ */ new Map();
|
|
4247
4279
|
for (const evaluation of evaluations) {
|
|
4248
|
-
const dayKey = (0,
|
|
4280
|
+
const dayKey = (0, import_time2.getRuntimeStorageDayKey)(evaluation.timestamp);
|
|
4249
4281
|
const dayEvaluations = evaluationsByDay.get(dayKey) ?? [];
|
|
4250
4282
|
dayEvaluations.push(evaluation);
|
|
4251
4283
|
evaluationsByDay.set(dayKey, dayEvaluations);
|
|
@@ -4257,7 +4289,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
|
|
|
4257
4289
|
runtimeLineageScopeCacheDayKey = dayKey;
|
|
4258
4290
|
runtimeLineageScopeCacheLoaded = false;
|
|
4259
4291
|
}
|
|
4260
|
-
const lineageBucket =
|
|
4292
|
+
const lineageBucket = import_redis2.redisKeys.runtimeLineageScopeBucket(
|
|
4261
4293
|
dayEvaluations[0].userName,
|
|
4262
4294
|
dayKey
|
|
4263
4295
|
);
|
|
@@ -4265,7 +4297,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
|
|
|
4265
4297
|
(evaluation) => evaluation.runtimeLineage != null
|
|
4266
4298
|
);
|
|
4267
4299
|
if (lineageEvaluations.length > 0 && !runtimeLineageScopeCacheLoaded) {
|
|
4268
|
-
const storedScopes = await (0,
|
|
4300
|
+
const storedScopes = await (0, import_redis2.getHashJsonValues)(lineageBucket);
|
|
4269
4301
|
for (const scope of storedScopes) {
|
|
4270
4302
|
const lineageField = buildLineageField(scope);
|
|
4271
4303
|
runtimeLineageScopeCache.set(`${lineageBucket}:${lineageField}`, scope);
|
|
@@ -4306,7 +4338,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
|
|
|
4306
4338
|
lineageWrites.set(lineageField, scope);
|
|
4307
4339
|
}
|
|
4308
4340
|
if (lineageWrites.size > 0) {
|
|
4309
|
-
await (0,
|
|
4341
|
+
await (0, import_redis2.setHashJsonFields)(
|
|
4310
4342
|
lineageBucket,
|
|
4311
4343
|
[...lineageWrites].map(([field, data]) => ({ field, data })),
|
|
4312
4344
|
{
|
|
@@ -4318,7 +4350,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
|
|
|
4318
4350
|
const statsIncrements = /* @__PURE__ */ new Map();
|
|
4319
4351
|
for (const evaluation of dayEvaluations) {
|
|
4320
4352
|
if (shouldStoreDetailedRuntimeSignalEvaluation(evaluation)) {
|
|
4321
|
-
const bucket =
|
|
4353
|
+
const bucket = import_redis2.redisKeys.runtimeSignalEvaluationBucket(
|
|
4322
4354
|
evaluation.userName,
|
|
4323
4355
|
dayKey,
|
|
4324
4356
|
evaluation.strategy
|
|
@@ -4330,7 +4362,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
|
|
|
4330
4362
|
});
|
|
4331
4363
|
detailedWrites.set(bucket, entries);
|
|
4332
4364
|
}
|
|
4333
|
-
const statsBucket =
|
|
4365
|
+
const statsBucket = import_redis2.redisKeys.runtimeSignalEvaluationStatsBucket(
|
|
4334
4366
|
evaluation.userName,
|
|
4335
4367
|
dayKey,
|
|
4336
4368
|
evaluation.strategy,
|
|
@@ -4346,12 +4378,12 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
|
|
|
4346
4378
|
}
|
|
4347
4379
|
await Promise.all([
|
|
4348
4380
|
...[...detailedWrites].map(
|
|
4349
|
-
([bucket, entries]) => (0,
|
|
4381
|
+
([bucket, entries]) => (0, import_redis2.setHashJsonFields)(bucket, entries, {
|
|
4350
4382
|
expire: runtimeSignalRetentionTtl
|
|
4351
4383
|
})
|
|
4352
4384
|
),
|
|
4353
4385
|
...[...statsIncrements].map(
|
|
4354
|
-
([bucket, increments]) => (0,
|
|
4386
|
+
([bucket, increments]) => (0, import_redis2.incrHashFields)(bucket, increments, {
|
|
4355
4387
|
expire: runtimeSignalRetentionTtl
|
|
4356
4388
|
})
|
|
4357
4389
|
)
|
|
@@ -4367,9 +4399,9 @@ var createRuntimeSignalEvaluationBuffer = () => {
|
|
|
4367
4399
|
const runtimeSignalRetentionTtl = getRuntimeSignalRetentionTtlSeconds();
|
|
4368
4400
|
const signalRefsByBucket = /* @__PURE__ */ new Map();
|
|
4369
4401
|
for (const { userName, signal } of pendingSignals) {
|
|
4370
|
-
const bucket =
|
|
4402
|
+
const bucket = import_redis2.redisKeys.runtimeSignalBucket(
|
|
4371
4403
|
userName,
|
|
4372
|
-
(0,
|
|
4404
|
+
(0, import_time2.getRuntimeStorageDayKey)(signal.timestamp),
|
|
4373
4405
|
signal.strategy
|
|
4374
4406
|
);
|
|
4375
4407
|
const entries = signalRefsByBucket.get(bucket) ?? [];
|
|
@@ -4381,14 +4413,14 @@ var createRuntimeSignalEvaluationBuffer = () => {
|
|
|
4381
4413
|
}
|
|
4382
4414
|
await Promise.all([
|
|
4383
4415
|
...pendingSignals.map(
|
|
4384
|
-
({ signal }) => (0,
|
|
4385
|
-
|
|
4416
|
+
({ signal }) => (0, import_redis2.setData)(
|
|
4417
|
+
import_redis2.redisKeys.storeSignal(signal.symbol, signal.signalId),
|
|
4386
4418
|
toStoredRuntimeSignal(signal),
|
|
4387
4419
|
{ expire: runtimeSignalRetentionTtl }
|
|
4388
4420
|
)
|
|
4389
4421
|
),
|
|
4390
4422
|
...[...signalRefsByBucket].map(
|
|
4391
|
-
([bucket, entries]) => (0,
|
|
4423
|
+
([bucket, entries]) => (0, import_redis2.setHashJsonFields)(bucket, entries, {
|
|
4392
4424
|
expire: runtimeSignalRetentionTtl
|
|
4393
4425
|
})
|
|
4394
4426
|
)
|
|
@@ -4627,9 +4659,9 @@ var import_candles = require("@tradejs/infra/timescale/candles");
|
|
|
4627
4659
|
|
|
4628
4660
|
// src/lib/marketData/klineEvents.ts
|
|
4629
4661
|
var import_ioredis = __toESM(require("ioredis"));
|
|
4630
|
-
var
|
|
4662
|
+
var import_redis3 = require("@tradejs/infra/redis");
|
|
4631
4663
|
var MARKET_KLINE_CHANNEL = "tradejs:market:kline";
|
|
4632
|
-
var publishMarketKlineEvent = (event) => (0,
|
|
4664
|
+
var publishMarketKlineEvent = (event) => (0, import_redis3.publishData)(MARKET_KLINE_CHANNEL, event);
|
|
4633
4665
|
|
|
4634
4666
|
// src/lib/signals/klineFeed.ts
|
|
4635
4667
|
var FLUSH_DELAY_MS = 100;
|
|
@@ -5035,6 +5067,23 @@ var createSignalsTickerEvaluator = ({
|
|
|
5035
5067
|
// src/lib/signals/configuredScopes.ts
|
|
5036
5068
|
var sorted = (values) => values ? [...values].sort() : [];
|
|
5037
5069
|
var formatConfiguredStrategyIdentity = (strategy) => `${strategy.strategyName}@v${strategy.version}[${strategy.controlState}]`;
|
|
5070
|
+
var getConfiguredScopeActiveSymbols = ({
|
|
5071
|
+
trades,
|
|
5072
|
+
deploymentId,
|
|
5073
|
+
strategyNames,
|
|
5074
|
+
universe,
|
|
5075
|
+
accountId,
|
|
5076
|
+
interval
|
|
5077
|
+
}) => {
|
|
5078
|
+
const strategyNameSet = new Set(strategyNames);
|
|
5079
|
+
return [
|
|
5080
|
+
...new Set(
|
|
5081
|
+
trades.filter(
|
|
5082
|
+
(trade) => trade.status === "active" && trade.deploymentId === deploymentId && strategyNameSet.has(trade.strategy) && (trade.universe ?? "crypto") === universe && (!trade.accountId || trade.accountId === accountId) && (!trade.interval || String(trade.interval) === String(interval))
|
|
5083
|
+
).map(({ symbol }) => symbol)
|
|
5084
|
+
)
|
|
5085
|
+
].sort();
|
|
5086
|
+
};
|
|
5038
5087
|
var buildConfiguredSignalsScopes = ({
|
|
5039
5088
|
connectorName,
|
|
5040
5089
|
deployment,
|
|
@@ -5051,21 +5100,30 @@ var buildConfiguredSignalsScopes = ({
|
|
|
5051
5100
|
});
|
|
5052
5101
|
const groups = /* @__PURE__ */ new Map();
|
|
5053
5102
|
for (const strategy of strategies) {
|
|
5103
|
+
const selectionIdentity = JSON.stringify({
|
|
5104
|
+
tickers: sorted(strategy.selection?.tickers)
|
|
5105
|
+
});
|
|
5054
5106
|
const baseKey = [
|
|
5055
5107
|
connectorName,
|
|
5056
5108
|
strategy.universe,
|
|
5057
5109
|
strategy.accountId ?? "default",
|
|
5058
|
-
strategy.interval
|
|
5110
|
+
strategy.interval,
|
|
5111
|
+
selectionIdentity
|
|
5059
5112
|
].join(":");
|
|
5060
5113
|
const group = groups.get(baseKey) ?? {
|
|
5061
5114
|
scope: {
|
|
5062
5115
|
connectorName,
|
|
5063
5116
|
universe: strategy.universe,
|
|
5064
5117
|
accountId: strategy.accountId,
|
|
5065
|
-
interval: strategy.interval
|
|
5118
|
+
interval: strategy.interval,
|
|
5119
|
+
strategyNames: [],
|
|
5120
|
+
...strategy.selection ? {
|
|
5121
|
+
selection: { tickers: [...strategy.selection.tickers] }
|
|
5122
|
+
} : {}
|
|
5066
5123
|
},
|
|
5067
5124
|
strategyIdentities: []
|
|
5068
5125
|
};
|
|
5126
|
+
group.scope.strategyNames.push(strategy.strategyName);
|
|
5069
5127
|
group.strategyIdentities.push(formatConfiguredStrategyIdentity(strategy));
|
|
5070
5128
|
groups.set(baseKey, group);
|
|
5071
5129
|
}
|
|
@@ -5073,7 +5131,10 @@ var buildConfiguredSignalsScopes = ({
|
|
|
5073
5131
|
key: [baseKey, deploymentIdentity, ...group.strategyIdentities.sort()].join(
|
|
5074
5132
|
":"
|
|
5075
5133
|
),
|
|
5076
|
-
scope:
|
|
5134
|
+
scope: {
|
|
5135
|
+
...group.scope,
|
|
5136
|
+
strategyNames: [...group.scope.strategyNames].sort()
|
|
5137
|
+
}
|
|
5077
5138
|
}));
|
|
5078
5139
|
};
|
|
5079
5140
|
|
|
@@ -5186,6 +5247,10 @@ var createSignalsRunner = (config2) => {
|
|
|
5186
5247
|
interval: runtimeInterval,
|
|
5187
5248
|
intervalMs: runtimeIntervalMs,
|
|
5188
5249
|
deployment,
|
|
5250
|
+
...scope.strategyNames ? { strategyNames: [...scope.strategyNames] } : {},
|
|
5251
|
+
...scope.selection ? {
|
|
5252
|
+
selection: { tickers: [...scope.selection.tickers] }
|
|
5253
|
+
} : {},
|
|
5189
5254
|
startedAt: Date.now(),
|
|
5190
5255
|
marketConnector,
|
|
5191
5256
|
btcReferences,
|
|
@@ -5215,13 +5280,31 @@ var createSignalsRunner = (config2) => {
|
|
|
5215
5280
|
btcReferences,
|
|
5216
5281
|
lifecycle,
|
|
5217
5282
|
interval: interval2,
|
|
5218
|
-
intervalMs
|
|
5283
|
+
intervalMs,
|
|
5284
|
+
strategyNames,
|
|
5285
|
+
selection
|
|
5219
5286
|
} = session;
|
|
5220
5287
|
const universe = sessionUniverse ?? marketConnector.universe ?? "crypto";
|
|
5288
|
+
const selectedActiveSymbols = !config2.tickers && selection?.tickers?.length && strategyNames?.length ? getConfiguredScopeActiveSymbols({
|
|
5289
|
+
trades: await loadRuntimeActiveTrades(config2.userName),
|
|
5290
|
+
deploymentId: deployment?.id ?? "",
|
|
5291
|
+
strategyNames,
|
|
5292
|
+
universe,
|
|
5293
|
+
accountId,
|
|
5294
|
+
interval: interval2
|
|
5295
|
+
}) : [];
|
|
5296
|
+
const selectedTickers = selection?.tickers ? [.../* @__PURE__ */ new Set([...selection.tickers, ...selectedActiveSymbols])] : void 0;
|
|
5297
|
+
if (selectedActiveSymbols.length) {
|
|
5298
|
+
import_logger4.logger.info(
|
|
5299
|
+
import_chalk7.default.gray(
|
|
5300
|
+
`strategy selection retained active symbols: ${selectedActiveSymbols.join(", ")}`
|
|
5301
|
+
)
|
|
5302
|
+
);
|
|
5303
|
+
}
|
|
5221
5304
|
const tickers = await timeOperation2("tickers load", () => {
|
|
5222
5305
|
const baseArgs = [
|
|
5223
5306
|
marketConnector,
|
|
5224
|
-
config2.tickers || deployment?.tickers?.join(","),
|
|
5307
|
+
config2.tickers || selectedTickers?.join(",") || deployment?.tickers?.join(","),
|
|
5225
5308
|
config2.exclude,
|
|
5226
5309
|
config2.tickersLimit,
|
|
5227
5310
|
config2.chunk
|
|
@@ -5248,8 +5331,8 @@ var createSignalsRunner = (config2) => {
|
|
|
5248
5331
|
}
|
|
5249
5332
|
const projectConfig = await (0, import_cli2.loadTradejsConfig)(projectRoot);
|
|
5250
5333
|
projectHooks = projectConfig.hooks;
|
|
5251
|
-
const currentTimestamp = (0,
|
|
5252
|
-
const preloadStart = (0,
|
|
5334
|
+
const currentTimestamp = (0, import_time3.getTimestamp)();
|
|
5335
|
+
const preloadStart = (0, import_time3.getTimestamp)(import_constants3.SIGNALS_CLI_PRELOAD_DAYS);
|
|
5253
5336
|
if (!config2.cacheOnly && session.klineFeed) {
|
|
5254
5337
|
const expectedSymbols = universe === "crypto" ? [.../* @__PURE__ */ new Set([...tickers, "BTCUSDT", "ETHUSDT"])] : tickers;
|
|
5255
5338
|
session.klineFeed.setSubscriptions(expectedSymbols);
|
|
@@ -5364,7 +5447,7 @@ var createSignalsRunner = (config2) => {
|
|
|
5364
5447
|
const primaryEthByTimestamp = new Map(
|
|
5365
5448
|
primaryEthClosedData.map((candle) => [candle.timestamp, candle])
|
|
5366
5449
|
);
|
|
5367
|
-
const
|
|
5450
|
+
const configuredRuntimeStrategies = await (0, import_runtimeStrategies.loadResolvedRuntimeStrategies)({
|
|
5368
5451
|
userName: config2.userName,
|
|
5369
5452
|
projectRoot,
|
|
5370
5453
|
deploymentId: deployment?.id ?? "",
|
|
@@ -5372,6 +5455,10 @@ var createSignalsRunner = (config2) => {
|
|
|
5372
5455
|
accountId,
|
|
5373
5456
|
interval: interval2
|
|
5374
5457
|
});
|
|
5458
|
+
const selectedStrategyNames = strategyNames?.length ? new Set(strategyNames) : null;
|
|
5459
|
+
const runtimeStrategies = selectedStrategyNames ? configuredRuntimeStrategies.filter(
|
|
5460
|
+
({ strategyName }) => selectedStrategyNames.has(strategyName)
|
|
5461
|
+
) : configuredRuntimeStrategies;
|
|
5375
5462
|
if (!runtimeStrategies.length) {
|
|
5376
5463
|
lifecycle.clear();
|
|
5377
5464
|
import_logger4.logger.warn(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tradejs/cli",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.11-beta.216",
|
|
4
4
|
"description": "Official CLI for the TradeJS TypeScript framework: infra setup, backtests, signals, bots, and ML workflows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tradejs",
|
|
@@ -32,13 +32,13 @@
|
|
|
32
32
|
"tradejs": "dist/cli.js"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@tradejs/connectors": "^3.1.
|
|
36
|
-
"@tradejs/core": "^3.1.
|
|
37
|
-
"@tradejs/indicators": "^3.1.
|
|
38
|
-
"@tradejs/infra": "^3.1.
|
|
39
|
-
"@tradejs/node": "^3.1.
|
|
35
|
+
"@tradejs/connectors": "^3.1.11-beta.216",
|
|
36
|
+
"@tradejs/core": "^3.1.11-beta.216",
|
|
37
|
+
"@tradejs/indicators": "^3.1.11-beta.216",
|
|
38
|
+
"@tradejs/infra": "^3.1.11-beta.216",
|
|
39
|
+
"@tradejs/node": "^3.1.11-beta.216",
|
|
40
40
|
"@tradejs/strategy-kit": "^3.0.0",
|
|
41
|
-
"@tradejs/types": "^3.1.
|
|
41
|
+
"@tradejs/types": "^3.1.11-beta.216",
|
|
42
42
|
"args": "^5.0.3",
|
|
43
43
|
"bcryptjs": "^2.4.3",
|
|
44
44
|
"chalk": "4.1.2",
|