opticore-webapp 1.0.67 → 1.0.69

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,17 +1,19 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
1
  // src/core/webServer.core.ts
9
2
  import * as path2 from "path";
10
3
  import process4 from "process";
11
4
  import corsOrigin from "cors";
12
- import { CEventNameError as eventName2, ServerListenEventError as ServerListenEventError2 } from "opticore-catch-exception-error";
13
- import { express } from "opticore-express";
14
- import { getEnvironnementValue as getEnvironnementValue2 } from "opticore-env-access";
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";
15
+ import { HttpStatusCode as HttpStatusCode3 } from "opticore-http-response";
16
+ import { TranslationLoader as TranslationLoader2 } from "opticore-translator";
15
17
 
16
18
  // src/core/handlers/eventProcess.handler.ts
17
19
  import process from "process";
@@ -449,63 +451,149 @@ var SServerStartError = (err, environmentPath) => {
449
451
  };
450
452
 
451
453
  // src/core/webServer.core.ts
452
- import { requestCallsEvent } from "opticore-request-call-event";
453
- import { SContainer as SContainer2 } from "opticore-dependency-inject";
454
- import { HttpStatusCode as HttpStatusCode3 } from "opticore-http-response";
455
- import { TranslationLoader as TranslationLoader2 } from "opticore-translator";
456
454
  var WebServerCore = class {
457
455
  serverUtility;
458
- expressApp = express();
456
+ expressApp;
459
457
  localLanguage;
460
458
  loggerConfig;
461
- routerExpressApp;
462
459
  getEnvironment;
463
460
  environmentPath;
464
461
  serverListenEvent;
462
+ // Existing properties
465
463
  currentRoutes = [];
466
464
  currentDependencies = [];
467
465
  server = void 0;
468
466
  errorEmitter;
469
- // ✅✅✅ ÉTAT DU SERVEUR
470
- serverState = "READY";
471
- lastError = null;
472
- // Statistiques
473
- hotReloadAttempts = 0;
474
- hotReloadSuccesses = 0;
475
- hotReloadFailures = 0;
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
+ */
476
502
  constructor(paramsConstructor) {
477
- this.getEnvironment = getEnvironnementValue2(paramsConstructor.environmentPath);
478
- this.routerExpressApp = paramsConstructor.app;
503
+ this.getEnvironment = getEnvironmentValue(paramsConstructor.environmentPath);
479
504
  this.loggerConfig = paramsConstructor.loggerConfig;
480
505
  this.localLanguage = paramsConstructor.localLanguage;
481
506
  this.environmentPath = paramsConstructor.environmentPath;
507
+ this.expressApp = express();
508
+ this.serverListenEvent = new ServerListenEventError2(paramsConstructor.localLanguage);
482
509
  this.expressApp.use(express.json());
483
510
  this.expressApp.use(express.raw());
484
511
  this.expressApp.use(express.text());
485
512
  this.expressApp.use(express.urlencoded({ extended: true }));
486
513
  this.expressApp.use(corsOrigin(paramsConstructor.corsOriginOptions));
487
- this.serverListenEvent = new ServerListenEventError2(paramsConstructor.localLanguage);
488
514
  this.serverUtility = new CoreService(paramsConstructor.localLanguage, paramsConstructor.environmentPath);
489
- this.setupSignalHandlers();
490
- this.setupIPCHandlers();
515
+ this.serverStatus = "STARTING";
516
+ this.serverStartTime = /* @__PURE__ */ new Date();
517
+ this.setupProcessEventListeners();
491
518
  }
519
+ /**
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
+ *
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
+ * ```
550
+ */
492
551
  onStartServer(routers, databaseCallback, dependenciesProvider) {
552
+ 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();
564
+ }
565
+ return server;
566
+ } catch (error) {
567
+ this.handleStartupError(error);
568
+ }
569
+ }
570
+ /**
571
+ * Validates server configuration parameters.
572
+ *
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)
584
+ */
585
+ validateServerParameters() {
493
586
  loaderTranslationFile(this.localLanguage);
494
- this.currentRoutes = routers;
495
- this.currentDependencies = dependenciesProvider || [];
496
- if (this.getEnvironment.appPort === "" && Number(this.getEnvironment.appPort) === 0) {
497
- this.serverListenEvent.hostPortUndefined(Number(this.getEnvironment.appPort));
498
- return void 0;
587
+ const port = Number(this.getEnvironment.appPort);
588
+ if (isNaN(port) || port <= 0) {
589
+ this.serverListenEvent.portUndefined();
590
+ return false;
499
591
  }
500
- if (this.getEnvironment.appHost === "") {
592
+ if (!this.getEnvironment.appHost || this.getEnvironment.appHost.trim() === "") {
501
593
  this.serverListenEvent.hostUndefined(this.getEnvironment.appHost);
502
- return void 0;
594
+ return false;
503
595
  }
504
- if (Number(this.getEnvironment.appPort) === 0) {
505
- this.serverListenEvent.portUndefined();
506
- return void 0;
507
- }
508
- if (this.localLanguage === "") {
596
+ if (!this.localLanguage || this.localLanguage.trim() === "") {
509
597
  SLogger(this.localLanguage).logger.error({
510
598
  message: TranslationLoader2.t("noDefaultLocalLang", this.localLanguage),
511
599
  title: TranslationLoader2.t("noLocalLang", this.localLanguage),
@@ -513,75 +601,225 @@ var WebServerCore = class {
513
601
  stackTrace: void 0,
514
602
  httpCodeValue: HttpStatusCode3.NOT_FOUND
515
603
  });
516
- return void 0;
604
+ return false;
517
605
  }
518
- this.server = this.expressApp.listen(
519
- Number(this.getEnvironment.appPort),
520
- this.getEnvironment.appHost,
521
- () => {
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, () => {
522
634
  try {
635
+ this.configureServerComponents(databaseCallback);
636
+ this.serverStatus = "READY";
523
637
  SLogger(this.localLanguage).logger.info({
524
- message: TranslationLoader2.t("HOT_RELOAD_READY", this.localLanguage),
525
- title: TranslationLoader2.t("HOT_RELOAD", this.localLanguage)
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
+ })
526
643
  });
527
- if (databaseCallback) {
528
- databaseCallback(this.getEnvironment);
529
- }
530
- new SContainer2(this.localLanguage, this.currentDependencies);
531
- this.expressApp.use(express.static(path2.join(process4.cwd(), "public/template")));
532
- this.registerRoutes(this.currentRoutes);
533
- this.setupErrorHandling();
534
- this.setupServerEvents();
535
- this.serverState = "READY";
536
- this.infoWebApp();
537
- this.notifyServerReady();
538
644
  } catch (err) {
539
- SLogger(this.localLanguage).logger.error({
540
- title: TranslationLoader2.t("STARTUP_ERROR", this.localLanguage),
541
- message: err.message,
542
- errorType: err.code,
543
- stackTrace: err.stackTrace,
544
- httpCodeValue: HttpStatusCode3.INTERNAL_SERVER_ERROR
545
- });
546
- SServerStartError(err, this.environmentPath);
645
+ this.handleServerConfigurationError(err);
646
+ }
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
+ );
658
+ }
659
+ }
660
+ /**
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
+ *
670
+ * @throws {Error} If component configuration fails
671
+ */
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
+ );
547
682
  }
548
683
  }
