opticore-webapp 1.0.70 → 1.0.72

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
@@ -30,20 +30,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ HotReloadWatcher: () => HotReloadWatcher,
33
34
  WebServer: () => WebServerCore,
34
35
  envPath: () => envPath
35
36
  });
36
37
  module.exports = __toCommonJS(index_exports);
37
38
 
38
39
  // src/core/webServer.core.ts
39
- var path2 = __toESM(require("path"), 1);
40
- var import_node_process3 = __toESM(require("process"), 1);
40
+ var path3 = __toESM(require("path"), 1);
41
+ var import_node_process4 = __toESM(require("process"), 1);
41
42
  var import_cors = __toESM(require("cors"), 1);
42
43
  var import_opticore_catch_exception_error3 = require("opticore-catch-exception-error");
43
44
  var import_opticore_express2 = require("opticore-express");
44
45
  var import_opticore_env_access2 = require("opticore-env-access");
45
- var import_opticore_http_response3 = require("opticore-http-response");
46
- var import_opticore_translator2 = require("opticore-translator");
46
+ var import_opticore_http_response4 = require("opticore-http-response");
47
+ var import_opticore_translator3 = require("opticore-translator");
47
48
  var import_opticore_request_call_event = require("opticore-request-call-event");
48
49
 
49
50
  // src/core/handlers/eventProcess.handler.ts
@@ -149,16 +150,35 @@ var dependenciesContainerProvider = (localLang) => {
149
150
  return new import_opticore_dependency_inject.SContainer(localLang, dependencies);
150
151
  };
151
152
 
153
+ // src/application/service/core.service.ts
154
+ var import_opticore_dependency_inject2 = require("opticore-dependency-inject");
155
+
156
+ // src/utils/parsingDateTime.utils.ts
157
+ var parsingDateTimeUtils = (localFormatTime) => {
158
+ const now = /* @__PURE__ */ new Date();
159
+ const formatter = new Intl.DateTimeFormat(localFormatTime, {
160
+ day: "2-digit",
161
+ month: "2-digit",
162
+ year: "numeric",
163
+ hour: "2-digit",
164
+ minute: "2-digit",
165
+ hour12: false
166
+ });
167
+ return formatter.format(now);
168
+ };
169
+
152
170
  // src/application/service/core.service.ts
