@zudojs/lifecycle 0.1.0 → 1.1.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/LICENSE +21 -0
- package/README.md +100 -12
- package/dist/lifecycleExecutor/lifecycleExecutor.core.js +36 -5
- package/dist/lifecycleInternal/asyncUtils.core.js +58 -14
- package/dist/lifecycleManager/lifecycleManager.context.d.ts +53 -0
- package/dist/lifecycleManager/lifecycleManager.context.js +25 -0
- package/dist/lifecycleManager/lifecycleManager.core.d.ts +6 -2
- package/dist/lifecycleManager/lifecycleManager.core.js +12 -7
- package/dist/lifecycleManager/lifecycleManager.shutdown.d.ts +4 -1
- package/dist/lifecycleManager/lifecycleManager.shutdown.js +123 -14
- package/dist/lifecycleManager/lifecycleManager.startup.d.ts +6 -0
- package/dist/lifecycleManager/lifecycleManager.startup.js +165 -36
- package/dist/lifecyclePhase/lifecyclePhase.core.d.ts +7 -1
- package/dist/lifecyclePhase/lifecyclePhase.core.js +8 -13
- package/dist/lifecyclePlan/lifecyclePlan.core.js +7 -3
- package/dist/lifecycleSignal/lifecycleSignal.handler.d.ts +2 -2
- package/dist/lifecycleSignal/lifecycleSignal.handler.js +6 -3
- package/package.json +25 -14
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lifecycleComponent/index.d.ts.map +0 -1
- package/dist/lifecycleComponent/index.js.map +0 -1
- package/dist/lifecycleComponent/lifecycleComponent.type.d.ts.map +0 -1
- package/dist/lifecycleComponent/lifecycleComponent.type.js.map +0 -1
- package/dist/lifecycleContext/index.d.ts.map +0 -1
- package/dist/lifecycleContext/index.js.map +0 -1
- package/dist/lifecycleContext/lifecycleContext.type.d.ts.map +0 -1
- package/dist/lifecycleContext/lifecycleContext.type.js.map +0 -1
- package/dist/lifecycleEvents/index.d.ts.map +0 -1
- package/dist/lifecycleEvents/index.js.map +0 -1
- package/dist/lifecycleEvents/lifecycleEvents.core.d.ts.map +0 -1
- package/dist/lifecycleEvents/lifecycleEvents.core.js.map +0 -1
- package/dist/lifecycleExecutor/index.d.ts.map +0 -1
- package/dist/lifecycleExecutor/index.js.map +0 -1
- package/dist/lifecycleExecutor/lifecycleExecutor.core.d.ts.map +0 -1
- package/dist/lifecycleExecutor/lifecycleExecutor.core.js.map +0 -1
- package/dist/lifecycleInternal/asyncUtils.core.d.ts.map +0 -1
- package/dist/lifecycleInternal/asyncUtils.core.js.map +0 -1
- package/dist/lifecycleInternal/dependencyGraph.core.d.ts.map +0 -1
- package/dist/lifecycleInternal/dependencyGraph.core.js.map +0 -1
- package/dist/lifecycleInternal/index.d.ts.map +0 -1
- package/dist/lifecycleInternal/index.js.map +0 -1
- package/dist/lifecycleInternal/topologicalSort.core.d.ts.map +0 -1
- package/dist/lifecycleInternal/topologicalSort.core.js.map +0 -1
- package/dist/lifecycleManager/index.d.ts.map +0 -1
- package/dist/lifecycleManager/index.js.map +0 -1
- package/dist/lifecycleManager/lifecycleManager.context.d.ts.map +0 -1
- package/dist/lifecycleManager/lifecycleManager.context.js.map +0 -1
- package/dist/lifecycleManager/lifecycleManager.core.d.ts.map +0 -1
- package/dist/lifecycleManager/lifecycleManager.core.js.map +0 -1
- package/dist/lifecycleManager/lifecycleManager.shutdown.d.ts.map +0 -1
- package/dist/lifecycleManager/lifecycleManager.shutdown.js.map +0 -1
- package/dist/lifecycleManager/lifecycleManager.startup.d.ts.map +0 -1
- package/dist/lifecycleManager/lifecycleManager.startup.js.map +0 -1
- package/dist/lifecyclePhase/index.d.ts.map +0 -1
- package/dist/lifecyclePhase/index.js.map +0 -1
- package/dist/lifecyclePhase/lifecyclePhase.core.d.ts.map +0 -1
- package/dist/lifecyclePhase/lifecyclePhase.core.js.map +0 -1
- package/dist/lifecyclePhase/lifecyclePhase.type.d.ts +0 -16
- package/dist/lifecyclePhase/lifecyclePhase.type.d.ts.map +0 -1
- package/dist/lifecyclePhase/lifecyclePhase.type.js +0 -17
- package/dist/lifecyclePhase/lifecyclePhase.type.js.map +0 -1
- package/dist/lifecyclePlan/index.d.ts.map +0 -1
- package/dist/lifecyclePlan/index.js.map +0 -1
- package/dist/lifecyclePlan/lifecyclePlan.core.d.ts.map +0 -1
- package/dist/lifecyclePlan/lifecyclePlan.core.js.map +0 -1
- package/dist/lifecycleRegistry/index.d.ts.map +0 -1
- package/dist/lifecycleRegistry/index.js.map +0 -1
- package/dist/lifecycleRegistry/lifecycleRegistry.core.d.ts.map +0 -1
- package/dist/lifecycleRegistry/lifecycleRegistry.core.js.map +0 -1
- package/dist/lifecycleSignal/index.d.ts.map +0 -1
- package/dist/lifecycleSignal/index.js.map +0 -1
- package/dist/lifecycleSignal/lifecycleSignal.handler.d.ts.map +0 -1
- package/dist/lifecycleSignal/lifecycleSignal.handler.js.map +0 -1
- package/dist/lifecycleState/index.d.ts.map +0 -1
- package/dist/lifecycleState/index.js.map +0 -1
- package/dist/lifecycleState/lifecycleState.machine.d.ts.map +0 -1
- package/dist/lifecycleState/lifecycleState.machine.js.map +0 -1
|
@@ -6,27 +6,58 @@
|
|
|
6
6
|
import { LifecyclePhase, LifecycleState } from "@zudojs/constants";
|
|
7
7
|
import { buildExecutionPlan } from "../lifecyclePlan/lifecyclePlan.core.js";
|
|
8
8
|
import { createLifecycleContext } from "../lifecycleContext/lifecycleContext.type.js";
|
|
9
|
-
import { transitionComponent } from "./lifecycleManager.context.js";
|
|
9
|
+
import { emitComponentFailed, recordResult, transitionComponent, wasAttempted, } from "./lifecycleManager.context.js";
|
|
10
10
|
/** Shutdown phases in execution order. */
|
|
11
|
-
const SHUTDOWN_PHASES = [
|
|
11
|
+
const SHUTDOWN_PHASES = [LifecyclePhase.STOP, LifecyclePhase.DISPOSE];
|
|
12
12
|
/**
|
|
13
13
|
* Performs the full shutdown sequence: stop → dispose.
|
|
14
|
-
*
|
|
14
|
+
*
|
|
15
|
+
* Idempotent and single-flight: concurrent callers (a signal handler
|
|
16
|
+
* and a failing startup, say) all await the SAME teardown rather than
|
|
17
|
+
* running overlapping ones.
|
|
15
18
|
*/
|
|
16
|
-
export
|
|
19
|
+
export function performShutdown(ctx) {
|
|
20
|
+
ctx.shutdownPromise ??= runShutdown(ctx);
|
|
21
|
+
return ctx.shutdownPromise;
|
|
22
|
+
}
|
|
23
|
+
async function runShutdown(ctx) {
|
|
17
24
|
if (ctx.state.state === LifecycleState.DISPOSED)
|
|
18
25
|
return;
|
|
26
|
+
// Shutdown must always be possible, including mid-startup. A plain
|
|
27
|
+
// transition() threw LifecycleStateError when shutdown() was called
|
|
28
|
+
// while the application was still INITIALIZING or STARTING, so a
|
|
29
|
+
// signal arriving during startup crashed instead of tearing down.
|
|
19
30
|
if (ctx.state.state !== LifecycleState.STOPPING &&
|
|
20
31
|
ctx.state.state !== LifecycleState.FAILED) {
|
|
21
|
-
ctx.state.
|
|
32
|
+
if (ctx.state.canTransition(LifecycleState.STOPPING)) {
|
|
33
|
+
ctx.state.transition(LifecycleState.STOPPING);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
ctx.state.forceState(LifecycleState.STOPPING);
|
|
37
|
+
}
|
|
22
38
|
}
|
|
23
39
|
ctx.events.emit("application:stopping", {});
|
|
24
40
|
const deadline = Date.now() + ctx.shutdownTimeout;
|
|
41
|
+
// A startup stage still executing must settle before its components
|
|
42
|
+
// are stopped, otherwise `stop()` overlaps the component's own
|
|
43
|
+
// `start()`. Startup itself refuses to launch further stages once
|
|
44
|
+
// `shutdownPromise` is set, so this wait is bounded by one stage.
|
|
45
|
+
if (ctx.inFlight !== undefined) {
|
|
46
|
+
await raceDeadline(ctx, ctx.inFlight.then(() => undefined, () => undefined), Math.max(deadline - Date.now(), 1));
|
|
47
|
+
}
|
|
48
|
+
// The shutdown deadline used to be checked only BETWEEN the two
|
|
49
|
+
// phases, so a single hook that never settled hung shutdown (and the
|
|
50
|
+
// process) forever. Race the whole phase against the remaining
|
|
51
|
+
// budget and abort the run's signal when it expires, so hooks that
|
|
52
|
+
// honour cancellation stop and the rest are abandoned.
|
|
25
53
|
for (const phase of SHUTDOWN_PHASES) {
|
|
26
|
-
|
|
54
|
+
const remaining = deadline - Date.now();
|
|
55
|
+
if (remaining <= 0) {
|
|
56
|
+
ctx.controller.abort(new Error(`Lifecycle shutdown exceeded its ${ctx.shutdownTimeout}ms deadline.`));
|
|
27
57
|
break;
|
|
58
|
+
}
|
|
28
59
|
try {
|
|
29
|
-
await executeShutdownPhase(ctx, phase);
|
|
60
|
+
await raceDeadline(ctx, executeShutdownPhase(ctx, phase), remaining);
|
|
30
61
|
}
|
|
31
62
|
catch {
|
|
32
63
|
// Shutdown must continue even if individual components fail.
|
|
@@ -36,24 +67,102 @@ export async function performShutdown(ctx) {
|
|
|
36
67
|
ctx.events.emit("application:stopped", {
|
|
37
68
|
duration: Date.now() - ctx.startTime,
|
|
38
69
|
});
|
|
70
|
+
ctx.events.emit("application:disposed", {
|
|
71
|
+
duration: Date.now() - ctx.startTime,
|
|
72
|
+
});
|
|
39
73
|
}
|
|
40
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* Resolves when the phase finishes or the shutdown budget runs out.
|
|
76
|
+
*
|
|
77
|
+
* On expiry the run's AbortController is aborted so in-flight hooks
|
|
78
|
+
* observing `context.signal` unwind, and the timer is always cleared
|
|
79
|
+
* so it can never hold the event loop open.
|
|
80
|
+
*/
|
|
81
|
+
async function raceDeadline(ctx, phase, remainingMs) {
|
|
82
|
+
let timer;
|
|
83
|
+
const expiry = new Promise((resolve) => {
|
|
84
|
+
timer = setTimeout(() => {
|
|
85
|
+
ctx.controller.abort(new Error(`Lifecycle shutdown exceeded its ${ctx.shutdownTimeout}ms deadline.`));
|
|
86
|
+
resolve();
|
|
87
|
+
}, remainingMs);
|
|
88
|
+
});
|
|
89
|
+
try {
|
|
90
|
+
await Promise.race([phase, expiry]);
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
if (timer !== undefined) {
|
|
94
|
+
clearTimeout(timer);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// The abandoned phase promise must never surface as an unhandled
|
|
98
|
+
// rejection once the race has been decided.
|
|
99
|
+
void phase.catch(() => { });
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Executes a single shutdown phase across all registered components.
|
|
103
|
+
*
|
|
104
|
+
* A hook only runs for components that reached the matching startup
|
|
105
|
+
* phase: `stop()` when `start` ran, `dispose()` when `initialize` ran.
|
|
106
|
+
* Rollback after an early failure — and `shutdown()` on a manager that
|
|
107
|
+
* was never started — used to call `stop()` on components that had
|
|
108
|
+
* never started; a real server's `close()` throws in that situation
|
|
109
|
+
* and the phantom failure was then recorded against the component.
|
|
110
|
+
*/
|
|
41
111
|
async function executeShutdownPhase(ctx, phase) {
|
|
42
112
|
const plan = buildExecutionPlan(ctx.registry.getAll(), phase);
|
|
43
|
-
const context = createLifecycleContext(phase, ctx.startTime);
|
|
113
|
+
const context = createLifecycleContext(phase, ctx.startTime, ctx.controller.signal);
|
|
114
|
+
const isStop = phase === LifecyclePhase.STOP;
|
|
115
|
+
const prerequisite = isStop
|
|
116
|
+
? LifecyclePhase.START
|
|
117
|
+
: LifecyclePhase.INITIALIZE;
|
|
118
|
+
const successState = isStop
|
|
119
|
+
? LifecycleState.STOPPED
|
|
120
|
+
: LifecycleState.DISPOSED;
|
|
44
121
|
for (const stage of plan.stages) {
|
|
45
122
|
const stageRegs = stage.components
|
|
46
123
|
.map((id) => ctx.registry.get(id))
|
|
47
124
|
.filter((r) => r !== undefined);
|
|
48
125
|
if (stageRegs.length === 0)
|
|
49
126
|
continue;
|
|
127
|
+
const runnable = [];
|
|
50
128
|
for (const reg of stageRegs) {
|
|
51
|
-
|
|
129
|
+
if (!wasAttempted(ctx, reg.id, prerequisite)) {
|
|
130
|
+
// Never reached the phase this hook undoes. It still ends up
|
|
131
|
+
// DISPOSED so status reflects the teardown.
|
|
132
|
+
if (!isStop) {
|
|
133
|
+
transitionComponent(ctx, reg.id, LifecycleState.DISPOSED);
|
|
134
|
+
}
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
runnable.push(reg);
|
|
138
|
+
// Only the stop phase moves a component into STOPPING; dispose
|
|
139
|
+
// runs from STOPPED (or FAILED) and transitions straight to
|
|
140
|
+
// DISPOSED.
|
|
141
|
+
if (isStop) {
|
|
142
|
+
transitionComponent(ctx, reg.id, LifecycleState.STOPPING);
|
|
143
|
+
}
|
|
144
|
+
ctx.events.emit("component:stopping", {
|
|
145
|
+
component: { componentId: reg.id },
|
|
146
|
+
});
|
|
52
147
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
148
|
+
if (runnable.length === 0)
|
|
149
|
+
continue;
|
|
150
|
+
const results = await ctx.executor.executeStage(runnable, phase, context, ctx.concurrency);
|
|
151
|
+
// Shutdown results used to be discarded entirely: a component whose
|
|
152
|
+
// stop() or dispose() threw was still reported as cleanly STOPPED,
|
|
153
|
+
// its failure never reached getStatus() or the event stream, and
|
|
154
|
+
// operators had no way to learn a resource had leaked.
|
|
155
|
+
for (const result of results) {
|
|
156
|
+
recordResult(ctx, result);
|
|
157
|
+
if (result.success) {
|
|
158
|
+
transitionComponent(ctx, result.id, successState);
|
|
159
|
+
ctx.events.emit("component:stopped", {
|
|
160
|
+
component: { componentId: result.id, duration: result.duration },
|
|
161
|
+
});
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
transitionComponent(ctx, result.id, LifecycleState.FAILED);
|
|
165
|
+
emitComponentFailed(ctx, result);
|
|
57
166
|
}
|
|
58
167
|
}
|
|
59
168
|
}
|
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
import type { LifecycleManagerContext } from "./lifecycleManager.context.js";
|
|
7
7
|
/**
|
|
8
8
|
* Performs the full startup sequence: initialize → start → ready.
|
|
9
|
+
*
|
|
10
|
+
* When a CRITICAL component fails, the application is rolled back
|
|
11
|
+
* (stop → dispose) and this function REJECTS with a LifecycleStartError.
|
|
12
|
+
* It previously resolved normally after rolling everything back, so
|
|
13
|
+
* `await manager.start()` reported success for an application that had
|
|
14
|
+
* just been torn down.
|
|
9
15
|
*/
|
|
10
16
|
export declare function performStartup(ctx: LifecycleManagerContext): Promise<void>;
|
|
11
17
|
//# sourceMappingURL=lifecycleManager.startup.d.ts.map
|
|
@@ -4,97 +4,226 @@
|
|
|
4
4
|
* Startup orchestration — initializes, starts, and readies components.
|
|
5
5
|
*/
|
|
6
6
|
import { LifecyclePhase, LifecycleState } from "@zudojs/constants";
|
|
7
|
+
import { LifecycleComponentError, LifecycleStartError } from "@zudojs/errors";
|
|
7
8
|
import { buildExecutionPlan } from "../lifecyclePlan/lifecyclePlan.core.js";
|
|
8
9
|
import { createLifecycleContext } from "../lifecycleContext/lifecycleContext.type.js";
|
|
9
|
-
import { transitionComponent, transitionComponentBatch, emitComponentFailed, } from "./lifecycleManager.context.js";
|
|
10
|
+
import { transitionComponent, transitionComponentBatch, emitComponentFailed, failComponent, markAttempted, recordResult, } from "./lifecycleManager.context.js";
|
|
10
11
|
import { performShutdown } from "./lifecycleManager.shutdown.js";
|
|
11
12
|
/** Maps startup phases to their target component state after success. */
|
|
12
13
|
const SUCCESS_STATE = {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
[LifecyclePhase.INITIALIZE]: LifecycleState.INITIALIZED,
|
|
15
|
+
[LifecyclePhase.START]: LifecycleState.STARTED,
|
|
16
|
+
[LifecyclePhase.READY]: LifecycleState.READY,
|
|
16
17
|
};
|
|
17
18
|
/** Maps startup phases to their component state during execution. */
|
|
18
19
|
const EXECUTING_STATE = {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
[LifecyclePhase.INITIALIZE]: LifecycleState.INITIALIZING,
|
|
21
|
+
[LifecyclePhase.START]: LifecycleState.STARTING,
|
|
22
|
+
[LifecyclePhase.READY]: LifecycleState.STARTED,
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Per-phase component events.
|
|
26
|
+
*
|
|
27
|
+
* Every one of these event types was declared by LifecycleEventType
|
|
28
|
+
* from the first release, but startup only ever emitted the generic
|
|
29
|
+
* "component:starting"/"component:started" pair — and the "starting"
|
|
30
|
+
* event carried a comma-joined list of ids in the `componentId` field
|
|
31
|
+
* instead of a real component id.
|
|
32
|
+
*/
|
|
33
|
+
const PHASE_EVENTS = {
|
|
34
|
+
[LifecyclePhase.INITIALIZE]: {
|
|
35
|
+
begin: "component:initializing",
|
|
36
|
+
end: "component:initialized",
|
|
37
|
+
},
|
|
38
|
+
[LifecyclePhase.START]: {
|
|
39
|
+
begin: "component:starting",
|
|
40
|
+
end: "component:started",
|
|
41
|
+
},
|
|
42
|
+
[LifecyclePhase.READY]: {
|
|
43
|
+
begin: "component:starting",
|
|
44
|
+
end: "component:ready",
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
/** Application-level event emitted when a startup phase begins. */
|
|
48
|
+
const PHASE_APPLICATION_EVENT = {
|
|
49
|
+
[LifecyclePhase.INITIALIZE]: "application:initializing",
|
|
50
|
+
[LifecyclePhase.START]: "application:starting",
|
|
51
|
+
[LifecyclePhase.READY]: "application:starting",
|
|
22
52
|
};
|
|
23
53
|
/**
|
|
24
54
|
* Performs the full startup sequence: initialize → start → ready.
|
|
55
|
+
*
|
|
56
|
+
* When a CRITICAL component fails, the application is rolled back
|
|
57
|
+
* (stop → dispose) and this function REJECTS with a LifecycleStartError.
|
|
58
|
+
* It previously resolved normally after rolling everything back, so
|
|
59
|
+
* `await manager.start()` reported success for an application that had
|
|
60
|
+
* just been torn down.
|
|
25
61
|
*/
|
|
26
62
|
export async function performStartup(ctx) {
|
|
27
63
|
ctx.startTime = Date.now();
|
|
28
64
|
ctx.state.transition(LifecycleState.INITIALIZING);
|
|
29
|
-
ctx.events.emit("application:initializing", {});
|
|
30
65
|
try {
|
|
31
66
|
ctx.registry.freeze();
|
|
32
|
-
const
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
return;
|
|
67
|
+
const failedInit = await executePhase(ctx, LifecyclePhase.INITIALIZE);
|
|
68
|
+
if (failedInit) {
|
|
69
|
+
throw new LifecycleStartError(failedInit.id, failedInit.error);
|
|
36
70
|
}
|
|
71
|
+
assertNotShuttingDown(ctx, LifecyclePhase.INITIALIZE);
|
|
37
72
|
ctx.state.transition(LifecycleState.INITIALIZED);
|
|
73
|
+
ctx.events.emit("application:initialized", {
|
|
74
|
+
duration: Date.now() - ctx.startTime,
|
|
75
|
+
});
|
|
38
76
|
ctx.state.transition(LifecycleState.STARTING);
|
|
39
|
-
const
|
|
40
|
-
if (
|
|
41
|
-
|
|
42
|
-
return;
|
|
77
|
+
const failedStart = await executePhase(ctx, LifecyclePhase.START);
|
|
78
|
+
if (failedStart) {
|
|
79
|
+
throw new LifecycleStartError(failedStart.id, failedStart.error);
|
|
43
80
|
}
|
|
81
|
+
assertNotShuttingDown(ctx, LifecyclePhase.START);
|
|
44
82
|
ctx.state.transition(LifecycleState.STARTED);
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
83
|
+
// A failing `ready` hook on a critical component used to be
|
|
84
|
+
// ignored completely: no state change, no rollback, and start()
|
|
85
|
+
// resolved with the application stuck in STARTED.
|
|
86
|
+
const failedReady = await executePhase(ctx, LifecyclePhase.READY);
|
|
87
|
+
if (failedReady) {
|
|
88
|
+
throw new LifecycleStartError(failedReady.id, failedReady.error);
|
|
51
89
|
}
|
|
90
|
+
assertNotShuttingDown(ctx, LifecyclePhase.READY);
|
|
91
|
+
ctx.state.transition(LifecycleState.READY);
|
|
92
|
+
ctx.events.emit("application:ready", {
|
|
93
|
+
duration: Date.now() - ctx.startTime,
|
|
94
|
+
});
|
|
52
95
|
}
|
|
53
96
|
catch (error) {
|
|
54
|
-
|
|
97
|
+
// Rollback happens on exactly one path, so a completed teardown is
|
|
98
|
+
// never re-entered and its DISPOSED state is never overwritten
|
|
99
|
+
// with FAILED.
|
|
100
|
+
// When the failure IS a requested shutdown, the teardown already
|
|
101
|
+
// owns the application state.
|
|
102
|
+
if (ctx.shutdownPromise === undefined &&
|
|
103
|
+
ctx.state.state !== LifecycleState.FAILED &&
|
|
104
|
+
ctx.state.state !== LifecycleState.DISPOSED) {
|
|
55
105
|
ctx.state.forceState(LifecycleState.FAILED);
|
|
56
106
|
}
|
|
57
107
|
await performShutdown(ctx);
|
|
58
108
|
throw error;
|
|
59
109
|
}
|
|
60
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Throws when a shutdown has been requested while startup is running.
|
|
113
|
+
*
|
|
114
|
+
* Startup used to keep launching later stages after `shutdown()` had
|
|
115
|
+
* already torn everything down: a component started that way was
|
|
116
|
+
* never stopped, and the eventual failure was an opaque
|
|
117
|
+
* LifecycleStateError from the DISPOSED → INITIALIZED transition.
|
|
118
|
+
*/
|
|
119
|
+
function assertNotShuttingDown(ctx, phase) {
|
|
120
|
+
if (ctx.shutdownPromise !== undefined) {
|
|
121
|
+
throw new LifecycleStartError("application", new LifecycleComponentError("application", phase, new Error("Startup was cancelled because shutdown was requested while the application was starting.")));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
61
124
|
/**
|
|
62
125
|
* Executes a single startup phase across all registered components.
|
|
63
|
-
* Returns
|
|
126
|
+
* Returns the failure of the first critical component, or undefined.
|
|
127
|
+
*
|
|
128
|
+
* Two bookkeeping rules apply to every stage:
|
|
129
|
+
*
|
|
130
|
+
* - A component that FAILED an earlier phase, or whose dependency has
|
|
131
|
+
* failed, does not enter this phase. It used to have `start()` and
|
|
132
|
+
* `ready()` invoked after its own `initialize()` had thrown, and its
|
|
133
|
+
* dependents were started as if the dependency were healthy —
|
|
134
|
+
* silently voiding the `dependsOn` contract. A skipped dependent is
|
|
135
|
+
* recorded as FAILED with a LifecycleComponentError naming the
|
|
136
|
+
* failed dependency, and its own `critical` flag decides whether
|
|
137
|
+
* startup aborts.
|
|
138
|
+
*
|
|
139
|
+
* - Every result of a stage is recorded, transitioned and announced
|
|
140
|
+
* before a critical failure aborts the phase. Returning on the first
|
|
141
|
+
* failed result dropped the results of siblings in the same stage,
|
|
142
|
+
* which were then left in INITIALIZING / STARTING forever (no
|
|
143
|
+
* transition leads out of those states except to their success or
|
|
144
|
+
* FAILED) even after rollback had disposed them.
|
|
64
145
|
*/
|
|
65
146
|
async function executePhase(ctx, phase) {
|
|
66
147
|
const plan = buildExecutionPlan(ctx.registry.getAll(), phase);
|
|
67
|
-
const context = createLifecycleContext(phase, ctx.startTime);
|
|
148
|
+
const context = createLifecycleContext(phase, ctx.startTime, ctx.controller.signal);
|
|
149
|
+
const events = PHASE_EVENTS[phase];
|
|
150
|
+
ctx.events.emit(PHASE_APPLICATION_EVENT[phase], {});
|
|
68
151
|
for (const stage of plan.stages) {
|
|
69
152
|
const stageRegs = stage.components
|
|
70
153
|
.map((id) => ctx.registry.get(id))
|
|
71
154
|
.filter((r) => r !== undefined);
|
|
72
155
|
if (stageRegs.length === 0)
|
|
73
156
|
continue;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
157
|
+
assertNotShuttingDown(ctx, phase);
|
|
158
|
+
const runnable = [];
|
|
159
|
+
let criticalFailure;
|
|
160
|
+
for (const reg of stageRegs) {
|
|
161
|
+
if (ctx.componentStates.get(reg.id)?.state === LifecycleState.FAILED) {
|
|
162
|
+
// Already failed in an earlier phase; nothing more to run.
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const failedDependency = reg.dependsOn.find((dep) => ctx.componentStates.get(dep)?.state === LifecycleState.FAILED);
|
|
166
|
+
if (failedDependency === undefined) {
|
|
167
|
+
runnable.push(reg);
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
const skipped = {
|
|
171
|
+
id: reg.id,
|
|
172
|
+
phase,
|
|
173
|
+
duration: 0,
|
|
174
|
+
success: false,
|
|
175
|
+
error: new LifecycleComponentError(reg.id, phase, new Error(`Component "${reg.id}" was not started because its dependency "${failedDependency}" failed.`)),
|
|
176
|
+
};
|
|
177
|
+
recordResult(ctx, skipped);
|
|
178
|
+
failComponent(ctx, reg.id);
|
|
179
|
+
emitComponentFailed(ctx, skipped);
|
|
180
|
+
if (reg.critical) {
|
|
181
|
+
criticalFailure ??= { id: reg.id, error: skipped.error };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (criticalFailure) {
|
|
185
|
+
ctx.state.forceState(LifecycleState.FAILED);
|
|
186
|
+
return criticalFailure;
|
|
187
|
+
}
|
|
188
|
+
if (runnable.length === 0)
|
|
189
|
+
continue;
|
|
190
|
+
transitionComponentBatch(ctx, runnable.map((r) => r.id), EXECUTING_STATE[phase]);
|
|
191
|
+
for (const reg of runnable) {
|
|
192
|
+
markAttempted(ctx, reg.id, phase);
|
|
193
|
+
ctx.events.emit(events.begin, {
|
|
194
|
+
component: { componentId: reg.id },
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
const pending = ctx.executor.executeStage(runnable, phase, context, ctx.concurrency);
|
|
198
|
+
ctx.inFlight = pending;
|
|
199
|
+
let results;
|
|
200
|
+
try {
|
|
201
|
+
results = await pending;
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
ctx.inFlight = undefined;
|
|
205
|
+
}
|
|
79
206
|
for (const result of results) {
|
|
80
|
-
|
|
81
|
-
ctx.results.set(result.id, [...existing, result]);
|
|
207
|
+
recordResult(ctx, result);
|
|
82
208
|
if (!result.success) {
|
|
83
209
|
transitionComponent(ctx, result.id, LifecycleState.FAILED);
|
|
84
210
|
emitComponentFailed(ctx, result);
|
|
85
211
|
if (ctx.registry.get(result.id)?.critical) {
|
|
86
|
-
|
|
87
|
-
return false;
|
|
212
|
+
criticalFailure ??= { id: result.id, error: result.error };
|
|
88
213
|
}
|
|
89
214
|
}
|
|
90
215
|
else {
|
|
91
216
|
transitionComponent(ctx, result.id, SUCCESS_STATE[phase]);
|
|
92
|
-
ctx.events.emit(
|
|
217
|
+
ctx.events.emit(events.end, {
|
|
93
218
|
component: { componentId: result.id, duration: result.duration },
|
|
94
219
|
});
|
|
95
220
|
}
|
|
96
221
|
}
|
|
222
|
+
if (criticalFailure) {
|
|
223
|
+
ctx.state.forceState(LifecycleState.FAILED);
|
|
224
|
+
return criticalFailure;
|
|
225
|
+
}
|
|
97
226
|
}
|
|
98
|
-
return
|
|
227
|
+
return undefined;
|
|
99
228
|
}
|
|
100
229
|
//# sourceMappingURL=lifecycleManager.startup.js.map
|
|
@@ -13,7 +13,13 @@ export declare const SHUTDOWN_PHASES: readonly LifecyclePhase[];
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function getPhaseHookName(phase: LifecyclePhase): string;
|
|
15
15
|
/**
|
|
16
|
-
* Returns the
|
|
16
|
+
* Returns the name of the LifecycleComponent method a phase invokes.
|
|
17
|
+
*
|
|
18
|
+
* This is the same mapping as {@link getPhaseHookName}; the two names
|
|
19
|
+
* exist for readability at the call site. The doc comment previously
|
|
20
|
+
* claimed an "on-prefixed" name, which this function has never
|
|
21
|
+
* returned — component hooks are `initialize`, `start`, `ready`,
|
|
22
|
+
* `stop` and `dispose`.
|
|
17
23
|
*/
|
|
18
24
|
export declare function getComponentMethod(phase: LifecyclePhase): string;
|
|
19
25
|
//# sourceMappingURL=lifecyclePhase.core.d.ts.map
|
|
@@ -33,20 +33,15 @@ export function getPhaseHookName(phase) {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
|
-
* Returns the
|
|
36
|
+
* Returns the name of the LifecycleComponent method a phase invokes.
|
|
37
|
+
*
|
|
38
|
+
* This is the same mapping as {@link getPhaseHookName}; the two names
|
|
39
|
+
* exist for readability at the call site. The doc comment previously
|
|
40
|
+
* claimed an "on-prefixed" name, which this function has never
|
|
41
|
+
* returned — component hooks are `initialize`, `start`, `ready`,
|
|
42
|
+
* `stop` and `dispose`.
|
|
37
43
|
*/
|
|
38
44
|
export function getComponentMethod(phase) {
|
|
39
|
-
|
|
40
|
-
case LifecyclePhase.INITIALIZE:
|
|
41
|
-
return "initialize";
|
|
42
|
-
case LifecyclePhase.START:
|
|
43
|
-
return "start";
|
|
44
|
-
case LifecyclePhase.READY:
|
|
45
|
-
return "ready";
|
|
46
|
-
case LifecyclePhase.STOP:
|
|
47
|
-
return "stop";
|
|
48
|
-
case LifecyclePhase.DISPOSE:
|
|
49
|
-
return "dispose";
|
|
50
|
-
}
|
|
45
|
+
return getPhaseHookName(phase);
|
|
51
46
|
}
|
|
52
47
|
//# sourceMappingURL=lifecyclePhase.core.js.map
|
|
@@ -12,7 +12,7 @@ export function buildExecutionPlan(registrations, phase) {
|
|
|
12
12
|
for (const reg of registrations) {
|
|
13
13
|
priorities.set(reg.id, reg.priority);
|
|
14
14
|
}
|
|
15
|
-
const graph = buildGraphForPhase(registrations
|
|
15
|
+
const graph = buildGraphForPhase(registrations);
|
|
16
16
|
const isShutdown = phase === "stop" || phase === "dispose";
|
|
17
17
|
const sorted = isShutdown
|
|
18
18
|
? reverseTopologicalSort(graph, priorities)
|
|
@@ -24,9 +24,13 @@ export function buildExecutionPlan(registrations, phase) {
|
|
|
24
24
|
return { stages, phase };
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
|
-
* Builds
|
|
27
|
+
* Builds the component dependency graph.
|
|
28
|
+
*
|
|
29
|
+
* The graph is phase-independent: startup and shutdown share the same
|
|
30
|
+
* edges and differ only in traversal direction, which
|
|
31
|
+
* buildExecutionPlan applies.
|
|
28
32
|
*/
|
|
29
|
-
function buildGraphForPhase(registrations
|
|
33
|
+
function buildGraphForPhase(registrations) {
|
|
30
34
|
const graph = new DependencyGraph();
|
|
31
35
|
for (const reg of registrations) {
|
|
32
36
|
graph.addNode(reg.id);
|
|
@@ -10,11 +10,11 @@ export interface SignalHandlerOptions {
|
|
|
10
10
|
/** Function to call when a signal is received. */
|
|
11
11
|
readonly handler: () => void;
|
|
12
12
|
}
|
|
13
|
+
/** Default signal configuration for graceful shutdown. */
|
|
14
|
+
export declare const DEFAULT_SHUTDOWN_SIGNALS: readonly NodeJS.Signals[];
|
|
13
15
|
/**
|
|
14
16
|
* Installs process signal handlers that trigger lifecycle shutdown.
|
|
15
17
|
* Returns a cleanup function to remove the handlers.
|
|
16
18
|
*/
|
|
17
19
|
export declare function installSignalHandlers(options: SignalHandlerOptions): () => void;
|
|
18
|
-
/** Default signal configuration for graceful shutdown. */
|
|
19
|
-
export declare const DEFAULT_SHUTDOWN_SIGNALS: readonly NodeJS.Signals[];
|
|
20
20
|
//# sourceMappingURL=lifecycleSignal.handler.d.ts.map
|
|
@@ -3,12 +3,17 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Process signal handler — manages SIGTERM, SIGINT, SIGHUP for graceful shutdown.
|
|
5
5
|
*/
|
|
6
|
+
/** Default signal configuration for graceful shutdown. */
|
|
7
|
+
export const DEFAULT_SHUTDOWN_SIGNALS = Object.freeze(["SIGINT", "SIGTERM"]);
|
|
6
8
|
/**
|
|
7
9
|
* Installs process signal handlers that trigger lifecycle shutdown.
|
|
8
10
|
* Returns a cleanup function to remove the handlers.
|
|
9
11
|
*/
|
|
10
12
|
export function installSignalHandlers(options) {
|
|
11
|
-
|
|
13
|
+
// DEFAULT_SHUTDOWN_SIGNALS was exported as the documented default
|
|
14
|
+
// while this function hard-coded its own copy of the same list, so
|
|
15
|
+
// changing the constant had no effect on the actual default.
|
|
16
|
+
const signals = options.signals ?? DEFAULT_SHUTDOWN_SIGNALS;
|
|
12
17
|
const handler = options.handler;
|
|
13
18
|
const installed = [];
|
|
14
19
|
for (const signal of signals) {
|
|
@@ -24,6 +29,4 @@ export function installSignalHandlers(options) {
|
|
|
24
29
|
}
|
|
25
30
|
};
|
|
26
31
|
}
|
|
27
|
-
/** Default signal configuration for graceful shutdown. */
|
|
28
|
-
export const DEFAULT_SHUTDOWN_SIGNALS = Object.freeze(["SIGINT", "SIGTERM"]);
|
|
29
32
|
//# sourceMappingURL=lifecycleSignal.handler.js.map
|
package/package.json
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zudojs/lifecycle",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Application and component lifecycle orchestration with state machine, dependency ordering, graceful shutdown, rollback, and signals.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Oluwayemi Oyinlola",
|
|
8
|
+
"url": "https://github.com/oyinlola-tech"
|
|
9
|
+
},
|
|
6
10
|
"type": "module",
|
|
7
11
|
"main": "./dist/index.js",
|
|
8
12
|
"module": "./dist/index.js",
|
|
@@ -14,22 +18,18 @@
|
|
|
14
18
|
}
|
|
15
19
|
},
|
|
16
20
|
"files": [
|
|
17
|
-
"dist"
|
|
21
|
+
"dist",
|
|
22
|
+
"!dist/**/*.map",
|
|
23
|
+
"!dist/**/*.tsbuildinfo",
|
|
24
|
+
"!dist/.tsbuildinfo"
|
|
18
25
|
],
|
|
19
|
-
"scripts": {
|
|
20
|
-
"build": "tsc -p tsconfig.json",
|
|
21
|
-
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
22
|
-
"clean": "rm -rf dist",
|
|
23
|
-
"test": "vitest run",
|
|
24
|
-
"test:watch": "vitest"
|
|
25
|
-
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@zudojs/errors": "0.1
|
|
28
|
-
"@zudojs/constants": "0.1
|
|
27
|
+
"@zudojs/errors": "1.0.1",
|
|
28
|
+
"@zudojs/constants": "1.0.1"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/node": "^26.4.1",
|
|
32
|
-
"typescript": "
|
|
32
|
+
"typescript": "7.0.2",
|
|
33
33
|
"vitest": "^4.1.11"
|
|
34
34
|
},
|
|
35
35
|
"engines": {
|
|
@@ -46,8 +46,19 @@
|
|
|
46
46
|
"shutdown"
|
|
47
47
|
],
|
|
48
48
|
"homepage": "https://github.com/oyinlola-tech/zudo#readme",
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/oyinlola-tech/zudo/issues"
|
|
51
|
+
},
|
|
49
52
|
"repository": {
|
|
50
53
|
"type": "git",
|
|
51
|
-
"url": "https://github.com/oyinlola-tech/zudo"
|
|
54
|
+
"url": "https://github.com/oyinlola-tech/zudo",
|
|
55
|
+
"directory": "packages/lifecycle"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"build": "tsc -p tsconfig.json",
|
|
59
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json --noEmit",
|
|
60
|
+
"clean": "rm -rf dist",
|
|
61
|
+
"test": "vitest run",
|
|
62
|
+
"test:watch": "vitest"
|
|
52
63
|
}
|
|
53
|
-
}
|
|
64
|
+
}
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EACL,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,kBAAkB,EAClB,4BAA4B,EAC5B,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,+BAA+B,CAAC;AACvC,YAAY,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EACL,eAAe,EACf,eAAe,EACf,sBAAsB,EACtB,WAAW,EACX,SAAS,EACT,eAAe,GAChB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,YAAY,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EACL,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,6BAA6B,CAAC;AACrC,YAAY,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAC3E,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,YAAY,EACV,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,EACzB,cAAc,EACd,sBAAsB,GACvB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC"}
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EACL,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,2BAA2B,CAAC;AAQnC,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EACL,eAAe,EACf,eAAe,EACf,sBAAsB,EACtB,WAAW,EACX,SAAS,EACT,eAAe,GAChB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,EACL,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAQnE,OAAO,EACL,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,4BAA4B,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lifecycleComponent/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EACV,kBAAkB,EAClB,4BAA4B,EAC5B,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,8BAA8B,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lifecycleComponent/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycleComponent.type.d.ts","sourceRoot":"","sources":["../../src/lifecycleComponent/lifecycleComponent.type.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8CAA8C,CAAC;AAErF;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,iFAAiF;IACjF,UAAU,CAAC,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtD,iEAAiE;IACjE,KAAK,CAAC,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjD,uEAAuE;IACvE,KAAK,CAAC,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjD,yEAAyE;IACzE,IAAI,CAAC,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhD,gEAAgE;IAChE,OAAO,CAAC,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpD;AAED,qDAAqD;AACrD,MAAM,WAAW,4BAA4B;IAC3C,gEAAgE;IAChE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAErB,yDAAyD;IACzD,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAEvC,gFAAgF;IAChF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B,sFAAsF;IACtF,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAE5B,yDAAyD;IACzD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAE1B,iDAAiD;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,qBAAqB,CAAC;CACxC;AAED,8CAA8C;AAC9C,MAAM,WAAW,qBAAqB;IACpC,wCAAwC;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B,mCAAmC;IACnC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAExB,2CAA2C;IAC3C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B,wBAAwB;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,aAAa,CAAC;CAC5C;AAED,kDAAkD;AAClD,MAAM,WAAW,qBAAqB;IACpC,iBAAiB;IACjB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAEpB,8BAA8B;IAC9B,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IAEvC,2BAA2B;IAC3B,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IAEtC,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAE3B,+CAA+C;IAC/C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,2BAA2B;IAC3B,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAC;CACvC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycleComponent.type.js","sourceRoot":"","sources":["../../src/lifecycleComponent/lifecycleComponent.type.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|