• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

graphty-org / graphty-element / 20514590651

26 Dec 2025 02:37AM UTC coverage: 70.559% (-0.3%) from 70.836%
20514590651

push

github

apowers313
ci: fix npm ci

9591 of 13363 branches covered (71.77%)

Branch coverage included in aggregate %.

25136 of 35854 relevant lines covered (70.11%)

6233.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.81
/src/managers/LifecycleManager.ts
1
import {GraphtyLogger, type Logger} from "../logging/GraphtyLogger.js";
15✔
2
import type {EventManager} from "./EventManager";
3
import type {Manager} from "./interfaces";
4

5
// Type guard for render managers with startRenderLoop method
6
type RenderManagerWithStartLoop = Manager & {startRenderLoop(callback: () => void): void};
7

8
function hasStartRenderLoop(manager: Manager): manager is RenderManagerWithStartLoop {
918✔
9
    return "startRenderLoop" in manager;
918✔
10
}
918✔
11

12
/**
13
 * Manages the lifecycle of all other managers
14
 * Ensures proper initialization order and cleanup
15
 */
16
export class LifecycleManager implements Manager {
15✔
17
    private initialized = false;
15✔
18
    private initializing = false;
15✔
19
    private logger: Logger = GraphtyLogger.getLogger(["graphty", "lifecycle"]);
15✔
20

21
    /**
22
     * Creates a new lifecycle manager for coordinating manager initialization and disposal
23
     * @param managers - Map of manager names to manager instances
24
     * @param eventManager - Event manager for emitting lifecycle events
25
     * @param initOrder - Array of manager names defining initialization order
26
     */
27
    constructor(
15✔
28
        private managers: Map<string, Manager>,
997✔
29
        private eventManager: EventManager,
997✔
30
        private initOrder: string[],
997✔
31
    ) {}
997✔
32

33
    /**
34
     * Initialize all managers in the specified order
35
     */
36
    async init(): Promise<void> {
15✔
37
        if (this.initialized || this.initializing) {
925!
38
            return;
×
39
        }
×
40

41
        this.initializing = true;
925✔
42
        const startTime = performance.now();
925✔
43

44
        this.logger.info("Initializing managers", {
925✔
45
            managerCount: this.managers.size,
925✔
46
            initOrder: this.initOrder,
925✔
47
        });
925✔
48

49
        try {
925✔
50
            // Initialize managers in specified order
51
            for (const managerName of this.initOrder) {
925✔
52
                const manager = this.managers.get(managerName);
10,111✔
53
                if (!manager) {
10,111!
54
                    throw new Error(`Manager '${managerName}' not found in manager map`);
×
55
                }
×
56

57
                try {
10,111✔
58
                    const managerStartTime = performance.now();
10,111✔
59
                    this.logger.debug(`Initializing manager: ${managerName}`);
10,111✔
60
                    await manager.init();
10,111✔
61
                    const managerDuration = performance.now() - managerStartTime;
10,109✔
62

63
                    this.logger.debug(`Manager initialized: ${managerName}`, {
10,109✔
64
                        duration: managerDuration.toFixed(2),
10,109✔
65
                    });
10,109✔
66

67
                    this.eventManager.emitGraphEvent("manager-initialized", {
10,109✔
68
                        managerName,
10,109✔
69
                        elapsedTime: performance.now() - startTime,
10,109✔
70
                    });
10,109✔
71
                } catch (error) {
10,111!
72
                    // Log the failure
73
                    this.logger.error(
1✔
74
                        `Manager initialization failed: ${managerName}`,
1✔
75
                        error instanceof Error ? error : new Error(String(error)),
1!
76
                        {managerName},
1✔
77
                    );
1✔
78

79
                    // Clean up any managers that were already initialized
80
                    this.cleanup(managerName);
1✔
81

82
                    throw new Error(
1✔
83
                        `Failed to initialize manager '${managerName}': ${
1✔
84
                            error instanceof Error ? error.message : String(error)
1!
85
                        }`,
1✔
86
                    );
1✔
87
                }
1✔
88
            }
10,111✔
89

90
            this.initialized = true;
923✔
91
            this.initializing = false;
923✔
92

93
            const totalTime = performance.now() - startTime;
923✔
94
            this.logger.info("All managers initialized", {
923✔
95
                totalTime: totalTime.toFixed(2),
923✔
96
                managerCount: this.managers.size,
923✔
97
            });
923✔
98

99
            // Emit overall initialization complete event
100
            this.eventManager.emitGraphEvent("lifecycle-initialized", {
923✔
101
                totalTime,
923✔
102
                managerCount: this.managers.size,
923✔
103
            });
923✔
104
        } catch (error) {
923!
105
            this.initializing = false;
1✔
106

107
            const err = error instanceof Error ? error : new Error(String(error));
1!
108
            this.eventManager.emitGraphError(
1✔
109
                null,
1✔
110
                err,
1✔
111
                "init",
1✔
112
                {component: "LifecycleManager"},
1✔
113
            );
1✔
114

115
            throw error;
1✔
116
        }
1✔
117
    }
925✔
118

119
    /**
120
     * Start the graph system after initialization
121
     * This coordinates starting the render loop and other post-init setup
122
     * @param updateCallback - Callback function to execute on each render frame
123
     */
124
    startGraph(updateCallback: () => void): void {
15✔
125
        if (!this.initialized) {
918!
126
            throw new Error("Cannot start graph before initialization");
×
127
        }
×
128

129
        try {
918✔
130
            // Get the render manager and start the render loop
131
            const renderManager = this.managers.get("render");
918✔
132
            if (renderManager && hasStartRenderLoop(renderManager)) {
918✔
133
                renderManager.startRenderLoop(updateCallback);
918✔
134
            }
918✔
135

136
            // Emit graph started event
137
            this.eventManager.emitGraphEvent("graph-started", {
918✔
138
                timestamp: Date.now(),
918✔
139
            });
918✔
140
        } catch (error) {
918!
141
            const err = error instanceof Error ? error : new Error(String(error));
×
142
            this.eventManager.emitGraphError(
×
143
                null,
×
144
                err,
×
145
                "init",
×
146
                {component: "LifecycleManager", operation: "startGraph"},
×
147
            );
×
148
            throw new Error(`Failed to start graph: ${err.message}`);
×
149
        }
×
150
    }
918✔
151

152
    /**
153
     * Dispose all managers in reverse initialization order
154
     */
155
    dispose(): void {
15✔
156
        if (!this.initialized) {
846!
157
            return;
66✔
158
        }
66✔
159

160
        this.logger.info("Disposing managers", {
780✔
161
            managerCount: this.managers.size,
780✔
162
        });
780✔
163

164
        // Dispose managers in reverse order
165
        const reverseOrder = [... this.initOrder].reverse();
780✔
166

167
        for (const managerName of reverseOrder) {
787✔
168
            const manager = this.managers.get(managerName);
8,561✔
169
            if (manager) {
8,561✔
170
                try {
8,561✔
171
                    this.logger.debug(`Disposing manager: ${managerName}`);
8,561✔
172
                    manager.dispose();
8,561✔
173
                } catch (error) {
8,561!
174
                    // Log error but continue disposing other managers
175
                    this.logger.error(
×
176
                        `Error disposing manager: ${managerName}`,
×
177
                        error instanceof Error ? error : new Error(String(error)),
×
178
                        {managerName},
×
179
                    );
×
180

181
                    this.eventManager.emitGraphError(
×
182
                        null,
×
183
                        error instanceof Error ? error : new Error(String(error)),
×
184
                        "other",
×
185
                        {component: "LifecycleManager", operation: "dispose", managerName},
×
186
                    );
×
187
                }
×
188
            }
8,561✔
189
        }
8,561✔
190

191
        this.initialized = false;
780✔
192

193
        this.logger.info("All managers disposed", {
780✔
194
            managerCount: this.managers.size,
780✔
195
        });
780✔
196

197
        // Emit lifecycle disposed event
198
        this.eventManager.emitGraphEvent("lifecycle-disposed", {
780✔
199
            managerCount: this.managers.size,
780✔
200
        });
780✔
201
    }
846✔
202

203
    /**
204
     * Clean up managers that were initialized before the given manager failed
205
     * @param failedManager - Name of the manager that failed to initialize
206
     */
207
    private cleanup(failedManager: string): void {
15✔
208
        const failedIndex = this.initOrder.indexOf(failedManager);
1✔
209
        if (failedIndex === -1) {
1!
210
            return;
×
211
        }
×
212

213
        // Dispose all managers that were initialized before the failure
214
        for (let i = failedIndex - 1; i >= 0; i--) {
1!
215
            const managerName = this.initOrder[i];
×
216
            const manager = this.managers.get(managerName);
×
217

218
            if (manager) {
×
219
                try {
×
220
                    manager.dispose();
×
221
                } catch (error) {
×
222
                    this.logger.error(
×
223
                        `Error during cleanup of manager: ${managerName}`,
×
224
                        error instanceof Error ? error : new Error(String(error)),
×
225
                        {managerName},
×
226
                    );
×
227
                }
×
228
            }
×
229
        }
×
230
    }
1✔
231

232
    /**
233
     * Check if all managers are initialized
234
     * @returns True if initialization is complete, false otherwise
235
     */
236
    isInitialized(): boolean {
15✔
237
        return this.initialized;
×
238
    }
×
239

240
    /**
241
     * Add a new manager to the lifecycle
242
     * TODO: This should only be done before init() is called
243
     * @param name - Unique name for the manager
244
     * @param manager - Manager instance to add
245
     * @param position - Optional position in initialization order (defaults to end)
246
     */
247
    addManager(name: string, manager: Manager, position?: number): void {
15✔
248
        if (this.initialized || this.initializing) {
×
249
            throw new Error("Cannot add managers after initialization has started");
×
250
        }
×
251

252
        this.managers.set(name, manager);
×
253

254
        if (position !== undefined) {
×
255
            this.initOrder.splice(position, 0, name);
×
256
        } else {
×
257
            this.initOrder.push(name);
×
258
        }
×
259
    }
×
260

261
    /**
262
     * Get a manager by name
263
     * @param name - Name of the manager to retrieve
264
     * @returns The manager instance or undefined if not found
265
     */
266
    getManager(name: string): Manager | undefined {
15✔
267
        return this.managers.get(name);
×
268
    }
×
269
}
15✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc