neex 0.6.46 → 0.6.50

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.
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.BuildManager = void 0;
7
- // src/build-manager.ts - Build manager for TypeScript projects using tsx
7
+ // src/build-manager.ts - Build manager for TypeScript projects using tsc
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");
@@ -19,7 +19,7 @@ class BuildManager {
19
19
  this.buildProcess = null;
20
20
  this.isBuilding = false;
21
21
  this.buildCount = 0;
22
- this.debouncedBuild = this.debounce(this.runBuild.bind(this), 500);
22
+ this.debouncedBuild = this.debounce(this.runBuild.bind(this), 300);
23
23
  this.options = options;
24
24
  }
25
25
  async cleanOutputDirectory() {
@@ -78,49 +78,28 @@ class BuildManager {
78
78
  }
79
79
  }
80
80
  }
81
- getBuildCommand() {
82
- if (this.options.bundle) {
83
- // Use tsx for bundling
84
- const args = [
85
- 'build',
86
- this.options.source,
87
- '--out-dir',
88
- this.options.output,
89
- '--target',
90
- this.options.target
91
- ];
92
- if (this.options.minify) {
93
- args.push('--minify');
94
- }
95
- if (this.options.sourcemap) {
96
- args.push('--sourcemap');
97
- }
98
- if (this.options.format === 'esm') {
99
- args.push('--format', 'esm');
100
- }
101
- if (this.options.external.length > 0) {
102
- args.push('--external', this.options.external.join(','));
103
- }
104
- return { command: 'tsx', args };
81
+ getTscCommand() {
82
+ const args = [
83
+ '--project',
84
+ this.options.tsconfig,
85
+ '--outDir',
86
+ this.options.output,
87
+ '--target',
88
+ this.options.target,
89
+ '--declaration'
90
+ ];
91
+ if (this.options.sourcemap) {
92
+ args.push('--sourceMap');
93
+ }
94
+ if (this.options.format === 'esm') {
95
+ args.push('--module', 'es2020', '--moduleResolution', 'node');
105
96
  }
106
97
  else {
107
- // Use TypeScript compiler directly
108
- const args = [
109
- '--project',
110
- this.options.tsconfig,
111
- '--outDir',
112
- this.options.output,
113
- '--target',
114
- this.options.target
115
- ];
116
- if (this.options.sourcemap) {
117
- args.push('--sourceMap');
118
- }
119
- if (this.options.format === 'esm') {
120
- args.push('--module', 'es2020');
121
- }
122
- return { command: 'tsc', args };
98
+ args.push('--module', 'commonjs');
123
99
  }
100
+ // Always include these for better compatibility
101
+ args.push('--esModuleInterop', '--allowSyntheticDefaultImports', '--strict');
102
+ return { command: 'tsc', args };
124
103
  }
125
104
  async runBuild() {
126
105
  if (this.isBuilding) {
@@ -135,31 +114,28 @@ class BuildManager {
135
114
  }
136
115
  try {
137
116
  await this.ensureOutputDirectory();
138
- const { command, args } = this.getBuildCommand();
117
+ const { command, args } = this.getTscCommand();
139
118
  if (this.options.verbose) {
140
119
  logger_manager_js_1.loggerManager.printLine(`Executing: ${command} ${args.join(' ')}`, 'info');
141
120
  }
142
121
  return new Promise((resolve, reject) => {
143
122
  var _a, _b;
144
123
  this.buildProcess = (0, child_process_1.spawn)(command, args, {
145
- stdio: this.options.verbose ? ['ignore', 'pipe', 'pipe'] : ['ignore', 'ignore', 'pipe'],
124
+ stdio: ['ignore', 'pipe', 'pipe'],
146
125
  shell: false,
147
126
  env: {
148
127
  ...process.env,
149
- FORCE_COLOR: this.options.color ? '1' : '0'
128
+ FORCE_COLOR: '0' // Disable TSC colors to avoid log pollution
150
129
  }
151
130
  });
131
+ let stdout = '';
152
132
  let stderr = '';
153
- if (this.options.verbose) {
154
- (_a = this.buildProcess.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
155
- process.stdout.write(data);
156
- });
157
- }
133
+ // Capture all output but don't display TSC logs
134
+ (_a = this.buildProcess.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
135
+ stdout += data.toString();
136
+ });
158
137
  (_b = this.buildProcess.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
159
138
  stderr += data.toString();
160
- if (this.options.verbose) {
161
- process.stderr.write(data);
162
- }
163
139
  });
164
140
  this.buildProcess.on('error', (error) => {
165
141
  this.buildProcess = null;
@@ -182,11 +158,15 @@ class BuildManager {
182
158
  resolve();
183
159
  }
184
160
  else {
185
- const error = new Error(`Build failed with code ${code}`);
186
- if (stderr) {
187
- logger_manager_js_1.loggerManager.printLine(`Build errors:\n${stderr}`, 'error');
161
+ // Only show meaningful errors, filter out TSC verbosity
162
+ const meaningfulErrors = this.filterTscErrors(stderr);
163
+ if (meaningfulErrors) {
164
+ logger_manager_js_1.loggerManager.printLine(`Build failed:\n${meaningfulErrors}`, 'error');
188
165
  }
189
- reject(error);
166
+ else {
167
+ logger_manager_js_1.loggerManager.printLine(`Build failed with code ${code}`, 'error');
168
+ }
169
+ reject(new Error(`Build failed with code ${code}`));
190
170
  }
191
171
  });
192
172
  });
@@ -196,14 +176,28 @@ class BuildManager {
196
176
  throw error;
197
177
  }
198
178
  }
179
+ filterTscErrors(stderr) {
180
+ if (!stderr)
181
+ return '';
182
+ const lines = stderr.split('\n');
183
+ const meaningfulLines = lines.filter(line => {
184
+ const trimmed = line.trim();
185
+ // Filter out TSC verbose output, keep only actual errors
186
+ return trimmed &&
187
+ !trimmed.includes('message TS') &&
188
+ !trimmed.includes('Found 0 errors') &&
189
+ !trimmed.match(/^\s*\d+\s*$/) && // Filter line numbers
190
+ !trimmed.includes('Watching for file changes');
191
+ });
192
+ return meaningfulLines.join('\n').trim();
193
+ }
199
194
  async analyzeBuild() {
200
195
  try {
201
- const stats = await promises_1.default.stat(this.options.output);
202
196
  const files = await promises_1.default.readdir(this.options.output, { withFileTypes: true });
203
197
  let totalSize = 0;
204
198
  const fileStats = [];
205
199
  for (const file of files) {
206
- if (file.isFile()) {
200
+ if (file.isFile() && (file.name.endsWith('.js') || file.name.endsWith('.d.ts'))) {
207
201
  const filePath = path_1.default.join(this.options.output, file.name);
208
202
  const stat = await promises_1.default.stat(filePath);
209
203
  totalSize += stat.size;
@@ -211,11 +205,13 @@ class BuildManager {
211
205
  }
212
206
  }
213
207
  fileStats.sort((a, b) => b.size - a.size);
214
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Bundle Analysis:`, 'info');
208
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Build Analysis:`, 'info');
215
209
  logger_manager_js_1.loggerManager.printLine(`Total size: ${chalk_1.default.cyan(this.formatBytes(totalSize))}`, 'info');
216
- logger_manager_js_1.loggerManager.printLine(`Files: ${files.length}`, 'info');
217
- if (this.options.verbose) {
218
- fileStats.slice(0, 10).forEach(file => {
210
+ logger_manager_js_1.loggerManager.printLine(`Generated files: ${fileStats.length}`, 'info');
211
+ if (this.options.verbose && fileStats.length > 0) {
212
+ const topFiles = fileStats.slice(0, 5);
213
+ logger_manager_js_1.loggerManager.printLine('Largest files:', 'info');
214
+ topFiles.forEach(file => {
219
215
  logger_manager_js_1.loggerManager.printLine(` ${file.name}: ${this.formatBytes(file.size)}`, 'info');
220
216
  });
221
217
  }
@@ -237,7 +233,7 @@ class BuildManager {
237
233
  this.buildProcess = null;
238
234
  const cleanup = () => {
239
235
  if (!this.options.quiet) {
240
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.square)} Stopped build process`, 'info');
236
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.square)} Build process stopped`, 'info');
241
237
  }
242
238
  resolve();
243
239
  };
@@ -257,7 +253,7 @@ class BuildManager {
257
253
  // Ignore
258
254
  }
259
255
  }
260
- }, 5000);
256
+ }, 3000);
261
257
  }
