opticore-webapp 1.0.68 → 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,8 +39,9 @@ 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");
45
46
  var import_opticore_request_call_event = require("opticore-request-call-event");
46
47
  var import_opticore_dependency_inject2 = require("opticore-dependency-inject");
@@ -476,55 +477,149 @@ var SServerStartError = (err, environmentPath) => {
476
477
  };
477
478
 
478
479
  // src/core/webServer.core.ts
479
- var import_opticore_watcher = require("opticore-watcher");
480
480
  var WebServerCore = class {
481
481
  serverUtility;
482
- expressApp = (0, import_opticore_express.express)();
483
- fileWatcher;
482
+ expressApp;
484
483
  localLanguage;
485
484
  loggerConfig;
486
- routerExpressApp;
487
485
  getEnvironment;
488
486
  environmentPath;
489
487
  serverListenEvent;
488
+ // Existing properties
490
489
  currentRoutes = [];
491
490
  currentDependencies = [];
492
491
  server = void 0;
493
492
  errorEmitter;
494
- isWatcherEnabled = true;
495
493
  serverStatus = "STARTING";
496
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
+ */
497
528
  constructor(paramsConstructor) {
498
- this.getEnvironment = (0, import_opticore_env_access2.getEnvironnementValue)(paramsConstructor.environmentPath);
499
- this.routerExpressApp = paramsConstructor.app;
529
+ this.getEnvironment = (0, import_opticore_env_access2.getEnvironmentValue)(paramsConstructor.environmentPath);
500
530
  this.loggerConfig = paramsConstructor.loggerConfig;
501
531
  this.localLanguage = paramsConstructor.localLanguage;
502
532
  this.environmentPath = paramsConstructor.environmentPath;
503
- this.expressApp.use(import_opticore_express.express.json());
504
- this.expressApp.use(import_opticore_express.express.raw());
505
- this.expressApp.use(import_opticore_express.express.text());
506
- this.expressApp.use(import_opticore_express.express.urlencoded({ extended: true }));
507
- this.expressApp.use((0, import_cors.default)(paramsConstructor.corsOriginOptions));
533
+ this.expressApp = (0, import_express.default)();
508
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));
509
540
  this.serverUtility = new CoreService(paramsConstructor.localLanguage, paramsConstructor.environmentPath);
