@nmakarov/cli-toolkit 0.14.0 → 0.16.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.
Files changed (49) hide show
  1. package/dist/args.cjs +49 -9
  2. package/dist/args.cjs.map +1 -1
  3. package/dist/args.js +49 -9
  4. package/dist/args.js.map +1 -1
  5. package/dist/db.cjs +9 -2
  6. package/dist/db.cjs.map +1 -1
  7. package/dist/db.js +9 -2
  8. package/dist/db.js.map +1 -1
  9. package/dist/errors.cjs +17 -0
  10. package/dist/errors.cjs.map +1 -1
  11. package/dist/errors.js +15 -0
  12. package/dist/errors.js.map +1 -1
  13. package/dist/filedatabase.cjs +16 -24
  14. package/dist/filedatabase.cjs.map +1 -1
  15. package/dist/filedatabase.js +16 -23
  16. package/dist/filedatabase.js.map +1 -1
  17. package/dist/http-client.cjs +44 -34
  18. package/dist/http-client.cjs.map +1 -1
  19. package/dist/http-client.js +44 -34
  20. package/dist/http-client.js.map +1 -1
  21. package/dist/http-client2.cjs +368 -0
  22. package/dist/http-client2.cjs.map +1 -0
  23. package/dist/http-client2.js +340 -0
  24. package/dist/http-client2.js.map +1 -0
  25. package/dist/index.cjs +214 -91
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.js +214 -90
  28. package/dist/index.js.map +1 -1
  29. package/dist/init.cjs +192 -76
  30. package/dist/init.cjs.map +1 -1
  31. package/dist/init.js +192 -76
  32. package/dist/init.js.map +1 -1
  33. package/dist/logger.cjs +19 -36
  34. package/dist/logger.cjs.map +1 -1
  35. package/dist/logger.js +19 -36
  36. package/dist/logger.js.map +1 -1
  37. package/dist/mock-server.cjs +13 -16
  38. package/dist/mock-server.cjs.map +1 -1
  39. package/dist/mock-server.js +13 -16
  40. package/dist/mock-server.js.map +1 -1
  41. package/dist/params.cjs +114 -15
  42. package/dist/params.cjs.map +1 -1
  43. package/dist/params.js +114 -15
  44. package/dist/params.js.map +1 -1
  45. package/dist/screen.cjs +2 -0
  46. package/dist/screen.cjs.map +1 -1
  47. package/dist/screen.js +2 -0
  48. package/dist/screen.js.map +1 -1
  49. package/package.json +9 -2
package/dist/init.js CHANGED
@@ -661,6 +661,8 @@ async function showScreen(config2) {
661
661
  keyMatches = true;
662
662
  } else if (input === binding.key) {
663
663
  keyMatches = true;
664
+ } else if (key?.name === binding.key) {
665
+ keyMatches = true;
664
666
  }