549
- );
550
- return this.server;
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();
694
+ 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
+ }
551
701
  }
552
702
  /**
553
- * ✅✅✅ Configuration de la gestion d'erreurs
703
+ * Handles server configuration errors.
704
+ *
705
+ * @method handleServerConfigurationError
706
+ * @private
707
+ *
708
+ * @param {any} err - The error that occurred
709
+ *
710
+ * @returns {void}
554
711
  */
555
- setupErrorHandling() {
556
- SLogger(this.localLanguage).logger.info({
557
- title: TranslationLoader2.t("SETTING_UP", this.localLanguage),
558
- message: TranslationLoader2.t("SETTING_UP_ERROR", this.localLanguage)
559
- });
560
- this.errorEmitter = eventProcessHandler(this.localLanguage, this.expressApp);
561
- if (this.errorEmitter) {
562
- this.errorEmitter.on("transformError", (error) => {
563
- SLogger(this.localLanguage).logger.info({
564
- title: TranslationLoader2.t("", this.localLanguage),
565
- message: TranslationLoader2.t("", this.localLanguage)
566
- });
567
- this.lastError = error;
568
- this.serverState = "BLOCKED";
569
- this.notifyWatcherBlockedByError(error);
570
- });
571
- this.errorEmitter.on("error", (error) => {
572
- SLogger(this.localLanguage).logger.info({
573
- title: TranslationLoader2.t("ERROR_EMITTED", this.localLanguage),
574
- message: `Error emitted: ${error.message}`
575
- });
576
- });
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);
577
720
  }
