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