opticore-webapp 1.0.72 → 1.0.74

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,7 +30,6 @@ 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,
34
33
  WebServer: () => WebServerCore,
35
34
  envPath: () => envPath
36
35
  });
@@ -461,245 +460,8 @@ var getEnvFileLoadingService = (filePath, localLanguage) => {
461
460
  }
462
461
  };
463
462
 
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
463
  // src/core/webServer.core.ts
464
+ var import_opticore_watcher = require("opticore-watcher");
703
465
  var WebServerCore = class {
704
466
  serverUtility;
705
467
  expressApp = (0, import_opticore_express2.express)();
@@ -792,7 +554,7 @@ var WebServerCore = class {
792
554
  this.registerRoutes(routers);
793
555
  const resolvedHmr = this.resolveHotReloadConfig();
794
556
  if (resolvedHmr !== null) {
795
- new HotReloadWatcher(resolvedHmr).attach(server);
557
+ new import_opticore_watcher.HotReloadWatcher(resolvedHmr).attach(server);
796
558
  }
797
559
  } catch (err) {
798
560
  SServerStartError(err, this.environmentPath);
@@ -932,7 +694,6 @@ var WebServerCore = class {
932
694
  };
933
695
  // Annotate the CommonJS export names for ESM import in node:
934
696
  0 && (module.exports = {
935
- HotReloadWatcher,
936
697
  WebServer,
937
698
  envPath
938
699
  });
package/dist/index.d.cts CHANGED
@@ -5,76 +5,7 @@ import { TDependency } from 'opticore-dependency-inject';
5
5
  import { express } from 'opticore-express';
6
6
  import { LoggerCore } from 'opticore-logger';
7
7
  import { CorsOptions } from 'cors';
8
-
9
- interface HotReloadConfig {
10
- /**
11
- * Entry point file to run.
12
- * Reserved for future standalone mode — not used in integrated mode.
13
- */
14
- entry?: string;
15
- /**
16
- * Runtime to use for spawning the child process.
17
- * - 'node' : compiled JS only, supports IPC hot reload for .env
18
- * - 'tsx' : runs TypeScript directly via tsx
19
- * - 'ts-node' : runs TypeScript directly via ts-node
20
- * @default 'node'
21
- */
22
- runtime?: 'node' | 'tsx' | 'ts-node';
23
- /**
24
- * Extra arguments passed to the runtime before the entry file.
25
- * Example: ['--experimental-specifier-resolution=node']
26
- */
27
- runtimeArgs?: string[];
28
- /**
29
- * Root directory to watch for file changes.
30
- * @default process.cwd()
31
- */
32
- rootDir?: string;
33
- /**
34
- * Additional directories or glob patterns to watch.
35
- * Merged with the default watched extensions (.ts, .js, .env, .json).
36
- * Example: ['config', 'locales']
37
- */
38
- watchDirs?: string[];
39
- /**
40
- * File extensions to watch.
41
- * @default ['.ts', '.js', '.mjs', '.cjs', '.json', '.env']
42
- */
43
- watchExtensions?: string[];
44
- /**
45
- * Patterns / file names to ignore in addition to the built-in ignores.
46
- * Built-in ignores: node_modules, dist, .git, package.json, package-lock.json
47
- * Example: ['coverage', 'tmp', 'myIgnored.json']
48
- */
49
- ignore?: string[];
50
- /**
51
- * Path to the .env file that should be hot-reloaded without restarting.
52
- * @default '.env'
53
- */
54
- envFile?: string;
55
- /**
56
- * File extensions that support in-process hot reload (no server restart).
57
- * All other watched extensions trigger a full server restart.
58
- * @default ['.env', '.json']
59
- */
60
- hotReloadExtensions?: string[];
61
- /**
62
- * Milliseconds to wait after the last change before acting (debounce).
63
- * Prevents rapid successive restarts when many files change at once.
64
- * @default 300
65
- */
66
- debounceMs?: number;
67
- /**
68
- * Automatically restart the child process if it exits unexpectedly.
69
- * @default true
70
- */
71
- restartOnCrash?: boolean;
72
- /**
73
- * Maximum number of automatic restarts on crash before giving up.
74
- * @default 5
75
- */
76
- maxCrashRestarts?: number;
77
- }
8
+ import { HotReloadConfig } from 'opticore-watcher';
78
9
 
79
10
  interface WebServerConstructorInterface {
80
11
  app: express.Application;
@@ -180,54 +111,6 @@ declare class WebServerCore {
180
111
 
181
112
  declare const envPath: string;
182
113
 
183
- declare class HotReloadWatcher {
184
- private readonly cfg;
185
- private server;
186
- private watchers;
187
- private debounceTimer;
188
- private restarting;
189
- constructor(config?: HotReloadConfig);
190
- attach(server: Server): Promise<void>;
191
- private setupWatchers;
192
- private watchRecursive;
193
- private onFileChange;
194
- private doHotReload;
195
- private scheduleRestart;
196
- /**
197
- * Gracefully close the HTTP server so in-flight requests can finish,
198
- * then exit with code 0 — the external runner restarts the process.
199
- */
200
- private closeAndExit;
201
- private shouldIgnoreDir;
202
- private shouldIgnoreFile;
203
- private isWatched;
204
- private isHotReloadable;
205
- private isEnvFile;
206
- private strip;
207
- private ts;
208
- /**
209
- * Startup banner — same bgGreen box style as CoreService.infoServer().
210
- *
211
- * OPTICORE HOT RELOAD ← gradient
212
- * ████████████████████████████████ ← bgGreen border
213
- * root ./src
214
- * watching .ts .js .json .env
215
- * debounce 300ms
216
- * ████████████████████████████████ ← bgGreen border
217
- * watching for changes...
218
- */
219
- private printBanner;
220
- /**
221
- * ✔ [ HOT ] 14:23:45 | .env → env variables reloaded
222
- */
223
- private printHot;
224
- /**
225
- * ⚡ [ RELOAD ] 14:24:03 | src/routes/user.ts → restarting server...
226
- */
227
- private printReloading;
228
- private setupProcessSignals;
229
- }
230
-
231
114
  type KernelModuleType = [any[], () => void];
232
115
 
233
- export { type HotReloadConfig, HotReloadWatcher, type KernelModuleType, WebServerCore as WebServer, envPath };
116
+ export { type KernelModuleType, WebServerCore as WebServer, envPath };
package/dist/index.d.ts CHANGED
@@ -5,76 +5,7 @@ import { TDependency } from 'opticore-dependency-inject';
5
5
  import { express } from 'opticore-express';
6
6
  import { LoggerCore } from 'opticore-logger';
7
7
  import { CorsOptions } from 'cors';
8
-
9
- interface HotReloadConfig {
10
- /**
11
- * Entry point file to run.
12
- * Reserved for future standalone mode — not used in integrated mode.
13
- */
14
- entry?: string;
15
- /**
16
- * Runtime to use for spawning the child process.
17
- * - 'node' : compiled JS only, supports IPC hot reload for .env
18
- * - 'tsx' : runs TypeScript directly via tsx
19
- * - 'ts-node' : runs TypeScript directly via ts-node
20
- * @default 'node'
21
- */
22
- runtime?: 'node' | 'tsx' | 'ts-node';
23
- /**
24
- * Extra arguments passed to the runtime before the entry file.
25
- * Example: ['--experimental-specifier-resolution=node']
26
- */
27
- runtimeArgs?: string[];
28
- /**
29
- * Root directory to watch for file changes.
30
- * @default process.cwd()
31
- */
32
- rootDir?: string;
33
- /**
34
- * Additional directories or glob patterns to watch.
35
- * Merged with the default watched extensions (.ts, .js, .env, .json).
36
- * Example: ['config', 'locales']
37
- */
38
- watchDirs?: string[];
39
- /**
40
- * File extensions to watch.
41
- * @default ['.ts', '.js', '.mjs', '.cjs', '.json', '.env']
42
- */
43
- watchExtensions?: string[];
44
- /**
45
- * Patterns / file names to ignore in addition to the built-in ignores.
46
- * Built-in ignores: node_modules, dist, .git, package.json, package-lock.json
47
- * Example: ['coverage', 'tmp', 'myIgnored.json']
48
- */
49
- ignore?: string[];
50
- /**
51
- * Path to the .env file that should be hot-reloaded without restarting.
52
- * @default '.env'
53
- */
54
- envFile?: string;
55
- /**
56
- * File extensions that support in-process hot reload (no server restart).
57
- * All other watched extensions trigger a full server restart.
58
- * @default ['.env', '.json']
59
- */
60
- hotReloadExtensions?: string[];
61
- /**
62
- * Milliseconds to wait after the last change before acting (debounce).
63
- * Prevents rapid successive restarts when many files change at once.
64
- * @default 300
65
- */
66
- debounceMs?: number;
67
- /**
68
- * Automatically restart the child process if it exits unexpectedly.
69
- * @default true
70
- */
71
- restartOnCrash?: boolean;
72
- /**
73
- * Maximum number of automatic restarts on crash before giving up.
74
- * @default 5
75
- */
76
- maxCrashRestarts?: number;
77
- }
8
+ import { HotReloadConfig } from 'opticore-watcher';
78
9
 
79
10
  interface WebServerConstructorInterface {
80
11
  app: express.Application;
@@ -180,54 +111,6 @@ declare class WebServerCore {
180
111
 
181
112
  declare const envPath: string;
182
113
 
183
- declare class HotReloadWatcher {
184
- private readonly cfg;
185
- private server;
186
- private watchers;
187
- private debounceTimer;
188
- private restarting;
189
- constructor(config?: HotReloadConfig);
190
- attach(server: Server): Promise<void>;
191
- private setupWatchers;
192
- private watchRecursive;
193
- private onFileChange;
194
- private doHotReload;
195
- private scheduleRestart;
196
- /**
197
- * Gracefully close the HTTP server so in-flight requests can finish,
198
- * then exit with code 0 — the external runner restarts the process.
199
- */
200
- private closeAndExit;
201
- private shouldIgnoreDir;
202
- private shouldIgnoreFile;
203
- private isWatched;
204
- private isHotReloadable;
205
- private isEnvFile;
206
- private strip;
207
- private ts;
208
- /**
209
- * Startup banner — same bgGreen box style as CoreService.infoServer().
210
- *
211
- * OPTICORE HOT RELOAD ← gradient
212
- * ████████████████████████████████ ← bgGreen border
213
- * root ./src
214
- * watching .ts .js .json .env
215
- * debounce 300ms
216
- * ████████████████████████████████ ← bgGreen border
217
- * watching for changes...
218
- */
219
- private printBanner;
220
- /**
221
- * ✔ [ HOT ] 14:23:45 | .env → env variables reloaded
222
- */
223
- private printHot;
224
- /**
225
- * ⚡ [ RELOAD ] 14:24:03 | src/routes/user.ts → restarting server...
226
- */
227
- private printReloading;
228
- private setupProcessSignals;
229
- }
230
-
231
114
  type KernelModuleType = [any[], () => void];
232
115
 
233
- export { type HotReloadConfig, HotReloadWatcher, type KernelModuleType, WebServerCore as WebServer, envPath };
116
+ export { type KernelModuleType, WebServerCore as WebServer, envPath };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/core/webServer.core.ts
2
2
  import * as path3 from "path";
3
- import process6 from "process";
3
+ import process5 from "process";
4
4
  import corsOrigin from "cors";
5
5
  import { CEventNameError as eventName2, ServerListenEventError as ServerListenEventError2 } from "opticore-catch-exception-error";
6
6
  import { express as express2 } from "opticore-express";
@@ -10,7 +10,7 @@ import { TranslationLoader as TranslationLoader3 } from "opticore-translator";
10
10
  import { requestCallsEvent } from "opticore-request-call-event";
11
11
 
12
12
  // src/core/handlers/eventProcess.handler.ts
13
- import process2 from "process";
13
+ import process from "process";
14
14
  import EventEmitter from "events";
15
15
  import { express } from "opticore-express";
16
16
  import { ServerListenEventError, CEventNameError as eventName, CEvent as event } from "opticore-catch-exception-error";
@@ -21,42 +21,42 @@ function eventProcessHandler(localeLanguage) {
21
21
  errorEmitter.on(eventName.error, (error) => {
22
22
  serverListenEvent.listenerError(error);
23
23
  });
24
- process2.on(event.beforeExit, (code) => {
24
+ process.on(event.beforeExit, (code) => {
25
25
  setTimeout(() => {
26
26
  serverListenEvent.processBeforeExit(code);
27
27
  }, 100);
28
28
  });
29
- process2.on(event.disconnect, () => {
29
+ process.on(event.disconnect, () => {
30
30
  serverListenEvent.processDisconnected();
31
31
  });
32
- process2.on(event.exit, (code) => {
32
+ process.on(event.exit, (code) => {
33
33
  serverListenEvent.exited(code);
34
34
  });
35
- process2.on(event.rejectionHandled, (promise) => {
35
+ process.on(event.rejectionHandled, (promise) => {
36
36
  serverListenEvent.promiseRejectionHandled(promise);
37
37
  });
38
- process2.on(event.uncaughtException, (error) => {
38
+ process.on(event.uncaughtException, (error) => {
39
39
  serverListenEvent.uncaughtException(error);
40
40
  });
41
- process2.on(event.uncaughtExceptionMonitor, (error) => {
41
+ process.on(event.uncaughtExceptionMonitor, (error) => {
42
42
  serverListenEvent.uncaughtExceptionMonitor(error);
43
43
  });
44
- process2.on(event.unhandledRejection, (reason, promise) => {
44
+ process.on(event.unhandledRejection, (reason, promise) => {
45
45
  serverListenEvent.unhandledRejection(reason, promise);
46
46
  });
47
- process2.on(event.warning, (warning) => {
47
+ process.on(event.warning, (warning) => {
48
48
  serverListenEvent.warning(warning);
49
49
  });
50
- process2.on(event.message, (message) => {
50
+ process.on(event.message, (message) => {
51
51
  serverListenEvent.message(message);
52
52
  });
53
- process2.on(event.multipleResolves, (type, promise, reason) => {
53
+ process.on(event.multipleResolves, (type, promise, reason) => {
54
54
  serverListenEvent.multipleResolves(type, promise, reason);
55
55
  });
56
- process2.on(event.sigint, () => {
56
+ process.on(event.sigint, () => {
57
57
  serverListenEvent.processInterrupted();
58
58
  });
59
- process2.on(event.sigterm, (signal) => {
59
+ process.on(event.sigterm, (signal) => {
60
60
  serverListenEvent.sigtermSignalReceived(signal);
61
61
  });
62
62
  app.use((err, req, res, next) => {
@@ -65,7 +65,7 @@ function eventProcessHandler(localeLanguage) {
65
65
  }
66
66
 
67
67
  // src/application/service/core.service.ts
68
- import process4 from "process";
68
+ import process3 from "process";
69
69
  import chalk from "chalk";
70
70
  import * as path from "path";
71
71
  import * as fs from "fs";
@@ -75,8 +75,8 @@ import { TranslationLoader } from "opticore-translator";
75
75
  import { getEnvironmentValue } from "opticore-env-access";
76
76
 
77
77
  // src/utils/envPath.utils.ts
78
- import process3 from "process";
79
- var envPath = process3.cwd() + "/config/env/.env";
78
+ import process2 from "process";
79
+ var envPath = process2.cwd() + "/config/env/.env";
80
80
 
81
81
  // src/application/service/loaderTranslationFile.service.ts
82
82
  import { translationLoaderConfig } from "opticore-loader-translation";
@@ -167,7 +167,7 @@ var CoreService = class {
167
167
  getEnvFileLoading(filePath) {
168
168
  this.loadTranslationFiles();
169
169
  try {
170
- const fullPath = path.resolve(process4.cwd(), filePath);
170
+ const fullPath = path.resolve(process3.cwd(), filePath);
171
171
  if (fs.existsSync(fullPath)) {
172
172
  const env = fs.readFileSync(fullPath, "utf-8");
173
173
  const lines = env.split("\n");
@@ -175,7 +175,7 @@ var CoreService = class {
175
175
  const match = line.match(/^([^#=]+)=([^#]+)$/);
176
176
  if (match) {
177
177
  const key = match[1].trim();
178
- process4.env[key] = match[2].trim();
178
+ process3.env[key] = match[2].trim();
179
179
  }
180
180
  });
181
181
  }
@@ -193,7 +193,7 @@ var CoreService = class {
193
193
  * Returns an Object containing a node version, openssl, and v0
194
194
  */
195
195
  getVersions() {
196
- const { node, openssl, v8 } = process4.versions;
196
+ const { node, openssl, v8 } = process3.versions;
197
197
  const data = {
198
198
  "node version": node,
199
199
  "openssl": openssl,
@@ -214,14 +214,14 @@ var CoreService = class {
214
214
  */
215
215
  getUsageMemory() {
216
216
  this.loadTranslationFiles();
217
- const memoryData = process4.memoryUsage();
217
+ const memoryData = process3.memoryUsage();
218
218
  const data = {
219
219
  [TranslationLoader.t("totalMemoryAllocated", this.localLanguage)]: this.formatMemoryUsage(memoryData.rss),
220
220
  [TranslationLoader.t("sizeAllocatedHeap", this.localLanguage)]: this.formatMemoryUsage(memoryData.heapTotal),
221
221
  [TranslationLoader.t("memoryUsedExecution", this.localLanguage)]: this.formatMemoryUsage(memoryData.heapUsed),
222
222
  [TranslationLoader.t("externalMemory", this.localLanguage)]: this.formatMemoryUsage(memoryData.external),
223
- [TranslationLoader.t("memoryUsageUser", this.localLanguage)]: this.formatMemoryUsage(process4.cpuUsage().user),
224
- [TranslationLoader.t("memoryUsageSystem", this.localLanguage)]: this.formatMemoryUsage(process4.cpuUsage().system)
223
+ [TranslationLoader.t("memoryUsageUser", this.localLanguage)]: this.formatMemoryUsage(process3.cpuUsage().user),
224
+ [TranslationLoader.t("memoryUsageSystem", this.localLanguage)]: this.formatMemoryUsage(process3.cpuUsage().system)
225
225
  };
226
226
  return {
227
227
  "rss": data[TranslationLoader.t("totalMemoryAllocated", this.localLanguage)],
@@ -230,18 +230,18 @@ var CoreService = class {
230
230
  "external": data[TranslationLoader.t("externalMemory", this.localLanguage)],
231
231
  "user": data[TranslationLoader.t("memoryUsageUser", this.localLanguage)],
232
232
  "system": data[TranslationLoader.t("memoryUsageSystem", this.localLanguage)],
233
- "pid": process4.pid
233
+ "pid": process3.pid
234
234
  };
235
235
  }
236
236
  /**
237
237
  * Return an Object containing a project path and running server time
238
238
  */
239
239
  getProjectInfo() {
240
- const startTime = process4.hrtime();
241
- const endTime = process4.hrtime(startTime);
240
+ const startTime = process3.hrtime();
241
+ const endTime = process3.hrtime(startTime);
242
242
  const executionTime = (endTime[0] * 1e9 + endTime[1]) / 1e6;
243
243
  return {
244
- "projectPath": path.join(process4.cwd()),
244
+ "projectPath": path.join(process3.cwd()),
245
245
  "startingTime": `${executionTime.toFixed(5)} ms`
246
246
  };
247
247
  }
@@ -290,7 +290,7 @@ var CoreService = class {
290
290
  const maxLength = Math.max(...messages.map((m) => {
291
291
  return m.replace(/\u001b\[[0-9]{1,2}m/g, "").length;
292
292
  })) + 4;
293
- console.log(chalk.blackBright(`${TranslationLoader.t("tailingServerLog", this.localLanguage)} (${path.join(path.basename(process4.cwd()), "logs", "app.log")})`));
293
+ console.log(chalk.blackBright(`${TranslationLoader.t("tailingServerLog", this.localLanguage)} (${path.join(path.basename(process3.cwd()), "logs", "app.log")})`));
294
294
  const border = chalk.bgGreen.white(" ".repeat(maxLength));
295
295
  console.log(border);
296
296
  messages.forEach((msg) => {
@@ -395,7 +395,7 @@ import { SContainer as SContainer3 } from "opticore-dependency-inject";
395
395
 
396
396
  // src/application/service/getEnvFileLoading.service.ts
397
397
  import path2 from "path";
398
- import process5 from "process";
398
+ import process4 from "process";
399
399
  import fs2 from "fs";
400
400
  import { TranslationLoader as TranslationLoader2 } from "opticore-translator";
401
401
  import { HttpStatusCode as status2 } from "opticore-http-response";
@@ -403,7 +403,7 @@ var getEnvFileLoadingService = (filePath, localLanguage) => {
403
403
  loaderTranslationFile(localLanguage);
404
404
  const logger = dependenciesContainerProvider(localLanguage).resolve("LoggerCore");
405
405
  try {
406
- const fullPath = path2.resolve(process5.cwd(), filePath);
406
+ const fullPath = path2.resolve(process4.cwd(), filePath);
407
407
  if (fs2.existsSync(fullPath)) {
408
408
  const env = fs2.readFileSync(fullPath, "utf-8");
409
409
  const lines = env.split("\n");
@@ -411,7 +411,7 @@ var getEnvFileLoadingService = (filePath, localLanguage) => {
411
411
  const match = line.match(/^([^#=]+)=([^#]+)$/);
412
412
  if (match) {
413
413
  const key = match[1].trim();
414
- process5.env[key] = match[2].trim();
414
+ process4.env[key] = match[2].trim();
415
415
  }
416
416
  });
417
417
  }
@@ -426,245 +426,8 @@ var getEnvFileLoadingService = (filePath, localLanguage) => {
426
426
  }
427
427
  };
428
428
 
429
- // src/hotReload/hotReload.watcher.ts
430
- import { watch as fsWatch } from "fs";
431
- import { readdir, access } from "fs/promises";
432
- import { join as join2, resolve as resolve2, extname, relative, basename as basename2 } from "path";
433
- import { config as dotenvConfig } from "dotenv";
434
- import chalk2 from "chalk";
435
- import colors2 from "ansi-colors";
436
- import gradient from "gradient-string";
437
- var DEFAULT_WATCH_EXTENSIONS = [".ts", ".js", ".mjs", ".cjs", ".json"];
438
- var DEFAULT_HOT_RELOAD_EXTENSIONS = [".json"];
439
- var DEFAULT_IGNORE = [
440
- "node_modules",
441
- "dist",
442
- ".git",
443
- "package.json",
444
- "package-lock.json",
445
- ".idea",
446
- ".vscode",
447
- "coverage",
448
- "logs"
449
- ];
450
- var DEFAULT_DEBOUNCE_MS = 300;
451
- var HotReloadWatcher = class {
452
- cfg;
453
- server = null;
454
- watchers = [];
455
- debounceTimer = null;
456
- restarting = false;
457
- constructor(config) {
458
- const c = config ?? {};
459
- this.cfg = {
460
- rootDir: resolve2(c.rootDir ?? process.cwd()),
461
- watchDirs: c.watchDirs ?? [],
462
- watchExtensions: c.watchExtensions ?? DEFAULT_WATCH_EXTENSIONS,
463
- ignore: [...DEFAULT_IGNORE, ...c.ignore ?? []],
464
- envFile: c.envFile ?? ".env",
465
- hotReloadExtensions: c.hotReloadExtensions ?? DEFAULT_HOT_RELOAD_EXTENSIONS,
466
- debounceMs: c.debounceMs ?? DEFAULT_DEBOUNCE_MS
467
- };
468
- }
469
- // ─── Called by WebServerCore once the HTTP server is listening ────────────
470
- async attach(server) {
471
- this.server = server;
472
- this.printBanner();
473
- await this.setupWatchers();
474
- this.setupProcessSignals();
475
- }
476
- // ─── File watching ────────────────────────────────────────────────────────
477
- async setupWatchers() {
478
- const dirs = [
479
- this.cfg.rootDir,
480
- ...this.cfg.watchDirs.map((d) => resolve2(d))
481
- ];
482
- for (const dir of dirs) {
483
- await this.watchRecursive(dir);
484
- }
485
- }
486
- async watchRecursive(dir) {
487
- if (this.shouldIgnoreDir(dir)) return;
488
- try {
489
- await access(dir);
490
- } catch {
491
- return;
492
- }
493
- try {
494
- const w = fsWatch(dir, (_, filename) => {
495
- if (filename) this.onFileChange(join2(dir, filename));
496
- });
497
- w.on("error", () => {
498
- });
499
- this.watchers.push(w);
500
- const entries = await readdir(dir, { withFileTypes: true });
501
- for (const entry of entries) {
502
- if (entry.isDirectory()) await this.watchRecursive(join2(dir, entry.name));
503
- }
504
- } catch {
505
- }
506
- }
507
- // ─── Change handling ──────────────────────────────────────────────────────
508
- onFileChange(filePath) {
509
- if (this.shouldIgnoreFile(filePath)) return;
510
- if (!this.isWatched(filePath)) return;
511
- if (this.restarting) return;
512
- const rel = relative(this.cfg.rootDir, filePath);
513
- if (this.isHotReloadable(filePath)) {
514
- this.doHotReload(filePath, rel);
515
- } else {
516
- this.scheduleRestart(rel);
517
- }
518
- }
519
- doHotReload(filePath, rel) {
520
- if (this.isEnvFile(filePath)) {
521
- dotenvConfig({ path: filePath, override: true });
522
- this.printHot(rel, "env variables reloaded");
523
- } else {
524
- this.printHot(rel, "json config reloaded");
525
- }
526
- }
527
- scheduleRestart(rel) {
528
- if (this.debounceTimer) clearTimeout(this.debounceTimer);
529
- this.debounceTimer = setTimeout(() => {
530
- this.restarting = true;
531
- this.printReloading(rel);
532
- this.closeAndExit();
533
- }, this.cfg.debounceMs);
534
- }
535
- /**
536
- * Gracefully close the HTTP server so in-flight requests can finish,
537
- * then exit with code 0 — the external runner restarts the process.
538
- */
539
- closeAndExit() {
540
- if (!this.server) {
541
- process.exit(0);
542
- return;
543
- }
544
- this.server.close(() => {
545
- process.exit(0);
546
- });
547
- setTimeout(() => {
548
- process.exit(0);
549
- }, 3e3).unref();
550
- }
551
- // ─── Filters ──────────────────────────────────────────────────────────────
552
- shouldIgnoreDir(dir) {
553
- return this.cfg.ignore.some(
554
- (p) => dir.includes(`/${p}`) || dir.endsWith(`/${p}`) || dir.endsWith(p)
555
- );
556
- }
557
- shouldIgnoreFile(filePath) {
558
- const name = basename2(filePath);
559
- const rel = relative(this.cfg.rootDir, filePath);
560
- return this.cfg.ignore.some(
561
- (p) => name === p || rel.includes(p) || filePath.includes(`/${p}/`)
562
- );
563
- }
564
- isWatched(filePath) {
565
- if (this.isEnvFile(filePath)) return true;
566
- return this.cfg.watchExtensions.includes(extname(filePath));
567
- }
568
- isHotReloadable(filePath) {
569
- if (this.isEnvFile(filePath)) return true;
570
- return this.cfg.hotReloadExtensions.includes(extname(filePath));
571
- }
572
- isEnvFile(filePath) {
573
- const name = basename2(filePath);
574
- return name === this.cfg.envFile || name.startsWith(".env");
575
- }
576
- // ─── ANSI strip (box width calculation) ───────────────────────────────────
577
- strip(str) {
578
- return str.replace(/\x1b\[[0-9;]*m/g, "");
579
- }
580
- // ─── Timestamp ────────────────────────────────────────────────────────────
581
- ts() {
582
- const n = /* @__PURE__ */ new Date();
583
- return [n.getHours(), n.getMinutes(), n.getSeconds()].map((v) => String(v).padStart(2, "0")).join(":");
584
- }
585
- // ─── Console output ───────────────────────────────────────────────────────
586
- /**
587
- * Startup banner — same bgGreen box style as CoreService.infoServer().
588
- *
589
- * OPTICORE HOT RELOAD ← gradient
590
- * ████████████████████████████████ ← bgGreen border
591
- * root ./src
592
- * watching .ts .js .json .env
593
- * debounce 300ms
594
- * ████████████████████████████████ ← bgGreen border
595
- * watching for changes...
596
- */
597
- printBanner() {
598
- const rootRel = relative(process.cwd(), this.cfg.rootDir) || ".";
599
- const extStr = [...this.cfg.watchExtensions, ".env"].join(" ");
600
- const ignoreParts = this.cfg.ignore.slice(0, 4);
601
- const ignoreMore = this.cfg.ignore.length > 4 ? ` ${chalk2.blackBright(`+${this.cfg.ignore.length - 4} more`)}` : "";
602
- const ignoreStr = chalk2.blackBright(ignoreParts.join(" ")) + ignoreMore;
603
- const TITLE = "OPTICORE HOT RELOAD";
604
- const rows = [
605
- ` root ${chalk2.white(rootRel)}`,
606
- ` watching ${chalk2.cyan(extStr)}`,
607
- ` ignoring ${ignoreStr}`,
608
- ` debounce ${chalk2.white(`${this.cfg.debounceMs}ms`)}`
609
- ];
610
- const maxLen = Math.max(
611
- TITLE.length + 4,
612
- ...rows.map((r) => this.strip(r).length)
613
- ) + 4;
614
- const border = chalk2.bgGreen.white(" ".repeat(maxLen));
615
- const titlePad = " ".repeat(Math.max(0, Math.floor((maxLen - TITLE.length) / 2)));
616
- console.log("\n" + titlePad + gradient(["#43e97b", "#38f9d7", "#00c6fb"])(TITLE));
617
- console.log(border);
618
- for (const row of rows) {
619
- const pad = Math.max(0, maxLen - this.strip(row).length - 2);
620
- console.log(chalk2.bgGreen.white(` ${row}${" ".repeat(pad)} `));
621
- }
622
- console.log(border);
623
- console.log(chalk2.blackBright(` watching for changes...
624
- `));
625
- }
626
- /**
627
- * ✔ [ HOT ] 14:23:45 | .env → env variables reloaded
628
- */
629
- printHot(file, action) {
630
- const badge = colors2.bgCyan(colors2.white.bold(" HOT "));
631
- const filePart = chalk2.cyan.bold(file.padEnd(32));
632
- console.log(
633
- ` ${chalk2.green("\u2714")} ${badge} ${chalk2.blackBright(this.ts())} ${chalk2.blackBright("|")} ${filePart} ${chalk2.green("\u2192")} ${chalk2.green(action)}`
634
- );
635
- }
636
- /**
637
- * ⚡ [ RELOAD ] 14:24:03 | src/routes/user.ts → restarting server...
638
- */
639
- printReloading(file) {
640
- const badge = colors2.bgYellow(colors2.white.bold(" RELOAD "));
641
- const filePart = chalk2.yellow.bold(file.padEnd(32));
642
- console.log(
643
- `
644
- ${chalk2.yellow("\u26A1")} ${badge} ${chalk2.blackBright(this.ts())} ${chalk2.blackBright("|")} ${filePart} ${chalk2.yellow("\u2192")} ${chalk2.yellow("restarting server...")}
645
- `
646
- );
647
- }
648
- // ─── Graceful shutdown on CTRL+C ──────────────────────────────────────────
649
- setupProcessSignals() {
650
- const shutdown = () => {
651
- const badge = colors2.bgBlackBright(colors2.white.bold(" STOPPED"));
652
- console.log(
653
- `
654
- ${chalk2.gray("\u25A0")} ${badge} ${chalk2.blackBright(this.ts())} ${chalk2.blackBright("|")} ${chalk2.gray("watcher stopped")}
655
- `
656
- );
657
- this.watchers.forEach((w) => {
658
- w.close();
659
- });
660
- process.exit(0);
661
- };
662
- process.once("SIGINT", shutdown);
663
- process.once("SIGTERM", shutdown);
664
- }
665
- };
666
-
667
429
  // src/core/webServer.core.ts
430
+ import { HotReloadWatcher } from "opticore-watcher";
668
431
  var WebServerCore = class {
669
432
  serverUtility;
670
433
  expressApp = express2();
@@ -753,7 +516,7 @@ var WebServerCore = class {
753
516
  this.registerDependencies(dependenciesProvider);
754
517
  }
755
518
  this.container.loadServices();
756
- this.expressApp.use(express2.static(path3.join(process6.cwd(), "public/template")));
519
+ this.expressApp.use(express2.static(path3.join(process5.cwd(), "public/template")));
757
520
  this.registerRoutes(routers);
758
521
  const resolvedHmr = this.resolveHotReloadConfig();
759
522
  if (resolvedHmr !== null) {
@@ -896,7 +659,6 @@ var WebServerCore = class {
896
659
  }
897
660
  };
898
661
  export {
899
- HotReloadWatcher,
900
662
  WebServerCore as WebServer,
901
663
  envPath
902
664
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opticore-webapp",
3
- "version": "1.0.72",
3
+ "version": "1.0.74",
4
4
  "description": "wep server",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -44,7 +44,8 @@
44
44
  "opticore-router": "^1.0.20",
45
45
  "opticore-server-logger": "^1.0.7",
46
46
  "opticore-translator": "^1.0.11",
47
- "opticore-validator": "^1.0.3"
47
+ "opticore-validator": "^1.0.3",
48
+ "opticore-watcher": "^1.0.26"
48
49
  },
49
50
  "devDependencies": {
50
51
  "@types/cors": "^2.8.18",