262
258
  }
263
259
  catch (error) {
@@ -286,12 +282,13 @@ class BuildManager {
286
282
  ignoreInitial: true,
287
283
  followSymlinks: false,
288
284
  usePolling: false,
289
- atomic: 300,
285
+ atomic: 200,
290
286
  ignored: [
291
287
  '**/node_modules/**',
292
288
  '**/.git/**',
293
289
  `**/${this.options.output}/**`,
294
- '**/*.log'
290
+ '**/*.log',
291
+ '**/*.map'
295
292
  ]
296
293
  });
297
294
  this.watcher.on('change', (filePath) => {
@@ -342,7 +339,7 @@ class BuildManager {
342
339
  if (this.options.watch) {
343
340
  this.setupWatcher();
344
341
  if (!this.options.quiet) {
345
- logger_manager_js_1.loggerManager.printLine('Watching for file changes...', 'info');
342
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Watching for changes...`, 'info');
346
343
  }
347
344
  }
348
345
  }
@@ -354,13 +351,15 @@ class BuildManager {
354
351
  }
355
352
  }
356
353
  async stop() {
357
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.warning)} Stopping build process...`, 'info');
354
+ if (!this.options.quiet) {
355
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.warning)} Stopping build process...`, 'info');
356
+ }
358
357
  if (this.watcher) {
359
358
  await this.watcher.close();
360
359
  this.watcher = null;
361
360
  }
362
361
  await this.stopProcess();
363
- if (this.buildCount > 0) {
362
+ if (this.buildCount > 0 && !this.options.quiet) {
364
363
  logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Build process stopped after ${this.buildCount} build(s)`, 'info');
365
364
  }
366
365
  }
@@ -7,6 +7,7 @@ exports.addBuildCommands = void 0;
7
7
  const build_manager_js_1 = require("../build-manager.js");
8
8
  const logger_manager_js_1 = require("../logger-manager.js");
9
9
  const chalk_1 = __importDefault(require("chalk"));
