@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.cjs
CHANGED
|
@@ -1758,46 +1758,83 @@ var Params = class _Params {
|
|
|
1758
1758
|
paramGetters = [];
|
|
1759
1759
|
trackedParams = [];
|
|
1760
1760
|
_currentModule = "script";
|
|
1761
|
-
/**
|
|
1762
|
-
|
|
1761
|
+
/**
|
|
1762
|
+
* Resolved early in constructor so cleanup does not read params lazily.
|
|
1763
|
+
* One of: false (off) | "end" (print at exit) | "top" (print after init,
|
|
1764
|
+
* via context.showUsedParamsIfNeeded()).
|
|
1765
|
+
*/
|
|
1766
|
+
_showUsedParamsMode = false;
|
|
1767
|
+
/** Guard so the dump prints at most once (top OR end, never both). */
|
|
1768
|
+
_usedParamsPrinted = false;
|
|
1763
1769
|
constructor(context, options = {}) {
|
|
1764
1770
|
this.context = context;
|
|
1765
1771
|
this.args = context.args;
|
|
1766
1772
|
if (Object.keys(options).length > 0) {
|
|
1767
1773
|
this.configure(options);
|
|
1768
1774
|
}
|
|
1769
|
-
this.
|
|
1775
|
+
this._resolveShowUsedParams();
|
|
1770
1776
|
if (context && typeof context.registerCleanup === "function") {
|
|
1771
1777
|
context.registerCleanup((ctx) => {
|
|
1772
|
-
if (!ctx.params.
|
|
1773
|
-
|
|
1774
|
-
const modules = Object.keys(byModule).sort();
|
|
1775
|
-
if (modules.length === 0) return;
|
|
1776
|
-
const logger = ctx.logger;
|
|
1777
|
-
logger.debug("[Params]: list of used params:");
|
|
1778
|
-
if (typeof logger.highlight !== "function") {
|
|
1779
|
-
for (const mod of modules) {
|
|
1780
|
-
logger.debug(` [${mod}]`);
|
|
1781
|
-
for (const [key, entry] of Object.entries(byModule[mod])) {
|
|
1782
|
-
logger.debug(` ${key}: ${JSON.stringify(entry.value)} (${entry.source})`);
|
|
1783
|
-
}
|
|
1784
|
-
}
|
|
1785
|
-
return;
|
|
1786
|
-
}
|
|
1787
|
-
for (const mod of modules) {
|
|
1788
|
-
logger.debug(` [${mod}]`);
|
|
1789
|
-
for (const [key, entry] of Object.entries(byModule[mod])) {
|
|
1790
|
-
const valueStr = JSON.stringify(entry.value);
|
|
1791
|
-
const display = entry.source === "default" ? valueStr : logger.highlight(valueStr);
|
|
1792
|
-
logger.debug(` ${key}: ${display} (${entry.source})`);
|
|
1793
|
-
}
|
|
1794
|
-
}
|
|
1778
|
+
if (!ctx.params.getShowUsedParamsMode()) return;
|
|
1779
|
+
ctx.params.printUsedParams(ctx.logger);
|
|
1795
1780
|
});
|
|
1796
1781
|
}
|
|
1797
1782
|
}
|
|
1798
|
-
/**
|
|
1783
|
+
/**
|
|
1784
|
+
* Resolve the --showUsedParams mode. The flag is intentionally dual-typed:
|
|
1785
|
+
* (absent) / --no-showUsedParams / =false -> false (off)
|
|
1786
|
+
* --showUsedParams / =true -> "end" (print at exit)
|
|
1787
|
+
* --showUsedParams=top -> "top" (print after init)
|
|
1788
|
+
* Read raw (uncoerced) from args so the string "top" isn't forced to a
|
|
1789
|
+
* boolean, then track it under the "script" module for the dump itself.
|
|
1790
|
+
*/
|
|
1791
|
+
_resolveShowUsedParams() {
|
|
1792
|
+
const raw = this.args.get("showUsedParams");
|
|
1793
|
+
const source = this.args.getSource?.("showUsedParams") ?? "default";
|
|
1794
|
+
let mode = false;
|
|
1795
|
+
if (raw === void 0 || raw === null) {
|
|
1796
|
+
mode = false;
|
|
1797
|
+
} else if (typeof raw === "string" && raw.trim().toLowerCase() === "top") {
|
|
1798
|
+
mode = "top";
|
|
1799
|
+
} else {
|
|
1800
|
+
const s = typeof raw === "string" ? raw.trim().toLowerCase() : raw;
|
|
1801
|
+
const falsey = s === false || s === "false" || s === "0" || s === "no" || s === "off";
|
|
1802
|
+
mode = falsey ? false : "end";
|
|
1803
|
+
}
|
|
1804
|
+
this._showUsedParamsMode = mode;
|
|
1805
|
+
this.trackParam("showUsedParams", "string", mode, raw === void 0 ? "default" : source, "script");
|
|
1806
|
+
return mode;
|
|
1807
|
+
}
|
|
1808
|
+
/** Whether --showUsedParams was requested in any mode (truthy = on). */
|
|
1799
1809
|
getShowUsedParams() {
|
|
1800
|
-
return this.
|
|
1810
|
+
return this._showUsedParamsMode !== false;
|
|
1811
|
+
}
|
|
1812
|
+
/** Resolved mode: false | "end" | "top". */
|
|
1813
|
+
getShowUsedParamsMode() {
|
|
1814
|
+
return this._showUsedParamsMode;
|
|
1815
|
+
}
|
|
1816
|
+
/**
|
|
1817
|
+
* Print the module-grouped list of figured params (the --showUsedParams
|
|
1818
|
+
* dump). Idempotent: only the first call prints, so callers can invoke it
|
|
1819
|
+
* at the top (long-running services) without double-printing at exit.
|
|
1820
|
+
*/
|
|
1821
|
+
printUsedParams(logger) {
|
|
1822
|
+
if (this._usedParamsPrinted) return;
|
|
1823
|
+
const byModule = this.getFiguredByModule();
|
|
1824
|
+
const modules = Object.keys(byModule).sort();
|
|
1825
|
+
if (modules.length === 0) return;
|
|
1826
|
+
this._usedParamsPrinted = true;
|
|
1827
|
+
logger = logger ?? this.context?.logger ?? console;
|
|
1828
|
+
const hasHighlight = typeof logger.highlight === "function";
|
|
1829
|
+
logger.debug("[Params]: list of used params:");
|
|
1830
|
+
for (const mod of modules) {
|
|
1831
|
+
logger.debug(` [${mod}]`);
|
|
1832
|
+
for (const [key, entry] of Object.entries(byModule[mod])) {
|
|
1833
|
+
const valueStr = JSON.stringify(entry.value);
|
|
1834
|
+
const display = hasHighlight && entry.source !== "default" ? logger.highlight(valueStr) : valueStr;
|
|
1835
|
+
logger.debug(` ${key}: ${display} (${entry.source})`);
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1801
1838
|
}
|
|
1802
1839
|
/**
|
|
1803
1840
|
* Configure parameters
|
|
@@ -2065,6 +2102,18 @@ var Params = class _Params {
|
|
|
2065
2102
|
this._currentModule = prev;
|
|
2066
2103
|
}
|
|
2067
2104
|
}
|
|
2105
|
+
/**
|
|
2106
|
+
* Async variant of {@link runWithModule} for modules that await params.get().
|
|
2107
|
+
*/
|
|
2108
|
+
async runWithModuleAsync(moduleName, fn) {
|
|
2109
|
+
const prev = this._currentModule;
|
|
2110
|
+
this._currentModule = moduleName;
|
|
2111
|
+
try {
|
|
2112
|
+
return await fn();
|
|
2113
|
+
} finally {
|
|
2114
|
+
this._currentModule = prev;
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2068
2117
|
/**
|
|
2069
2118
|
* Infer module name from call stack: first caller outside params/index gives path like .../src/<moduleName>/...
|
|
2070
2119
|
*/
|
|
@@ -2470,7 +2519,16 @@ function setup(opts = {}) {
|
|
|
2470
2519
|
emitter: partialContext.emitter,
|
|
2471
2520
|
isStop: partialContext.isStop,
|
|
2472
2521
|
cleanupFunctions: partialContext.cleanupFunctions,
|
|
2473
|
-
registerCleanup: partialContext.registerCleanup
|
|
2522
|
+
registerCleanup: partialContext.registerCleanup,
|
|
2523
|
+
// For long-running scripts (servers): with --showUsedParams=top, print
|
|
2524
|
+
// the used-params list now (after the script has initialized all its
|
|
2525
|
+
// own components), instead of at exit. No-op for the default mode,
|
|
2526
|
+
// which prints at exit via the cleanup registered by Params.
|
|
2527
|
+
showUsedParamsIfNeeded: () => {
|
|
2528
|
+
if (params.getShowUsedParamsMode?.() === "top") {
|
|
2529
|
+
params.printUsedParams(logger);
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2474
2532
|
};
|
|
2475
2533
|
logger.debug("[setup] completed successfully");
|
|
2476
2534
|
return context;
|
|
@@ -2537,15 +2595,19 @@ async function init(flow2, opts = {}) {
|
|
|
2537
2595
|
process.exit(0);
|
|
2538
2596
|
}
|
|
2539
2597
|
let sigintCount = 0;
|
|
2598
|
+
let firstSigintAt = 0;
|
|
2540
2599
|
process.on("SIGINT", async () => {
|
|
2541
2600
|
if (!context) return;
|
|
2542
|
-
|
|
2543
|
-
if (sigintCount ===
|
|
2601
|
+
const now = Date.now();
|
|
2602
|
+
if (sigintCount === 0) {
|
|
2603
|
+
sigintCount = 1;
|
|
2604
|
+
firstSigintAt = now;
|
|
2544
2605
|
stop = true;
|
|
2545
2606
|
context.logger.info(`>> emitting stop with allowance ${stopAllowance}`);
|
|
2546
2607
|
context.emitter.emit("stop", stopAllowance);
|
|
2547
2608
|
return;
|
|
2548
2609
|
}
|
|
2610
|
+
if (now - firstSigintAt < 250) return;
|
|
2549
2611
|
context.logger.warn("[process] second SIGINT: running cleanup then exit");
|
|
2550
2612
|
await runRegisteredCleanups(context);
|
|
2551
2613
|
process.exit(2);
|
|
@@ -2593,38 +2655,71 @@ var KNEX_DEFAULTS = {
|
|
|
2593
2655
|
};
|
|
2594
2656
|
var Db = class {
|
|
2595
2657
|
static async init(context, options = {}) {
|
|
2596
|
-
const
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
dbName = "local";
|
|
2607
|
-
}
|
|
2608
|
-
if (dbName && /^(postgresql|mysql):\/\//.test(dbName)) {
|
|
2609
|
-
dbConnectionString = dbName;
|
|
2610
|
-
dbName = void 0;
|
|
2611
|
-
}
|
|
2612
|
-
if (dbName && !dbConnectionString) {
|
|
2613
|
-
const paramName = `dbConnectionString${capitalizeFirstLetter(dbName)}`;
|
|
2614
|
-
dbConnectionString = await context.params.get(paramName, "string");
|
|
2658
|
+
const buildConfig = async () => {
|
|
2659
|
+
const defs2 = {
|
|
2660
|
+
dbName: "string",
|
|
2661
|
+
dbProfile: "boolean default false"
|
|
2662
|
+
};
|
|
2663
|
+
const discovered = context?.params?.getAllForModule?.("db", defs2) ?? {};
|
|
2664
|
+
const merged = { ...discovered, ...options };
|
|
2665
|
+
let { dbName, dbProfile } = merged;
|
|
2666
|
+
let dbConnectionString = options.dbConnectionString ?? options.connectionString;
|
|
2667
|
+
let connectionParam = dbConnectionString ? "options" : null;
|
|
2615
2668
|
if (!dbConnectionString) {
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2669
|
+
const src = context?.args?.getSource?.("dbConnectionString");
|
|
2670
|
+
if (src === "cli" || src === "overrides" || src === "config") {
|
|
2671
|
+
dbConnectionString = await context.params.get("dbConnectionString", "string");
|
|
2672
|
+
connectionParam = "dbConnectionString";
|
|
2673
|
+
}
|
|
2619
2674
|
}
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2675
|
+
if (!dbConnectionString && dbName && /^(postgresql|mysql):\/\//.test(dbName)) {
|
|
2676
|
+
dbConnectionString = dbName;
|
|
2677
|
+
dbName = void 0;
|
|
2678
|
+
}
|
|
2679
|
+
if (!dbConnectionString && dbName) {
|
|
2680
|
+
const paramName = `dbConnectionString${capitalizeFirstLetter(dbName)}`;
|
|
2681
|
+
dbConnectionString = await context.params.get(paramName, "string");
|
|
2682
|
+
connectionParam = paramName;
|
|
2683
|
+
if (!dbConnectionString) {
|
|
2684
|
+
throw new ParamError(
|
|
2685
|
+
`Db: cannot find dbConnectionString for dbName="${dbName}" (looked for param "${paramName}")`
|
|
2686
|
+
);
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
if (!dbConnectionString) {
|
|
2690
|
+
dbConnectionString = await context.params.get("dbConnectionString", "string");
|
|
2691
|
+
if (dbConnectionString) {
|
|
2692
|
+
connectionParam = "dbConnectionString";
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
if (!dbConnectionString) {
|
|
2696
|
+
if (!dbName) {
|
|
2697
|
+
dbName = "local";
|
|
2698
|
+
}
|
|
2699
|
+
const paramName = `dbConnectionString${capitalizeFirstLetter(dbName)}`;
|
|
2700
|
+
dbConnectionString = await context.params.get(paramName, "string");
|
|
2701
|
+
connectionParam = paramName;
|
|
2702
|
+
if (!dbConnectionString) {
|
|
2703
|
+
throw new ParamError(
|
|
2704
|
+
`Db: cannot find dbConnectionString for dbName="${dbName}" (looked for param "${paramName}")`
|
|
2705
|
+
);
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
const displayName = resolveDbDisplayName(
|
|
2709
|
+
dbName,
|
|
2710
|
+
connectionParam,
|
|
2711
|
+
context?.args?.env,
|
|
2712
|
+
merged.name
|
|
2713
|
+
);
|
|
2714
|
+
return {
|
|
2715
|
+
...KNEX_DEFAULTS,
|
|
2716
|
+
connectionString: dbConnectionString,
|
|
2717
|
+
name: displayName,
|
|
2718
|
+
profile: !!dbProfile,
|
|
2719
|
+
logger: context.logger
|
|
2720
|
+
};
|
|
2627
2721
|
};
|
|
2722
|
+
const config2 = context?.params?.runWithModuleAsync ? await context.params.runWithModuleAsync("db", buildConfig) : await buildConfig();
|
|
2628
2723
|
return dbConnect(context, config2);
|
|
2629
2724
|
}
|
|
2630
2725
|
constructor(config2) {
|
|
@@ -2641,7 +2736,6 @@ var Db = class {
|
|
|
2641
2736
|
acquireConnectionTimeout: 1e4,
|
|
2642
2737
|
ssl: { rejectUnauthorized: false },
|
|
2643
2738
|
logger: console,
|
|
2644
|
-
name: "default",
|
|
2645
2739
|
...config2
|
|
2646
2740
|
};
|
|
2647
2741
|
this.logger = this.config.logger;
|
|
@@ -2741,9 +2835,7 @@ var Db = class {
|
|
|
2741
2835
|
await this.testConnection();
|
|
2742
2836
|
}
|
|
2743
2837
|
this.isConnected = true;
|
|
2744
|
-
this.logger.debug?.(
|
|
2745
|
-
`[Db] Connected to database "${this.config.name || this.config.connectionString}"`
|
|
2746
|
-
);
|
|
2838
|
+
this.logger.debug?.(formatDbConnectMessage(this.config.name, this.config.connectionString));
|
|
2747
2839
|
} catch (error) {
|
|
2748
2840
|
if (error instanceof ParamError) {
|
|
2749
2841
|
throw error;
|
|
@@ -2761,9 +2853,7 @@ var Db = class {
|
|
|
2761
2853
|
this.knexInstance = null;
|
|
2762
2854
|
this.isConnected = false;
|
|
2763
2855
|
this.queriesLog = [];
|
|
2764
|
-
this.logger.debug?.(
|
|
2765
|
-
`[Db] Disconnected from database "${this.config.name || this.config.connectionString}"`
|
|
2766
|
-
);
|
|
2856
|
+
this.logger.debug?.(formatDbDisconnectMessage(this.config.name, this.config.connectionString));
|
|
2767
2857
|
} catch (error) {
|
|
2768
2858
|
const errorMsg = this.getErrorMessage(error);
|
|
2769
2859
|
this.logger.error?.(`[Db] Error disconnecting: ${errorMsg}`);
|
|
@@ -2892,15 +2982,74 @@ var Db = class {
|
|
|
2892
2982
|
function capitalizeFirstLetter(str) {
|
|
2893
2983
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
2894
2984
|
}
|
|
2985
|
+
function resolveDbDisplayName(dbName, connectionParam, argsEnv, mergedName) {
|
|
2986
|
+
if (dbName) {
|
|
2987
|
+
return dbName;
|
|
2988
|
+
}
|
|
2989
|
+
if (mergedName) {
|
|
2990
|
+
return mergedName;
|
|
2991
|
+
}
|
|
2992
|
+
if (connectionParam?.startsWith("dbConnectionString") && connectionParam.length > "dbConnectionString".length) {
|
|
2993
|
+
return connectionParam.slice("dbConnectionString".length).toLowerCase();
|
|
2994
|
+
}
|
|
2995
|
+
if (connectionParam === "dbConnectionString" && argsEnv) {
|
|
2996
|
+
return argsEnv;
|
|
2997
|
+
}
|
|
2998
|
+
return void 0;
|
|
2999
|
+
}
|
|
3000
|
+
function formatDbConnectMessage(name, connectionString) {
|
|
3001
|
+
const endpointSuffix = formatConnectionEndpointSuffix(connectionString);
|
|
3002
|
+
if (name) {
|
|
3003
|
+
return `[Db] Connected to database "${name}"${endpointSuffix}`;
|
|
3004
|
+
}
|
|
3005
|
+
return `[Db] Connected${endpointSuffix}`;
|
|
3006
|
+
}
|
|
3007
|
+
function formatDbDisconnectMessage(name, connectionString) {
|
|
3008
|
+
const endpointSuffix = formatConnectionEndpointSuffix(connectionString);
|
|
3009
|
+
if (name) {
|
|
3010
|
+
return `[Db] Disconnected from database "${name}"${endpointSuffix}`;
|
|
3011
|
+
}
|
|
3012
|
+
return `[Db] Disconnected${endpointSuffix}`;
|
|
3013
|
+
}
|
|
3014
|
+
function formatDbInstanceMessage(action, name) {
|
|
3015
|
+
if (name) {
|
|
3016
|
+
return `[Db] instance "${name}" ${action}`;
|
|
3017
|
+
}
|
|
3018
|
+
return `[Db] instance ${action}`;
|
|
3019
|
+
}
|
|
3020
|
+
function formatConnectionEndpointSuffix(connectionString) {
|
|
3021
|
+
const endpoint = formatConnectionEndpoint(connectionString);
|
|
3022
|
+
return endpoint ? ` (${endpoint})` : "";
|
|
3023
|
+
}
|
|
3024
|
+
function formatConnectionEndpoint(connectionString) {
|
|
3025
|
+
try {
|
|
3026
|
+
const url = new URL(connectionString);
|
|
3027
|
+
const host = url.hostname;
|
|
3028
|
+
if (!host) {
|
|
3029
|
+
return null;
|
|
3030
|
+
}
|
|
3031
|
+
let port = url.port;
|
|
3032
|
+
if (!port) {
|
|
3033
|
+
if (url.protocol === "postgresql:") {
|
|
3034
|
+
port = "5432";
|
|
3035
|
+
} else if (url.protocol === "mysql:") {
|
|
3036
|
+
port = "3306";
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
return port ? `${host}:${port}` : host;
|
|
3040
|
+
} catch {
|
|
3041
|
+
return null;
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
2895
3044
|
async function dbConnect(context, config2) {
|
|
2896
3045
|
try {
|
|
2897
3046
|
const db = new Db(config2);
|
|
2898
3047
|
context.registerCleanup(async () => {
|
|
2899
3048
|
await db.disconnect();
|
|
2900
|
-
context.logger.debug?.(
|
|
3049
|
+
context.logger.debug?.(formatDbInstanceMessage("disconnected", config2.name));
|
|
2901
3050
|
});
|
|
2902
3051
|
await db.connect();
|
|
2903
|
-
context.logger.debug?.(
|
|
3052
|
+
context.logger.debug?.(formatDbInstanceMessage("initialized", config2.name));
|
|
2904
3053
|
return db;
|
|
2905
3054
|
} catch (error) {
|
|
2906
3055
|
if (error instanceof ParamError) {
|