@nmakarov/cli-toolkit 0.7.2 → 0.9.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/init.cjs CHANGED
@@ -1127,7 +1127,7 @@ module.exports = __toCommonJS(init_exports);
1127
1127
  var import_fs = require("fs");
1128
1128
  var import_path = require("path");
1129
1129
  var import_dotenv = require("dotenv");
1130
- var Args = class {
1130
+ var Args = class _Args {
1131
1131
  args = {};
1132
1132
  flags = {};
1133
1133
  options = {};
@@ -1142,10 +1142,13 @@ var Args = class {
1142
1142
  configsLoaded = [];
1143
1143
  env = "local";
1144
1144
  constructor(config2 = {}) {
1145
- this.aliases = config2.aliases || {};
1146
- this.overrides = config2.overrides || {};
1147
- this.defaults = config2.defaults || {};
1148
- this.prefixes = config2.prefixes || ["not", "no"];
1145
+ this.aliases = {};
1146
+ this.overrides = {};
1147
+ this.defaults = {};
1148
+ this.prefixes = ["not", "no"];
1149
+ if (Object.keys(config2).length > 0) {
1150
+ this.configure(config2);
1151
+ }
1149
1152
  const args = config2.args || process.argv.slice(2);
1150
1153
  this.parseArgs(args);
1151
1154
  this.env = this.get("env")?.toLowerCase() || "local";
@@ -1153,6 +1156,33 @@ var Args = class {
1153
1156
  this.loadConfigFiles();
1154
1157
  this.checkConflicts();
1155
1158
  }
1159
+ /**
1160
+ * Configure Args options
1161
+ * Only parameters present in config are updated
1162
+ * Note: Args is special - it's initialized first, so it can't take context
1163
+ */
1164
+ configure(config2) {
1165
+ if (config2.aliases !== void 0) {
1166
+ this.aliases = config2.aliases;
1167
+ }
1168
+ if (config2.overrides !== void 0) {
1169
+ this.overrides = config2.overrides;
1170
+ }
1171
+ if (config2.defaults !== void 0) {
1172
+ this.defaults = config2.defaults;
1173
+ }
1174
+ if (config2.prefixes !== void 0) {
1175
+ this.prefixes = config2.prefixes;
1176
+ }
1177
+ }
1178
+ /**
1179
+ * Initialize Args instance
1180
+ * Note: Args is special - it's initialized first, so it can't take context
1181
+ * This static method is for consistency with other components
1182
+ */
1183
+ static init(config2 = {}) {
1184
+ return new _Args(config2);
1185
+ }
1156
1186
  /**
1157
1187
  * Parse command line arguments
1158
1188
  */
@@ -1291,11 +1321,11 @@ var Args = class {
1291
1321
  */
1292
1322
  get(key) {
1293
1323
  const resolvedKey = this.aliases[key] || key;
1294
- this.usedKeys.add(resolvedKey);
1324
+ const lcKey = resolvedKey.toLowerCase();
1325
+ this.usedKeys.add(lcKey);
1295
1326
  if (this.overrides[resolvedKey] !== void 0) {
1296
1327
  return this.overrides[resolvedKey];
1297
1328
  }
1298
- const lcKey = resolvedKey.toLowerCase();
1299
1329
  const lcKeyWithEnv = `${lcKey}${this.env ? `_${this.env.toLowerCase()}` : ""}`;
1300
1330
  if (this.env && this.args[lcKeyWithEnv] !== void 0) {
1301
1331
  return this.args[lcKeyWithEnv];
@@ -1668,18 +1698,76 @@ var joiStringArrayType = (type) => (value, helpers) => {
1668
1698
  };
1669
1699
 
1670
1700
  // src/params/index.ts
1671
- var Params = class {
1701
+ var Params = class _Params {
1702
+ context;
1703
+ // Partial context during initialization
1672
1704
  params = {};
1673
1705
  definitions = {};
1674
1706
  args;
1675
1707
  paramSetters = [];
1676
1708
  paramGetters = [];
1677
- constructor({ args }, opts = {}) {
1678
- this.args = args;
1679
- for (const [k, v] of Object.entries(opts)) {
1709
+ trackedParams = [];
1710
+ constructor(context, options = {}) {
1711
+ this.context = context;
1712
+ this.args = context.args;
1713
+ if (Object.keys(options).length > 0) {
1714
+ this.configure(options);
1715
+ }
1716
+ }
1717
+ /**
1718
+ * Configure parameters
1719
+ * Only parameters present in options are updated
1720
+ */
1721
+ configure(options) {
1722
+ for (const [k, v] of Object.entries(options)) {
1680
1723
  this.params[k] = v;
1681
1724
  }
1682
1725
  }
1726
+ /**
1727
+ * Initialize Params from context and CLI parameters
1728
+ * Note: Params is special - it's initialized early with partial context
1729
+ */
1730
+ static init(context, options) {
1731
+ return new _Params(context, options || {});
1732
+ }
1733
+ /**
1734
+ * Track a parameter request for --stopAfter=init feature
1735
+ */
1736
+ trackParam(key, definition, value, source) {
1737
+ this.trackedParams.push({
1738
+ key,
1739
+ definition,
1740
+ value,
1741
+ source
1742
+ });
1743
+ }
1744
+ /**
1745
+ * Get all tracked parameters (for --stopAfter=init)
1746
+ */
1747
+ getTrackedParams() {
1748
+ return [...this.trackedParams];
1749
+ }
1750
+ /**
1751
+ * Get all figured parameters as a record
1752
+ * Returns all parameters that were collected during initialization,
1753
+ * whether from CLI args, options, or defaults
1754
+ */
1755
+ getAllFigured() {
1756
+ const result = {};
1757
+ for (const param of this.trackedParams) {
1758
+ result[param.key] = {
1759
+ value: param.value,
1760
+ source: param.source
1761
+ };
1762
+ }
1763
+ return result;
1764
+ }
1765
+ /**
1766
+ * Clear tracked parameters
1767
+ */
1768
+ clearTrackedParams() {
1769
+ this.trackedParams = [];
1770
+ }
1683
1771
  /**
1684
1772
  * Assign a parameter definition
1685
1773
  */
@@ -1753,6 +1841,8 @@ var Params = class {
1753
1841
  type = type.default(defValObj.value);
1754
1842
  } else if (str.match(/required/)) {
1755
1843
  type = type.required();
1844
+ } else {
1845
+ type = type.optional();
1756
1846
  }
1757
1847
  return type;
1758
1848
  }
@@ -1760,7 +1850,12 @@ var Params = class {
1760
1850
  * Validate a value against a definition
1761
1851
  */
1762
1852
  validate(key, val, def) {
1763
- const { value, error } = def.type.validate(val, { context: { params: this.params } });
1853
+ const normalizedVal = val === null ? void 0 : val;
1854
+ const { value, error } = def.type.validate(normalizedVal, {
1855
+ context: { params: this.params },
1856
+ abortEarly: false,
1857
+ allowUnknown: false
1858
+ });
1764
1859
  if (error) {
1765
1860
  const errs = error.details.map((el) => el.message).join(", ");
1766
1861
  throw new ParamError(`"${key}" validation error: ${errs}`);
@@ -1778,11 +1873,26 @@ var Params = class {
1778
1873
  }
1779
1874
  const valFromArgs = this.args.get(key);
1780
1875
  const valFromParams = this.params[key];
1781
- const res = valFromGetters ? this.validate(key, valFromGetters, def) : valFromArgs ? this.validate(key, valFromArgs, def) : this.validate(key, valFromParams, def);
1782
- if (res !== void 0 && def.values && !def.values.includes(res)) {
1876
+ let source = "default";
1877
+ let value;
1878
+ if (valFromGetters !== void 0 && valFromGetters !== null) {
1879
+ value = this.validate(key, valFromGetters, def);
1880
+ source = "options";
1881
+ } else if (valFromArgs !== void 0 && valFromArgs !== null) {
1882
+ value = this.validate(key, valFromArgs, def);
1883
+ source = "cli";
1884
+ } else if (valFromParams !== void 0 && valFromParams !== null) {
1885
+ value = this.validate(key, valFromParams, def);
1886
+ source = "options";
1887
+ } else {
1888
+ value = this.validate(key, void 0, def);
1889
+ source = "default";
1890
+ }
1891
+ this.trackParam(key, definition || "string", value, source);
1892
+ if (value !== void 0 && def.values && !def.values.includes(value)) {
1783
1893
  throw new ParamError(`key ${key} should be one of ${def.values}`);
1784
1894
  }
1785
- return res;
1895
+ return value;
1786
1896
  }
1787
1897
  /**
1788
1898
  * Set a parameter value with validation
@@ -1816,10 +1926,10 @@ var Params = class {
1816
1926
  * Run all registered getters for a key
1817
1927
  */
1818
1928
  runAllRegisteredGetters(key) {
1819
- let val = null;
1929
+ let val = void 0;
1820
1930
  for (const getter of this.paramGetters) {
1821
1931
  val = getter(key, this.definitions[key]);
1822
- if (val !== void 0) {
1932
+ if (val !== void 0 && val !== null) {
1823
1933
  break;
1824
1934
  }
1825
1935
  }
@@ -1890,6 +2000,7 @@ var ALL_LEVELS = [
1890
2000
  "response",
1891
2001
  "progress"
1892
2002
  ];
2003
+ var MAX_LEVEL_LENGTH = Math.max(...ALL_LEVELS.map((level) => level.toUpperCase().length));
1893
2004
  var LEVEL_COLORS = {
1894
2005
  error: import_chalk.default.red.bold,
1895
2006
  warn: import_chalk.default.rgb(255, 165, 0),
@@ -1903,13 +2014,104 @@ var LEVEL_COLORS = {
1903
2014
  progress: import_chalk.default.green,
1904
2015
  results: import_chalk.default.magenta
1905
2016
  };
1906
- var CliToolkitLogger = class {
2017
+ var Logger = class _Logger {
2018
+ context;
2019
+ // Partial context during initialization
1907
2020
  options;
1908
2021
  transport;
1909
2022
  startTimes = {};
1910
2023
  lastProgressTimes = {};
1911
- constructor(options = {}) {
1912
- this.options = this.normalizeOptions(options);
2024
+ constructor(context, options = {}) {
2025
+ this.context = context;
2026
+ this.options = this.getDefaultOptions();
2027
+ if (options) {
2028
+ this.configure(options);
2029
+ }
2030
+ this.updateTransport();
2031
+ }
2032
+ /**
2033
+ * Configure logger options
2034
+ * Only parameters present in options are updated
2035
+ */
2036
+ configure(options) {
2037
+ if (options.mode !== void 0) {
2038
+ this.options.mode = this.isValidMode(options.mode) ? options.mode : "text";
2039
+ }
2040
+ if (options.route !== void 0) {
2041
+ this.options.route = options.route;
2042
+ this.updateTransport();
2043
+ }
2044
+ if (options.prefix !== void 0) {
2045
+ this.options.prefix = options.prefix;
2046
+ }
2047
+ if (options.silent !== void 0) {
2048
+ this.options.silent = options.silent;
2049
+ }
2050
+ if (options.showLevel !== void 0) {
2051
+ this.options.showLevel = options.showLevel;
2052
+ }
2053
+ if (options.timestamp !== void 0) {
2054
+ this.options.timestamp = options.timestamp;
2055
+ }
2056
+ if (options.levels !== void 0) {
2057
+ this.options.levels = this.normalizeLevels(options.levels);
2058
+ }
2059
+ if (options.progress !== void 0) {
2060
+ if (options.progress.withTimes !== void 0) {
2061
+ this.options.progressTimes = options.progress.withTimes;
2062
+ }
2063
+ if (options.progress.throttleMs !== void 0) {
2064
+ this.options.progressThrottle = options.progress.throttleMs;
2065
+ }
2066
+ }
2067
+ }
2068
+ /**
2069
+ * Initialize logger from context and CLI parameters
2070
+ */
2071
+ static init(context, options) {
2072
+ const paramDefs = {
2073
+ mode: "string default text",
2074
+ route: "string default console",
2075
+ prefix: "string",
2076
+ silent: "boolean default false",
2077
+ showLevel: "boolean default true",
2078
+ timestamp: "boolean default false",
2079
+ levels: "string",
2080
+ progressWithTimes: "boolean default false",
2081
+ progressThrottleMs: "number"
2082
+ };
2083
+ const cliParams = context.params.getAll(paramDefs);
2084
+ const config2 = {
2085
+ mode: options?.mode ?? cliParams.mode,
2086
+ route: options?.route ?? cliParams.route,
2087
+ prefix: options?.prefix ?? cliParams.prefix,
2088
+ silent: options?.silent ?? cliParams.silent,
2089
+ showLevel: options?.showLevel ?? cliParams.showLevel,
2090
+ timestamp: options?.timestamp ?? cliParams.timestamp,
2091
+ levels: options?.levels ?? (cliParams.levels ? cliParams.levels.split(",") : void 0),
2092
+ progress: options?.progress ?? {
2093
+ withTimes: cliParams.progressWithTimes,
2094
+ throttleMs: cliParams.progressThrottleMs
2095
+ }
2096
+ };
2097
+ const logger = new _Logger(context, config2);
2098
+ context.logger = logger;
2099
+ return logger;
2100
+ }
2101
+ getDefaultOptions() {
2102
+ return {
2103
+ mode: "text",
2104
+ route: this.shouldUseIpcRoute() ? "ipc" : "console",
2105
+ prefix: void 0,
2106
+ silent: false,
2107
+ showLevel: true,
2108
+ timestamp: false,
2109
+ levels: ALL_LEVELS,
2110
+ progressTimes: false,
2111
+ progressThrottle: void 0
2112
+ };
2113
+ }
2114
+ updateTransport() {
1913
2115
  this.transport = this.options.route === "ipc" ? new ParentProcessTransport() : new ConsoleTransport();
1914
2116
  }
1915
2117
  setMode(mode) {
@@ -2018,7 +2220,7 @@ var CliToolkitLogger = class {
2018
2220
  parts.push(now.toISOString());
2019
2221
  }
2020
2222
  if (this.options.showLevel) {
2021
- parts.push(struct.level.toUpperCase());
2223
+ parts.push(struct.level.toUpperCase().padEnd(MAX_LEVEL_LENGTH));
2022
2224
  }
2023
2225
  if (struct.level === "progress") {
2024
2226
  if (struct.prefix) {
@@ -2052,22 +2254,6 @@ var CliToolkitLogger = class {
2052
2254
  inspectChunks(chunks) {
2053
2255
  return chunks.map((chunk) => import_util.default.inspect(chunk, { colors: true, depth: null })).join(" ");
2054
2256
  }
2055
- normalizeOptions(options) {
2056
- const { route, mode, prefix, silent, showLevel, timestamp, levels, progress } = options;
2057
- const shouldUseIpc = this.shouldUseIpcRoute();
2058
- const normalized = {
2059
- mode: this.isValidMode(mode) ? mode : "text",
2060
- route: route ?? (shouldUseIpc ? "ipc" : "console"),
2061
- prefix,
2062
- silent: silent ?? false,
2063
- showLevel: showLevel ?? true,
2064
- timestamp: timestamp ?? false,
2065
- levels: this.normalizeLevels(levels),
2066
- progressTimes: progress?.withTimes ?? false,
2067
- progressThrottle: progress?.throttleMs
2068
- };
2069
- return normalized;
2070
- }
2071
2257
  shouldUseIpcRoute() {
2072
2258
  if (process.env.VITEST || process.env.NODE_ENV === "test") {
2073
2259
  return false;
@@ -2098,35 +2284,44 @@ var CliToolkitLogger = class {
2098
2284
 
2099
2285
  // src/init/index.ts
2100
2286
  var import_events = require("events");
2287
+ function extractComponentOptions(opts, componentName) {
2288
+ const reservedKeys = ["overrides", "defaults", "modules"];
2289
+ const componentOptions = {};
2290
+ for (const [key, value] of Object.entries(opts)) {
2291
+ if (!reservedKeys.includes(key)) {
2292
+ componentOptions[key] = value;
2293
+ }
2294
+ }
2295
+ return componentOptions;
2296
+ }
2101
2297
  function setup(opts = {}) {
2102
- const args = new Args({
2298
+ const args = Args.init({
2103
2299
  overrides: opts.overrides || {},
2104
2300
  defaults: opts.defaults || {}
2105
2301
  });
2106
- const params = new Params({ args }, opts.overrides || {});
2107
- const loggerOptions = opts.logger || {};
2108
- const logger = new CliToolkitLogger({
2109
- mode: loggerOptions.mode || "text",
2110
- route: loggerOptions.route || "console",
2111
- prefix: loggerOptions.prefix,
2112
- silent: loggerOptions.silent,
2113
- showLevel: loggerOptions.showLevel,
2114
- timestamp: loggerOptions.timestamp,
2115
- levels: loggerOptions.levels
2116
- });
2117
- const cleanupFunctions = [];
2118
- const context = {
2302
+ const partialContext = {
2119
2303
  args,
2120
- params,
2121
- logger,
2122
2304
  emitter: new import_events.EventEmitter(),
2123
2305
  isStop: () => false,
2124
- // Will be set in init function
2125
- cleanupFunctions,
2306
+ cleanupFunctions: [],
2126
2307
  registerCleanup: (fn) => {
2127
- cleanupFunctions.push(fn);
2308
+ partialContext.cleanupFunctions.push(fn);
2128
2309
  }
2129
2310
  };
2311
+ const params = Params.init(partialContext, opts.overrides || {});
2312
+ partialContext.params = params;
2313
+ const loggerOptions = extractComponentOptions(opts, "logger");
2314
+ const logger = Logger.init(partialContext, loggerOptions);
2315
+ partialContext.logger = logger;
2316
+ const context = {
2317
+ args,
2318
+ params,
2319
+ logger,
2320
+ emitter: partialContext.emitter,
2321
+ isStop: partialContext.isStop,
2322
+ cleanupFunctions: partialContext.cleanupFunctions,
2323
+ registerCleanup: partialContext.registerCleanup
2324
+ };
2130
2325
  logger.debug("[setup] completed successfully");
2131
2326
  return context;
2132
2327
  }
@@ -2137,6 +2332,22 @@ async function setupModules(context, opts = {}) {
2137
2332
  context.logger.debug("[setupModules] completed successfully");
2138
2333
  return context;
2139
2334
  }
2335
+ function printAllParameters(context) {
2336
+ const trackedParams = context.params.getTrackedParams();
2337
+ console.log("\n=== All Figured Parameters ===");
2338
+ console.log("\nComponent: Logger");
2339
+ const loggerParams = trackedParams.filter(
2340
+ (p) => ["mode", "route", "prefix", "silent", "showLevel", "timestamp", "levels"].includes(p.key)
2341
+ );
2342
+ if (loggerParams.length > 0) {
2343
+ loggerParams.forEach((p) => {
2344
+ console.log(` ${p.key}: ${JSON.stringify(p.value)} (from ${p.source})`);
2345
+ });
2346
+ } else {
2347
+ console.log(" (no parameters requested)");
2348
+ }
2349
+ console.log("\n=== End Parameters ===\n");
2350
+ }
2140
2351
  async function init(flow, opts = {}) {
2141
2352
  let stop = false;
2142
2353
  let context = null;
@@ -2156,6 +2367,11 @@ async function init(flow, opts = {}) {
2156
2367
  context = setup(opts);
2157
2368
  context.isStop = () => stop;
2158
2369
  context = await setupModules(context, opts);
2370
+ const stopAfter = context.args.get("stopAfter");
2371
+ if (stopAfter === "init") {
2372
+ printAllParameters(context);
2373
+ process.exit(0);
2374
+ }
2159
2375
  process.on("SIGINT", async () => {
2160
2376
  if (stop) {
2161
2377
  context.logger.warn("[process] killed");
@@ -2173,14 +2389,21 @@ async function init(flow, opts = {}) {
2173
2389
  await flow(context);
2174
2390
  } catch (error) {
2175
2391
  const errorLocation = error instanceof Error && error.stack ? error.stack.split("\n")[1]?.trim() || "Unknown location" : "Unknown location";
2392
+ const logError = (msg, ...args) => {
2393
+ if (context?.logger) {
2394
+ context.logger.error(msg, ...args);
2395
+ } else {
2396
+ console.error(msg, ...args);
2397
+ }
2398
+ };
2176
2399
  if (error instanceof ParamError) {
2177
- context?.logger.error(`[params]: ${error.message} (${errorLocation})`);
2400
+ logError(`[params]: ${error.message} (${errorLocation})`);
2178
2401
  process.exitCode = 3;
2179
2402
  } else if (error instanceof InitError) {
2180
- context?.logger.error(`[init]: ${error.message} (${errorLocation})`);
2403
+ logError(`[init]: ${error.message} (${errorLocation})`);
2181
2404
  process.exitCode = 4;
2182
2405
  } else {
2183
- context?.logger.error(`[other] error:`, error, errorLocation);
2406
+ logError(`[other] error:`, error, errorLocation);
2184
2407
  process.exitCode = 5;
2185
2408
  }
2186
2409
  } finally {