541
+ this.serverStatus = "STARTING";
542
+ this.serverStartTime = /* @__PURE__ */ new Date();
543
+ this.setupProcessEventListeners();
510
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
+ */
511
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() {
512
612
  loaderTranslationFile(this.localLanguage);
513
- this.currentRoutes = routers;
514
- this.currentDependencies = dependenciesProvider || [];
515
- if (this.getEnvironment.appPort === "" && Number(this.getEnvironment.appPort) === 0) {
516
- this.serverListenEvent.hostPortUndefined(Number(this.getEnvironment.appPort));
517
- return void 0;
613
+ const port = Number(this.getEnvironment.appPort);
614
+ if (isNaN(port) || port <= 0) {
615
+ this.serverListenEvent.portUndefined();
616
+ return false;
518
617
  }
519
- if (this.getEnvironment.appHost === "") {
618
+ if (!this.getEnvironment.appHost || this.getEnvironment.appHost.trim() === "") {
520
619
  this.serverListenEvent.hostUndefined(this.getEnvironment.appHost);
521
- return void 0;
522
- }
523
- if (Number(this.getEnvironment.appPort) === 0) {
524
- this.serverListenEvent.portUndefined();
525
- return void 0;
620
+ return false;
526
621
  }
527
- if (this.localLanguage === "") {
622
+ if (!this.localLanguage || this.localLanguage.trim() === "") {
528
623
  SLogger(this.localLanguage).logger.error({
529
624
  message: import_opticore_translator2.TranslationLoader.t("noDefaultLocalLang", this.localLanguage),
530
625
  title: import_opticore_translator2.TranslationLoader.t("noLocalLang", this.localLanguage),
@@ -532,81 +627,225 @@ var WebServerCore = class {
532
627
  stackTrace: void 0,
533
628
  httpCodeValue: import_opticore_http_response3.HttpStatusCode.NOT_FOUND
534
629
  });
535
- return void 0;
630
+ return false;
536
631
  }
537
- this.server = this.expressApp.listen(
538
- Number(this.getEnvironment.appPort),
539
- this.getEnvironment.appHost,
540
- () => {
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, () => {
541
660
  try {
542
- if (databaseCallback && typeof databaseCallback === "function") {
543
- databaseCallback(this.getEnvironment);
544
- }
545
- new import_opticore_dependency_inject2.SContainer(this.localLanguage, this.currentDependencies);
546
- this.expressApp.use(import_opticore_express.express.static(path2.join(import_node_process3.default.cwd(), "public/template")));
547
- this.registerRoutes(this.currentRoutes);
548
- this.setupErrorHandling();
549
- this.setupServerEvents();
550
- if (this.isWatcherEnabled) {
551
- this.initializeFileWatcher();
552
- }
553
- this.infoWebApp();
554
- } catch (err) {
555
- SLogger(this.localLanguage).logger.error({
556
- title: import_opticore_translator2.TranslationLoader.t("STARTUP_ERROR", this.localLanguage),
557
- message: err.message,
558
- errorType: err.code,
559
- stackTrace: err.stackTrace,
560
- httpCodeValue: import_opticore_http_response3.HttpStatusCode.INTERNAL_SERVER_ERROR
661
+ this.configureServerComponents(databaseCallback);
662
+ this.serverStatus = "READY";
663
+ SLogger(this.localLanguage).logger.info({
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
+ })
561
669
  });
562
- SServerStartError(err, this.environmentPath);
670
+ } catch (err) {
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
+ );
563
708
  }
564
709
  }
565
- );
566
- 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
+ }
567
727
  }
568
728
  /**
569
- * Error handling configuration
729
+ * Handles server configuration errors.
730
+ *
731
+ * @method handleServerConfigurationError
732
+ * @private
733
+ *
734
+ * @param {any} err - The error that occurred
735
+ *
736
+ * @returns {void}
570
737
  */
571
- setupErrorHandling() {
572
- SLogger(this.localLanguage).logger.info({
573
- title: import_opticore_translator2.TranslationLoader.t("SETTING_UP", this.localLanguage),
574
- message: import_opticore_translator2.TranslationLoader.t("SETTING_UP_ERROR", this.localLanguage)
575
- });
576
- this.errorEmitter = eventProcessHandler(this.localLanguage, this.expressApp);
577
- if (this.errorEmitter) {
578
- this.errorEmitter.on("transformError", (error) => {
579
- SLogger(this.localLanguage).logger.error({
580
- title: import_opticore_translator2.TranslationLoader.t("TRANSFORM_ERROR", this.localLanguage),
581
- message: error.message,
582
- errorType: error.name,
583
- stackTrace: error.stackTrace,
584
- httpCodeValue: import_opticore_http_response3.HttpStatusCode.INTERNAL_SERVER_ERROR
585
- });
586
- });
587
- this.errorEmitter.on("error", (error) => {
588
- SLogger(this.localLanguage).logger.info({
589
- title: import_opticore_translator2.TranslationLoader.t("ERROR_EMITTED", this.localLanguage),
590
- message: error.message
591
- });
592
- });
593
- this.errorEmitter.on("hotReload", (data) => {
594
- SLogger(this.localLanguage).logger.info({
595
- title: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_EVENT", this.localLanguage),
596
- message: `Hot reload triggered for ${data.file}`
597
- });
598
- });
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);
599
746
  }
600
- SLogger(this.localLanguage).logger.info({
601
- title: import_opticore_translator2.TranslationLoader.t("ERROR_HANDLING", this.localLanguage),
602
- 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
603
768
  });
604
769
  }
605
770
  /**
606
- * Setup server events
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)
607
784
  */
608
- setupServerEvents() {
609
- if (!this.server) return;
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);
822
+ });
823
+ }
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
+ }
610
849
  this.server.on(import_opticore_catch_exception_error3.CEventNameError.error, (err) => {
611
850
  this.serverListenEvent.onEventError(err);
612
851
  });
@@ -629,544 +868,509 @@ var WebServerCore = class {
629
868
  });
630
869
  }
631
870
  /**
632
- * Initialize file watcher
633
- */
634
- initializeFileWatcher() {
635
- try {
636
- this.fileWatcher = new import_opticore_watcher.WebServerWatcherService(this.localLanguage);
637
- this.setupWatcherEvents();
638
- this.fileWatcher.startWatching();
639
- this.fileWatcher.setHotReload(true);
640
- SLogger(this.localLanguage).logger.info({
641
- title: import_opticore_translator2.TranslationLoader.t("WATCHER_INITIALIZED", this.localLanguage),
642
- message: "File watcher has been initialized successfully"
643
- });
644
- } catch (error) {
645
- SLogger(this.localLanguage).logger.error({
646
- title: import_opticore_translator2.TranslationLoader.t("WATCHER_INIT_ERROR", this.localLanguage),
647
- message: `Failed to initialize file watcher: ${error.message}`,
648
- errorType: "WatcherInitializationError",
649
- httpCodeValue: import_opticore_http_response3.HttpStatusCode.INTERNAL_SERVER_ERROR
650
- });
651
- }
652
- }
653
- /**
654
- * Setup watcher events
871
+ * Sets up error handling middleware and event emitters.
872
+ *
873
+ * @method setupErrorHandling
874
+ * @private
875
+ *
876
+ * @returns {void}
655
877
  */
