@aromix/core 0.4.4 → 0.4.6
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 +73 -83
- 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): void;
|
|
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,104 +1,94 @@
|
|
|
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
11
|
}
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
function load(loader) {
|
|
13
|
+
assertIdle("add a config loader");
|
|
14
|
+
configLoaders.push(loader);
|
|
18
15
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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--) {
|
|
26
24
|
try {
|
|
27
|
-
await
|
|
25
|
+
await units[i].stop?.();
|
|
28
26
|
} catch (err) {
|
|
29
|
-
|
|
30
|
-
await this.stopAll("start-failure");
|
|
31
|
-
process.exit(1);
|
|
27
|
+
console.error(`[Aromix] "${units[i].name}" failed to stop`, err);
|
|
32
28
|
}
|
|
33
29
|
}
|
|
34
|
-
|
|
35
|
-
this.notifyError(err, "runtime").finally(() => this.gracefulShutdown("uncaughtException"));
|
|
36
|
-
});
|
|
37
|
-
process.on("unhandledRejection", (reason) => {
|
|
38
|
-
const err = reason instanceof Error ? reason : new Error(String(reason));
|
|
39
|
-
this.notifyError(err, "runtime").finally(() => this.gracefulShutdown("unhandledRejection"));
|
|
40
|
-
});
|
|
41
|
-
process.once("SIGINT", () => this.gracefulShutdown("SIGINT"));
|
|
42
|
-
process.once("SIGTERM", () => this.gracefulShutdown("SIGTERM"));
|
|
30
|
+
state = "stopped";
|
|
43
31
|
}
|
|
44
|
-
async
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (block?.error && (phase === "start" || phase === "stop")) {
|
|
50
|
-
await block.error(error, phase);
|
|
51
|
-
}
|
|
52
|
-
for (const handler of this.errorHandlers) {
|
|
53
|
-
await handler(error, phase);
|
|
32
|
+
async function start() {
|
|
33
|
+
assertIdle("start");
|
|
34
|
+
state = "loading";
|
|
35
|
+
for (const loader of configLoaders) {
|
|
36
|
+
await loader();
|
|
54
37
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const block = this.blocks[i];
|
|
62
|
-
const startedAt = Date.now();
|
|
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.`);
|
|
43
|
+
}
|
|
63
44
|
try {
|
|
64
|
-
await
|
|
45
|
+
await unit.start();
|
|
46
|
+
units.push(unit);
|
|
65
47
|
} catch (err) {
|
|
66
|
-
|
|
67
|
-
|
|
48
|
+
await stop();
|
|
49
|
+
throw new Error(`[Aromix] "${unit.name}" failed to start`, { cause: err });
|
|
68
50
|
}
|
|
69
51
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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);
|
|
75
59
|
}
|
|
76
|
-
|
|
60
|
+
return {
|
|
61
|
+
load,
|
|
62
|
+
register,
|
|
63
|
+
start,
|
|
64
|
+
stop
|
|
65
|
+
};
|
|
66
|
+
}
|
|
77
67
|
|
|
78
|
-
// src/common/
|
|
79
|
-
import { resolve } from "path";
|
|
68
|
+
// src/common/LoadEnv.ts
|
|
80
69
|
import { existsSync } from "fs";
|
|
81
|
-
|
|
70
|
+
import { resolve } from "path";
|
|
71
|
+
function LoadEnv(options) {
|
|
82
72
|
const path = resolve(options.path ?? ".env");
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
throw new Error("[Load Env] Schema Validation Failed", {
|
|
94
|
-
cause: issues
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
}
|
|
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
|
+
console.error(issues);
|
|
82
|
+
throw new Error("[LoadEnv] Schema validation failed");
|
|
98
83
|
}
|
|
99
|
-
|
|
84
|
+
values = result;
|
|
85
|
+
}
|
|
86
|
+
function env(key) {
|
|
87
|
+
return values[key];
|
|
88
|
+
}
|
|
89
|
+
return { env };
|
|
100
90
|
}
|
|
101
91
|
export {
|
|
102
|
-
|
|
103
|
-
|
|
92
|
+
LoadEnv,
|
|
93
|
+
system
|
|
104
94
|
};
|