578
- SLogger(this.localLanguage).logger.info({
579
- title: TranslationLoader2.t("ERROR_HANDLING", this.localLanguage),
580
- message: TranslationLoader2.t("ERROR_HANDLING_CONFIGURED", this.localLanguage)
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);
581
796
  });
582
797
  }
583
- setupServerEvents() {
584
- if (!this.server) return;
798
+ /**
799
+ * Sets up HTTP server event listeners.
800
+ *
801
+ * @method setupServerEventListeners
802
+ * @private
803
+ *
804
+ * @returns {void}
805
+ *
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
817
+ */
818
+ setupServerEventListeners() {
819
+ loaderTranslationFile(this.localLanguage);
820
+ if (!this.server) {
821
+ return;
822
+ }
585
823
  this.server.on(eventName2.error, (err) => {
586
824
  this.serverListenEvent.onEventError(err);
587
825
  });
@@ -603,270 +841,624 @@ var WebServerCore = class {
603
841
  );
604
842
  });
605
843
  }
606
- notifyServerReady() {
607
- setTimeout(() => {
608
- if (process4.send) {
609
- process4.send({
610
- type: "SERVER_READY",
611
- timestamp: Date.now(),
612
- port: Number(this.getEnvironment.appPort),
613
- host: this.getEnvironment.appHost,
614
- message: "Server ready for hot reload"
844
+ /**
845
+ * Sets up error handling middleware and event emitters.
846
+ *
847
+ * @method setupErrorHandling
848
+ * @private
849
+ *
850
+ * @returns {void}
851
+ */
852
+ setupErrorHandling() {
853
+ 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
+ }
887
+ /**
888
+ * Registers routes with the Express application.
889
+ *
890
+ * @method registerRoutes
891
+ * @private
892
+ *
893
+ * @param {any[]} allFeatureRoutes - Array of feature routes to register
894
+ *
895
+ * @returns {void}
896
+ */
897
+ registerRoutes(allFeatureRoutes) {
898
+ allFeatureRoutes.forEach((router) => {
899
+ if (router.routes) {
900
+ router.routes.forEach((route) => {
901
+ this.expressApp.use(route.path, route.handler);
615
902
  });
616
903
  }
617
- }, 100);
904
+ });
905
+ }
906
+ /**
907
+ * Displays server information.
908
+ *
909
+ * @method infoWebApp
910
+ * @private
911
+ *
912
+ * @returns {void}
913
+ */
914
+ infoWebApp() {
915
+ loaderTranslationFile(this.localLanguage);
916
+ this.serverUtility.infoServer(
917
+ this.getEnvironment.appHost,
918
+ Number(this.getEnvironment.appPort)
919
+ );
618
920
  }
619
- setupSignalHandlers() {
620
- process4.on("SIGHUP", async () => {
621
- if (this.serverState === "READY") {
921
+ /**
922
+ * Starts the Hot Module Replacement (HMR) file watching system.
923
+ *
924
+ * @method startHMR
925
+ * @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
+ */
937
+ startHMR() {
938
+ try {
939
+ loaderTranslationFile(this.localLanguage);
940
+ const hmrConfig = this.getEnvironment;
941
+ if (!hmrConfig.hmrEnabled) {
622
942
  SLogger(this.localLanguage).logger.info({
623
- title: TranslationLoader2.t("SIGHUP", this.localLanguage),
624
- message: TranslationLoader2.t("RECEIVED_SIGHUP", this.localLanguage)
943
+ title: TranslationLoader2.t("HMR_DISABLED", this.localLanguage),
944
+ message: "HMR is disabled in configuration"
625
945
  });
626
- await this.performTrueHotReload();
946
+ return;
627
947
  }
628
- });
629
- process4.on("SIGTERM", () => {
630
- SLogger(this.localLanguage).logger.info({
631
- title: TranslationLoader2.t("SIGHUP", this.localLanguage),
632
- message: TranslationLoader2.t("RECEIVED_SIGHUP", this.localLanguage)
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
633
979
  });
634
- this.shutdown();
635
- });
636
- process4.on("SIGINT", () => {
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();
637
983
  SLogger(this.localLanguage).logger.info({
638
- title: TranslationLoader2.t("SIGINT", this.localLanguage),
639
- message: TranslationLoader2.t("RECEIVED_SIGINT", this.localLanguage)
984
+ title: TranslationLoader2.t("HMR_STARTED", this.localLanguage),
985
+ message: TranslationLoader2.t("HMR_MONITORING_STARTED", this.localLanguage)
640
986
  });
641
- this.shutdown();
642
- });
643
- }
644
- setupIPCHandlers() {
645
- if (!process4.send) {
646
- SLogger(this.localLanguage).logger.info({
647
- title: TranslationLoader2.t("IPC", this.localLanguage),
648
- message: TranslationLoader2.t("IPC_NOT_AVAILABLE", this.localLanguage)
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
649
993
  });
650
- return;
651
994
  }
652
- SLogger(this.localLanguage).logger.info({
653
- title: TranslationLoader2.t("IPC_READY", this.localLanguage),
654
- message: TranslationLoader2.t("IPC_READY_HOT_RELOAD", this.localLanguage)
655
- });
656
- process4.on("message", async (message) => {
657
- if (message.type === "HOT_RELOAD_REQUEST") {
658
- if (this.serverState === "BLOCKED") {
659
- SLogger(this.localLanguage).logger.info({
660
- title: TranslationLoader2.t("BLOCKED", this.localLanguage),
661
- message: TranslationLoader2.t("BLOCKED_STATE", this.localLanguage)
662
- });
663
- return;
664
- }
665
- if (this.serverState === "READY") {
666
- await this.performTrueHotReload();
667
- }
668
- }
669
- });
670
995
  }
671
996
  /**
672
- * ✅✅✅ HOT RELOAD avec gestion d'état stricte
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.
673
1010
  */
674
- async performTrueHotReload() {
675
- this.serverState = "RELOADING";
676
- this.hotReloadAttempts++;
677
- const startTime = Date.now();
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);
678
1019
  SLogger(this.localLanguage).logger.info({
679
- title: TranslationLoader2.t("RELOAD_STARTING", this.localLanguage),
680
- message: TranslationLoader2.t("HOT_RELOAD_ATTEMPTED", this.localLanguage, { hotReloadAttempts: this.hotReloadAttempts })
1020
+ title: TranslationLoader2.t("FILE_CHANGE_DETECTED", this.localLanguage),
1021
+ message: `File ${action}: ${filePath}`
681
1022
  });
1023
+ }
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();
682
1051
  try {
683
- const clearedModules = this.clearApplicationModulesCache();
684
- SLogger(this.localLanguage).logger.info({
685
- title: TranslationLoader2.t("APP_MODULES_CLEARED", this.localLanguage),
686
- message: TranslationLoader2.t("CLEARED_MODULES", this.localLanguage, { clearedModules })
687
- });
688
- this.reloadConfigurations();
689
- SLogger(this.localLanguage).logger.info({
690
- title: TranslationLoader2.t("APP_MODULES_CLEARED", this.localLanguage),
691
- message: TranslationLoader2.t("CLEARED_MODULES", this.localLanguage, { hotReloadAttempts: this.hotReloadAttempts })
692
- });
693
- this.reloadDependencies();
1052
+ loaderTranslationFile(this.localLanguage);
694
1053
  SLogger(this.localLanguage).logger.info({
695
- title: TranslationLoader2.t("DEPENDENCIES", this.localLanguage),
696
- message: TranslationLoader2.t("RELOAD_DEPENDENCIES", this.localLanguage)
1054
+ title: TranslationLoader2.t("HOT_RELOAD_STARTING", this.localLanguage),
1055
+ message: TranslationLoader2.t("RELOADING_APPLICATION", this.localLanguage, {
1056
+ file: filePath,
1057
+ action
1058
+ })
697
1059
  });
698
- const duration = Date.now() - startTime;
699
- this.hotReloadSuccesses++;
700
- this.serverState = "READY";
701
- this.lastError = null;
702
- SLogger(this.localLanguage).logger.info({
703
- title: TranslationLoader2.t("RELOAD_SUCCESS", this.localLanguage),
704
- message: TranslationLoader2.t("HOT_RELOAD_SUCCESS", this.localLanguage, { duration })
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
+ })
705
1076
  });
706
- this.notifyWatcherReloadSuccess(duration);
707
1077
  } catch (error) {
708
- this.hotReloadFailures++;
709
- const duration = Date.now() - startTime;
710
- SLogger(this.localLanguage).logger.info({
711
- title: TranslationLoader2.t("RELOAD_FAILED", this.localLanguage),
712
- message: TranslationLoader2.t("HOT_RELOAD_FAILED", this.localLanguage, { duration, errorMessage: error.message })
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
713
1084
  });
714
- const isTransformError = error?.name === "TransformError" || error?.message?.includes("Transform failed") || error?.message?.includes("esbuild");
715
- if (isTransformError) {
716
- this.lastError = error;
717
- this.serverState = "BLOCKED";
718
- this.notifyWatcherBlockedByError(error);
719
- } else {
720
- this.serverState = "BLOCKED";
721
- this.notifyWatcherReloadError(error);
722
- }
1085
+ } finally {
1086
+ this.hmrRestartPending = false;
723
1087
  }
724
1088
  }
725
1089
  /**
726
- * Nettoie le cache des modules applicatifs
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
727
1104
  */
728
- clearApplicationModulesCache() {
729
- const baseDirs = [
730
- path2.join(process4.cwd(), "src"),
731
- path2.join(process4.cwd(), "dist")
732
- ];
733
- let clearedCount = 0;
734
- for (const key in __require.cache) {
735
- const isApplicationModule = baseDirs.some((dir) => key.startsWith(dir));
736
- const isNodeModule = key.includes("node_modules");
737
- const isJsonFile = key.endsWith(".json");
738
- if (isApplicationModule && !isNodeModule && !isJsonFile) {
739
- delete __require.cache[key];
740
- clearedCount++;
741
- }
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();
742
1114
  }
743
- return clearedCount;
744
1115
  }
745
- reloadConfigurations() {
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() {
746
1131
  try {
747
- const newEnv = getEnvironnementValue2(this.environmentPath);
748
- Object.keys(newEnv).forEach((key) => {
749
- this.getEnvironment[key] = newEnv[key];
1132
+ SLogger(this.localLanguage).logger.info({
1133
+ title: TranslationLoader2.t("ROUTES_RELOADED", this.localLanguage),
1134
+ message: "Routes dynamically reloaded"
750
1135
  });
751
- loaderTranslationFile(this.localLanguage);
752
1136
  } catch (error) {
753
- throw new Error(`Configuration reload failed: ${error.message}`);
1137
+ throw new Error(`Route reload failed: ${error.message}`);
754
1138
  }
755
1139
  }
756
- reloadDependencies() {
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() {
757
1151
  try {
758
- new SContainer2(this.localLanguage, this.currentDependencies);
759
- const container = dependenciesContainerProvider(this.localLanguage);
760
- if (!container) {
761
- throw new Error("Dependency container is not available");
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
+ });
762
1158
  }
763
1159
  } catch (error) {
764
1160
  throw new Error(`Dependency reload failed: ${error.message}`);
765
1161
  }
766
1162
  }
767
1163
  /**
768
- * ✅✅✅ Notifier le watcher : SUCCÈS
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
769
1175
  */
770
- notifyWatcherReloadSuccess(duration) {
771
- if (process4.send && process4.env.WATCHER_MODE === "true") {
772
- process4.send({
773
- type: "HOT_RELOAD_SUCCESS",
774
- timestamp: Date.now(),
775
- duration,
776
- serverState: this.serverState,
777
- message: "Code reloaded successfully"
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"
778
1182
  });
1183
+ return false;
779
1184
  }
780
- }
781
- /**
782
- * ✅✅✅ Notifier le watcher : BLOQUÉ PAR ERREUR TRANSFORM
783
- */
784
- notifyWatcherBlockedByError(error) {
785
- if (process4.send && process4.env.WATCHER_MODE === "true") {
786
- const errorDetails = this.parseTransformError(error);
787
- process4.send({
788
- type: "HOT_RELOAD_BLOCKED_BY_ERROR",
789
- timestamp: Date.now(),
790
- serverState: this.serverState,
791
- error: {
792
- message: error.message,
793
- errorType: error.name || "TransformError",
794
- file: errorDetails.file,
795
- line: errorDetails.line,
796
- column: errorDetails.column,
797
- detail: errorDetails.errorDetail,
798
- tool: errorDetails.tool
799
- }
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
+ })
800
1199
  });
1200
+ return false;
801
1201
  }
1202
+ return true;
802
1203
  }
803
1204
  /**
804
- * ✅✅✅ Notifier le watcher : ERREUR GÉNÉRIQUE
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}
805
1214
  */
806
- notifyWatcherReloadError(error) {
807
- if (process4.send && process4.env.WATCHER_MODE === "true") {
808
- process4.send({
809
- type: "HOT_RELOAD_ERROR",
810
- timestamp: Date.now(),
811
- serverState: this.serverState,
812
- error: error.message,
813
- errorType: error.name || "UNKNOWN_ERROR"
814
- });
815
- }
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
+ });
816
1223
  }
817
1224
  /**
818
- * ✅✅✅ Parser les détails d'une TransformError
1225
+ * Handles HMR watcher errors.
1226
+ *
1227
+ * @method onHMRWatcherError
1228
+ * @private
1229
+ *
1230
+ * @param {Error} error - The watcher error
1231
+ *
1232
+ * @returns {void}
819
1233
  */
820
- parseTransformError(error) {
821
- const errorMessage = error.message || String(error);
822
- let tool = "Unknown";
823
- if (errorMessage.includes("esbuild")) tool = "esbuild";
824
- if (errorMessage.includes("webpack")) tool = "webpack";
825
- const fileMatch = errorMessage.match(/([^:\s]+\.(?:ts|js|tsx|jsx)):(\d+):(\d+):/);
826
- const file = fileMatch ? fileMatch[1] : null;
827
- const line = fileMatch ? parseInt(fileMatch[2], 10) : null;
828
- const column = fileMatch ? parseInt(fileMatch[3], 10) : null;
829
- const errorDetailMatch = errorMessage.match(/ERROR:\s*(.+?)(?:\n|$)/);
830
- const errorDetail = errorDetailMatch ? errorDetailMatch[1].trim() : null;
831
- return {
832
- message: errorMessage,
833
- file,
834
- line,
835
- column,
836
- errorDetail,
837
- tool
838
- };
839
- }
840
- registerRoutes(allFeatureRoutes) {
841
- allFeatureRoutes.forEach((router) => {
842
- if (router.routes) {
843
- router.routes.forEach((route) => {
844
- this.expressApp.use(route.path, route.handler);
845
- });
846
- }
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
847
1240
  });
848
1241
  }
849
- infoWebApp() {
850
- this.serverUtility.infoServer(
851
- this.getEnvironment.appHost,
852
- Number(this.getEnvironment.appPort)
853
- );
1242
+ /**
1243
+ * Stops the HMR system.
1244
+ *
1245
+ * @method stopHMR
1246
+ * @private
1247
+ *
1248
+ * @returns {void}
1249
+ */
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
+ });
854
1263
  }
