@aromix/core 0.4.3 → 0.4.5
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.d.ts +15 -21
- package/dist/index.js +71 -102
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,32 +1,26 @@
|
|
|
1
1
|
import { AxSchemaShape, AxObjectSchema } from '@aromix/validator';
|
|
2
2
|
|
|
3
|
-
type
|
|
4
|
-
|
|
3
|
+
type Loader<T> = () => Promise<{
|
|
4
|
+
default: T;
|
|
5
|
+
}>;
|
|
6
|
+
interface Unit {
|
|
5
7
|
name: string;
|
|
6
8
|
start(): void | Promise<void>;
|
|
7
9
|
stop?(): void | Promise<void>;
|
|
8
|
-
error?(err: Error, phase: Phase): void | Promise<void>;
|
|
9
10
|
}
|
|
10
|
-
type ErrorHandler = (err: Error, phase: Phase | 'runtime') => void | Promise<void>;
|
|
11
11
|
|
|
12
|
-
declare
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
onError(handler: ErrorHandler): this;
|
|
19
|
-
start(): Promise<void>;
|
|
20
|
-
stop(): Promise<void>;
|
|
21
|
-
private notifyError;
|
|
22
|
-
private stopAll;
|
|
23
|
-
private gracefulShutdown;
|
|
24
|
-
}
|
|
12
|
+
declare function system(): {
|
|
13
|
+
load: (loader: Loader<unknown>) => void;
|
|
14
|
+
register: (loader: Loader<Unit>) => void;
|
|
15
|
+
start: () => Promise<void>;
|
|
16
|
+
stop: () => Promise<void>;
|
|
17
|
+
};
|
|
25
18
|
|
|
26
|
-
|
|
19
|
+
declare function LoadEnv<Shape extends AxSchemaShape = AxSchemaShape>(options: Partial<{
|
|
27
20
|
path: string;
|
|
28
21
|
schema: AxObjectSchema<Shape>;
|
|
29
|
-
}
|
|
30
|
-
|
|
22
|
+
}>): {
|
|
23
|
+
env: <Key extends keyof Shape["base"]>(key: Key) => Shape["base"][Key];
|
|
24
|
+
};
|
|
31
25
|
|
|
32
|
-
export {
|
|
26
|
+
export { LoadEnv, type Unit, system };
|
package/dist/index.js
CHANGED
|
@@ -1,124 +1,93 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (
|
|
9
|
-
throw new Error(`[Aromix]
|
|
1
|
+
// src/system/def.ts
|
|
2
|
+
function system() {
|
|
3
|
+
const configLoaders = [];
|
|
4
|
+
const unitLoaders = [];
|
|
5
|
+
const units = [];
|
|
6
|
+
let state = "idle";
|
|
7
|
+
function assertIdle(action) {
|
|
8
|
+
if (state !== "idle") {
|
|
9
|
+
throw new Error(`[Aromix] Cannot ${action}: system is "${state}", expected "idle".`);
|
|
10
10
|
}
|
|
11
|
-
if (this.blocks.some((b) => b.name === block.name)) {
|
|
12
|
-
throw new Error(`[Aromix] Block with name "${block.name}" is already registered.`);
|
|
13
|
-
}
|
|
14
|
-
this.blocks.push(block);
|
|
15
|
-
console.debug("[Aromix] Block registered", { block: block.name, phase: "register" });
|
|
16
11
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
function load(loader) {
|
|
13
|
+
assertIdle("add a config loader");
|
|
14
|
+
configLoaders.push(loader);
|
|
20
15
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
16
|
+
function register(loader) {
|
|
17
|
+
assertIdle("register a unit");
|
|
18
|
+
unitLoaders.push(loader);
|
|
19
|
+
}
|
|
20
|
+
async function stop() {
|
|
21
|
+
if (state === "stopped" || state === "stopping") return;
|
|
22
|
+
state = "stopping";
|
|
23
|
+
for (let i = units.length - 1; i >= 0; i--) {
|
|
29
24
|
try {
|
|
30
|
-
await
|
|
31
|
-
console.log("[Aromix] Block started", { block: block.name, phase: "start", durationMs: Date.now() - startedAt });
|
|
25
|
+
await units[i].stop?.();
|
|
32
26
|
} catch (err) {
|
|
33
|
-
console.error(
|
|
34
|
-
await this.notifyError(err, "start", block);
|
|
35
|
-
await this.stopAll("start-failure");
|
|
36
|
-
process.exit(1);
|
|
27
|
+
console.error(`[Aromix] "${units[i].name}" failed to stop`, err);
|
|
37
28
|
}
|
|
38
29
|
}
|
|
39
|
-
|
|
40
|
-
console.error("[Aromix] Uncaught exception. Shutting down...", err);
|
|
41
|
-
this.notifyError(err, "runtime").finally(() => this.gracefulShutdown("uncaughtException"));
|
|
42
|
-
});
|
|
43
|
-
process.on("unhandledRejection", (reason) => {
|
|
44
|
-
const err = reason instanceof Error ? reason : new Error(String(reason));
|
|
45
|
-
console.error("[Aromix] Unhandled rejection. Shutting down...", err);
|
|
46
|
-
this.notifyError(err, "runtime").finally(() => this.gracefulShutdown("unhandledRejection"));
|
|
47
|
-
});
|
|
48
|
-
process.once("SIGINT", () => this.gracefulShutdown("SIGINT"));
|
|
49
|
-
process.once("SIGTERM", () => this.gracefulShutdown("SIGTERM"));
|
|
50
|
-
console.log("[Aromix] Application started successfully", { totalBlocks: this.blocks.length });
|
|
30
|
+
state = "stopped";
|
|
51
31
|
}
|
|
52
|
-
async
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (block?.error && (phase === "start" || phase === "stop")) {
|
|
58
|
-
try {
|
|
59
|
-
await block.error(error, phase);
|
|
60
|
-
} catch (handlerErr) {
|
|
61
|
-
console.error(`[Aromix] Block "${block.name}" error handler itself threw`, handlerErr, { block: block.name, phase });
|
|
62
|
-
}
|
|
32
|
+
async function start() {
|
|
33
|
+
assertIdle("start");
|
|
34
|
+
state = "loading";
|
|
35
|
+
for (const loader of configLoaders) {
|
|
36
|
+
await loader();
|
|
63
37
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
38
|
+
state = "starting";
|
|
39
|
+
for (const loader of unitLoaders) {
|
|
40
|
+
const { default: unit } = await loader();
|
|
41
|
+
if (units.some((u) => u.name === unit.name)) {
|
|
42
|
+
throw new Error(`[Aromix] Unit with name "${unit.name}" is already registered.`);
|
|
69
43
|
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
async stopAll(reason) {
|
|
73
|
-
if (this.isStopping) return true;
|
|
74
|
-
this.isStopping = true;
|
|
75
|
-
console.info("[Aromix] Stopping all blocks", { phase: "stop", signal: reason, totalBlocks: this.blocks.length });
|
|
76
|
-
let allStoppedCleanly = true;
|
|
77
|
-
for (let i = this.blocks.length - 1; i >= 0; i--) {
|
|
78
|
-
const block = this.blocks[i];
|
|
79
|
-
const startedAt = Date.now();
|
|
80
44
|
try {
|
|
81
|
-
await
|
|
82
|
-
|
|
45
|
+
await unit.start();
|
|
46
|
+
units.push(unit);
|
|
83
47
|
} catch (err) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
await this.notifyError(err, "stop", block);
|
|
48
|
+
await stop();
|
|
49
|
+
throw new Error(`[Aromix] "${unit.name}" failed to start`, { cause: err });
|
|
87
50
|
}
|
|
88
51
|
}
|
|
89
|
-
|
|
52
|
+
state = "running";
|
|
53
|
+
const onSignal = async () => {
|
|
54
|
+
await stop();
|
|
55
|
+
process.exit(0);
|
|
56
|
+
};
|
|
57
|
+
process.once("SIGINT", onSignal);
|
|
58
|
+
process.once("SIGTERM", onSignal);
|
|
90
59
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
};
|
|
60
|
+
return {
|
|
61
|
+
load,
|
|
62
|
+
register,
|
|
63
|
+
start,
|
|
64
|
+
stop
|
|
65
|
+
};
|
|
66
|
+
}
|
|
97
67
|
|
|
98
|
-
// src/common/
|
|
99
|
-
import { resolve } from "path";
|
|
68
|
+
// src/common/LoadEnv.ts
|
|
100
69
|
import { existsSync } from "fs";
|
|
101
|
-
|
|
70
|
+
import { resolve } from "path";
|
|
71
|
+
function LoadEnv(options) {
|
|
102
72
|
const path = resolve(options.path ?? ".env");
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (issues) {
|
|
113
|
-
throw new Error("[Load Env] Schema Validation Failed", {
|
|
114
|
-
cause: issues
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
}
|
|
73
|
+
if (!existsSync(path)) {
|
|
74
|
+
throw new Error(`[LoadEnv] Environment file not found: ${path}`);
|
|
75
|
+
}
|
|
76
|
+
process.loadEnvFile(path);
|
|
77
|
+
let values = process.env;
|
|
78
|
+
if (options.schema) {
|
|
79
|
+
const [result, issues] = options.schema.parseBase(process.env);
|
|
80
|
+
if (issues) {
|
|
81
|
+
throw new Error("[LoadEnv] Schema validation failed", { cause: JSON.stringify(issues) });
|
|
118
82
|
}
|
|
119
|
-
|
|
83
|
+
values = result;
|
|
84
|
+
}
|
|
85
|
+
function env(key) {
|
|
86
|
+
return values[key];
|
|
87
|
+
}
|
|
88
|
+
return { env };
|
|
120
89
|
}
|
|
121
90
|
export {
|
|
122
|
-
|
|
123
|
-
|
|
91
|
+
LoadEnv,
|
|
92
|
+
system
|
|
124
93
|
};
|