10
+ const figures_1 = __importDefault(require("figures"));
10
11
  function addBuildCommands(program) {
11
12
  let buildManager = null;
12
13
  // Build command for TypeScript projects
@@ -15,13 +16,10 @@ function addBuildCommands(program) {
15
16
  .description('Build TypeScript project for production (default: src)')
16
17
  .option('-o, --output <dir>', 'Output directory', 'dist')
17
18
  .option('-w, --watch', 'Watch mode for continuous building')
18
- .option('-c, --clean', 'Clean output directory before build')
19
- .option('-m, --minify', 'Minify output')
19
+ .option('-c, --clean', 'Clean output directory before build', true)
20
20
  .option('-s, --sourcemap', 'Generate source maps')
21
21
  .option('-t, --target <target>', 'TypeScript target (es2020, es2022, etc.)', 'es2020')
22
22
  .option('-f, --format <format>', 'Output format (cjs, esm)', 'cjs')
23
- .option('--no-bundle', 'Don\'t bundle, just compile')
24
- .option('--external <packages>', 'External packages (comma-separated)')
25
23
  .option('--tsconfig <file>', 'TypeScript config file', 'tsconfig.json')
26
24
  .option('-v, --verbose', 'Verbose output')
27
25
  .option('-q, --quiet', 'Quiet output')
@@ -30,18 +28,20 @@ function addBuildCommands(program) {
30
28
  .action(async (source, options) => {
31
29
  try {
32
30
  const sourceDir = source || 'src';
33
- logger_manager_js_1.loggerManager.printLine(`Building TypeScript project from ${chalk_1.default.cyan(sourceDir)}`, 'info');
31
+ if (!options.quiet) {
32
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Building TypeScript project from ${chalk_1.default.cyan(sourceDir)}`, 'info');
33
+ }
34
34
  buildManager = new build_manager_js_1.BuildManager({
35
35
  source: sourceDir,
36
36
  output: options.output,
37
37
  watch: options.watch,
38
38
  clean: options.clean,
39
- minify: options.minify,
39
+ minify: false,
40
40
  sourcemap: options.sourcemap,
41
41
  target: options.target,
42
42
  format: options.format,
43
- bundle: options.bundle,
44
- external: options.external ? options.external.split(',').map((p) => p.trim()) : [],
43
+ bundle: false,
44
+ external: [],
45
45
  tsconfig: options.tsconfig,
46
46
  verbose: options.verbose,
47
47
  quiet: options.quiet,
@@ -49,24 +49,94 @@ function addBuildCommands(program) {
49
49
  analyze: options.analyze
50
50
  });
51
51
  await buildManager.build();
52
+ // If not in watch mode, show completion message
53
+ if (!options.watch && !options.quiet) {
54
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.tick)} Build completed successfully`, 'info');
55
+ }
56
+ }
57
+ catch (error) {
58
+ if (error instanceof Error) {
59
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Build failed: ${error.message}`, 'error');
60
+ }
61
+ else {
62
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} An unknown build error occurred`, 'error');
63
+ }
64
+ process.exit(1);
65
+ }
66
+ });
67
+ // Add a quick build command without options
68
+ program
69
+ .command('compile [source]')
70
+ .alias('tsc')
71
+ .description('Quick TypeScript compilation (alias for build)')
72
+ .action(async (source) => {
73
+ try {
74
+ const sourceDir = source || 'src';
75
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Compiling TypeScript...`, 'info');
76
+ buildManager = new build_manager_js_1.BuildManager({
77
+ source: sourceDir,
78
+ output: 'dist',
79
+ watch: false,
80
+ clean: true,
81
+ minify: false,
82
+ sourcemap: false,
83
+ target: 'es2020',
84
+ format: 'cjs',
85
+ bundle: false,
86
+ external: [],
87
+ tsconfig: 'tsconfig.json',
88
+ verbose: false,
89
+ quiet: false,
90
+ color: true,
91
+ analyze: false
92
+ });
93
+ await buildManager.build();
94
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.tick)} Compilation completed`, 'info');
52
95
  }
53
96
  catch (error) {
54
97
  if (error instanceof Error) {
55
- logger_manager_js_1.loggerManager.printLine(`Build error: ${error.message}`, 'error');
98
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Compilation failed: ${error.message}`, 'error');
56
99
  }
57
100
  else {
58
- logger_manager_js_1.loggerManager.printLine('An unknown build error occurred', 'error');
101
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} An unknown compilation error occurred`, 'error');
59
102
  }
60
103
  process.exit(1);
61
104
  }
62
105
  });
63
106
  // Cleanup function
64
- const cleanupBuild = () => {
107
+ const cleanupBuild = async () => {
108
+ if (buildManager) {
109
+ try {
110
+ await buildManager.stop();
111
+ buildManager = null;
112
+ }
113
+ catch (error) {
114
+ // Ignore cleanup errors
115
+ }
116
+ }
117
+ };
118
+ // Handle process termination
119
+ const handleExit = (signal) => {
65
120
  if (buildManager) {
66
- buildManager.stop();
67
- buildManager = null;
121
+ logger_manager_js_1.loggerManager.printLine(`\n${chalk_1.default.yellow(figures_1.default.warning)} Received ${signal}, stopping build process...`, 'info');
122
+ cleanupBuild().then(() => {
123
+ process.exit(0);
124
+ }).catch(() => {
125
+ process.exit(1);
126
+ });
127
+ }
128
+ else {
129
+ process.exit(0);
68
130
  }
69
131
  };
132
+ // Register signal handlers
133
+ process.on('SIGINT', () => handleExit('SIGINT'));
134
+ process.on('SIGTERM', () => handleExit('SIGTERM'));
135
+ process.on('exit', () => {
136
+ if (buildManager) {
137
+ cleanupBuild();
138
+ }
139
+ });
70
140
  return { cleanupBuild };
71
141
  }
72
142
  exports.addBuildCommands = addBuildCommands;
@@ -6,55 +6,250 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.addStartCommands = void 0;
7
7
  const start_manager_js_1 = require("../start-manager.js");
8
8
  const logger_manager_js_1 = require("../logger-manager.js");
