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
|
+
};
|