opticore-webapp 1.0.69 → 1.0.71

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/index.js CHANGED
@@ -1,178 +1,82 @@
1
1
  // src/core/webServer.core.ts
2
- import * as path2 from "path";
3
- import process4 from "process";
2
+ import * as path3 from "path";
3
+ import process6 from "process";
4
4
  import corsOrigin from "cors";
5
- import express from "express";
6
- import chokidar from "chokidar";
7
- import {
8
- CEvent,
9
- CEventNameError as eventName2,
10
- ServerListenEventError as ServerListenEventError2
11
- } from "opticore-catch-exception-error";
12
- import { getEnvironmentValue } from "opticore-env-access";
13
- import { requestCallsEvent } from "opticore-request-call-event";
14
- import { SContainer as SContainer2 } from "opticore-dependency-inject";
5
+ import { CEventNameError as eventName2, ServerListenEventError as ServerListenEventError2 } from "opticore-catch-exception-error";
6
+ import { express as express2 } from "opticore-express";
7
+ import { getEnvironmentValue as getEnvironmentValue2 } from "opticore-env-access";
15
8
  import { HttpStatusCode as HttpStatusCode3 } from "opticore-http-response";
16
- import { TranslationLoader as TranslationLoader2 } from "opticore-translator";
9
+ import { TranslationLoader as TranslationLoader3 } from "opticore-translator";
10
+ import { requestCallsEvent } from "opticore-request-call-event";
17
11
 
18
12
  // src/core/handlers/eventProcess.handler.ts
19
- import process from "process";
13
+ import process2 from "process";
20
14
  import EventEmitter from "events";
