@nmakarov/cli-toolkit 0.78.0 → 0.80.0
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-runner.cjs +135 -31
- package/dist/cli-runner.cjs.map +1 -1
- package/dist/cli-runner.js +135 -31
- package/dist/cli-runner.js.map +1 -1
- package/dist/db.cjs +105 -25
- package/dist/db.cjs.map +1 -1
- package/dist/db.js +105 -25
- package/dist/db.js.map +1 -1
- package/dist/deploy.cjs +10 -0
- package/dist/deploy.cjs.map +1 -1
- package/dist/deploy.js +10 -0
- package/dist/deploy.js.map +1 -1
- package/dist/index.cjs +126 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +126 -30
- package/dist/index.js.map +1 -1
- package/dist/init.cjs +30 -6
- package/dist/init.cjs.map +1 -1
- package/dist/init.js +30 -6
- package/dist/init.js.map +1 -1
- package/dist/logger.cjs +6 -5
- package/dist/logger.cjs.map +1 -1
- package/dist/logger.js +6 -5
- package/dist/logger.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3760,7 +3760,7 @@ var CONNECTION_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
|
3760
3760
|
"57P02",
|
|
3761
3761
|
"57P03"
|
|
3762
3762
|
]);
|
|
3763
|
-
var CONNECTION_ERROR_MESSAGE_RE = /connection (terminated|ended|closed|destroyed|reset|refused|not open)|Connection terminated unexpectedly|Client has encountered a connection error|server closed the connection|Cannot use a pool after calling end|This socket has been ended|connect ECONNRESET/i;
|
|
3763
|
+
var CONNECTION_ERROR_MESSAGE_RE = /connection (terminated|ended|closed|destroyed|reset|refused|not open)|Connection (terminated|ended) unexpectedly|Client has encountered a connection error|server closed the connection|Cannot use a pool after calling end|This socket has been ended|connect ECONNRESET/i;
|
|
3764
3764
|
var Db = class _Db {
|
|
3765
3765
|
static async init(context, options = {}) {
|
|
3766
3766
|
const buildConfig = async () => {
|
|
@@ -4028,6 +4028,10 @@ var Db = class _Db {
|
|
|
4028
4028
|
this.isConnected = false;
|
|
4029
4029
|
this.queriesLog = [];
|
|
4030
4030
|
this._reconnectPromise = null;
|
|
4031
|
+
this._closed = false;
|
|
4032
|
+
this._liveKnex = /* @__PURE__ */ new Set();
|
|
4033
|
+
this._reconnectCooldownUntil = 0;
|
|
4034
|
+
this._reconnectAcquireTimeoutMs = null;
|
|
4031
4035
|
this.config = {
|
|
4032
4036
|
testConnection: true,
|
|
4033
4037
|
profile: false,
|
|
@@ -4112,6 +4116,9 @@ var Db = class _Db {
|
|
|
4112
4116
|
return null;
|
|
4113
4117
|
}
|
|
4114
4118
|
async connect() {
|
|
4119
|
+
if (this._closed) {
|
|
4120
|
+
throw new ParamError("Db: Connection closed");
|
|
4121
|
+
}
|
|
4115
4122
|
if (this.isConnected && this.knexInstance) {
|
|
4116
4123
|
this.logger.warn?.("[Db] Already connected");
|
|
4117
4124
|
return;
|
|
@@ -4127,22 +4134,46 @@ var Db = class _Db {
|
|
|
4127
4134
|
connectionString: this.config.connectionString,
|
|
4128
4135
|
family: 4
|
|
4129
4136
|
};
|
|
4137
|
+
const acquireTimeout = this._reconnectAcquireTimeoutMs ?? this.config.acquireConnectionTimeout;
|
|
4130
4138
|
this.knexInstance = knex({
|
|
4131
4139
|
client,
|
|
4132
4140
|
connection: connectionConfig,
|
|
4133
4141
|
pool: this.config.pool,
|
|
4134
|
-
acquireConnectionTimeout:
|
|
4142
|
+
acquireConnectionTimeout: acquireTimeout,
|
|
4135
4143
|
...this.config.ssl && { ssl: this.config.ssl }
|
|
4136
4144
|
});
|
|
4145
|
+
this._liveKnex.add(this.knexInstance);
|
|
4146
|
+
this.knexInstance.on?.("error", (err) => {
|
|
4147
|
+
if (this._closed) return;
|
|
4148
|
+
this.logger.warn?.(
|
|
4149
|
+
`[Db] Connection error (${this.getErrorMessage(err)})`
|
|
4150
|
+
);
|
|
4151
|
+
});
|
|
4152
|
+
if (this._closed) {
|
|
4153
|
+
await this._destroyKnex(this.knexInstance, "connect aborted (closed)");
|
|
4154
|
+
this.knexInstance = null;
|
|
4155
|
+
throw new ParamError("Db: Connection closed");
|
|
4156
|
+
}
|
|
4137
4157
|
if (this.config.profile) {
|
|
4138
4158
|
this.attachProfiler();
|
|
4139
4159
|
}
|
|
4140
4160
|
if (this.config.testConnection) {
|
|
4141
4161
|
await this.testConnection();
|
|
4142
4162
|
}
|
|
4163
|
+
if (this._closed) {
|
|
4164
|
+
await this._destroyKnex(this.knexInstance, "connect aborted (closed)");
|
|
4165
|
+
this.knexInstance = null;
|
|
4166
|
+
throw new ParamError("Db: Connection closed");
|
|
4167
|
+
}
|
|
4143
4168
|
this.isConnected = true;
|
|
4144
4169
|
this.logger.debug?.(formatDbConnectMessage(this.config.name, this.config.connectionString));
|
|
4145
4170
|
} catch (error) {
|
|
4171
|
+
const failed = this.knexInstance;
|
|
4172
|
+
this.knexInstance = null;
|
|
4173
|
+
this.isConnected = false;
|
|
4174
|
+
if (failed) {
|
|
4175
|
+
await this._destroyKnex(failed, "connect failed");
|
|
4176
|
+
}
|
|
4146
4177
|
if (error instanceof ParamError) {
|
|
4147
4178
|
throw error;
|
|
4148
4179
|
}
|
|
@@ -4150,20 +4181,42 @@ var Db = class _Db {
|
|
|
4150
4181
|
throw new ParamError(`Db: Connection failed - ${errorMsg}`);
|
|
4151
4182
|
}
|
|
4152
4183
|
}
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4184
|
+
/**
|
|
4185
|
+
* Destroy a knex pool without hanging exit on stuck TCP sockets (ETIMEDOUT).
|
|
4186
|
+
* @param {import("knex").Knex | null | undefined} knexInst
|
|
4187
|
+
* @param {string} [reason]
|
|
4188
|
+
* @param {number} [timeoutMs]
|
|
4189
|
+
*/
|
|
4190
|
+
async _destroyKnex(knexInst, reason = "destroy", timeoutMs = 3e3) {
|
|
4191
|
+
if (!knexInst || typeof knexInst.destroy !== "function") return;
|
|
4192
|
+
this._liveKnex.delete(knexInst);
|
|
4157
4193
|
try {
|
|
4158
|
-
await
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4194
|
+
await Promise.race([
|
|
4195
|
+
knexInst.destroy(),
|
|
4196
|
+
new Promise((_, reject) => {
|
|
4197
|
+
const t = setTimeout(
|
|
4198
|
+
() => reject(new Error(`Db: ${reason} timed out after ${timeoutMs}ms`)),
|
|
4199
|
+
timeoutMs
|
|
4200
|
+
);
|
|
4201
|
+
t.unref?.();
|
|
4202
|
+
})
|
|
4203
|
+
]);
|
|
4163
4204
|
} catch (error) {
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4205
|
+
this.logger.debug?.(
|
|
4206
|
+
`[Db] ${reason}: ${this.getErrorMessage(error)}`
|
|
4207
|
+
);
|
|
4208
|
+
}
|
|
4209
|
+
}
|
|
4210
|
+
async disconnect() {
|
|
4211
|
+
this._closed = true;
|
|
4212
|
+
this.isConnected = false;
|
|
4213
|
+
this.knexInstance = null;
|
|
4214
|
+
this.queriesLog = [];
|
|
4215
|
+
const all = [...this._liveKnex];
|
|
4216
|
+
this._liveKnex.clear();
|
|
4217
|
+
await Promise.all(all.map((inst) => this._destroyKnex(inst, "disconnect")));
|
|
4218
|
+
if (all.length > 0) {
|
|
4219
|
+
this.logger.debug?.(formatDbDisconnectMessage(this.config.name, this.config.connectionString));
|
|
4167
4220
|
}
|
|
4168
4221
|
}
|
|
4169
4222
|
/**
|
|
@@ -4186,27 +4239,39 @@ var Db = class _Db {
|
|
|
4186
4239
|
}
|
|
4187
4240
|
/**
|
|
4188
4241
|
* Destroy the current knex pool and open a new one. Concurrent callers share one attempt.
|
|
4242
|
+
* No-ops once disconnect() has closed the handle.
|
|
4189
4243
|
*/
|
|
4190
4244
|
async reconnect() {
|
|
4245
|
+
if (this._closed) {
|
|
4246
|
+
return;
|
|
4247
|
+
}
|
|
4248
|
+
if (this._reconnectCooldownUntil && Date.now() < this._reconnectCooldownUntil) {
|
|
4249
|
+
return;
|
|
4250
|
+
}
|
|
4191
4251
|
if (this._reconnectPromise) {
|
|
4192
4252
|
await this._reconnectPromise;
|
|
4193
4253
|
return;
|
|
4194
4254
|
}
|
|
4195
4255
|
this._reconnectPromise = (async () => {
|
|
4256
|
+
if (this._closed) return;
|
|
4196
4257
|
const old = this.knexInstance;
|
|
4197
4258
|
this.isConnected = false;
|
|
4198
4259
|
this.knexInstance = null;
|
|
4199
4260
|
this.queriesLog = [];
|
|
4200
4261
|
if (old) {
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4262
|
+
await this._destroyKnex(old, "destroy during reconnect");
|
|
4263
|
+
}
|
|
4264
|
+
if (this._closed) return;
|
|
4265
|
+
this._reconnectAcquireTimeoutMs = 3e3;
|
|
4266
|
+
try {
|
|
4267
|
+
await this.connect();
|
|
4268
|
+
this._reconnectCooldownUntil = 0;
|
|
4269
|
+
} catch (error) {
|
|
4270
|
+
this._reconnectCooldownUntil = Date.now() + 5e3;
|
|
4271
|
+
throw error;
|
|
4272
|
+
} finally {
|
|
4273
|
+
this._reconnectAcquireTimeoutMs = null;
|
|
4208
4274
|
}
|
|
4209
|
-
await this.connect();
|
|
4210
4275
|
})();
|
|
4211
4276
|
try {
|
|
4212
4277
|
await this._reconnectPromise;
|
|
@@ -4215,10 +4280,19 @@ var Db = class _Db {
|
|
|
4215
4280
|
}
|
|
4216
4281
|
}
|
|
4217
4282
|
async reconnectAfterConnectionError(error) {
|
|
4283
|
+
if (this._closed) {
|
|
4284
|
+
return;
|
|
4285
|
+
}
|
|
4286
|
+
if (this._reconnectCooldownUntil && Date.now() < this._reconnectCooldownUntil) {
|
|
4287
|
+
return;
|
|
4288
|
+
}
|
|
4218
4289
|
this.logger.warn?.(
|
|
4219
4290
|
`[Db] Connection lost (${this.getErrorMessage(error)}) \u2014 reconnecting\u2026`
|
|
4220
4291
|
);
|
|
4221
|
-
|
|
4292
|
+
try {
|
|
4293
|
+
await this.reconnect();
|
|
4294
|
+
} catch {
|
|
4295
|
+
}
|
|
4222
4296
|
}
|
|
4223
4297
|
/**
|
|
4224
4298
|
* Run `fn`; on a connection error, reconnect once (by default) and retry `fn`.
|
|
@@ -4231,10 +4305,13 @@ var Db = class _Db {
|
|
|
4231
4305
|
return await fn();
|
|
4232
4306
|
} catch (error) {
|
|
4233
4307
|
lastError = error;
|
|
4234
|
-
if (!this.isConnectionError(error) || attempt >= retries) {
|
|
4308
|
+
if (this._closed || !this.isConnectionError(error) || attempt >= retries) {
|
|
4235
4309
|
throw error;
|
|
4236
4310
|
}
|
|
4237
4311
|
await this.reconnectAfterConnectionError(error);
|
|
4312
|
+
if (this._closed) {
|
|
4313
|
+
throw error;
|
|
4314
|
+
}
|
|
4238
4315
|
}
|
|
4239
4316
|
}
|
|
4240
4317
|
throw lastError;
|
|
@@ -4258,10 +4335,13 @@ var Db = class _Db {
|
|
|
4258
4335
|
try {
|
|
4259
4336
|
return await protoThen.call(builder);
|
|
4260
4337
|
} catch (error) {
|
|
4261
|
-
if (!inst.isConnectionError(error)) {
|
|
4338
|
+
if (inst._closed || !inst.isConnectionError(error)) {
|
|
4262
4339
|
throw error;
|
|
4263
4340
|
}
|
|
4264
4341
|
await inst.reconnectAfterConnectionError(error);
|
|
4342
|
+
if (inst._closed || !inst.knexInstance) {
|
|
4343
|
+
throw error;
|
|
4344
|
+
}
|
|
4265
4345
|
if (typeof builder.clone === "function") {
|
|
4266
4346
|
const retry = builder.clone();
|
|
4267
4347
|
retry.client = inst.knexInstance.client;
|
|
@@ -5309,13 +5389,14 @@ var Logger = class _Logger {
|
|
|
5309
5389
|
*/
|
|
5310
5390
|
progress(message, opts) {
|
|
5311
5391
|
const { prefix, count, total } = opts;
|
|
5312
|
-
const
|
|
5392
|
+
const displayTotal = Math.max(Number(total) || 0, Number(count) || 0);
|
|
5393
|
+
const paddedTotal = String(displayTotal).length;
|
|
5313
5394
|
const paddedCount = String(count).padStart(paddedTotal, " ");
|
|
5314
5395
|
const payload = {
|
|
5315
5396
|
level: "progress",
|
|
5316
5397
|
message,
|
|
5317
5398
|
count: paddedCount,
|
|
5318
|
-
total,
|
|
5399
|
+
total: displayTotal,
|
|
5319
5400
|
prefix
|
|
5320
5401
|
};
|
|
5321
5402
|
const key = prefix ?? "";
|
|
@@ -5332,7 +5413,7 @@ var Logger = class _Logger {
|
|
|
5332
5413
|
if (wantTimes) {
|
|
5333
5414
|
let remaining = -1;
|
|
5334
5415
|
if (itemsPerSec > 0) {
|
|
5335
|
-
remaining = (
|
|
5416
|
+
remaining = Math.max(0, (displayTotal - count) / itemsPerSec);
|
|
5336
5417
|
}
|
|
5337
5418
|
payload.elapsed = this.round(elapsedSeconds, 2);
|
|
5338
5419
|
payload.remaining = remaining >= 0 ? this.round(remaining, 2) : remaining;
|
|
@@ -5341,12 +5422,12 @@ var Logger = class _Logger {
|
|
|
5341
5422
|
payload.rate = itemsPerSec >= 0 ? this.round(itemsPerSec, 2) : itemsPerSec;
|
|
5342
5423
|
}
|
|
5343
5424
|
}
|
|
5344
|
-
if (count
|
|
5425
|
+
if (count === total) {
|
|
5345
5426
|
delete this.startTimes[key];
|
|
5346
5427
|
delete this.startCounts[key];
|
|
5347
5428
|
delete this.lastProgressTimes[key];
|
|
5348
5429
|
}
|
|
5349
|
-
if (this.shouldOutputProgress(prefix ?? "", count,
|
|
5430
|
+
if (this.shouldOutputProgress(prefix ?? "", count, displayTotal)) {
|
|
5350
5431
|
this.out(payload);
|
|
5351
5432
|
if (this.options.progressThrottle && prefix) {
|
|
5352
5433
|
this.lastProgressTimes[prefix] = Date.now();
|
|
@@ -5505,6 +5586,8 @@ function setup(opts = {}) {
|
|
|
5505
5586
|
// a quick "show me the figured params and quit" that skips the flow's
|
|
5506
5587
|
// actual work. Like --stopAfter=init, this is a hard exit(0) (registered
|
|
5507
5588
|
// cleanups are skipped); call it once components/params are resolved.
|
|
5589
|
+
_requestExitCode: null,
|
|
5590
|
+
requestExit: null,
|
|
5508
5591
|
showUsedParamsIfNeeded: () => {
|
|
5509
5592
|
const mode = params.getShowUsedParamsMode?.();
|
|
5510
5593
|
if (mode !== "top" && mode !== "stop") return;
|
|
@@ -5515,6 +5598,9 @@ function setup(opts = {}) {
|
|
|
5515
5598
|
}
|
|
5516
5599
|
}
|
|
5517
5600
|
};
|
|
5601
|
+
context.requestExit = (code = 0) => {
|
|
5602
|
+
context._requestExitCode = code;
|
|
5603
|
+
};
|
|
5518
5604
|
logger.debug("[setup] completed successfully");
|
|
5519
5605
|
return context;
|
|
5520
5606
|
}
|
|
@@ -6320,6 +6406,12 @@ function resolveKillTimeoutMs(pm2) {
|
|
|
6320
6406
|
if (Number.isFinite(stopSec) && stopSec > 0) return Math.floor(stopSec * 1e3) + 5e3;
|
|
6321
6407
|
return 65e3;
|
|
6322
6408
|
}
|
|
6409
|
+
var DEFAULT_KILL_RETRY_TIME_MS = 1e4;
|
|
6410
|
+
function resolveKillRetryTimeMs(pm2) {
|
|
6411
|
+
const explicit = Number(pm2?.killRetryTime);
|
|
6412
|
+
if (Number.isFinite(explicit) && explicit > 0) return Math.floor(explicit);
|
|
6413
|
+
return DEFAULT_KILL_RETRY_TIME_MS;
|
|
6414
|
+
}
|
|
6323
6415
|
function resolvePm2Args(pm2) {
|
|
6324
6416
|
const base = String(pm2?.args ?? "").trim();
|
|
6325
6417
|
const stopSec = Number(pm2?.stopAllowance);
|
|
@@ -6343,6 +6435,7 @@ function buildEcosystemConfig(service, paths) {
|
|
|
6343
6435
|
const outLog = join9(paths.logs, `${pm2.appName}.out.log`);
|
|
6344
6436
|
const errLog = join9(paths.logs, `${pm2.appName}.err.log`);
|
|
6345
6437
|
const killTimeoutMs = resolveKillTimeoutMs(pm2);
|
|
6438
|
+
const killRetryTimeMs = resolveKillRetryTimeMs(pm2);
|
|
6346
6439
|
const args = resolvePm2Args(pm2);
|
|
6347
6440
|
const ensureEnv = paths.ensureEnv;
|
|
6348
6441
|
return `/**
|
|
@@ -6368,6 +6461,9 @@ module.exports = {
|
|
|
6368
6461
|
max_memory_restart: "1500M",
|
|
6369
6462
|
// Grace window after SIGINT/SIGTERM before SIGKILL (ms). Align with --stopAllowance.
|
|
6370
6463
|
kill_timeout: ${killTimeoutMs},
|
|
6464
|
+
// How often pm2 re-checks / logs "failed to kill - retrying in \u2026"
|
|
6465
|
+
// while waiting for graceful exit (pm2 default 100ms \u2192 noisy logs).
|
|
6466
|
+
kill_retry_time: ${killRetryTimeMs},
|
|
6371
6467
|
out_file: "${outLog}",
|
|
6372
6468
|
error_file: "${errLog}",
|
|
6373
6469
|
merge_logs: true,
|