656
- setupWatcherEvents() {
657
- if (!this.fileWatcher) return;
658
- try {
659
- this.fileWatcher.on("fileChangeDetected", (data) => {
660
- this.handleFileChange(data);
661
- });
662
- this.fileWatcher.on("hotReloadRequired", (data) => {
663
- this.handleHotReload(data);
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
+ );
664
894
  });
665
- this.fileWatcher.on("watcherError", (error) => {
666
- this.handleWatcherError(error);
895
+ this.errorEmitter.on("transformError", (error) => {
896
+ this.serverListenEvent.listenerError(error);
667
897
  });
668
- this.fileWatcher.on("started", () => {
669
- SLogger(this.localLanguage).logger.info({
670
- title: import_opticore_translator2.TranslationLoader.t("WATCHER_STARTED", this.localLanguage),
671
- message: import_opticore_translator2.TranslationLoader.t("FILE_WATCHER_ACTIVE", this.localLanguage)
672
- });
898
+ this.errorEmitter.on("error", (error) => {
899
+ this.serverListenEvent.listenerError(error);
673
900
  });
674
- this.fileWatcher.on("stopped", () => {
901
+ this.errorEmitter.on("hotReload", (data) => {
675
902
  SLogger(this.localLanguage).logger.info({
676
- title: import_opticore_translator2.TranslationLoader.t("WATCHER_STOPPED", this.localLanguage),
677
- message: import_opticore_translator2.TranslationLoader.t("FILE_WATCHER_INACTIVE", this.localLanguage)
903
+ title: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_EVENT", this.localLanguage),
904
+ message: `Hot reload triggered for ${data.file}`
678
905
  });
679
906
  });
680
- } catch (error) {
681
- SLogger(this.localLanguage).logger.error({
682
- title: import_opticore_translator2.TranslationLoader.t("WATCHER_EVENTS_ERROR", this.localLanguage),
683
- message: `Failed to setup watcher events: ${error.message}`,
684
- errorType: "WatcherEventsError",
685
- httpCodeValue: import_opticore_http_response3.HttpStatusCode.INTERNAL_SERVER_ERROR
686
- });
687
- }
688
- }
689
- /**
690
- * Handle file changes detected by watcher
691
- */
692
- handleFileChange(data) {
693
- const { event: event2, action, requiresReload } = data;
694
- SLogger(this.localLanguage).logger.info({
695
- title: import_opticore_translator2.TranslationLoader.t("FILE_CHANGE_HANDLED", this.localLanguage),
696
- message: import_opticore_translator2.TranslationLoader.t(
697
- "PROCESSING_FILE_CHANGE",
698
- this.localLanguage
699
- ).replace(
700
- "{file}",
701
- path2.basename(event2.filePath)
702
- ).replace(
703
- "{action}",
704
- action
705
- )
706
- });
707
- if (this.errorEmitter) {
708
- this.errorEmitter.emit("fileChanged", {
709
- file: event2.filePath,
710
- type: event2.extension,
711
- action,
712
- timestamp: /* @__PURE__ */ new Date()
713
- });
714
- }
715
- switch (action) {
716
- case "reloadEnvironment":
717
- this.reloadEnvironmentConfig();
718
- break;
719
- case "reloadConfig":
720
- this.reloadConfigurationFiles();
721
- break;
722
- case "reloadRoutes":
723
- this.reloadApplicationRoutes(event2.filePath);
724
- break;
725
- case "reloadDependencies":
726
- this.reloadDependencies();
727
- break;
728
- case "notifyOnly":
729
- this.notifyFileChange(event2);
730
- break;
731
907
  }
732
- }
733
- /**
734
- * Handle hot reload requested by watcher
735
- */
736
- handleHotReload(data) {
737
- const { file, type, action } = data;
738
908
  SLogger(this.localLanguage).logger.info({
739
- title: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_STARTING", this.localLanguage),
740
- message: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_FOR_FILE", this.localLanguage).replace("{file}", path2.basename(file)).replace("{type}", type)
741
- });
742
- if (this.errorEmitter) {
743
- this.errorEmitter.emit("hotReload", {
744
- file,
745
- type,
746
- action,
747
- timestamp: /* @__PURE__ */ new Date()
748
- });
749
- }
750
- switch (action) {
751
- case "reloadEnvironment":
752
- this.executeHotReloadEnvironment(file);
753
- break;
754
- case "reloadConfig":
755
- this.executeHotReloadConfig(file);
756
- break;
757
- case "reloadRoutes":
758
- this.executeHotReloadRoutes(file);
759
- break;
760
- case "reloadDependencies":
761
- this.executeHotReloadDependencies(file);
762
- break;
763
- }
764
- }
765
- /**
766
- * Handle watcher errors
767
- */
768
- handleWatcherError(error) {
769
- SLogger(this.localLanguage).logger.error({
770
- title: import_opticore_translator2.TranslationLoader.t("WATCHER_SYSTEM_ERROR", this.localLanguage),
771
- message: error.message || "Unknown watcher system error",
772
- errorType: "WatcherSystemError",
773
- stackTrace: error.stack,
774
- httpCodeValue: import_opticore_http_response3.HttpStatusCode.INTERNAL_SERVER_ERROR
909
+ title: import_opticore_translator2.TranslationLoader.t("ERROR_HANDLING", this.localLanguage),
910
+ message: import_opticore_translator2.TranslationLoader.t("ERROR_HANDLING_CONFIGURED", this.localLanguage)
775
911
  });
776
912
  }
777
913
  /**
778
- * Notify file change without action
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}
779
922
  */
780
- notifyFileChange(event2) {
781
- SLogger(this.localLanguage).logger.info({
782
- title: import_opticore_translator2.TranslationLoader.t("FILE_CHANGE_NOTIFIED", this.localLanguage),
783
- message: import_opticore_translator2.TranslationLoader.t("FILE_CHANGE_NO_ACTION", this.localLanguage).replace("{file}", path2.basename(event2.filePath))
923
+ registerRoutes(allFeatureRoutes) {
924
+ allFeatureRoutes.forEach((router) => {
925
+ if (router.routes) {
926
+ router.routes.forEach((route) => {
927
+ this.expressApp.use(route.path, route.handler);
928
+ });
929
+ }
784
930
  });
785
931
  }
786
932
  /**
787
- * Reload environment configuration
933
+ * Displays server information.
934
+ *
935
+ * @method infoWebApp
936
+ * @private
937
+ *
938
+ * @returns {void}
788
939
  */
789
- reloadEnvironmentConfig() {
790
- try {
791
- this.reloadConfigurations();
792
- SLogger(this.localLanguage).logger.info({
793
- title: import_opticore_translator2.TranslationLoader.t("ENV_RELOADED", this.localLanguage),
794
- message: import_opticore_translator2.TranslationLoader.t("ENVIRONMENT_RELOADED_SUCCESS", this.localLanguage)
795
- });
796
- this.notifyEnvironmentReload();
797
- } catch (error) {
798
- SLogger(this.localLanguage).logger.error({
799
- title: import_opticore_translator2.TranslationLoader.t("ENV_RELOAD_FAILED", this.localLanguage),
800
- message: error.message,
801
- errorType: "EnvironmentReloadError",
802
- httpCodeValue: import_opticore_http_response3.HttpStatusCode.INTERNAL_SERVER_ERROR
803
- });
804
- }
940
+ infoWebApp() {
941
+ loaderTranslationFile(this.localLanguage);
942
+ this.serverUtility.infoServer(
943
+ this.getEnvironment.appHost,
944
+ Number(this.getEnvironment.appPort)
945
+ );
805
946
  }
806
947
  /**
807
- * Execute hot reload for environment files
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
808
962
  */
809
- executeHotReloadEnvironment(filePath) {
963
+ startHMR() {
810
964
  try {
811
- const newEnv = (0, import_opticore_env_access2.getEnvironnementValue)(this.environmentPath);
812
- Object.keys(newEnv).forEach((key) => {
813
- this.getEnvironment[key] = newEnv[key];
814
- });
815
965
  loaderTranslationFile(this.localLanguage);
816
- SLogger(this.localLanguage).logger.info({
817
- title: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_ENV_SUCCESS", this.localLanguage),
818
- message: import_opticore_translator2.TranslationLoader.t("ENV_HOT_RELOAD_COMPLETE", this.localLanguage).replace("{file}", path2.basename(filePath))
819
- });
820
- } catch (error) {
821
- SLogger(this.localLanguage).logger.error({
822
- title: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_ENV_FAILED", this.localLanguage),
823
- message: error.message,
824
- errorType: "HotReloadEnvironmentError",
825
- httpCodeValue: import_opticore_http_response3.HttpStatusCode.INTERNAL_SERVER_ERROR
966
+ const hmrConfig = this.getEnvironment;
967
+ if (!hmrConfig.hmrEnabled) {
968
+ SLogger(this.localLanguage).logger.info({
969
+ title: import_opticore_translator2.TranslationLoader.t("HMR_DISABLED", this.localLanguage),
970
+ message: "HMR is disabled in configuration"
971
+ });
972
+ return;
973
+ }
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
826
1005
  });
827
- }
828
- }
829
- /**
830
- * Reload configuration files
831
- */
832
- reloadConfigurationFiles() {
833
- try {
834
- loaderTranslationFile(this.localLanguage);
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();
835
1009
  SLogger(this.localLanguage).logger.info({
836
- title: import_opticore_translator2.TranslationLoader.t("CONFIG_RELOADED", this.localLanguage),
837
- message: import_opticore_translator2.TranslationLoader.t("CONFIGURATION_RELOADED_SUCCESS", 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)
838
1012
  });
839
1013
  } catch (error) {
840
1014
  SLogger(this.localLanguage).logger.error({
841
- title: import_opticore_translator2.TranslationLoader.t("CONFIG_RELOAD_FAILED", this.localLanguage),
1015
+ title: import_opticore_translator2.TranslationLoader.t("HMR_START_FAILED", this.localLanguage),
842
1016
  message: error.message,
843
- errorType: "ConfigurationReloadError",
844
- httpCodeValue: import_opticore_http_response3.HttpStatusCode.INTERNAL_SERVER_ERROR
1017
+ errorType: "HMR Error",
1018
+ stackTrace: error.stack
845
1019
  });
846
1020
  }
847
1021
  }
848
1022
  /**
849
- * Execute hot reload for config files
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.
850
1036
  */
851
- executeHotReloadConfig(filePath) {
852
- try {
853
- if (filePath.includes("locales") || filePath.includes("translations")) {
854
- loaderTranslationFile(this.localLanguage);
855
- SLogger(this.localLanguage).logger.info({
856
- title: import_opticore_translator2.TranslationLoader.t("TRANSLATIONS_RELOADED", this.localLanguage),
857
- message: import_opticore_translator2.TranslationLoader.t("TRANSLATIONS_HOT_RELOAD_COMPLETE", this.localLanguage).replace("{file}", path2.basename(filePath))
858
- });
859
- }
860
- } catch (error) {
861
- SLogger(this.localLanguage).logger.error({
862
- title: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_CONFIG_FAILED", this.localLanguage),
863
- message: error.message,
864
- errorType: "HotReloadConfigError",
865
- httpCodeValue: import_opticore_http_response3.HttpStatusCode.INTERNAL_SERVER_ERROR
866
- });
1037
+ handleFileChange(filePath, action = "modified") {
1038
+ const debounceMs = this.getEnvironment.hmrDebounceMs || 500;
1039
+ if (this.hmrDebounceTimeout) {
1040
+ clearTimeout(this.hmrDebounceTimeout);
867
1041
  }
1042
+ this.hmrDebounceTimeout = setTimeout(async () => {
1043
+ await this.triggerHotReload(filePath, action);
1044
+ }, debounceMs);
1045
+ SLogger(this.localLanguage).logger.info({
1046
+ title: import_opticore_translator2.TranslationLoader.t("FILE_CHANGE_DETECTED", this.localLanguage),
1047
+ message: `File ${action}: ${filePath}`
1048
+ });
868
1049
  }
869
1050
  /**
870
- * Reload application routes
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
871
1066
  */
872
- reloadApplicationRoutes(filePath) {
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();
873
1077
  try {
1078
+ loaderTranslationFile(this.localLanguage);
874
1079
  SLogger(this.localLanguage).logger.info({
875
- title: import_opticore_translator2.TranslationLoader.t("ROUTES_RELOADING", this.localLanguage),
876
- message: import_opticore_translator2.TranslationLoader.t("APPLICATION_ROUTES_RELOADING", this.localLanguage).replace("{file}", path2.basename(filePath))
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
+ })
877
1085
  });
878
- if (filePath.includes("routes") || filePath.includes("controller")) {
879
- this.reloadSpecificRoute(filePath);
880
- }
881
1086
  if (this.errorEmitter) {
882
- this.errorEmitter.emit("routesReloaded", {
883
- timestamp: /* @__PURE__ */ new Date(),
1087
+ this.errorEmitter.emit("hotReload", {
884
1088
  file: filePath,
885
- routesCount: this.currentRoutes.length
1089
+ action,
1090
+ timestamp: /* @__PURE__ */ new Date(),
1091
+ restartCount: this.hmrRestartCount
886
1092
  });
887
1093
  }
888
- } catch (error) {
889
- SLogger(this.localLanguage).logger.error({
890
- title: import_opticore_translator2.TranslationLoader.t("ROUTES_RELOAD_FAILED", this.localLanguage),
891
- message: error.message,
892
- errorType: "RoutesReloadError",
893
- httpCodeValue: import_opticore_http_response3.HttpStatusCode.INTERNAL_SERVER_ERROR
894
- });
895
- }
896
- }
897
- /**
898
- * Execute hot reload for routes
899
- */
900
- executeHotReloadRoutes(filePath) {
901
- try {
902
- SLogger(this.localLanguage).logger.info({
903
- title: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_ROUTES_START", this.localLanguage),
904
- message: import_opticore_translator2.TranslationLoader.t("ROUTES_HOT_RELOADING", this.localLanguage).replace("{file}", path2.basename(filePath))
905
- });
906
- this.reloadRouterForFile(filePath);
907
- SLogger(this.localLanguage).logger.info({
908
- title: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_ROUTES_SUCCESS", this.localLanguage),
909
- message: import_opticore_translator2.TranslationLoader.t("ROUTES_HOT_RELOAD_COMPLETE", this.localLanguage)
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
+ })
910
1102
  });
911
1103
  } catch (error) {
912
1104
  SLogger(this.localLanguage).logger.error({
913
- title: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_ROUTES_FAILED", this.localLanguage),
1105
+ title: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_FAILED", this.localLanguage),
914
1106
  message: error.message,
915
- errorType: "HotReloadRoutesError",
1107
+ errorType: "HotReloadError",
1108
+ stackTrace: error.stack,
916
1109
  httpCodeValue: import_opticore_http_response3.HttpStatusCode.INTERNAL_SERVER_ERROR
917
1110
  });
1111
+ } finally {
1112
+ this.hmrRestartPending = false;
918
1113
  }
919
1114
  }
920
1115
  /**
921
- * Execute hot reload for dependencies
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
922
1130
  */
923
- executeHotReloadDependencies(filePath) {
924
- try {
925
- SLogger(this.localLanguage).logger.info({
926
- title: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_DEPS_START", this.localLanguage),
927
- message: import_opticore_translator2.TranslationLoader.t(
928
- "DEPENDENCIES_HOT_RELOADING",
929
- this.localLanguage
930
- ).replace("{file}", path2.basename(filePath))
931
- });
932
- this.reloadDependencies();
933
- SLogger(this.localLanguage).logger.info({
934
- title: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_DEPS_SUCCESS", this.localLanguage),
935
- message: import_opticore_translator2.TranslationLoader.t("DEPENDENCIES_HOT_RELOAD_COMPLETE", this.localLanguage)
936
- });
937
- } catch (error) {
938
- SLogger(this.localLanguage).logger.error({
939
- title: import_opticore_translator2.TranslationLoader.t("HOT_RELOAD_DEPS_FAILED", this.localLanguage),
940
- message: error.message,
941
- errorType: "HotReloadDependenciesError",
942
- httpCodeValue: import_opticore_http_response3.HttpStatusCode.INTERNAL_SERVER_ERROR
943
- });
1131
+ async performHotReloadActions(filePath) {
1132
+ if (filePath.includes(".json") || filePath.endsWith(".env")) {
1133
+ loaderTranslationFile(this.localLanguage);
944
1134
  }
945
- }
946
- /**
947
- * Notify environment reload
948
- */
949
- notifyEnvironmentReload() {
950
- if (this.errorEmitter) {
951
- this.errorEmitter.emit("environmentReloaded", {
952
- timestamp: /* @__PURE__ */ new Date(),
953
- environment: this.getEnvironment
954
- });
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();
955
1140
  }
956
1141
  }
957
1142
  /**
958
- * Reload specific route
959
- */
960
- reloadSpecificRoute(filePath) {
961
- SLogger(this.localLanguage).logger.info({
962
- title: "Route Reload",
963
- message: `Reloading route from: ${filePath}`
964
- });
965
- }
966
- /**
967
- * Reload router for specific file
968
- */
969
- reloadRouterForFile(filePath) {
970
- SLogger(this.localLanguage).logger.info({
971
- title: "Router Reload",
972
- message: `Reloading router for file: ${filePath}`
973
- });
974
- }
975
- /**
976
- * Reload configurations
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.
977
1155
  */
978
- reloadConfigurations() {
1156
+ async reloadRoutes() {
979
1157
  try {
980
- const newEnv = (0, import_opticore_env_access2.getEnvironnementValue)(this.environmentPath);
981
- Object.keys(newEnv).forEach((key) => {
982
- 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"
983
1161
  });
984
- loaderTranslationFile(this.localLanguage);
985
1162
  } catch (error) {
986
- throw new Error(`Configuration reload failed: ${error.message}`);
1163
+ throw new Error(`Route reload failed: ${error.message}`);
987
1164
  }
988
1165
  }
989
1166
  /**
990
- * Reload dependencies
1167
+ * Reloads dependencies dynamically.
1168
+ *
1169
+ * @method reloadDependencies
1170
+ * @private
1171
+ *
1172
+ * @returns {Promise<void>}
1173
+ *
1174
+ * @throws {Error} If dependency reloading fails
991
1175
  */
992
- reloadDependencies() {
1176
+ async reloadDependencies() {
993
1177
  try {
994
- new import_opticore_dependency_inject2.SContainer(this.localLanguage, this.currentDependencies);
995
- const container = dependenciesContainerProvider(this.localLanguage);
996
- if (!container) {
997
- 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
+ });
998
1184
  }
999
1185
  } catch (error) {
1000
1186
  throw new Error(`Dependency reload failed: ${error.message}`);
1001
1187
  }
1002
1188
  }
1003
1189
  /**
1004
- * Register routes
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
1005
1201
  */
1006
- registerRoutes(allFeatureRoutes) {
1007
- allFeatureRoutes.forEach((router) => {
1008
- if (router.routes) {
1009
- router.routes.forEach((route) => {
1010
- this.expressApp.use(route.path, route.handler);
1011
- });
1012
- }
1013
- });
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"
1208
+ });
1209
+ return false;
1210
+ }
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
+ })
1225
+ });
1226
+ return false;
1227
+ }
1228
+ return true;
1014
1229
  }
1015
1230
  /**
1016
- * Parse transform error
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}
1017
1240
  */
1018
- parseTransformError(error) {
1019
- const errorMessage = error.message || String(error);
1020
- let tool = "Unknown";
1021
- if (errorMessage.includes("esbuild")) tool = "esbuild";
1022
- if (errorMessage.includes("webpack")) tool = "webpack";
1023
- const fileMatch = errorMessage.match(/([^:\s]+\.(?:ts|js|tsx|jsx)):(\d+):(\d+):/);
1024
- const file = fileMatch ? fileMatch[1] : null;
1025
- const line = fileMatch ? parseInt(fileMatch[2], 10) : null;
1026
- const column = fileMatch ? parseInt(fileMatch[3], 10) : null;
1027
- const errorDetailMatch = errorMessage.match(/ERROR:\s*(.+?)(?:\n|$)/);
1028
- const errorDetail = errorDetailMatch ? errorDetailMatch[1].trim() : null;
1029
- return {
1030
- message: errorMessage,
1031
- file,
1032
- line,
1033
- column,
1034
- errorDetail,
1035
- tool
1036
- };
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
+ });
1037
1249
  }
1038
1250
  /**
1039
- * Display server info
1251
+ * Handles HMR watcher errors.
1252
+ *
1253
+ * @method onHMRWatcherError
1254
+ * @private
1255
+ *
1256
+ * @param {Error} error - The watcher error
1257
+ *
1258
+ * @returns {void}
1040
1259
  */
1041
- infoWebApp() {
1042
- this.serverUtility.infoServer(
1043
- this.getEnvironment.appHost,
1044
- Number(this.getEnvironment.appPort)
1045
- );
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
1266
+ });
1046
1267
  }
