@nmakarov/cli-toolkit 0.44.0 → 0.47.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/args.cjs +17 -24
- package/dist/args.cjs.map +1 -1
- package/dist/args.js +17 -24
- package/dist/args.js.map +1 -1
- package/dist/cli-runner.cjs +180 -28
- package/dist/cli-runner.cjs.map +1 -1
- package/dist/cli-runner.js +180 -28
- package/dist/cli-runner.js.map +1 -1
- package/dist/db.cjs +163 -4
- package/dist/db.cjs.map +1 -1
- package/dist/db.js +163 -4
- package/dist/db.js.map +1 -1
- package/dist/deploy.cjs +2 -0
- package/dist/deploy.cjs.map +1 -1
- package/dist/deploy.js +2 -0
- package/dist/deploy.js.map +1 -1
- package/dist/http-client.cjs +1 -1
- package/dist/http-client.cjs.map +1 -1
- package/dist/http-client.js +1 -1
- package/dist/http-client.js.map +1 -1
- package/dist/index.cjs +182 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +182 -28
- package/dist/index.js.map +1 -1
- package/dist/init.cjs +17 -24
- package/dist/init.cjs.map +1 -1
- package/dist/init.js +17 -24
- package/dist/init.js.map +1 -1
- package/dist/s3.cjs.map +1 -1
- package/dist/s3.js.map +1 -1
- package/package.json +1 -1
package/dist/cli-runner.js
CHANGED
|
@@ -1439,36 +1439,29 @@ var Args = class _Args {
|
|
|
1439
1439
|
}
|
|
1440
1440
|
return;
|
|
1441
1441
|
}
|
|
1442
|
-
let dotEnvPathFile =
|
|
1443
|
-
|
|
1444
|
-
const envSpecificPath = resolve(dotEnvPath, envSpecificFile);
|
|
1445
|
-
if (existsSync(envSpecificPath)) {
|
|
1446
|
-
dotEnvPathFile = envSpecificPath;
|
|
1447
|
-
}
|
|
1448
|
-
if (!dotEnvPathFile && !this.get("dotEnvPath")) {
|
|
1442
|
+
let dotEnvPathFile = resolve(dotEnvPath, dotEnvFile);
|
|
1443
|
+
if (!existsSync(dotEnvPathFile) && !this.get("dotEnvPath")) {
|
|
1449
1444
|
const examplesPath = resolve(dotEnvPath, "examples");
|
|
1450
|
-
const
|
|
1451
|
-
if (existsSync(
|
|
1452
|
-
dotEnvPathFile =
|
|
1453
|
-
}
|
|
1454
|
-
|
|
1455
|
-
if (!dotEnvPathFile) {
|
|
1456
|
-
dotEnvPathFile = resolve(dotEnvPath, dotEnvFile);
|
|
1457
|
-
if (!existsSync(dotEnvPathFile)) {
|
|
1458
|
-
if (!this.get("dotEnvPath")) {
|
|
1459
|
-
const examplesPath = resolve(dotEnvPath, "examples");
|
|
1460
|
-
const examplesEnvFile = resolve(examplesPath, dotEnvFile);
|
|
1461
|
-
if (existsSync(examplesEnvFile)) {
|
|
1462
|
-
dotEnvPathFile = examplesEnvFile;
|
|
1463
|
-
} else {
|
|
1464
|
-
dotEnvPathFile = resolve(dotEnvPath, "..", dotEnvFile);
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1445
|
+
const examplesEnvFile = resolve(examplesPath, dotEnvFile);
|
|
1446
|
+
if (existsSync(examplesEnvFile)) {
|
|
1447
|
+
dotEnvPathFile = examplesEnvFile;
|
|
1448
|
+
} else {
|
|
1449
|
+
dotEnvPathFile = resolve(dotEnvPath, "..", dotEnvFile);
|
|
1467
1450
|
}
|
|
1468
1451
|
}
|
|
1469
1452
|
if (dotEnvPathFile && existsSync(dotEnvPathFile)) {
|
|
1470
1453
|
config({ path: dotEnvPathFile, quiet: true });
|
|
1471
1454
|
}
|
|
1455
|
+
const envSpecificFile = `.env.${this.env}`;
|
|
1456
|
+
const envSpecificPath = resolve(dotEnvPath, envSpecificFile);
|
|
1457
|
+
if (existsSync(envSpecificPath) && envSpecificPath !== dotEnvPathFile) {
|
|
1458
|
+
config({ path: envSpecificPath, quiet: true });
|
|
1459
|
+
} else if (!this.get("dotEnvPath")) {
|
|
1460
|
+
const examplesOverlay = resolve(dotEnvPath, "examples", envSpecificFile);
|
|
1461
|
+
if (existsSync(examplesOverlay)) {
|
|
1462
|
+
config({ path: examplesOverlay, quiet: true });
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1472
1465
|
}
|
|
1473
1466
|
/**
|
|
1474
1467
|
* Load configuration files
|
|
@@ -2788,10 +2781,35 @@ async function ensureSchema(db, spec, options = {}) {
|
|
|
2788
2781
|
// src/db/index.js
|
|
2789
2782
|
var KNEX_DEFAULTS = {
|
|
2790
2783
|
testConnection: true,
|
|
2791
|
-
|
|
2784
|
+
// min: 0 avoids holding idle sockets that go stale during long-running CLIs
|
|
2785
|
+
pool: { min: 0, max: 10, idleTimeoutMillis: 3e4 },
|
|
2792
2786
|
acquireConnectionTimeout: 1e4,
|
|
2793
2787
|
ssl: { rejectUnauthorized: false }
|
|
2794
2788
|
};
|
|
2789
|
+
var CONNECTION_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
2790
|
+
"ECONNRESET",
|
|
2791
|
+
"ECONNREFUSED",
|
|
2792
|
+
"EPIPE",
|
|
2793
|
+
"ETIMEDOUT",
|
|
2794
|
+
"ENOTFOUND",
|
|
2795
|
+
"EHOSTUNREACH",
|
|
2796
|
+
"ENETUNREACH",
|
|
2797
|
+
"ECONNABORTED",
|
|
2798
|
+
"CONNECTION_ENDED",
|
|
2799
|
+
"CONNECTION_CLOSED",
|
|
2800
|
+
// PostgreSQL SQLSTATE class 08xxx (connection exception) + admin shutdowns
|
|
2801
|
+
"08000",
|
|
2802
|
+
"08001",
|
|
2803
|
+
"08003",
|
|
2804
|
+
"08004",
|
|
2805
|
+
"08006",
|
|
2806
|
+
"08007",
|
|
2807
|
+
"08P01",
|
|
2808
|
+
"57P01",
|
|
2809
|
+
"57P02",
|
|
2810
|
+
"57P03"
|
|
2811
|
+
]);
|
|
2812
|
+
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|Timeout acquiring a connection/i;
|
|
2795
2813
|
var Db = class _Db {
|
|
2796
2814
|
static async init(context, options = {}) {
|
|
2797
2815
|
const buildConfig = async () => {
|
|
@@ -3039,10 +3057,11 @@ var Db = class _Db {
|
|
|
3039
3057
|
this.knexInstance = null;
|
|
3040
3058
|
this.isConnected = false;
|
|
3041
3059
|
this.queriesLog = [];
|
|
3060
|
+
this._reconnectPromise = null;
|
|
3042
3061
|
this.config = {
|
|
3043
3062
|
testConnection: true,
|
|
3044
3063
|
profile: false,
|
|
3045
|
-
pool: { min:
|
|
3064
|
+
pool: { min: 0, max: 10, idleTimeoutMillis: 3e4 },
|
|
3046
3065
|
acquireConnectionTimeout: 1e4,
|
|
3047
3066
|
ssl: { rejectUnauthorized: false },
|
|
3048
3067
|
logger: console,
|
|
@@ -3060,7 +3079,7 @@ var Db = class _Db {
|
|
|
3060
3079
|
if (!inst.knexInstance) {
|
|
3061
3080
|
throw new Error("Db: Not connected. Call connect() first.");
|
|
3062
3081
|
}
|
|
3063
|
-
return inst.knexInstance(...argumentsList);
|
|
3082
|
+
return inst.wrapQueryBuilder(inst.knexInstance(...argumentsList));
|
|
3064
3083
|
},
|
|
3065
3084
|
get: (target, prop) => {
|
|
3066
3085
|
if (prop === "_instance") {
|
|
@@ -3070,8 +3089,12 @@ var Db = class _Db {
|
|
|
3070
3089
|
const ownMethods = [
|
|
3071
3090
|
"connect",
|
|
3072
3091
|
"disconnect",
|
|
3092
|
+
"reconnect",
|
|
3073
3093
|
"testConnection",
|
|
3074
3094
|
"tableExists",
|
|
3095
|
+
"raw",
|
|
3096
|
+
"withConnectionRetry",
|
|
3097
|
+
"isConnectionError",
|
|
3075
3098
|
"getQueryLog",
|
|
3076
3099
|
"getKnex",
|
|
3077
3100
|
"isConnectedToDb",
|
|
@@ -3091,6 +3114,9 @@ var Db = class _Db {
|
|
|
3091
3114
|
if (inst.knexInstance) {
|
|
3092
3115
|
const knexProp = inst.knexInstance[prop];
|
|
3093
3116
|
if (typeof knexProp === "function") {
|
|
3117
|
+
if (prop === "raw") {
|
|
3118
|
+
return (...args) => inst.raw(...args);
|
|
3119
|
+
}
|
|
3094
3120
|
return knexProp.bind(inst.knexInstance);
|
|
3095
3121
|
}
|
|
3096
3122
|
return knexProp;
|
|
@@ -3170,6 +3196,130 @@ var Db = class _Db {
|
|
|
3170
3196
|
throw error;
|
|
3171
3197
|
}
|
|
3172
3198
|
}
|
|
3199
|
+
/**
|
|
3200
|
+
* True when the error indicates a dead socket / pool that a fresh connect may fix.
|
|
3201
|
+
* Safe to call as `Db.prototype.isConnectionError(err)` or via a connected handle.
|
|
3202
|
+
*/
|
|
3203
|
+
isConnectionError(error) {
|
|
3204
|
+
if (!error) {
|
|
3205
|
+
return false;
|
|
3206
|
+
}
|
|
3207
|
+
if (error instanceof AggregateError && Array.isArray(error.errors)) {
|
|
3208
|
+
return error.errors.some((e) => this.isConnectionError(e));
|
|
3209
|
+
}
|
|
3210
|
+
const code = error.code ?? error.errno;
|
|
3211
|
+
if (code != null && CONNECTION_ERROR_CODES.has(String(code))) {
|
|
3212
|
+
return true;
|
|
3213
|
+
}
|
|
3214
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
3215
|
+
return CONNECTION_ERROR_MESSAGE_RE.test(msg);
|
|
3216
|
+
}
|
|
3217
|
+
/**
|
|
3218
|
+
* Destroy the current knex pool and open a new one. Concurrent callers share one attempt.
|
|
3219
|
+
*/
|
|
3220
|
+
async reconnect() {
|
|
3221
|
+
if (this._reconnectPromise) {
|
|
3222
|
+
await this._reconnectPromise;
|
|
3223
|
+
return;
|
|
3224
|
+
}
|
|
3225
|
+
this._reconnectPromise = (async () => {
|
|
3226
|
+
const old = this.knexInstance;
|
|
3227
|
+
this.isConnected = false;
|
|
3228
|
+
this.knexInstance = null;
|
|
3229
|
+
this.queriesLog = [];
|
|
3230
|
+
if (old) {
|
|
3231
|
+
try {
|
|
3232
|
+
await old.destroy();
|
|
3233
|
+
} catch (error) {
|
|
3234
|
+
this.logger.debug?.(
|
|
3235
|
+
`[Db] destroy during reconnect: ${this.getErrorMessage(error)}`
|
|
3236
|
+
);
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
await this.connect();
|
|
3240
|
+
})();
|
|
3241
|
+
try {
|
|
3242
|
+
await this._reconnectPromise;
|
|
3243
|
+
} finally {
|
|
3244
|
+
this._reconnectPromise = null;
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
async reconnectAfterConnectionError(error) {
|
|
3248
|
+
this.logger.warn?.(
|
|
3249
|
+
`[Db] Connection lost (${this.getErrorMessage(error)}) \u2014 reconnecting\u2026`
|
|
3250
|
+
);
|
|
3251
|
+
await this.reconnect();
|
|
3252
|
+
}
|
|
3253
|
+
/**
|
|
3254
|
+
* Run `fn`; on a connection error, reconnect once (by default) and retry `fn`.
|
|
3255
|
+
* `fn` should look up `this.knexInstance` each call so the retry uses the new client.
|
|
3256
|
+
*/
|
|
3257
|
+
async withConnectionRetry(fn, { retries = 1 } = {}) {
|
|
3258
|
+
let lastError;
|
|
3259
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
3260
|
+
try {
|
|
3261
|
+
return await fn();
|
|
3262
|
+
} catch (error) {
|
|
3263
|
+
lastError = error;
|
|
3264
|
+
if (!this.isConnectionError(error) || attempt >= retries) {
|
|
3265
|
+
throw error;
|
|
3266
|
+
}
|
|
3267
|
+
await this.reconnectAfterConnectionError(error);
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3270
|
+
throw lastError;
|
|
3271
|
+
}
|
|
3272
|
+
/**
|
|
3273
|
+
* Wrap a knex QueryBuilder / Raw so that awaiting it retries once after reconnect
|
|
3274
|
+
* when the first attempt dies with a connection error.
|
|
3275
|
+
*/
|
|
3276
|
+
wrapQueryBuilder(builder) {
|
|
3277
|
+
if (!builder || typeof builder.then !== "function" || builder.__dbReconnectWrapped) {
|
|
3278
|
+
return builder;
|
|
3279
|
+
}
|
|
3280
|
+
builder.__dbReconnectWrapped = true;
|
|
3281
|
+
const inst = this;
|
|
3282
|
+
const protoThen = Object.getPrototypeOf(builder)?.then;
|
|
3283
|
+
if (typeof protoThen !== "function") {
|
|
3284
|
+
return builder;
|
|
3285
|
+
}
|
|
3286
|
+
builder.then = function(onFulfilled, onRejected) {
|
|
3287
|
+
const run = async () => {
|
|
3288
|
+
try {
|
|
3289
|
+
return await protoThen.call(builder);
|
|
3290
|
+
} catch (error) {
|
|
3291
|
+
if (!inst.isConnectionError(error)) {
|
|
3292
|
+
throw error;
|
|
3293
|
+
}
|
|
3294
|
+
await inst.reconnectAfterConnectionError(error);
|
|
3295
|
+
if (typeof builder.clone === "function") {
|
|
3296
|
+
const retry = builder.clone();
|
|
3297
|
+
retry.client = inst.knexInstance.client;
|
|
3298
|
+
return await protoThen.call(retry);
|
|
3299
|
+
}
|
|
3300
|
+
if (typeof builder.toSQL === "function" && inst.knexInstance) {
|
|
3301
|
+
const sql = builder.toSQL();
|
|
3302
|
+
const statements = Array.isArray(sql) ? sql : [sql];
|
|
3303
|
+
let last;
|
|
3304
|
+
for (const stmt of statements) {
|
|
3305
|
+
last = await inst.knexInstance.raw(stmt.sql, stmt.bindings);
|
|
3306
|
+
}
|
|
3307
|
+
return last;
|
|
3308
|
+
}
|
|
3309
|
+
throw error;
|
|
3310
|
+
}
|
|
3311
|
+
};
|
|
3312
|
+
return run().then(onFulfilled, onRejected);
|
|
3313
|
+
};
|
|
3314
|
+
return builder;
|
|
3315
|
+
}
|
|
3316
|
+
/** knex.raw with auto-reconnect on dead connections. */
|
|
3317
|
+
raw(...args) {
|
|
3318
|
+
if (!this.knexInstance) {
|
|
3319
|
+
throw new Error("Db: Not connected. Call connect() first.");
|
|
3320
|
+
}
|
|
3321
|
+
return this.wrapQueryBuilder(this.knexInstance.raw(...args));
|
|
3322
|
+
}
|
|
3173
3323
|
getErrorMessage(error) {
|
|
3174
3324
|
if (error instanceof AggregateError) {
|
|
3175
3325
|
const errors = error.errors || [];
|
|
@@ -3340,7 +3490,9 @@ var Db = class _Db {
|
|
|
3340
3490
|
throw new Error("Db: Not connected. Call connect() first.");
|
|
3341
3491
|
}
|
|
3342
3492
|
try {
|
|
3343
|
-
return await this.
|
|
3493
|
+
return await this.withConnectionRetry(
|
|
3494
|
+
() => this.knexInstance.schema.hasTable(tableName)
|
|
3495
|
+
);
|
|
3344
3496
|
} catch (error) {
|
|
3345
3497
|
this.logger.error?.(`[Db] Error checking table existence: ${error.message}`);
|
|
3346
3498
|
throw error;
|