@nmakarov/cli-toolkit 0.3.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/db.cjs +351 -0
- package/dist/db.cjs.map +1 -0
- package/dist/db.js +314 -0
- package/dist/db.js.map +1 -0
- package/dist/index.cjs +581 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +579 -0
- package/dist/index.js.map +1 -1
- package/dist/init.cjs +1110 -0
- package/dist/init.cjs.map +1 -0
- package/dist/init.js +1079 -0
- package/dist/init.js.map +1 -0
- package/dist/logger.cjs +13 -2
- package/dist/logger.cjs.map +1 -1
- package/dist/logger.js +13 -2
- package/dist/logger.js.map +1 -1
- package/package.json +24 -3
package/dist/index.cjs
CHANGED
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
Args: () => Args,
|
|
34
|
+
Db: () => Db,
|
|
34
35
|
Divider: () => Divider,
|
|
35
36
|
FileDatabase: () => FileDatabase,
|
|
36
37
|
FileDatabaseError: () => FileDatabaseError,
|
|
@@ -59,6 +60,7 @@ __export(src_exports, {
|
|
|
59
60
|
joiEdateType: () => joiEdateType,
|
|
60
61
|
joiStringArrayType: () => joiStringArrayType,
|
|
61
62
|
organizeFooterMessages: () => organizeFooterMessages,
|
|
63
|
+
setupContext: () => setupContext,
|
|
62
64
|
showListScreen: () => showListScreen,
|
|
63
65
|
showMenuScreen: () => showMenuScreen,
|
|
64
66
|
showMultiColumnListScreen: () => showMultiColumnListScreen,
|
|
@@ -2664,9 +2666,587 @@ var FileDatabase = class {
|
|
|
2664
2666
|
return { ...this.metadata };
|
|
2665
2667
|
}
|
|
2666
2668
|
};
|
|
2669
|
+
|
|
2670
|
+
// src/db/index.ts
|
|
2671
|
+
var import_knex = __toESM(require("knex"), 1);
|
|
2672
|
+
var Db = class {
|
|
2673
|
+
knexInstance = null;
|
|
2674
|
+
config;
|
|
2675
|
+
logger;
|
|
2676
|
+
queriesLog = [];
|
|
2677
|
+
isConnected = false;
|
|
2678
|
+
constructor(config2) {
|
|
2679
|
+
if (!config2.connectionString) {
|
|
2680
|
+
throw new ParamError("Db: connectionString is required");
|
|
2681
|
+
}
|
|
2682
|
+
this.config = {
|
|
2683
|
+
testConnection: true,
|
|
2684
|
+
profile: false,
|
|
2685
|
+
pool: { min: 2, max: 10 },
|
|
2686
|
+
acquireConnectionTimeout: 1e4,
|
|
2687
|
+
ssl: { rejectUnauthorized: false },
|
|
2688
|
+
logger: console,
|
|
2689
|
+
name: "default",
|
|
2690
|
+
...config2
|
|
2691
|
+
};
|
|
2692
|
+
this.logger = this.config.logger;
|
|
2693
|
+
const instance2 = this;
|
|
2694
|
+
const callableWrapper = function(...args) {
|
|
2695
|
+
throw new Error("This should never be called directly");
|
|
2696
|
+
};
|
|
2697
|
+
callableWrapper._instance = instance2;
|
|
2698
|
+
return new Proxy(callableWrapper, {
|
|
2699
|
+
// Intercept function calls: db('table')
|
|
2700
|
+
apply: (target, thisArg, argumentsList) => {
|
|
2701
|
+
const inst = target._instance;
|
|
2702
|
+
if (!inst.knexInstance) {
|
|
2703
|
+
throw new Error("Db: Not connected. Call connect() first.");
|
|
2704
|
+
}
|
|
2705
|
+
return inst.knexInstance(...argumentsList);
|
|
2706
|
+
},
|
|
2707
|
+
// Intercept property access: db.schema, db.raw, etc.
|
|
2708
|
+
get: (target, prop) => {
|
|
2709
|
+
if (prop === "_instance") {
|
|
2710
|
+
return target._instance;
|
|
2711
|
+
}
|
|
2712
|
+
const instance3 = target._instance;
|
|
2713
|
+
const ownMethods = [
|
|
2714
|
+
"connect",
|
|
2715
|
+
"disconnect",
|
|
2716
|
+
"testConnection",
|
|
2717
|
+
"tableExists",
|
|
2718
|
+
"getQueryLog",
|
|
2719
|
+
"getKnex",
|
|
2720
|
+
"isConnectedToDb",
|
|
2721
|
+
"getErrorMessage",
|
|
2722
|
+
"detectClient",
|
|
2723
|
+
"attachProfiler"
|
|
2724
|
+
];
|
|
2725
|
+
if (prop in instance3) {
|
|
2726
|
+
const value = instance3[prop];
|
|
2727
|
+
if (typeof value === "function" && ownMethods.includes(prop)) {
|
|
2728
|
+
return value.bind(instance3);
|
|
2729
|
+
}
|
|
2730
|
+
if (typeof value !== "function") {
|
|
2731
|
+
return value;
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
if (instance3.knexInstance) {
|
|
2735
|
+
const knexProp = instance3.knexInstance[prop];
|
|
2736
|
+
if (typeof knexProp === "function") {
|
|
2737
|
+
return knexProp.bind(instance3.knexInstance);
|
|
2738
|
+
}
|
|
2739
|
+
return knexProp;
|
|
2740
|
+
}
|
|
2741
|
+
if (prop in instance3) {
|
|
2742
|
+
const method = instance3[prop];
|
|
2743
|
+
if (typeof method === "function") {
|
|
2744
|
+
return method.bind(instance3);
|
|
2745
|
+
}
|
|
2746
|
+
return method;
|
|
2747
|
+
}
|
|
2748
|
+
return void 0;
|
|
2749
|
+
}
|
|
2750
|
+
});
|
|
2751
|
+
}
|
|
2752
|
+
/**
|
|
2753
|
+
* Detect database client type from connection string
|
|
2754
|
+
*/
|
|
2755
|
+
detectClient(connectionString) {
|
|
2756
|
+
if (connectionString.match(/^postgresql/)) {
|
|
2757
|
+
return "pg";
|
|
2758
|
+
}
|
|
2759
|
+
if (connectionString.match(/^mysql/)) {
|
|
2760
|
+
return "mysql2";
|
|
2761
|
+
}
|
|
2762
|
+
return null;
|
|
2763
|
+
}
|
|
2764
|
+
/**
|
|
2765
|
+
* Connect to the database
|
|
2766
|
+
*/
|
|
2767
|
+
async connect() {
|
|
2768
|
+
if (this.isConnected && this.knexInstance) {
|
|
2769
|
+
this.logger.warn?.("[Db] Already connected");
|
|
2770
|
+
return;
|
|
2771
|
+
}
|
|
2772
|
+
const client = this.detectClient(this.config.connectionString);
|
|
2773
|
+
if (!client) {
|
|
2774
|
+
throw new ParamError(
|
|
2775
|
+
`Db: Cannot determine client type from connection string. Expected postgresql:// or mysql://`
|
|
2776
|
+
);
|
|
2777
|
+
}
|
|
2778
|
+
try {
|
|
2779
|
+
const connectionConfig = {
|
|
2780
|
+
connectionString: this.config.connectionString,
|
|
2781
|
+
family: 4
|
|
2782
|
+
// Force IPv4 only (disable IPv6)
|
|
2783
|
+
};
|
|
2784
|
+
this.knexInstance = (0, import_knex.default)({
|
|
2785
|
+
client,
|
|
2786
|
+
connection: connectionConfig,
|
|
2787
|
+
pool: this.config.pool,
|
|
2788
|
+
acquireConnectionTimeout: this.config.acquireConnectionTimeout,
|
|
2789
|
+
...this.config.ssl && { ssl: this.config.ssl }
|
|
2790
|
+
});
|
|
2791
|
+
if (this.config.profile) {
|
|
2792
|
+
this.attachProfiler();
|
|
2793
|
+
}
|
|
2794
|
+
if (this.config.testConnection) {
|
|
2795
|
+
await this.testConnection();
|
|
2796
|
+
}
|
|
2797
|
+
this.isConnected = true;
|
|
2798
|
+
this.logger.debug?.(`[Db] Connected to database "${this.config.name || this.config.connectionString}"`);
|
|
2799
|
+
} catch (error) {
|
|
2800
|
+
if (error instanceof ParamError) {
|
|
2801
|
+
throw error;
|
|
2802
|
+
}
|
|
2803
|
+
const errorMsg = this.getErrorMessage(error);
|
|
2804
|
+
throw new ParamError(`Db: Connection failed - ${errorMsg}`);
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
/**
|
|
2808
|
+
* Disconnect from the database
|
|
2809
|
+
*/
|
|
2810
|
+
async disconnect() {
|
|
2811
|
+
if (!this.knexInstance) {
|
|
2812
|
+
return;
|
|
2813
|
+
}
|
|
2814
|
+
try {
|
|
2815
|
+
await this.knexInstance.destroy();
|
|
2816
|
+
this.knexInstance = null;
|
|
2817
|
+
this.isConnected = false;
|
|
2818
|
+
this.queriesLog = [];
|
|
2819
|
+
this.logger.debug?.(`[Db] Disconnected from database "${this.config.name || this.config.connectionString}"`);
|
|
2820
|
+
} catch (error) {
|
|
2821
|
+
const errorMsg = this.getErrorMessage(error);
|
|
2822
|
+
this.logger.error?.(`[Db] Error disconnecting: ${errorMsg}`);
|
|
2823
|
+
throw error;
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
/**
|
|
2827
|
+
* Extract error message from various error types
|
|
2828
|
+
*/
|
|
2829
|
+
getErrorMessage(error) {
|
|
2830
|
+
if (error instanceof AggregateError) {
|
|
2831
|
+
const errors = error.errors || [];
|
|
2832
|
+
if (errors.length > 0) {
|
|
2833
|
+
const firstError = errors[0];
|
|
2834
|
+
const firstErrorMsg = firstError instanceof Error ? firstError.message : String(firstError);
|
|
2835
|
+
const allSimilar = errors.every((e) => {
|
|
2836
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
2837
|
+
const codeMatch = msg.match(/^(\w+)\s/);
|
|
2838
|
+
const firstCodeMatch = firstErrorMsg.match(/^(\w+)\s/);
|
|
2839
|
+
return codeMatch && firstCodeMatch && codeMatch[1] === firstCodeMatch[1];
|
|
2840
|
+
});
|
|
2841
|
+
if (allSimilar && errors.length > 1) {
|
|
2842
|
+
const addresses = errors.map((e) => {
|
|
2843
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
2844
|
+
const addrMatch = msg.match(/([:\d.]+:\d+)/);
|
|
2845
|
+
return addrMatch ? addrMatch[1] : null;
|
|
2846
|
+
}).filter(Boolean);
|
|
2847
|
+
if (addresses.length > 0) {
|
|
2848
|
+
const codeMatch = firstErrorMsg.match(/^(\w+)\s/);
|
|
2849
|
+
const code = codeMatch ? codeMatch[1] : "Connection error";
|
|
2850
|
+
return `${code} (tried: ${addresses.join(", ")})`;
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
const uniqueMessages = [...new Set(errors.map((e) => {
|
|
2854
|
+
return e instanceof Error ? e.message : String(e);
|
|
2855
|
+
}))];
|
|
2856
|
+
if (uniqueMessages.length === 1) {
|
|
2857
|
+
return uniqueMessages[0];
|
|
2858
|
+
}
|
|
2859
|
+
return uniqueMessages.join("; ");
|
|
2860
|
+
}
|
|
2861
|
+
return error.message || "Multiple errors occurred";
|
|
2862
|
+
}
|
|
2863
|
+
if (error instanceof Error) {
|
|
2864
|
+
const errorWithCode = error;
|
|
2865
|
+
if (errorWithCode.code) {
|
|
2866
|
+
return `${errorWithCode.code}: ${error.message || String(error)}`;
|
|
2867
|
+
}
|
|
2868
|
+
return error.message || String(error);
|
|
2869
|
+
}
|
|
2870
|
+
if (typeof error === "string") {
|
|
2871
|
+
return error;
|
|
2872
|
+
}
|
|
2873
|
+
if (error?.message) {
|
|
2874
|
+
const msg = String(error.message);
|
|
2875
|
+
const errorWithCode = error;
|
|
2876
|
+
if (errorWithCode.code) {
|
|
2877
|
+
return `${errorWithCode.code}: ${msg}`;
|
|
2878
|
+
}
|
|
2879
|
+
return msg;
|
|
2880
|
+
}
|
|
2881
|
+
return String(error) || "Unknown error";
|
|
2882
|
+
}
|
|
2883
|
+
/**
|
|
2884
|
+
* Test database connection
|
|
2885
|
+
*/
|
|
2886
|
+
async testConnection() {
|
|
2887
|
+
if (!this.knexInstance) {
|
|
2888
|
+
throw new Error("Db: Not connected. Call connect() first.");
|
|
2889
|
+
}
|
|
2890
|
+
try {
|
|
2891
|
+
const result = await this.knexInstance.raw("SELECT 2+3 AS result");
|
|
2892
|
+
const isOk = result.rows?.[0]?.result === 5 || result[0]?.[0]?.result === 5;
|
|
2893
|
+
this.logger.debug?.(`[Db] Connection test: ${isOk ? "OK" : "FAILED"}`);
|
|
2894
|
+
return isOk;
|
|
2895
|
+
} catch (error) {
|
|
2896
|
+
const errorMsg = this.getErrorMessage(error);
|
|
2897
|
+
this.logger.error?.(`[Db] Connection test failed: ${errorMsg}`);
|
|
2898
|
+
throw new ParamError(`Db: Connection test failed - ${errorMsg}`);
|
|
2899
|
+
}
|
|
2900
|
+
}
|
|
2901
|
+
/**
|
|
2902
|
+
* Attach query profiler to log all queries
|
|
2903
|
+
*/
|
|
2904
|
+
attachProfiler() {
|
|
2905
|
+
if (!this.knexInstance) {
|
|
2906
|
+
return;
|
|
2907
|
+
}
|
|
2908
|
+
this.queriesLog = [];
|
|
2909
|
+
this.knexInstance.queriesLog = this.queriesLog;
|
|
2910
|
+
this.knexInstance.on("query", (query) => {
|
|
2911
|
+
query.__startTime = process.hrtime();
|
|
2912
|
+
});
|
|
2913
|
+
this.knexInstance.on("query-response", (response, query) => {
|
|
2914
|
+
const [seconds, nanoseconds] = process.hrtime(query.__startTime);
|
|
2915
|
+
const executionTimeMs = (seconds * 1e3 + nanoseconds / 1e6).toFixed(2);
|
|
2916
|
+
const logEntry = {
|
|
2917
|
+
sql: query.sql,
|
|
2918
|
+
bindings: query.bindings || [],
|
|
2919
|
+
executionTimeMs
|
|
2920
|
+
};
|
|
2921
|
+
this.queriesLog.push(logEntry);
|
|
2922
|
+
this.logger.debug?.(`[Db] Query: ${query.sql} | Duration: ${executionTimeMs}ms`);
|
|
2923
|
+
});
|
|
2924
|
+
this.knexInstance.on("query-error", (error, query) => {
|
|
2925
|
+
this.logger.error?.(`[Db] Query failed: ${query.sql}`, error);
|
|
2926
|
+
});
|
|
2927
|
+
}
|
|
2928
|
+
/**
|
|
2929
|
+
* Get query log (only available if profiling is enabled)
|
|
2930
|
+
*/
|
|
2931
|
+
getQueryLog() {
|
|
2932
|
+
return [...this.queriesLog];
|
|
2933
|
+
}
|
|
2934
|
+
/**
|
|
2935
|
+
* Check if a table exists
|
|
2936
|
+
*/
|
|
2937
|
+
async tableExists(tableName) {
|
|
2938
|
+
if (!this.knexInstance) {
|
|
2939
|
+
throw new Error("Db: Not connected. Call connect() first.");
|
|
2940
|
+
}
|
|
2941
|
+
try {
|
|
2942
|
+
return await this.knexInstance.schema.hasTable(tableName);
|
|
2943
|
+
} catch (error) {
|
|
2944
|
+
this.logger.error?.(`[Db] Error checking table existence: ${error.message}`);
|
|
2945
|
+
throw error;
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
/**
|
|
2949
|
+
* Get the underlying Knex instance (for advanced usage)
|
|
2950
|
+
*/
|
|
2951
|
+
getKnex() {
|
|
2952
|
+
if (!this.knexInstance) {
|
|
2953
|
+
throw new Error("Db: Not connected. Call connect() first.");
|
|
2954
|
+
}
|
|
2955
|
+
return this.knexInstance;
|
|
2956
|
+
}
|
|
2957
|
+
/**
|
|
2958
|
+
* Get connection status
|
|
2959
|
+
*/
|
|
2960
|
+
isConnectedToDb() {
|
|
2961
|
+
return this.isConnected && this.knexInstance !== null;
|
|
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
|
+
}
|
|
2667
3246
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2668
3247
|
0 && (module.exports = {
|
|
2669
3248
|
Args,
|
|
3249
|
+
Db,
|
|
2670
3250
|
Divider,
|
|
2671
3251
|
FileDatabase,
|
|
2672
3252
|
FileDatabaseError,
|
|
@@ -2695,6 +3275,7 @@ var FileDatabase = class {
|
|
|
2695
3275
|
joiEdateType,
|
|
2696
3276
|
joiStringArrayType,
|
|
2697
3277
|
organizeFooterMessages,
|
|
3278
|
+
setupContext,
|
|
2698
3279
|
showListScreen,
|
|
2699
3280
|
showMenuScreen,
|
|
2700
3281
|
showMultiColumnListScreen,
|