neex 0.6.23 → 0.6.24
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/src/commands/start-commands.js +94 -13
- package/dist/src/start-manager.js +34 -121
- package/package.json +1 -1
|
@@ -27,6 +27,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
exports.addStartCommands = void 0;
|
|
30
|
+
const start_manager_js_1 = require("../start-manager.js");
|
|
30
31
|
const chalk_1 = __importDefault(require("chalk"));
|
|
31
32
|
const figures_1 = __importDefault(require("figures"));
|
|
32
33
|
const path = __importStar(require("path"));
|
|
@@ -90,12 +91,7 @@ async function getStartCommand(filePath, showInfo) {
|
|
|
90
91
|
}
|
|
91
92
|
function addStartCommands(program) {
|
|
92
93
|
let startManager = null;
|
|
93
|
-
|
|
94
|
-
if (startManager) {
|
|
95
|
-
startManager.stop();
|
|
96
|
-
startManager = null;
|
|
97
|
-
}
|
|
98
|
-
};
|
|
94
|
+
// Start command - run applications in production mode
|
|
99
95
|
program
|
|
100
96
|
.command('start <file>')
|
|
101
97
|
.alias('s')
|
|
@@ -122,10 +118,6 @@ function addStartCommands(program) {
|
|
|
122
118
|
.action(async (file, options) => {
|
|
123
119
|
try {
|
|
124
120
|
const showInfo = options.info || false;
|
|
125
|
-
if (showInfo) {
|
|
126
|
-
console.log(chalk_1.default.blue(`${figures_1.default.info} neex start: Starting production application server...`));
|
|
127
|
-
console.log(chalk_1.default.blue(`${figures_1.default.info} neex start: Target file: ${chalk_1.default.cyan(file)}`));
|
|
128
|
-
}
|
|
129
121
|
// Validate file parameter
|
|
130
122
|
if (!file || file.trim() === '') {
|
|
131
123
|
console.error(chalk_1.default.red(`${figures_1.default.cross} neex start: Error - No file specified!`));
|
|
@@ -133,6 +125,11 @@ function addStartCommands(program) {
|
|
|
133
125
|
console.error(chalk_1.default.yellow(`${figures_1.default.pointer} Example: neex start dist/server.js`));
|
|
134
126
|
process.exit(1);
|
|
135
127
|
}
|
|
128
|
+
// Show initial info (only if --info flag is set)
|
|
129
|
+
if (showInfo) {
|
|
130
|
+
console.log(chalk_1.default.blue(`${figures_1.default.info} neex start: Starting production application server...`));
|
|
131
|
+
console.log(chalk_1.default.blue(`${figures_1.default.info} neex start: Target file: ${chalk_1.default.cyan(file)}`));
|
|
132
|
+
}
|
|
136
133
|
// Get the start command configuration
|
|
137
134
|
let startConfig;
|
|
138
135
|
try {
|
|
@@ -162,17 +159,101 @@ function addStartCommands(program) {
|
|
|
162
159
|
// Log configuration only if --info flag is set
|
|
163
160
|
if (showInfo) {
|
|
164
161
|
console.log(chalk_1.default.blue(`${figures_1.default.info} neex start: Configuration:`));
|
|
165
|
-
console;
|
|
162
|
+
console.log(chalk_1.default.blue(` ${figures_1.default.arrowRight} Target: ${chalk_1.default.cyan(file)}`));
|
|
163
|
+
console.log(chalk_1.default.blue(` ${figures_1.default.arrowRight} Runtime: ${chalk_1.default.cyan(startConfig.runtime)}`));
|
|
164
|
+
console.log(chalk_1.default.blue(` ${figures_1.default.arrowRight} Process name: ${chalk_1.default.cyan(startConfig.processName)}`));
|
|
165
|
+
console.log(chalk_1.default.blue(` ${figures_1.default.arrowRight} Environment: ${chalk_1.default.cyan(options.env)}`));
|
|
166
|
+
console.log(chalk_1.default.blue(` ${figures_1.default.arrowRight} Watch mode: ${chalk_1.default.cyan(options.watch ? 'Yes' : 'No')}`));
|
|
167
|
+
console.log(chalk_1.default.blue(` ${figures_1.default.arrowRight} Cluster mode: ${chalk_1.default.cyan(options.cluster ? 'Yes' : 'No')}`));
|
|
168
|
+
if (options.cluster) {
|
|
169
|
+
console.log(chalk_1.default.blue(` ${figures_1.default.arrowRight} Cluster instances: ${chalk_1.default.cyan(options.clusterInstances || 'auto')}`));
|
|
170
|
+
}
|
|
171
|
+
if (options.watch) {
|
|
172
|
+
console.log(chalk_1.default.blue(` ${figures_1.default.arrowRight} Watch paths: ${chalk_1.default.cyan(watchPaths.join(', '))}`));
|
|
173
|
+
console.log(chalk_1.default.blue(` ${figures_1.default.arrowRight} Extensions: ${chalk_1.default.cyan(extensions.join(', '))}`));
|
|
174
|
+
console.log(chalk_1.default.blue(` ${figures_1.default.arrowRight} Restart delay: ${chalk_1.default.cyan(options.delay || 1000)}ms`));
|
|
175
|
+
}
|
|
176
|
+
if (options.maxRestarts) {
|
|
177
|
+
console.log(chalk_1.default.blue(` ${figures_1.default.arrowRight} Max restarts: ${chalk_1.default.cyan(options.maxRestarts)}`));
|
|
178
|
+
}
|
|
179
|
+
if (options.memoryLimit) {
|
|
180
|
+
console.log(chalk_1.default.blue(` ${figures_1.default.arrowRight} Memory limit: ${chalk_1.default.cyan(options.memoryLimit)}MB`));
|
|
181
|
+
}
|
|
182
|
+
if (options.cpuLimit) {
|
|
183
|
+
console.log(chalk_1.default.blue(` ${figures_1.default.arrowRight} CPU limit: ${chalk_1.default.cyan(options.cpuLimit)}%`));
|
|
184
|
+
}
|
|
185
|
+
if (options.verbose) {
|
|
186
|
+
console.log(chalk_1.default.blue(`${figures_1.default.info} neex start: Verbose mode enabled - showing detailed logs`));
|
|
187
|
+
}
|
|
188
|
+
console.log(chalk_1.default.green(`${figures_1.default.tick} neex start: Starting production application server...`));
|
|
189
|
+
console.log(chalk_1.default.green(`${figures_1.default.tick} neex start: Launching ${chalk_1.default.cyan(startConfig.processName)} in ${chalk_1.default.cyan(options.env)} mode...`));
|
|
190
|
+
console.log(chalk_1.default.blue(`${figures_1.default.info} neex start: Press Ctrl+C to stop the application server`));
|
|
191
|
+
console.log(chalk_1.default.gray(`${'='.repeat(60)}`));
|
|
166
192
|
}
|
|
193
|
+
else {
|
|
194
|
+
// Minimal output without --info flag (similar to neex dev)
|
|
195
|
+
console.log(chalk_1.default.green(`${figures_1.default.tick} neex start: Starting ${chalk_1.default.cyan(path.basename(file))} in ${chalk_1.default.cyan(options.env)} mode...`));
|
|
196
|
+
}
|
|
197
|
+
// Create StartManager instance
|
|
198
|
+
startManager = new start_manager_js_1.StartManager({
|
|
199
|
+
runnerName: 'neex start',
|
|
200
|
+
targetFile: file,
|
|
201
|
+
command: startConfig.command,
|
|
202
|
+
processName: startConfig.processName,
|
|
203
|
+
runtime: startConfig.runtime,
|
|
204
|
+
environment: options.env,
|
|
205
|
+
parallel: false,
|
|
206
|
+
color: options.color,
|
|
207
|
+
showTiming: options.timing,
|
|
208
|
+
prefix: options.prefix,
|
|
209
|
+
printOutput: options.output,
|
|
210
|
+
minimalOutput: options.minimal,
|
|
211
|
+
watch: options.watch,
|
|
212
|
+
watchPaths: watchPaths,
|
|
213
|
+
ignore: ignorePatterns,
|
|
214
|
+
ext: extensions,
|
|
215
|
+
delay: options.delay || 1000,
|
|
216
|
+
maxRestarts: options.maxRestarts,
|
|
217
|
+
restartDelay: options.restartDelay || 3000,
|
|
218
|
+
verbose: options.verbose,
|
|
219
|
+
showInfo: showInfo,
|
|
220
|
+
signal: options.signal,
|
|
221
|
+
cluster: options.cluster,
|
|
222
|
+
clusterInstances: options.clusterInstances,
|
|
223
|
+
memoryLimit: options.memoryLimit,
|
|
224
|
+
cpuLimit: options.cpuLimit,
|
|
225
|
+
groupOutput: false,
|
|
226
|
+
isServerMode: true,
|
|
227
|
+
stopOnError: false
|
|
228
|
+
});
|
|
229
|
+
// Start the application server
|
|
230
|
+
await startManager.start();
|
|
167
231
|
}
|
|
168
232
|
catch (error) {
|
|
169
|
-
console.error(chalk_1.default.red(`${figures_1.default.cross} neex start:
|
|
233
|
+
console.error(chalk_1.default.red(`${figures_1.default.cross} neex start: Fatal error occurred`));
|
|
234
|
+
if (error instanceof Error) {
|
|
235
|
+
console.error(chalk_1.default.red(`${figures_1.default.cross} Details: ${error.message}`));
|
|
236
|
+
if (options.verbose && error.stack) {
|
|
237
|
+
console.error(chalk_1.default.gray(`Stack trace:\n${error.stack}`));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
console.error(chalk_1.default.red(`${figures_1.default.cross} Unknown error occurred`));
|
|
242
|
+
}
|
|
243
|
+
console.error(chalk_1.default.yellow(`${figures_1.default.pointer} Try running with --verbose flag for more details`));
|
|
170
244
|
process.exit(1);
|
|
171
245
|
}
|
|
172
246
|
});
|
|
247
|
+
// Return cleanup function for start manager
|
|
173
248
|
return {
|
|
174
249
|
getStartManager: () => startManager,
|
|
175
|
-
cleanupStart
|
|
250
|
+
cleanupStart: () => {
|
|
251
|
+
if (startManager && startManager.isActive()) {
|
|
252
|
+
console.log(chalk_1.default.blue(`${figures_1.default.info} neex start: Stopping application server...`));
|
|
253
|
+
startManager.stop();
|
|
254
|
+
console.log(chalk_1.default.green(`${figures_1.default.tick} neex start: Application server stopped successfully`));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
176
257
|
};
|
|
177
258
|
}
|
|
178
259
|
exports.addStartCommands = addStartCommands;
|
|
@@ -45,9 +45,6 @@ class StartManager {
|
|
|
45
45
|
cpuUsage: 0,
|
|
46
46
|
uptime: 0
|
|
47
47
|
};
|
|
48
|
-
if (!options.targetFile || !options.command || !options.processName || !options.runtime) {
|
|
49
|
-
throw new Error('Missing required options: targetFile, command, processName, and runtime are required');
|
|
50
|
-
}
|
|
51
48
|
const defaultOptions = {
|
|
52
49
|
parallel: false,
|
|
53
50
|
printOutput: true,
|
|
@@ -84,31 +81,10 @@ class StartManager {
|
|
|
84
81
|
cluster: false,
|
|
85
82
|
clusterInstances: os.cpus().length
|
|
86
83
|
};
|
|
87
|
-
// Validate numeric values
|
|
88
|
-
const validateNumber = (value, name) => {
|
|
89
|
-
const num = parseInt(value);
|
|
90
|
-
if (isNaN(num) || num < 0) {
|
|
91
|
-
throw new Error(`Invalid ${name}: must be a non-negative number`);
|
|
92
|
-
}
|
|
93
|
-
return num;
|
|
94
|
-
};
|
|
95
84
|
this.options = {
|
|
96
85
|
...defaultOptions,
|
|
97
|
-
...options
|
|
98
|
-
delay: validateNumber(options.delay || defaultOptions.delay, 'delay'),
|
|
99
|
-
clusterInstances: validateNumber(options.clusterInstances || defaultOptions.clusterInstances, 'clusterInstances'),
|
|
100
|
-
memoryLimit: options.memoryLimit ? validateNumber(options.memoryLimit, 'memoryLimit') : undefined,
|
|
101
|
-
cpuLimit: options.cpuLimit ? validateNumber(options.cpuLimit, 'cpuLimit') : undefined,
|
|
102
|
-
maxRestarts: options.maxRestarts ? validateNumber(options.maxRestarts, 'maxRestarts') : undefined,
|
|
103
|
-
restartDelay: validateNumber(options.restartDelay || defaultOptions.restartDelay, 'restartDelay')
|
|
86
|
+
...options
|
|
104
87
|
};
|
|
105
|
-
// Validate environment
|
|
106
|
-
if (this.options.environment) {
|
|
107
|
-
this.options.environment = this.options.environment.toLowerCase();
|
|
108
|
-
if (!['production', 'development', 'staging', 'test'].includes(this.options.environment)) {
|
|
109
|
-
throw new Error(`Invalid environment: ${this.options.environment}. Must be one of: production, development, staging, test`);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
88
|
}
|
|
113
89
|
setupFileWatcher() {
|
|
114
90
|
var _a;
|
|
@@ -195,59 +171,20 @@ class StartManager {
|
|
|
195
171
|
logger_1.default.printLine(`${prefix} ${chalk_1.default.red(`${figures_1.default.cross} Application crashed`)}`, 'error');
|
|
196
172
|
logger_1.default.printLine(`${prefix} ${chalk_1.default.yellow(`${figures_1.default.warning} Attempting restart in ${this.options.restartDelay}ms...`)}`, 'info');
|
|
197
173
|
}
|
|
198
|
-
// Check if we should restart
|
|
199
|
-
if (!this.shouldRestart()) {
|
|
200
|
-
if (this.options.showInfo) {
|
|
201
|
-
logger_1.default.printLine(`${prefix} ${chalk_1.default.red(`${figures_1.default.cross} Max restarts reached (${this.options.maxRestarts}). Stopping application.`)}`, 'error');
|
|
202
|
-
}
|
|
203
|
-
await this.stop();
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
174
|
// Wait before restarting
|
|
207
175
|
await new Promise(resolve => setTimeout(resolve, this.options.restartDelay));
|
|
208
|
-
|
|
209
|
-
await this.gracefulRestart();
|
|
210
|
-
}
|
|
211
|
-
catch (error) {
|
|
212
|
-
logger_1.default.printLine(`Failed to restart application: ${error.message}`, 'error');
|
|
213
|
-
if (this.options.stopOnError) {
|
|
214
|
-
await this.stop();
|
|
215
|
-
}
|
|
216
|
-
}
|
|
176
|
+
await this.gracefulRestart();
|
|
217
177
|
}
|
|
218
178
|
collectProcessStats() {
|
|
219
|
-
var _a;
|
|
220
179
|
if (!this.options.showInfo) {
|
|
221
180
|
return;
|
|
222
181
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const totalCpuTime = cpuUsage.user + cpuUsage.system;
|
|
230
|
-
const timeDiff = Date.now() - (((_a = this.lastRestartTime) === null || _a === void 0 ? void 0 : _a.getTime()) || this.startTime.getTime());
|
|
231
|
-
this.processStats.cpuUsage = Math.round((totalCpuTime / timeDiff) * 100);
|
|
232
|
-
// Check resource limits
|
|
233
|
-
if (this.options.memoryLimit && this.processStats.memoryUsage > this.options.memoryLimit) {
|
|
234
|
-
const prefix = chalk_1.default.cyan(`[${this.options.runnerName}]`);
|
|
235
|
-
logger_1.default.printLine(`${prefix} ${chalk_1.default.red(`${figures_1.default.warning} Memory limit exceeded: ${this.processStats.memoryUsage}MB > ${this.options.memoryLimit}MB`)}`, 'warn');
|
|
236
|
-
if (this.options.stopOnError) {
|
|
237
|
-
this.stop();
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
if (this.options.cpuLimit && this.processStats.cpuUsage > this.options.cpuLimit) {
|
|
241
|
-
const prefix = chalk_1.default.cyan(`[${this.options.runnerName}]`);
|
|
242
|
-
logger_1.default.printLine(`${prefix} ${chalk_1.default.red(`${figures_1.default.warning} CPU limit exceeded: ${this.processStats.cpuUsage}% > ${this.options.cpuLimit}%`)}`, 'warn');
|
|
243
|
-
if (this.options.stopOnError) {
|
|
244
|
-
this.stop();
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
catch (error) {
|
|
249
|
-
logger_1.default.printLine(`Failed to collect process stats: ${error.message}`, 'error');
|
|
250
|
-
}
|
|
182
|
+
const memUsage = process.memoryUsage();
|
|
183
|
+
this.processStats.memoryUsage = Math.round(memUsage.heapUsed / 1024 / 1024);
|
|
184
|
+
this.processStats.uptime = Math.floor((Date.now() - this.startTime.getTime()) / 1000);
|
|
185
|
+
// Simple CPU usage approximation
|
|
186
|
+
const cpuUsage = process.cpuUsage();
|
|
187
|
+
this.processStats.cpuUsage = Math.round(((cpuUsage.user + cpuUsage.system) / 1000000) * 100) / 100;
|
|
251
188
|
}
|
|
252
189
|
printStartBanner() {
|
|
253
190
|
var _a, _b;
|
|
@@ -382,35 +319,6 @@ class StartManager {
|
|
|
382
319
|
logger_1.default.printLine(`${prefix} ${chalk_1.default.green(`${figures_1.default.tick} ${this.options.processName} restarted successfully`)}`, 'info');
|
|
383
320
|
}
|
|
384
321
|
}
|
|
385
|
-
setupGracefulShutdown() {
|
|
386
|
-
const handleSignal = async (signal) => {
|
|
387
|
-
if (!this.isRunning) {
|
|
388
|
-
return;
|
|
389
|
-
}
|
|
390
|
-
try {
|
|
391
|
-
if (this.options.showInfo) {
|
|
392
|
-
console.log(`\n${chalk_1.default.yellow(`${figures_1.default.warning} Received ${signal}. Gracefully shutting down ${this.options.processName}...`)}`);
|
|
393
|
-
}
|
|
394
|
-
await this.stop();
|
|
395
|
-
process.exit(0);
|
|
396
|
-
}
|
|
397
|
-
catch (error) {
|
|
398
|
-
logger_1.default.printLine(`Failed to handle ${signal}: ${error.message}`, 'error');
|
|
399
|
-
process.exit(1);
|
|
400
|
-
}
|
|
401
|
-
};
|
|
402
|
-
process.on('SIGINT', () => handleSignal('SIGINT'));
|
|
403
|
-
process.on('SIGTERM', () => handleSignal('SIGTERM'));
|
|
404
|
-
process.on('SIGQUIT', () => handleSignal('SIGQUIT'));
|
|
405
|
-
process.on('uncaughtException', (error) => {
|
|
406
|
-
logger_1.default.printLine(`Uncaught Exception: ${error.message}`, 'error');
|
|
407
|
-
handleSignal('SIGTERM');
|
|
408
|
-
});
|
|
409
|
-
process.on('unhandledRejection', (reason) => {
|
|
410
|
-
logger_1.default.printLine(`Unhandled Rejection: ${reason}`, 'error');
|
|
411
|
-
handleSignal('SIGTERM');
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
322
|
async stop() {
|
|
415
323
|
const prefix = chalk_1.default.cyan(`[${this.options.runnerName}]`);
|
|
416
324
|
if (!this.isRunning) {
|
|
@@ -420,30 +328,35 @@ class StartManager {
|
|
|
420
328
|
logger_1.default.printLine(`${prefix} ${chalk_1.default.yellow(`${figures_1.default.warning} Stopping ${this.options.processName}...`)}`, 'info');
|
|
421
329
|
}
|
|
422
330
|
this.isRunning = false;
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
await this.fileWatcher.stop();
|
|
427
|
-
}
|
|
428
|
-
// Stop application gracefully
|
|
429
|
-
if (this.runner) {
|
|
430
|
-
await this.runner.cleanup(this.options.signal);
|
|
431
|
-
}
|
|
432
|
-
// Calculate final stats
|
|
433
|
-
const uptime = Math.floor((Date.now() - this.startTime.getTime()) / 1000);
|
|
434
|
-
const uptimeStr = this.formatUptime(uptime);
|
|
435
|
-
if (this.options.showInfo) {
|
|
436
|
-
logger_1.default.printLine(`${prefix} ${chalk_1.default.green(`${figures_1.default.tick} ${this.options.processName} stopped successfully`)}`, 'info');
|
|
437
|
-
logger_1.default.printLine(`${prefix} Final stats: ${uptimeStr} uptime, ${this.restartCount} restarts, ${this.crashCount} crashes`, 'info');
|
|
438
|
-
}
|
|
331
|
+
// Stop file watcher
|
|
332
|
+
if (this.fileWatcher) {
|
|
333
|
+
this.fileWatcher.stop();
|
|
439
334
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
335
|
+
// Stop application gracefully
|
|
336
|
+
if (this.runner) {
|
|
337
|
+
this.runner.cleanup(this.options.signal);
|
|
338
|
+
}
|
|
339
|
+
// Calculate final stats
|
|
340
|
+
const uptime = Math.floor((Date.now() - this.startTime.getTime()) / 1000);
|
|
341
|
+
const uptimeStr = this.formatUptime(uptime);
|
|
342
|
+
if (this.options.showInfo) {
|
|
343
|
+
logger_1.default.printLine(`${prefix} ${chalk_1.default.green(`${figures_1.default.tick} ${this.options.processName} stopped successfully`)}`, 'info');
|
|
344
|
+
logger_1.default.printLine(`${prefix} Final stats: ${uptimeStr} uptime, ${this.restartCount} restarts, ${this.crashCount} crashes`, 'info');
|
|
445
345
|
}
|
|
446
346
|
}
|
|
347
|
+
setupGracefulShutdown() {
|
|
348
|
+
const handleSignal = (signal) => {
|
|
349
|
+
if (this.options.showInfo) {
|
|
350
|
+
console.log(`\n${chalk_1.default.yellow(`${figures_1.default.warning} Received ${signal}. Gracefully shutting down ${this.options.processName}...`)}`);
|
|
351
|
+
}
|
|
352
|
+
this.stop().then(() => {
|
|
353
|
+
process.exit(0);
|
|
354
|
+
});
|
|
355
|
+
};
|
|
356
|
+
process.on('SIGINT', () => handleSignal('SIGINT'));
|
|
357
|
+
process.on('SIGTERM', () => handleSignal('SIGTERM'));
|
|
358
|
+
process.on('SIGQUIT', () => handleSignal('SIGQUIT'));
|
|
359
|
+
}
|
|
447
360
|
isActive() {
|
|
448
361
|
return this.isRunning;
|
|
449
362
|
}
|