855
- shutdown() {
1264
+ /**
1265
+ * Stops the server and HMR system cleanly.
1266
+ *
1267
+ * @method onStopServer
1268
+ * @public
1269
+ *
1270
+ * @returns {void}
1271
+ */
1272
+ onStopServer() {
1273
+ this.stopHMR();
856
1274
  if (this.server) {
857
1275
  this.server.close(() => {
858
1276
  SLogger(this.localLanguage).logger.info({
859
- title: TranslationLoader2.t("CLOSED", this.localLanguage),
860
- message: TranslationLoader2.t("SERVER_CLOSED", this.localLanguage)
1277
+ title: TranslationLoader2.t("SERVER_STOPPED", this.localLanguage),
1278
+ message: "Server stopped cleanly"
861
1279
  });
862
- process4.exit(0);
863
1280
  });
864
- setTimeout(() => {
865
- process4.exit(1);
866
- }, 5e3);
1281
+ this.serverStatus = "STOPPED";
1282
+ }
1283
+ }
1284
+ /**
1285
+ * Gets the current server state information.
1286
+ *
1287
+ * @method getServerState
1288
+ * @public
1289
+ *
1290
+ * @returns {IServerStateInfo} Server state information object
1291
+ *
1292
+ * @remarks
1293
+ * Includes:
1294
+ * - Status, host, port
1295
+ * - Route and dependency counts
1296
+ * - Uptime and memory usage
1297
+ * - HMR configuration and statistics
1298
+ */
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
+ };
1323
+ }
1324
+ /**
1325
+ * Gets the current server status.
1326
+ *
1327
+ * @method getServerStatus
1328
+ * @public
1329
+ *
1330
+ * @returns {TServerStatus} Current server status
1331
+ */
1332
+ getServerStatus() {
1333
+ return this.serverStatus;
1334
+ }
1335
+ /**
1336
+ * Gets detailed server statistics.
1337
+ *
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
1348
+ */
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
+ };
1372
+ }
1373
+ /**
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"
1385
+ */
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`;
867
1397
  } else {
868
- process4.exit(0);
1398
+ return `${seconds}s`;
1399
+ }
1400
+ }
1401
+ /**
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"
1413
+ */
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++;
869
1421
  }
1422
+ return `${value.toFixed(2)} ${units[unitIndex]}`;
1423
+ }
1424
+ /**
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
1431
+ */
1432
+ isHMRActive() {
1433
+ return this.getEnvironment.hmrEnabled && this.fileWatcher !== null;
1434
+ }
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
1461
+ };
870
1462
  }
871
1463
  };
872
1464
  export {