@nmakarov/cli-toolkit 0.7.2 → 0.8.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.js CHANGED
@@ -1101,7 +1101,7 @@ var init_screen = __esm({
1101
1101
  import { readFileSync, existsSync } from "fs";
1102
1102
  import { resolve, dirname, basename, extname, join, isAbsolute } from "path";
1103
1103
  import { config } from "dotenv";
1104
- var Args = class {
1104
+ var Args = class _Args {
1105
1105
  args = {};
1106
1106
  flags = {};
1107
1107
  options = {};
@@ -1116,10 +1116,13 @@ var Args = class {
1116
1116
  configsLoaded = [];
1117
1117
  env = "local";
1118
1118
  constructor(config2 = {}) {
1119
- this.aliases = config2.aliases || {};
1120
- this.overrides = config2.overrides || {};
1121
- this.defaults = config2.defaults || {};
1122
- this.prefixes = config2.prefixes || ["not", "no"];
1119
+ this.aliases = {};
1120
+ this.overrides = {};
1121
+ this.defaults = {};
1122
+ this.prefixes = ["not", "no"];
1123
+ if (Object.keys(config2).length > 0) {
1124
+ this.configure(config2);
1125
+ }
1123
1126
  const args = config2.args || process.argv.slice(2);
1124
1127
  this.parseArgs(args);
1125
1128
  this.env = this.get("env")?.toLowerCase() || "local";
@@ -1127,6 +1130,33 @@ var Args = class {
1127
1130
  this.loadConfigFiles();
1128
1131
  this.checkConflicts();
1129
1132
  }
1133
+ /**
1134
+ * Configure Args options
1135
+ * Only parameters present in config are updated
1136
+ * Note: Args is special - it's initialized first, so it can't take context
1137
+ */
1138
+ configure(config2) {
1139
+ if (config2.aliases !== void 0) {
1140
+ this.aliases = config2.aliases;
1141
+ }
1142
+ if (config2.overrides !== void 0) {
1143
+ this.overrides = config2.overrides;
1144
+ }
1145
+ if (config2.defaults !== void 0) {
1146
+ this.defaults = config2.defaults;
1147
+ }
1148
+ if (config2.prefixes !== void 0) {
1149
+ this.prefixes = config2.prefixes;
1150
+ }
1151
+ }
1152
+ /**
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
1156
+ */
1157
+ static init(config2 = {}) {
1158
+ return new _Args(config2);
1159
+ }
1130
1160
  /**
1131
1161
  * Parse command line arguments
1132
1162
  */
@@ -1642,18 +1672,76 @@ var joiStringArrayType = (type) => (value, helpers) => {
1642
1672
  };
1643
1673
 
1644
1674
  // src/params/index.ts
1645
- var Params = class {
1675
+ var Params = class _Params {
1676
+ context;
1677
+ // Partial context during initialization
1646
1678
  params = {};
1647
1679
  definitions = {};
1648
1680
  args;
1649
1681
  paramSetters = [];
1650
1682
  paramGetters = [];
1651
- constructor({ args }, opts = {}) {
1652
- this.args = args;
1653
- for (const [k, v] of Object.entries(opts)) {
1683
+ trackedParams = [];
1684
+ constructor(context, options = {}) {
1685
+ this.context = context;
1686
+ this.args = context.args;
1687
+ if (Object.keys(options).length > 0) {
1688
+ this.configure(options);
1689
+ }
1690
+ }
1691
+ /**
1692
+ * Configure parameters
1693
+ * Only parameters present in options are updated
1694
+ */
1695
+ configure(options) {
1696
+ for (const [k, v] of Object.entries(options)) {
1654
1697
  this.params[k] = v;
1655
1698
  }
1656
1699
  }
1700
+ /**
1701
+ * Initialize Params from context and CLI parameters
1702
+ * Note: Params is special - it's initialized early with partial context
1703
+ */
1704
+ static init(context, options) {
1705
+ return new _Params(context, options || {});
1706
+ }
1707
+ /**
1708
+ * Track a parameter request for --stopAfter=init feature
1709
+ */
1710
+ trackParam(key, definition, value, source) {
1711
+ this.trackedParams.push({
1712
+ key,
1713
+ definition,
1714
+ value,
1715
+ source
1716
+ });
1717
+ }
1718
+ /**
1719
+ * Get all tracked parameters (for --stopAfter=init)
1720
+ */
1721
+ getTrackedParams() {
1722
+ return [...this.trackedParams];
1723
+ }
1724
+ /**
1725
+ * Get all figured parameters as a record
1726
+ * Returns all parameters that were collected during initialization,
1727
+ * whether from CLI args, options, or defaults
1728
+ */
1729
+ getAllFigured() {
1730
+ const result = {};
1731
+ for (const param of this.trackedParams) {
1732
+ result[param.key] = {
1733
+ value: param.value,
1734
+ source: param.source
1735
+ };
1736
+ }
1737
+ return result;
1738
+ }
1739
+ /**
1740
+ * Clear tracked parameters
1741
+ */
1742
+ clearTrackedParams() {
1743
+ this.trackedParams = [];
1744
+ }
1657
1745
  /**
1658
1746
  * Assign a parameter definition
1659
1747
  */
@@ -1727,6 +1815,8 @@ var Params = class {
1727
1815
  type = type.default(defValObj.value);
1728
1816
  } else if (str.match(/required/)) {
1729
1817
  type = type.required();
1818
+ } else {
1819
+ type = type.optional();
1730
1820
  }
1731
1821
  return type;
1732
1822
  }
@@ -1734,7 +1824,12 @@ var Params = class {
1734
1824
  * Validate a value against a definition
1735
1825
  */
1736
1826
  validate(key, val, def) {
1737
- const { value, error } = def.type.validate(val, { context: { params: this.params } });
1827
+ const normalizedVal = val === null ? void 0 : val;
1828
+ const { value, error } = def.type.validate(normalizedVal, {
1829
+ context: { params: this.params },
1830
+ abortEarly: false,
1831
+ allowUnknown: false
1832
+ });
1738
1833
  if (error) {
1739
1834
  const errs = error.details.map((el) => el.message).join(", ");
1740
1835
  throw new ParamError(`"${key}" validation error: ${errs}`);
@@ -1752,11 +1847,26 @@ var Params = class {
1752
1847
  }
1753
1848
  const valFromArgs = this.args.get(key);
1754
1849
  const valFromParams = this.params[key];
1755
- const res = valFromGetters ? this.validate(key, valFromGetters, def) : valFromArgs ? this.validate(key, valFromArgs, def) : this.validate(key, valFromParams, def);
1756
- if (res !== void 0 && def.values && !def.values.includes(res)) {
1850
+ let source = "default";
1851
+ let value;
1852
+ if (valFromGetters !== void 0 && valFromGetters !== null) {
1853
+ value = this.validate(key, valFromGetters, def);
1854
+ source = "options";
1855
+ } else if (valFromArgs !== void 0 && valFromArgs !== null) {
1856
+ value = this.validate(key, valFromArgs, def);
1857
+ source = "cli";
1858
+ } else if (valFromParams !== void 0 && valFromParams !== null) {
1859
+ value = this.validate(key, valFromParams, def);
1860
+ source = "options";
1861
+ } else {
1862
+ value = this.validate(key, void 0, def);
1863
+ source = "default";
1864
+ }
1865
+ this.trackParam(key, definition || "string", value, source);
1866
+ if (value !== void 0 && def.values && !def.values.includes(value)) {
1757
1867
  throw new ParamError(`key ${key} should be one of ${def.values}`);
1758
1868
  }
1759
- return res;
1869
+ return value;
1760
1870
  }
1761
1871
  /**
1762
1872
  * Set a parameter value with validation
@@ -1790,10 +1900,10 @@ var Params = class {
1790
1900
  * Run all registered getters for a key
1791
1901
  */
1792
1902
  runAllRegisteredGetters(key) {
1793
- let val = null;
1903
+ let val = void 0;
1794
1904
  for (const getter of this.paramGetters) {
1795
1905
  val = getter(key, this.definitions[key]);
1796
- if (val !== void 0) {
1906
+ if (val !== void 0 && val !== null) {
1797
1907
  break;
1798
1908
  }
1799
1909
  }
@@ -1864,6 +1974,7 @@ var ALL_LEVELS = [
1864
1974
  "response",
1865
1975
  "progress"
1866
1976
  ];
1977
+ var MAX_LEVEL_LENGTH = Math.max(...ALL_LEVELS.map((level) => level.toUpperCase().length));
1867
1978
  var LEVEL_COLORS = {
1868
1979
  error: chalk.red.bold,
1869
1980
  warn: chalk.rgb(255, 165, 0),
@@ -1877,13 +1988,104 @@ var LEVEL_COLORS = {
1877
1988
  progress: chalk.green,
1878
1989
  results: chalk.magenta
1879
1990
  };
1880
- var CliToolkitLogger = class {
1991
+ var Logger = class _Logger {
1992
+ context;
1993
+ // Partial context during initialization
1881
1994
  options;
1882
1995
  transport;
1883
1996
  startTimes = {};
1884
1997
  lastProgressTimes = {};
1885
- constructor(options = {}) {
1886
- this.options = this.normalizeOptions(options);
1998
+ constructor(context, options = {}) {
1999
+ this.context = context;
2000
+ this.options = this.getDefaultOptions();
2001
+ if (options) {
2002
+ this.configure(options);
2003
+ }
2004
+ this.updateTransport();
2005
+ }
2006
+ /**
2007
+ * Configure logger options
2008
+ * Only parameters present in options are updated
2009
+ */
2010
+ configure(options) {
2011
+ if (options.mode !== void 0) {
2012
+ this.options.mode = this.isValidMode(options.mode) ? options.mode : "text";
2013
+ }
2014
+ if (options.route !== void 0) {
2015
+ this.options.route = options.route;
2016
+ this.updateTransport();
2017
+ }
2018
+ if (options.prefix !== void 0) {
2019
+ this.options.prefix = options.prefix;
2020
+ }
2021
+ if (options.silent !== void 0) {
2022
+ this.options.silent = options.silent;
2023
+ }
2024
+ if (options.showLevel !== void 0) {
2025
+ this.options.showLevel = options.showLevel;
2026
+ }
2027
+ if (options.timestamp !== void 0) {
2028
+ this.options.timestamp = options.timestamp;
2029
+ }
2030
+ if (options.levels !== void 0) {
2031
+ this.options.levels = this.normalizeLevels(options.levels);
2032
+ }
2033
+ if (options.progress !== void 0) {
2034
+ if (options.progress.withTimes !== void 0) {
2035
+ this.options.progressTimes = options.progress.withTimes;
2036
+ }
2037
+ if (options.progress.throttleMs !== void 0) {
2038
+ this.options.progressThrottle = options.progress.throttleMs;
2039
+ }
2040
+ }
2041
+ }
2042
+ /**
2043
+ * Initialize logger from context and CLI parameters
2044
+ */
2045
+ static init(context, options) {
2046
+ const paramDefs = {
2047
+ mode: "string default text",
2048
+ route: "string default console",
2049
+ prefix: "string",
2050
+ silent: "boolean default false",
2051
+ showLevel: "boolean default true",
2052
+ timestamp: "boolean default false",
2053
+ levels: "string",
2054
+ progressWithTimes: "boolean default false",
2055
+ progressThrottleMs: "number"
2056
+ };
2057
+ const cliParams = context.params.getAll(paramDefs);
2058
+ const config2 = {
2059
+ mode: options?.mode ?? cliParams.mode,
2060
+ route: options?.route ?? cliParams.route,
2061
+ prefix: options?.prefix ?? cliParams.prefix,
2062
+ silent: options?.silent ?? cliParams.silent,
2063
+ showLevel: options?.showLevel ?? cliParams.showLevel,
2064
+ timestamp: options?.timestamp ?? cliParams.timestamp,
2065
+ levels: options?.levels ?? (cliParams.levels ? cliParams.levels.split(",") : void 0),
2066
+ progress: options?.progress ?? {
2067
+ withTimes: cliParams.progressWithTimes,
2068
+ throttleMs: cliParams.progressThrottleMs
2069
+ }
2070
+ };
2071
+ const logger = new _Logger(context, config2);
2072
+ context.logger = logger;
2073
+ return logger;
2074
+ }
2075
+ getDefaultOptions() {
2076
+ return {
2077
+ mode: "text",
2078
+ route: this.shouldUseIpcRoute() ? "ipc" : "console",
2079
+ prefix: void 0,
2080
+ silent: false,
2081
+ showLevel: true,
2082
+ timestamp: false,
2083
+ levels: ALL_LEVELS,
2084
+ progressTimes: false,
2085
+ progressThrottle: void 0
2086
+ };
2087
+ }
2088
+ updateTransport() {
1887
2089
  this.transport = this.options.route === "ipc" ? new ParentProcessTransport() : new ConsoleTransport();
1888
2090
  }
1889
2091
  setMode(mode) {
@@ -1992,7 +2194,7 @@ var CliToolkitLogger = class {
1992
2194
  parts.push(now.toISOString());
1993
2195
  }
1994
2196
  if (this.options.showLevel) {
1995
- parts.push(struct.level.toUpperCase());
2197
+ parts.push(struct.level.toUpperCase().padEnd(MAX_LEVEL_LENGTH));
1996
2198
  }
1997
2199
  if (struct.level === "progress") {
1998
2200
  if (struct.prefix) {
@@ -2026,22 +2228,6 @@ var CliToolkitLogger = class {
2026
2228
  inspectChunks(chunks) {
2027
2229
  return chunks.map((chunk) => util.inspect(chunk, { colors: true, depth: null })).join(" ");
2028
2230
  }
2029
- normalizeOptions(options) {
2030
- const { route, mode, prefix, silent, showLevel, timestamp, levels, progress } = options;
2031
- const shouldUseIpc = this.shouldUseIpcRoute();
2032
- const normalized = {
2033
- mode: this.isValidMode(mode) ? mode : "text",
2034
- route: route ?? (shouldUseIpc ? "ipc" : "console"),
2035
- prefix,
2036
- silent: silent ?? false,
2037
- showLevel: showLevel ?? true,
2038
- timestamp: timestamp ?? false,
2039
- levels: this.normalizeLevels(levels),
2040
- progressTimes: progress?.withTimes ?? false,
2041
- progressThrottle: progress?.throttleMs
2042
- };
2043
- return normalized;
2044
- }
2045
2231
  shouldUseIpcRoute() {
2046
2232
  if (process.env.VITEST || process.env.NODE_ENV === "test") {
2047
2233
  return false;
@@ -2072,35 +2258,44 @@ var CliToolkitLogger = class {
2072
2258
 
2073
2259
  // src/init/index.ts
2074
2260
  import { EventEmitter } from "events";
2261
+ function extractComponentOptions(opts, componentName) {
2262
+ const reservedKeys = ["overrides", "defaults", "modules"];
2263
+ const componentOptions = {};
2264
+ for (const [key, value] of Object.entries(opts)) {
2265
+ if (!reservedKeys.includes(key)) {
2266
+ componentOptions[key] = value;
2267
+ }
2268
+ }
2269
+ return componentOptions;
2270
+ }
2075
2271
  function setup(opts = {}) {
2076
- const args = new Args({
2272
+ const args = Args.init({
2077
2273
  overrides: opts.overrides || {},
2078
2274
  defaults: opts.defaults || {}
2079
2275
  });
2080
- const params = new Params({ args }, opts.overrides || {});
2081
- const loggerOptions = opts.logger || {};
2082
- const logger = new CliToolkitLogger({
2083
- mode: loggerOptions.mode || "text",
2084
- route: loggerOptions.route || "console",
2085
- prefix: loggerOptions.prefix,
2086
- silent: loggerOptions.silent,
2087
- showLevel: loggerOptions.showLevel,
2088
- timestamp: loggerOptions.timestamp,
2089
- levels: loggerOptions.levels
2090
- });
2091
- const cleanupFunctions = [];
2092
- const context = {
2276
+ const partialContext = {
2093
2277
  args,
2094
- params,
2095
- logger,
2096
2278
  emitter: new EventEmitter(),
2097
2279
  isStop: () => false,
2098
- // Will be set in init function
2099
- cleanupFunctions,
2280
+ cleanupFunctions: [],
2100
2281
  registerCleanup: (fn) => {
2101
- cleanupFunctions.push(fn);
2282
+ partialContext.cleanupFunctions.push(fn);
2102
2283
  }
2103
2284
  };
2285
+ const params = Params.init(partialContext, opts.overrides || {});
2286
+ partialContext.params = params;
2287
+ const loggerOptions = extractComponentOptions(opts, "logger");
2288
+ const logger = Logger.init(partialContext, loggerOptions);
2289
+ partialContext.logger = logger;
2290
+ const context = {
2291
+ args,
2292
+ params,
2293
+ logger,
2294
+ emitter: partialContext.emitter,
2295
+ isStop: partialContext.isStop,
2296
+ cleanupFunctions: partialContext.cleanupFunctions,
2297
+ registerCleanup: partialContext.registerCleanup
2298
+ };
2104
2299
  logger.debug("[setup] completed successfully");
2105
2300
  return context;
2106
2301
  }
@@ -2111,6 +2306,22 @@ async function setupModules(context, opts = {}) {
2111
2306
  context.logger.debug("[setupModules] completed successfully");
2112
2307
  return context;
2113
2308
  }
2309
+ function printAllParameters(context) {
2310
+ const trackedParams = context.params.getTrackedParams();
2311
+ console.log("\n=== All Figured Parameters ===");
2312
+ console.log("\nComponent: Logger");
2313
+ const loggerParams = trackedParams.filter(
2314
+ (p) => ["mode", "route", "prefix", "silent", "showLevel", "timestamp", "levels"].includes(p.key)
2315
+ );
2316
+ if (loggerParams.length > 0) {
2317
+ loggerParams.forEach((p) => {
2318
+ console.log(` ${p.key}: ${JSON.stringify(p.value)} (from ${p.source})`);
2319
+ });
2320
+ } else {
2321
+ console.log(" (no parameters requested)");
2322
+ }
2323
+ console.log("\n=== End Parameters ===\n");
2324
+ }
2114
2325
  async function init(flow, opts = {}) {
2115
2326
  let stop = false;
2116
2327
  let context = null;
@@ -2130,6 +2341,11 @@ async function init(flow, opts = {}) {
2130
2341
  context = setup(opts);
2131
2342
  context.isStop = () => stop;
2132
2343
  context = await setupModules(context, opts);
2344
+ const stopAfter = context.args.get("stopAfter");
2345
+ if (stopAfter === "init") {
2346
+ printAllParameters(context);
2347
+ process.exit(0);
2348
+ }
2133
2349
  process.on("SIGINT", async () => {
2134
2350
  if (stop) {
2135
2351
  context.logger.warn("[process] killed");
@@ -2147,14 +2363,21 @@ async function init(flow, opts = {}) {
2147
2363
  await flow(context);
2148
2364
  } catch (error) {
2149
2365
  const errorLocation = error instanceof Error && error.stack ? error.stack.split("\n")[1]?.trim() || "Unknown location" : "Unknown location";
2366
+ const logError = (msg, ...args) => {
2367
+ if (context?.logger) {
2368
+ context.logger.error(msg, ...args);
2369
+ } else {
2370
+ console.error(msg, ...args);
2371
+ }
2372
+ };
2150
2373
  if (error instanceof ParamError) {
2151
- context?.logger.error(`[params]: ${error.message} (${errorLocation})`);
2374
+ logError(`[params]: ${error.message} (${errorLocation})`);
2152
2375
  process.exitCode = 3;
2153
2376
  } else if (error instanceof InitError) {
2154
- context?.logger.error(`[init]: ${error.message} (${errorLocation})`);
2377
+ logError(`[init]: ${error.message} (${errorLocation})`);
2155
2378
  process.exitCode = 4;
2156
2379
  } else {
2157
- context?.logger.error(`[other] error:`, error, errorLocation);
2380
+ logError(`[other] error:`, error, errorLocation);
2158
2381
  process.exitCode = 5;
2159
2382
  }
2160
2383
  } finally {