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.cjs CHANGED
@@ -39,9 +39,14 @@ module.exports = __toCommonJS(index_exports);
39
39
  var path2 = __toESM(require("path"), 1);
40
40
  var import_node_process3 = __toESM(require("process"), 1);
41
41
  var import_cors = __toESM(require("cors"), 1);
42
+ var import_express = __toESM(require("express"), 1);
43
+ var import_chokidar = __toESM(require("chokidar"), 1);
42
44
  var import_opticore_catch_exception_error3 = require("opticore-catch-exception-error");
43
- var import_opticore_express = require("opticore-express");
44
45
  var import_opticore_env_access2 = require("opticore-env-access");
46
+ var import_opticore_request_call_event = require("opticore-request-call-event");
47
+ var import_opticore_dependency_inject2 = require("opticore-dependency-inject");
48
+ var import_opticore_http_response3 = require("opticore-http-response");
49
+ var import_opticore_translator2 = require("opticore-translator");
45
50
 
46
51
  // src/core/handlers/eventProcess.handler.ts
47
52
  var import_node_process = __toESM(require("process"), 1);
@@ -472,63 +477,149 @@ var SServerStartError = (err, environmentPath) => {
472
477
  };
473
478
 
474
479
  // src/core/webServer.core.ts
475
- var import_opticore_request_call_event = require("opticore-request-call-event");
476
- var import_opticore_dependency_inject2 = require("opticore-dependency-inject");
477
- var import_opticore_http_response3 = require("opticore-http-response");
478
- var import_opticore_translator2 = require("opticore-translator");
479
480
  var WebServerCore = class {
480
481
  serverUtility;
481
- expressApp = (0, import_opticore_express.express)();
482
+ expressApp;
482
483
  localLanguage;
483
484
  loggerConfig;
484
- routerExpressApp;
485
485
  getEnvironment;
486
486
  environmentPath;
487
487
  serverListenEvent;
488
+ // Existing properties
488
489
  currentRoutes = [];
489
490
  currentDependencies = [];
490
491
  server = void 0;
491
492
  errorEmitter;
492
- // ✅✅✅ ÉTAT DU SERVEUR
493
- serverState = "READY";
494
- lastError = null;
495
- // Statistiques
496
- hotReloadAttempts = 0;
497
- hotReloadSuccesses = 0;
498
- hotReloadFailures = 0;
493
+ serverStatus = "STARTING";
494
+ serverStartTime = /* @__PURE__ */ new Date();
495
+ // HMR properties
496
+ fileWatcher = null;
497
+ hmrRestartPending = false;
498
+ hmrDebounceTimeout = null;
499
+ hmrRestartCount = 0;
500
+ lastHmrRestartTime = 0;
501
+ /**
502
+ * Creates a new WebServerCore instance.
503
+ *
504
+ * @constructor
505
+ * @param {WebServerConstructorInterface} paramsConstructor - Configuration parameters
506
+ *
507
+ * @param {express.Application} paramsConstructor.app - Express application instance
508
+ * @param {LoggerCore} paramsConstructor.loggerConfig - Logger configuration
509
+ * @param {string} paramsConstructor.environmentPath - Path to environment file
510
+ * @param {string} paramsConstructor.localLanguage - Default language for translations
511
+ * @param {CorsOptions} paramsConstructor.corsOriginOptions - CORS configuration
512
+ *
513
+ * @returns {WebServerCore} New WebServerCore instance
514
+ *
515
+ * @throws {Error} If environment file cannot be loaded
516
+ *
517
+ * @example
518
+ * ```typescript
519
+ * const server = new WebServerCore({
520
+ * app: express(),
521
+ * loggerConfig: new LoggerCore(config),
522
+ * environmentPath: ".env",
523
+ * localLanguage: "fr",
524
+ * corsOriginOptions: { origin: "http://localhost:3000" }
525
+ * });
526
+ * ```
527
+ */
499
528
  constructor(paramsConstructor) {
500
- this.getEnvironment = (0, import_opticore_env_access2.getEnvironnementValue)(paramsConstructor.environmentPath);
501
- this.routerExpressApp = paramsConstructor.app;
529
+ this.getEnvironment = (0, import_opticore_env_access2.getEnvironmentValue)(paramsConstructor.environmentPath);
502
530
  this.loggerConfig = paramsConstructor.loggerConfig;
503
531
  this.localLanguage = paramsConstructor.localLanguage;
504
532
  this.environmentPath = paramsConstructor.environmentPath;
505
- this.expressApp.use(import_opticore_express.express.json());
506
- this.expressApp.use(import_opticore_express.express.raw());
507
- this.expressApp.use(import_opticore_express.express.text());
508
- this.expressApp.use(import_opticore_express.express.urlencoded({ extended: true }));
509
- this.expressApp.use((0, import_cors.default)(paramsConstructor.corsOriginOptions));
533
+ this.expressApp = (0, import_express.default)();
510
534
  this.serverListenEvent = new import_opticore_catch_exception_error3.ServerListenEventError(paramsConstructor.localLanguage);
535
+ this.expressApp.use(import_express.default.json());
536
+ this.expressApp.use(import_express.default.raw());
537
+ this.expressApp.use(import_express.default.text());
538
+ this.expressApp.use(import_express.default.urlencoded({ extended: true }));
539
+ this.expressApp.use((0, import_cors.default)(paramsConstructor.corsOriginOptions));
511
540
  this.serverUtility = new CoreService(paramsConstructor.localLanguage, paramsConstructor.environmentPath);
512
- this.setupSignalHandlers();
513
- this.setupIPCHandlers();
541
+ this.serverStatus = "STARTING";
542
+ this.serverStartTime = /* @__PURE__ */ new Date();
543
+ this.setupProcessEventListeners();
514
544
  }
545
+ /**
546
+ * Starts the HTTP server and initializes all components including HMR if enabled.
547
+ *
548
+ * @method onStartServer
549
+ * @public
550
+ *
551
+ * @param {TFeatureRoutes[]} routers - Array of feature routes to register
552
+ * @param {(env: IEnvVariables) => void} [databaseCallback] - Optional database connection callback
553
+ * @param {TDependency[]} [dependenciesProvider] - Optional dependency injection providers
554
+ *
555
+ * @returns {serverWebApp | undefined} HTTP server instance or undefined if startup fails
556
+ *
557
+ * @throws {ServerListenEventError} If port/host configuration is invalid
558
+ * @throws {Error} If server initialization fails
559
+ *
560
+ * @fires WebServerCore#startHttpServer - When server successfully starts
561
+ * @fires WebServerCore#startHMR - When HMR is started (if enabled)
562
+ * @fires WebServerCore#serverError - When server fails to start
563
+ *
564
+ * @example
565
+ * ```typescript
566
+ * const server = app.onStartServer(
567
+ * routes,
568
+ * (env) => connectToDatabase(env),
569
+ * dependencies
570
+ * );
571
+ *
572
+ * if (server) {
573
+ * console.log("Server started successfully");
574
+ * }
575
+ * ```
576
+ */
515
577
  onStartServer(routers, databaseCallback, dependenciesProvider) {
578
+ try {
579
+ loaderTranslationFile(this.localLanguage);
580
+ this.currentRoutes = routers;
581
+ this.currentDependencies = dependenciesProvider || [];
582
+ this.serverStartTime = /* @__PURE__ */ new Date();
583
+ this.serverStatus = "STARTING";
584
+ if (!this.validateServerParameters()) {
585
+ return void 0;
586
+ }
587
+ const server = this.startHttpServer(databaseCallback);
588
+ if (this.getEnvironment.hmrEnabled) {
589
+ this.startHMR();
590
+ }
591
+ return server;
592
+ } catch (error) {
593
+ this.handleStartupError(error);
594
+ }
595
+ }
596
+ /**
597
+ * Validates server configuration parameters.
598
+ *
599
+ * @method validateServerParameters
600
+ * @private
601
+ *
602
+ * @returns {boolean} True if all parameters are valid, false otherwise
603
+ *
604
+ * @remarks
605
+ * Validates:
606
+ * - Port number is valid and positive
607
+ * - Host is not empty
608
+ * - Local language is specified
609
+ * - HMR configuration (if enabled)
610
+ */
611
+ validateServerParameters() {
516
612
  loaderTranslationFile(this.localLanguage);
517
- this.currentRoutes = routers;
518
- this.currentDependencies = dependenciesProvider || [];
519
- if (this.getEnvironment.appPort === "" && Number(this.getEnvironment.appPort) === 0) {
520
- this.serverListenEvent.hostPortUndefined(Number(this.getEnvironment.appPort));
521
- return void 0;
613
+ const port = Number(this.getEnvironment.appPort);
614
+ if (isNaN(port) || port <= 0) {
615
+ this.serverListenEvent.portUndefined();
616
+ return false;
522
617
  }
523
- if (this.getEnvironment.appHost === "") {
618
+ if (!this.getEnvironment.appHost || this.getEnvironment.appHost.trim() === "") {
524
619
  this.serverListenEvent.hostUndefined(this.getEnvironment.appHost);
525
- return void 0;
526
- }
527
- if (Number(this.getEnvironment.appPort) === 0) {
528
- this.serverListenEvent.portUndefined();
529
- return void 0;
620
+ return false;
530
621
  }
531
- if (this.localLanguage === "") {
622
+ if (!this.localLanguage || this.localLanguage.trim() === "") {
532
623
  SLogger(this.localLanguage).logger.error({
533
624
  message: import_opticore_translator2.TranslationLoader.t("noDefaultLocalLang", this.localLanguage),
534
625
  title: import_opticore_translator2.TranslationLoader.t("noLocalLang", this.localLanguage),
@@ -536,75 +627,225 @@ var WebServerCore = class {
536
627
  stackTrace: void 0,
537
628
  httpCodeValue: import_opticore_http_response3.HttpStatusCode.NOT_FOUND
538
629
  });
539
- return void 0;
630
+ return false;
540
631
  }
541
- this.server = this.expressApp.listen(
542
- Number(this.getEnvironment.appPort),
543
- this.getEnvironment.appHost,
544
- () => {
632
+ if (this.getEnvironment.hmrEnabled) {
633
+ if (!this.getEnvironment.hmrWatchPatterns || this.getEnvironment.hmrWatchPatterns.length === 0) {
634
+ SLogger(this.localLanguage).logger.warn({
635
+ title: import_opticore_translator2.TranslationLoader.t("HMR_CONFIG_WARNING", this.localLanguage),
636
+ message: "HMR enabled but no watch patterns defined"
637
+ });
638
+ }
639
+ }
640
+ return true;
641
+ }
642
+ /**
643
+ * Starts the HTTP server and sets up event listeners.
644
+ *
645
+ * @method startHttpServer
646
+ * @private
647
+ *
648
+ * @param {(env: IEnvVariables) => void} [databaseCallback] - Database connection callback
649
+ *
650
+ * @returns {serverWebApp | undefined} HTTP server instance or undefined if startup fails
651
+ *
652
+ * @throws {Error} If server fails to start
653
+ */
654
+ startHttpServer(databaseCallback) {
655
+ try {
656
+ loaderTranslationFile(this.localLanguage);
657
+ const port = Number(this.getEnvironment.appPort);
658
+ const host = this.getEnvironment.appHost;
659
+ this.server = this.expressApp.listen(port, host, () => {
545
660
  try {
661
+ this.configureServerComponents(databaseCallback);
662
+ this.serverStatus = "READY";
546
663
  SLogger(this.localLanguage).logger.info({
547
- message: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_READY", this.localLanguage),
548
- title: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD", this.localLanguage)
664
+ title: import_opticore_translator2.TranslationLoader.t("SERVER_RUNNING_TITLE", this.localLanguage),
665
+ message: import_opticore_translator2.TranslationLoader.t("SERVER_RUNNING_AT", this.localLanguage, {
666
+ server: `${host}:${port}`,
667
+ hmrEnabled: this.getEnvironment.hmrEnabled ? "with HMR" : "without HMR"
668
+ })
549
669
  });
550
- if (databaseCallback) {
551
- databaseCallback(this.getEnvironment);
552
- }
553
- new import_opticore_dependency_inject2.SContainer(this.localLanguage, this.currentDependencies);
554
- this.expressApp.use(import_opticore_express.express.static(path2.join(import_node_process3.default.cwd(), "public/template")));
555
- this.registerRoutes(this.currentRoutes);
556
- this.setupErrorHandling();
557
- this.setupServerEvents();
558
- this.serverState = "READY";
559
- this.infoWebApp();
560
- this.notifyServerReady();
561
670
  } catch (err) {
562
- SLogger(this.localLanguage).logger.error({
563
- title: import_opticore_translator2.TranslationLoader.t("STARTUP_ERROR", this.localLanguage),
564
- message: err.message,
565
- errorType: err.code,
566
- stackTrace: err.stackTrace,
567
- httpCodeValue: import_opticore_http_response3.HttpStatusCode.INTERNAL_SERVER_ERROR
568
- });
569
- SServerStartError(err, this.environmentPath);
671
+ this.handleServerConfigurationError(err);
672
+ }
673
+ });
674
+ this.setupServerEventListeners();
675
+ return this.server;
676
+ } catch (error) {
677
+ this.serverListenEvent.onEventError(
678
+ new Error(import_opticore_translator2.TranslationLoader.t(
679
+ "HTTP_SERVER_FAILED",
680
+ this.localLanguage,
681
+ { errorMessage: error.message }
682
+ ))
683
+ );
684
+ }
685
+ }
686
+ /**
687
+ * Configures server components (database, dependencies, routes, etc.).
688
+ *
689
+ * @method configureServerComponents
690
+ * @private
691
+ *
692
+ * @param {(env: IEnvVariables) => void} [databaseCallback] - Database connection callback
693
+ *
694
+ * @returns {void}
695
+ *
696
+ * @throws {Error} If component configuration fails
697
+ */
698
+ configureServerComponents(databaseCallback) {
699
+ try {
700
+ loaderTranslationFile(this.localLanguage);
701
+ if (databaseCallback && typeof databaseCallback === "function") {
702
+ try {
703
+ databaseCallback(this.getEnvironment);
704
+ } catch (dbError) {
705
+ this.serverListenEvent.listenerError(
706
+ new Error(import_opticore_translator2.TranslationLoader.t("DB_CON_FAILED", this.localLanguage, { dbErrorMessage: dbError.message }))
707
+ );
570
708
  }
571
709
  }
572
- );
573
- return this.server;
710
+ try {
711
+ new import_opticore_dependency_inject2.SContainer(this.localLanguage, this.currentDependencies);
712
+ } catch (depError) {
713
+ this.serverListenEvent.listenerError(
714
+ new Error(import_opticore_translator2.TranslationLoader.t("DEPENDENCY_INJECTION_FAILED", this.localLanguage, { depErrorMessage: depError.message }))
715
+ );
716
+ }
717
+ this.expressApp.use(import_express.default.static(path2.join(import_node_process3.default.cwd(), "public/template")));
718
+ this.registerRoutes(this.currentRoutes);
719
+ this.setupErrorHandling();
720
+ this.infoWebApp();
721
+ } catch (error) {
722
+ this.serverListenEvent.listenerError(
723
+ new Error(import_opticore_translator2.TranslationLoader.t("SERVER_COMPONENT_CONFIG_FAILED", this.localLanguage, { errorMessage: error.message }))
724
+ );
725
+ throw error;
726
+ }
574
727
  }
575
728
  /**
576
- * ✅✅✅ Configuration de la gestion d'erreurs
729
+ * Handles server configuration errors.
730
+ *
731
+ * @method handleServerConfigurationError
732
+ * @private
733
+ *
734
+ * @param {any} err - The error that occurred
735
+ *
736
+ * @returns {void}
577
737
  */
578
- setupErrorHandling() {
579
- SLogger(this.localLanguage).logger.info({
580
- title: import_opticore_translator2.TranslationLoader.t("SETTING_UP", this.localLanguage),
581
- message: import_opticore_translator2.TranslationLoader.t("SETTING_UP_ERROR", this.localLanguage)
582
- });
583
- this.errorEmitter = eventProcessHandler(this.localLanguage, this.expressApp);
584
- if (this.errorEmitter) {
585
- this.errorEmitter.on("transformError", (error) => {
586
- SLogger(this.localLanguage).logger.info({
587
- title: import_opticore_translator2.TranslationLoader.t("", this.localLanguage),
588
- message: import_opticore_translator2.TranslationLoader.t("", this.localLanguage)
589
- });
590
- this.lastError = error;
591
- this.serverState = "BLOCKED";
592
- this.notifyWatcherBlockedByError(error);
593
- });
594
- this.errorEmitter.on("error", (error) => {
595
- SLogger(this.localLanguage).logger.info({
596
- title: import_opticore_translator2.TranslationLoader.t("ERROR_EMITTED", this.localLanguage),
597
- message: `Error emitted: ${error.message}`
598
- });
599
- });
738
+ handleServerConfigurationError(err) {
739
+ loaderTranslationFile(this.localLanguage);
740
+ this.serverStatus = "ERROR";
741
+ this.serverListenEvent.onEventError(err);
742
+ try {
743
+ SServerStartError(err, this.environmentPath);
744
+ } catch (startError) {
745
+ this.serverListenEvent.listenerError(startError);
600
746
  }
601
- SLogger(this.localLanguage).logger.info({
602
- title: import_opticore_translator2.TranslationLoader.t("ERROR_HANDLING", this.localLanguage),
603
- message: import_opticore_translator2.TranslationLoader.t("ERROR_HANDLING_CONFIGURED", this.localLanguage)
747
+ }
748
+ /**
749
+ * Handles general startup errors.
750
+ *
751
+ * @method handleStartupError
752
+ * @private
753
+ *
754
+ * @param {any} error - The startup error
755
+ *
756
+ * @returns {void}
757
+ */
758
+ handleStartupError(error) {
759
+ loaderTranslationFile(this.localLanguage);
760
+ this.serverStatus = "ERROR";
761
+ this.serverListenEvent.listenerError(error);
762
+ SLogger(this.localLanguage).logger.error({
763
+ title: import_opticore_translator2.TranslationLoader.t("GLOBAL_STARTUP_ERROR", this.localLanguage),
764
+ message: error.message,
765
+ errorType: error.name || "StartupError",
766
+ stackTrace: error.stack,
767
+ httpCodeValue: import_opticore_http_response3.HttpStatusCode.INTERNAL_SERVER_ERROR
768
+ });
769
+ }
770
+ /**
771
+ * Sets up Node.js process event listeners.
772
+ *
773
+ * @method setupProcessEventListeners
774
+ * @private
775
+ *
776
+ * @returns {void}
777
+ *
778
+ * @remarks
779
+ * Listens for:
780
+ * - Process exit events
781
+ * - Uncaught exceptions
782
+ * - Unhandled rejections
783
+ * - System signals (SIGINT, SIGTERM)
784
+ */
785
+ setupProcessEventListeners() {
786
+ loaderTranslationFile(this.localLanguage);
787
+ import_node_process3.default.on(import_opticore_catch_exception_error3.CEvent.beforeExit, (code) => {
788
+ this.serverListenEvent.processBeforeExit(code);
789
+ });
790
+ import_node_process3.default.on(import_opticore_catch_exception_error3.CEvent.disconnect, () => {
791
+ this.serverListenEvent.processDisconnected();
792
+ });
793
+ import_node_process3.default.on(import_opticore_catch_exception_error3.CEvent.exit, (code) => {
794
+ this.serverListenEvent.exited(code);
795
+ });
796
+ import_node_process3.default.on(import_opticore_catch_exception_error3.CEvent.message, (message) => {
797
+ this.serverListenEvent.message(message);
798
+ });
799
+ import_node_process3.default.on(import_opticore_catch_exception_error3.CEvent.multipleResolves, (type, promise, reason) => {
800
+ this.serverListenEvent.multipleResolves(type, promise, reason);
801
+ });
802
+ import_node_process3.default.on(import_opticore_catch_exception_error3.CEvent.rejectionHandled, (promise) => {
803
+ this.serverListenEvent.promiseRejectionHandled(promise);
804
+ });
805
+ import_node_process3.default.on(import_opticore_catch_exception_error3.CEvent.uncaughtException, (error) => {
806
+ this.serverListenEvent.uncaughtException(error);
807
+ });
808
+ import_node_process3.default.on(import_opticore_catch_exception_error3.CEvent.uncaughtExceptionMonitor, (error) => {
809
+ this.serverListenEvent.uncaughtExceptionMonitor(error);
810
+ });
811
+ import_node_process3.default.on(import_opticore_catch_exception_error3.CEvent.unhandledRejection, (reason, promise) => {
812
+ this.serverListenEvent.unhandledRejection(reason, promise);
813
+ });
814
+ import_node_process3.default.on(import_opticore_catch_exception_error3.CEvent.warning, (warning) => {
815
+ this.serverListenEvent.warning(warning);
816
+ });
817
+ import_node_process3.default.on(import_opticore_catch_exception_error3.CEvent.sigint, () => {
818
+ this.serverListenEvent.processInterrupted();
819
+ });
820
+ import_node_process3.default.on(import_opticore_catch_exception_error3.CEvent.sigterm, (signal) => {
821
+ this.serverListenEvent.sigtermSignalReceived(signal);
604
822
  });
605
823
  }
606
- setupServerEvents() {
607
- if (!this.server) return;
824
+ /**
825
+ * Sets up HTTP server event listeners.
826
+ *
827
+ * @method setupServerEventListeners
828
+ * @private
829
+ *
830
+ * @returns {void}
831
+ *
832
+ * @remarks
833
+ * Configures listeners for:
834
+ * - Server errors
835
+ * - Connection closing
836
+ * - Connection dropping
837
+ * - HTTP requests (for logging)
838
+ *
839
+ * @listens Server#error - Server error events
840
+ * @listens Server#close - Server closing events
841
+ * @listens Server#drop - Connection drop events
842
+ * @listens Server#request - HTTP request events
843
+ */
844
+ setupServerEventListeners() {
845
+ loaderTranslationFile(this.localLanguage);
846
+ if (!this.server) {
847
+ return;
848
+ }
608
849
  this.server.on(import_opticore_catch_exception_error3.CEventNameError.error, (err) => {
609
850
  this.serverListenEvent.onEventError(err);
610
851
  });
@@ -626,270 +867,624 @@ var WebServerCore = class {
626
867
  );
627
868
  });
628
869
  }
629
- notifyServerReady() {
630
- setTimeout(() => {
631
- if (import_node_process3.default.send) {
632
- import_node_process3.default.send({
633
- type: "SERVER_READY",
634
- timestamp: Date.now(),
635
- port: Number(this.getEnvironment.appPort),
636
- host: this.getEnvironment.appHost,
637
- message: "Server ready for hot reload"
870
+ /**
871
+ * Sets up error handling middleware and event emitters.
872
+ *
873
+ * @method setupErrorHandling
874
+ * @private
875
+ *
876
+ * @returns {void}
877
+ */
878
+ setupErrorHandling() {
879
+ loaderTranslationFile(this.localLanguage);
880
+ SLogger(this.localLanguage).logger.info({
881
+ title: import_opticore_translator2.TranslationLoader.t("SETTING_UP", this.localLanguage),
882
+ message: import_opticore_translator2.TranslationLoader.t("SETTING_UP_ERROR", this.localLanguage)
883
+ });
884
+ this.errorEmitter = eventProcessHandler(this.localLanguage, this.expressApp);
885
+ if (this.errorEmitter) {
886
+ this.expressApp.use((err, req, res, next) => {
887
+ this.serverListenEvent.expressErrorHandlingMiddleware(
888
+ this.errorEmitter,
889
+ err,
890
+ req,
891
+ res,
892
+ next
893
+ );
894
+ });
895
+ this.errorEmitter.on("transformError", (error) => {
896
+ this.serverListenEvent.listenerError(error);
897
+ });
898
+ this.errorEmitter.on("error", (error) => {
899
+ this.serverListenEvent.listenerError(error);
900
+ });
901
+ this.errorEmitter.on("hotReload", (data) => {
902
+ SLogger(this.localLanguage).logger.info({
903
+ title: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_EVENT", this.localLanguage),
904
+ message: `Hot reload triggered for ${data.file}`
905
+ });
906
+ });
907
+ }
908
+ SLogger(this.localLanguage).logger.info({
909
+ title: import_opticore_translator2.TranslationLoader.t("ERROR_HANDLING", this.localLanguage),
910
+ message: import_opticore_translator2.TranslationLoader.t("ERROR_HANDLING_CONFIGURED", this.localLanguage)
911
+ });
912
+ }
913
+ /**
914
+ * Registers routes with the Express application.
915
+ *
916
+ * @method registerRoutes
917
+ * @private
918
+ *
919
+ * @param {any[]} allFeatureRoutes - Array of feature routes to register
920
+ *
921
+ * @returns {void}
922
+ */
923
+ registerRoutes(allFeatureRoutes) {
924
+ allFeatureRoutes.forEach((router) => {
925
+ if (router.routes) {
926
+ router.routes.forEach((route) => {
927
+ this.expressApp.use(route.path, route.handler);
638
928
  });
639
929
  }
640
- }, 100);
930
+ });
641
931
  }
642
- setupSignalHandlers() {
643
- import_node_process3.default.on("SIGHUP", async () => {
644
- if (this.serverState === "READY") {
932
+ /**
933
+ * Displays server information.
934
+ *
935
+ * @method infoWebApp
936
+ * @private
937
+ *
938
+ * @returns {void}
939
+ */
940
+ infoWebApp() {
941
+ loaderTranslationFile(this.localLanguage);
942
+ this.serverUtility.infoServer(
943
+ this.getEnvironment.appHost,
944
+ Number(this.getEnvironment.appPort)
945
+ );
946
+ }
947
+ /**
948
+ * Starts the Hot Module Replacement (HMR) file watching system.
949
+ *
950
+ * @method startHMR
951
+ * @private
952
+ *
953
+ * @returns {void}
954
+ *
955
+ * @remarks
956
+ * Configures file watcher based on environment variables:
957
+ * - HMR_ENABLED: Enable/disable HMR
958
+ * - HMR_WATCH_PATTERNS: Files to watch
959
+ * - HMR_IGNORE_PATTERNS: Files to ignore
960
+ *
961
+ * @throws {Error} If HMR configuration is invalid
962
+ */
963
+ startHMR() {
964
+ try {
965
+ loaderTranslationFile(this.localLanguage);
966
+ const hmrConfig = this.getEnvironment;
967
+ if (!hmrConfig.hmrEnabled) {
645
968
  SLogger(this.localLanguage).logger.info({
646
- title: import_opticore_translator2.TranslationLoader.t("SIGHUP", this.localLanguage),
647
- message: import_opticore_translator2.TranslationLoader.t("RECEIVED_SIGHUP", this.localLanguage)
969
+ title: import_opticore_translator2.TranslationLoader.t("HMR_DISABLED", this.localLanguage),
970
+ message: "HMR is disabled in configuration"
648
971
  });
649
- await this.performTrueHotReload();
972
+ return;
650
973
  }
651
- });
652
- import_node_process3.default.on("SIGTERM", () => {
653
- SLogger(this.localLanguage).logger.info({
654
- title: import_opticore_translator2.TranslationLoader.t("SIGHUP", this.localLanguage),
655
- message: import_opticore_translator2.TranslationLoader.t("RECEIVED_SIGHUP", this.localLanguage)
974
+ if (!hmrConfig.hmrWatchPatterns || hmrConfig.hmrWatchPatterns.length === 0) {
975
+ SLogger(this.localLanguage).logger.error({
976
+ title: import_opticore_translator2.TranslationLoader.t("HMR_WATCH_PATTERNS_MISSING", this.localLanguage),
977
+ message: "No watch patterns defined for HMR",
978
+ errorType: "HMR Configuration Error",
979
+ httpCodeValue: import_opticore_http_response3.HttpStatusCode.BAD_REQUEST
980
+ });
981
+ return;
982
+ }
983
+ const watchPatterns = hmrConfig.hmrWatchPatterns;
984
+ const ignorePatterns = hmrConfig.hmrIgnorePatterns || [
985
+ "node_modules/**",
986
+ "dist/**",
987
+ "build/**",
988
+ "*.log",
989
+ ".git/**"
990
+ ];
991
+ const allPatterns = [
992
+ ...watchPatterns,
993
+ ...ignorePatterns.map((pattern) => `!${pattern}`)
994
+ ];
995
+ this.fileWatcher = import_chokidar.default.watch(allPatterns, {
996
+ ignored: /(^|[/\\])\../,
997
+ persistent: true,
998
+ ignoreInitial: true,
999
+ awaitWriteFinish: {
1000
+ stabilityThreshold: 300,
1001
+ pollInterval: 100
1002
+ },
1003
+ cwd: import_node_process3.default.cwd(),
1004
+ depth: 10
656
1005
  });
657
- this.shutdown();
658
- });
659
- import_node_process3.default.on("SIGINT", () => {
1006
+ 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));
1007
+ this.hmrRestartCount = 0;
1008
+ this.lastHmrRestartTime = Date.now();
660
1009
  SLogger(this.localLanguage).logger.info({
661
- title: import_opticore_translator2.TranslationLoader.t("SIGINT", this.localLanguage),
662
- message: import_opticore_translator2.TranslationLoader.t("RECEIVED_SIGINT", this.localLanguage)
1010
+ title: import_opticore_translator2.TranslationLoader.t("HMR_STARTED", this.localLanguage),
1011
+ message: import_opticore_translator2.TranslationLoader.t("HMR_MONITORING_STARTED", this.localLanguage)
663
1012
  });
664
- this.shutdown();
665
- });
666
- }
667
- setupIPCHandlers() {
668
- if (!import_node_process3.default.send) {
669
- SLogger(this.localLanguage).logger.info({
670
- title: import_opticore_translator2.TranslationLoader.t("IPC", this.localLanguage),
671
- message: import_opticore_translator2.TranslationLoader.t("IPC_NOT_AVAILABLE", this.localLanguage)
1013
+ } catch (error) {
1014
+ SLogger(this.localLanguage).logger.error({
1015
+ title: import_opticore_translator2.TranslationLoader.t("HMR_START_FAILED", this.localLanguage),
1016
+ message: error.message,
1017
+ errorType: "HMR Error",
1018
+ stackTrace: error.stack
672
1019
  });
673
- return;
674
1020
  }
675
- SLogger(this.localLanguage).logger.info({
676
- title: import_opticore_translator2.TranslationLoader.t("IPC_READY", this.localLanguage),
677
- message: import_opticore_translator2.TranslationLoader.t("IPC_READY_HOT_RELOAD", this.localLanguage)
678
- });
679
- import_node_process3.default.on("message", async (message) => {
680
- if (message.type === "HOT_RELOAD_REQUEST") {
681
- if (this.serverState === "BLOCKED") {
682
- SLogger(this.localLanguage).logger.info({
683
- title: import_opticore_translator2.TranslationLoader.t("BLOCKED", this.localLanguage),
684
- message: import_opticore_translator2.TranslationLoader.t("BLOCKED_STATE", this.localLanguage)
685
- });
686
- return;
687
- }
688
- if (this.serverState === "READY") {
689
- await this.performTrueHotReload();
690
- }
691
- }
692
- });
693
1021
  }
694
1022
  /**
695
- * ✅✅✅ HOT RELOAD avec gestion d'état stricte
1023
+ * Handles file change events with debouncing.
1024
+ *
1025
+ * @method handleFileChange
1026
+ * @private
1027
+ *
1028
+ * @param {string} filePath - Path of the changed file
1029
+ * @param {string} [action="modified"] - Type of file change (modified/added/deleted)
1030
+ *
1031
+ * @returns {void}
1032
+ *
1033
+ * @remarks
1034
+ * Uses debouncing to prevent multiple rapid reloads.
1035
+ * Debounce time configurable via HMR_DEBOUNCE_MS environment variable.
696
1036
  */
697
- async performTrueHotReload() {
698
- this.serverState = "RELOADING";
699
- this.hotReloadAttempts++;
700
- const startTime = Date.now();
1037
+ handleFileChange(filePath, action = "modified") {
1038
+ const debounceMs = this.getEnvironment.hmrDebounceMs || 500;
1039
+ if (this.hmrDebounceTimeout) {
1040
+ clearTimeout(this.hmrDebounceTimeout);
1041
+ }
1042
+ this.hmrDebounceTimeout = setTimeout(async () => {
1043
+ await this.triggerHotReload(filePath, action);
1044
+ }, debounceMs);
701
1045
  SLogger(this.localLanguage).logger.info({
702
- title: import_opticore_translator2.TranslationLoader.t("RELOAD_STARTING", this.localLanguage),
703
- message: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_ATTEMPTED", this.localLanguage, { hotReloadAttempts: this.hotReloadAttempts })
1046
+ title: import_opticore_translator2.TranslationLoader.t("FILE_CHANGE_DETECTED", this.localLanguage),
1047
+ message: `File ${action}: ${filePath}`
704
1048
  });
1049
+ }
1050
+ /**
1051
+ * Triggers a hot reload operation.
1052
+ *
1053
+ * @method triggerHotReload
1054
+ * @private
1055
+ *
1056
+ * @param {string} filePath - Path of the changed file
1057
+ * @param {string} action - Type of file change
1058
+ *
1059
+ * @returns {Promise<void>}
1060
+ *
1061
+ * @remarks
1062
+ * - Checks if reload is already in progress
1063
+ * - Validates restart limits
1064
+ * - Performs appropriate reload actions based on file type
1065
+ * - Emits hotReload event
1066
+ */
1067
+ async triggerHotReload(filePath, action) {
1068
+ if (this.hmrRestartPending) {
1069
+ return;
1070
+ }
1071
+ if (!this.canProceedWithHMRRestart()) {
1072
+ return;
1073
+ }
1074
+ this.hmrRestartPending = true;
1075
+ this.hmrRestartCount++;
1076
+ this.lastHmrRestartTime = Date.now();
705
1077
  try {
706
- const clearedModules = this.clearApplicationModulesCache();
707
- SLogger(this.localLanguage).logger.info({
708
- title: import_opticore_translator2.TranslationLoader.t("APP_MODULES_CLEARED", this.localLanguage),
709
- message: import_opticore_translator2.TranslationLoader.t("CLEARED_MODULES", this.localLanguage, { clearedModules })
710
- });
711
- this.reloadConfigurations();
712
- SLogger(this.localLanguage).logger.info({
713
- title: import_opticore_translator2.TranslationLoader.t("APP_MODULES_CLEARED", this.localLanguage),
714
- message: import_opticore_translator2.TranslationLoader.t("CLEARED_MODULES", this.localLanguage, { hotReloadAttempts: this.hotReloadAttempts })
715
- });
716
- this.reloadDependencies();
1078
+ loaderTranslationFile(this.localLanguage);
717
1079
  SLogger(this.localLanguage).logger.info({
718
- title: import_opticore_translator2.TranslationLoader.t("DEPENDENCIES", this.localLanguage),
719
- message: import_opticore_translator2.TranslationLoader.t("RELOAD_DEPENDENCIES", this.localLanguage)
1080
+ title: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_STARTING", this.localLanguage),
1081
+ message: import_opticore_translator2.TranslationLoader.t("RELOADING_APPLICATION", this.localLanguage, {
1082
+ file: filePath,
1083
+ action
1084
+ })
720
1085
  });
721
- const duration = Date.now() - startTime;
722
- this.hotReloadSuccesses++;
723
- this.serverState = "READY";
724
- this.lastError = null;
725
- SLogger(this.localLanguage).logger.info({
726
- title: import_opticore_translator2.TranslationLoader.t("RELOAD_SUCCESS", this.localLanguage),
727
- message: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_SUCCESS", this.localLanguage, { duration })
1086
+ if (this.errorEmitter) {
1087
+ this.errorEmitter.emit("hotReload", {
1088
+ file: filePath,
1089
+ action,
1090
+ timestamp: /* @__PURE__ */ new Date(),
1091
+ restartCount: this.hmrRestartCount
1092
+ });
1093
+ }
1094
+ await this.performHotReloadActions(filePath);
1095
+ SLogger(this.localLanguage).logger.success({
1096
+ title: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_COMPLETE", this.localLanguage),
1097
+ message: import_opticore_translator2.TranslationLoader.t("APPLICATION_RELOADED", this.localLanguage, {
1098
+ file: filePath,
1099
+ restartCount: this.hmrRestartCount,
1100
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1101
+ })
728
1102
  });
729
- this.notifyWatcherReloadSuccess(duration);
730
1103
  } catch (error) {
731
- this.hotReloadFailures++;
732
- const duration = Date.now() - startTime;
733
- SLogger(this.localLanguage).logger.info({
734
- title: import_opticore_translator2.TranslationLoader.t("RELOAD_FAILED", this.localLanguage),
735
- message: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_FAILED", this.localLanguage, { duration, errorMessage: error.message })
1104
+ SLogger(this.localLanguage).logger.error({
1105
+ title: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_FAILED", this.localLanguage),
1106
+ message: error.message,
1107
+ errorType: "HotReloadError",
1108
+ stackTrace: error.stack,
1109
+ httpCodeValue: import_opticore_http_response3.HttpStatusCode.INTERNAL_SERVER_ERROR
736
1110
  });
737
- const isTransformError = error?.name === "TransformError" || error?.message?.includes("Transform failed") || error?.message?.includes("esbuild");
738
- if (isTransformError) {
739
- this.lastError = error;
740
- this.serverState = "BLOCKED";
741
- this.notifyWatcherBlockedByError(error);
742
- } else {
743
- this.serverState = "BLOCKED";
744
- this.notifyWatcherReloadError(error);
745
- }
1111
+ } finally {
1112
+ this.hmrRestartPending = false;
746
1113
  }
747
1114
  }
748
1115
  /**
749
- * Nettoie le cache des modules applicatifs
1116
+ * Performs appropriate hot reload actions based on file type.
1117
+ *
1118
+ * @method performHotReloadActions
1119
+ * @private
1120
+ *
1121
+ * @param {string} filePath - Path of the changed file
1122
+ *
1123
+ * @returns {Promise<void>}
1124
+ *
1125
+ * @remarks
1126
+ * Different actions for different file types:
1127
+ * - .json/.env: Reload translations
1128
+ * - routes/controller files: Reload routes
1129
+ * - config/.env files: Reload dependencies
750
1130
  */
751
- clearApplicationModulesCache() {
752
- const baseDirs = [
753
- path2.join(import_node_process3.default.cwd(), "src"),
754
- path2.join(import_node_process3.default.cwd(), "dist")
755
- ];
756
- let clearedCount = 0;
757
- for (const key in require.cache) {
758
- const isApplicationModule = baseDirs.some((dir) => key.startsWith(dir));
759
- const isNodeModule = key.includes("node_modules");
760
- const isJsonFile = key.endsWith(".json");
761
- if (isApplicationModule && !isNodeModule && !isJsonFile) {
762
- delete require.cache[key];
763
- clearedCount++;
764
- }
1131
+ async performHotReloadActions(filePath) {
1132
+ if (filePath.includes(".json") || filePath.endsWith(".env")) {
1133
+ loaderTranslationFile(this.localLanguage);
1134
+ }
1135
+ if (filePath.includes("routes") || filePath.includes("controller")) {
1136
+ await this.reloadRoutes();
1137
+ }
1138
+ if (filePath.includes("config") || filePath.includes(".env")) {
1139
+ await this.reloadDependencies();
765
1140
  }
766
- return clearedCount;
767
1141
  }
768
- reloadConfigurations() {
1142
+ /**
1143
+ * Reloads routes dynamically.
1144
+ *
1145
+ * @method reloadRoutes
1146
+ * @private
1147
+ *
1148
+ * @returns {Promise<void>}
1149
+ *
1150
+ * @throws {Error} If route reloading fails
1151
+ *
1152
+ * @remarks
1153
+ * This method should be implemented based on your architecture.
1154
+ * It should reload route modules from the filesystem.
1155
+ */
1156
+ async reloadRoutes() {
769
1157
  try {
770
- const newEnv = (0, import_opticore_env_access2.getEnvironnementValue)(this.environmentPath);
771
- Object.keys(newEnv).forEach((key) => {
772
- this.getEnvironment[key] = newEnv[key];
1158
+ SLogger(this.localLanguage).logger.info({
1159
+ title: import_opticore_translator2.TranslationLoader.t("ROUTES_RELOADED", this.localLanguage),
1160
+ message: "Routes dynamically reloaded"
773
1161
  });
774
- loaderTranslationFile(this.localLanguage);
775
1162
  } catch (error) {
776
- throw new Error(`Configuration reload failed: ${error.message}`);
1163
+ throw new Error(`Route reload failed: ${error.message}`);
777
1164
  }
778
1165
  }
779
- reloadDependencies() {
1166
+ /**
1167
+ * Reloads dependencies dynamically.
1168
+ *
1169
+ * @method reloadDependencies
1170
+ * @private
1171
+ *
1172
+ * @returns {Promise<void>}
1173
+ *
1174
+ * @throws {Error} If dependency reloading fails
1175
+ */
1176
+ async reloadDependencies() {
780
1177
  try {
781
- new import_opticore_dependency_inject2.SContainer(this.localLanguage, this.currentDependencies);
782
- const container = dependenciesContainerProvider(this.localLanguage);
783
- if (!container) {
784
- throw new Error("Dependency container is not available");
1178
+ if (this.currentDependencies.length > 0) {
1179
+ new import_opticore_dependency_inject2.SContainer(this.localLanguage, this.currentDependencies);
1180
+ SLogger(this.localLanguage).logger.info({
1181
+ title: import_opticore_translator2.TranslationLoader.t("DEPENDENCIES_RELOADED", this.localLanguage),
1182
+ message: "Dependencies reloaded"
1183
+ });
785
1184
  }
786
1185
  } catch (error) {
787
1186
  throw new Error(`Dependency reload failed: ${error.message}`);
788
1187
  }
789
1188
  }
790
1189
  /**
791
- * ✅✅✅ Notifier le watcher : SUCCÈS
1190
+ * Checks if HMR restart can proceed based on configuration limits.
1191
+ *
1192
+ * @method canProceedWithHMRRestart
1193
+ * @private
1194
+ *
1195
+ * @returns {boolean} True if restart can proceed, false otherwise
1196
+ *
1197
+ * @remarks
1198
+ * Checks:
1199
+ * - Auto-restart enabled/disabled
1200
+ * - Maximum restarts per minute limit
792
1201
  */
793
- notifyWatcherReloadSuccess(duration) {
794
- if (import_node_process3.default.send && import_node_process3.default.env.WATCHER_MODE === "true") {
795
- import_node_process3.default.send({
796
- type: "HOT_RELOAD_SUCCESS",
797
- timestamp: Date.now(),
798
- duration,
799
- serverState: this.serverState,
800
- message: "Code reloaded successfully"
1202
+ canProceedWithHMRRestart() {
1203
+ const hmrConfig = this.getEnvironment;
1204
+ if (!hmrConfig.hmrAutoRestarts) {
1205
+ SLogger(this.localLanguage).logger.warn({
1206
+ title: import_opticore_translator2.TranslationLoader.t("HMR_AUTO_RESTART_DISABLED", this.localLanguage),
1207
+ message: "HMR auto-restart disabled"
801
1208
  });
1209
+ return false;
802
1210
  }
803
- }
804
- /**
805
- * ✅✅✅ Notifier le watcher : BLOQUÉ PAR ERREUR TRANSFORM
806
- */
807
- notifyWatcherBlockedByError(error) {
808
- if (import_node_process3.default.send && import_node_process3.default.env.WATCHER_MODE === "true") {
809
- const errorDetails = this.parseTransformError(error);
810
- import_node_process3.default.send({
811
- type: "HOT_RELOAD_BLOCKED_BY_ERROR",
812
- timestamp: Date.now(),
813
- serverState: this.serverState,
814
- error: {
815
- message: error.message,
816
- errorType: error.name || "TransformError",
817
- file: errorDetails.file,
818
- line: errorDetails.line,
819
- column: errorDetails.column,
820
- detail: errorDetails.errorDetail,
821
- tool: errorDetails.tool
822
- }
1211
+ const maxRestarts = hmrConfig.hmrMaxRestarts || 5;
1212
+ const now = Date.now();
1213
+ const oneMinuteAgo = now - 6e4;
1214
+ if (this.lastHmrRestartTime < oneMinuteAgo) {
1215
+ this.hmrRestartCount = 0;
1216
+ }
1217
+ if (this.hmrRestartCount >= maxRestarts) {
1218
+ const nextReset = Math.ceil((this.lastHmrRestartTime + 6e4 - now) / 1e3);
1219
+ SLogger(this.localLanguage).logger.error({
1220
+ title: import_opticore_translator2.TranslationLoader.t("HMR_RESTART_LIMIT_EXCEEDED", this.localLanguage),
1221
+ message: import_opticore_translator2.TranslationLoader.t("HMR_RESTART_LIMIT_MESSAGE", this.localLanguage, {
1222
+ max: maxRestarts,
1223
+ nextReset
1224
+ })
823
1225
  });
1226
+ return false;
824
1227
  }
1228
+ return true;
825
1229
  }
826
1230
  /**
827
- * ✅✅✅ Notifier le watcher : ERREUR GÉNÉRIQUE
1231
+ * Called when the HMR watcher is ready.
1232
+ *
1233
+ * @method onHMRWatcherReady
1234
+ * @private
1235
+ *
1236
+ * @param {string[]} watchPatterns - Patterns being watched
1237
+ * @param {string[]} ignorePatterns - Patterns being ignored
1238
+ *
1239
+ * @returns {void}
828
1240
  */
829
- notifyWatcherReloadError(error) {
830
- if (import_node_process3.default.send && import_node_process3.default.env.WATCHER_MODE === "true") {
831
- import_node_process3.default.send({
832
- type: "HOT_RELOAD_ERROR",
833
- timestamp: Date.now(),
834
- serverState: this.serverState,
835
- error: error.message,
836
- errorType: error.name || "UNKNOWN_ERROR"
837
- });
838
- }
1241
+ onHMRWatcherReady(watchPatterns, ignorePatterns) {
1242
+ SLogger(this.localLanguage).logger.info({
1243
+ title: import_opticore_translator2.TranslationLoader.t("HMR_WATCHER_READY", this.localLanguage),
1244
+ message: import_opticore_translator2.TranslationLoader.t("HMR_WATCHING_DETAILS", this.localLanguage, {
1245
+ watchCount: watchPatterns,
1246
+ ignoreCount: ignorePatterns
1247
+ })
1248
+ });
839
1249
  }
840
1250
  /**
841
- * ✅✅✅ Parser les détails d'une TransformError
1251
+ * Handles HMR watcher errors.
1252
+ *
1253
+ * @method onHMRWatcherError
1254
+ * @private
1255
+ *
1256
+ * @param {Error} error - The watcher error
1257
+ *
1258
+ * @returns {void}
842
1259
  */
843
- parseTransformError(error) {
844
- const errorMessage = error.message || String(error);
845
- let tool = "Unknown";
846
- if (errorMessage.includes("esbuild")) tool = "esbuild";
847
- if (errorMessage.includes("webpack")) tool = "webpack";
848
- const fileMatch = errorMessage.match(/([^:\s]+\.(?:ts|js|tsx|jsx)):(\d+):(\d+):/);
849
- const file = fileMatch ? fileMatch[1] : null;
850
- const line = fileMatch ? parseInt(fileMatch[2], 10) : null;
851
- const column = fileMatch ? parseInt(fileMatch[3], 10) : null;
852
- const errorDetailMatch = errorMessage.match(/ERROR:\s*(.+?)(?:\n|$)/);
853
- const errorDetail = errorDetailMatch ? errorDetailMatch[1].trim() : null;
854
- return {
855
- message: errorMessage,
856
- file,
857
- line,
858
- column,
859
- errorDetail,
860
- tool
861
- };
862
- }
863
- registerRoutes(allFeatureRoutes) {
864
- allFeatureRoutes.forEach((router) => {
865
- if (router.routes) {
866
- router.routes.forEach((route) => {
867
- this.expressApp.use(route.path, route.handler);
868
- });
869
- }
1260
+ onHMRWatcherError(error) {
1261
+ SLogger(this.localLanguage).logger.error({
1262
+ title: import_opticore_translator2.TranslationLoader.t("HMR_WATCHER_ERROR", this.localLanguage),
1263
+ message: error.message,
1264
+ errorType: "FileWatcherError",
1265
+ stackTrace: error.stack
870
1266
  });
871
1267
  }
872
- infoWebApp() {
873
- this.serverUtility.infoServer(
874
- this.getEnvironment.appHost,
875
- Number(this.getEnvironment.appPort)
876
- );
1268
+ /**
1269
+ * Stops the HMR system.
1270
+ *
1271
+ * @method stopHMR
1272
+ * @private
1273
+ *
1274
+ * @returns {void}
1275
+ */
1276
+ stopHMR() {
1277
+ if (this.fileWatcher) {
1278
+ this.fileWatcher.close();
1279
+ this.fileWatcher = null;
1280
+ }
1281
+ if (this.hmrDebounceTimeout) {
1282
+ clearTimeout(this.hmrDebounceTimeout);
1283
+ this.hmrDebounceTimeout = null;
1284
+ }
1285
+ SLogger(this.localLanguage).logger.info({
1286
+ title: import_opticore_translator2.TranslationLoader.t("HMR_STOPPED", this.localLanguage),
1287
+ message: import_opticore_translator2.TranslationLoader.t("HMR_MONITORING_STOPPED", this.localLanguage)
1288
+ });
877
1289
  }
878
- shutdown() {
1290
+ /**
1291
+ * Stops the server and HMR system cleanly.
1292
+ *
1293
+ * @method onStopServer
1294
+ * @public
1295
+ *
1296
+ * @returns {void}
1297
+ */
1298
+ onStopServer() {
1299
+ this.stopHMR();
879
1300
  if (this.server) {
880
1301
  this.server.close(() => {
881
1302
  SLogger(this.localLanguage).logger.info({
882
- title: import_opticore_translator2.TranslationLoader.t("CLOSED", this.localLanguage),
883
- message: import_opticore_translator2.TranslationLoader.t("SERVER_CLOSED", this.localLanguage)
1303
+ title: import_opticore_translator2.TranslationLoader.t("SERVER_STOPPED", this.localLanguage),
1304
+ message: "Server stopped cleanly"
884
1305
  });
885
- import_node_process3.default.exit(0);
886
1306
  });
887
- setTimeout(() => {
888
- import_node_process3.default.exit(1);
889
- }, 5e3);
1307
+ this.serverStatus = "STOPPED";
1308
+ }
1309
+ }
1310
+ /**
1311
+ * Gets the current server state information.
1312
+ *
1313
+ * @method getServerState
1314
+ * @public
1315
+ *
1316
+ * @returns {IServerStateInfo} Server state information object
1317
+ *
1318
+ * @remarks
1319
+ * Includes:
1320
+ * - Status, host, port
1321
+ * - Route and dependency counts
1322
+ * - Uptime and memory usage
1323
+ * - HMR configuration and statistics
1324
+ */
1325
+ getServerState() {
1326
+ const now = /* @__PURE__ */ new Date();
1327
+ const uptime = this.serverStartTime ? now.getTime() - this.serverStartTime.getTime() : 0;
1328
+ return {
1329
+ status: this.serverStatus,
1330
+ isRunning: this.server !== void 0 && this.serverStatus === "READY",
1331
+ host: this.getEnvironment.appHost,
1332
+ port: Number(this.getEnvironment.appPort),
1333
+ language: this.localLanguage,
1334
+ routesCount: this.currentRoutes.length,
1335
+ dependenciesCount: this.currentDependencies.length,
1336
+ startTime: this.serverStartTime,
1337
+ currentTime: now,
1338
+ uptime,
1339
+ uptimeFormatted: this.formatUptime(uptime),
1340
+ memoryUsage: import_node_process3.default.memoryUsage(),
1341
+ pid: import_node_process3.default.pid,
1342
+ platform: import_node_process3.default.platform,
1343
+ nodeVersion: import_node_process3.default.version,
1344
+ cwd: import_node_process3.default.cwd(),
1345
+ hmrEnabled: this.getEnvironment.hmrEnabled,
1346
+ hmrWatchingFiles: this.getEnvironment.hmrWatchPatterns?.length || 0,
1347
+ hmrRestartCount: this.hmrRestartCount
1348
+ };
1349
+ }
1350
+ /**
1351
+ * Gets the current server status.
1352
+ *
1353
+ * @method getServerStatus
1354
+ * @public
1355
+ *
1356
+ * @returns {TServerStatus} Current server status
1357
+ */
1358
+ getServerStatus() {
1359
+ return this.serverStatus;
1360
+ }
1361
+ /**
1362
+ * Gets detailed server statistics.
1363
+ *
1364
+ * @method getServerStats
1365
+ * @public
1366
+ *
1367
+ * @returns {IServerStats} Server statistics object
1368
+ *
1369
+ * @remarks
1370
+ * Includes:
1371
+ * - Performance metrics (CPU, memory)
1372
+ * - Uptime information
1373
+ * - HMR statistics
1374
+ */
1375
+ getServerStats() {
1376
+ const uptime = this.serverStartTime ? Date.now() - this.serverStartTime.getTime() : 0;
1377
+ const memory = import_node_process3.default.memoryUsage();
1378
+ return {
1379
+ status: this.serverStatus,
1380
+ uptime: this.formatUptime(uptime),
1381
+ memory: {
1382
+ rss: this.formatBytes(memory.rss),
1383
+ heapTotal: this.formatBytes(memory.heapTotal),
1384
+ heapUsed: this.formatBytes(memory.heapUsed),
1385
+ external: this.formatBytes(memory.external)
1386
+ },
1387
+ performance: {
1388
+ cpuUsage: import_node_process3.default.cpuUsage(),
1389
+ resourceUsage: import_node_process3.default.resourceUsage?.()
1390
+ },
1391
+ hmrStats: {
1392
+ enabled: this.getEnvironment.hmrEnabled,
1393
+ restartCount: this.hmrRestartCount,
1394
+ lastRestartTime: this.lastHmrRestartTime,
1395
+ watchingFiles: this.getEnvironment.hmrWatchPatterns?.length || 0
1396
+ }
1397
+ };
1398
+ }
1399
+ /**
1400
+ * Formats milliseconds into a human-readable uptime string.
1401
+ *
1402
+ * @method formatUptime
1403
+ * @private
1404
+ *
1405
+ * @param {number} ms - Milliseconds to format
1406
+ *
1407
+ * @returns {string} Formatted uptime string
1408
+ *
1409
+ * @example
1410
+ * formatUptime(3661000) // returns "1h 1m 1s"
1411
+ */
1412
+ formatUptime(ms) {
1413
+ const seconds = Math.floor(ms / 1e3);
1414
+ const minutes = Math.floor(seconds / 60);
1415
+ const hours = Math.floor(minutes / 60);
1416
+ const days = Math.floor(hours / 24);
1417
+ if (days > 0) {
1418
+ return `${days}d ${hours % 24}h ${minutes % 60}m`;
1419
+ } else if (hours > 0) {
1420
+ return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
1421
+ } else if (minutes > 0) {
1422
+ return `${minutes}m ${seconds % 60}s`;
890
1423
  } else {
891
- import_node_process3.default.exit(0);
1424
+ return `${seconds}s`;
1425
+ }
1426
+ }
1427
+ /**
1428
+ * Formats bytes into a human-readable size string.
1429
+ *
1430
+ * @method formatBytes
1431
+ * @private
1432
+ *
1433
+ * @param {number} bytes - Bytes to format
1434
+ *
1435
+ * @returns {string} Formatted size string
1436
+ *
1437
+ * @example
1438
+ * formatBytes(1048576) // returns "1.00 MB"
1439
+ */
1440
+ formatBytes(bytes) {
1441
+ const units = ["B", "KB", "MB", "GB", "TB"];
1442
+ let value = bytes;
1443
+ let unitIndex = 0;
1444
+ while (value >= 1024 && unitIndex < units.length - 1) {
1445
+ value /= 1024;
1446
+ unitIndex++;
892
1447
  }
1448
+ return `${value.toFixed(2)} ${units[unitIndex]}`;
1449
+ }
1450
+ /**
1451
+ * Checks if HMR is currently active.
1452
+ *
1453
+ * @method isHMRActive
1454
+ * @public
1455
+ *
1456
+ * @returns {boolean} True if HMR is enabled and watching files, false otherwise
1457
+ */
1458
+ isHMRActive() {
1459
+ return this.getEnvironment.hmrEnabled && this.fileWatcher !== null;
1460
+ }
1461
+ /**
1462
+ * Gets detailed HMR information and status.
1463
+ *
1464
+ * @method getHMRInfo
1465
+ * @public
1466
+ *
1467
+ * @returns {any} HMR information object
1468
+ *
1469
+ * @remarks
1470
+ * Includes:
1471
+ * - Configuration settings from .env
1472
+ * - Current restart count
1473
+ * - Watcher status
1474
+ */
1475
+ getHMRInfo() {
1476
+ return {
1477
+ enabled: this.getEnvironment.hmrEnabled,
1478
+ watchPatterns: this.getEnvironment.hmrWatchPatterns,
1479
+ ignorePatterns: this.getEnvironment.hmrIgnorePatterns,
1480
+ debounceMs: this.getEnvironment.hmrDebounceMs,
1481
+ maxRestarts: this.getEnvironment.hmrMaxRestarts,
1482
+ autoRestart: this.getEnvironment.hmrAutoRestarts,
1483
+ currentRestartCount: this.hmrRestartCount,
1484
+ lastRestartTime: this.lastHmrRestartTime,
1485
+ isWatching: this.fileWatcher !== null,
1486
+ isRestartPending: this.hmrRestartPending
1487
+ };
893
1488
  }
894
1489
  };
895
1490
  // Annotate the CommonJS export names for ESM import in node: