musubi-sdd 5.6.3 → 5.7.2

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.
@@ -0,0 +1,343 @@
1
+ /**
2
+ * MUSUBI Lazy Loader
3
+ *
4
+ * On-demand module loading for performance optimization.
5
+ * Phase 6 P0: Performance Optimization
6
+ *
7
+ * Features:
8
+ * - Dynamic imports for large modules
9
+ * - Module preloading hints
10
+ * - Load time tracking
11
+ * - Memory-efficient loading
12
+ */
13
+
14
+ /**
15
+ * Module categories for lazy loading
16
+ */
17
+ const ModuleCategory = {
18
+ CORE: 'core', // Always loaded
19
+ ANALYSIS: 'analysis', // Loaded on analysis operations
20
+ ORCHESTRATION: 'orchestration', // Loaded on orchestration
21
+ MONITORING: 'monitoring', // Loaded on monitoring
22
+ INTEGRATION: 'integration', // Loaded on external integration
23
+ GUI: 'gui', // Loaded on GUI start
24
+ };
25
+
26
+ /**
27
+ * Module registry for lazy loading
28
+ */
29
+ const MODULE_REGISTRY = {
30
+ // Analysis modules (heavy)
31
+ 'large-project-analyzer': {
32
+ path: '../analyzers/large-project-analyzer',
33
+ category: ModuleCategory.ANALYSIS,
34
+ exports: ['LargeProjectAnalyzer', 'THRESHOLDS', 'CHUNK_SIZE'],
35
+ estimatedSize: 'large',
36
+ },
37
+ 'complexity-analyzer': {
38
+ path: '../analyzers/complexity-analyzer',
39
+ category: ModuleCategory.ANALYSIS,
40
+ exports: ['ComplexityAnalyzer', 'THRESHOLDS'],
41
+ estimatedSize: 'medium',
42
+ },
43
+ 'rust-migration-generator': {
44
+ path: '../generators/rust-migration-generator',
45
+ category: ModuleCategory.ANALYSIS,
46
+ exports: ['RustMigrationGenerator', 'UNSAFE_PATTERNS', 'SECURITY_COMPONENTS'],
47
+ estimatedSize: 'large',
48
+ },
49
+
50
+ // Orchestration modules
51
+ 'replanning-engine': {
52
+ path: '../orchestration/replanning/replanning-engine',
53
+ category: ModuleCategory.ORCHESTRATION,
54
+ exports: ['ReplanningEngine'],
55
+ estimatedSize: 'large',
56
+ },
57
+ 'workflow-executor': {
58
+ path: '../orchestration/workflow-executor',
59
+ category: ModuleCategory.ORCHESTRATION,
60
+ exports: ['WorkflowExecutor'],
61
+ estimatedSize: 'medium',
62
+ },
63
+
64
+ // Monitoring modules
65
+ 'incident-manager': {
66
+ path: '../monitoring/incident-manager',
67
+ category: ModuleCategory.MONITORING,
68
+ exports: ['IncidentManager'],
69
+ estimatedSize: 'large',
70
+ },
71
+ 'quality-dashboard': {
72
+ path: '../monitoring/quality-dashboard',
73
+ category: ModuleCategory.MONITORING,
74
+ exports: ['QualityDashboard'],
75
+ estimatedSize: 'medium',
76
+ },
77
+
78
+ // Integration modules
79
+ 'codegraph-mcp': {
80
+ path: '../integrations/codegraph-mcp',
81
+ category: ModuleCategory.INTEGRATION,
82
+ exports: ['CodeGraphMCP'],
83
+ estimatedSize: 'large',
84
+ optional: true,
85
+ },
86
+
87
+ // GUI modules
88
+ 'gui-server': {
89
+ path: '../gui/server',
90
+ category: ModuleCategory.GUI,
91
+ exports: ['default'],
92
+ estimatedSize: 'large',
93
+ },
94
+ };
95
+
96
+ /**
97
+ * LazyLoader class for on-demand module loading
98
+ */
99
+ class LazyLoader {
100
+ constructor(options = {}) {
101
+ this.cache = new Map();
102
+ this.loadTimes = new Map();
103
+ this.preloadHints = new Set();
104
+ this.options = {
105
+ enableCache: options.enableCache !== false,
106
+ trackLoadTimes: options.trackLoadTimes !== false,
107
+ preloadOnIdle: options.preloadOnIdle || false,
108
+ ...options,
109
+ };
110
+ }
111
+
112
+ /**
113
+ * Load a module by name
114
+ * @param {string} moduleName - Name of the module to load
115
+ * @returns {Promise<Object>} - Loaded module exports
116
+ */
117
+ async load(moduleName) {
118
+ // Check cache first
119
+ if (this.options.enableCache && this.cache.has(moduleName)) {
120
+ return this.cache.get(moduleName);
121
+ }
122
+
123
+ const registry = MODULE_REGISTRY[moduleName];
124
+ if (!registry) {
125
+ throw new Error(`Unknown module: ${moduleName}`);
126
+ }
127
+
128
+ const startTime = Date.now();
129
+
130
+ try {
131
+ // Use dynamic import for ES modules or require for CommonJS
132
+ const loaded = require(registry.path);
133
+
134
+ const loadTime = Date.now() - startTime;
135
+
136
+ if (this.options.trackLoadTimes) {
137
+ this.loadTimes.set(moduleName, loadTime);
138
+ }
139
+
140
+ if (this.options.enableCache) {
141
+ this.cache.set(moduleName, loaded);
142
+ }
143
+
144
+ return loaded;
145
+ } catch (error) {
146
+ if (registry.optional) {
147
+ return null;
148
+ }
149
+ throw new Error(`Failed to load module ${moduleName}: ${error.message}`);
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Synchronous load for compatibility
155
+ * @param {string} moduleName - Name of the module to load
156
+ * @returns {Object} - Loaded module exports
157
+ */
158
+ loadSync(moduleName) {
159
+ // Check cache first
160
+ if (this.options.enableCache && this.cache.has(moduleName)) {
161
+ return this.cache.get(moduleName);
162
+ }
163
+
164
+ const registry = MODULE_REGISTRY[moduleName];
165
+ if (!registry) {
166
+ throw new Error(`Unknown module: ${moduleName}`);
167
+ }
168
+
169
+ const startTime = Date.now();
170
+
171
+ try {
172
+ const loaded = require(registry.path);
173
+
174
+ const loadTime = Date.now() - startTime;
175
+
176
+ if (this.options.trackLoadTimes) {
177
+ this.loadTimes.set(moduleName, loadTime);
178
+ }
179
+
180
+ if (this.options.enableCache) {
181
+ this.cache.set(moduleName, loaded);
182
+ }
183
+
184
+ return loaded;
185
+ } catch (error) {
186
+ if (registry.optional) {
187
+ return null;
188
+ }
189
+ throw new Error(`Failed to load module ${moduleName}: ${error.message}`);
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Load all modules in a category
195
+ * @param {string} category - Module category
196
+ * @returns {Promise<Map<string, Object>>} - Map of loaded modules
197
+ */
198
+ async loadCategory(category) {
199
+ const modules = new Map();
200
+
201
+ for (const [name, registry] of Object.entries(MODULE_REGISTRY)) {
202
+ if (registry.category === category) {
203
+ try {
204
+ const loaded = await this.load(name);
205
+ if (loaded) {
206
+ modules.set(name, loaded);
207
+ }
208
+ } catch (error) {
209
+ console.warn(`Failed to load ${name}: ${error.message}`);
210
+ }
211
+ }
212
+ }
213
+
214
+ return modules;
215
+ }
216
+
217
+ /**
218
+ * Add preload hint for a module
219
+ * @param {string} moduleName - Name of the module to preload
220
+ */
221
+ hint(moduleName) {
222
+ if (MODULE_REGISTRY[moduleName]) {
223
+ this.preloadHints.add(moduleName);
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Preload hinted modules (call during idle time)
229
+ * @returns {Promise<void>}
230
+ */
231
+ async preload() {
232
+ const promises = [];
233
+
234
+ for (const moduleName of this.preloadHints) {
235
+ if (!this.cache.has(moduleName)) {
236
+ promises.push(
237
+ this.load(moduleName).catch(err => {
238
+ console.warn(`Preload failed for ${moduleName}: ${err.message}`);
239
+ })
240
+ );
241
+ }
242
+ }
243
+
244
+ await Promise.all(promises);
245
+ this.preloadHints.clear();
246
+ }
247
+
248
+ /**
249
+ * Clear the module cache
250
+ * @param {string} [moduleName] - Optional specific module to clear
251
+ */
252
+ clearCache(moduleName) {
253
+ if (moduleName) {
254
+ this.cache.delete(moduleName);
255
+ } else {
256
+ this.cache.clear();
257
+ }
258
+ }
259
+
260
+ /**
261
+ * Get load time statistics
262
+ * @returns {Object} - Load time statistics
263
+ */
264
+ getLoadStats() {
265
+ const stats = {
266
+ totalModules: this.loadTimes.size,
267
+ cachedModules: this.cache.size,
268
+ loadTimes: Object.fromEntries(this.loadTimes),
269
+ averageLoadTime: 0,
270
+ totalLoadTime: 0,
271
+ };
272
+
273
+ if (this.loadTimes.size > 0) {
274
+ const times = Array.from(this.loadTimes.values());
275
+ stats.totalLoadTime = times.reduce((a, b) => a + b, 0);
276
+ stats.averageLoadTime = Math.round(stats.totalLoadTime / times.length);
277
+ }
278
+
279
+ return stats;
280
+ }
281
+
282
+ /**
283
+ * Check if a module is loaded
284
+ * @param {string} moduleName - Name of the module
285
+ * @returns {boolean} - True if loaded
286
+ */
287
+ isLoaded(moduleName) {
288
+ return this.cache.has(moduleName);
289
+ }
290
+
291
+ /**
292
+ * Get list of available modules
293
+ * @param {string} [category] - Optional category filter
294
+ * @returns {string[]} - List of module names
295
+ */
296
+ getAvailableModules(category) {
297
+ if (category) {
298
+ return Object.entries(MODULE_REGISTRY)
299
+ .filter(([, reg]) => reg.category === category)
300
+ .map(([name]) => name);
301
+ }
302
+ return Object.keys(MODULE_REGISTRY);
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Create a lazy proxy for a module
308
+ * @param {string} moduleName - Name of the module
309
+ * @param {LazyLoader} loader - LazyLoader instance
310
+ * @returns {Proxy} - Lazy proxy object
311
+ */
312
+ function createLazyProxy(moduleName, loader) {
313
+ let module = null;
314
+
315
+ return new Proxy(
316
+ {},
317
+ {
318
+ get(target, prop) {
319
+ if (!module) {
320
+ module = loader.loadSync(moduleName);
321
+ }
322
+ return module ? module[prop] : undefined;
323
+ },
324
+ has(target, prop) {
325
+ if (!module) {
326
+ module = loader.loadSync(moduleName);
327
+ }
328
+ return module ? prop in module : false;
329
+ },
330
+ }
331
+ );
332
+ }
333
+
334
+ // Singleton instance
335
+ const defaultLoader = new LazyLoader();
336
+
337
+ module.exports = {
338
+ LazyLoader,
339
+ ModuleCategory,
340
+ MODULE_REGISTRY,
341
+ createLazyProxy,
342
+ defaultLoader,
343
+ };