@nmakarov/cli-toolkit 0.4.0 → 0.5.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/index.cjs CHANGED
@@ -60,6 +60,7 @@ __export(src_exports, {
60
60
  joiEdateType: () => joiEdateType,
61
61
  joiStringArrayType: () => joiStringArrayType,
62
62
  organizeFooterMessages: () => organizeFooterMessages,
63
+ setupContext: () => setupContext,
63
64
  showListScreen: () => showListScreen,
64
65
  showMenuScreen: () => showMenuScreen,
65
66
  showMultiColumnListScreen: () => showMultiColumnListScreen,
@@ -2960,6 +2961,288 @@ var Db = class {
2960
2961
  return this.isConnected && this.knexInstance !== null;
2961
2962
  }
2962
2963
  };
2964
+
2965
+ // src/logger/index.ts
2966
+ var import_chalk = __toESM(require("chalk"), 1);
2967
+ var import_util = __toESM(require("util"), 1);
2968
+
2969
+ // src/logger/transports.ts
2970
+ var ConsoleTransport = class {
2971
+ write(payload) {
2972
+ console.info(payload);
2973
+ }
2974
+ };
2975
+ var ParentProcessTransport = class {
2976
+ write(payload) {
2977
+ if (process.env.VITEST || process.env.NODE_ENV === "test") {
2978
+ console.info(payload);
2979
+ return;
2980
+ }
2981
+ if (typeof process.send === "function" && process.connected === true) {
2982
+ process.send(payload);
2983
+ } else {
2984
+ console.info(payload);
2985
+ }
2986
+ }
2987
+ };
2988
+
2989
+ // src/logger/index.ts
2990
+ var ALL_LEVELS = [
2991
+ "silly",
2992
+ "debug",
2993
+ "logic",
2994
+ "info",
2995
+ "notice",
2996
+ "warn",
2997
+ "error",
2998
+ "results",
2999
+ "request",
3000
+ "response",
3001
+ "progress"
3002
+ ];
3003
+ var LEVEL_COLORS = {
3004
+ error: import_chalk.default.red.bold,
3005
+ warn: import_chalk.default.rgb(255, 165, 0),
3006
+ notice: import_chalk.default.cyan,
3007
+ info: import_chalk.default.white.bold,
3008
+ logic: import_chalk.default.gray,
3009
+ debug: import_chalk.default.gray,
3010
+ silly: import_chalk.default.gray,
3011
+ request: import_chalk.default.green,
3012
+ response: import_chalk.default.yellow,
3013
+ progress: import_chalk.default.green,
3014
+ results: import_chalk.default.magenta
3015
+ };
3016
+ var CliToolkitLogger = class {
3017
+ options;
3018
+ transport;
3019
+ startTimes = {};
3020
+ lastProgressTimes = {};
3021
+ constructor(options = {}) {
3022
+ this.options = this.normalizeOptions(options);
3023
+ this.transport = this.options.route === "ipc" ? new ParentProcessTransport() : new ConsoleTransport();
3024
+ }
3025
+ setMode(mode) {
3026
+ if (!this.isValidMode(mode)) {
3027
+ throw new Error(`Unsupported logger mode: ${mode}`);
3028
+ }
3029
+ this.options.mode = mode;
3030
+ }
3031
+ debug(message, ...chunks) {
3032
+ this.out({ level: "debug", message, chunks });
3033
+ }
3034
+ info(message, ...chunks) {
3035
+ this.out({ level: "info", message, chunks });
3036
+ }
3037
+ notice(message, ...chunks) {
3038
+ this.out({ level: "notice", message, chunks });
3039
+ }
3040
+ warn(message, ...chunks) {
3041
+ this.out({ level: "warn", message, chunks });
3042
+ }
3043
+ error(message, ...chunks) {
3044
+ this.out({ level: "error", message, chunks });
3045
+ }
3046
+ logic(message, ...chunks) {
3047
+ this.out({ level: "logic", message, chunks });
3048
+ }
3049
+ silly(message, ...chunks) {
3050
+ this.out({ level: "silly", message, chunks });
3051
+ }
3052
+ results(results) {
3053
+ this.out({ level: "results", message: "results", results });
3054
+ }
3055
+ request(operation, ...chunks) {
3056
+ const message = this.inspectChunks([operation, ...chunks]);
3057
+ this.out({ level: "request", message });
3058
+ }
3059
+ response(operation, ...chunks) {
3060
+ const message = this.inspectChunks([operation, ...chunks]);
3061
+ this.out({ level: "response", message });
3062
+ }
3063
+ progress(message, opts) {
3064
+ const { prefix, count, total } = opts;
3065
+ const paddedTotal = String(total).length;
3066
+ const paddedCount = String(count).padStart(paddedTotal, " ");
3067
+ const payload = {
3068
+ level: "progress",
3069
+ message,
3070
+ count: paddedCount,
3071
+ total,
3072
+ prefix
3073
+ };
3074
+ if (!this.startTimes[prefix ?? ""]) {
3075
+ this.startTimes[prefix ?? ""] = Date.now();
3076
+ }
3077
+ if (this.options.progressTimes) {
3078
+ const elapsedSeconds = (Date.now() - this.startTimes[prefix ?? ""]) / 1e3;
3079
+ let remaining = -1;
3080
+ if (count > 1) {
3081
+ const rate = elapsedSeconds / (count - 1);
3082
+ remaining = (total - count) * rate;
3083
+ }
3084
+ payload.elapsed = this.round(elapsedSeconds, 2);
3085
+ payload.remaining = remaining >= 0 ? this.round(remaining, 2) : remaining;
3086
+ }
3087
+ if (count >= total) {
3088
+ delete this.startTimes[prefix ?? ""];
3089
+ delete this.lastProgressTimes[prefix ?? ""];
3090
+ }
3091
+ if (this.shouldOutputProgress(prefix ?? "", count, total)) {
3092
+ this.out(payload);
3093
+ if (this.options.progressThrottle && prefix) {
3094
+ this.lastProgressTimes[prefix] = Date.now();
3095
+ }
3096
+ }
3097
+ }
3098
+ shouldOutputProgress(prefix, count, total) {
3099
+ if (!this.options.progressThrottle) {
3100
+ return true;
3101
+ }
3102
+ if (count === 1 || count === total || !prefix) {
3103
+ return true;
3104
+ }
3105
+ const lastTime = this.lastProgressTimes[prefix];
3106
+ if (!lastTime) {
3107
+ return true;
3108
+ }
3109
+ return Date.now() - lastTime >= this.options.progressThrottle;
3110
+ }
3111
+ out(struct) {
3112
+ if (this.options.silent) {
3113
+ return;
3114
+ }
3115
+ if (!this.options.levels.includes(struct.level)) {
3116
+ return;
3117
+ }
3118
+ if (this.options.prefix && !struct.prefix) {
3119
+ struct.prefix = this.options.prefix;
3120
+ }
3121
+ const output = this.options.mode === "json" ? struct : this.formatLog(struct);
3122
+ this.transport.write(output);
3123
+ }
3124
+ formatLog(struct) {
3125
+ const parts = [];
3126
+ const now = /* @__PURE__ */ new Date();
3127
+ if (this.options.timestamp) {
3128
+ parts.push(now.toISOString());
3129
+ }
3130
+ if (this.options.showLevel) {
3131
+ parts.push(struct.level.toUpperCase());
3132
+ }
3133
+ if (struct.level === "progress") {
3134
+ if (struct.prefix) {
3135
+ parts.push(LEVEL_COLORS[struct.level].bold(struct.prefix));
3136
+ }
3137
+ if (struct.count !== void 0 && struct.total !== void 0) {
3138
+ parts.push(LEVEL_COLORS[struct.level](`${struct.count}/${struct.total}`));
3139
+ }
3140
+ } else if (struct.prefix) {
3141
+ parts.push(import_chalk.default.cyan(`[${struct.prefix}]`));
3142
+ }
3143
+ if (struct.message) {
3144
+ const formatter = LEVEL_COLORS[struct.level] ?? import_chalk.default.white;
3145
+ parts.push(formatter.bold(struct.message));
3146
+ }
3147
+ if (struct.level === "progress") {
3148
+ if (struct.elapsed !== void 0 && struct.remaining !== void 0) {
3149
+ const formatter = LEVEL_COLORS[struct.level];
3150
+ parts.push(formatter(`${struct.elapsed}/${struct.remaining}`));
3151
+ }
3152
+ }
3153
+ if (struct.chunks && struct.chunks.length) {
3154
+ parts.push(this.inspectChunks(struct.chunks));
3155
+ }
3156
+ if (struct.results) {
3157
+ const formatter = LEVEL_COLORS[struct.level] ?? import_chalk.default.white;
3158
+ parts.push(formatter(JSON.stringify(struct.results, null, 4)));
3159
+ }
3160
+ return parts.join(" ");
3161
+ }
3162
+ inspectChunks(chunks) {
3163
+ return chunks.map((chunk) => import_util.default.inspect(chunk, { colors: true, depth: null })).join(" ");
3164
+ }
3165
+ normalizeOptions(options) {
3166
+ const { route, mode, prefix, silent, showLevel, timestamp, levels, progress } = options;
3167
+ const shouldUseIpc = this.shouldUseIpcRoute();
3168
+ const normalized = {
3169
+ mode: this.isValidMode(mode) ? mode : "text",
3170
+ route: route ?? (shouldUseIpc ? "ipc" : "console"),
3171
+ prefix,
3172
+ silent: silent ?? false,
3173
+ showLevel: showLevel ?? true,
3174
+ timestamp: timestamp ?? false,
3175
+ levels: this.normalizeLevels(levels),
3176
+ progressTimes: progress?.withTimes ?? false,
3177
+ progressThrottle: progress?.throttleMs
3178
+ };
3179
+ return normalized;
3180
+ }
3181
+ shouldUseIpcRoute() {
3182
+ if (process.env.VITEST || process.env.NODE_ENV === "test") {
3183
+ return false;
3184
+ }
3185
+ return typeof process.send === "function" && process.connected === true;
3186
+ }
3187
+ normalizeLevels(levels) {
3188
+ if (!levels || !levels.length) {
3189
+ return ALL_LEVELS;
3190
+ }
3191
+ const includes = levels.filter((level) => !level.startsWith("-"));
3192
+ const excludes = levels.filter((level) => level.startsWith("-")).map((level) => level.slice(1));
3193
+ const unknown = [...includes, ...excludes].filter((level) => !ALL_LEVELS.includes(level));
3194
+ if (unknown.length) {
3195
+ console.warn(`[Logger] Unknown level(s): ${unknown.join(", ")}`);
3196
+ }
3197
+ const base = includes.length ? includes : ALL_LEVELS;
3198
+ return base.filter((level) => !excludes.includes(level));
3199
+ }
3200
+ isValidMode(mode) {
3201
+ return mode === void 0 || mode === null || mode === "text" || mode === "json";
3202
+ }
3203
+ round(value, places) {
3204
+ const factor = Math.pow(10, places);
3205
+ return Math.round(value * factor) / factor;
3206
+ }
3207
+ };
3208
+
3209
+ // src/init/index.ts
3210
+ var import_events = require("events");
3211
+ function setup(opts = {}) {
3212
+ const args = new Args({
3213
+ overrides: opts.overrides || {},
3214
+ defaults: opts.defaults || {}
3215
+ });
3216
+ const params = new Params({ args }, opts.overrides || {});
3217
+ const loggerOptions = opts.logger || {};
3218
+ const logger = new CliToolkitLogger({
3219
+ mode: loggerOptions.mode || "text",
3220
+ route: loggerOptions.route || "console",
3221
+ prefix: loggerOptions.prefix,
3222
+ silent: loggerOptions.silent,
3223
+ showLevel: loggerOptions.showLevel,
3224
+ timestamp: loggerOptions.timestamp,
3225
+ levels: loggerOptions.levels
3226
+ });
3227
+ const cleanupFunctions = [];
3228
+ const context = {
3229
+ args,
3230
+ params,
3231
+ logger,
3232
+ emitter: new import_events.EventEmitter(),
3233
+ isStop: () => false,
3234
+ // Will be set in init function
3235
+ cleanupFunctions,
3236
+ registerCleanup: (fn) => {
3237
+ cleanupFunctions.push(fn);
3238
+ }
3239
+ };
3240
+ logger.debug("[setup] completed successfully");
3241
+ return context;
3242
+ }
3243
+ function setupContext(opts = {}) {
3244
+ return setup(opts);
3245
+ }
2963
3246
  // Annotate the CommonJS export names for ESM import in node:
2964
3247
  0 && (module.exports = {
2965
3248
  Args,
@@ -2992,6 +3275,7 @@ var Db = class {
2992
3275
  joiEdateType,
2993
3276
  joiStringArrayType,
2994
3277
  organizeFooterMessages,
3278
+ setupContext,
2995
3279
  showListScreen,
2996
3280
  showMenuScreen,
2997
3281
  showMultiColumnListScreen,