1047
1268
  /**
1048
- * Stop file watcher
1269
+ * Stops the HMR system.
1270
+ *
1271
+ * @method stopHMR
1272
+ * @private
1273
+ *
1274
+ * @returns {void}
1049
1275
  */
1050
- stopFileWatcher() {
1276
+ stopHMR() {
1051
1277
  if (this.fileWatcher) {
1052
- try {
1053
- this.fileWatcher.stopWatching();
1054
- SLogger(this.localLanguage).logger.info({
1055
- title: import_opticore_translator2.TranslationLoader.t("WATCHER_STOPPED", this.localLanguage),
1056
- message: import_opticore_translator2.TranslationLoader.t("FILE_WATCHER_STOPPED_GRACEFULLY", this.localLanguage)
1057
- });
1058
- } catch (error) {
1059
- SLogger(this.localLanguage).logger.error({
1060
- title: import_opticore_translator2.TranslationLoader.t("WATCHER_STOP_ERROR", this.localLanguage),
1061
- message: `Error stopping watcher: ${error.message}`,
1062
- errorType: "WatcherStopError",
1063
- httpCodeValue: import_opticore_http_response3.HttpStatusCode.INTERNAL_SERVER_ERROR
1064
- });
1065
- } finally {
1066
- this.fileWatcher = void 0;
1067
- }
1278
+ this.fileWatcher.close();
1279
+ this.fileWatcher = null;
1068
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
+ });
1069
1289
  }
