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

graphty-org / graphty-monorepo / 20661584252

02 Jan 2026 03:50PM UTC coverage: 77.924% (+7.3%) from 70.62%
20661584252

push

github

apowers313
ci: fix flakey performance test

13438 of 17822 branches covered (75.4%)

Branch coverage included in aggregate %.

41247 of 52355 relevant lines covered (78.78%)

145534.85 hits per line

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

64.6
/graphty-element/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(null, err, "init", { component: "LifecycleManager" });
1✔
109

110
            throw error;
1✔
111
        }
1✔
112
    }
925✔
113

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

124
        try {
918✔
125
            // Get the render manager and start the render loop
126
            const renderManager = this.managers.get("render");
918✔
127
            if (renderManager && hasStartRenderLoop(renderManager)) {
918✔
128
                renderManager.startRenderLoop(updateCallback);
918✔
129
            }
918✔
130

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

145
    /**
146
     * Dispose all managers in reverse initialization order
147
     */
148
    dispose(): void {
15✔
149
        if (!this.initialized) {
846!
150
            return;
66✔
151
        }
66✔
152

153
        this.logger.info("Disposing managers", {
780✔
154
            managerCount: this.managers.size,
780✔
155
        });
780✔
156

157
        // Dispose managers in reverse order
158
        const reverseOrder = [...this.initOrder].reverse();
780✔
159

160
        for (const managerName of reverseOrder) {
787✔
161
            const manager = this.managers.get(managerName);
8,561✔
162
            if (manager) {
8,561✔
163
                try {
8,561✔
164
                    this.logger.debug(`Disposing manager: ${managerName}`);
8,561✔
165
                    manager.dispose();
8,561✔
166
                } catch (error) {
8,561!
167
                    // Log error but continue disposing other managers
168
                    this.logger.error(
×
169
                        `Error disposing manager: ${managerName}`,
×
170
                        error instanceof Error ? error : new Error(String(error)),
×
171
                        { managerName },
×
172
                    );
×
173

174
                    this.eventManager.emitGraphError(
×
175
                        null,
×
176
                        error instanceof Error ? error : new Error(String(error)),
×
177
                        "other",
×
178
                        { component: "LifecycleManager", operation: "dispose", managerName },
×
179
                    );
×
180
                }
×
181
            }
8,561✔
182
        }
8,561✔
183

184
        this.initialized = false;
780✔
185

186
        this.logger.info("All managers disposed", {
780✔
187
            managerCount: this.managers.size,
780✔
188
        });
780✔
189

190
        // Emit lifecycle disposed event
191
        this.eventManager.emitGraphEvent("lifecycle-disposed", {
780✔
192
            managerCount: this.managers.size,
780✔
193
        });
780✔
194
    }
846✔
195

196
    /**
197
     * Clean up managers that were initialized before the given manager failed
198
     * @param failedManager - Name of the manager that failed to initialize
199
     */
200
    private cleanup(failedManager: string): void {
15✔
201
        const failedIndex = this.initOrder.indexOf(failedManager);
1✔
202
        if (failedIndex === -1) {
1!
203
            return;
×
204
        }
×
205

206
        // Dispose all managers that were initialized before the failure
207
        for (let i = failedIndex - 1; i >= 0; i--) {
1!
208
            const managerName = this.initOrder[i];
×
209
            const manager = this.managers.get(managerName);
×
210

211
            if (manager) {
×
212
                try {
×
213
                    manager.dispose();
×
214
                } catch (error) {
×
215
                    this.logger.error(
×
216
                        `Error during cleanup of manager: ${managerName}`,
×
217
                        error instanceof Error ? error : new Error(String(error)),
×
218
                        { managerName },
×
219
                    );
×
220
                }
×
221
            }
×
222
        }
×
223
    }
1✔
224

225
    /**
226
     * Check if all managers are initialized
227
     * @returns True if initialization is complete, false otherwise
228
     */
229
    isInitialized(): boolean {
15✔
230
        return this.initialized;
×
231
    }
×
232

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

245
        this.managers.set(name, manager);
×
246

247
        if (position !== undefined) {
×
248
            this.initOrder.splice(position, 0, name);
×
249
        } else {
×
250
            this.initOrder.push(name);
×
251
        }
×
252
    }
×
253

254
    /**
255
     * Get a manager by name
256
     * @param name - Name of the manager to retrieve
257
     * @returns The manager instance or undefined if not found
258
     */
259
    getManager(name: string): Manager | undefined {
15✔
260
        return this.managers.get(name);
×
261
    }
×
262
}
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