9
- const path_1 = __importDefault(require("path"));
10
- const fs_1 = __importDefault(require("fs"));
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const figures_1 = __importDefault(require("figures"));
11
+ const os_1 = __importDefault(require("os"));
11
12
  function addStartCommands(program) {
12
- let manager = null;
13
- const startCommand = program
13
+ let startManager = null;
14
+ // Main start command
15
+ program
14
16
  .command('start [entry]')
15
- .description('Start a production-ready application')
16
- .option('-n, --name <name>', 'Application name')
17
- .option('-w, --watch', 'Watch for file changes and restart the application', false)
18
- .option('--quiet', 'Suppress all output', false)
19
- .option('--color', 'Force color output', true)
20
- .option('--verbose', 'Enable verbose logging', false)
21
- .option('--node-args <args>', 'Arguments to pass to the node process', (value) => value.split(' '), [])
17
+ .description('Start the production server (default: dist/index.js)')
18
+ .option('-p, --port <port>', 'Port to run the server on', '3000')
19
+ .option('-h, --host <host>', 'Host to bind the server to', '0.0.0.0')
20
+ .option('-e, --env <env>', 'Environment mode', 'production')
21
+ .option('-w, --watch', 'Watch for file changes and restart')
22
+ .option('-c, --cluster', 'Enable cluster mode')
23
+ .option('--workers <count>', 'Number of worker processes (default: CPU count)', os_1.default.cpus().length.toString())
24
+ .option('-m, --memory <mb>', 'Memory limit in MB', '512')
25
+ .option('-t, --timeout <ms>', 'Startup timeout in milliseconds', '30000')
26
+ .option('--graceful-timeout <ms>', 'Graceful shutdown timeout in milliseconds', '5000')
27
+ .option('-v, --verbose', 'Verbose output')
28
+ .option('-q, --quiet', 'Quiet output')
29
+ .option('--no-color', 'Disable colored output')
30
+ .option('--log-level <level>', 'Log level (error, warn, info, debug)', 'info')
31
+ .option('--node-args <args>', 'Additional Node.js arguments (comma-separated)')
32
+ .option('--inspect', 'Enable Node.js inspector')
33
+ .option('--inspect-port <port>', 'Inspector port', '9229')
34
+ .option('--pid <file>', 'Write process ID to file')
35
+ .option('--restart', 'Auto-restart on failure', true)
36
+ .option('--restart-delay <ms>', 'Delay between restarts in milliseconds', '1000')
37
+ .option('--max-restarts <count>', 'Maximum restart attempts', '10')
22
38
  .action(async (entry, options) => {
23
- let entryPoint = entry;
24
- if (!entryPoint) {
25
- const defaultPath = path_1.default.join(process.cwd(), 'dist', 'index.js');
26
- if (fs_1.default.existsSync(defaultPath)) {
27
- entryPoint = defaultPath;
39
+ try {
40
+ const entryFile = entry || 'dist/index.js';
41
+ if (!options.quiet) {
42
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Starting production server...`, 'info');
43
+ }
44
+ const nodeArgs = options.nodeArgs ?
45
+ options.nodeArgs.split(',').map((arg) => arg.trim()) : [];
46
+ startManager = new start_manager_js_1.StartManager({
47
+ entry: entryFile,
48
+ port: parseInt(options.port),
49
+ host: options.host,
50
+ env: options.env,
51
+ watch: options.watch,
52
+ cluster: options.cluster,
53
+ workers: parseInt(options.workers),
54
+ memory: parseInt(options.memory),
55
+ timeout: parseInt(options.timeout),
56
+ gracefulTimeout: parseInt(options.gracefulTimeout),
57
+ verbose: options.verbose,
58
+ quiet: options.quiet,
59
+ color: options.color,
60
+ logLevel: options.logLevel,
61
+ nodeArgs,
62
+ inspect: options.inspect,
63
+ inspectPort: parseInt(options.inspectPort),
64
+ pid: options.pid,
65
+ restart: options.restart,
66
+ restartDelay: parseInt(options.restartDelay),
67
+ maxRestarts: parseInt(options.maxRestarts)
68
+ });
69
+ await startManager.start();
70
+ }
71
+ catch (error) {
72
+ if (error instanceof Error) {
73
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Start failed: ${error.message}`, 'error');
28
74
  }
29
75
  else {
30
- logger_manager_js_1.loggerManager.printLine('Entry file not found. Please specify an entry file or build the project first.', 'error');
31
- process.exit(1);
76
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} An unknown start error occurred`, 'error');
77
+ }
78
+ process.exit(1);
79
+ }
80
+ });
81
+ // Quick start command for development
82
+ program
83
+ .command('serve [entry]')
84
+ .description('Quick start with watch mode (development)')
85
+ .option('-p, --port <port>', 'Port to run the server on', '3000')
86
+ .option('-v, --verbose', 'Verbose output')
87
+ .action(async (entry, options) => {
88
+ try {
89
+ const entryFile = entry || 'dist/index.js';
90
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Starting development server...`, 'info');
91
+ startManager = new start_manager_js_1.StartManager({
92
+ entry: entryFile,
93
+ port: parseInt(options.port),
94
+ host: '127.0.0.1',
95
+ env: 'development',
96
+ watch: true,
97
+ cluster: false,
98
+ workers: 1,
99
+ memory: 256,
100
+ timeout: 15000,
101
+ gracefulTimeout: 3000,
102
+ verbose: options.verbose,
103
+ quiet: false,
104
+ color: true,
105
+ logLevel: 'info',
106
+ nodeArgs: [],
107
+ inspect: false,
108
+ inspectPort: 9229,
109
+ pid: '',
110
+ restart: true,
111
+ restartDelay: 500,
112
+ maxRestarts: 50
113
+ });
114
+ await startManager.start();
115
+ }
116
+ catch (error) {
117
+ if (error instanceof Error) {
118
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Serve failed: ${error.message}`, 'error');
32
119
  }
120
+ else {
121
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} An unknown serve error occurred`, 'error');
122
+ }
123
+ process.exit(1);
33
124
  }
34
- const startOptions = {
35
- entry: entryPoint,
36
- name: options.name,
37
- watch: options.watch,
38
- quiet: options.quiet,
39
- color: options.color,
40
- verbose: options.verbose,
41
- nodeArgs: options.nodeArgs,
42
- };
43
- manager = new start_manager_js_1.StartManager(startOptions);
125
+ });
126
+ // Production deployment command
127
+ program
128
+ .command('deploy [entry]')
129
+ .description('Deploy to production with optimal settings')
130
+ .option('-p, --port <port>', 'Port to run the server on', process.env.PORT || '3000')
131
+ .option('-w, --workers <count>', 'Number of worker processes', os_1.default.cpus().length.toString())
132
+ .option('-m, --memory <mb>', 'Memory limit in MB', '1024')
133
+ .option('--pid <file>', 'Write process ID to file', 'server.pid')
134
+ .option('-v, --verbose', 'Verbose output')
135
+ .action(async (entry, options) => {
44
136
  try {
45
- await manager.start();
137
+ const entryFile = entry || 'dist/index.js';
138
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Deploying to production...`, 'info');
139
+ startManager = new start_manager_js_1.StartManager({
140
+ entry: entryFile,
141
+ port: parseInt(options.port),
142
+ host: '0.0.0.0',
143
+ env: 'production',
144
+ watch: false,
145
+ cluster: true,
146
+ workers: parseInt(options.workers),
147
+ memory: parseInt(options.memory),
148
+ timeout: 30000,
149
+ gracefulTimeout: 10000,
150
+ verbose: options.verbose,
151
+ quiet: false,
152
+ color: false,
153
+ logLevel: 'warn',
154
+ nodeArgs: ['--optimize-for-size'],
155
+ inspect: false,
156
+ inspectPort: 9229,
157
+ pid: options.pid,
158
+ restart: true,
159
+ restartDelay: 2000,
160
+ maxRestarts: 5
161
+ });
162
+ await startManager.start();
46
163
  }
47
164
  catch (error) {
48
- logger_manager_js_1.loggerManager.printLine(error.message, 'error');
165
+ if (error instanceof Error) {
166
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Deploy failed: ${error.message}`, 'error');
167
+ }
168
+ else {
169
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} An unknown deploy error occurred`, 'error');
170
+ }
49
171
  process.exit(1);
50
172
  }
51
173
  });
52
- return {
53
- cleanupStart: async () => {
54
- if (manager) {
55
- await manager.stop();
174
+ // Health check command
175
+ program
176
+ .command('health [url]')
177
+ .description('Check server health')
178
+ .option('-t, --timeout <ms>', 'Request timeout in milliseconds', '5000')
179
+ .option('-i, --interval <ms>', 'Check interval in milliseconds', '1000')
180
+ .option('-c, --count <number>', 'Number of checks', '1')
181
+ .action(async (url, options) => {
182
+ const checkUrl = url || 'http://localhost:3000/health';
183
+ const timeout = parseInt(options.timeout);
184
+ const interval = parseInt(options.interval);
185
+ const count = parseInt(options.count);
186
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Health check: ${checkUrl}`, 'info');
187
+ for (let i = 0; i < count; i++) {
188
+ try {
189
+ const controller = new AbortController();
190
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
191
+ const response = await fetch(checkUrl, {
192
+ signal: controller.signal
193
+ });
194
+ clearTimeout(timeoutId);
195
+ if (response.ok) {
196
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.tick)} Health check ${i + 1}/${count}: OK (${response.status})`, 'info');
197
+ }
198
+ else {
199
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Health check ${i + 1}/${count}: Failed (${response.status})`, 'error');
200
+ }
201
+ }
202
+ catch (error) {
203
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Health check ${i + 1}/${count}: Error (${error.message})`, 'error');
204
+ }
205
+ if (i < count - 1) {
206
+ await new Promise(resolve => setTimeout(resolve, interval));
207
+ }
208
+ }
209
+ });
210
+ // Cleanup function
211
+ const cleanupStart = async () => {
212
+ if (startManager) {
213
+ try {
214
+ await startManager.stop();
215
+ startManager = null;
216
+ }
217
+ catch (error) {
218
+ // Ignore cleanup errors
56
219
  }
57
- },
220
+ }
221
+ };
222
+ // Handle process termination
223
+ const handleExit = (signal) => {
224
+ if (startManager) {
225
+ logger_manager_js_1.loggerManager.printLine(`\n${chalk_1.default.yellow(figures_1.default.warning)} Received ${signal}, shutting down gracefully...`, 'info');
226
+ cleanupStart().then(() => {
227
+ process.exit(0);
228
+ }).catch(() => {
229
+ process.exit(1);
230
+ });
231
+ }
232
+ else {
233
+ process.exit(0);
234
+ }
58
235
  };
236
+ // Register signal handlers
237
+ process.on('SIGINT', () => handleExit('SIGINT'));
238
+ process.on('SIGTERM', () => handleExit('SIGTERM'));
239
+ process.on('SIGQUIT', () => handleExit('SIGQUIT'));
240
+ // Handle uncaught exceptions
241
+ process.on('uncaughtException', (error) => {
242
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Uncaught exception: ${error.message}`, 'error');
243
+ cleanupStart().then(() => {
244
+ process.exit(1);
245
+ });
246
+ });
247
+ process.on('unhandledRejection', (reason, promise) => {
248
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Unhandled rejection at: ${promise} reason: ${reason}`, 'error');
249
+ cleanupStart().then(() => {
250
+ process.exit(1);
251
+ });
252
+ });
253
+ return { cleanupStart };
59
254
  }
60
255
  exports.addStartCommands = addStartCommands;
@@ -4,118 +4,407 @@ 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 - Production application runner
7
+ // src/start-manager.ts - Production server manager for built TypeScript projects
8
8
  const child_process_1 = require("child_process");
9
+ const chokidar_1 = require("chokidar");
9
10
  const logger_manager_js_1 = require("./logger-manager.js");
10
11
  const chalk_1 = __importDefault(require("chalk"));
11
12
  const figures_1 = __importDefault(require("figures"));
12
- const fs_1 = __importDefault(require("fs"));
13
13
  const path_1 = __importDefault(require("path"));
14
+ const promises_1 = __importDefault(require("fs/promises"));
15
+ const fs_1 = require("fs");
16
+ const perf_hooks_1 = require("perf_hooks");
14
17
  class StartManager {
15
18
  constructor(options) {
16
- this.process = null;
17
- this.isStopping = false;
19
+ this.serverProcess = null;
20
+ this.watcher = null;
21
+ this.isStarting = false;
22
+ this.restartCount = 0;
23
+ this.startTime = 0;
24
+ this.isShuttingDown = false;
25
+ this.restartTimer = null;
18
26
  this.options = options;
19
27
  }
20
- async startProcess() {
21
- var _a, _b;
22
- if (this.process) {
23
- return;
28
+ async validateEntry() {
29
+ let entryPath = this.options.entry;
30
+ // Check if entry exists as provided
31
+ if ((0, fs_1.existsSync)(entryPath)) {
32
+ return path_1.default.resolve(entryPath);
24
33
  }
25
- const nodeArgs = this.options.nodeArgs || [];
26
- const args = [...nodeArgs, this.options.entry];
27
- if (this.options.verbose) {
28
- logger_manager_js_1.loggerManager.printLine(`Executing: node ${args.join(' ')}`, 'info');
29
- }
30
- this.process = (0, child_process_1.spawn)('node', args, {
31
- stdio: ['ignore', 'pipe', 'pipe'],
32
- shell: false,
33
- env: {
34
- ...process.env,
35
- NODE_ENV: 'production',
36
- FORCE_COLOR: this.options.color ? '1' : '0'
37
- },
38
- detached: true
39
- });
40
- const appName = this.options.name || path_1.default.basename(this.options.entry);
41
- if (!this.options.quiet) {
42
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.play)} Starting ${chalk_1.default.cyan(appName)} in production mode...`, 'info');
34
+ // Try with .js extension
35
+ if (!entryPath.endsWith('.js')) {
36
+ const jsPath = entryPath + '.js';
37
+ if ((0, fs_1.existsSync)(jsPath)) {
38
+ return path_1.default.resolve(jsPath);
39
+ }
43
40
  }
44
- (_a = this.process.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
45
- if (!this.options.quiet) {
46
- process.stdout.write(data);
41
+ // Try in dist directory
42
+ const distPath = path_1.default.join('dist', entryPath);
43
+ if ((0, fs_1.existsSync)(distPath)) {
44
+ return path_1.default.resolve(distPath);
45
+ }
46
+ // Try in dist with .js extension
47
+ if (!entryPath.endsWith('.js')) {
48
+ const distJsPath = path_1.default.join('dist', entryPath + '.js');
49
+ if ((0, fs_1.existsSync)(distJsPath)) {
50
+ return path_1.default.resolve(distJsPath);
47
51
  }
48
- });
49
- (_b = this.process.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
52
+ }
53
+ // Try common entry points
54
+ const commonEntries = [
55
+ 'dist/index.js',
56
+ 'dist/server.js',
57
+ 'dist/app.js',
58
+ 'dist/main.js',
59
+ 'index.js',
60
+ 'server.js'
61
+ ];
62
+ for (const entry of commonEntries) {
63
+ if ((0, fs_1.existsSync)(entry)) {
64
+ if (!this.options.quiet) {
65
+ logger_manager_js_1.loggerManager.printLine(`Using entry point: ${entry}`, 'info');
66
+ }
67
+ return path_1.default.resolve(entry);
68
+ }
69
+ }
70
+ throw new Error(`Entry file not found: ${entryPath}`);
71
+ }
72
+ async loadPackageJson() {
73
+ try {
74
+ const packageJsonPath = path_1.default.join(process.cwd(), 'package.json');
75
+ if ((0, fs_1.existsSync)(packageJsonPath)) {
76
+ const content = await promises_1.default.readFile(packageJsonPath, 'utf8');
77
+ return JSON.parse(content);
78
+ }
79
+ }
80
+ catch (error) {
81
+ // Ignore package.json errors
82
+ }
83
+ return null;
84
+ }
85
+ getNodeArgs() {
86
+ const args = [];
87
+ // Add custom node arguments
88
+ if (this.options.nodeArgs.length > 0) {
89
+ args.push(...this.options.nodeArgs);
90
+ }
91
+ // Memory limit
92
+ if (this.options.memory > 0) {
93
+ args.push(`--max-old-space-size=${this.options.memory}`);
94
+ }
95
+ // Debugging
96
+ if (this.options.inspect) {
97
+ args.push(`--inspect=${this.options.inspectPort}`);
98
+ }
99
+ // Production optimizations
100
+ if (this.options.env === 'production') {
101
+ args.push('--optimize-for-size');
102
+ }
103
+ return args;
104
+ }
105
+ getEnvironment() {
106
+ const env = {
107
+ ...process.env,
108
+ NODE_ENV: this.options.env,
109
+ PORT: this.options.port.toString(),
110
+ HOST: this.options.host,
111
+ LOG_LEVEL: this.options.logLevel
112
+ };
113
+ // Add color support
114
+ if (this.options.color) {
115
+ env.FORCE_COLOR = '1';
116
+ }
117
+ return env;
118
+ }
119
+ async writePidFile(pid) {
120
+ if (this.options.pid) {
121
+ try {
122
+ await promises_1.default.writeFile(this.options.pid, pid.toString());
123
+ if (this.options.verbose) {
124
+ logger_manager_js_1.loggerManager.printLine(`PID file written: ${this.options.pid}`, 'info');
125
+ }
126
+ }
127
+ catch (error) {
128
+ logger_manager_js_1.loggerManager.printLine(`Failed to write PID file: ${error.message}`, 'warn');
129
+ }
130
+ }
131
+ }
132
+ async removePidFile() {
133
+ if (this.options.pid && (0, fs_1.existsSync)(this.options.pid)) {
134
+ try {
135
+ await promises_1.default.unlink(this.options.pid);
136
+ if (this.options.verbose) {
137
+ logger_manager_js_1.loggerManager.printLine(`PID file removed: ${this.options.pid}`, 'info');
138
+ }
139
+ }
140
+ catch (error) {
141
+ // Ignore cleanup errors
142
+ }
143
+ }
144
+ }
145
+ async startServer() {
146
+ if (this.isStarting || this.isShuttingDown) {
147
+ return;
148
+ }
149
+ this.isStarting = true;
150
+ this.startTime = perf_hooks_1.performance.now();
151
+ try {
152
+ const entryPath = await this.validateEntry();
153
+ const nodeArgs = this.getNodeArgs();
154
+ const env = this.getEnvironment();
50
155
  if (!this.options.quiet) {
51
- process.stderr.write(data);
156
+ const restartInfo = this.restartCount > 0 ? ` (restart #${this.restartCount})` : '';
157
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Starting server${restartInfo}...`, 'info');
158
+ if (this.options.verbose) {
159
+ logger_manager_js_1.loggerManager.printLine(`Entry: ${entryPath}`, 'info');
160
+ logger_manager_js_1.loggerManager.printLine(`Environment: ${this.options.env}`, 'info');
161
+ logger_manager_js_1.loggerManager.printLine(`Host: ${this.options.host}:${this.options.port}`, 'info');
162
+ if (nodeArgs.length > 0) {
163
+ logger_manager_js_1.loggerManager.printLine(`Node args: ${nodeArgs.join(' ')}`, 'info');
164
+ }
165
+ }
52
166
  }
53
- });
54
- this.process.on('error', (error) => {
55
- logger_manager_js_1.loggerManager.printLine(`Application error: ${error.message}`, 'error');
56
- });
57
- this.process.on('exit', (code) => {
58
- this.process = null;
59
- if (!this.isStopping) {
60
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Application ${appName} exited with code ${code}`, 'error');
61
- if (this.options.watch) {
62
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.arrowRight)} Restarting application...`, 'info');
63
- this.startProcess();
167
+ const args = [...nodeArgs, entryPath];
168
+ this.serverProcess = (0, child_process_1.spawn)('node', args, {
169
+ stdio: this.options.verbose ? ['ignore', 'pipe', 'pipe'] : ['ignore', 'ignore', 'pipe'],
170
+ env,
171
+ detached: false,
172
+ cwd: process.cwd()
173
+ });
174
+ if (this.serverProcess.pid) {
175
+ await this.writePidFile(this.serverProcess.pid);
176
+ }
177
+ // Handle server output
178
+ if (this.options.verbose && this.serverProcess.stdout) {
179
+ this.serverProcess.stdout.on('data', (data) => {
180
+ const output = data.toString().trim();
181
+ if (output) {
182
+ logger_manager_js_1.loggerManager.printLine(`[SERVER] ${output}`, 'info');
183
+ }
184
+ });
185
+ }
186
+ // Handle server errors
187
+ if (this.serverProcess.stderr) {
188
+ this.serverProcess.stderr.on('data', (data) => {
189
+ const error = data.toString().trim();
190
+ if (error && !this.options.quiet) {
191
+ logger_manager_js_1.loggerManager.printLine(`[SERVER] ${error}`, 'error');
192
+ }
193
+ });
194
+ }
195
+ // Handle process events
196
+ this.serverProcess.on('error', (error) => {
197
+ this.isStarting = false;
198
+ if (!this.isShuttingDown) {
199
+ logger_manager_js_1.loggerManager.printLine(`Server process error: ${error.message}`, 'error');
200
+ this.handleServerExit(1);
201
+ }
202
+ });
203
+ this.serverProcess.on('exit', (code, signal) => {
204
+ this.isStarting = false;
205
+ if (!this.isShuttingDown) {
206
+ this.handleServerExit(code || 0, signal || undefined);
64
207
  }
208
+ });
209
+ // Wait for server to start
210
+ await this.waitForServerStart();
211
+ this.isStarting = false;
212
+ const duration = Math.round(perf_hooks_1.performance.now() - this.startTime);
213
+ if (!this.options.quiet) {
214
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.tick)} Server started successfully in ${duration}ms`, 'info');
215
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Running on ${chalk_1.default.cyan(`http://${this.options.host}:${this.options.port}`)}`, 'info');
216
+ }
217
+ }
218
+ catch (error) {
219
+ this.isStarting = false;
220
+ throw error;
221
+ }
222
+ }
223
+ async waitForServerStart() {
224
+ return new Promise((resolve, reject) => {
225
+ const timeout = setTimeout(() => {
226
+ reject(new Error(`Server startup timeout after ${this.options.timeout}ms`));
227
+ }, this.options.timeout);
228
+ if (this.serverProcess) {
229
+ // If process exits immediately, it's likely an error
230
+ const exitHandler = (code) => {
231
+ clearTimeout(timeout);
232
+ if (code !== 0) {
233
+ reject(new Error(`Server exited with code ${code}`));
234
+ }
235
+ else {
236
+ resolve();
237
+ }
238
+ };
239
+ this.serverProcess.once('exit', exitHandler);
240
+ // Give some time for the server to start
241
+ setTimeout(() => {
242
+ if (this.serverProcess && !this.serverProcess.killed) {
243
+ this.serverProcess.removeListener('exit', exitHandler);
244
+ clearTimeout(timeout);
245
+ resolve();
246
+ }
247
+ }, 1000);
248
+ }
249
+ else {
250
+ clearTimeout(timeout);
251
+ reject(new Error('Server process not created'));
65
252
  }
66
253
  });
67
254
  }
68
- async start() {
69
- if (!fs_1.default.existsSync(this.options.entry)) {
70
- throw new Error(`Entry file not found: ${this.options.entry}`);
255
+ handleServerExit(code, signal) {
256
+ this.removePidFile();
257
+ if (this.isShuttingDown) {
258
+ return;
259
+ }
260
+ const exitMessage = signal
261
+ ? `Server process killed with signal ${signal}`
262
+ : `Server process exited with code ${code}`;
263
+ if (code !== 0 && !this.options.quiet) {
264
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} ${exitMessage}`, 'error');
265
+ }
266
+ // Handle restart logic
267
+ if (this.options.restart && code !== 0 && this.restartCount < this.options.maxRestarts) {
268
+ this.scheduleRestart();
71
269
  }
