@zudojs/core 1.0.0 → 1.2.0
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/README.md +15 -5
- package/dist/application/applicationContext.context.d.ts +12 -1
- package/dist/application/applicationContext.context.js +10 -2
- package/dist/application/createApplication.js +6 -1
- package/dist/configuration/configurationManager.manager.d.ts +10 -0
- package/dist/configuration/configurationManager.manager.js +19 -1
- package/dist/container/container.d.ts +4 -3
- package/dist/container/container.js +15 -10
- package/dist/container/container.lifetime.d.ts +21 -0
- package/dist/container/container.lifetime.js +39 -0
- package/dist/container/scope.d.ts +3 -1
- package/dist/context/provider/contextStorage.storage.d.ts +8 -0
- package/dist/context/provider/contextStorage.storage.js +12 -1
- package/dist/lifecycle/core/lifecycle.d.ts +8 -3
- package/dist/lifecycle/core/lifecycle.js +18 -5
- package/dist/lifecycle/core/lifecycle.rollback.d.ts +18 -0
- package/dist/lifecycle/core/lifecycle.rollback.js +34 -0
- package/dist/logging/core/logEntry.entry.js +12 -1
- package/dist/modules/moduleLifecycle/index.d.ts +1 -1
- package/dist/modules/moduleLifecycle/moduleLifecycle.lifecycle.d.ts +30 -5
- package/dist/modules/moduleLifecycle/moduleLifecycle.lifecycle.js +58 -17
- package/dist/modules/moduleLifecycle/moduleLifecycle.stateMachine.d.ts +1 -1
- package/dist/modules/moduleLifecycle/moduleLifecycle.stateMachine.js +3 -1
- package/dist/modules/moduleLifecycle/moduleLifecycle.type.d.ts +20 -0
- package/dist/modules/moduleLoader/moduleLoader.loader.js +61 -25
- package/dist/runtime/runtime.d.ts +2 -0
- package/dist/runtime/runtime.js +15 -4
- package/dist/runtime/runtimeBootstrap/pipeline/runtimeBootstrap.pipeline.d.ts +3 -1
- package/dist/runtime/runtimeBootstrap/pipeline/runtimeBootstrap.pipeline.js +29 -5
- package/dist/runtime/runtimeOptions/runtimeOptions.defaults.js +3 -1
- package/dist/runtime/runtimeOptions/runtimeOptions.mode.d.ts +14 -0
- package/dist/runtime/runtimeOptions/runtimeOptions.mode.js +16 -0
- package/dist/runtime/runtimeOptions/runtimeOptions.resolver.js +17 -1
- package/dist/runtime/runtimeOptions/runtimeOptions.type.d.ts +18 -2
- package/dist/runtime/runtimeOptions/runtimeOptions.validation.d.ts +1 -1
- package/dist/runtime/runtimeShutdown/pipeline/runtimeShutdown.pipeline.js +10 -2
- package/dist/runtime/runtimeSignals/runtimeSignals.d.ts +14 -3
- package/dist/runtime/runtimeSignals/runtimeSignals.fatal.d.ts +29 -0
- package/dist/runtime/runtimeSignals/runtimeSignals.fatal.js +51 -0
- package/dist/runtime/runtimeSignals/runtimeSignals.js +15 -4
- package/package.json +7 -3
|
@@ -17,16 +17,24 @@ export async function executeShutdownPipeline(options, services, state, signal,
|
|
|
17
17
|
runtimeName: services.runtimeName,
|
|
18
18
|
};
|
|
19
19
|
if (options.stopModules) {
|
|
20
|
-
await runShutdownPhase("stopping", "stopped", () => moduleLifecycle.stop(), moduleLifecycle, options.continueOnStopError, (count) => {
|
|
20
|
+
await runShutdownPhase("stopping", "stopped", () => moduleLifecycle.stop(phaseOptions(options.continueOnStopError)), moduleLifecycle, options.continueOnStopError, (count) => {
|
|
21
21
|
state.counters.stoppedModules = count;
|
|
22
22
|
}, RuntimeErrorCode.MODULE_STOP_FAILED, identity, state, publish, log);
|
|
23
23
|
}
|
|
24
24
|
if (options.destroyModules) {
|
|
25
|
-
await runShutdownPhase("destroying", "destroyed", () => moduleLifecycle.destroy(), moduleLifecycle, options.continueOnDestroyError, (count) => {
|
|
25
|
+
await runShutdownPhase("destroying", "destroyed", () => moduleLifecycle.destroy(phaseOptions(options.continueOnDestroyError)), moduleLifecycle, options.continueOnDestroyError, (count) => {
|
|
26
26
|
state.counters.destroyedModules = count;
|
|
27
27
|
}, RuntimeErrorCode.MODULE_DESTROY_FAILED, identity, state, publish, log);
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Per-phase options for the ModuleLifecycleManager: the runtime flag
|
|
32
|
+
* relaxes the manager when on and leaves its own setting when off
|
|
33
|
+
* (see the bootstrap pipeline for the rationale).
|
|
34
|
+
*/
|
|
35
|
+
function phaseOptions(continueOnError) {
|
|
36
|
+
return continueOnError ? { continueOnError: true } : {};
|
|
37
|
+
}
|
|
30
38
|
async function runShutdownPhase(phase, donePhase, run, moduleLifecycle, continueOnError, setCount, code, identity, state, publish, log) {
|
|
31
39
|
publish(phase);
|
|
32
40
|
log("debug", `Runtime modules ${phase}.`);
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { Logger } from "../../logging/core/logger.js";
|
|
2
2
|
import type { RuntimeSignalOptions } from "../runtimeOptions/runtimeOptions.type.js";
|
|
3
|
+
/**
|
|
4
|
+
* Fatal-exit settings are optional on the manager so that a
|
|
5
|
+
* hand-assembled `signals` object from before they existed still
|
|
6
|
+
* type-checks; they default to exiting after 10 seconds.
|
|
7
|
+
*/
|
|
8
|
+
type ManagerSignalOptions = Required<Omit<RuntimeSignalOptions, "exitOnFatalError" | "fatalExitTimeout">> & Pick<RuntimeSignalOptions, "exitOnFatalError" | "fatalExitTimeout">;
|
|
3
9
|
/**
|
|
4
10
|
* Termination signals the runtime can react to.
|
|
5
11
|
*/
|
|
@@ -39,7 +45,7 @@ export interface RuntimeSignalHandlers {
|
|
|
39
45
|
* Options for the signal manager.
|
|
40
46
|
*/
|
|
41
47
|
export interface RuntimeSignalManagerOptions {
|
|
42
|
-
readonly signals:
|
|
48
|
+
readonly signals: ManagerSignalOptions;
|
|
43
49
|
readonly target?: RuntimeSignalTarget;
|
|
44
50
|
readonly logger?: Logger;
|
|
45
51
|
}
|
|
@@ -52,8 +58,11 @@ export interface RuntimeSignalManagerOptions {
|
|
|
52
58
|
* `unregister()`; both are idempotent.
|
|
53
59
|
* - The first termination signal triggers `onSignal` (graceful stop).
|
|
54
60
|
* - A second termination signal while the first is still being
|
|
55
|
-
* handled
|
|
56
|
-
* is on,
|
|
61
|
+
* handled exits with `forceExitCode` when `forceExitOnSecondSignal`
|
|
62
|
+
* is on (the default), and is logged and ignored otherwise.
|
|
63
|
+
* - An uncaught exception or unhandled rejection runs the fatal
|
|
64
|
+
* handler and then exits with code 1, unless `exitOnFatalError` is
|
|
65
|
+
* off; `fatalExitTimeout` bounds a shutdown that hangs.
|
|
57
66
|
* - `process.exit` is never called otherwise.
|
|
58
67
|
*/
|
|
59
68
|
export declare class RuntimeSignalManager {
|
|
@@ -88,6 +97,8 @@ export declare class RuntimeSignalManager {
|
|
|
88
97
|
unregister(): void;
|
|
89
98
|
private createListener;
|
|
90
99
|
private handleSignal;
|
|
100
|
+
private runFatal;
|
|
91
101
|
private run;
|
|
92
102
|
}
|
|
103
|
+
export {};
|
|
93
104
|
//# sourceMappingURL=runtimeSignals.d.ts.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Logger } from "../../logging/core/logger.js";
|
|
2
|
+
import type { RuntimeSignalTarget } from "./runtimeSignals.js";
|
|
3
|
+
/** Default grace period for a fatal-error shutdown, in milliseconds. */
|
|
4
|
+
export declare const DEFAULT_FATAL_EXIT_TIMEOUT = 10000;
|
|
5
|
+
/**
|
|
6
|
+
* How a fatal process event (uncaughtException, unhandledRejection)
|
|
7
|
+
* ends the process.
|
|
8
|
+
*/
|
|
9
|
+
export interface RuntimeFatalExitPolicy {
|
|
10
|
+
/** Exit non-zero once the fatal-error shutdown settles. */
|
|
11
|
+
readonly exitOnFatalError: boolean;
|
|
12
|
+
/** Exit anyway if that shutdown takes longer than this. */
|
|
13
|
+
readonly fatalExitTimeout: number;
|
|
14
|
+
/** Exit code used for a fatal exit. */
|
|
15
|
+
readonly exitCode: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Runs the runtime's fatal-error handler and then ends the process
|
|
19
|
+
* with a non-zero code.
|
|
20
|
+
*
|
|
21
|
+
* Installing an `uncaughtException` listener suppresses Node's own
|
|
22
|
+
* crash, so without an explicit exit the process either ended with
|
|
23
|
+
* code 0 (supervisors saw a clean exit and never restarted or alerted)
|
|
24
|
+
* or lingered as a stopped zombie behind any open handle. The grace
|
|
25
|
+
* timer bounds a shutdown that hangs; both are skipped when
|
|
26
|
+
* `exitOnFatalError` is off.
|
|
27
|
+
*/
|
|
28
|
+
export declare function runFatalHandler(handler: () => void | Promise<void> | undefined, target: RuntimeSignalTarget | undefined, policy: RuntimeFatalExitPolicy, logger: Logger | undefined, event: string): void;
|
|
29
|
+
//# sourceMappingURL=runtimeSignals.fatal.d.ts.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** Default grace period for a fatal-error shutdown, in milliseconds. */
|
|
2
|
+
export const DEFAULT_FATAL_EXIT_TIMEOUT = 10_000;
|
|
3
|
+
/**
|
|
4
|
+
* Runs the runtime's fatal-error handler and then ends the process
|
|
5
|
+
* with a non-zero code.
|
|
6
|
+
*
|
|
7
|
+
* Installing an `uncaughtException` listener suppresses Node's own
|
|
8
|
+
* crash, so without an explicit exit the process either ended with
|
|
9
|
+
* code 0 (supervisors saw a clean exit and never restarted or alerted)
|
|
10
|
+
* or lingered as a stopped zombie behind any open handle. The grace
|
|
11
|
+
* timer bounds a shutdown that hangs; both are skipped when
|
|
12
|
+
* `exitOnFatalError` is off.
|
|
13
|
+
*/
|
|
14
|
+
export function runFatalHandler(handler, target, policy, logger, event) {
|
|
15
|
+
const exit = () => {
|
|
16
|
+
if (!policy.exitOnFatalError)
|
|
17
|
+
return;
|
|
18
|
+
target?.exit?.(policy.exitCode);
|
|
19
|
+
};
|
|
20
|
+
let timer;
|
|
21
|
+
if (policy.exitOnFatalError && policy.fatalExitTimeout > 0) {
|
|
22
|
+
timer = setTimeout(() => {
|
|
23
|
+
logger?.error(`Runtime shutdown after ${event} timed out; exiting.`, {
|
|
24
|
+
event,
|
|
25
|
+
timeoutMs: policy.fatalExitTimeout,
|
|
26
|
+
});
|
|
27
|
+
exit();
|
|
28
|
+
}, Math.min(policy.fatalExitTimeout, 2_147_483_647));
|
|
29
|
+
timer.unref?.();
|
|
30
|
+
}
|
|
31
|
+
const finish = () => {
|
|
32
|
+
if (timer !== undefined)
|
|
33
|
+
clearTimeout(timer);
|
|
34
|
+
exit();
|
|
35
|
+
};
|
|
36
|
+
let result;
|
|
37
|
+
try {
|
|
38
|
+
result = handler();
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
logger?.error(`Runtime ${event} handler failed.`, error, { event });
|
|
42
|
+
finish();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
void Promise.resolve(result)
|
|
46
|
+
.catch((error) => {
|
|
47
|
+
logger?.error(`Runtime ${event} handler failed.`, error, { event });
|
|
48
|
+
})
|
|
49
|
+
.finally(finish);
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=runtimeSignals.fatal.js.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DEFAULT_FATAL_EXIT_TIMEOUT, runFatalHandler, } from "./runtimeSignals.fatal.js";
|
|
1
2
|
/**
|
|
2
3
|
* Registers and removes process signal/exception handlers on behalf
|
|
3
4
|
* of the runtime.
|
|
@@ -7,8 +8,11 @@
|
|
|
7
8
|
* `unregister()`; both are idempotent.
|
|
8
9
|
* - The first termination signal triggers `onSignal` (graceful stop).
|
|
9
10
|
* - A second termination signal while the first is still being
|
|
10
|
-
* handled
|
|
11
|
-
* is on,
|
|
11
|
+
* handled exits with `forceExitCode` when `forceExitOnSecondSignal`
|
|
12
|
+
* is on (the default), and is logged and ignored otherwise.
|
|
13
|
+
* - An uncaught exception or unhandled rejection runs the fatal
|
|
14
|
+
* handler and then exits with code 1, unless `exitOnFatalError` is
|
|
15
|
+
* off; `fatalExitTimeout` bounds a shutdown that hangs.
|
|
12
16
|
* - `process.exit` is never called otherwise.
|
|
13
17
|
*/
|
|
14
18
|
export class RuntimeSignalManager {
|
|
@@ -91,11 +95,11 @@ export class RuntimeSignalManager {
|
|
|
91
95
|
};
|
|
92
96
|
case "uncaughtException":
|
|
93
97
|
return ((error) => {
|
|
94
|
-
this.
|
|
98
|
+
this.runFatal(() => this._handlers?.onUncaughtException(error), "uncaughtException");
|
|
95
99
|
});
|
|
96
100
|
case "unhandledRejection":
|
|
97
101
|
return ((reason) => {
|
|
98
|
-
this.
|
|
102
|
+
this.runFatal(() => this._handlers?.onUnhandledRejection(reason), "unhandledRejection");
|
|
99
103
|
});
|
|
100
104
|
default:
|
|
101
105
|
return () => { };
|
|
@@ -118,6 +122,13 @@ export class RuntimeSignalManager {
|
|
|
118
122
|
});
|
|
119
123
|
this.run(() => this._handlers?.onSignal(signal), signal);
|
|
120
124
|
}
|
|
125
|
+
runFatal(handler, event) {
|
|
126
|
+
runFatalHandler(handler, this._target, {
|
|
127
|
+
exitOnFatalError: this._signals.exitOnFatalError ?? true,
|
|
128
|
+
fatalExitTimeout: this._signals.fatalExitTimeout ?? DEFAULT_FATAL_EXIT_TIMEOUT,
|
|
129
|
+
exitCode: 1,
|
|
130
|
+
}, this._logger, event);
|
|
131
|
+
}
|
|
121
132
|
run(handler, event) {
|
|
122
133
|
try {
|
|
123
134
|
const result = handler();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zudojs/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Application lifecycle management, execution context propagation, and runtime orchestration for Zudojs applications.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -55,10 +55,14 @@
|
|
|
55
55
|
"vitest": "^4.1.11"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@zudojs/errors": "1.
|
|
59
|
-
"@zudojs/constants": "1.
|
|
58
|
+
"@zudojs/errors": "1.1.0",
|
|
59
|
+
"@zudojs/constants": "1.1.0"
|
|
60
60
|
},
|
|
61
61
|
"license": "MIT",
|
|
62
|
+
"author": {
|
|
63
|
+
"name": "Oluwayemi Oyinlola",
|
|
64
|
+
"url": "https://github.com/oyinlola-tech"
|
|
65
|
+
},
|
|
62
66
|
"publishConfig": {
|
|
63
67
|
"access": "public"
|
|
64
68
|
},
|