neex 0.6.54 → 0.6.55

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.
@@ -13,10 +13,10 @@ const fs_1 = __importDefault(require("fs"));
13
13
  const os_1 = __importDefault(require("os"));
14
14
  function addStartCommands(program) {
15
15
  let startManager = null;
16
- // Single optimized start command for production
16
+ // Production start command
17
17
  program
18
18
  .command('start [file]')
19
- .description('Start production application with automatic optimization')
19
+ .description('Start production application')
20
20
  .option('-d, --dir <directory>', 'Working directory', process.cwd())
21
21
  .option('-e, --env <file>', 'Environment file to load', '.env')
22
22
  .option('-p, --port <port>', 'Port number', parseInt)
@@ -32,7 +32,7 @@ function addStartCommands(program) {
32
32
  .option('--node-args <args>', 'Additional Node.js arguments')
33
33
  .action(async (file, options) => {
34
34
  try {
35
- const targetFile = file || 'dist/index.js';
35
+ const targetFile = file || 'dist/server.js';
36
36
  let resolvedFile = path_1.default.resolve(options.dir, targetFile);
37
37
  // Auto-detect main file if not found
38
38
  if (!fs_1.default.existsSync(resolvedFile)) {
@@ -53,12 +53,13 @@ function addStartCommands(program) {
53
53
  const commonLocations = [
54
54
  'dist/server.js',
55
55
  'dist/app.js',
56
- 'build/index.js',
56
+ 'dist/index.js',
57
57
  'build/server.js',
58
- 'src/index.js',
59
- 'src/server.js',
60
- 'index.js',
61
- 'server.js'
58
+ 'build/app.js',
59
+ 'build/index.js',
60
+ 'server.js',
61
+ 'app.js',
62
+ 'index.js'
62
63
  ];
63
64
  let found = false;
64
65
  for (const location of commonLocations) {
@@ -85,7 +86,7 @@ function addStartCommands(program) {
85
86
  throw new Error(`Application file not found: ${resolvedFile}`);
86
87
  }
87
88
  }
88
- // Detect environment and set optimal configuration
89
+ // Environment detection
89
90
  const isDevelopment = options.watch || process.env.NODE_ENV === 'development';
90
91
  const isProduction = !isDevelopment;
91
92
  const cpuCount = os_1.default.cpus().length;
@@ -93,17 +94,17 @@ function addStartCommands(program) {
93
94
  let workers = options.workers;
94
95
  if (!workers) {
95
96
  if (isDevelopment) {
96
- workers = 1; // Single worker for development
97
+ workers = 1;
97
98
  }
98
99
  else {
99
- // For production, use CPU count but cap at 8 for most applications
100
- workers = Math.min(cpuCount, 8);
100
+ // For production, use CPU count but cap at reasonable limit
101
+ workers = Math.min(cpuCount, 4); // Reduced from 8 to 4 for better resource management
101
102
  }
102
103
  }
103
- const healthCheck = options.health !== false && isProduction;
104
+ const healthCheck = options.health !== false;
104
105
  const defaultPort = parseInt(process.env.PORT || '8000');
105
106
  const port = options.port || defaultPort;
106
- // Environment setup
107
+ // Set NODE_ENV if not already set
107
108
  if (!process.env.NODE_ENV) {
108
109
  process.env.NODE_ENV = isProduction ? 'production' : 'development';
109
110
  }
@@ -134,7 +135,7 @@ function addStartCommands(program) {
134
135
  watch: options.watch,
135
136
  maxMemory: options.maxMemory || (isProduction ? '1G' : undefined),
136
137
  maxCrashes: isProduction ? 3 : 10,
137
- restartDelay: isProduction ? 2000 : 1000,
138
+ restartDelay: isProduction ? 1000 : 500,
138
139
  healthCheck,
139
140
  healthPort: options.healthPort,
140
141
  gracefulTimeout: options.gracefulTimeout,
@@ -162,14 +163,13 @@ function addStartCommands(program) {
162
163
  startManager = null;
163
164
  }
164
165
  catch (error) {
165
- // Ignore cleanup errors but log in verbose mode
166
166
  if (process.env.VERBOSE) {
167
167
  console.error('Cleanup error:', error);
168
168
  }
169
169
  }
170
170
  }
171
171
  };
172
- // Enhanced signal handling
172
+ // Signal handling
173
173
  const handleExit = (signal) => {
174
174
  if (startManager) {
175
175
  console.log(`\n${chalk_1.default.yellow(figures_1.default.warning)} Received ${signal}, shutting down gracefully...`);
@@ -183,11 +183,9 @@ function addStartCommands(program) {
183
183
  process.exit(0);
184
184
  }
185
185
  };
186
- // Register signal handlers
187
186
  process.on('SIGINT', () => handleExit('SIGINT'));
188
187
  process.on('SIGTERM', () => handleExit('SIGTERM'));
189
188
  process.on('SIGUSR2', () => handleExit('SIGUSR2'));
190
- // Handle uncaught exceptions
191
189
  process.on('uncaughtException', (error) => {
192
190
  console.error(`${chalk_1.default.red(figures_1.default.cross)} Uncaught Exception:`, error);
193
191
  cleanupStart().then(() => {
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.StartManager = void 0;
7
- // src/start-manager.ts - Fixed and optimized production start manager
7
+ // src/start-manager.ts - Fixed production start manager
8
8
  const child_process_1 = require("child_process");
9
9
  const chokidar_1 = require("chokidar");
10
10
  const logger_manager_js_1 = require("./logger-manager.js");
@@ -23,6 +23,7 @@ class StartManager {
23
23
  this.isShuttingDown = false;
24
24
  this.totalRestarts = 0;
25
25
  this.envLoaded = false;
26
+ this.masterProcess = null;
26
27
  this.options = options;
27
28
  this.startTime = new Date();
28
29
  this.debouncedRestart = (0, lodash_1.debounce)(this.restartAll.bind(this), options.restartDelay);
@@ -96,58 +97,110 @@ class StartManager {
96
97
  }
97
98
  return args;
98
99
  }
100
+ async startSingleProcess() {
101
+ const nodeArgs = this.getNodeArgs();
102
+ const port = this.options.port || 8000;
103
+ const env = {
104
+ ...process.env,
105
+ NODE_ENV: process.env.NODE_ENV || 'production',
106
+ PORT: port.toString(),
107
+ FORCE_COLOR: this.options.color ? '1' : '0',
108
+ NODE_OPTIONS: '--no-deprecation'
109
+ };
110
+ this.masterProcess = (0, child_process_1.fork)(this.options.file, [], {
111
+ cwd: this.options.workingDir,
112
+ env,
113
+ execArgv: nodeArgs,
114
+ silent: false // Let the process handle its own output
115
+ });
116
+ this.masterProcess.on('error', (error) => {
117
+ this.log(`Process error: ${error.message}`, 'error');
118
+ });
119
+ this.masterProcess.on('exit', (code, signal) => {
120
+ if (!this.isShuttingDown && code !== 0 && signal !== 'SIGTERM') {
121
+ this.log(`Process crashed (code: ${code}, signal: ${signal})`, 'error');
122
+ this.totalRestarts++;
123
+ if (this.totalRestarts < this.options.maxCrashes) {
124
+ setTimeout(() => {
125
+ this.startSingleProcess();
126
+ }, this.options.restartDelay);
127
+ }
128
+ else {
129
+ this.log(`Max crashes reached (${this.options.maxCrashes}), not restarting`, 'error');
130
+ }
131
+ }
132
+ });
133
+ // Wait for process to be ready
134
+ await new Promise((resolve, reject) => {
135
+ const timeout = setTimeout(() => {
136
+ resolve(); // Don't reject, assume it's ready
137
+ }, 5000);
138
+ this.masterProcess.on('message', (message) => {
139
+ if (message && message.type === 'ready') {
140
+ clearTimeout(timeout);
141
+ resolve();
142
+ }
143
+ });
144
+ this.masterProcess.on('error', (error) => {
145
+ clearTimeout(timeout);
146
+ reject(error);
147
+ });
148
+ this.masterProcess.on('exit', (code) => {
149
+ clearTimeout(timeout);
150
+ if (code !== 0) {
151
+ reject(new Error(`Process exited with code ${code}`));
152
+ }
153
+ else {
154
+ resolve();
155
+ }
156
+ });
157
+ });
158
+ if (this.options.verbose) {
159
+ this.log(`Process started (PID: ${this.masterProcess.pid}, Port: ${port})`);
160
+ }
161
+ }
99
162
  async startWorker(workerId) {
100
163
  var _a, _b;
101
164
  const nodeArgs = this.getNodeArgs();
102
- const basePort = this.options.port || 8000;
103
- const workerPort = this.options.workers > 1 ? basePort + workerId - 1 : basePort;
165
+ const port = this.options.port || 8000;
104
166
  const env = {
105
167
  ...process.env,
106
168
  NODE_ENV: process.env.NODE_ENV || 'production',
107
169
  WORKER_ID: workerId.toString(),
108
- PORT: workerPort.toString(),
170
+ PORT: port.toString(),
109
171
  CLUSTER_WORKER: 'true',
110
172
  FORCE_COLOR: this.options.color ? '1' : '0',
111
- // Prevent dotenv from loading again in worker processes
112
- DOTENV_CONFIG_PATH: '',
113
173
  NODE_OPTIONS: '--no-deprecation'
114
174
  };
115
175
  const workerProcess = (0, child_process_1.fork)(this.options.file, [], {
116
176
  cwd: this.options.workingDir,
117
177
  env,
118
178
  execArgv: nodeArgs,
119
- silent: true // Capture output to avoid duplication
179
+ silent: true
120
180
  });
121
181
  const workerInfo = {
122
182
  process: workerProcess,
123
183
  pid: workerProcess.pid,
124
184
  restarts: 0,
125
185
  startTime: new Date(),
126
- id: workerId
186
+ id: workerId,
187
+ port: port
127
188
  };
128
189
  this.workers.set(workerId, workerInfo);
129
- // Handle worker output with proper prefixing
190
+ // Handle worker output
130
191
  (_a = workerProcess.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
131
192
  const message = data.toString().trim();
132
- if (message) {
133
- // Filter out repetitive dotenv messages
134
- if (message.includes('[dotenv@') || message.includes('injecting env')) {
135
- return;
136
- }
193
+ if (message && !message.includes('[dotenv@') && !message.includes('injecting env')) {
137
194
  const prefix = this.options.verbose ?
138
- chalk_1.default.dim(`[Worker ${workerId}:${workerPort}] `) : '';
195
+ chalk_1.default.dim(`[Worker ${workerId}] `) : '';
139
196
  console.log(prefix + message);
140
197
  }
141
198
  });
142
199
  (_b = workerProcess.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
143
200
  const message = data.toString().trim();
144
- if (message) {
145
- // Filter out non-critical warnings
146
- if (message.includes('[dotenv@') || message.includes('injecting env')) {
147
- return;
148
- }
201
+ if (message && !message.includes('[dotenv@') && !message.includes('injecting env')) {
149
202
  const prefix = this.options.verbose ?
150
- chalk_1.default.dim(`[Worker ${workerId}:${workerPort}] `) : '';
203
+ chalk_1.default.dim(`[Worker ${workerId}] `) : '';
151
204
  console.error(prefix + chalk_1.default.red(message));
152
205
  }
153
206
  });
@@ -156,18 +209,16 @@ class StartManager {
156
209
  });
157
210
  workerProcess.on('exit', (code, signal) => {
158
211
  this.workers.delete(workerId);
159
- if (!this.isShuttingDown) {
160
- if (code !== 0 && signal !== 'SIGTERM') {
161
- this.log(`Worker ${workerId} crashed (code: ${code}, signal: ${signal})`, 'error');
162
- this.restartWorker(workerId);
163
- }
212
+ if (!this.isShuttingDown && code !== 0 && signal !== 'SIGTERM') {
213
+ this.log(`Worker ${workerId} crashed (code: ${code}, signal: ${signal})`, 'error');
214
+ this.restartWorker(workerId);
164
215
  }
165
216
  });
166
217
  // Wait for worker to be ready
167
218
  await new Promise((resolve, reject) => {
168
219
  const timeout = setTimeout(() => {
169
- reject(new Error(`Worker ${workerId} failed to start within timeout`));
170
- }, 10000);
220
+ resolve(); // Don't reject, assume it's ready
221
+ }, 5000);
171
222
  workerProcess.on('message', (message) => {
172
223
  if (message && message.type === 'ready') {
173
224
  clearTimeout(timeout);
@@ -179,19 +230,17 @@ class StartManager {
179
230
  reject(error);
180
231
  });
181
232
  workerProcess.on('exit', (code) => {
233
+ clearTimeout(timeout);
182
234
  if (code !== 0) {
183
- clearTimeout(timeout);
184
235
  reject(new Error(`Worker ${workerId} exited with code ${code}`));
185
236
  }
237
+ else {
238
+ resolve();
239
+ }
186
240
  });
187
- // Fallback - assume ready after 2 seconds if no message
188
- setTimeout(() => {
189
- clearTimeout(timeout);
190
- resolve();
191
- }, 2000);
192
241
  });
193
242
  if (this.options.verbose) {
194
- this.log(`Worker ${workerId} started (PID: ${workerProcess.pid}, Port: ${workerPort})`);
243
+ this.log(`Worker ${workerId} started (PID: ${workerProcess.pid})`);
195
244
  }
196
245
  return workerInfo;
197
246
  }
@@ -235,6 +284,12 @@ class StartManager {
235
284
  });
236
285
  }
237
286
  async startCluster() {
287
+ if (this.options.workers === 1) {
288
+ // Single process mode
289
+ await this.startSingleProcess();
290
+ return;
291
+ }
292
+ // Multi-worker mode
238
293
  const startPromises = [];
239
294
  for (let i = 0; i < this.options.workers; i++) {
240
295
  startPromises.push(this.startWorker(i + 1));
@@ -272,7 +327,8 @@ class StartManager {
272
327
  })),
273
328
  totalRestarts: this.totalRestarts,
274
329
  memory: process.memoryUsage(),
275
- cpu: os_1.default.loadavg()
330
+ cpu: os_1.default.loadavg(),
331
+ port: this.options.port || 8000
276
332
  };
277
333
  res.writeHead(200, { 'Content-Type': 'application/json' });
278
334
  res.end(JSON.stringify(stats, null, 2));
@@ -321,67 +377,54 @@ class StartManager {
321
377
  async restartAll() {
322
378
  if (this.isShuttingDown)
323
379
  return;
324
- this.log('Restarting all workers due to file changes...');
325
- const restartPromises = [];
326
- for (const [workerId, workerInfo] of this.workers.entries()) {
327
- restartPromises.push((async () => {
328
- try {
329
- workerInfo.process.kill('SIGTERM');
330
- await this.waitForProcessExit(workerInfo.process, 5000);
331
- }
332
- catch (error) {
333
- workerInfo.process.kill('SIGKILL');
334
- }
335
- })());
336
- }
337
- await Promise.allSettled(restartPromises);
338
- // Wait a bit before restarting
339
- setTimeout(() => {
340
- this.startCluster();
341
- }, this.options.restartDelay);
342
- }
343
- setupMemoryMonitoring() {
344
- if (!this.options.maxMemory)
345
- return;
346
- const maxMemory = this.parseMemoryLimit(this.options.maxMemory);
347
- if (!maxMemory)
348
- return;
349
- const checkInterval = setInterval(() => {
350
- if (this.isShuttingDown) {
351
- clearInterval(checkInterval);
352
- return;
380
+ this.log('Restarting due to file changes...');
381
+ if (this.options.workers === 1 && this.masterProcess) {
382
+ // Single process restart
383
+ try {
384
+ this.masterProcess.kill('SIGTERM');
385
+ await this.waitForProcessExit(this.masterProcess, 5000);
353
386
  }
354
- this.workers.forEach((workerInfo, workerId) => {
355
- try {
356
- // This is a simplified check - in practice, you'd need to get actual worker memory usage
357
- const usage = process.memoryUsage();
358
- if (usage.heapUsed > maxMemory) {
359
- this.log(`Worker ${workerId} may have exceeded memory limit, restarting`, 'warn');
360
- this.restartWorker(workerId);
387
+ catch (error) {
388
+ this.masterProcess.kill('SIGKILL');
389
+ }
390
+ setTimeout(() => {
391
+ this.startSingleProcess();
392
+ }, this.options.restartDelay);
393
+ }
394
+ else {
395
+ // Multi-worker restart
396
+ const restartPromises = [];
397
+ for (const [workerId, workerInfo] of this.workers.entries()) {
398
+ restartPromises.push((async () => {
399
+ try {
400
+ workerInfo.process.kill('SIGTERM');
401
+ await this.waitForProcessExit(workerInfo.process, 5000);
361
402
  }
362
- }
363
- catch (error) {
364
- // Ignore monitoring errors
365
- }
366
- });
367
- }, 30000); // Check every 30 seconds
403
+ catch (error) {
404
+ workerInfo.process.kill('SIGKILL');
405
+ }
406
+ })());
407
+ }
408
+ await Promise.allSettled(restartPromises);
409
+ setTimeout(() => {
410
+ this.startCluster();
411
+ }, this.options.restartDelay);
412
+ }
368
413
  }
369
414
  async start() {
370
415
  try {
371
- // Load environment variables only once in the main process
416
+ // Load environment variables
372
417
  this.loadEnvFile();
373
418
  // Set up monitoring and health checks
374
419
  this.setupHealthCheck();
375
420
  this.setupWatcher();
376
- this.setupMemoryMonitoring();
377
- // Start worker cluster
421
+ // Start the application
378
422
  await this.startCluster();
379
423
  // Success message
380
- const basePort = this.options.port || 8000;
381
- const portInfo = this.options.workers > 1 ?
382
- ` on ports ${basePort}-${basePort + this.options.workers - 1}` :
383
- ` on port ${basePort}`;
384
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.tick)} Server ready${portInfo} (${this.workers.size} workers)`, 'info');
424
+ const port = this.options.port || 8000;
425
+ const workerInfo = this.options.workers === 1 ?
426
+ '' : ` (${this.workers.size} workers)`;
427
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.tick)} Server ready on port ${port}${workerInfo}`, 'info');
385
428
  }
386
429
  catch (error) {
387
430
  this.log(`Failed to start server: ${error.message}`, 'error');
@@ -399,6 +442,17 @@ class StartManager {
399
442
  if (this.healthServer) {
400
443
  this.healthServer.close();
401
444
  }
445
+ // Stop single process
446
+ if (this.masterProcess) {
447
+ try {
448
+ this.masterProcess.kill('SIGTERM');
449
+ await this.waitForProcessExit(this.masterProcess, this.options.gracefulTimeout);
450
+ }
451
+ catch (error) {
452
+ this.masterProcess.kill('SIGKILL');
453
+ }
454
+ }
455
+ // Stop workers
402
456
  const shutdownPromises = [];
403
457
  for (const [workerId, workerInfo] of this.workers.entries()) {
404
458
  shutdownPromises.push((async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neex",
3
- "version": "0.6.54",
3
+ "version": "0.6.55",
4
4
  "description": "The Modern Build System for Polyrepo-in-Monorepo Architecture",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",