@nmakarov/cli-toolkit 0.23.0 → 0.27.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 +218 -69
- package/dist/cli-runner.cjs.map +1 -1
- package/dist/cli-runner.js +218 -69
- package/dist/cli-runner.js.map +1 -1
- package/dist/db.cjs +125 -38
- package/dist/db.cjs.map +1 -1
- package/dist/db.js +125 -38
- package/dist/db.js.map +1 -1
- package/dist/index.cjs +212 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +212 -67
- package/dist/index.js.map +1 -1
- package/dist/init.cjs +93 -31
- package/dist/init.cjs.map +1 -1
- package/dist/init.js +93 -31
- package/dist/init.js.map +1 -1
- package/dist/params.cjs +77 -28
- package/dist/params.cjs.map +1 -1
- package/dist/params.js +77 -28
- package/dist/params.js.map +1 -1
- package/package.json +1 -1
package/dist/cli-runner.js
CHANGED
|
@@ -1742,46 +1742,83 @@ var Params = class _Params {
|
|
|
1742
1742
|
paramGetters = [];
|
|
1743
1743
|
trackedParams = [];
|
|
1744
1744
|
_currentModule = "script";
|
|
1745
|
-
/**
|
|
1746
|
-
|
|
1745
|
+
/**
|
|
1746
|
+
* Resolved early in constructor so cleanup does not read params lazily.
|
|
1747
|
+
* One of: false (off) | "end" (print at exit) | "top" (print after init,
|
|
1748
|
+
* via context.showUsedParamsIfNeeded()).
|
|
1749
|
+
*/
|
|
1750
|
+
_showUsedParamsMode = false;
|
|
1751
|
+
/** Guard so the dump prints at most once (top OR end, never both). */
|
|
1752
|
+
_usedParamsPrinted = false;
|
|
1747
1753
|
constructor(context, options = {}) {
|
|
1748
1754
|
this.context = context;
|
|
1749
1755
|
this.args = context.args;
|
|
1750
1756
|
if (Object.keys(options).length > 0) {
|
|
1751
1757
|
this.configure(options);
|
|
1752
1758
|
}
|
|
1753
|
-
this.
|
|
1759
|
+
this._resolveShowUsedParams();
|
|
1754
1760
|
if (context && typeof context.registerCleanup === "function") {
|
|
1755
1761
|
context.registerCleanup((ctx) => {
|
|
1756
|
-
if (!ctx.params.
|
|
1757
|
-
|
|
1758
|
-
const modules = Object.keys(byModule).sort();
|
|
1759
|
-
if (modules.length === 0) return;
|
|
1760
|
-
const logger = ctx.logger;
|
|
1761
|
-
logger.debug("[Params]: list of used params:");
|
|
1762
|
-
if (typeof logger.highlight !== "function") {
|
|
1763
|
-
for (const mod of modules) {
|
|
1764
|
-
logger.debug(` [${mod}]`);
|
|
1765
|
-
for (const [key, entry] of Object.entries(byModule[mod])) {
|
|
1766
|
-
logger.debug(` ${key}: ${JSON.stringify(entry.value)} (${entry.source})`);
|
|
1767
|
-
}
|
|
1768
|
-
}
|
|
1769
|
-
return;
|
|
1770
|
-
}
|
|
1771
|
-
for (const mod of modules) {
|
|
1772
|
-
logger.debug(` [${mod}]`);
|
|
1773
|
-
for (const [key, entry] of Object.entries(byModule[mod])) {
|
|
1774
|
-
const valueStr = JSON.stringify(entry.value);
|
|
1775
|
-
const display = entry.source === "default" ? valueStr : logger.highlight(valueStr);
|
|
1776
|
-
logger.debug(` ${key}: ${display} (${entry.source})`);
|
|
1777
|
-
}
|
|
1778
|
-
}
|
|
1762
|
+
if (!ctx.params.getShowUsedParamsMode()) return;
|
|
1763
|
+
ctx.params.printUsedParams(ctx.logger);
|
|
1779
1764
|
});
|
|
1780
1765
|
}
|
|
1781
1766
|
}
|
|
1782
|
-
/**
|
|
1767
|
+
/**
|
|
1768
|
+
* Resolve the --showUsedParams mode. The flag is intentionally dual-typed:
|
|
1769
|
+
* (absent) / --no-showUsedParams / =false -> false (off)
|
|
1770
|
+
* --showUsedParams / =true -> "end" (print at exit)
|
|
1771
|
+
* --showUsedParams=top -> "top" (print after init)
|
|
1772
|
+
* Read raw (uncoerced) from args so the string "top" isn't forced to a
|
|
1773
|
+
* boolean, then track it under the "script" module for the dump itself.
|
|
1774
|
+
*/
|
|
1775
|
+
_resolveShowUsedParams() {
|
|
1776
|
+
const raw = this.args.get("showUsedParams");
|
|
1777
|
+
const source = this.args.getSource?.("showUsedParams") ?? "default";
|
|
1778
|
+
let mode = false;
|
|
1779
|
+
if (raw === void 0 || raw === null) {
|
|
1780
|
+
mode = false;
|
|
1781
|
+
} else if (typeof raw === "string" && raw.trim().toLowerCase() === "top") {
|
|
1782
|
+
mode = "top";
|
|
1783
|
+
} else {
|
|
1784
|
+
const s = typeof raw === "string" ? raw.trim().toLowerCase() : raw;
|
|
1785
|
+
const falsey = s === false || s === "false" || s === "0" || s === "no" || s === "off";
|
|
1786
|
+
mode = falsey ? false : "end";
|
|
1787
|
+
}
|
|
1788
|
+
this._showUsedParamsMode = mode;
|
|
1789
|
+
this.trackParam("showUsedParams", "string", mode, raw === void 0 ? "default" : source, "script");
|
|
1790
|
+
return mode;
|
|
1791
|
+
}
|
|
1792
|
+
/** Whether --showUsedParams was requested in any mode (truthy = on). */
|
|
1783
1793
|
getShowUsedParams() {
|
|
1784
|
-
return this.
|
|
1794
|
+
return this._showUsedParamsMode !== false;
|
|
1795
|
+
}
|
|
1796
|
+
/** Resolved mode: false | "end" | "top". */
|
|
1797
|
+
getShowUsedParamsMode() {
|
|
1798
|
+
return this._showUsedParamsMode;
|
|
1799
|
+
}
|
|
1800
|
+
/**
|
|
1801
|
+
* Print the module-grouped list of figured params (the --showUsedParams
|
|
1802
|
+
* dump). Idempotent: only the first call prints, so callers can invoke it
|
|
1803
|
+
* at the top (long-running services) without double-printing at exit.
|
|
1804
|
+
*/
|
|
1805
|
+
printUsedParams(logger) {
|
|
1806
|
+
if (this._usedParamsPrinted) return;
|
|
1807
|
+
const byModule = this.getFiguredByModule();
|
|
1808
|
+
const modules = Object.keys(byModule).sort();
|
|
1809
|
+
if (modules.length === 0) return;
|
|
1810
|
+
this._usedParamsPrinted = true;
|
|
1811
|
+
logger = logger ?? this.context?.logger ?? console;
|
|
1812
|
+
const hasHighlight = typeof logger.highlight === "function";
|
|
1813
|
+
logger.debug("[Params]: list of used params:");
|
|
1814
|
+
for (const mod of modules) {
|
|
1815
|
+
logger.debug(` [${mod}]`);
|
|
1816
|
+
for (const [key, entry] of Object.entries(byModule[mod])) {
|
|
1817
|
+
const valueStr = JSON.stringify(entry.value);
|
|
1818
|
+
const display = hasHighlight && entry.source !== "default" ? logger.highlight(valueStr) : valueStr;
|
|
1819
|
+
logger.debug(` ${key}: ${display} (${entry.source})`);
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1785
1822
|
}
|
|
1786
1823
|
/**
|
|
1787
1824
|
* Configure parameters
|
|
@@ -2049,6 +2086,18 @@ var Params = class _Params {
|
|
|
2049
2086
|
this._currentModule = prev;
|
|
2050
2087
|
}
|
|
2051
2088
|
}
|
|
2089
|
+
/**
|
|
2090
|
+
* Async variant of {@link runWithModule} for modules that await params.get().
|
|
2091
|
+
*/
|
|
2092
|
+
async runWithModuleAsync(moduleName, fn) {
|
|
2093
|
+
const prev = this._currentModule;
|
|
2094
|
+
this._currentModule = moduleName;
|
|
2095
|
+
try {
|
|
2096
|
+
return await fn();
|
|
2097
|
+
} finally {
|
|
2098
|
+
this._currentModule = prev;
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2052
2101
|
/**
|
|
2053
2102
|
* Infer module name from call stack: first caller outside params/index gives path like .../src/<moduleName>/...
|
|
2054
2103
|
*/
|
|
@@ -2454,7 +2503,16 @@ function setup(opts = {}) {
|
|
|
2454
2503
|
emitter: partialContext.emitter,
|
|
2455
2504
|
isStop: partialContext.isStop,
|
|
2456
2505
|
cleanupFunctions: partialContext.cleanupFunctions,
|
|
2457
|
-
registerCleanup: partialContext.registerCleanup
|
|
2506
|
+
registerCleanup: partialContext.registerCleanup,
|
|
2507
|
+
// For long-running scripts (servers): with --showUsedParams=top, print
|
|
2508
|
+
// the used-params list now (after the script has initialized all its
|
|
2509
|
+
// own components), instead of at exit. No-op for the default mode,
|
|
2510
|
+
// which prints at exit via the cleanup registered by Params.
|
|
2511
|
+
showUsedParamsIfNeeded: () => {
|
|
2512
|
+
if (params.getShowUsedParamsMode?.() === "top") {
|
|
2513
|
+
params.printUsedParams(logger);
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2458
2516
|
};
|
|
2459
2517
|
logger.debug("[setup] completed successfully");
|
|
2460
2518
|
return context;
|
|
@@ -2521,15 +2579,19 @@ async function init(flow2, opts = {}) {
|
|
|
2521
2579
|
process.exit(0);
|
|
2522
2580
|
}
|
|
2523
2581
|
let sigintCount = 0;
|
|
2582
|
+
let firstSigintAt = 0;
|
|
2524
2583
|
process.on("SIGINT", async () => {
|
|
2525
2584
|
if (!context) return;
|
|
2526
|
-
|
|
2527
|
-
if (sigintCount ===
|
|
2585
|
+
const now = Date.now();
|
|
2586
|
+
if (sigintCount === 0) {
|
|
2587
|
+
sigintCount = 1;
|
|
2588
|
+
firstSigintAt = now;
|
|
2528
2589
|
stop = true;
|
|
2529
2590
|
context.logger.info(`>> emitting stop with allowance ${stopAllowance}`);
|
|
2530
2591
|
context.emitter.emit("stop", stopAllowance);
|
|
2531
2592
|
return;
|
|
2532
2593
|
}
|
|
2594
|
+
if (now - firstSigintAt < 250) return;
|
|
2533
2595
|
context.logger.warn("[process] second SIGINT: running cleanup then exit");
|
|
2534
2596
|
await runRegisteredCleanups(context);
|
|
2535
2597
|
process.exit(2);
|
|
@@ -2577,38 +2639,71 @@ var KNEX_DEFAULTS = {
|
|
|
2577
2639
|
};
|
|
2578
2640
|
var Db = class {
|
|
2579
2641
|
static async init(context, options = {}) {
|
|
2580
|
-
const
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
dbName = "local";
|
|
2591
|
-
}
|
|
2592
|
-
if (dbName && /^(postgresql|mysql):\/\//.test(dbName)) {
|
|
2593
|
-
dbConnectionString = dbName;
|
|
2594
|
-
dbName = void 0;
|
|
2595
|
-
}
|
|
2596
|
-
if (dbName && !dbConnectionString) {
|
|
2597
|
-
const paramName = `dbConnectionString${capitalizeFirstLetter(dbName)}`;
|
|
2598
|
-
dbConnectionString = await context.params.get(paramName, "string");
|
|
2642
|
+
const buildConfig = async () => {
|
|
2643
|
+
const defs2 = {
|
|
2644
|
+
dbName: "string",
|
|
2645
|
+
dbProfile: "boolean default false"
|
|
2646
|
+
};
|
|
2647
|
+
const discovered = context?.params?.getAllForModule?.("db", defs2) ?? {};
|
|
2648
|
+
const merged = { ...discovered, ...options };
|
|
2649
|
+
let { dbName, dbProfile } = merged;
|
|
2650
|
+
let dbConnectionString = options.dbConnectionString ?? options.connectionString;
|
|
2651
|
+
let connectionParam = dbConnectionString ? "options" : null;
|
|
2599
2652
|
if (!dbConnectionString) {
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2653
|
+
const src = context?.args?.getSource?.("dbConnectionString");
|
|
2654
|
+
if (src === "cli" || src === "overrides" || src === "config") {
|
|
2655
|
+
dbConnectionString = await context.params.get("dbConnectionString", "string");
|
|
2656
|
+
connectionParam = "dbConnectionString";
|
|
2657
|
+
}
|
|
2603
2658
|
}
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2659
|
+
if (!dbConnectionString && dbName && /^(postgresql|mysql):\/\//.test(dbName)) {
|
|
2660
|
+
dbConnectionString = dbName;
|
|
2661
|
+
dbName = void 0;
|
|
2662
|
+
}
|
|
2663
|
+
if (!dbConnectionString && dbName) {
|
|
2664
|
+
const paramName = `dbConnectionString${capitalizeFirstLetter(dbName)}`;
|
|
2665
|
+
dbConnectionString = await context.params.get(paramName, "string");
|
|
2666
|
+
connectionParam = paramName;
|
|
2667
|
+
if (!dbConnectionString) {
|
|
2668
|
+
throw new ParamError(
|
|
2669
|
+
`Db: cannot find dbConnectionString for dbName="${dbName}" (looked for param "${paramName}")`
|
|
2670
|
+
);
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
if (!dbConnectionString) {
|
|
2674
|
+
dbConnectionString = await context.params.get("dbConnectionString", "string");
|
|
2675
|
+
if (dbConnectionString) {
|
|
2676
|
+
connectionParam = "dbConnectionString";
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
if (!dbConnectionString) {
|
|
2680
|
+
if (!dbName) {
|
|
2681
|
+
dbName = "local";
|
|
2682
|
+
}
|
|
2683
|
+
const paramName = `dbConnectionString${capitalizeFirstLetter(dbName)}`;
|
|
2684
|
+
dbConnectionString = await context.params.get(paramName, "string");
|
|
2685
|
+
connectionParam = paramName;
|
|
2686
|
+
if (!dbConnectionString) {
|
|
2687
|
+
throw new ParamError(
|
|
2688
|
+
`Db: cannot find dbConnectionString for dbName="${dbName}" (looked for param "${paramName}")`
|
|
2689
|
+
);
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
const displayName = resolveDbDisplayName(
|
|
2693
|
+
dbName,
|
|
2694
|
+
connectionParam,
|
|
2695
|
+
context?.args?.env,
|
|
2696
|
+
merged.name
|
|
2697
|
+
);
|
|
2698
|
+
return {
|
|
2699
|
+
...KNEX_DEFAULTS,
|
|
2700
|
+
connectionString: dbConnectionString,
|
|
2701
|
+
name: displayName,
|
|
2702
|
+
profile: !!dbProfile,
|
|
2703
|
+
logger: context.logger
|
|
2704
|
+
};
|
|
2611
2705
|
};
|
|
2706
|
+
const config2 = context?.params?.runWithModuleAsync ? await context.params.runWithModuleAsync("db", buildConfig) : await buildConfig();
|
|
2612
2707
|
return dbConnect(context, config2);
|
|
2613
2708
|
}
|
|
2614
2709
|
constructor(config2) {
|
|
@@ -2625,7 +2720,6 @@ var Db = class {
|
|
|
2625
2720
|
acquireConnectionTimeout: 1e4,
|
|
2626
2721
|
ssl: { rejectUnauthorized: false },
|
|
2627
2722
|
logger: console,
|
|
2628
|
-
name: "default",
|
|
2629
2723
|
...config2
|
|
2630
2724
|
};
|
|
2631
2725
|
this.logger = this.config.logger;
|
|
@@ -2725,9 +2819,7 @@ var Db = class {
|
|
|
2725
2819
|
await this.testConnection();
|
|
2726
2820
|
}
|
|
2727
2821
|
this.isConnected = true;
|
|
2728
|
-
this.logger.debug?.(
|
|
2729
|
-
`[Db] Connected to database "${this.config.name || this.config.connectionString}"`
|
|
2730
|
-
);
|
|
2822
|
+
this.logger.debug?.(formatDbConnectMessage(this.config.name, this.config.connectionString));
|
|
2731
2823
|
} catch (error) {
|
|
2732
2824
|
if (error instanceof ParamError) {
|
|
2733
2825
|
throw error;
|
|
@@ -2745,9 +2837,7 @@ var Db = class {
|
|
|
2745
2837
|
this.knexInstance = null;
|
|
2746
2838
|
this.isConnected = false;
|
|
2747
2839
|
this.queriesLog = [];
|
|
2748
|
-
this.logger.debug?.(
|
|
2749
|
-
`[Db] Disconnected from database "${this.config.name || this.config.connectionString}"`
|
|
2750
|
-
);
|
|
2840
|
+
this.logger.debug?.(formatDbDisconnectMessage(this.config.name, this.config.connectionString));
|
|
2751
2841
|
} catch (error) {
|
|
2752
2842
|
const errorMsg = this.getErrorMessage(error);
|
|
2753
2843
|
this.logger.error?.(`[Db] Error disconnecting: ${errorMsg}`);
|
|
@@ -2876,15 +2966,74 @@ var Db = class {
|
|
|
2876
2966
|
function capitalizeFirstLetter(str) {
|
|
2877
2967
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
2878
2968
|
}
|
|
2969
|
+
function resolveDbDisplayName(dbName, connectionParam, argsEnv, mergedName) {
|
|
2970
|
+
if (dbName) {
|
|
2971
|
+
return dbName;
|
|
2972
|
+
}
|
|
2973
|
+
if (mergedName) {
|
|
2974
|
+
return mergedName;
|
|
2975
|
+
}
|
|
2976
|
+
if (connectionParam?.startsWith("dbConnectionString") && connectionParam.length > "dbConnectionString".length) {
|
|
2977
|
+
return connectionParam.slice("dbConnectionString".length).toLowerCase();
|
|
2978
|
+
}
|
|
2979
|
+
if (connectionParam === "dbConnectionString" && argsEnv) {
|
|
2980
|
+
return argsEnv;
|
|
2981
|
+
}
|
|
2982
|
+
return void 0;
|
|
2983
|
+
}
|
|
2984
|
+
function formatDbConnectMessage(name, connectionString) {
|
|
2985
|
+
const endpointSuffix = formatConnectionEndpointSuffix(connectionString);
|
|
2986
|
+
if (name) {
|
|
2987
|
+
return `[Db] Connected to database "${name}"${endpointSuffix}`;
|
|
2988
|
+
}
|
|
2989
|
+
return `[Db] Connected${endpointSuffix}`;
|
|
2990
|
+
}
|
|
2991
|
+
function formatDbDisconnectMessage(name, connectionString) {
|
|
2992
|
+
const endpointSuffix = formatConnectionEndpointSuffix(connectionString);
|
|
2993
|
+
if (name) {
|
|
2994
|
+
return `[Db] Disconnected from database "${name}"${endpointSuffix}`;
|
|
2995
|
+
}
|
|
2996
|
+
return `[Db] Disconnected${endpointSuffix}`;
|
|
2997
|
+
}
|
|
2998
|
+
function formatDbInstanceMessage(action, name) {
|
|
2999
|
+
if (name) {
|
|
3000
|
+
return `[Db] instance "${name}" ${action}`;
|
|
3001
|
+
}
|
|
3002
|
+
return `[Db] instance ${action}`;
|
|
3003
|
+
}
|
|
3004
|
+
function formatConnectionEndpointSuffix(connectionString) {
|
|
3005
|
+
const endpoint = formatConnectionEndpoint(connectionString);
|
|
3006
|
+
return endpoint ? ` (${endpoint})` : "";
|
|
3007
|
+
}
|
|
3008
|
+
function formatConnectionEndpoint(connectionString) {
|
|
3009
|
+
try {
|
|
3010
|
+
const url = new URL(connectionString);
|
|
3011
|
+
const host = url.hostname;
|
|
3012
|
+
if (!host) {
|
|
3013
|
+
return null;
|
|
3014
|
+
}
|
|
3015
|
+
let port = url.port;
|
|
3016
|
+
if (!port) {
|
|
3017
|
+
if (url.protocol === "postgresql:") {
|
|
3018
|
+
port = "5432";
|
|
3019
|
+
} else if (url.protocol === "mysql:") {
|
|
3020
|
+
port = "3306";
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
3023
|
+
return port ? `${host}:${port}` : host;
|
|
3024
|
+
} catch {
|
|
3025
|
+
return null;
|
|
3026
|
+
}
|
|
3027
|
+
}
|
|
2879
3028
|
async function dbConnect(context, config2) {
|
|
2880
3029
|
try {
|
|
2881
3030
|
const db = new Db(config2);
|
|
2882
3031
|
context.registerCleanup(async () => {
|
|
2883
3032
|
await db.disconnect();
|
|
2884
|
-
context.logger.debug?.(
|
|
3033
|
+
context.logger.debug?.(formatDbInstanceMessage("disconnected", config2.name));
|
|
2885
3034
|
});
|
|
2886
3035
|
await db.connect();
|
|
2887
|
-
context.logger.debug?.(
|
|
3036
|
+
context.logger.debug?.(formatDbInstanceMessage("initialized", config2.name));
|
|
2888
3037
|
return db;
|
|
2889
3038
|
} catch (error) {
|
|
2890
3039
|
if (error instanceof ParamError) {
|