neex 0.6.17 → 0.6.20
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/build-manager.js +340 -0
- package/dist/src/cli.js +4 -2
- package/dist/src/commands/build-commands.js +227 -0
- package/dist/src/commands/dev-commands.js +26 -20
- package/dist/src/commands/index.js +2 -1
- package/dist/src/commands/start-commands.js +178 -0
- package/dist/src/dev-runner.js +54 -17
- package/dist/src/start-manager.js +384 -0
- package/package.json +1 -1
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.StartManager = void 0;
|
|
30
|
+
// src/start-manager.ts - Production application start manager with monitoring
|
|
31
|
+
const watcher_1 = require("./watcher");
|
|
32
|
+
const runner_1 = require("./runner");
|
|
33
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
34
|
+
const figures_1 = __importDefault(require("figures"));
|
|
35
|
+
const logger_1 = __importDefault(require("./logger"));
|
|
36
|
+
const os = __importStar(require("os"));
|
|
37
|
+
class StartManager {
|
|
38
|
+
constructor(options) {
|
|
39
|
+
this.isRunning = false;
|
|
40
|
+
this.restartCount = 0;
|
|
41
|
+
this.crashCount = 0;
|
|
42
|
+
this.startTime = new Date();
|
|
43
|
+
this.processStats = {
|
|
44
|
+
memoryUsage: 0,
|
|
45
|
+
cpuUsage: 0,
|
|
46
|
+
uptime: 0
|
|
47
|
+
};
|
|
48
|
+
const defaultOptions = {
|
|
49
|
+
parallel: false,
|
|
50
|
+
printOutput: true,
|
|
51
|
+
color: true,
|
|
52
|
+
showTiming: true,
|
|
53
|
+
prefix: true,
|
|
54
|
+
stopOnError: false,
|
|
55
|
+
minimalOutput: false,
|
|
56
|
+
groupOutput: false,
|
|
57
|
+
isServerMode: true,
|
|
58
|
+
watch: false,
|
|
59
|
+
watchPaths: [],
|
|
60
|
+
ignore: [
|
|
61
|
+
'node_modules/**',
|
|
62
|
+
'.git/**',
|
|
63
|
+
'*.log',
|
|
64
|
+
'src/**',
|
|
65
|
+
'test/**',
|
|
66
|
+
'tests/**',
|
|
67
|
+
'coverage/**',
|
|
68
|
+
'.nyc_output/**',
|
|
69
|
+
'*.tmp',
|
|
70
|
+
'*.temp'
|
|
71
|
+
],
|
|
72
|
+
ext: ['js', 'mjs', 'json'],
|
|
73
|
+
delay: 1000,
|
|
74
|
+
verbose: false,
|
|
75
|
+
showInfo: false,
|
|
76
|
+
runnerName: 'neex start',
|
|
77
|
+
environment: 'production',
|
|
78
|
+
maxRestarts: 10,
|
|
79
|
+
restartDelay: 3000,
|
|
80
|
+
signal: 'SIGTERM',
|
|
81
|
+
cluster: false,
|
|
82
|
+
clusterInstances: os.cpus().length
|
|
83
|
+
};
|
|
84
|
+
this.options = {
|
|
85
|
+
...defaultOptions,
|
|
86
|
+
...options
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
setupFileWatcher() {
|
|
90
|
+
var _a;
|
|
91
|
+
if (!this.options.watch || !((_a = this.options.watchPaths) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const watchOptions = {
|
|
95
|
+
watch: this.options.watchPaths,
|
|
96
|
+
ignore: this.options.ignore,
|
|
97
|
+
ext: this.options.ext,
|
|
98
|
+
delay: this.options.delay,
|
|
99
|
+
verbose: this.options.verbose && this.options.showInfo
|
|
100
|
+
};
|
|
101
|
+
this.fileWatcher = new watcher_1.FileWatcher(watchOptions);
|
|
102
|
+
this.fileWatcher.on('change', (event) => {
|
|
103
|
+
if (this.options.watch && this.isRunning) {
|
|
104
|
+
this.handleFileChange(event);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
async handleFileChange(event) {
|
|
109
|
+
const prefix = chalk_1.default.cyan(`[${this.options.runnerName}]`);
|
|
110
|
+
if (this.options.showInfo) {
|
|
111
|
+
logger_1.default.printLine(`${prefix} File changed: ${chalk_1.default.yellow(event.relativePath)}`, 'info');
|
|
112
|
+
}
|
|
113
|
+
await this.gracefulRestart();
|
|
114
|
+
}
|
|
115
|
+
generateClusterCommand() {
|
|
116
|
+
if (!this.options.cluster) {
|
|
117
|
+
return this.options.command;
|
|
118
|
+
}
|
|
119
|
+
const instances = this.options.clusterInstances || os.cpus().length;
|
|
120
|
+
const baseCommand = this.options.command;
|
|
121
|
+
// For cluster mode, we'll use a simple approach with PM2-like clustering
|
|
122
|
+
// This is a simplified implementation - in real production, you'd use PM2 or similar
|
|
123
|
+
return `${baseCommand} --cluster=${instances}`;
|
|
124
|
+
}
|
|
125
|
+
async startApplication() {
|
|
126
|
+
const command = this.generateClusterCommand();
|
|
127
|
+
// Set environment variables
|
|
128
|
+
const env = {
|
|
129
|
+
...process.env,
|
|
130
|
+
NODE_ENV: this.options.environment,
|
|
131
|
+
NEEX_START_MODE: 'production',
|
|
132
|
+
NEEX_PROCESS_NAME: this.options.processName
|
|
133
|
+
};
|
|
134
|
+
// Create a modified options object for the runner
|
|
135
|
+
const runnerOptions = {
|
|
136
|
+
...this.options,
|
|
137
|
+
env,
|
|
138
|
+
customPrefix: () => this.options.processName,
|
|
139
|
+
// Override some options for production
|
|
140
|
+
stopOnError: false,
|
|
141
|
+
restartOnFailure: true,
|
|
142
|
+
maxRestarts: this.options.maxRestarts,
|
|
143
|
+
restartDelay: this.options.restartDelay
|
|
144
|
+
};
|
|
145
|
+
this.runner = new runner_1.Runner(runnerOptions);
|
|
146
|
+
try {
|
|
147
|
+
const results = await this.runner.run([command]);
|
|
148
|
+
return results;
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
this.crashCount++;
|
|
152
|
+
if (this.options.showInfo) {
|
|
153
|
+
logger_1.default.printLine(`Application crashed: ${error.message}`, 'error');
|
|
154
|
+
}
|
|
155
|
+
// Check if we should restart
|
|
156
|
+
if (this.shouldRestart()) {
|
|
157
|
+
await this.handleCrash();
|
|
158
|
+
}
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
shouldRestart() {
|
|
163
|
+
if (!this.options.maxRestarts) {
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
return this.restartCount < this.options.maxRestarts;
|
|
167
|
+
}
|
|
168
|
+
async handleCrash() {
|
|
169
|
+
const prefix = chalk_1.default.cyan(`[${this.options.runnerName}]`);
|
|
170
|
+
if (this.options.showInfo) {
|
|
171
|
+
logger_1.default.printLine(`${prefix} ${chalk_1.default.red(`${figures_1.default.cross} Application crashed`)}`, 'error');
|
|
172
|
+
logger_1.default.printLine(`${prefix} ${chalk_1.default.yellow(`${figures_1.default.warning} Attempting restart in ${this.options.restartDelay}ms...`)}`, 'info');
|
|
173
|
+
}
|
|
174
|
+
// Wait before restarting
|
|
175
|
+
await new Promise(resolve => setTimeout(resolve, this.options.restartDelay));
|
|
176
|
+
await this.gracefulRestart();
|
|
177
|
+
}
|
|
178
|
+
collectProcessStats() {
|
|
179
|
+
if (!this.options.showInfo) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
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;
|
|
188
|
+
}
|
|
189
|
+
printStartBanner() {
|
|
190
|
+
var _a, _b;
|
|
191
|
+
if (!this.options.showInfo) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const prefix = chalk_1.default.cyan(`[${this.options.runnerName}]`);
|
|
195
|
+
const uptime = Math.floor((Date.now() - this.startTime.getTime()) / 1000);
|
|
196
|
+
const uptimeStr = this.formatUptime(uptime);
|
|
197
|
+
console.log('\n' + chalk_1.default.bgGreen.black(` ${(_a = this.options.runnerName) === null || _a === void 0 ? void 0 : _a.toUpperCase()} - ${(_b = this.options.environment) === null || _b === void 0 ? void 0 : _b.toUpperCase()} MODE `) + '\n');
|
|
198
|
+
console.log(`${prefix} ${chalk_1.default.green(`${figures_1.default.tick} ${this.options.processName} started successfully`)}`);
|
|
199
|
+
console.log(`${prefix} ${chalk_1.default.blue(`${figures_1.default.info} Runtime: ${this.options.runtime}`)}`);
|
|
200
|
+
console.log(`${prefix} ${chalk_1.default.blue(`${figures_1.default.info} Environment: ${this.options.environment}`)}`);
|
|
201
|
+
console.log(`${prefix} ${chalk_1.default.blue(`${figures_1.default.info} Uptime: ${uptimeStr}`)}`);
|
|
202
|
+
if (this.options.cluster) {
|
|
203
|
+
console.log(`${prefix} ${chalk_1.default.blue(`${figures_1.default.info} Cluster mode: ${this.options.clusterInstances} instances`)}`);
|
|
204
|
+
}
|
|
205
|
+
if (this.options.watch) {
|
|
206
|
+
console.log(`${prefix} ${chalk_1.default.blue(`${figures_1.default.info} Hot reload: enabled`)}`);
|
|
207
|
+
}
|
|
208
|
+
if (this.restartCount > 0) {
|
|
209
|
+
console.log(`${prefix} ${chalk_1.default.yellow(`${figures_1.default.warning} Restarts: ${this.restartCount}`)}`);
|
|
210
|
+
}
|
|
211
|
+
if (this.crashCount > 0) {
|
|
212
|
+
console.log(`${prefix} ${chalk_1.default.red(`${figures_1.default.cross} Crashes: ${this.crashCount}`)}`);
|
|
213
|
+
}
|
|
214
|
+
// Show process stats
|
|
215
|
+
this.collectProcessStats();
|
|
216
|
+
console.log(`${prefix} ${chalk_1.default.blue(`${figures_1.default.info} Memory: ${this.processStats.memoryUsage}MB`)}`);
|
|
217
|
+
if (this.options.memoryLimit) {
|
|
218
|
+
const memoryPercent = Math.round((this.processStats.memoryUsage / this.options.memoryLimit) * 100);
|
|
219
|
+
console.log(`${prefix} ${chalk_1.default.blue(`${figures_1.default.info} Memory usage: ${memoryPercent}%`)}`);
|
|
220
|
+
}
|
|
221
|
+
console.log('');
|
|
222
|
+
}
|
|
223
|
+
formatUptime(seconds) {
|
|
224
|
+
if (seconds < 60) {
|
|
225
|
+
return `${seconds}s`;
|
|
226
|
+
}
|
|
227
|
+
else if (seconds < 3600) {
|
|
228
|
+
const minutes = Math.floor(seconds / 60);
|
|
229
|
+
const remainingSeconds = seconds % 60;
|
|
230
|
+
return `${minutes}m ${remainingSeconds}s`;
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
const hours = Math.floor(seconds / 3600);
|
|
234
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
235
|
+
return `${hours}h ${minutes}m`;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
monitorResources() {
|
|
239
|
+
if (!this.options.showInfo) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const checkInterval = setInterval(() => {
|
|
243
|
+
if (!this.isRunning) {
|
|
244
|
+
clearInterval(checkInterval);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
this.collectProcessStats();
|
|
248
|
+
// Check memory limit
|
|
249
|
+
if (this.options.memoryLimit && this.processStats.memoryUsage > this.options.memoryLimit) {
|
|
250
|
+
const prefix = chalk_1.default.cyan(`[${this.options.runnerName}]`);
|
|
251
|
+
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');
|
|
252
|
+
}
|
|
253
|
+
// Log stats every 30 seconds in verbose mode
|
|
254
|
+
if (this.options.verbose && this.processStats.uptime % 30 === 0) {
|
|
255
|
+
const prefix = chalk_1.default.cyan(`[${this.options.runnerName}]`);
|
|
256
|
+
logger_1.default.printLine(`${prefix} ${chalk_1.default.gray(`Stats: ${this.processStats.memoryUsage}MB memory, ${this.formatUptime(this.processStats.uptime)} uptime`)}`, 'info');
|
|
257
|
+
}
|
|
258
|
+
}, 1000);
|
|
259
|
+
}
|
|
260
|
+
async start() {
|
|
261
|
+
this.isRunning = true;
|
|
262
|
+
this.startTime = new Date();
|
|
263
|
+
// Setup file watcher if enabled
|
|
264
|
+
if (this.options.watch) {
|
|
265
|
+
this.setupFileWatcher();
|
|
266
|
+
}
|
|
267
|
+
// Print start banner
|
|
268
|
+
this.printStartBanner();
|
|
269
|
+
// Start file watcher if enabled
|
|
270
|
+
if (this.fileWatcher) {
|
|
271
|
+
await this.fileWatcher.start();
|
|
272
|
+
}
|
|
273
|
+
// Start resource monitoring
|
|
274
|
+
this.monitorResources();
|
|
275
|
+
// Start application
|
|
276
|
+
const prefix = chalk_1.default.cyan(`[${this.options.runnerName}]`);
|
|
277
|
+
if (this.options.showInfo) {
|
|
278
|
+
logger_1.default.printLine(`${prefix} Starting ${this.options.processName}...`, 'info');
|
|
279
|
+
}
|
|
280
|
+
await this.startApplication();
|
|
281
|
+
// Set up graceful shutdown
|
|
282
|
+
this.setupGracefulShutdown();
|
|
283
|
+
if (this.options.showInfo) {
|
|
284
|
+
logger_1.default.printLine(`${prefix} ${this.options.processName} is running in ${this.options.environment} mode`, 'info');
|
|
285
|
+
if (this.options.watch) {
|
|
286
|
+
logger_1.default.printLine(`${prefix} Watching for changes...`, 'info');
|
|
287
|
+
}
|
|
288
|
+
logger_1.default.printLine(`${prefix} Press ${chalk_1.default.cyan('Ctrl+C')} to stop`, 'info');
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
async gracefulRestart() {
|
|
292
|
+
const prefix = chalk_1.default.cyan(`[${this.options.runnerName}]`);
|
|
293
|
+
if (!this.isRunning) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
if (!this.shouldRestart()) {
|
|
297
|
+
if (this.options.showInfo) {
|
|
298
|
+
logger_1.default.printLine(`${prefix} ${chalk_1.default.red(`${figures_1.default.cross} Max restarts reached (${this.options.maxRestarts}). Stopping application.`)}`, 'error');
|
|
299
|
+
}
|
|
300
|
+
await this.stop();
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if (this.options.showInfo) {
|
|
304
|
+
logger_1.default.printLine(`${prefix} ${chalk_1.default.yellow(`${figures_1.default.warning} Gracefully restarting ${this.options.processName}...`)}`, 'info');
|
|
305
|
+
}
|
|
306
|
+
this.restartCount++;
|
|
307
|
+
this.lastRestartTime = new Date();
|
|
308
|
+
// Stop current processes gracefully
|
|
309
|
+
if (this.runner) {
|
|
310
|
+
this.runner.cleanup(this.options.signal);
|
|
311
|
+
}
|
|
312
|
+
// Wait for graceful shutdown
|
|
313
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
314
|
+
// Print restart banner
|
|
315
|
+
this.printStartBanner();
|
|
316
|
+
// Start application again
|
|
317
|
+
await this.startApplication();
|
|
318
|
+
if (this.options.showInfo) {
|
|
319
|
+
logger_1.default.printLine(`${prefix} ${chalk_1.default.green(`${figures_1.default.tick} ${this.options.processName} restarted successfully`)}`, 'info');
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
async stop() {
|
|
323
|
+
const prefix = chalk_1.default.cyan(`[${this.options.runnerName}]`);
|
|
324
|
+
if (!this.isRunning) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (this.options.showInfo) {
|
|
328
|
+
logger_1.default.printLine(`${prefix} ${chalk_1.default.yellow(`${figures_1.default.warning} Stopping ${this.options.processName}...`)}`, 'info');
|
|
329
|
+
}
|
|
330
|
+
this.isRunning = false;
|
|
331
|
+
// Stop file watcher
|
|
332
|
+
if (this.fileWatcher) {
|
|
333
|
+
this.fileWatcher.stop();
|
|
334
|
+
}
|
|
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');
|
|
345
|
+
}
|
|
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
|
+
}
|
|
360
|
+
isActive() {
|
|
361
|
+
return this.isRunning;
|
|
362
|
+
}
|
|
363
|
+
getUptime() {
|
|
364
|
+
return Math.floor((Date.now() - this.startTime.getTime()) / 1000);
|
|
365
|
+
}
|
|
366
|
+
getRestartCount() {
|
|
367
|
+
return this.restartCount;
|
|
368
|
+
}
|
|
369
|
+
getCrashCount() {
|
|
370
|
+
return this.crashCount;
|
|
371
|
+
}
|
|
372
|
+
getProcessStats() {
|
|
373
|
+
this.collectProcessStats();
|
|
374
|
+
return { ...this.processStats };
|
|
375
|
+
}
|
|
376
|
+
getLastRestartTime() {
|
|
377
|
+
return this.lastRestartTime;
|
|
378
|
+
}
|
|
379
|
+
getWatchedFiles() {
|
|
380
|
+
var _a;
|
|
381
|
+
return ((_a = this.fileWatcher) === null || _a === void 0 ? void 0 : _a.getWatchedFiles()) || [];
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
exports.StartManager = StartManager;
|