1070
1290
  /**
1071
- * Shutdown server
1291
+ * Stops the server and HMR system cleanly.
1292
+ *
1293
+ * @method onStopServer
1294
+ * @public
1295
+ *
1296
+ * @returns {void}
1072
1297
  */
1073
- shutdown() {
1074
- this.stopFileWatcher();
1298
+ onStopServer() {
1299
+ this.stopHMR();
1075
1300
  if (this.server) {
1076
1301
  this.server.close(() => {
1077
1302
  SLogger(this.localLanguage).logger.info({
1078
- title: import_opticore_translator2.TranslationLoader.t("CLOSED", this.localLanguage),
1079
- 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"
1080
1305
  });
1081
- import_node_process3.default.exit(0);
1082
1306
  });
1083
- setTimeout(() => {
1084
- SLogger(this.localLanguage).logger.error({
1085
- title: import_opticore_translator2.TranslationLoader.t("FORCE_SHUTDOWN", this.localLanguage),
1086
- message: import_opticore_translator2.TranslationLoader.t("SERVER_FORCE_SHUTDOWN", this.localLanguage)
1087
- });
1088
- import_node_process3.default.exit(1);
1089
- }, 5e3);
1090
- } else {
1091
- import_node_process3.default.exit(0);
1092
- }
1093
- }
1094
- /**
1095
- * Enable/disable watcher
1096
- */
1097
- setWatcherEnabled(enabled) {
1098
- this.isWatcherEnabled = enabled;
1099
- if (enabled && !this.fileWatcher) {
1100
- this.initializeFileWatcher();
1101
- } else if (!enabled && this.fileWatcher) {
1102
- this.stopFileWatcher();
1307
+ this.serverStatus = "STOPPED";
1103
1308
  }
1104
- SLogger(this.localLanguage).logger.info({
1105
- title: "Watcher Status",
1106
- message: `File watcher ${enabled ? "enabled" : "disabled"}`
1107
- });
1108
- }
1109
- /**
1110
- * Get watcher status
1111
- */
1112
- getWatcherStatus() {
1113
- return {
1114
- enabled: this.isWatcherEnabled,
1115
- active: this.fileWatcher !== void 0
1116
- };
1117
1309
  }
1118
1310
  /**
1119
- * Get server state information
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
1120
1324
  */
1121
1325
  getServerState() {
1122
1326
  const now = /* @__PURE__ */ new Date();
1123
1327
  const uptime = this.serverStartTime ? now.getTime() - this.serverStartTime.getTime() : 0;
1124
- const formatUptime = (ms) => {
1125
- const seconds = Math.floor(ms / 1e3);
1126
- const minutes = Math.floor(seconds / 60);
1127
- const hours = Math.floor(minutes / 60);
1128
- const days = Math.floor(hours / 24);
1129
- if (days > 0) return `${days}j ${hours % 24}h ${minutes % 60}m`;
1130
- if (hours > 0) return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
1131
- if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
1132
- return `${seconds}s`;
1133
- };
1134
- const memory = import_node_process3.default.memoryUsage();
1135
1328
  return {
1136
1329
  status: this.serverStatus,
1137
- isRunning: this.server !== void 0,
1330
+ isRunning: this.server !== void 0 && this.serverStatus === "READY",
1138
1331
  host: this.getEnvironment.appHost,
1139
1332
  port: Number(this.getEnvironment.appPort),
1140
1333
  language: this.localLanguage,
1141
- watcherEnabled: this.isWatcherEnabled,
1142
- watcherActive: this.fileWatcher !== void 0,
1143
1334
  routesCount: this.currentRoutes.length,
1144
1335
  dependenciesCount: this.currentDependencies.length,
1145
1336
  startTime: this.serverStartTime,
1146
1337
  currentTime: now,
1147
1338
  uptime,
1148
- uptimeFormatted: formatUptime(uptime),
1149
- memoryUsage: {
1150
- rss: memory.rss,
1151
- heapTotal: memory.heapTotal,
1152
- heapUsed: memory.heapUsed,
1153
- external: memory.external,
1154
- arrayBuffers: memory.arrayBuffers
1155
- },
1339
+ uptimeFormatted: this.formatUptime(uptime),
1340
+ memoryUsage: import_node_process3.default.memoryUsage(),
1156
1341
  pid: import_node_process3.default.pid,
1157
1342
  platform: import_node_process3.default.platform,
1158
1343
  nodeVersion: import_node_process3.default.version,
1159
- cwd: import_node_process3.default.cwd()
1344
+ cwd: import_node_process3.default.cwd(),
1345
+ hmrEnabled: this.getEnvironment.hmrEnabled,
1346
+ hmrWatchingFiles: this.getEnvironment.hmrWatchPatterns?.length || 0,
1347
+ hmrRestartCount: this.hmrRestartCount
1160
1348
  };
1161
1349
  }
1162
1350
  /**
1163
- * Simple method to get just the status
1351
+ * Gets the current server status.
1352
+ *
1353
+ * @method getServerStatus
1354
+ * @public
1355
+ *
1356
+ * @returns {TServerStatus} Current server status
1164
1357
  */
1165
1358
  getServerStatus() {
1166
1359
  return this.serverStatus;
1167
1360
  }
1168
1361
  /**
1169
- * Get server statistics for monitoring
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
1170
1374
  */
1171
1375
  getServerStats() {
1172
1376
  const uptime = this.serverStartTime ? Date.now() - this.serverStartTime.getTime() : 0;
@@ -1183,24 +1387,27 @@ var WebServerCore = class {
1183
1387
  performance: {
1184
1388
  cpuUsage: import_node_process3.default.cpuUsage(),
1185
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
1186
1396
  }
1187
1397
  };
1188
1398
  }
1189
1399
  /**
1190
- * Format bytes to human readable string
1191
- */
1192
- formatBytes(bytes) {
1193
- const units = ["B", "KB", "MB", "GB", "TB"];
1194
- let value = bytes;
1195
- let unitIndex = 0;
1196
- while (value >= 1024 && unitIndex < units.length - 1) {
1197
- value /= 1024;
1198
- unitIndex++;
1199
- }
1200
- return `${value.toFixed(2)} ${units[unitIndex]}`;
1201
- }
1202
- /**
1203
- * Format uptime to human readable string
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"
1204
1411
  */
1205
1412
  formatUptime(ms) {
1206
1413
  const seconds = Math.floor(ms / 1e3);
@@ -1208,7 +1415,7 @@ var WebServerCore = class {
1208
1415
  const hours = Math.floor(minutes / 60);
1209
1416
  const days = Math.floor(hours / 24);
1210
1417
  if (days > 0) {
1211
- return `${days}j ${hours % 24}h ${minutes % 60}m`;
1418
+ return `${days}d ${hours % 24}h ${minutes % 60}m`;
1212
1419
  } else if (hours > 0) {
1213
1420
  return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
1214
1421
  } else if (minutes > 0) {
@@ -1218,55 +1425,66 @@ var WebServerCore = class {
1218
1425
  }
1219
1426
  }
1220
1427
  /**
1221
- * Add watch directories
1222
- */
1223
- addWatchDirectories(directories) {
1224
- if (this.fileWatcher) {
1225
- this.fileWatcher.addWatchDirectories(directories);
1226
- }
1227
- }
1228
- /**
1229
- * Clear file cache
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"
1230
1439
  */
1231
- clearFileCache() {
1232
- if (this.fileWatcher) {
1233
- this.fileWatcher.clearFileCache();
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++;
1234
1447
  }
1448
+ return `${value.toFixed(2)} ${units[unitIndex]}`;
1235
1449
  }
1236
1450
  /**
1237
- * Force reload configurations
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
1238
1457
  */
1239
- forceReloadConfig() {
1240
- try {
1241
- this.reloadConfigurations();
1242
- this.reloadDependencies();
1243
- SLogger(this.localLanguage).logger.info({
1244
- title: "Force Reload",
1245
- message: "All configurations and dependencies have been reloaded"
1246
- });
1247
- } catch (error) {
1248
- SLogger(this.localLanguage).logger.error({
1249
- title: "Force Reload Failed",
1250
- message: error.message,
1251
- errorType: "ForceReloadError",
1252
- httpCodeValue: import_opticore_http_response3.HttpStatusCode.INTERNAL_SERVER_ERROR
1253
- });
1254
- }
1458
+ isHMRActive() {
1459
+ return this.getEnvironment.hmrEnabled && this.fileWatcher !== null;
1255
1460
  }
1256
1461
  /**
1257
- * Restart watcher
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
1258
1474
  */
1259
- restartWatcher() {
1260
- this.stopFileWatcher();
1261
- if (this.isWatcherEnabled) {
1262
- setTimeout(() => {
1263
- this.initializeFileWatcher();
1264
- SLogger(this.localLanguage).logger.info({
1265
- title: "Watcher Restarted",
1266
- message: "File watcher has been restarted successfully"
1267
- });
1268
- }, 1e3);
1269
- }
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
+ };
1270
1488
  }
1271
1489
  };
1272
1490
  // Annotate the CommonJS export names for ESM import in node: