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

graphty-org / graphty-element / 20390753610

20 Dec 2025 06:53AM UTC coverage: 82.423% (-1.2%) from 83.666%
20390753610

push

github

apowers313
Merge branch 'master' of https://github.com/graphty-org/graphty-element

5162 of 6088 branches covered (84.79%)

Branch coverage included in aggregate %.

24775 of 30233 relevant lines covered (81.95%)

6480.4 hits per line

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

67.32
/src/managers/LifecycleManager.ts
1
import {GraphtyLogger, type Logger} from "../logging/GraphtyLogger.js";
3✔
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 {
738✔
9
    return "startRenderLoop" in manager;
738✔
10
}
738✔
11

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

21
    constructor(
3✔
22
        private managers: Map<string, Manager>,
816✔
23
        private eventManager: EventManager,
816✔
24
        private initOrder: string[],
816✔
25
    ) {}
816✔
26

27
    async init(): Promise<void> {
3✔
28
        if (this.initialized || this.initializing) {
744!
29
            return;
×
30
        }
×
31

32
        this.initializing = true;
744✔
33
        const startTime = performance.now();
744✔
34

35
        this.logger.info("Initializing managers", {
744✔
36
            managerCount: this.managers.size,
744✔
37
            initOrder: this.initOrder,
744✔
38
        });
744✔
39

40
        try {
744✔
41
            // Initialize managers in specified order
42
            for (const managerName of this.initOrder) {
744✔
43
                const manager = this.managers.get(managerName);
8,126✔
44
                if (!manager) {
8,126!
45
                    throw new Error(`Manager '${managerName}' not found in manager map`);
×
46
                }
×
47

48
                try {
8,126✔
49
                    const managerStartTime = performance.now();
8,126✔
50
                    this.logger.debug(`Initializing manager: ${managerName}`);
8,126✔
51
                    await manager.init();
8,126✔
52
                    const managerDuration = performance.now() - managerStartTime;
8,125✔
53

54
                    this.logger.debug(`Manager initialized: ${managerName}`, {
8,125✔
55
                        duration: managerDuration.toFixed(2),
8,125✔
56
                    });
8,125✔
57

58
                    this.eventManager.emitGraphEvent("manager-initialized", {
8,125✔
59
                        managerName,
8,125✔
60
                        elapsedTime: performance.now() - startTime,
8,125✔
61
                    });
8,125✔
62
                } catch (error) {
8,126✔
63
                    // Log the failure
64
                    this.logger.error(
1✔
65
                        `Manager initialization failed: ${managerName}`,
1✔
66
                        error instanceof Error ? error : new Error(String(error)),
1!
67
                        {managerName},
1✔
68
                    );
1✔
69

70
                    // Clean up any managers that were already initialized
71
                    this.cleanup(managerName);
1✔
72

73
                    throw new Error(
1✔
74
                        `Failed to initialize manager '${managerName}': ${
1✔
75
                            error instanceof Error ? error.message : String(error)
1!
76
                        }`,
1✔
77
                    );
1✔
78
                }
1✔
79
            }
8,126✔
80

81
            this.initialized = true;
743✔
82
            this.initializing = false;
743✔
83

84
            const totalTime = performance.now() - startTime;
743✔
85
            this.logger.info("All managers initialized", {
743✔
86
                totalTime: totalTime.toFixed(2),
743✔
87
                managerCount: this.managers.size,
743✔
88
            });
743✔
89

90
            // Emit overall initialization complete event
91
            this.eventManager.emitGraphEvent("lifecycle-initialized", {
743✔
92
                totalTime,
743✔
93
                managerCount: this.managers.size,
743✔
94
            });
743✔
95
        } catch (error) {
743✔
96
            this.initializing = false;
1✔
97

98
            const err = error instanceof Error ? error : new Error(String(error));
1!
99
            this.eventManager.emitGraphError(
1✔
100
                null,
1✔
101
                err,
1✔
102
                "init",
1✔
103
                {component: "LifecycleManager"},
1✔
104
            );
1✔
105

106
            throw error;
1✔
107
        }
1✔
108
    }
744✔
109

110
    /**
111
     * Start the graph system after initialization
112
     * This coordinates starting the render loop and other post-init setup
113
     */