72
- await this.startProcess();
270
+ else if (this.restartCount >= this.options.maxRestarts) {
271
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Maximum restart attempts reached (${this.options.maxRestarts})`, 'error');
272
+ process.exit(1);
273
+ }
274
+ else if (code !== 0) {
275
+ process.exit(code);
276
+ }
277
+ }
278
+ scheduleRestart() {
279
+ this.restartCount++;
280
+ const delay = this.options.restartDelay * Math.min(this.restartCount, 5); // Exponential backoff
73
281
  if (!this.options.quiet) {
74
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.tick)} Application is running.`, 'info');
282
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.warning)} Restarting server in ${delay}ms... (${this.restartCount}/${this.options.maxRestarts})`, 'info');
75
283
  }
284
+ this.restartTimer = setTimeout(() => {
285
+ this.restartTimer = null;
286
+ this.startServer().catch(error => {
287
+ logger_manager_js_1.loggerManager.printLine(`Restart failed: ${error.message}`, 'error');
288
+ this.handleServerExit(1);
289
+ });
290
+ }, delay);
76
291
  }
77
- async stop() {
78
- this.isStopping = true;
79
- const proc = this.process;
80
- if (!proc) {
292
+ setupWatcher() {
293
+ if (!this.options.watch)
294
+ return;
295
+ const watchPatterns = [
296
+ 'dist/**/*.js',
297
+ 'package.json',
298
+ 'dist/package.json'
299
+ ];
300
+ this.watcher = (0, chokidar_1.watch)(watchPatterns, {
301
+ ignoreInitial: true,
302
+ followSymlinks: false,
303
+ usePolling: false,
304
+ atomic: 500,
305
+ ignored: [
306
+ '**/node_modules/**',
307
+ '**/.git/**',
308
+ '**/*.log',
309
+ '**/*.map'
310
+ ]
311
+ });
312
+ this.watcher.on('change', (filePath) => {
313
+ if (this.options.verbose) {
314
+ logger_manager_js_1.loggerManager.printLine(`File changed: ${path_1.default.relative(process.cwd(), filePath)}`, 'info');
315
+ }
316
+ this.restart();
317
+ });
318
+ this.watcher.on('error', (error) => {
319
+ logger_manager_js_1.loggerManager.printLine(`Watcher error: ${error.message}`, 'error');
320
+ });
321
+ if (this.options.verbose) {
322
+ logger_manager_js_1.loggerManager.printLine(`Watching: ${watchPatterns.join(', ')}`, 'info');
323
+ }
324
+ }
325
+ async restart() {
326
+ if (this.isShuttingDown)
81
327
  return;
328
+ if (!this.options.quiet) {
329
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.warning)} Restarting server...`, 'info');
82
330
  }