21
- import {
22
- ServerListenEventError,
23
- CEventNameError as eventName,
24
- CEvent as event
25
- } from "opticore-catch-exception-error";
26
-
27
- // src/utils/isTransformError.utils.ts
28
- var isTransformErrorUtils = (error) => {
29
- return error?.name === "TransformError" || error?.message?.includes("Transform failed") || error?.message?.includes("esbuild");
30
- };
31
-
32
- // src/core/handlers/eventProcess.handler.ts
33
- function eventProcessHandler(localeLanguage, expressApp) {
15
+ import { express } from "opticore-express";
16
+ import { ServerListenEventError, CEventNameError as eventName, CEvent as event } from "opticore-catch-exception-error";
17
+ function eventProcessHandler(localeLanguage) {
34
18
  const errorEmitter = new EventEmitter();
19
+ const app = express();
35
20
  const serverListenEvent = new ServerListenEventError(localeLanguage);
36
- console.log("[Server] Setting up error event handlers...");
37
21
  errorEmitter.on(eventName.error, (error) => {
38
- console.error("[Server] EventEmitter error caught:", error.message);
39
- if (isTransformErrorUtils(error)) {
40
- handleTransformError(error);
41
- }
42
22
  serverListenEvent.listenerError(error);
43
23
  });
44
- function handleTransformError(error) {
45
- console.error("[Server] ============================================");
46
- console.error("[Server] TRANSFORM ERROR DETECTED");
47
- console.error("[Server] ============================================");
48
- console.error("[Server] Message:", error.message);
49
- console.error("[Server] Stack:", error.stack);
50
- if (error.errors && Array.isArray(error.errors)) {
51
- error.errors.forEach((err, index) => {
52
- console.error(`[Server] Error ${index + 1}:`, err.text);
53
- if (err.location) {
54
- console.error(`[Server] File: ${err.location.file}`);
55
- console.error(`[Server] Line: ${err.location.line}, Column: ${err.location.column}`);
56
- }
57
- });
58
- }
59
- const fileMatch = error.message.match(/([^:]+\.ts):(\d+):(\d+):/);
60
- if (fileMatch) {
61
- console.error("[Server] Problem file:", fileMatch[1]);
62
- console.error("[Server] Line:", fileMatch[2], "Column:", fileMatch[3]);
63
- }
64
- const errorDetailMatch = error.message.match(/ERROR: (.+)$/m);
65
- if (errorDetailMatch) {
66
- console.error("[Server] Error detail:", errorDetailMatch[1]);
67
- }
68
- console.error("[Server] ============================================");
69
- if (process.send) {
70
- process.send({
71
- type: "TRANSFORM_ERROR",
72
- error: error.message,
73
- stack: error.stack,
74
- errorName: error.name,
75
- details: error.errors,
76
- timestamp: Date.now()
77
- });
78
- }
79
- }
80
- process.on(event.uncaughtException, (error) => {
81
- console.error("[Server] UNCAUGHT EXCEPTION:", error.message);
82
- console.error("[Server] Stack:", error.stack);
83
- if (isTransformErrorUtils(error)) {
84
- errorEmitter.emit("transformError", error);
85
- }
86
- serverListenEvent.uncaughtException(error);
87
- if (process.send) {
88
- const messageType = isTransformErrorUtils(error) ? "TRANSFORM_ERROR" : "HOT_RELOAD_ERROR";
89
- process.send({
90
- type: messageType,
91
- error: error.message,
92
- stack: error.stack,
93
- errorName: error.name,
94
- timestamp: Date.now()
95
- });
96
- }
97
- });
98
- process.on(event.unhandledRejection, (reason, promise) => {
99
- console.error("[Server] UNHANDLED REJECTION:", reason);
100
- if (isTransformErrorUtils(reason)) {
101
- errorEmitter.emit("transformError", reason);
102
- }
103
- serverListenEvent.unhandledRejection(reason, promise);
104
- if (process.send) {
105
- const messageType = isTransformErrorUtils(reason) ? "TRANSFORM_ERROR" : "HOT_RELOAD_ERROR";
106
- process.send({
107
- type: messageType,
108
- error: reason?.message || String(reason),
109
- errorName: reason?.name,
110
- timestamp: Date.now()
111
- });
112
- }
113
- });
114
- process.on(event.beforeExit, (code) => {
24
+ process2.on(event.beforeExit, (code) => {
115
25
  setTimeout(() => {
116
26
  serverListenEvent.processBeforeExit(code);
117
27
  }, 100);
118
28
  });
119
- process.on(event.disconnect, () => {
29
+ process2.on(event.disconnect, () => {
120
30
  serverListenEvent.processDisconnected();
121
31
  });
122
- process.on(event.exit, (code) => {
32
+ process2.on(event.exit, (code) => {
123
33
  serverListenEvent.exited(code);
124
34
  });
125
- process.on(event.rejectionHandled, (promise) => {
35
+ process2.on(event.rejectionHandled, (promise) => {
126
36
  serverListenEvent.promiseRejectionHandled(promise);
127
37
  });
128
- process.on(event.uncaughtExceptionMonitor, (error) => {
129
- console.error("[Server] UNCAUGHT EXCEPTION MONITOR:", error.message);
130
- if (isTransformErrorUtils(error)) {
131
- errorEmitter.emit("transformError", error);
132
- }
38
+ process2.on(event.uncaughtException, (error) => {
39
+ serverListenEvent.uncaughtException(error);
40
+ });
41
+ process2.on(event.uncaughtExceptionMonitor, (error) => {
133
42
  serverListenEvent.uncaughtExceptionMonitor(error);
134
43
  });
135
- process.on(event.warning, (warning) => {
44
+ process2.on(event.unhandledRejection, (reason, promise) => {
45
+ serverListenEvent.unhandledRejection(reason, promise);
46
+ });
47
+ process2.on(event.warning, (warning) => {
136
48
  serverListenEvent.warning(warning);
137
49
  });
138
- process.on(event.message, (message) => {
139
- if (message.type !== "HOT_RELOAD_REQUEST" && message.type !== "SERVER_READY") {
140
- serverListenEvent.message(message);
141
- }
50
+ process2.on(event.message, (message) => {
51
+ serverListenEvent.message(message);
142
52
  });
143
- process.on(event.multipleResolves, (type, promise, reason) => {
53
+ process2.on(event.multipleResolves, (type, promise, reason) => {
144
54
  serverListenEvent.multipleResolves(type, promise, reason);
145
55
  });
146
- process.on(event.sigint, () => {
56
+ process2.on(event.sigint, () => {
147
57
  serverListenEvent.processInterrupted();
148
58
  });
149
- process.on(event.sigterm, (signal) => {
59
+ process2.on(event.sigterm, (signal) => {
150
60
  serverListenEvent.sigtermSignalReceived(signal);
151
61
  });
152
- expressApp.use((err, req, res, next) => {
153
- console.error("[Server] Express error middleware triggered:", err.message);
154
- console.error("[Server] Request URL:", req.url);
155
- console.error("[Server] Request method:", req.method);
156
- errorEmitter.emit(eventName.error, err);
62
+ app.use((err, req, res, next) => {
157
63
  serverListenEvent.expressErrorHandlingMiddleware(errorEmitter, err, req, res, next);
158
64
  });
159
- console.log("[Server] Error event handlers configured successfully");
160
- return errorEmitter;
161
65
  }
162
66
 
163
67
  // src/application/service/core.service.ts
164
- import process3 from "process";
68
+ import process4 from "process";
165
69
  import chalk from "chalk";
166
70
  import * as path from "path";
167
71
  import * as fs from "fs";
168
72
  import colors from "ansi-colors";
169
73
  import { HttpStatusCode, HttpStatusCode as status } from "opticore-http-response";
170
74
  import { TranslationLoader } from "opticore-translator";
171
- import { getEnvironnementValue } from "opticore-env-access";
75
+ import { getEnvironmentValue } from "opticore-env-access";
172
76
 
173
77
  // src/utils/envPath.utils.ts
174
- import process2 from "process";
175
- var envPath = process2.cwd() + "/config/env/.env";
78
+ import process3 from "process";
79
+ var envPath = process3.cwd() + "/config/env/.env";
176
80
 
177
81
  // src/application/service/loaderTranslationFile.service.ts
178
82
  import { translationLoaderConfig } from "opticore-loader-translation";
@@ -209,18 +113,20 @@ var dependenciesContainerProvider = (localLang) => {
209
113
  };
210
114
 
211
115
  // src/application/service/core.service.ts
212
- import { LoggerCore as LoggerCore2 } from "opticore-logger";
116
+ import { SContainer as SContainer2 } from "opticore-dependency-inject";
213
117
 
214
- // src/application/service/logger.service.ts
215
- var SLogger = (localLang) => {
216
- return {
217
- get serverLog() {
218
- return dependenciesContainerProvider(localLang).resolve("OpticoreLogger");
219
- },
220
- get logger() {
221
- return dependenciesContainerProvider(localLang).resolve("LoggerCore");
222
- }
223
- };
118
+ // src/utils/parsingDateTime.utils.ts
119
+ var parsingDateTimeUtils = (localFormatTime) => {
120
+ const now = /* @__PURE__ */ new Date();
121
+ const formatter = new Intl.DateTimeFormat(localFormatTime, {
122
+ day: "2-digit",
123
+ month: "2-digit",
124
+ year: "numeric",
125
+ hour: "2-digit",
126
+ minute: "2-digit",
127
+ hour12: false
128
+ });
129
+ return formatter.format(now);
224
130
  };
225
131
 
226
132
  // src/application/service/core.service.ts
@@ -229,10 +135,12 @@ var CoreService = class {
229
135
  environmentPath;
230
136
  serverLog;
231
137
  logger;
138
+ container;
232
139
  constructor(localLang, environmentPath) {
140
+ this.loadTranslationFiles();
233
141
  this.environmentPath = environmentPath;
234
142
  this.localLanguage = localLang;
235
- loaderTranslationFile(this.localLanguage);
143
+ this.container = new SContainer2(localLang);
236
144
  this.serverLog = dependenciesContainerProvider(localLang).resolve("OpticoreLogger");
237
145
  this.logger = dependenciesContainerProvider(localLang).resolve("LoggerCore");
238
146
  }
@@ -246,11 +154,46 @@ var CoreService = class {
246
154
  formatMemoryUsage(data) {
247
155
  return `${Math.round(data / 1024 / 1024 * 100) / 100} MB`;
248
156
  }
157
+ loadTranslationFiles() {
158
+ loaderTranslationFile(this.localLanguage);
159
+ }
160
+ /**
161
+ *
162
+ * @param filePath
163
+ * @private
164
+ *
165
+ * Return void
166
+ */
167
+ getEnvFileLoading(filePath) {
168
+ this.loadTranslationFiles();
169
+ try {
170
+ const fullPath = path.resolve(process4.cwd(), filePath);
171
+ if (fs.existsSync(fullPath)) {
172
+ const env = fs.readFileSync(fullPath, "utf-8");
173
+ const lines = env.split("\n");
174
+ lines.forEach((line) => {
175
+ const match = line.match(/^([^#=]+)=([^#]+)$/);
176
+ if (match) {
177
+ const key = match[1].trim();
178
+ process4.env[key] = match[2].trim();
179
+ }
180
+ });
181
+ }
182
+ } catch (err) {
183
+ this.logger.error({
184
+ message: err.message,
185
+ title: TranslationLoader.t("EnvFileLoading", this.localLanguage),
186
+ errorType: err.code,
187
+ stackTrace: err.stack,
188
+ httpCodeValue: status.INTERNAL_SERVER_ERROR
189
+ });
190
+ }
191
+ }
249
192
  /**
250
193
  * Returns an Object containing a node version, openssl, and v0
251
194
  */
252
195
  getVersions() {
253
- const { node, openssl, v8 } = process3.versions;
196
+ const { node, openssl, v8 } = process4.versions;
254
197
  const data = {
255
198
  "node version": node,
256
199
  "openssl": openssl,
@@ -270,15 +213,15 @@ var CoreService = class {
270
213
  * and Memory usage system
271
214
  */
272
215
  getUsageMemory() {
273
- loaderTranslationFile(this.localLanguage);
274
- const memoryData = process3.memoryUsage();
216
+ this.loadTranslationFiles();
217
+ const memoryData = process4.memoryUsage();
275
218
  const data = {
276
219
  [TranslationLoader.t("totalMemoryAllocated", this.localLanguage)]: this.formatMemoryUsage(memoryData.rss),
277
220
  [TranslationLoader.t("sizeAllocatedHeap", this.localLanguage)]: this.formatMemoryUsage(memoryData.heapTotal),
278
221
  [TranslationLoader.t("memoryUsedExecution", this.localLanguage)]: this.formatMemoryUsage(memoryData.heapUsed),
279
222
  [TranslationLoader.t("externalMemory", this.localLanguage)]: this.formatMemoryUsage(memoryData.external),
280
- [TranslationLoader.t("memoryUsageUser", this.localLanguage)]: this.formatMemoryUsage(process3.cpuUsage().user),
281
- [TranslationLoader.t("memoryUsageSystem", this.localLanguage)]: this.formatMemoryUsage(process3.cpuUsage().system)
223
+ [TranslationLoader.t("memoryUsageUser", this.localLanguage)]: this.formatMemoryUsage(process4.cpuUsage().user),
224
+ [TranslationLoader.t("memoryUsageSystem", this.localLanguage)]: this.formatMemoryUsage(process4.cpuUsage().system)
282
225
  };
283
226
  return {
284
227
  "rss": data[TranslationLoader.t("totalMemoryAllocated", this.localLanguage)],
@@ -287,52 +230,21 @@ var CoreService = class {
287
230
  "external": data[TranslationLoader.t("externalMemory", this.localLanguage)],
288
231
  "user": data[TranslationLoader.t("memoryUsageUser", this.localLanguage)],
289
232
  "system": data[TranslationLoader.t("memoryUsageSystem", this.localLanguage)],
290
- "pid": process3.pid
233
+ "pid": process4.pid
291
234
  };
292
235
  }
293
236
  /**
294
237
  * Return an Object containing a project path and running server time
295
238
  */
296
239
  getProjectInfo() {
297
- const startTime = process3.hrtime();
298
- const endTime = process3.hrtime(startTime);
240
+ const startTime = process4.hrtime();
241
+ const endTime = process4.hrtime(startTime);
299
242
  const executionTime = (endTime[0] * 1e9 + endTime[1]) / 1e6;
300
243
  return {
301
- "projectPath": path.join(process3.cwd()),
244
+ "projectPath": path.join(process4.cwd()),
302
245
  "startingTime": `${executionTime.toFixed(5)} ms`
303
246
  };
304
247
  }
305
- /**
306
- *
307
- * @param filePath
308
- * @private
309
- *
310
- * Return void
311
- */
312
- getEnvFileLoading(filePath) {
313
- try {
314
- const fullPath = path.resolve(process3.cwd(), filePath);
315
- if (fs.existsSync(fullPath)) {
316
- const env = fs.readFileSync(fullPath, "utf-8");
317
- const lines = env.split("\n");
318
- lines.forEach((line) => {
319
- const match = line.match(/^([^#=]+)=([^#]+)$/);
320
- if (match) {
321
- const key = match[1].trim();
322
- process3.env[key] = match[2].trim();
323
- }
324
- });
325
- }
326
- } catch (err) {
327
- this.logger.error({
328
- message: err.message,
329
- title: TranslationLoader.t("EnvFileLoading", this.localLanguage),
330
- errorType: err.code,
331
- stackTrace: err.stack,
332
- httpCodeValue: status.INTERNAL_SERVER_ERROR
333
- });
334
- }
335
- }
336
248
  /**
337
249
  *
338
250
  * @param development
@@ -341,14 +253,15 @@ var CoreService = class {
341
253
  * Return string
342
254
  */
343
255
  getServerRunningMode(development, production) {
256
+ this.loadTranslationFiles();
344
257
  try {
345
- loaderTranslationFile(this.localLanguage);
346
258
  this.getEnvFileLoading(".env");
347
- const env = getEnvironnementValue(path.join(envPath));
348
- const isDevelopment = env.devEnv === development && env.prodEnv === "";
259
+ const env = getEnvironmentValue(path.join(envPath));
260
+ const isDevelopment = env.devEnv === development;
261
+ const isProd = env.prodEnv === production;
349
262
  if (isDevelopment) {
350
263
  return `${TranslationLoader.t("serverRunning", this.localLanguage)} ${colors.bgBlue(`${colors.bold(`${development}`)}`)} mode`;
351
- } else if (!isDevelopment) {
264
+ } else if (isProd) {
352
265
  return `${TranslationLoader.t("serverRunning", this.localLanguage)} ${colors.bgBlue(`${colors.bold(`${production}`)}`)} mode`;
353
266
  } else {
354
267
  return `${TranslationLoader.t("serverRunning", this.localLanguage)} ${colors.bgBlue(`${colors.bold(`${development}`)}`)} mode`;
@@ -364,10 +277,10 @@ var CoreService = class {
364
277
  }
365
278
  }
366
279
  infoServer(host, port) {
280
+ this.loadTranslationFiles();
367
281
  try {
368
- loaderTranslationFile(this.localLanguage);
369
- const getEnvironment = getEnvironnementValue(this.environmentPath);
370
- const msg5 = getEnvironment.protocolTransfert === "" ? colors.underline(`http://${host}:${port}`) : colors.underline(`${getEnvironment.protocolTransfert}://${host}:${port}`);
282
+ const getEnvironment = getEnvironmentValue(this.environmentPath);
283
+ const msg5 = getEnvironment.protocolTransfer === "" ? colors.underline(`http://${host}:${port}`) : colors.underline(`${getEnvironment.protocolTransfer}://${host}:${port}`);
371
284
  const messages = [
372
285
  TranslationLoader.t("webServerListening", this.localLanguage),
373
286
  TranslationLoader.t("webServerUsingNodeVersion", this.localLanguage, { nodeVersion: this.getVersions().nodeVersion }),
@@ -377,7 +290,7 @@ var CoreService = class {
377
290
  const maxLength = Math.max(...messages.map((m) => {
378
291
  return m.replace(/\u001b\[[0-9]{1,2}m/g, "").length;
379
292
  })) + 4;
380
- console.log(chalk.blackBright(`${TranslationLoader.t("tailingServerLog", this.localLanguage)} (${path.join(path.basename(process3.cwd()), "logs", "app.log")})`));
293
+ console.log(chalk.blackBright(`${TranslationLoader.t("tailingServerLog", this.localLanguage)} (${path.join(path.basename(process4.cwd()), "logs", "app.log")})`));
381
294
  const border = chalk.bgGreen.white(" ".repeat(maxLength));
382
295
  console.log(border);
383
296
  messages.forEach((msg) => {
@@ -387,20 +300,33 @@ var CoreService = class {
387
300
  });
388
301
  console.log(border);
389
302
  console.log("\n");
390
- const logCore = new LoggerCore2();
391
- logCore.success({
392
- title: TranslationLoader.t("serverRunningTitle", this.localLanguage),
393
- message: TranslationLoader.t("serverRunningAt", this.localLanguage, { server: msg5 })
394
- });
395
- SLogger(this.localLanguage).serverLog.serverLog({
396
- timestamp: (/* @__PURE__ */ new Date()).toString(),
303
+ this.serverLog.serverLog({
304
+ timestamp: parsingDateTimeUtils(getEnvironment.localFormatTime),
397
305
  level: "SERVER",
398
306
  title: TranslationLoader.t("serverRunningTitle", this.localLanguage),
399
307
  typeName: TranslationLoader.t("opticoreServerTypeName", this.localLanguage),
400
308
  message: TranslationLoader.t("serverRunningAt", this.localLanguage, { server: msg5 })
401
309
  });
310
+ const dependenciesList = this.container.listDependencies();
311
+ if (dependenciesList.length > 0) {
312
+ this.serverLog.opticoreLog({
313
+ level: "OPTICORE",
314
+ message: TranslationLoader.t("DEPENDENCIES_STORED", this.localLanguage, { dependenciesList: dependenciesList.join(", ") }),
315
+ timestamp: parsingDateTimeUtils(getEnvironment.localFormatTime),
316
+ title: TranslationLoader.t("DEPENDENCIES_CONTAINER", this.localLanguage),
317
+ typeName: "DEPENDENCIES_CONTAINER"
318
+ });
319
+ } else {
320
+ this.serverLog.opticoreLog({
321
+ level: "OPTICORE",
322
+ message: TranslationLoader.t("NO_DEPENDENCIES_STORED", this.localLanguage),
323
+ timestamp: parsingDateTimeUtils(getEnvironment.localFormatTime),
324
+ title: TranslationLoader.t("DEPENDENCIES_CONTAINER", this.localLanguage),
325
+ typeName: "DEPENDENCIES_CONTAINER"
326
+ });
327
+ }
402
328
  } catch (err) {
403
- SLogger(this.localLanguage).logger.error({
329
+ this.logger.error({
404
330
  message: err.message,
405
331
  title: TranslationLoader.t("server", this.localLanguage),
406
332
  errorType: err.code,
@@ -419,6 +345,20 @@ import {
419
345
  CCodeError,
420
346
  CErrorName
421
347
  } from "opticore-catch-exception-error";
348
+
349
+ // src/application/service/logger.service.ts
350
+ var SLogger = (localLang) => {
351
+ return {
352
+ get serverLog() {
353
+ return dependenciesContainerProvider(localLang).resolve("OpticoreLogger");
354
+ },
355
+ get logger() {
356
+ return dependenciesContainerProvider(localLang).resolve("LoggerCore");
357
+ }
358
+ };
359
+ };
360
+
361
+ // src/application/service/serverStartError.service.ts
422
362
  import { HttpStatusCode as HttpStatusCode2 } from "opticore-http-response";
423
363
  var SServerStartError = (err, environmentPath) => {
424
364
  if (err.name) {
@@ -450,391 +390,165 @@ var SServerStartError = (err, environmentPath) => {
450
390
  }
451
391
  };
452
392
 
393
+ // src/core/webServer.core.ts
394
+ import { SContainer as SContainer3 } from "opticore-dependency-inject";
395
+
396
+ // src/application/service/getEnvFileLoading.service.ts
397
+ import path2 from "path";
398
+ import process5 from "process";
399
+ import fs2 from "fs";
400
+ import { TranslationLoader as TranslationLoader2 } from "opticore-translator";
401
+ import { HttpStatusCode as status2 } from "opticore-http-response";
402
+ var getEnvFileLoadingService = (filePath, localLanguage) => {
403
+ loaderTranslationFile(localLanguage);
404
+ const logger = dependenciesContainerProvider(localLanguage).resolve("LoggerCore");
405
+ try {
406
+ const fullPath = path2.resolve(process5.cwd(), filePath);
407
+ if (fs2.existsSync(fullPath)) {
408
+ const env = fs2.readFileSync(fullPath, "utf-8");
409
+ const lines = env.split("\n");
410
+ lines.forEach((line) => {
411
+ const match = line.match(/^([^#=]+)=([^#]+)$/);
412
+ if (match) {
413
+ const key = match[1].trim();
414
+ process5.env[key] = match[2].trim();
415
+ }
416
+ });
417
+ }
418
+ } catch (err) {
419
+ logger.error({
420
+ message: err.message,
421
+ title: TranslationLoader2.t("EnvFileLoading", localLanguage),
422
+ errorType: err.code,
423
+ stackTrace: err.stack,
424
+ httpCodeValue: status2.INTERNAL_SERVER_ERROR
425
+ });
426
+ }
427
+ };
428
+
453
429
  // src/core/webServer.core.ts
454
430
  var WebServerCore = class {
455
431
  serverUtility;
456
- expressApp;
432
+ expressApp = express2();
433
+ container;
457
434
  localLanguage;
458
435
  loggerConfig;
459
- getEnvironment;
436
+ routerExpressApp;
437
+ getEnvironmentValue;
460
438
  environmentPath;
461
439
  serverListenEvent;
462
- // Existing properties
463
- currentRoutes = [];
464
- currentDependencies = [];
465
- server = void 0;
466
- errorEmitter;
467
- serverStatus = "STARTING";
468
- serverStartTime = /* @__PURE__ */ new Date();
469
- // HMR properties
470
- fileWatcher = null;
471
- hmrRestartPending = false;
472
- hmrDebounceTimeout = null;
473
- hmrRestartCount = 0;
474
- lastHmrRestartTime = 0;
475
- /**
476
- * Creates a new WebServerCore instance.
477
- *
478
- * @constructor
479
- * @param {WebServerConstructorInterface} paramsConstructor - Configuration parameters
480
- *
481
- * @param {express.Application} paramsConstructor.app - Express application instance
482
- * @param {LoggerCore} paramsConstructor.loggerConfig - Logger configuration
483
- * @param {string} paramsConstructor.environmentPath - Path to environment file
484
- * @param {string} paramsConstructor.localLanguage - Default language for translations
485
- * @param {CorsOptions} paramsConstructor.corsOriginOptions - CORS configuration
486
- *
487
- * @returns {WebServerCore} New WebServerCore instance
488
- *
489
- * @throws {Error} If environment file cannot be loaded
490
- *
491
- * @example
492
- * ```typescript
493
- * const server = new WebServerCore({
494
- * app: express(),
495
- * loggerConfig: new LoggerCore(config),
496
- * environmentPath: ".env",
497
- * localLanguage: "fr",
498
- * corsOriginOptions: { origin: "http://localhost:3000" }
499
- * });
500
- * ```
501
- */
440
+ dependenciesRegistered = false;
502
441
  constructor(paramsConstructor) {
503
- this.getEnvironment = getEnvironmentValue(paramsConstructor.environmentPath);
442
+ this.loadTranslationFiles();
443
+ this.stackTraceErrorHandling();
444
+ this.getEnvironmentValue = getEnvironmentValue2(paramsConstructor.environmentPath);
445
+ this.routerExpressApp = paramsConstructor.app;
504
446
  this.loggerConfig = paramsConstructor.loggerConfig;
505
447
  this.localLanguage = paramsConstructor.localLanguage;
506
448
  this.environmentPath = paramsConstructor.environmentPath;
507
- this.expressApp = express();
508
- this.serverListenEvent = new ServerListenEventError2(paramsConstructor.localLanguage);
509
- this.expressApp.use(express.json());
510
- this.expressApp.use(express.raw());
511
- this.expressApp.use(express.text());
512
- this.expressApp.use(express.urlencoded({ extended: true }));
449
+ this.container = new SContainer3(paramsConstructor.localLanguage);
450
+ this.expressApp.use(express2.json());
451
+ this.expressApp.use(express2.raw());
452
+ this.expressApp.use(express2.text());
453
+ this.expressApp.use(express2.urlencoded({ extended: true }));
513
454
  this.expressApp.use(corsOrigin(paramsConstructor.corsOriginOptions));
455
+ this.serverListenEvent = new ServerListenEventError2(paramsConstructor.localLanguage);
514
456
  this.serverUtility = new CoreService(paramsConstructor.localLanguage, paramsConstructor.environmentPath);
515
- this.serverStatus = "STARTING";
516
- this.serverStartTime = /* @__PURE__ */ new Date();
517
- this.setupProcessEventListeners();
518
457
  }
519
458
  /**
520
- * Starts the HTTP server and initializes all components including HMR if enabled.
521
- *
522
- * @method onStartServer
523
- * @public
524
- *
525
- * @param {TFeatureRoutes[]} routers - Array of feature routes to register
526
- * @param {(env: IEnvVariables) => void} [databaseCallback] - Optional database connection callback
527
- * @param {TDependency[]} [dependenciesProvider] - Optional dependency injection providers
528
459
  *
529
- * @returns {serverWebApp | undefined} HTTP server instance or undefined if startup fails
530
- *
531
- * @throws {ServerListenEventError} If port/host configuration is invalid
532
- * @throws {Error} If server initialization fails
533
- *
534
- * @fires WebServerCore#startHttpServer - When server successfully starts
535
- * @fires WebServerCore#startHMR - When HMR is started (if enabled)
536
- * @fires WebServerCore#serverError - When server fails to start
537
- *
538
- * @example
539
- * ```typescript
540
- * const server = app.onStartServer(
541
- * routes,
542
- * (env) => connectToDatabase(env),
543
- * dependencies
544
- * );
545
- *
546
- * if (server) {
547
- * console.log("Server started successfully");
548
- * }
549
- * ```
460
+ * @param dependencies
550
461
  */
551
- onStartServer(routers, databaseCallback, dependenciesProvider) {
462
+ registerDependencies(dependencies2) {
463
+ this.loadTranslationFiles();
552
464
  try {
553
- loaderTranslationFile(this.localLanguage);
554
- this.currentRoutes = routers;
555
- this.currentDependencies = dependenciesProvider || [];
556
- this.serverStartTime = /* @__PURE__ */ new Date();
557
- this.serverStatus = "STARTING";
558
- if (!this.validateServerParameters()) {
559
- return void 0;
560
- }
561
- const server = this.startHttpServer(databaseCallback);
562
- if (this.getEnvironment.hmrEnabled) {
563
- this.startHMR();
465
+ getEnvFileLoadingService(".env", this.localLanguage);
466
+ if (dependencies2 && dependencies2.length > 0) {
467
+ dependencies2.forEach((dependency) => {
468
+ this.container.register(dependency.key, dependency.factory, dependency.scope);
469
+ });
564
470
  }
565
- return server;
566
- } catch (error) {
567
- this.handleStartupError(error);
471
+ } catch (err) {
472
+ SLogger(this.environmentPath).logger.error({
473
+ message: err.message,
474
+ title: "Register Dependencies",
475
+ errorType: err.code,
476
+ stackTrace: err.stack,
477
+ httpCodeValue: HttpStatusCode3.INTERNAL_SERVER_ERROR
478
+ });
568
479
  }
569
480
  }
570
481
  /**
571
- * Validates server configuration parameters.
572
482
  *
573
- * @method validateServerParameters
574
- * @private
575
- *
576
- * @returns {boolean} True if all parameters are valid, false otherwise
577
- *
578
- * @remarks
579
- * Validates:
580
- * - Port number is valid and positive
581
- * - Host is not empty
582
- * - Local language is specified
583
- * - HMR configuration (if enabled)
483
+ * @param routers
484
+ * @param databaseCallback
485
+ * @param dependenciesProvider
584
486
  */
585
- validateServerParameters() {
586
- loaderTranslationFile(this.localLanguage);
587
- const port = Number(this.getEnvironment.appPort);
588
- if (isNaN(port) || port <= 0) {
487
+ onStartServer(routers, databaseCallback, dependenciesProvider) {
488
+ this.loadTranslationFiles();
489
+ getEnvFileLoadingService(".env", this.localLanguage);
490
+ if (this.getEnvironmentValue.appPort === "" && Number(this.getEnvironmentValue.appPort) === 0) {
491
+ this.serverListenEvent.hostPortUndefined(Number(this.getEnvironmentValue.appPort));
492
+ } else if (this.getEnvironmentValue.appHost === "") {
493
+ this.serverListenEvent.hostUndefined(this.getEnvironmentValue.appHost);
494
+ } else if (Number(this.getEnvironmentValue.appPort) === 0) {
589
495
  this.serverListenEvent.portUndefined();
590
- return false;
591
- }
592
- if (!this.getEnvironment.appHost || this.getEnvironment.appHost.trim() === "") {
593
- this.serverListenEvent.hostUndefined(this.getEnvironment.appHost);
594
- return false;
595
- }
596
- if (!this.localLanguage || this.localLanguage.trim() === "") {
597
- SLogger(this.localLanguage).logger.error({
598
- message: TranslationLoader2.t("noDefaultLocalLang", this.localLanguage),
599
- title: TranslationLoader2.t("noLocalLang", this.localLanguage),
600
- errorType: TranslationLoader2.t("localLangMissing", this.localLanguage),
496
+ } else if (this.localLanguage === "") {
497
+ SLogger(this.environmentPath).logger.error({
498
+ message: TranslationLoader3.t("noDefaultLocalLang", this.localLanguage),
499
+ title: TranslationLoader3.t("noLocalLang", this.localLanguage),
500
+ errorType: TranslationLoader3.t("localLangMissing", this.localLanguage),
601
501
  stackTrace: void 0,
602
502
  httpCodeValue: HttpStatusCode3.NOT_FOUND
603
503
  });
604
- return false;
605
- }
606
- if (this.getEnvironment.hmrEnabled) {
607
- if (!this.getEnvironment.hmrWatchPatterns || this.getEnvironment.hmrWatchPatterns.length === 0) {
608
- SLogger(this.localLanguage).logger.warn({
609
- title: TranslationLoader2.t("HMR_CONFIG_WARNING", this.localLanguage),
610
- message: "HMR enabled but no watch patterns defined"
611
- });
612
- }
613
- }
614
- return true;
615
- }
616
- /**
617
- * Starts the HTTP server and sets up event listeners.
618
- *
619
- * @method startHttpServer
620
- * @private
621
- *
622
- * @param {(env: IEnvVariables) => void} [databaseCallback] - Database connection callback
623
- *
624
- * @returns {serverWebApp | undefined} HTTP server instance or undefined if startup fails
625
- *
626
- * @throws {Error} If server fails to start
627
- */
628
- startHttpServer(databaseCallback) {
629
- try {
630
- loaderTranslationFile(this.localLanguage);
631
- const port = Number(this.getEnvironment.appPort);
632
- const host = this.getEnvironment.appHost;
633
- this.server = this.expressApp.listen(port, host, () => {
634
- try {
635
- this.configureServerComponents(databaseCallback);
636
- this.serverStatus = "READY";
637
- SLogger(this.localLanguage).logger.info({
638
- title: TranslationLoader2.t("SERVER_RUNNING_TITLE", this.localLanguage),
639
- message: TranslationLoader2.t("SERVER_RUNNING_AT", this.localLanguage, {
640
- server: `${host}:${port}`,
641
- hmrEnabled: this.getEnvironment.hmrEnabled ? "with HMR" : "without HMR"
642
- })
643
- });
644
- } catch (err) {
645
- this.handleServerConfigurationError(err);
504
+ } else {
505
+ return this.expressApp.listen(
506
+ Number(this.getEnvironmentValue.appPort),
507
+ () => {
508
+ this.loadTranslationFiles();
509
+ try {
510
+ databaseCallback(this.getEnvironmentValue);
511
+ if (dependenciesProvider) {
512
+ this.registerDependencies(dependenciesProvider);
513
+ }
514
+ this.container.loadServices();
515
+ this.expressApp.use(express2.static(path3.join(process6.cwd(), "public/template")));
516
+ this.registerRoutes(routers);
517
+ } catch (err) {
518
+ SServerStartError(err, this.environmentPath);
519
+ }
646
520
  }
647
- });
648
- this.setupServerEventListeners();
649
- return this.server;
650
- } catch (error) {
651
- this.serverListenEvent.onEventError(
652
- new Error(TranslationLoader2.t(
653
- "HTTP_SERVER_FAILED",
654
- this.localLanguage,
655
- { errorMessage: error.message }
656
- ))
657
521
  );
658
522
  }
659
523
  }
660
524
  /**
661
- * Configures server components (database, dependencies, routes, etc.).
662
- *
663
- * @method configureServerComponents
664
- * @private
665
- *
666
- * @param {(env: IEnvVariables) => void} [databaseCallback] - Database connection callback
667
- *
668
- * @returns {void}
669
525
  *
670
- * @throws {Error} If component configuration fails
526
+ * @param serverWeb
671
527
  */
672
- configureServerComponents(databaseCallback) {
673
- try {
674
- loaderTranslationFile(this.localLanguage);
675
- if (databaseCallback && typeof databaseCallback === "function") {
676
- try {
677
- databaseCallback(this.getEnvironment);
678
- } catch (dbError) {
679
- this.serverListenEvent.listenerError(
680
- new Error(TranslationLoader2.t("DB_CON_FAILED", this.localLanguage, { dbErrorMessage: dbError.message }))
681
- );
682
- }
683
- }
684
- try {
685
- new SContainer2(this.localLanguage, this.currentDependencies);
686
- } catch (depError) {
687
- this.serverListenEvent.listenerError(
688
- new Error(TranslationLoader2.t("DEPENDENCY_INJECTION_FAILED", this.localLanguage, { depErrorMessage: depError.message }))
689
- );
690
- }
691
- this.expressApp.use(express.static(path2.join(process4.cwd(), "public/template")));
692
- this.registerRoutes(this.currentRoutes);
693
- this.setupErrorHandling();
528
+ onListeningOnServerEvent(serverWeb) {
529
+ this.loadTranslationFiles();
530
+ serverWeb.on(eventName2.error, (err) => {
531
+ this.serverListenEvent.onEventError(err);
532
+ }).on(eventName2.close, () => {
533
+ this.serverListenEvent.serverClosing();
534
+ }).on(eventName2.drop, () => {
535
+ this.serverListenEvent.dropNewConnection();
536
+ }).on(eventName2.listening, () => {
694
537
  this.infoWebApp();
695
- } catch (error) {
696
- this.serverListenEvent.listenerError(
697
- new Error(TranslationLoader2.t("SERVER_COMPONENT_CONFIG_FAILED", this.localLanguage, { errorMessage: error.message }))
698
- );
699
- throw error;
700
- }
701
- }
702
- /**
703
- * Handles server configuration errors.
704
- *
705
- * @method handleServerConfigurationError
706
- * @private
707
- *
708
- * @param {any} err - The error that occurred
709
- *
710
- * @returns {void}
711
- */
712
- handleServerConfigurationError(err) {
713
- loaderTranslationFile(this.localLanguage);
714
- this.serverStatus = "ERROR";
715
- this.serverListenEvent.onEventError(err);
716
- try {
717
- SServerStartError(err, this.environmentPath);
718
- } catch (startError) {
719
- this.serverListenEvent.listenerError(startError);
720
- }
721
- }
722
- /**
723
- * Handles general startup errors.
724
- *
725
- * @method handleStartupError
726
- * @private
727
- *
728
- * @param {any} error - The startup error
729
- *
730
- * @returns {void}
731
- */
732
- handleStartupError(error) {
733
- loaderTranslationFile(this.localLanguage);
734
- this.serverStatus = "ERROR";
735
- this.serverListenEvent.listenerError(error);
736
- SLogger(this.localLanguage).logger.error({
737
- title: TranslationLoader2.t("GLOBAL_STARTUP_ERROR", this.localLanguage),
738
- message: error.message,
739
- errorType: error.name || "StartupError",
740
- stackTrace: error.stack,
741
- httpCodeValue: HttpStatusCode3.INTERNAL_SERVER_ERROR
742
- });
743
- }
744
- /**
745
- * Sets up Node.js process event listeners.
746
- *
747
- * @method setupProcessEventListeners
748
- * @private
749
- *
750
- * @returns {void}
751
- *
752
- * @remarks
753
- * Listens for:
754
- * - Process exit events
755
- * - Uncaught exceptions
756
- * - Unhandled rejections
757
- * - System signals (SIGINT, SIGTERM)
758
- */
759
- setupProcessEventListeners() {
760
- loaderTranslationFile(this.localLanguage);
761
- process4.on(CEvent.beforeExit, (code) => {
762
- this.serverListenEvent.processBeforeExit(code);
763
- });
764
- process4.on(CEvent.disconnect, () => {
765
- this.serverListenEvent.processDisconnected();
766
- });
767
- process4.on(CEvent.exit, (code) => {
768
- this.serverListenEvent.exited(code);
769
- });
770
- process4.on(CEvent.message, (message) => {
771
- this.serverListenEvent.message(message);
772
- });
773
- process4.on(CEvent.multipleResolves, (type, promise, reason) => {
774
- this.serverListenEvent.multipleResolves(type, promise, reason);
775
- });
776
- process4.on(CEvent.rejectionHandled, (promise) => {
777
- this.serverListenEvent.promiseRejectionHandled(promise);
778
- });
779
- process4.on(CEvent.uncaughtException, (error) => {
780
- this.serverListenEvent.uncaughtException(error);
781
- });
782
- process4.on(CEvent.uncaughtExceptionMonitor, (error) => {
783
- this.serverListenEvent.uncaughtExceptionMonitor(error);
784
- });
785
- process4.on(CEvent.unhandledRejection, (reason, promise) => {
786
- this.serverListenEvent.unhandledRejection(reason, promise);
787
- });
788
- process4.on(CEvent.warning, (warning) => {
789
- this.serverListenEvent.warning(warning);
790
- });
791
- process4.on(CEvent.sigint, () => {
792
- this.serverListenEvent.processInterrupted();
793
- });
794
- process4.on(CEvent.sigterm, (signal) => {
795
- this.serverListenEvent.sigtermSignalReceived(signal);
796
538
  });
797
539
  }
798
540
  /**
799
- * Sets up HTTP server event listeners.
800
- *
801
- * @method setupServerEventListeners
802
- * @private
803
- *
804
- * @returns {void}
805
541
  *
806
- * @remarks
807
- * Configures listeners for:
808
- * - Server errors
809
- * - Connection closing
810
- * - Connection dropping
811
- * - HTTP requests (for logging)
812
- *
813
- * @listens Server#error - Server error events
814
- * @listens Server#close - Server closing events
815
- * @listens Server#drop - Connection drop events
816
- * @listens Server#request - HTTP request events
542
+ * @param serverWeb
817
543
  */
818
- setupServerEventListeners() {
819
- loaderTranslationFile(this.localLanguage);
820
- if (!this.server) {
821
- return;
822
- }
823
- this.server.on(eventName2.error, (err) => {
824
- this.serverListenEvent.onEventError(err);
825
- });
826
- this.server.on(eventName2.close, () => {
827
- this.serverListenEvent.serverClosing();
828
- });
829
- this.server.on(eventName2.drop, () => {
830
- this.serverListenEvent.dropNewConnection();
831
- });
832
- this.server.on(eventName2.request, (req, res) => {
544
+ onRequestOnServerEvent(serverWeb) {
545
+ this.loadTranslationFiles();
546
+ serverWeb.on(eventName2.request, (req, res) => {
833
547
  requestCallsEvent(
834
548
  req,
835
549
  res,
836
- this.getEnvironment.appHost,
837
- Number(this.getEnvironment.appPort),
550
+ this.getEnvironmentValue.appHost,
551
+ Number(this.getEnvironmentValue.appPort),
838
552
  dateTimeFormattedUtils,
839
553
  this.environmentPath,
840
554
  this.localLanguage
@@ -842,626 +556,436 @@ var WebServerCore = class {
842
556
  });
843
557
  }
844
558
  /**
845
- * Sets up error handling middleware and event emitters.
846
559
  *
847
- * @method setupErrorHandling
848
560
  * @private
849
- *
850
- * @returns {void}
851
561
  */
852
- setupErrorHandling() {
562
+ loadTranslationFiles() {
853
563
  loaderTranslationFile(this.localLanguage);
854
- SLogger(this.localLanguage).logger.info({
855
- title: TranslationLoader2.t("SETTING_UP", this.localLanguage),
856
- message: TranslationLoader2.t("SETTING_UP_ERROR", this.localLanguage)
857
- });
858
- this.errorEmitter = eventProcessHandler(this.localLanguage, this.expressApp);
859
- if (this.errorEmitter) {
860
- this.expressApp.use((err, req, res, next) => {
861
- this.serverListenEvent.expressErrorHandlingMiddleware(
862
- this.errorEmitter,
863
- err,
864
- req,
865
- res,
866
- next
867
- );
868
- });
869
- this.errorEmitter.on("transformError", (error) => {
870
- this.serverListenEvent.listenerError(error);
871
- });
872
- this.errorEmitter.on("error", (error) => {
873
- this.serverListenEvent.listenerError(error);
874
- });
875
- this.errorEmitter.on("hotReload", (data) => {
876
- SLogger(this.localLanguage).logger.info({
877
- title: TranslationLoader2.t("HOT_RELOAD_EVENT", this.localLanguage),
878
- message: `Hot reload triggered for ${data.file}`
879
- });
880
- });
881
- }
882
- SLogger(this.localLanguage).logger.info({
883
- title: TranslationLoader2.t("ERROR_HANDLING", this.localLanguage),
884
- message: TranslationLoader2.t("ERROR_HANDLING_CONFIGURED", this.localLanguage)
885
- });
886
564
  }
887
565
  /**
888
- * Registers routes with the Express application.
889
566
  *
890
- * @method registerRoutes
567
+ * @param allFeatureRoutes
891
568
  * @private
892
- *
893
- * @param {any[]} allFeatureRoutes - Array of feature routes to register
894
- *
895
- * @returns {void}
896
569
  */
897
570
  registerRoutes(allFeatureRoutes) {
898
- allFeatureRoutes.forEach((router) => {
899
- if (router.routes) {
900
- router.routes.forEach((route) => {
901
- this.expressApp.use(route.path, route.handler);
902
- });
903
- }
571
+ allFeatureRoutes.map((router) => {
572
+ router.routes.map((route) => {
573
+ this.expressApp.use(route.path, route.handler);
574
+ });
904
575
  });
905
576
  }
906
577
  /**
907
- * Displays server information.
908
578
  *
909
- * @method infoWebApp
910
579
  * @private
911
- *
912
- * @returns {void}
913
580
  */
914
- infoWebApp() {
915
- loaderTranslationFile(this.localLanguage);
916
- this.serverUtility.infoServer(
917
- this.getEnvironment.appHost,
918
- Number(this.getEnvironment.appPort)
919
- );
581
+ stackTraceErrorHandling() {
582
+ return eventProcessHandler(this.localLanguage);
920
583
  }
921
584
  /**
922
- * Starts the Hot Module Replacement (HMR) file watching system.
923
585
  *
924
- * @method startHMR
925
586
  * @private
926
- *
927
- * @returns {void}
928
- *
929
- * @remarks
930
- * Configures file watcher based on environment variables:
931
- * - HMR_ENABLED: Enable/disable HMR
932
- * - HMR_WATCH_PATTERNS: Files to watch
933
- * - HMR_IGNORE_PATTERNS: Files to ignore
934
- *
935
- * @throws {Error} If HMR configuration is invalid
936
587
  */
937
- startHMR() {
938
- try {
939
- loaderTranslationFile(this.localLanguage);
940
- const hmrConfig = this.getEnvironment;
941
- if (!hmrConfig.hmrEnabled) {
942
- SLogger(this.localLanguage).logger.info({
943
- title: TranslationLoader2.t("HMR_DISABLED", this.localLanguage),
944
- message: "HMR is disabled in configuration"
945
- });
946
- return;
947
- }
948
- if (!hmrConfig.hmrWatchPatterns || hmrConfig.hmrWatchPatterns.length === 0) {
949
- SLogger(this.localLanguage).logger.error({
950
- title: TranslationLoader2.t("HMR_WATCH_PATTERNS_MISSING", this.localLanguage),
951
- message: "No watch patterns defined for HMR",
952
- errorType: "HMR Configuration Error",
953
- httpCodeValue: HttpStatusCode3.BAD_REQUEST
954
- });
955
- return;
956
- }
957
- const watchPatterns = hmrConfig.hmrWatchPatterns;
958
- const ignorePatterns = hmrConfig.hmrIgnorePatterns || [
959
- "node_modules/**",
960
- "dist/**",
961
- "build/**",
962
- "*.log",
963
- ".git/**"
964
- ];
965
- const allPatterns = [
966
- ...watchPatterns,
967
- ...ignorePatterns.map((pattern) => `!${pattern}`)
968
- ];
969
- this.fileWatcher = chokidar.watch(allPatterns, {
970
- ignored: /(^|[/\\])\../,
971
- persistent: true,
972
- ignoreInitial: true,
973
- awaitWriteFinish: {
974
- stabilityThreshold: 300,
975
- pollInterval: 100
976
- },
977
- cwd: process4.cwd(),
978
- depth: 10
979
- });
980
- this.fileWatcher.on("ready", () => this.onHMRWatcherReady(watchPatterns, ignorePatterns)).on("change", (filePath) => this.handleFileChange(filePath)).on("add", (filePath) => this.handleFileChange(filePath, "added")).on("unlink", (filePath) => this.handleFileChange(filePath, "deleted")).on("error", (error) => this.onHMRWatcherError(error));
981
- this.hmrRestartCount = 0;
982
- this.lastHmrRestartTime = Date.now();
983
- SLogger(this.localLanguage).logger.info({
984
- title: TranslationLoader2.t("HMR_STARTED", this.localLanguage),
985
- message: TranslationLoader2.t("HMR_MONITORING_STARTED", this.localLanguage)
986
- });
987
- } catch (error) {
988
- SLogger(this.localLanguage).logger.error({
989
- title: TranslationLoader2.t("HMR_START_FAILED", this.localLanguage),
990
- message: error.message,
991
- errorType: "HMR Error",
992
- stackTrace: error.stack
993
- });
994
- }
588
+ infoWebApp() {
589
+ this.loadTranslationFiles();
590
+ this.serverUtility.infoServer(
591
+ this.getEnvironmentValue.appHost,
592
+ Number(this.getEnvironmentValue.appPort)
593
+ );
995
594
  }
996
- /**
997
- * Handles file change events with debouncing.
998
- *
999
- * @method handleFileChange
1000
- * @private
1001
- *
1002
- * @param {string} filePath - Path of the changed file
1003
- * @param {string} [action="modified"] - Type of file change (modified/added/deleted)
1004
- *
1005
- * @returns {void}
1006
- *
1007
- * @remarks
1008
- * Uses debouncing to prevent multiple rapid reloads.
1009
- * Debounce time configurable via HMR_DEBOUNCE_MS environment variable.
1010
- */
1011
- handleFileChange(filePath, action = "modified") {
1012
- const debounceMs = this.getEnvironment.hmrDebounceMs || 500;
1013
- if (this.hmrDebounceTimeout) {
1014
- clearTimeout(this.hmrDebounceTimeout);
1015
- }
1016
- this.hmrDebounceTimeout = setTimeout(async () => {
1017
- await this.triggerHotReload(filePath, action);
1018
- }, debounceMs);
1019
- SLogger(this.localLanguage).logger.info({
1020
- title: TranslationLoader2.t("FILE_CHANGE_DETECTED", this.localLanguage),
1021
- message: `File ${action}: ${filePath}`
595
+ };
596
+
597
+ // src/hotReload/hotReload.watcher.ts
598
+ import { fork, spawn } from "child_process";
599
+ import { watch as fsWatch } from "fs";
600
+ import { readdir, access } from "fs/promises";
601
+ import { join as join3, resolve as resolve2, extname, relative, basename as basename2 } from "path";
602
+ import { config as dotenvConfig } from "dotenv";
603
+ import chalk2 from "chalk";
604
+ import colors2 from "ansi-colors";
605
+ import gradient from "gradient-string";
606
+ var DEFAULT_WATCH_EXTENSIONS = [".ts", ".js", ".mjs", ".cjs", ".json"];
607
+ var DEFAULT_HOT_RELOAD_EXTENSIONS = [".json"];
608
+ var DEFAULT_IGNORE = [
609
+ "node_modules",
610
+ "dist",
611
+ ".git",
612
+ "package.json",
613
+ "package-lock.json",
614
+ ".idea",
615
+ ".vscode",
616
+ "coverage",
617
+ "logs"
618
+ ];
619
+ var DEFAULT_DEBOUNCE_MS = 300;
620
+ var DEFAULT_MAX_CRASH_RESTARTS = 5;
621
+ var HotReloadWatcher = class {
622
+ cfg;
623
+ child = null;
624
+ watchers = [];
625
+ debounceTimer = null;
626
+ isRestarting = false;
627
+ crashRestartCount = 0;
628
+ started = false;
629
+ restartStart = 0;
630
+ constructor(config) {
631
+ this.cfg = {
632
+ entry: resolve2(config.entry),
633
+ runtime: config.runtime ?? "node",
634
+ runtimeArgs: config.runtimeArgs ?? [],
635
+ rootDir: resolve2(config.rootDir ?? process.cwd()),
636
+ watchDirs: config.watchDirs ?? [],
637
+ watchExtensions: config.watchExtensions ?? DEFAULT_WATCH_EXTENSIONS,
638
+ ignore: [...DEFAULT_IGNORE, ...config.ignore ?? []],
639
+ envFile: config.envFile ?? ".env",
640
+ hotReloadExtensions: config.hotReloadExtensions ?? DEFAULT_HOT_RELOAD_EXTENSIONS,
641
+ debounceMs: config.debounceMs ?? DEFAULT_DEBOUNCE_MS,
642
+ restartOnCrash: config.restartOnCrash ?? true,
643
+ maxCrashRestarts: config.maxCrashRestarts ?? DEFAULT_MAX_CRASH_RESTARTS
644
+ };
645
+ }
646
+ // ─── Public API ──────────────────────────────────────────────────────────
647
+ async start() {
648
+ if (this.started) return;
649
+ this.started = true;
650
+ this.printBanner();
651
+ this.spawnChild();
652
+ await this.setupWatchers();
653
+ this.setupProcessSignals();
654
+ }
655
+ async stop() {
656
+ this.watchers.forEach((w) => {
657
+ w.close();
1022
658
  });
659
+ this.watchers = [];
660
+ await this.killChild(true);
661
+ this.printStopped();
1023
662
  }
1024
- /**
1025
- * Triggers a hot reload operation.
1026
- *
1027
- * @method triggerHotReload
1028
- * @private
1029
- *
1030
- * @param {string} filePath - Path of the changed file
1031
- * @param {string} action - Type of file change
1032
- *
1033
- * @returns {Promise<void>}
1034
- *
1035
- * @remarks
1036
- * - Checks if reload is already in progress
1037
- * - Validates restart limits
1038
- * - Performs appropriate reload actions based on file type
1039
- * - Emits hotReload event
1040
- */
1041
- async triggerHotReload(filePath, action) {
1042
- if (this.hmrRestartPending) {
1043
- return;
1044
- }
1045
- if (!this.canProceedWithHMRRestart()) {
1046
- return;
1047
- }
1048
- this.hmrRestartPending = true;
1049
- this.hmrRestartCount++;
1050
- this.lastHmrRestartTime = Date.now();
663
+ // ─── Child process management ────────────────────────────────────────────
664
+ spawnChild() {
665
+ const { entry, runtime, runtimeArgs } = this.cfg;
1051
666
  try {
1052
- loaderTranslationFile(this.localLanguage);
1053
- SLogger(this.localLanguage).logger.info({
1054
- title: TranslationLoader2.t("HOT_RELOAD_STARTING", this.localLanguage),
1055
- message: TranslationLoader2.t("RELOADING_APPLICATION", this.localLanguage, {
1056
- file: filePath,
1057
- action
1058
- })
667
+ this.child = runtime === "node" ? fork(entry, [], {
668
+ stdio: ["inherit", "inherit", "inherit", "ipc"],
669
+ env: process.env
670
+ }) : spawn(runtime, [...runtimeArgs, entry], {
671
+ stdio: "inherit",
672
+ env: process.env,
673
+ shell: false
1059
674
  });
1060
- if (this.errorEmitter) {
1061
- this.errorEmitter.emit("hotReload", {
1062
- file: filePath,
1063
- action,
1064
- timestamp: /* @__PURE__ */ new Date(),
1065
- restartCount: this.hmrRestartCount
1066
- });
1067
- }
1068
- await this.performHotReloadActions(filePath);
1069
- SLogger(this.localLanguage).logger.success({
1070
- title: TranslationLoader2.t("HOT_RELOAD_COMPLETE", this.localLanguage),
1071
- message: TranslationLoader2.t("APPLICATION_RELOADED", this.localLanguage, {
1072
- file: filePath,
1073
- restartCount: this.hmrRestartCount,
1074
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1075
- })
675
+ this.child.on("exit", (code) => {
676
+ if (this.isRestarting) return;
677
+ if (code !== 0 && code !== null) {
678
+ this.printCrash(code);
679
+ if (this.cfg.restartOnCrash && this.crashRestartCount < this.cfg.maxCrashRestarts) {
680
+ this.crashRestartCount++;
681
+ this.printCrashRetry(this.crashRestartCount, this.cfg.maxCrashRestarts);
682
+ setTimeout(() => {
683
+ this.spawnChild();
684
+ }, 1e3);
685
+ } else if (this.crashRestartCount >= this.cfg.maxCrashRestarts) {
686
+ this.printCrashLimit(this.cfg.maxCrashRestarts);
687
+ process.exit(1);
688
+ }
689
+ }
1076
690
  });
1077
- } catch (error) {
1078
- SLogger(this.localLanguage).logger.error({
1079
- title: TranslationLoader2.t("HOT_RELOAD_FAILED", this.localLanguage),
1080
- message: error.message,
1081
- errorType: "HotReloadError",
1082
- stackTrace: error.stack,
1083
- httpCodeValue: HttpStatusCode3.INTERNAL_SERVER_ERROR
691
+ this.child.on("error", (err) => {
692
+ this.printError(`Failed to start child process: ${err.message}`);
1084
693
  });
1085
- } finally {
1086
- this.hmrRestartPending = false;
1087
- }
694
+ } catch (err) {
695
+ this.printError(`Cannot spawn '${runtime} ${entry}': ${err.message}`);
696
+ process.exit(1);
697
+ }
698
+ }
699
+ async killChild(silent = false) {
700
+ if (!this.child) return;
701
+ const child = this.child;
702
+ this.child = null;
703
+ await new Promise((done) => {
704
+ const forceKill = setTimeout(() => {
705
+ child.kill("SIGKILL");
706
+ done();
707
+ }, 3e3);
708
+ child.once("exit", () => {
709
+ clearTimeout(forceKill);
710
+ done();
711
+ });
712
+ child.kill("SIGTERM");
713
+ });
1088
714
  }
1089
- /**
1090
- * Performs appropriate hot reload actions based on file type.
1091
- *
1092
- * @method performHotReloadActions
1093
- * @private
1094
- *
1095
- * @param {string} filePath - Path of the changed file
1096
- *
1097
- * @returns {Promise<void>}
1098
- *
1099
- * @remarks
1100
- * Different actions for different file types:
1101
- * - .json/.env: Reload translations
1102
- * - routes/controller files: Reload routes
1103
- * - config/.env files: Reload dependencies
1104
- */
1105
- async performHotReloadActions(filePath) {
1106
- if (filePath.includes(".json") || filePath.endsWith(".env")) {
1107
- loaderTranslationFile(this.localLanguage);
1108
- }
1109
- if (filePath.includes("routes") || filePath.includes("controller")) {
1110
- await this.reloadRoutes();
1111
- }
1112
- if (filePath.includes("config") || filePath.includes(".env")) {
1113
- await this.reloadDependencies();
715
+ // ─── File watching ────────────────────────────────────────────────────────
716
+ async setupWatchers() {
717
+ const dirs = [
718
+ this.cfg.rootDir,
719
+ ...this.cfg.watchDirs.map((d) => resolve2(d))
720
+ ];
721
+ for (const dir of dirs) {
722
+ await this.watchDirectoryRecursive(dir);
1114
723
  }
1115
724
  }
1116
- /**
1117
- * Reloads routes dynamically.
1118
- *
1119
- * @method reloadRoutes
1120
- * @private
1121
- *
1122
- * @returns {Promise<void>}
1123
- *
1124
- * @throws {Error} If route reloading fails
1125
- *
1126
- * @remarks
1127
- * This method should be implemented based on your architecture.
1128
- * It should reload route modules from the filesystem.
1129
- */
1130
- async reloadRoutes() {
725
+ async watchDirectoryRecursive(dir) {
726
+ if (this.shouldIgnoreDir(dir)) return;
1131
727
  try {
1132
- SLogger(this.localLanguage).logger.info({
1133
- title: TranslationLoader2.t("ROUTES_RELOADED", this.localLanguage),
1134
- message: "Routes dynamically reloaded"
1135
- });
1136
- } catch (error) {
1137
- throw new Error(`Route reload failed: ${error.message}`);
728
+ await access(dir);
729
+ } catch {
730
+ return;
1138
731
  }
1139
- }
1140
- /**
1141
- * Reloads dependencies dynamically.
1142
- *
1143
- * @method reloadDependencies
1144
- * @private
1145
- *
1146
- * @returns {Promise<void>}
1147
- *
1148
- * @throws {Error} If dependency reloading fails
1149
- */
1150
- async reloadDependencies() {
1151
732
  try {
1152
- if (this.currentDependencies.length > 0) {
1153
- new SContainer2(this.localLanguage, this.currentDependencies);
1154
- SLogger(this.localLanguage).logger.info({
1155
- title: TranslationLoader2.t("DEPENDENCIES_RELOADED", this.localLanguage),
1156
- message: "Dependencies reloaded"
1157
- });
733
+ const watcher = fsWatch(dir, (event2, filename) => {
734
+ if (!filename) return;
735
+ this.onFileChange(join3(dir, filename));
736
+ });
737
+ watcher.on("error", () => {
738
+ });
739
+ this.watchers.push(watcher);
740
+ const entries = await readdir(dir, { withFileTypes: true });
741
+ for (const entry of entries) {
742
+ if (entry.isDirectory()) {
743
+ await this.watchDirectoryRecursive(join3(dir, entry.name));
744
+ }
1158
745
  }
1159
- } catch (error) {
1160
- throw new Error(`Dependency reload failed: ${error.message}`);
746
+ } catch {
1161
747
  }
1162
748
  }
1163
- /**
1164
- * Checks if HMR restart can proceed based on configuration limits.
1165
- *
1166
- * @method canProceedWithHMRRestart
1167
- * @private
1168
- *
1169
- * @returns {boolean} True if restart can proceed, false otherwise
1170
- *
1171
- * @remarks
1172
- * Checks:
1173
- * - Auto-restart enabled/disabled
1174
- * - Maximum restarts per minute limit
1175
- */
1176
- canProceedWithHMRRestart() {
1177
- const hmrConfig = this.getEnvironment;
1178
- if (!hmrConfig.hmrAutoRestarts) {
1179
- SLogger(this.localLanguage).logger.warn({
1180
- title: TranslationLoader2.t("HMR_AUTO_RESTART_DISABLED", this.localLanguage),
1181
- message: "HMR auto-restart disabled"
1182
- });
1183
- return false;
1184
- }
1185
- const maxRestarts = hmrConfig.hmrMaxRestarts || 5;
1186
- const now = Date.now();
1187
- const oneMinuteAgo = now - 6e4;
1188
- if (this.lastHmrRestartTime < oneMinuteAgo) {
1189
- this.hmrRestartCount = 0;
1190
- }
1191
- if (this.hmrRestartCount >= maxRestarts) {
1192
- const nextReset = Math.ceil((this.lastHmrRestartTime + 6e4 - now) / 1e3);
1193
- SLogger(this.localLanguage).logger.error({
1194
- title: TranslationLoader2.t("HMR_RESTART_LIMIT_EXCEEDED", this.localLanguage),
1195
- message: TranslationLoader2.t("HMR_RESTART_LIMIT_MESSAGE", this.localLanguage, {
1196
- max: maxRestarts,
1197
- nextReset
1198
- })
1199
- });
1200
- return false;
749
+ // ─── Change handling ─────────────────────────────────────────────────────
750
+ onFileChange(filePath) {
751
+ if (this.shouldIgnoreFile(filePath)) return;
752
+ if (!this.isWatchedFile(filePath)) return;
753
+ const rel = relative(this.cfg.rootDir, filePath);
754
+ if (this.isHotReloadable(filePath)) {
755
+ this.doHotReload(filePath, rel);
756
+ } else {
757
+ this.scheduleRestart(rel);
1201
758
  }
1202
- return true;
1203
759
  }
1204
- /**
1205
- * Called when the HMR watcher is ready.
1206
- *
1207
- * @method onHMRWatcherReady
1208
- * @private
1209
- *
1210
- * @param {string[]} watchPatterns - Patterns being watched
1211
- * @param {string[]} ignorePatterns - Patterns being ignored
1212
- *
1213
- * @returns {void}
1214
- */
1215
- onHMRWatcherReady(watchPatterns, ignorePatterns) {
1216
- SLogger(this.localLanguage).logger.info({
1217
- title: TranslationLoader2.t("HMR_WATCHER_READY", this.localLanguage),
1218
- message: TranslationLoader2.t("HMR_WATCHING_DETAILS", this.localLanguage, {
1219
- watchCount: watchPatterns,
1220
- ignoreCount: ignorePatterns
1221
- })
1222
- });
760
+ doHotReload(filePath, relativePath) {
761
+ if (this.isEnvFile(filePath)) {
762
+ dotenvConfig({ path: filePath, override: true });
763
+ this.printHot(relativePath, "env variables reloaded");
764
+ this.sendIpc({ type: "hot-reload", file: relativePath, kind: "env" });
765
+ } else {
766
+ this.printHot(relativePath, "json config reloaded");
767
+ this.sendIpc({ type: "hot-reload", file: relativePath, kind: "json" });
768
+ }
769
+ }
770
+ scheduleRestart(relativePath) {
771
+ if (this.debounceTimer) clearTimeout(this.debounceTimer);
772
+ this.debounceTimer = setTimeout(async () => {
773
+ this.printReloading(relativePath);
774
+ this.restartStart = Date.now();
775
+ this.isRestarting = true;
776
+ this.crashRestartCount = 0;
777
+ await this.killChild(true);
778
+ this.isRestarting = false;
779
+ this.spawnChild();
780
+ this.printReady(Date.now() - this.restartStart);
781
+ }, this.cfg.debounceMs);
782
+ }
783
+ // ─── IPC ─────────────────────────────────────────────────────────────────
784
+ sendIpc(message) {
785
+ if (this.child && typeof this.child.send === "function") {
786
+ try {
787
+ this.child.send(message);
788
+ } catch {
789
+ }
790
+ }
1223
791
  }
1224
- /**
1225
- * Handles HMR watcher errors.
1226
- *
1227
- * @method onHMRWatcherError
1228
- * @private
1229
- *
1230
- * @param {Error} error - The watcher error
1231
- *
1232
- * @returns {void}
1233
- */
1234
- onHMRWatcherError(error) {
1235
- SLogger(this.localLanguage).logger.error({
1236
- title: TranslationLoader2.t("HMR_WATCHER_ERROR", this.localLanguage),
1237
- message: error.message,
1238
- errorType: "FileWatcherError",
1239
- stackTrace: error.stack
792
+ // ─── Ignore / watch filters ───────────────────────────────────────────────
793
+ shouldIgnoreDir(dirPath) {
794
+ return this.cfg.ignore.some((p) => dirPath.includes(`/${p}`) || dirPath.endsWith(p));
795
+ }
796
+ shouldIgnoreFile(filePath) {
797
+ const name = basename2(filePath);
798
+ const rel = relative(this.cfg.rootDir, filePath);
799
+ return this.cfg.ignore.some((p) => name === p || rel.includes(p) || filePath.includes(`/${p}/`));
800
+ }
801
+ isWatchedFile(filePath) {
802
+ if (this.isEnvFile(filePath)) return true;
803
+ return this.cfg.watchExtensions.includes(extname(filePath));
804
+ }
805
+ isHotReloadable(filePath) {
806
+ if (this.isEnvFile(filePath)) return true;
807
+ return this.cfg.hotReloadExtensions.includes(extname(filePath));
808
+ }
809
+ isEnvFile(filePath) {
810
+ const name = basename2(filePath);
811
+ return name === this.cfg.envFile || name.startsWith(".env");
812
+ }
813
+ // ─── ANSI strip helper (for width calculation) ────────────────────────────
814
+ strip(str) {
815
+ return str.replace(/\[[0-9;]*m/g, "").replace(/\][^]*/g, "");
816
+ }
817
+ // ─── Timestamp ────────────────────────────────────────────────────────────
818
+ ts() {
819
+ const n = /* @__PURE__ */ new Date();
820
+ const h = String(n.getHours()).padStart(2, "0");
821
+ const m = String(n.getMinutes()).padStart(2, "0");
822
+ const s = String(n.getSeconds()).padStart(2, "0");
823
+ return `${h}:${m}:${s}`;
824
+ }
825
+ // ─── Console output ───────────────────────────────────────────────────────
826
+ /**
827
+ * Startup banner — mirrors the infoServer() box style from CoreService.
828
+ *
829
+ * ╔══════════════════════════════════════════╗
830
+ * gradient title
831
+ * ╔══ bgGreen box ════════════════════════╗
832
+ * entry dist/index.js
833
+ * runtime node (IPC enabled)
834
+ * root ./src
835
+ * watching .ts .js .json .env
836
+ * debounce 300ms
837
+ * ╚══════════════════════════════════════════╝
838
+ */
839
+ printBanner() {
840
+ const entryRel = relative(process.cwd(), this.cfg.entry) || this.cfg.entry;
841
+ const rootRel = relative(process.cwd(), this.cfg.rootDir) || ".";
842
+ const runtimeStr = this.cfg.runtime === "node" ? `node ${chalk2.blackBright("(IPC enabled)")}` : this.cfg.runtime;
843
+ const extStr = [...this.cfg.watchExtensions, ".env"].join(" ");
844
+ const ignoreStr = this.cfg.ignore.slice(0, 4).join(" ") + (this.cfg.ignore.length > 4 ? ` ${chalk2.blackBright(`+${this.cfg.ignore.length - 4} more`)}` : "");
845
+ const TITLE = "OPTICORE HOT RELOAD";
846
+ const rows = [
847
+ ` entry ${chalk2.white.bold(entryRel)}`,
848
+ ` runtime ${runtimeStr}`,
849
+ ` root ${chalk2.white(rootRel)}`,
850
+ ` watching ${chalk2.cyan(extStr)}`,
851
+ ` ignoring ${chalk2.blackBright(ignoreStr)}`,
852
+ ` debounce ${chalk2.white(`${this.cfg.debounceMs}ms`)}`
853
+ ];
854
+ const maxLen = Math.max(
855
+ TITLE.length + 4,
856
+ ...rows.map((r) => this.strip(r).length)
857
+ ) + 4;
858
+ const border = chalk2.bgGreen.white(" ".repeat(maxLen));
859
+ const titlePad = " ".repeat(Math.max(0, Math.floor((maxLen - TITLE.length) / 2)));
860
+ console.log("\n" + titlePad + gradient(["#43e97b", "#38f9d7", "#00c6fb"])(TITLE));
861
+ console.log(border);
862
+ rows.forEach((row) => {
863
+ const cleanLen = this.strip(row).length;
864
+ const padding = Math.max(0, maxLen - cleanLen - 2);
865
+ console.log(chalk2.bgGreen.white(` ${row}${" ".repeat(padding)} `));
1240
866
  });
867
+ console.log(border);
868
+ console.log(chalk2.blackBright(` watching for changes...
869
+ `));
1241
870
  }
1242
871
  /**
1243
- * Stops the HMR system.
872
+ * HOT event in-process reload, no server restart.
1244
873
  *
1245
- * @method stopHMR
1246
- * @private
1247
- *
1248
- * @returns {void}
874
+ * ✔ [ HOT ] 14:23:45 | .env → env variables reloaded
1249
875
  */
1250
- stopHMR() {
1251
- if (this.fileWatcher) {
1252
- this.fileWatcher.close();
1253
- this.fileWatcher = null;
1254
- }
1255
- if (this.hmrDebounceTimeout) {
1256
- clearTimeout(this.hmrDebounceTimeout);
1257
- this.hmrDebounceTimeout = null;
1258
- }
1259
- SLogger(this.localLanguage).logger.info({
1260
- title: TranslationLoader2.t("HMR_STOPPED", this.localLanguage),
1261
- message: TranslationLoader2.t("HMR_MONITORING_STOPPED", this.localLanguage)
1262
- });
876
+ printHot(file, action) {
877
+ const badge = colors2.bgCyan(colors2.white.bold(" HOT "));
878
+ const ts = chalk2.blackBright(this.ts());
879
+ const sep = chalk2.blackBright("|");
880
+ const filePart = chalk2.cyan.bold(file.padEnd(32));
881
+ const arrow = chalk2.green("\u2192");
882
+ const msg = chalk2.green(action);
883
+ console.log(` ${chalk2.green("\u2714")} ${badge} ${ts} ${sep} ${filePart} ${arrow} ${msg}`);
1263
884
  }
1264
885
  /**
1265
- * Stops the server and HMR system cleanly.
886
+ * RELOAD event server restart triggered.
1266
887
  *
1267
- * @method onStopServer
1268
- * @public
1269
- *
1270
- * @returns {void}
888
+ * ⚡ [ RELOAD ] 14:24:03 | src/routes/user.ts → restarting server...
1271
889
  */
1272
- onStopServer() {
1273
- this.stopHMR();
1274
- if (this.server) {
1275
- this.server.close(() => {
1276
- SLogger(this.localLanguage).logger.info({
1277
- title: TranslationLoader2.t("SERVER_STOPPED", this.localLanguage),
1278
- message: "Server stopped cleanly"
1279
- });
1280
- });
1281
- this.serverStatus = "STOPPED";
1282
- }
890
+ printReloading(file) {
891
+ const badge = colors2.bgYellow(colors2.white.bold(" RELOAD "));
892
+ const ts = chalk2.blackBright(this.ts());
893
+ const sep = chalk2.blackBright("|");
894
+ const filePart = chalk2.yellow.bold(file.padEnd(32));
895
+ const arrow = chalk2.yellow("\u2192");
896
+ const msg = chalk2.yellow("restarting server...");
897
+ console.log(`
898
+ ${chalk2.yellow("\u26A1")} ${badge} ${ts} ${sep} ${filePart} ${arrow} ${msg}`);
1283
899
  }
1284
900
  /**
1285
- * Gets the current server state information.
1286
- *
1287
- * @method getServerState
1288
- * @public
1289
- *
1290
- * @returns {IServerStateInfo} Server state information object
901
+ * READY event server successfully restarted.
902
+ * Uses the same full-width bgGreen border as infoServer().
1291
903
  *
1292
- * @remarks
1293
- * Includes:
1294
- * - Status, host, port
1295
- * - Route and dependency counts
1296
- * - Uptime and memory usage
1297
- * - HMR configuration and statistics
904
+ * ════════════════════════════════════════════
905
+ * READY server restarted in 245ms
906
+ * ════════════════════════════════════════════
1298
907
  */
1299
- getServerState() {
1300
- const now = /* @__PURE__ */ new Date();
1301
- const uptime = this.serverStartTime ? now.getTime() - this.serverStartTime.getTime() : 0;
1302
- return {
1303
- status: this.serverStatus,
1304
- isRunning: this.server !== void 0 && this.serverStatus === "READY",
1305
- host: this.getEnvironment.appHost,
1306
- port: Number(this.getEnvironment.appPort),
1307
- language: this.localLanguage,
1308
- routesCount: this.currentRoutes.length,
1309
- dependenciesCount: this.currentDependencies.length,
1310
- startTime: this.serverStartTime,
1311
- currentTime: now,
1312
- uptime,
1313
- uptimeFormatted: this.formatUptime(uptime),
1314
- memoryUsage: process4.memoryUsage(),
1315
- pid: process4.pid,
1316
- platform: process4.platform,
1317
- nodeVersion: process4.version,
1318
- cwd: process4.cwd(),
1319
- hmrEnabled: this.getEnvironment.hmrEnabled,
1320
- hmrWatchingFiles: this.getEnvironment.hmrWatchPatterns?.length || 0,
1321
- hmrRestartCount: this.hmrRestartCount
1322
- };
908
+ printReady(elapsedMs) {
909
+ const msg = ` server restarted in ${chalk2.white.bold(`${elapsedMs}ms`)}`;
910
+ const cleanLen = this.strip(msg).length;
911
+ const width = Math.max(52, cleanLen + 6);
912
+ const border = chalk2.bgGreen.white(" ".repeat(width));
913
+ const padding = Math.max(0, width - cleanLen - 2);
914
+ const line = chalk2.bgGreen.white(` ${chalk2.bgGreen.white.bold(" READY ")} ${msg}${" ".repeat(padding)} `);
915
+ console.log(` ${border}`);
916
+ console.log(` ${line}`);
917
+ console.log(` ${border}
918
+ `);
1323
919
  }
1324
920
  /**
1325
- * Gets the current server status.
1326
- *
1327
- * @method getServerStatus
1328
- * @public
921
+ * CRASH event unexpected child process exit.
1329
922
  *
1330
- * @returns {TServerStatus} Current server status
923
+ * ✘ [ CRASH ] 14:24:10 | server exited with code 1
1331
924
  */
1332
- getServerStatus() {
1333
- return this.serverStatus;
925
+ printCrash(code) {
926
+ const badge = colors2.bgRed(colors2.white.bold(" CRASH "));
927
+ const ts = chalk2.blackBright(this.ts());
928
+ const sep = chalk2.blackBright("|");
929
+ const msg = chalk2.red(`server exited with code ${chalk2.bold(String(code))}`);
930
+ console.log(`
931
+ ${chalk2.red("\u2718")} ${badge} ${ts} ${sep} ${msg}`);
1334
932
  }
1335
933
  /**
1336
- * Gets detailed server statistics.
934
+ * RETRY event auto-restart after crash.
1337
935
  *
1338
- * @method getServerStats
1339
- * @public
1340
- *
1341
- * @returns {IServerStats} Server statistics object
1342
- *
1343
- * @remarks
1344
- * Includes:
1345
- * - Performance metrics (CPU, memory)
1346
- * - Uptime information
1347
- * - HMR statistics
936
+ * ↺ [ RETRY ] 14:24:11 | attempt 1 / 5
1348
937
  */
1349
- getServerStats() {
1350
- const uptime = this.serverStartTime ? Date.now() - this.serverStartTime.getTime() : 0;
1351
- const memory = process4.memoryUsage();
1352
- return {
1353
- status: this.serverStatus,
1354
- uptime: this.formatUptime(uptime),
1355
- memory: {
1356
- rss: this.formatBytes(memory.rss),
1357
- heapTotal: this.formatBytes(memory.heapTotal),
1358
- heapUsed: this.formatBytes(memory.heapUsed),
1359
- external: this.formatBytes(memory.external)
1360
- },
1361
- performance: {
1362
- cpuUsage: process4.cpuUsage(),
1363
- resourceUsage: process4.resourceUsage?.()
1364
- },
1365
- hmrStats: {
1366
- enabled: this.getEnvironment.hmrEnabled,
1367
- restartCount: this.hmrRestartCount,
1368
- lastRestartTime: this.lastHmrRestartTime,
1369
- watchingFiles: this.getEnvironment.hmrWatchPatterns?.length || 0
1370
- }
1371
- };
938
+ printCrashRetry(attempt, max) {
939
+ const badge = colors2.bgMagenta(colors2.white.bold(" RETRY "));
940
+ const ts = chalk2.blackBright(this.ts());
941
+ const sep = chalk2.blackBright("|");
942
+ const msg = chalk2.magenta(`auto-restart attempt ${chalk2.bold(`${attempt} / ${max}`)}`);
943
+ console.log(` ${chalk2.magenta("\u21BA")} ${badge} ${ts} ${sep} ${msg}`);
1372
944
  }
1373
945
  /**
1374
- * Formats milliseconds into a human-readable uptime string.
1375
- *
1376
- * @method formatUptime
1377
- * @private
1378
- *
1379
- * @param {number} ms - Milliseconds to format
1380
- *
1381
- * @returns {string} Formatted uptime string
1382
- *
1383
- * @example
1384
- * formatUptime(3661000) // returns "1h 1m 1s"
946
+ * LIMIT reached give up restarting.
1385
947
  */
1386
- formatUptime(ms) {
1387
- const seconds = Math.floor(ms / 1e3);
1388
- const minutes = Math.floor(seconds / 60);
1389
- const hours = Math.floor(minutes / 60);
1390
- const days = Math.floor(hours / 24);
1391
- if (days > 0) {
1392
- return `${days}d ${hours % 24}h ${minutes % 60}m`;
1393
- } else if (hours > 0) {
1394
- return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
1395
- } else if (minutes > 0) {
1396
- return `${minutes}m ${seconds % 60}s`;
1397
- } else {
1398
- return `${seconds}s`;
1399
- }
948
+ printCrashLimit(max) {
949
+ const badge = colors2.bgRed(colors2.white.bold(" ERROR "));
950
+ const ts = chalk2.blackBright(this.ts());
951
+ const sep = chalk2.blackBright("|");
952
+ const msg = chalk2.red(`max crash restarts reached (${chalk2.bold(String(max))}), giving up`);
953
+ console.log(`
954
+ ${chalk2.red("\u2718")} ${badge} ${ts} ${sep} ${msg}
955
+ `);
1400
956
  }
1401
957
  /**
1402
- * Formats bytes into a human-readable size string.
1403
- *
1404
- * @method formatBytes
1405
- * @private
1406
- *
1407
- * @param {number} bytes - Bytes to format
1408
- *
1409
- * @returns {string} Formatted size string
1410
- *
1411
- * @example
1412
- * formatBytes(1048576) // returns "1.00 MB"
958
+ * Error miscellaneous internal error.
1413
959
  */
1414
- formatBytes(bytes) {
1415
- const units = ["B", "KB", "MB", "GB", "TB"];
1416
- let value = bytes;
1417
- let unitIndex = 0;
1418
- while (value >= 1024 && unitIndex < units.length - 1) {
1419
- value /= 1024;
1420
- unitIndex++;
1421
- }
1422
- return `${value.toFixed(2)} ${units[unitIndex]}`;
960
+ printError(message) {
961
+ const badge = colors2.bgRed(colors2.white.bold(" ERROR "));
962
+ const ts = chalk2.blackBright(this.ts());
963
+ const sep = chalk2.blackBright("|");
964
+ console.error(` ${chalk2.red("\u2718")} ${badge} ${ts} ${sep} ${chalk2.red(message)}`);
1423
965
  }
1424
966
  /**
1425
- * Checks if HMR is currently active.
1426
- *
1427
- * @method isHMRActive
1428
- * @public
1429
- *
1430
- * @returns {boolean} True if HMR is enabled and watching files, false otherwise
967
+ * Stopped watcher shut down.
1431
968
  */
1432
- isHMRActive() {
1433
- return this.getEnvironment.hmrEnabled && this.fileWatcher !== null;
969
+ printStopped() {
970
+ const badge = colors2.bgBlackBright(colors2.white.bold(" STOPPED"));
971
+ const ts = chalk2.blackBright(this.ts());
972
+ const sep = chalk2.blackBright("|");
973
+ console.log(` ${chalk2.gray("\u25A0")} ${badge} ${ts} ${sep} ${chalk2.gray("watcher stopped")}
974
+ `);
1434
975
  }
1435
- /**
1436
- * Gets detailed HMR information and status.
1437
- *
1438
- * @method getHMRInfo
1439
- * @public
1440
- *
1441
- * @returns {any} HMR information object
1442
- *
1443
- * @remarks
1444
- * Includes:
1445
- * - Configuration settings from .env
1446
- * - Current restart count
1447
- * - Watcher status
1448
- */
1449
- getHMRInfo() {
1450
- return {
1451
- enabled: this.getEnvironment.hmrEnabled,
1452
- watchPatterns: this.getEnvironment.hmrWatchPatterns,
1453
- ignorePatterns: this.getEnvironment.hmrIgnorePatterns,
1454
- debounceMs: this.getEnvironment.hmrDebounceMs,
1455
- maxRestarts: this.getEnvironment.hmrMaxRestarts,
1456
- autoRestart: this.getEnvironment.hmrAutoRestarts,
1457
- currentRestartCount: this.hmrRestartCount,
1458
- lastRestartTime: this.lastHmrRestartTime,
1459
- isWatching: this.fileWatcher !== null,
1460
- isRestartPending: this.hmrRestartPending
976
+ // ─── Graceful shutdown ────────────────────────────────────────────────────
977
+ setupProcessSignals() {
978
+ const shutdown = async () => {
979
+ console.log("");
980
+ await this.stop();
981
+ process.exit(0);
1461
982
  };
983
+ process.once("SIGINT", shutdown);
984
+ process.once("SIGTERM", shutdown);
1462
985
  }
1463
986
  };
1464
987
  export {
988
+ HotReloadWatcher,
1465
989
  WebServerCore as WebServer,
1466
990
  envPath
1467
991
  };