opticore-webapp 1.0.72 → 1.0.73
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 +2 -241
- package/dist/index.d.cts +2 -119
- package/dist/index.d.ts +2 -119
- package/dist/index.js +33 -271
- package/package.json +3 -2
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
24
|
+
process.on(event.beforeExit, (code) => {
|
|
25
25
|
setTimeout(() => {
|
|
26
26
|
serverListenEvent.processBeforeExit(code);
|
|
27
27
|
}, 100);
|
|
28
28
|
});
|
|
29
|
-
|
|
29
|
+
process.on(event.disconnect, () => {
|
|
30
30
|
serverListenEvent.processDisconnected();
|
|
31
31
|
});
|
|
32
|
-
|
|
32
|
+
process.on(event.exit, (code) => {
|
|
33
33
|
serverListenEvent.exited(code);
|
|
34
34
|
});
|
|
35
|
-
|
|
35
|
+
process.on(event.rejectionHandled, (promise) => {
|
|
36
36
|
serverListenEvent.promiseRejectionHandled(promise);
|
|
37
37
|
});
|
|
38
|
-
|
|
38
|
+
process.on(event.uncaughtException, (error) => {
|
|
39
39
|
serverListenEvent.uncaughtException(error);
|
|
40
40
|
});
|
|
41
|
-
|
|
41
|
+
process.on(event.uncaughtExceptionMonitor, (error) => {
|
|
42
42
|
serverListenEvent.uncaughtExceptionMonitor(error);
|
|
43
43
|
});
|
|
44
|
-
|
|
44
|
+
process.on(event.unhandledRejection, (reason, promise) => {
|
|
45
45
|
serverListenEvent.unhandledRejection(reason, promise);
|
|
46
46
|
});
|
|
47
|
-
|
|
47
|
+
process.on(event.warning, (warning) => {
|
|
48
48
|
serverListenEvent.warning(warning);
|
|
49
49
|
});
|
|
50
|
-
|
|
50
|
+
process.on(event.message, (message) => {
|
|
51
51
|
serverListenEvent.message(message);
|
|
52
52
|
});
|
|
53
|
-
|
|
53
|
+
process.on(event.multipleResolves, (type, promise, reason) => {
|
|
54
54
|
serverListenEvent.multipleResolves(type, promise, reason);
|
|
55
55
|
});
|
|
56
|
-
|
|
56
|
+
process.on(event.sigint, () => {
|
|
57
57
|
serverListenEvent.processInterrupted();
|
|
58
58
|
});
|
|
59
|
-
|
|
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
|
|
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
|
|
79
|
-
var envPath =
|
|
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(
|
|
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
|
-
|
|
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 } =
|
|
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 =
|
|
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(
|
|
224
|
-
[TranslationLoader.t("memoryUsageSystem", this.localLanguage)]: this.formatMemoryUsage(
|
|
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":
|
|
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 =
|
|
241
|
-
const endTime =
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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.
|
|
3
|
+
"version": "1.0.73",
|
|
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.25"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
51
|
"@types/cors": "^2.8.18",
|