83
- this.process = null;
331
+ await this.stopServer();
332
+ // Small delay before restart
333
+ setTimeout(() => {
334
+ this.startServer().catch(error => {
335
+ logger_manager_js_1.loggerManager.printLine(`Restart failed: ${error.message}`, 'error');
336
+ process.exit(1);
337
+ });
338
+ }, 100);
339
+ }
340
+ async stopServer() {
341
+ if (!this.serverProcess)
342
+ return;
84
343
  return new Promise((resolve) => {
85
- const appName = this.options.name || path_1.default.basename(this.options.entry);
86
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.warning)} Stopping application ${appName}...`, 'info');
87
- proc.on('exit', () => {
88
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.square)} Application stopped.`, 'info');
344
+ if (!this.serverProcess) {
89
345
  resolve();
90
- });
91
- proc.on('error', () => {
92
- // Handle errors during shutdown, e.g., if the process is already gone
93
- logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.square)} Application stopped.`, 'info');
346
+ return;
347
+ }
348
+ const proc = this.serverProcess;
349
+ this.serverProcess = null;
350
+ if (this.restartTimer) {
351
+ clearTimeout(this.restartTimer);
352
+ this.restartTimer = null;
353
+ }
354
+ const cleanup = () => {
355
+ this.removePidFile();
94
356
  resolve();
95
- });
357
+ };
358
+ proc.on('exit', cleanup);
359
+ proc.on('error', cleanup);
96
360
  try {
97
361
  if (proc.pid) {
98
- const pid = proc.pid;
99
- // Kill the entire process group
100
- process.kill(-pid, 'SIGTERM');
101
- // Set a timeout to force kill if it doesn't terminate gracefully
362
+ // Graceful shutdown
363
+ proc.kill('SIGTERM');
364
+ // Force kill after timeout
102
365
  setTimeout(() => {
103
- if (!proc.killed) {
366
+ if (proc.pid && !proc.killed) {
104
367
  try {
105
- process.kill(-pid, 'SIGKILL');
368
+ proc.kill('SIGKILL');
106
369
  }
107
370
  catch (e) {
108
- // Ignore errors if the process is already gone
371
+ // Ignore
109
372
  }
110
373
  }
111
- }, 5000).unref(); // .unref() allows the main process to exit if this is the only thing running
374
+ }, this.options.gracefulTimeout);
112
375
  }
113
376
  }
114
- catch (e) {
115
- // This can happen if the process is already dead
116
- resolve();
377
+ catch (error) {
378
+ cleanup();
117
379
  }
118
380
  });
119
381
  }
382
+ async start() {
383
+ try {
384
+ await this.startServer();
385
+ this.setupWatcher();
386
+ if (this.options.watch && !this.options.quiet) {
387
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.blue(figures_1.default.info)} Watching for changes...`, 'info');
388
+ }
389
+ }
390
+ catch (error) {
391
+ logger_manager_js_1.loggerManager.printLine(`Failed to start server: ${error.message}`, 'error');
392
+ process.exit(1);
393
+ }
394
+ }
395
+ async stop() {
396
+ this.isShuttingDown = true;
397
+ if (!this.options.quiet) {
398
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.yellow(figures_1.default.warning)} Stopping server...`, 'info');
399
+ }
400
+ if (this.watcher) {
401
+ await this.watcher.close();
402
+ this.watcher = null;
403
+ }
404
+ await this.stopServer();
405
+ if (!this.options.quiet) {
406
+ logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.tick)} Server stopped successfully`, 'info');
407
+ }
408
+ }
120
409
  }
121
410
  exports.StartManager = StartManager;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neex",
3
- "version": "0.6.46",
3
+ "version": "0.6.50",
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",