114
    startGraph(updateCallback: () => void): void {
3✔
115
        if (!this.initialized) {
738!
116
            throw new Error("Cannot start graph before initialization");
×
117
        }
×
118

119
        try {
738✔
120
            // Get the render manager and start the render loop
121
            const renderManager = this.managers.get("render");
738✔
122
            if (renderManager && hasStartRenderLoop(renderManager)) {
738✔
123
                renderManager.startRenderLoop(updateCallback);
738✔
124
            }
738✔
125

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

142
    dispose(): void {
3✔
143
        if (!this.initialized) {
665✔
144
            return;
73✔
145
        }
73✔
146

147
        this.logger.info("Disposing managers", {
592✔
148
            managerCount: this.managers.size,
592✔
149
        });
592✔
150

151
        // Dispose managers in reverse order
152
        const reverseOrder = [... this.initOrder].reverse();
592✔
153

154
        for (const managerName of reverseOrder) {
604✔
155
            const manager = this.managers.get(managerName);
6,493✔
156
            if (manager) {
6,493✔
157
                try {
6,493✔
158
                    this.logger.debug(`Disposing manager: ${managerName}`);
6,493✔
159
                    manager.dispose();
6,493✔
160
                } catch (error) {
6,493!
161
                    // Log error but continue disposing other managers
162
                    this.logger.error(
×
163
                        `Error disposing manager: ${managerName}`,
×
164
                        error instanceof Error ? error : new Error(String(error)),
×
165
                        {managerName},
×
166
                    );
×
167

168
                    this.eventManager.emitGraphError(
×
169
                        null,
×
170
                        error instanceof Error ? error : new Error(String(error)),
×
171
                        "other",
×
172
                        {component: "LifecycleManager", operation: "dispose", managerName},
×
173
                    );
×
174
                }
×
175
            }
6,493✔
176
        }
6,493✔
177

178
        this.initialized = false;
592✔
179

180
        this.logger.info("All managers disposed", {
592✔
181
            managerCount: this.managers.size,
592✔
182
        });
592✔
183

184
        // Emit lifecycle disposed event
185
        this.eventManager.emitGraphEvent("lifecycle-disposed", {
592✔
186
            managerCount: this.managers.size,
592✔
187
        });
592✔
188
    }
665✔
189

190
    /**
191
     * Clean up managers that were initialized before the given manager failed
192
     */
193
    private cleanup(failedManager: string): void {
3✔
194
        const failedIndex = this.initOrder.indexOf(failedManager);
1✔
195
        if (failedIndex === -1) {
1!
196
            return;
×
197
        }
×
198

199
        // Dispose all managers that were initialized before the failure
200
        for (let i = failedIndex - 1; i >= 0; i--) {
1!
201
            const managerName = this.initOrder[i];
×
202
            const manager = this.managers.get(managerName);
×
203

204
            if (manager) {
×
205
                try {
×
206
                    manager.dispose();
×
207
                } catch (error) {
×
208
                    this.logger.error(
×
209
                        `Error during cleanup of manager: ${managerName}`,
×
210
                        error instanceof Error ? error : new Error(String(error)),
×
211
                        {managerName},
×
212
                    );
×
213
                }
×
214
            }
×
215
        }
×
216
    }
1✔
217

218
    /**
219
     * Check if all managers are initialized
220
     */
221
    isInitialized(): boolean {
3✔
222
        return this.initialized;
×
223
    }
×
224

225
    /**
226
     * Add a new manager to the lifecycle
227
     * TODO: This should only be done before init() is called
228
     */
229
    addManager(name: string, manager: Manager, position?: number): void {
3✔
230
        if (this.initialized || this.initializing) {
×
231
            throw new Error("Cannot add managers after initialization has started");
×
232
        }
×
233

234
        this.managers.set(name, manager);
×
235

236
        if (position !== undefined) {
×
237
            this.initOrder.splice(position, 0, name);
×
238
        } else {
×
239
            this.initOrder.push(name);
×
240
        }
×
241
    }
×
242

243
    /**
244
     * Get a manager by name
245
     */
246
    getManager(name: string): Manager | undefined {
3✔
247
        return this.managers.get(name);
×
248
    }
×
249
}
3✔
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