665
667
  if (keyMatches) {
666
668
  if (binding.enabled === false) {
@@ -1115,20 +1117,31 @@ var Args = class _Args {
1115
1117
  configValues = {};
1116
1118
  configsLoaded = [];
1117
1119
  env = "local";
1118
- constructor(config2 = {}) {
1120
+ constructor(contextOrConfig = {}, config2) {
1121
+ const hasContext = config2 !== void 0;
1122
+ const configToUse = hasContext ? config2 ?? {} : contextOrConfig ?? {};
1123
+ const context = hasContext ? contextOrConfig : void 0;
1119
1124
  this.aliases = {};
1120
1125
  this.overrides = {};
1121
1126
  this.defaults = {};
1122
1127
  this.prefixes = ["not", "no"];
1123
- if (Object.keys(config2).length > 0) {
1124
- this.configure(config2);
1128
+ if (Object.keys(configToUse).length > 0) {
1129
+ this.configure(configToUse);
1125
1130
  }
1126
- const args = config2.args || process.argv.slice(2);
1131
+ const args = configToUse.args || process.argv.slice(2);
1127
1132
  this.parseArgs(args);
1128
1133
  this.env = this.get("env")?.toLowerCase() || "local";
1129
1134
  this.loadDotEnv();
1130
1135
  this.loadConfigFiles();
1131
1136
  this.checkConflicts();
1137
+ if (context && typeof context.registerCleanup === "function") {
1138
+ context.registerCleanup((ctx) => {
1139
+ const unusedArgs = ctx.args.getUnused();
1140
+ if (unusedArgs.length > 0) {
1141
+ ctx.logger.warn("Unused CLI args:", unusedArgs.join(", "));
1142
+ }
1143
+ });
1144
+ }
1132
1145
  }
1133
1146
  /**
1134
1147
  * Configure Args options
@@ -1150,12 +1163,15 @@ var Args = class _Args {
1150
1163
  }
1151
1164
  }
1152
1165
  /**
1153
- * Initialize Args instance
1154
- * Note: Args is special - it's initialized first, so it can't take context
1155
- * This static method is for consistency with other components
1166
+ * Initialize Args instance.
1167
+ * Args.init(context, config) when used from init/setup: context has registerCleanup, Args registers unused-args cleanup.
1168
+ * Args.init(config) for standalone use (no cleanup).
1156
1169
  */
1157
- static init(config2 = {}) {
1158
- return new _Args(config2);
1170
+ static init(contextOrConfig, config2) {
1171
+ if (config2 !== void 0) {
1172
+ return new _Args(contextOrConfig, config2);
1173
+ }
1174
+ return new _Args(contextOrConfig ?? {});
1159
1175
  }
1160
1176
  /**
1161
1177
  * Parse command line arguments
@@ -1335,6 +1351,32 @@ var Args = class _Args {
1335
1351
  }
1336
1352
  return void 0;
1337
1353
  }
1354
+ /**
1355
+ * Return which layer provided the value for get(key): overrides, cli, config, env, or default.
1356
+ * Does not add key to usedKeys. Use after get(key) when you need the origin.
1357
+ */
1358
+ getSource(key) {
1359
+ const resolvedKey = this.aliases[key] || key;
1360
+ const lcKey = resolvedKey.toLowerCase();
1361
+ const overrideKey = Object.keys(this.overrides).find((k) => k.toLowerCase() === lcKey);
1362
+ if (overrideKey !== void 0) return "overrides";
1363
+ const lcKeyWithEnv = `${lcKey}${this.env ? `_${this.env.toLowerCase()}` : ""}`;
1364
+ if (this.env && this.args[lcKeyWithEnv] !== void 0) return "cli";
1365
+ if (this.args[lcKey] !== void 0) return "cli";
1366
+ const configKey = Object.keys(this.configValues).find((k) => k.toLowerCase() === lcKey);
1367
+ if (configKey !== void 0) return "config";
1368
+ const envKey = this.toEnvKey(resolvedKey);
1369
+ const envKeyWithEnv = `${envKey}${this.env ? `_${this.env.toUpperCase()}` : ""}`;
1370
+ const envSpecificKey = Object.keys(process.env).find((k) => this.env && k.toUpperCase() === envKeyWithEnv);
1371
+ const envKeyFound = Object.keys(process.env).find((k) => k.toUpperCase() === envKey);
1372
+ const envKeyAlt = envKey.replace(/_([0-9])/g, "$1");
1373
+ const envKeyAltFound = !envKeyFound ? Object.keys(process.env).find((k) => k.toUpperCase() === envKeyAlt) : null;
1374
+ if (envSpecificKey || envKeyFound || envKeyAltFound) return "env";
1375
+ const defaultKey = Object.keys(this.defaults).find((k) => k.toLowerCase() === lcKey);
1376
+ if (defaultKey !== void 0) return "default";
1377
+ if (lcKey === "env" && process.env.NODE_ENV !== void 0) return "env";
1378
+ return void 0;
1379
+ }
1338
1380
  /**
1339
1381
  * Set a value (for testing/internal use)
1340
1382
  */
@@ -1687,17 +1729,53 @@ var Params = class _Params {
1687
1729
  context;
1688
1730
  // Partial context during initialization
1689
1731
  params = {};
1732
+ paramSources = {};
1690
1733
  definitions = {};
1691
1734
  args;
1692
1735
  paramSetters = [];
1693
1736
  paramGetters = [];
1694
1737
  trackedParams = [];
1738
+ _currentModule = "script";
1739
+ /** Resolved early in constructor so cleanup does not read params lazily */
1740
+ _showUsedParams = false;
1695
1741
  constructor(context, options = {}) {
1696
1742
  this.context = context;
1697
1743
  this.args = context.args;
1698
1744
  if (Object.keys(options).length > 0) {
1699
1745
  this.configure(options);
1700
1746
  }
1747
+ this._showUsedParams = this.get("showUsedParams", "boolean default false");
1748
+ if (context && typeof context.registerCleanup === "function") {
1749
+ context.registerCleanup((ctx) => {
1750
+ if (!ctx.params.getShowUsedParams()) return;
1751
+ const byModule = ctx.params.getFiguredByModule();
1752
+ const modules = Object.keys(byModule).sort();
1753
+ if (modules.length === 0) return;
1754
+ const logger = ctx.logger;
1755
+ logger.debug("[Params]: list of used params:");
1756
+ if (typeof logger.highlight !== "function") {
1757
+ for (const mod of modules) {
1758
+ logger.debug(` [${mod}]`);
1759
+ for (const [key, entry] of Object.entries(byModule[mod])) {
1760
+ logger.debug(` ${key}: ${JSON.stringify(entry.value)} (${entry.source})`);
1761
+ }
1762
+ }
1763
+ return;
1764
+ }
1765
+ for (const mod of modules) {
1766
+ logger.debug(` [${mod}]`);
1767
+ for (const [key, entry] of Object.entries(byModule[mod])) {
1768
+ const valueStr = JSON.stringify(entry.value);
1769
+ const display = entry.source === "default" ? valueStr : logger.highlight(valueStr);
1770
+ logger.debug(` ${key}: ${display} (${entry.source})`);
1771
+ }
1772
+ }
1773
+ });
1774
+ }
1775
+ }
1776
+ /** Whether --showUsedParams was requested (resolved in constructor). */
1777
+ getShowUsedParams() {
1778
+ return this._showUsedParams;
1701
1779
  }
1702
1780
  /**
1703
1781
  * Configure parameters
@@ -1716,14 +1794,15 @@ var Params = class _Params {
1716
1794
  return new _Params(context, options || {});
1717
1795
  }
1718
1796
  /**
1719
- * Track a parameter request for --stopAfter=init feature
1797
+ * Track a parameter request for --stopAfter=init and --showUsedParams
1720
1798
  */
1721
- trackParam(key, definition, value, source) {
1799
+ trackParam(key, definition, value, source, moduleName) {
1722
1800
  this.trackedParams.push({
1723
1801
  key,
1724
1802
  definition,
1725
1803
  value,
1726
- source
1804
+ source,
1805
+ module: moduleName ?? this._currentModule
1727
1806
  });
1728
1807
  }
1729
1808
  /**
@@ -1733,7 +1812,7 @@ var Params = class _Params {
1733
1812
  return [...this.trackedParams];
1734
1813
  }
1735
1814
  /**
1736
- * Get all figured parameters as a record
1815
+ * Get all figured parameters as a record (flat, last occurrence per key)
1737
1816
  * Returns all parameters that were collected during initialization,
1738
1817
  * whether from CLI args, options, or defaults
1739
1818
  */
@@ -1747,6 +1826,19 @@ var Params = class _Params {
1747
1826
  }
1748
1827
  return result;
1749
1828
  }
1829
+ /**
1830
+ * Get figured parameters grouped by module name.
1831
+ * Same param can appear in multiple modules (e.g. source, resource).
1832
+ */
1833
+ getFiguredByModule() {
1834
+ const byModule = {};
1835
+ for (const param of this.trackedParams) {
1836
+ const mod = param.module;
1837
+ if (!byModule[mod]) byModule[mod] = {};
1838
+ byModule[mod][param.key] = { value: param.value, source: param.source };
1839
+ }
1840
+ return byModule;
1841
+ }
1750
1842
  /**
1751
1843
  * Clear tracked parameters
1752
1844
  */
@@ -1865,14 +1957,19 @@ var Params = class _Params {
1865
1957
  source = "options";
1866
1958
  } else if (valFromArgs !== void 0 && valFromArgs !== null) {
1867
1959
  value = this.validate(key, valFromArgs, def);
1868
- source = "cli";
1960
+ const argsSource = this.args.getSource?.(key);
1961
+ if (argsSource === "overrides") source = "options";
1962
+ else if (argsSource === "cli" || argsSource === "env" || argsSource === "config") source = argsSource;
1963
+ else if (argsSource === "default") source = "default";
1964
+ else source = "cli";
1869
1965
  } else if (valFromParams !== void 0 && valFromParams !== null) {
1870
1966
  value = this.validate(key, valFromParams, def);
1871
- source = "options";
1967
+ source = this.paramSources[key] ?? "options";
1872
1968
  } else {
1873
1969
  value = this.validate(key, void 0, def);
1874
1970
  source = "default";
1875
1971
  }
1972
+ this.paramSources[key] = source;
1876
1973
  this.trackParam(key, definition || "string", value, source);
1877
1974
  if (value !== void 0 && def.values && !def.values.includes(value)) {
1878
1975
  throw new ParamError(`key ${key} should be one of ${def.values}`);
@@ -1893,19 +1990,63 @@ var Params = class _Params {
1893
1990
  }
1894
1991
  }
1895
1992
  /**
1896
- * Get all parameters from definitions
1897
- * Processes parameters left-to-right to support cross-parameter references
1993
+ * Get all parameters from definitions (main script).
1994
+ * Same as getAllForModule("script", defs). Processes left-to-right for cross-parameter references.
1898
1995
  */
1899
1996
  getAll(defs) {
1900
- const res = {};
1901
- for (const [k, def] of Object.entries(defs)) {
1902
- const value = this.get(k, def);
1903
- res[k] = value;
1904
- if (value !== void 0) {
1905
- this.params[k] = value;
1997
+ return this.getAllForModule("script", defs);
1998
+ }
1999
+ /**
2000
+ * Get all parameters from definitions for a given module name.
2001
+ * Figured params are grouped by module when using --showUsedParams.
2002
+ * Processes parameters left-to-right to support cross-parameter references.
2003
+ * If moduleName is omitted, it is inferred from the caller's file path (directory name under src/).
2004
+ */
2005
+ getAllForModule(moduleNameOrDefs, defs) {
2006
+ let moduleName;
2007
+ let definitions;
2008
+ if (defs !== void 0) {
2009
+ moduleName = moduleNameOrDefs;
2010
+ definitions = defs;
2011
+ } else {
2012
+ definitions = moduleNameOrDefs;
2013
+ moduleName = this._inferModuleNameFromStack();
2014
+ }
2015
+ const prev = this._currentModule;
2016
+ this._currentModule = moduleName;
2017
+ try {
2018
+ const res = {};
2019
+ for (const [k, def] of Object.entries(definitions)) {
2020
+ const value = this.get(k, def);
2021
+ res[k] = value;
2022
+ if (value !== void 0) {
2023
+ this.params[k] = value;
2024
+ }
1906
2025
  }
2026
+ return res;
2027
+ } finally {
2028
+ this._currentModule = prev;
1907
2029
  }
1908
- return res;
2030
+ }
2031
+ /**
2032
+ * Infer module name from call stack: first caller outside params/index gives path like .../src/<moduleName>/...
2033
+ */
2034
+ _inferModuleNameFromStack() {
2035
+ const stack = new Error().stack;
2036
+ if (!stack) return "script";
2037
+ const lines = stack.split("\n");
2038
+ const paramsIndexPath = "params" + (typeof process !== "undefined" && process.platform === "win32" ? "\\" : "/") + "index.";
2039
+ for (const line of lines) {
2040
+ const parenMatch = line.match(/\(([^)]+)\)/);
2041
+ if (!parenMatch) continue;
2042
+ const parts = parenMatch[1].split(":");
2043
+ if (parts.length < 3) continue;
2044
+ const path = parts.slice(0, -2).join(":").replace(/^file:\/\//, "");
2045
+ if (!path || path.includes(paramsIndexPath)) continue;
2046
+ const srcMatch = path.match(/[/\\]src[/\\]([^/\\]+)(?:[/\\]|$)/);
2047
+ if (srcMatch) return srcMatch[1];
2048
+ }
2049
+ return "script";
1909
2050
  }
1910
2051
  /**
1911
2052
  * Run all registered getters for a key
@@ -2015,8 +2156,7 @@ var Logger = class _Logger {
2015
2156
  this.updateTransport();
2016
2157
  }
2017
2158
  /**
2018
- * Configure logger options
2019
- * Only parameters present in options are updated
2159
+ * Configure logger options. Accepts both LoggerOptions shape and flat param names (levels string, progressWithTimes, progressThrottleMs).
2020
2160
  */
2021
2161
  configure(options) {
2022
2162
  if (options.mode !== void 0) {
@@ -2026,32 +2166,24 @@ var Logger = class _Logger {
2026
2166
  this.options.route = options.route;
2027
2167
  this.updateTransport();
2028
2168
  }
2029
- if (options.prefix !== void 0) {
2030
- this.options.prefix = options.prefix;
2031
- }
2032
- if (options.silent !== void 0) {
2033
- this.options.silent = options.silent;
2034
- }
2035
- if (options.showLevel !== void 0) {
2036
- this.options.showLevel = options.showLevel;
2037
- }
2038
- if (options.timestamp !== void 0) {
2039
- this.options.timestamp = options.timestamp;
2040
- }
2169
+ if (options.prefix !== void 0) this.options.prefix = options.prefix;
2170
+ if (options.silent !== void 0) this.options.silent = options.silent;
2171
+ if (options.showLevel !== void 0) this.options.showLevel = options.showLevel;
2172
+ if (options.timestamp !== void 0) this.options.timestamp = options.timestamp;
2041
2173
  if (options.levels !== void 0) {
2042
- this.options.levels = this.normalizeLevels(options.levels);
2174
+ const levels = typeof options.levels === "string" ? options.levels.split(",") : options.levels;
2175
+ this.options.levels = this.normalizeLevels(levels);
2043
2176
  }
2044
2177
  if (options.progress !== void 0) {
2045
- if (options.progress.withTimes !== void 0) {
2046
- this.options.progressTimes = options.progress.withTimes;
2047
- }
2048
- if (options.progress.throttleMs !== void 0) {
2049
- this.options.progressThrottle = options.progress.throttleMs;
2050
- }
2178
+ if (options.progress.withTimes !== void 0) this.options.progressTimes = options.progress.withTimes;
2179
+ if (options.progress.throttleMs !== void 0) this.options.progressThrottle = options.progress.throttleMs;
2051
2180
  }
2181
+ const flat = options;
2182
+ if (flat.progressWithTimes !== void 0) this.options.progressTimes = flat.progressWithTimes;
2183
+ if (flat.progressThrottleMs !== void 0) this.options.progressThrottle = flat.progressThrottleMs;
2052
2184
  }
2053
2185
  /**
2054
- * Initialize logger from context and CLI parameters
2186
+ * Initialize logger from context and CLI parameters. Whatever is in options goes (after discovered params).
2055
2187
  */
2056
2188
  static init(context, options) {
2057
2189
  const paramDefs = {
@@ -2065,20 +2197,8 @@ var Logger = class _Logger {
2065
2197
  progressWithTimes: "boolean default false",
2066
2198
  progressThrottleMs: "number"
2067
2199
  };
2068
- const cliParams = context.params.getAll(paramDefs);
2069
- const config2 = {
2070
- mode: options?.mode ?? cliParams.mode,
2071
- route: options?.route ?? cliParams.route,
2072
- prefix: options?.prefix ?? cliParams.prefix,
2073
- silent: options?.silent ?? cliParams.silent,
2074
- showLevel: options?.showLevel ?? cliParams.showLevel,
2075
- timestamp: options?.timestamp ?? cliParams.timestamp,
2076
- levels: options?.levels ?? (cliParams.levels ? cliParams.levels.split(",") : void 0),
2077
- progress: options?.progress ?? {
2078
- withTimes: cliParams.progressWithTimes,
2079
- throttleMs: cliParams.progressThrottleMs
2080
- }
2081
- };
2200
+ const discovered = context.params.getAllForModule(paramDefs);
2201
+ const config2 = { ...discovered, ...options };
2082
2202
  const logger = new _Logger(context, config2);
2083
2203
  context.logger = logger;
2084
2204
  return logger;
@@ -2105,6 +2225,10 @@ var Logger = class _Logger {
2105
2225
  }
2106
2226
  this.options.mode = mode;
2107
2227
  }
2228
+ /** Returns a styled string (bright white) for highlighting; keeps chalk inside logger. */
2229
+ highlight(text) {
2230
+ return chalk.whiteBright(text);
2231
+ }
2108
2232
  debug(message, ...chunks) {
2109
2233
  this.out({ level: "debug", message, chunks });
2110
2234
  }
@@ -2280,12 +2404,7 @@ function extractComponentOptions(opts, componentName) {
2280
2404
  return componentOptions;
2281
2405
  }
2282
2406
  function setup(opts = {}) {
2283
- const args = Args.init({
2284
- overrides: opts.overrides || {},
2285
- defaults: opts.defaults || {}
2286
- });
2287
2407
  const partialContext = {
2288
- args,
2289
2408
  emitter: new EventEmitter(),
2290
2409
  isStop: () => false,
2291
2410
  cleanupFunctions: [],
@@ -2293,6 +2412,11 @@ function setup(opts = {}) {
2293
2412
  partialContext.cleanupFunctions.push(fn);
2294
2413
  }
2295
2414
  };
2415
+ const args = Args.init(partialContext, {
2416
+ overrides: opts.overrides || {},
2417
+ defaults: opts.defaults || {}
2418
+ });
2419
+ partialContext.args = args;
2296
2420
  const params = Params.init(partialContext, opts.overrides || {});
2297
2421
  partialContext.params = params;
2298
2422
  const loggerOptions = extractComponentOptions(opts, "logger");
@@ -2353,6 +2477,7 @@ async function init(flow, opts = {}) {
2353
2477
  context.isStop = () => stop;
2354
2478
  context = await setupModules(context, opts);
2355
2479
  const stopAfter = context.args.get("stopAfter");
2480
+ const stopAllowance = context.params.get("stopAllowance", "number default 5");
2356
2481
  if (stopAfter === "init") {
2357
2482
  printAllParameters(context);
2358
2483
  process.exit(0);
@@ -2363,13 +2488,8 @@ async function init(flow, opts = {}) {
2363
2488
  process.exit(2);
2364
2489
  }
2365
2490
  stop = true;
2366
- let allowance = 5;
2367
- try {
2368
- allowance = context.params.get("stopAllowance", "number default 5");
2369
- } catch {
2370
- }
2371
- context.logger.info(`>> emitting stop with allowance ${allowance}`);
2372
- context.emitter.emit("stop", allowance);
2491
+ context.logger.info(`>> emitting stop with allowance ${stopAllowance}`);
2492
+ context.emitter.emit("stop", stopAllowance);
2373
2493
  });
2374
2494
  await flow(context);
2375
2495
  } catch (error) {
@@ -2400,10 +2520,6 @@ async function init(flow, opts = {}) {
2400
2520
  context.logger.warn("[cleanup] error in cleanup function:", error);
2401
2521
  }
2402
2522
  }
2403
- const unusedArgs = context.args.getUnused();
2404
- if (unusedArgs.length > 0) {
2405
- context.logger.warn("Unused CLI args:", unusedArgs.join(", "));
2406
- }
2407
2523
  }
2408
2524
  }
2409
2525
  }