153
171
  var CoreService = class {
154
172
  localLanguage;
155
173
  environmentPath;
156
174
  serverLog;
157
175
  logger;
176
+ container;
158
177
  constructor(localLang, environmentPath) {
159
178
  this.loadTranslationFiles();
160
179
  this.environmentPath = environmentPath;
161
180
  this.localLanguage = localLang;
181
+ this.container = new import_opticore_dependency_inject2.SContainer(localLang);
162
182
  this.serverLog = dependenciesContainerProvider(localLang).resolve("OpticoreLogger");
163
183
  this.logger = dependenciesContainerProvider(localLang).resolve("LoggerCore");
164
184
  }
@@ -298,7 +318,7 @@ var CoreService = class {
298
318
  this.loadTranslationFiles();
299
319
  try {
300
320
  const getEnvironment = (0, import_opticore_env_access.getEnvironmentValue)(this.environmentPath);
301
- const msg5 = getEnvironment.protocolTransfert === "" ? import_ansi_colors.default.underline(`http://${host}:${port}`) : import_ansi_colors.default.underline(`${getEnvironment.protocolTransfert}://${host}:${port}`);
321
+ const msg5 = getEnvironment.protocolTransfer === "" ? import_ansi_colors.default.underline(`http://${host}:${port}`) : import_ansi_colors.default.underline(`${getEnvironment.protocolTransfer}://${host}:${port}`);
302
322
  const messages = [
303
323
  import_opticore_translator.TranslationLoader.t("webServerListening", this.localLanguage),
304
324
  import_opticore_translator.TranslationLoader.t("webServerUsingNodeVersion", this.localLanguage, { nodeVersion: this.getVersions().nodeVersion }),
@@ -318,17 +338,31 @@ var CoreService = class {
318
338
  });
319
339
  console.log(border);
320
340
  console.log("\n");
321
- this.logger.success({
322
- title: import_opticore_translator.TranslationLoader.t("serverRunningTitle", this.localLanguage),
323
- message: import_opticore_translator.TranslationLoader.t("serverRunningAt", this.localLanguage, { server: msg5 })
324
- });
325
341
  this.serverLog.serverLog({
326
- timestamp: (/* @__PURE__ */ new Date()).toString(),
342
+ timestamp: parsingDateTimeUtils(getEnvironment.localFormatTime),
327
343
  level: "SERVER",
328
344
  title: import_opticore_translator.TranslationLoader.t("serverRunningTitle", this.localLanguage),
329
345
  typeName: import_opticore_translator.TranslationLoader.t("opticoreServerTypeName", this.localLanguage),
330
346
  message: import_opticore_translator.TranslationLoader.t("serverRunningAt", this.localLanguage, { server: msg5 })
331
347
  });
348
+ const dependenciesList = this.container.listDependencies();
349
+ if (dependenciesList.length > 0) {
350
+ this.serverLog.opticoreLog({
351
+ level: "OPTICORE",
352
+ message: import_opticore_translator.TranslationLoader.t("DEPENDENCIES_STORED", this.localLanguage, { dependenciesList: dependenciesList.join(", ") }),
353
+ timestamp: parsingDateTimeUtils(getEnvironment.localFormatTime),
354
+ title: import_opticore_translator.TranslationLoader.t("DEPENDENCIES_CONTAINER", this.localLanguage),
355
+ typeName: "DEPENDENCIES_CONTAINER"
356
+ });
357
+ } else {
358
+ this.serverLog.opticoreLog({
359
+ level: "OPTICORE",
360
+ message: import_opticore_translator.TranslationLoader.t("NO_DEPENDENCIES_STORED", this.localLanguage),
361
+ timestamp: parsingDateTimeUtils(getEnvironment.localFormatTime),
362
+ title: import_opticore_translator.TranslationLoader.t("DEPENDENCIES_CONTAINER", this.localLanguage),
363
+ typeName: "DEPENDENCIES_CONTAINER"
364
+ });
365
+ }
332
366
  } catch (err) {
333
367
  this.logger.error({
334
368
  message: err.message,
@@ -392,10 +426,284 @@ var SServerStartError = (err, environmentPath) => {
392
426
  };
393
427
 
394
428
  // src/core/webServer.core.ts
395
- var import_opticore_dependency_inject2 = require("opticore-dependency-inject");
429
+ var import_opticore_dependency_inject3 = require("opticore-dependency-inject");
430
+
431
+ // src/application/service/getEnvFileLoading.service.ts
432
+ var import_path = __toESM(require("path"), 1);
433
+ var import_node_process3 = __toESM(require("process"), 1);
434
+ var import_fs = __toESM(require("fs"), 1);
435
+ var import_opticore_translator2 = require("opticore-translator");
436
+ var import_opticore_http_response3 = require("opticore-http-response");
437
+ var getEnvFileLoadingService = (filePath, localLanguage) => {
438
+ loaderTranslationFile(localLanguage);
439
+ const logger = dependenciesContainerProvider(localLanguage).resolve("LoggerCore");
440
+ try {
441
+ const fullPath = import_path.default.resolve(import_node_process3.default.cwd(), filePath);
442
+ if (import_fs.default.existsSync(fullPath)) {
443
+ const env = import_fs.default.readFileSync(fullPath, "utf-8");
444
+ const lines = env.split("\n");
445
+ lines.forEach((line) => {
446
+ const match = line.match(/^([^#=]+)=([^#]+)$/);
447
+ if (match) {
448
+ const key = match[1].trim();
449
+ import_node_process3.default.env[key] = match[2].trim();
450
+ }
451
+ });
452
+ }
453
+ } catch (err) {
454
+ logger.error({
455
+ message: err.message,
456
+ title: import_opticore_translator2.TranslationLoader.t("EnvFileLoading", localLanguage),
457
+ errorType: err.code,
458
+ stackTrace: err.stack,
459
+ httpCodeValue: import_opticore_http_response3.HttpStatusCode.INTERNAL_SERVER_ERROR
460
+ });
461
+ }
462
+ };
463
+
464
+ // src/hotReload/hotReload.watcher.ts
465
+ var import_fs2 = require("fs");
466
+ var import_promises = require("fs/promises");
467
+ var import_path2 = require("path");
468
+ var import_dotenv = require("dotenv");
469
+ var import_chalk2 = __toESM(require("chalk"), 1);
470
+ var import_ansi_colors2 = __toESM(require("ansi-colors"), 1);
471
+ var import_gradient_string = __toESM(require("gradient-string"), 1);
472
+ var DEFAULT_WATCH_EXTENSIONS = [".ts", ".js", ".mjs", ".cjs", ".json"];
473
+ var DEFAULT_HOT_RELOAD_EXTENSIONS = [".json"];
474
+ var DEFAULT_IGNORE = [
475
+ "node_modules",
476
+ "dist",
477
+ ".git",
478
+ "package.json",
479
+ "package-lock.json",
480
+ ".idea",
481
+ ".vscode",
482
+ "coverage",
483
+ "logs"
484
+ ];
485
+ var DEFAULT_DEBOUNCE_MS = 300;
486
+ var HotReloadWatcher = class {
487
+ cfg;
488
+ server = null;
489
+ watchers = [];
490
+ debounceTimer = null;
491
+ restarting = false;
492
+ constructor(config) {
493
+ const c = config ?? {};
494
+ this.cfg = {
495
+ rootDir: (0, import_path2.resolve)(c.rootDir ?? process.cwd()),
496
+ watchDirs: c.watchDirs ?? [],
497
+ watchExtensions: c.watchExtensions ?? DEFAULT_WATCH_EXTENSIONS,
498
+ ignore: [...DEFAULT_IGNORE, ...c.ignore ?? []],
499
+ envFile: c.envFile ?? ".env",
500
+ hotReloadExtensions: c.hotReloadExtensions ?? DEFAULT_HOT_RELOAD_EXTENSIONS,
501
+ debounceMs: c.debounceMs ?? DEFAULT_DEBOUNCE_MS
502
+ };
503
+ }
504
+ // ─── Called by WebServerCore once the HTTP server is listening ────────────
505
+ async attach(server) {
506
+ this.server = server;
507
+ this.printBanner();
508
+ await this.setupWatchers();
509
+ this.setupProcessSignals();
510
+ }
511
+ // ─── File watching ────────────────────────────────────────────────────────
512
+ async setupWatchers() {
513
+ const dirs = [
514
+ this.cfg.rootDir,
515
+ ...this.cfg.watchDirs.map((d) => (0, import_path2.resolve)(d))
516
+ ];
517
+ for (const dir of dirs) {
518
+ await this.watchRecursive(dir);
519
+ }
520
+ }
521
+ async watchRecursive(dir) {
522
+ if (this.shouldIgnoreDir(dir)) return;
523
+ try {
524
+ await (0, import_promises.access)(dir);
525
+ } catch {
526
+ return;
527
+ }
528
+ try {
529
+ const w = (0, import_fs2.watch)(dir, (_, filename) => {
530
+ if (filename) this.onFileChange((0, import_path2.join)(dir, filename));
531
+ });
532
+ w.on("error", () => {
533
+ });
534
+ this.watchers.push(w);
535
+ const entries = await (0, import_promises.readdir)(dir, { withFileTypes: true });
536
+ for (const entry of entries) {
537
+ if (entry.isDirectory()) await this.watchRecursive((0, import_path2.join)(dir, entry.name));
538
+ }
539
+ } catch {
540
+ }
541
+ }
542
+ // ─── Change handling ──────────────────────────────────────────────────────
543
+ onFileChange(filePath) {
544
+ if (this.shouldIgnoreFile(filePath)) return;
545
+ if (!this.isWatched(filePath)) return;
546
+ if (this.restarting) return;
547
+ const rel = (0, import_path2.relative)(this.cfg.rootDir, filePath);
548
+ if (this.isHotReloadable(filePath)) {
549
+ this.doHotReload(filePath, rel);
550
+ } else {
551
+ this.scheduleRestart(rel);
552
+ }
553
+ }
554
+ doHotReload(filePath, rel) {
555
+ if (this.isEnvFile(filePath)) {
556
+ (0, import_dotenv.config)({ path: filePath, override: true });
557
+ this.printHot(rel, "env variables reloaded");
558
+ } else {
559
+ this.printHot(rel, "json config reloaded");
560
+ }
561
+ }
562
+ scheduleRestart(rel) {
563
+ if (this.debounceTimer) clearTimeout(this.debounceTimer);
564
+ this.debounceTimer = setTimeout(() => {
565
+ this.restarting = true;
566
+ this.printReloading(rel);
567
+ this.closeAndExit();
568
+ }, this.cfg.debounceMs);
569
+ }
570
+ /**
571
+ * Gracefully close the HTTP server so in-flight requests can finish,
572
+ * then exit with code 0 — the external runner restarts the process.
573
+ */
574
+ closeAndExit() {
575
+ if (!this.server) {
576
+ process.exit(0);
577
+ return;
578
+ }
579
+ this.server.close(() => {
580
+ process.exit(0);
581
+ });
582
+ setTimeout(() => {
583
+ process.exit(0);
584
+ }, 3e3).unref();
585
+ }
586
+ // ─── Filters ──────────────────────────────────────────────────────────────
587
+ shouldIgnoreDir(dir) {
588
+ return this.cfg.ignore.some(
589
+ (p) => dir.includes(`/${p}`) || dir.endsWith(`/${p}`) || dir.endsWith(p)
590
+ );
591
+ }
592
+ shouldIgnoreFile(filePath) {
593
+ const name = (0, import_path2.basename)(filePath);
594
+ const rel = (0, import_path2.relative)(this.cfg.rootDir, filePath);
595
+ return this.cfg.ignore.some(
596
+ (p) => name === p || rel.includes(p) || filePath.includes(`/${p}/`)
597
+ );
598
+ }
599
+ isWatched(filePath) {
600
+ if (this.isEnvFile(filePath)) return true;
601
+ return this.cfg.watchExtensions.includes((0, import_path2.extname)(filePath));
602
+ }
603
+ isHotReloadable(filePath) {
604
+ if (this.isEnvFile(filePath)) return true;
605
+ return this.cfg.hotReloadExtensions.includes((0, import_path2.extname)(filePath));
606
+ }
607
+ isEnvFile(filePath) {
608
+ const name = (0, import_path2.basename)(filePath);
609
+ return name === this.cfg.envFile || name.startsWith(".env");
610
+ }
611
+ // ─── ANSI strip (box width calculation) ───────────────────────────────────
612
+ strip(str) {
613
+ return str.replace(/\x1b\[[0-9;]*m/g, "");
614
+ }
615
+ // ─── Timestamp ────────────────────────────────────────────────────────────
616
+ ts() {
617
+ const n = /* @__PURE__ */ new Date();
618
+ return [n.getHours(), n.getMinutes(), n.getSeconds()].map((v) => String(v).padStart(2, "0")).join(":");
619
+ }
620
+ // ─── Console output ───────────────────────────────────────────────────────
621
+ /**
622
+ * Startup banner — same bgGreen box style as CoreService.infoServer().
623
+ *
624
+ * OPTICORE HOT RELOAD ← gradient
625
+ * ████████████████████████████████ ← bgGreen border
626
+ * root ./src
627
+ * watching .ts .js .json .env
628
+ * debounce 300ms
629
+ * ████████████████████████████████ ← bgGreen border
630
+ * watching for changes...
631
+ */
632
+ printBanner() {
633
+ const rootRel = (0, import_path2.relative)(process.cwd(), this.cfg.rootDir) || ".";
634
+ const extStr = [...this.cfg.watchExtensions, ".env"].join(" ");
635
+ const ignoreParts = this.cfg.ignore.slice(0, 4);
636
+ const ignoreMore = this.cfg.ignore.length > 4 ? ` ${import_chalk2.default.blackBright(`+${this.cfg.ignore.length - 4} more`)}` : "";
637
+ const ignoreStr = import_chalk2.default.blackBright(ignoreParts.join(" ")) + ignoreMore;
638
+ const TITLE = "OPTICORE HOT RELOAD";
639
+ const rows = [
640
+ ` root ${import_chalk2.default.white(rootRel)}`,
641
+ ` watching ${import_chalk2.default.cyan(extStr)}`,
642
+ ` ignoring ${ignoreStr}`,
643
+ ` debounce ${import_chalk2.default.white(`${this.cfg.debounceMs}ms`)}`
644
+ ];
645
+ const maxLen = Math.max(
646
+ TITLE.length + 4,
647
+ ...rows.map((r) => this.strip(r).length)
648
+ ) + 4;
649
+ const border = import_chalk2.default.bgGreen.white(" ".repeat(maxLen));
650
+ const titlePad = " ".repeat(Math.max(0, Math.floor((maxLen - TITLE.length) / 2)));
651
+ console.log("\n" + titlePad + (0, import_gradient_string.default)(["#43e97b", "#38f9d7", "#00c6fb"])(TITLE));
652
+ console.log(border);
653
+ for (const row of rows) {
654
+ const pad = Math.max(0, maxLen - this.strip(row).length - 2);
655
+ console.log(import_chalk2.default.bgGreen.white(` ${row}${" ".repeat(pad)} `));
656
+ }
657
+ console.log(border);
658
+ console.log(import_chalk2.default.blackBright(` watching for changes...
659
+ `));
660
+ }
661
+ /**
662
+ * ✔ [ HOT ] 14:23:45 | .env → env variables reloaded
663
+ */
664
+ printHot(file, action) {
665
+ const badge = import_ansi_colors2.default.bgCyan(import_ansi_colors2.default.white.bold(" HOT "));
666
+ const filePart = import_chalk2.default.cyan.bold(file.padEnd(32));
667
+ console.log(
668
+ ` ${import_chalk2.default.green("\u2714")} ${badge} ${import_chalk2.default.blackBright(this.ts())} ${import_chalk2.default.blackBright("|")} ${filePart} ${import_chalk2.default.green("\u2192")} ${import_chalk2.default.green(action)}`
669
+ );
670
+ }
671
+ /**
672
+ * ⚡ [ RELOAD ] 14:24:03 | src/routes/user.ts → restarting server...
673
+ */
674
+ printReloading(file) {
675
+ const badge = import_ansi_colors2.default.bgYellow(import_ansi_colors2.default.white.bold(" RELOAD "));
676
+ const filePart = import_chalk2.default.yellow.bold(file.padEnd(32));
677
+ console.log(
678
+ `
679
+ ${import_chalk2.default.yellow("\u26A1")} ${badge} ${import_chalk2.default.blackBright(this.ts())} ${import_chalk2.default.blackBright("|")} ${filePart} ${import_chalk2.default.yellow("\u2192")} ${import_chalk2.default.yellow("restarting server...")}
680
+ `
681
+ );
682
+ }
683
+ // ─── Graceful shutdown on CTRL+C ──────────────────────────────────────────
684
+ setupProcessSignals() {
685
+ const shutdown = () => {
686
+ const badge = import_ansi_colors2.default.bgBlackBright(import_ansi_colors2.default.white.bold(" STOPPED"));
687
+ console.log(
688
+ `
689
+ ${import_chalk2.default.gray("\u25A0")} ${badge} ${import_chalk2.default.blackBright(this.ts())} ${import_chalk2.default.blackBright("|")} ${import_chalk2.default.gray("watcher stopped")}
690
+ `
691
+ );
692
+ this.watchers.forEach((w) => {
693
+ w.close();
694
+ });
695
+ process.exit(0);
696
+ };
697
+ process.once("SIGINT", shutdown);
698
+ process.once("SIGTERM", shutdown);
699
+ }
700
+ };
701
+
702
+ // src/core/webServer.core.ts
396
703
  var WebServerCore = class {
397
704
  serverUtility;
398
705
  expressApp = (0, import_opticore_express2.express)();
706
+ container;
399
707
  localLanguage;
400
708
  loggerConfig;
401
709
  routerExpressApp;
@@ -403,6 +711,7 @@ var WebServerCore = class {
403
711
  environmentPath;
404
712
  serverListenEvent;
405
713
  dependenciesRegistered = false;
714
+ hotReloadCfg;
406
715
  constructor(paramsConstructor) {
407
716
  this.loadTranslationFiles();
408
717
  this.stackTraceErrorHandling();
@@ -411,6 +720,7 @@ var WebServerCore = class {
411
720
  this.loggerConfig = paramsConstructor.loggerConfig;
412
721
  this.localLanguage = paramsConstructor.localLanguage;
413
722
  this.environmentPath = paramsConstructor.environmentPath;
723
+ this.container = new import_opticore_dependency_inject3.SContainer(paramsConstructor.localLanguage);
414
724
  this.expressApp.use(import_opticore_express2.express.json());
415
725
  this.expressApp.use(import_opticore_express2.express.raw());
416
726
  this.expressApp.use(import_opticore_express2.express.text());
@@ -418,17 +728,20 @@ var WebServerCore = class {
418
728
  this.expressApp.use((0, import_cors.default)(paramsConstructor.corsOriginOptions));
419
729
  this.serverListenEvent = new import_opticore_catch_exception_error3.ServerListenEventError(paramsConstructor.localLanguage);
420
730
  this.serverUtility = new CoreService(paramsConstructor.localLanguage, paramsConstructor.environmentPath);
731
+ const hr = paramsConstructor.hotReload;
732
+ this.hotReloadCfg = !hr ? null : hr === true ? {} : hr;
421
733
  }
734
+ /**
735
+ *
736
+ * @param dependencies
737
+ */
422
738
  registerDependencies(dependencies2) {
739
+ this.loadTranslationFiles();
423
740
  try {
741
+ getEnvFileLoadingService(".env", this.localLanguage);
424
742
  if (dependencies2 && dependencies2.length > 0) {
425
743
  dependencies2.forEach((dependency) => {
426
- return new import_opticore_dependency_inject2.SContainer(this.localLanguage).register(dependency.key, dependency.factory, dependency.scope);
427
- });
428
- } else {
429
- SLogger(this.environmentPath).logger.info({
430
- message: import_opticore_translator2.TranslationLoader.t("NO_REGISTER_DEPENDENCIES", this.localLanguage),
431
- title: import_opticore_translator2.TranslationLoader.t("REGISTER_DEPENDENCIES", this.localLanguage)
744
+ this.container.register(dependency.key, dependency.factory, dependency.scope);
432
745
  });
433
746
  }
434
747
  } catch (err) {
@@ -437,12 +750,19 @@ var WebServerCore = class {
437
750
  title: "Register Dependencies",
438
751
  errorType: err.code,
439
752
  stackTrace: err.stack,
440
- httpCodeValue: import_opticore_http_response3.HttpStatusCode.INTERNAL_SERVER_ERROR
753
+ httpCodeValue: import_opticore_http_response4.HttpStatusCode.INTERNAL_SERVER_ERROR
441
754
  });
442
755
  }
443
756
  }
757
+ /**
758
+ *
759
+ * @param routers
760
+ * @param databaseCallback
761
+ * @param dependenciesProvider
762
+ */
444
763
  onStartServer(routers, databaseCallback, dependenciesProvider) {
445
764
  this.loadTranslationFiles();
765
+ getEnvFileLoadingService(".env", this.localLanguage);
446
766
  if (this.getEnvironmentValue.appPort === "" && Number(this.getEnvironmentValue.appPort) === 0) {
447
767
  this.serverListenEvent.hostPortUndefined(Number(this.getEnvironmentValue.appPort));
448
768
  } else if (this.getEnvironmentValue.appHost === "") {
@@ -451,29 +771,41 @@ var WebServerCore = class {
451
771
  this.serverListenEvent.portUndefined();
452
772
  } else if (this.localLanguage === "") {
453
773
  SLogger(this.environmentPath).logger.error({
454
- message: import_opticore_translator2.TranslationLoader.t("noDefaultLocalLang", this.localLanguage),
455
- title: import_opticore_translator2.TranslationLoader.t("noLocalLang", this.localLanguage),
456
- errorType: import_opticore_translator2.TranslationLoader.t("localLangMissing", this.localLanguage),
774
+ message: import_opticore_translator3.TranslationLoader.t("noDefaultLocalLang", this.localLanguage),
775
+ title: import_opticore_translator3.TranslationLoader.t("noLocalLang", this.localLanguage),
776
+ errorType: import_opticore_translator3.TranslationLoader.t("localLangMissing", this.localLanguage),
457
777
  stackTrace: void 0,
458
- httpCodeValue: import_opticore_http_response3.HttpStatusCode.NOT_FOUND
778
+ httpCodeValue: import_opticore_http_response4.HttpStatusCode.NOT_FOUND
459
779
  });
460
780
  } else {
461
- return this.expressApp.listen(
781
+ const server = this.expressApp.listen(
462
782
  Number(this.getEnvironmentValue.appPort),
463
783
  () => {
464
784
  this.loadTranslationFiles();
465
785
  try {
466
786
  databaseCallback(this.getEnvironmentValue);
467
- new import_opticore_dependency_inject2.SContainer(this.localLanguage, dependenciesProvider);
468
- this.expressApp.use(import_opticore_express2.express.static(path2.join(import_node_process3.default.cwd(), "public/template")));
787
+ if (dependenciesProvider) {
788
+ this.registerDependencies(dependenciesProvider);
789
+ }
790
+ this.container.loadServices();
791
+ this.expressApp.use(import_opticore_express2.express.static(path3.join(import_node_process4.default.cwd(), "public/template")));
469
792
  this.registerRoutes(routers);
793
+ const resolvedHmr = this.resolveHotReloadConfig();
794
+ if (resolvedHmr !== null) {
795
+ new HotReloadWatcher(resolvedHmr).attach(server);
796
+ }
470
797
  } catch (err) {
471
798
  SServerStartError(err, this.environmentPath);
472
799
  }
473
800
  }
474
801
  );
802
+ return server;
475
803
  }
476
804
  }
805
+ /**
806
+ *
807
+ * @param serverWeb
808
+ */
477
809
  onListeningOnServerEvent(serverWeb) {
478
810
  this.loadTranslationFiles();
479
811
  serverWeb.on(import_opticore_catch_exception_error3.CEventNameError.error, (err) => {
@@ -486,6 +818,10 @@ var WebServerCore = class {
486
818
  this.infoWebApp();
487
819
  });
488
820
  }
821
+ /**
822
+ *
823
+ * @param serverWeb
824
+ */
489
825
  onRequestOnServerEvent(serverWeb) {
490
826
  this.loadTranslationFiles();
491
827
  serverWeb.on(import_opticore_catch_exception_error3.CEventNameError.request, (req, res) => {
@@ -507,6 +843,62 @@ var WebServerCore = class {
507
843
  loadTranslationFiles() {
508
844
  loaderTranslationFile(this.localLanguage);
509
845
  }
846
+ /**
847
+ * Resolve the final HotReloadConfig by merging constructor config with
848
+ * HMR environment variables.
849
+ *
850
+ * Priority (highest → lowest):
851
+ * 1. Constructor hotReload properties (explicit code-level config)
852
+ * 2. HMR_* env variables (runtime / per-environment config)
853
+ * 3. HotReloadWatcher internal defaults (built-in fallbacks)
854
+ *
855
+ * The watcher starts when:
856
+ * - constructor passed hotReload: true | HotReloadConfig
857
+ * - OR HMR_ENABLED=true in the .env file
858
+ */
859
+ resolveHotReloadConfig() {
860
+ const env = this.getEnvironmentValue;
861
+ if (this.hotReloadCfg === null && !env.hmrEnabled) {
862
+ return null;
863
+ }
864
+ const base = this.hotReloadCfg ?? {};
865
+ const envExtensions = this.hmrExtractExtensions(env.hmrWatchPatterns);
866
+ const envIgnore = this.hmrExtractIgnore(env.hmrIgnorePatterns);
867
+ return {
868
+ rootDir: base.rootDir,
869
+ watchDirs: base.watchDirs,
870
+ watchExtensions: base.watchExtensions ?? (envExtensions.length ? envExtensions : void 0),
871
+ ignore: base.ignore ?? (envIgnore.length ? envIgnore : void 0),
872
+ hotReloadExtensions: base.hotReloadExtensions,
873
+ debounceMs: base.debounceMs ?? (env.hmrDebounceMs > 0 ? env.hmrDebounceMs : void 0),
874
+ envFile: base.envFile
875
+ };
876
+ }
877
+ /**
878
+ * Extract file extensions from glob patterns such as "src/** /*.ts".
879
+ * "src/** /*.ts"
880
+ * ".env" skipped, handled natively by the watcher
881
+ */
882
+ hmrExtractExtensions(patterns) {
883
+ const exts = /* @__PURE__ */ new Set();
884
+ for (const p of patterns) {
885
+ const m = p.match(/\*(\.[a-zA-Z0-9]+)$/);
886
+ if (m) {
887
+ exts.add(m[1]);
888
+ }
889
+ }
890
+ return [...exts];
891
+ }
892
+ /**
893
+ * Extract ignore names from glob patterns such as "node_modules/**".
894
+ * "node_modules/**" → "node_modules"
895
+ * "dist/**" → "dist"
896
+ */
897
+ hmrExtractIgnore(patterns) {
898
+ return [...new Set(
899
+ patterns.map((p) => p.split("/")[0]).filter(Boolean)
900
+ )];
901
+ }
510
902
  /**
511
903
  *
512
904
  * @param allFeatureRoutes
@@ -540,6 +932,7 @@ var WebServerCore = class {
540
932
  };
541
933
  // Annotate the CommonJS export names for ESM import in node:
542
934
  0 && (module.exports = {
935
+ HotReloadWatcher,
543
936
  WebServer,
544
937
  envPath
545
938
  });