tywrap 0.8.0 → 0.9.0

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.
Files changed (157) hide show
  1. package/README.md +22 -7
  2. package/SECURITY.md +39 -0
  3. package/dist/config/index.d.ts.map +1 -1
  4. package/dist/config/index.js +8 -0
  5. package/dist/config/index.js.map +1 -1
  6. package/dist/core/annotation-parser.d.ts +2 -1
  7. package/dist/core/annotation-parser.d.ts.map +1 -1
  8. package/dist/core/annotation-parser.js +6 -3
  9. package/dist/core/annotation-parser.js.map +1 -1
  10. package/dist/core/generator.d.ts +23 -4
  11. package/dist/core/generator.d.ts.map +1 -1
  12. package/dist/core/generator.js +243 -170
  13. package/dist/core/generator.js.map +1 -1
  14. package/dist/core/mapper.d.ts +3 -2
  15. package/dist/core/mapper.d.ts.map +1 -1
  16. package/dist/core/mapper.js +5 -5
  17. package/dist/core/mapper.js.map +1 -1
  18. package/dist/index.d.ts +7 -5
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +6 -4
  21. package/dist/index.js.map +1 -1
  22. package/dist/runtime/base-bridge.d.ts +3 -7
  23. package/dist/runtime/base-bridge.d.ts.map +1 -1
  24. package/dist/runtime/base-bridge.js +4 -17
  25. package/dist/runtime/base-bridge.js.map +1 -1
  26. package/dist/runtime/bounded-context.d.ts +3 -22
  27. package/dist/runtime/bounded-context.d.ts.map +1 -1
  28. package/dist/runtime/bounded-context.js +13 -53
  29. package/dist/runtime/bounded-context.js.map +1 -1
  30. package/dist/runtime/bridge-codec.d.ts +1 -1
  31. package/dist/runtime/bridge-codec.d.ts.map +1 -1
  32. package/dist/runtime/bridge-codec.js +78 -55
  33. package/dist/runtime/bridge-codec.js.map +1 -1
  34. package/dist/runtime/errors.d.ts +16 -0
  35. package/dist/runtime/errors.d.ts.map +1 -1
  36. package/dist/runtime/errors.js +17 -0
  37. package/dist/runtime/errors.js.map +1 -1
  38. package/dist/runtime/http-transport.d.ts +1 -1
  39. package/dist/runtime/http-transport.d.ts.map +1 -1
  40. package/dist/runtime/http-transport.js +1 -1
  41. package/dist/runtime/http-transport.js.map +1 -1
  42. package/dist/runtime/index.d.ts +1 -0
  43. package/dist/runtime/index.d.ts.map +1 -1
  44. package/dist/runtime/index.js +1 -0
  45. package/dist/runtime/index.js.map +1 -1
  46. package/dist/runtime/node.d.ts +7 -36
  47. package/dist/runtime/node.d.ts.map +1 -1
  48. package/dist/runtime/node.js +2 -80
  49. package/dist/runtime/node.js.map +1 -1
  50. package/dist/runtime/pooled-transport.d.ts +120 -59
  51. package/dist/runtime/pooled-transport.d.ts.map +1 -1
  52. package/dist/runtime/pooled-transport.js +345 -78
  53. package/dist/runtime/pooled-transport.js.map +1 -1
  54. package/dist/runtime/pyodide-bootstrap-core.generated.d.ts.map +1 -1
  55. package/dist/runtime/pyodide-bootstrap-core.generated.js +1 -1
  56. package/dist/runtime/pyodide-bootstrap-core.generated.js.map +1 -1
  57. package/dist/runtime/pyodide-transport.d.ts +1 -1
  58. package/dist/runtime/pyodide-transport.d.ts.map +1 -1
  59. package/dist/runtime/pyodide-transport.js +2 -3
  60. package/dist/runtime/pyodide-transport.js.map +1 -1
  61. package/dist/runtime/rpc-client.d.ts +7 -36
  62. package/dist/runtime/rpc-client.d.ts.map +1 -1
  63. package/dist/runtime/rpc-client.js +20 -102
  64. package/dist/runtime/rpc-client.js.map +1 -1
  65. package/dist/runtime/subprocess-transport.d.ts +30 -69
  66. package/dist/runtime/subprocess-transport.d.ts.map +1 -1
  67. package/dist/runtime/subprocess-transport.js +179 -236
  68. package/dist/runtime/subprocess-transport.js.map +1 -1
  69. package/dist/runtime/timed-out-request-tracker.d.ts +2 -1
  70. package/dist/runtime/timed-out-request-tracker.d.ts.map +1 -1
  71. package/dist/runtime/transport.d.ts +9 -19
  72. package/dist/runtime/transport.d.ts.map +1 -1
  73. package/dist/runtime/transport.js +1 -1
  74. package/dist/runtime/transport.js.map +1 -1
  75. package/dist/runtime/validators.d.ts +49 -0
  76. package/dist/runtime/validators.d.ts.map +1 -1
  77. package/dist/runtime/validators.js +152 -0
  78. package/dist/runtime/validators.js.map +1 -1
  79. package/dist/types/index.d.ts +14 -39
  80. package/dist/types/index.d.ts.map +1 -1
  81. package/dist/tywrap.d.ts +3 -2
  82. package/dist/tywrap.d.ts.map +1 -1
  83. package/dist/tywrap.js +140 -13
  84. package/dist/tywrap.js.map +1 -1
  85. package/dist/utils/cache.d.ts +3 -16
  86. package/dist/utils/cache.d.ts.map +1 -1
  87. package/dist/utils/codec.d.ts +1 -0
  88. package/dist/utils/codec.d.ts.map +1 -1
  89. package/dist/utils/codec.js +13 -13
  90. package/dist/utils/codec.js.map +1 -1
  91. package/dist/utils/ir-cache.d.ts +2 -1
  92. package/dist/utils/ir-cache.d.ts.map +1 -1
  93. package/dist/utils/runtime.d.ts +0 -29
  94. package/dist/utils/runtime.d.ts.map +1 -1
  95. package/dist/utils/runtime.js +16 -107
  96. package/dist/utils/runtime.js.map +1 -1
  97. package/dist/version.js +1 -1
  98. package/package.json +7 -6
  99. package/runtime/frame_codec.py +7 -1
  100. package/runtime/python_bridge.py +85 -125
  101. package/runtime/tywrap_bridge_core.py +133 -150
  102. package/src/config/index.ts +11 -0
  103. package/src/core/annotation-parser.ts +7 -4
  104. package/src/core/generator.ts +315 -205
  105. package/src/core/mapper.ts +8 -8
  106. package/src/index.ts +7 -4
  107. package/src/runtime/base-bridge.ts +5 -30
  108. package/src/runtime/bounded-context.ts +12 -67
  109. package/src/runtime/bridge-codec.ts +94 -65
  110. package/src/runtime/errors.ts +21 -0
  111. package/src/runtime/http-transport.ts +6 -1
  112. package/src/runtime/index.ts +6 -0
  113. package/src/runtime/node.ts +9 -120
  114. package/src/runtime/pooled-transport.ts +424 -90
  115. package/src/runtime/pyodide-bootstrap-core.generated.ts +1 -1
  116. package/src/runtime/pyodide-transport.ts +7 -3
  117. package/src/runtime/rpc-client.ts +37 -139
  118. package/src/runtime/subprocess-transport.ts +210 -285
  119. package/src/runtime/timed-out-request-tracker.ts +1 -1
  120. package/src/runtime/transport.ts +15 -23
  121. package/src/runtime/validators.ts +204 -0
  122. package/src/types/index.ts +21 -60
  123. package/src/tywrap.ts +157 -22
  124. package/src/utils/cache.ts +3 -3
  125. package/src/utils/codec.ts +21 -13
  126. package/src/utils/ir-cache.ts +1 -1
  127. package/src/utils/runtime.ts +17 -128
  128. package/src/version.ts +1 -1
  129. package/dist/core/discovery.d.ts +0 -103
  130. package/dist/core/discovery.d.ts.map +0 -1
  131. package/dist/core/discovery.js +0 -380
  132. package/dist/core/discovery.js.map +0 -1
  133. package/dist/core/validation.d.ts +0 -102
  134. package/dist/core/validation.d.ts.map +0 -1
  135. package/dist/core/validation.js +0 -490
  136. package/dist/core/validation.js.map +0 -1
  137. package/dist/runtime/base.d.ts +0 -22
  138. package/dist/runtime/base.d.ts.map +0 -1
  139. package/dist/runtime/base.js +0 -23
  140. package/dist/runtime/base.js.map +0 -1
  141. package/dist/runtime/transport-pool.d.ts +0 -196
  142. package/dist/runtime/transport-pool.d.ts.map +0 -1
  143. package/dist/runtime/transport-pool.js +0 -418
  144. package/dist/runtime/transport-pool.js.map +0 -1
  145. package/runtime/__pycache__/_tywrap_conformance_chunking_fixtures.cpython-311.pyc +0 -0
  146. package/runtime/__pycache__/_tywrap_member_fixtures.cpython-311.pyc +0 -0
  147. package/runtime/__pycache__/_tywrap_w4_chunking_fixture.cpython-311.pyc +0 -0
  148. package/runtime/__pycache__/_tywrap_w5_request_chunking_fixture.cpython-311.pyc +0 -0
  149. package/runtime/__pycache__/_tywrap_w6_pool_chunking_fixture.cpython-311.pyc +0 -0
  150. package/runtime/__pycache__/frame_codec.cpython-311.pyc +0 -0
  151. package/runtime/__pycache__/safe_codec.cpython-311.pyc +0 -0
  152. package/runtime/__pycache__/tywrap_bridge_core.cpython-311.pyc +0 -0
  153. package/runtime/safe_codec.py +0 -352
  154. package/src/core/discovery.ts +0 -477
  155. package/src/core/validation.ts +0 -729
  156. package/src/runtime/base.ts +0 -24
  157. package/src/runtime/transport-pool.ts +0 -538
@@ -1,477 +0,0 @@
1
- /**
2
- * Python Module Discovery System
3
- *
4
- * Handles module resolution, dependency detection, and file system operations
5
- * across different runtime environments
6
- */
7
-
8
- // import type { PythonImport } from '../types/index.js';
9
- import { fsUtils, processUtils, pathUtils } from '../utils/runtime.js';
10
- import { resolvePythonExecutable } from '../utils/python.js';
11
- import { getComponentLogger } from '../utils/logger.js';
12
-
13
- const log = getComponentLogger('Discovery');
14
-
15
- /**
16
- * Strict allow-list for Python module names.
17
- *
18
- * Module names are interpolated into a `python -c` source string, so anything
19
- * outside a conservative identifier-plus-dots shape (e.g. quotes, semicolons,
20
- * newlines, parentheses, whitespace) could break out of the intended
21
- * expression and inject arbitrary code into the interpreter. Reject those
22
- * before they ever reach the subprocess argv.
23
- */
24
- const SAFE_MODULE_NAME = /^[A-Za-z_][A-Za-z0-9_.]*$/;
25
-
26
- /**
27
- * Validate a Python module name against {@link SAFE_MODULE_NAME}.
28
- *
29
- * @throws {Error} if the name is empty or contains characters that could
30
- * escape the `python -c` expression it is interpolated into.
31
- */
32
- function assertSafeModuleName(moduleName: string): void {
33
- if (typeof moduleName !== 'string' || !SAFE_MODULE_NAME.test(moduleName)) {
34
- throw new Error(
35
- `Unsafe Python module name rejected: ${JSON.stringify(moduleName)}. ` +
36
- 'Module names must match /^[A-Za-z_][A-Za-z0-9_.]*$/.'
37
- );
38
- }
39
- }
40
-
41
- export interface ModuleInfo {
42
- name: string;
43
- path: string;
44
- version?: string;
45
- isPackage: boolean;
46
- dependencies: string[];
47
- }
48
-
49
- export interface DiscoveryOptions {
50
- pythonPath?: string;
51
- virtualEnv?: string;
52
- searchPaths?: string[];
53
- excludePatterns?: string[];
54
- includeStdLib?: boolean;
55
- timeoutMs?: number;
56
- }
57
-
58
- export class ModuleDiscovery {
59
- private options: DiscoveryOptions;
60
- private moduleCache = new Map<string, ModuleInfo>();
61
- private dependencyGraph = new Map<string, Set<string>>();
62
- private pythonExecPromise?: Promise<string>;
63
-
64
- constructor(options: DiscoveryOptions = {}) {
65
- this.options = {
66
- includeStdLib: false,
67
- excludePatterns: ['__pycache__', '*.pyc', '.git', '.svn'],
68
- ...options,
69
- // Avoid "no timeout by default" for subprocess discovery paths.
70
- timeoutMs:
71
- typeof options.timeoutMs === 'number' &&
72
- Number.isFinite(options.timeoutMs) &&
73
- options.timeoutMs > 0
74
- ? options.timeoutMs
75
- : 30000,
76
- };
77
- }
78
-
79
- private async getPythonExecutable(): Promise<string> {
80
- this.pythonExecPromise ??= resolvePythonExecutable({
81
- pythonPath: this.options.pythonPath,
82
- virtualEnv: this.options.virtualEnv,
83
- });
84
- return this.pythonExecPromise;
85
- }
86
-
87
- /**
88
- * Find Python modules from various sources
89
- */
90
- async findPythonModules(paths: string[]): Promise<ModuleInfo[]> {
91
- const modules: ModuleInfo[] = [];
92
- const seen = new Set<string>();
93
-
94
- for (const path of paths) {
95
- try {
96
- const foundModules = await this.scanPath(path);
97
- for (const module of foundModules) {
98
- if (!seen.has(module.name)) {
99
- modules.push(module);
100
- seen.add(module.name);
101
- this.moduleCache.set(module.name, module);
102
- }
103
- }
104
- } catch (error) {
105
- log.warn('Failed to scan path', { path, error: String(error) });
106
- }
107
- }
108
-
109
- return modules;
110
- }
111
-
112
- /**
113
- * Resolve Python module path from module name
114
- */
115
- async resolvePythonPath(moduleName: string): Promise<string | null> {
116
- // Reject names that could break out of the `python -c` expression below.
117
- assertSafeModuleName(moduleName);
118
-
119
- // Check cache first
120
- const cached = this.moduleCache.get(moduleName);
121
- if (cached) {
122
- return cached.path;
123
- }
124
-
125
- // Try to resolve using Python's module finder, then fall back to a
126
- // filesystem search across the known Python paths.
127
- return (
128
- (await this.resolveViaInterpreter(moduleName)) ??
129
- (await this.resolveViaSearchPaths(moduleName))
130
- );
131
- }
132
-
133
- /**
134
- * Resolve a module path by asking the Python interpreter for `__file__`.
135
- * Caches and returns the resolved path, or `null` when the interpreter is
136
- * unavailable, reports a builtin, or fails.
137
- */
138
- private async resolveViaInterpreter(moduleName: string): Promise<string | null> {
139
- if (!processUtils.isAvailable()) {
140
- return null;
141
- }
142
-
143
- try {
144
- const pythonPath = await this.getPythonExecutable();
145
- const result = await processUtils.exec(
146
- pythonPath,
147
- [
148
- '-c',
149
- `import ${moduleName}; print(${moduleName}.__file__ if hasattr(${moduleName}, '__file__') else '${moduleName}.__path__[0]' if hasattr(${moduleName}, '__path__') else 'builtin')`,
150
- ],
151
- { timeoutMs: this.options.timeoutMs }
152
- );
153
-
154
- if (result.code === 0 && result.stdout.trim() !== 'builtin') {
155
- const modulePath = result.stdout.trim();
156
-
157
- // Cache the result
158
- this.moduleCache.set(moduleName, {
159
- name: moduleName,
160
- path: modulePath,
161
- isPackage: modulePath.endsWith('__init__.py'),
162
- dependencies: [],
163
- });
164
-
165
- return modulePath;
166
- }
167
- } catch (error) {
168
- log.warn('Failed to resolve module', { moduleName, error: String(error) });
169
- }
170
-
171
- return null;
172
- }
173
-
174
- /**
175
- * Fallback resolution: probe `<path>/<module>.py` and
176
- * `<path>/<module>/__init__.py` across the known Python search paths.
177
- * Returns the first existing candidate, or `null`.
178
- */
179
- private async resolveViaSearchPaths(moduleName: string): Promise<string | null> {
180
- const searchPaths = await this.getPythonSearchPaths();
181
-
182
- for (const searchPath of searchPaths) {
183
- const candidates = [
184
- pathUtils.join(searchPath, `${moduleName}.py`),
185
- pathUtils.join(searchPath, moduleName, '__init__.py'),
186
- ];
187
-
188
- for (const candidate of candidates) {
189
- try {
190
- if (fsUtils.isAvailable()) {
191
- await fsUtils.readFile(candidate);
192
- return candidate; // File exists
193
- }
194
- } catch {
195
- // File doesn't exist, continue
196
- }
197
- }
198
- }
199
-
200
- return null;
201
- }
202
-
203
- /**
204
- * Check if a file is a valid Python file
205
- */
206
- isValidPythonFile(path: string): boolean {
207
- if (!path.endsWith('.py')) {
208
- return false;
209
- }
210
-
211
- // Check against exclude patterns
212
- for (const pattern of this.options.excludePatterns ?? []) {
213
- if (pattern.startsWith('*') && path.endsWith(pattern.slice(1))) {
214
- return false;
215
- }
216
- if (path.includes(pattern)) {
217
- return false;
218
- }
219
- }
220
-
221
- return true;
222
- }
223
-
224
- /**
225
- * Build dependency graph for modules
226
- */
227
- async buildDependencyGraph(modules: ModuleInfo[]): Promise<Map<string, Set<string>>> {
228
- this.dependencyGraph.clear();
229
-
230
- for (const module of modules) {
231
- const dependencies = await this.extractDependencies(module.path);
232
- this.dependencyGraph.set(module.name, new Set(dependencies));
233
-
234
- // Update module info
235
- module.dependencies = dependencies;
236
- }
237
-
238
- return new Map(this.dependencyGraph);
239
- }
240
-
241
- /**
242
- * Extract dependencies from Python source file
243
- */
244
- async extractDependencies(filePath: string): Promise<string[]> {
245
- if (!fsUtils.isAvailable()) {
246
- return [];
247
- }
248
-
249
- try {
250
- const source = await fsUtils.readFile(filePath);
251
- return this.parseDependenciesFromSource(source);
252
- } catch (error) {
253
- log.warn('Failed to read file', { filePath, error: String(error) });
254
- return [];
255
- }
256
- }
257
-
258
- /**
259
- * Parse import statements from Python source code
260
- */
261
- parseDependenciesFromSource(source: string): string[] {
262
- const dependencies: string[] = [];
263
-
264
- for (const line of source.split('\n')) {
265
- const moduleName = this.extractImportedModule(line.trim());
266
- if (moduleName) {
267
- dependencies.push(moduleName);
268
- }
269
- }
270
-
271
- return [...new Set(dependencies)]; // Remove duplicates
272
- }
273
-
274
- /**
275
- * Extract the top-level module name imported by a single (trimmed) source
276
- * line, or `null` when the line is a comment, blank, or not an import.
277
- */
278
- private extractImportedModule(trimmed: string): string | null {
279
- // Skip comments and empty lines
280
- if (trimmed.startsWith('#') || trimmed === '') {
281
- return null;
282
- }
283
-
284
- // Match `import x.y.z` and `from x.y.z import ...` statements.
285
- const match =
286
- trimmed.match(/^import\s+([a-zA-Z_][a-zA-Z0-9_\.]*)/) ??
287
- trimmed.match(/^from\s+([a-zA-Z_][a-zA-Z0-9_\.]*)\s+import/);
288
-
289
- return match?.[1]?.split('.')[0] ?? null; // Get top-level module
290
- }
291
-
292
- /**
293
- * Detect circular dependencies
294
- */
295
- detectCircularDependencies(): string[][] {
296
- const cycles: string[][] = [];
297
- const visited = new Set<string>();
298
- const recursionStack = new Set<string>();
299
-
300
- const dfs = (module: string, path: string[]): void => {
301
- if (recursionStack.has(module)) {
302
- // Found cycle
303
- const cycleStart = path.indexOf(module);
304
- cycles.push([...path.slice(cycleStart), module]);
305
- return;
306
- }
307
-
308
- if (visited.has(module)) {
309
- return;
310
- }
311
-
312
- visited.add(module);
313
- recursionStack.add(module);
314
-
315
- const dependencies = this.dependencyGraph.get(module);
316
- if (dependencies) {
317
- for (const dep of dependencies) {
318
- dfs(dep, [...path, module]);
319
- }
320
- }
321
-
322
- recursionStack.delete(module);
323
- };
324
-
325
- for (const module of this.dependencyGraph.keys()) {
326
- if (!visited.has(module)) {
327
- dfs(module, []);
328
- }
329
- }
330
-
331
- return cycles;
332
- }
333
-
334
- /**
335
- * Get Python search paths
336
- */
337
- private async getPythonSearchPaths(): Promise<string[]> {
338
- const paths: string[] = [];
339
-
340
- // Add explicit search paths
341
- if (this.options.searchPaths) {
342
- paths.push(...this.options.searchPaths);
343
- }
344
-
345
- // Add virtual environment paths
346
- if (this.options.virtualEnv) {
347
- paths.push(
348
- pathUtils.join(this.options.virtualEnv, 'lib', 'python*', 'site-packages'),
349
- pathUtils.join(this.options.virtualEnv, 'Lib', 'site-packages') // Windows
350
- );
351
- }
352
-
353
- // Get Python's sys.path if available
354
- if (processUtils.isAvailable()) {
355
- try {
356
- const pythonPath = await this.getPythonExecutable();
357
- const result = await processUtils.exec(
358
- pythonPath,
359
- ['-c', 'import sys; print("\\n".join(sys.path))'],
360
- { timeoutMs: this.options.timeoutMs }
361
- );
362
-
363
- if (result.code === 0) {
364
- const sysPaths = result.stdout
365
- .trim()
366
- .split('\n')
367
- .filter(p => p.trim());
368
- paths.push(...sysPaths);
369
- }
370
- } catch (error) {
371
- log.warn('Failed to get Python sys.path', { error: String(error) });
372
- }
373
- }
374
-
375
- return [...new Set(paths)]; // Remove duplicates
376
- }
377
-
378
- /**
379
- * Scan a directory path for Python modules
380
- */
381
- private async scanPath(path: string): Promise<ModuleInfo[]> {
382
- const modules: ModuleInfo[] = [];
383
-
384
- if (!fsUtils.isAvailable()) {
385
- return modules;
386
- }
387
-
388
- try {
389
- // For now, just create a stub - full directory scanning would need more complex logic
390
- if (this.isValidPythonFile(path)) {
391
- // Single file
392
- const moduleName = this.extractModuleNameFromPath(path);
393
- const dependencies = await this.extractDependencies(path);
394
-
395
- modules.push({
396
- name: moduleName,
397
- path,
398
- isPackage: false,
399
- dependencies,
400
- });
401
- }
402
- } catch (error) {
403
- log.warn('Failed to scan path', { path, error: String(error) });
404
- }
405
-
406
- return modules;
407
- }
408
-
409
- /**
410
- * Extract module name from file path
411
- */
412
- private extractModuleNameFromPath(path: string): string {
413
- // Normalize backslashes to forward slashes for Windows compatibility
414
- const normalizedPath = path.replace(/\\/g, '/');
415
- const parts = normalizedPath.split('/');
416
- const filename = parts[parts.length - 1];
417
-
418
- if (filename === '__init__.py') {
419
- // Package module
420
- return parts[parts.length - 2] ?? 'unknown';
421
- }
422
-
423
- // Regular module
424
- return filename?.replace('.py', '') ?? 'unknown';
425
- }
426
-
427
- /**
428
- * Get module version information
429
- */
430
- async getModuleVersion(moduleName: string): Promise<string | undefined> {
431
- // Reject names that could break out of the `python -c` expression below.
432
- assertSafeModuleName(moduleName);
433
-
434
- if (!processUtils.isAvailable()) {
435
- return undefined;
436
- }
437
-
438
- try {
439
- const pythonPath = await this.getPythonExecutable();
440
- const result = await processUtils.exec(
441
- pythonPath,
442
- ['-c', `import ${moduleName}; print(getattr(${moduleName}, '__version__', 'unknown'))`],
443
- { timeoutMs: this.options.timeoutMs }
444
- );
445
-
446
- if (result.code === 0 && result.stdout.trim() !== 'unknown') {
447
- return result.stdout.trim();
448
- }
449
- } catch (error) {
450
- log.warn('Failed to get module version', { moduleName, error: String(error) });
451
- }
452
-
453
- return undefined;
454
- }
455
-
456
- /**
457
- * Clear module cache
458
- */
459
- clearCache(): void {
460
- this.moduleCache.clear();
461
- this.dependencyGraph.clear();
462
- }
463
-
464
- /**
465
- * Get cached module information
466
- */
467
- getCachedModule(moduleName: string): ModuleInfo | undefined {
468
- return this.moduleCache.get(moduleName);
469
- }
470
-
471
- /**
472
- * Get dependency graph
473
- */
474
- getDependencyGraph(): Map<string, Set<string>> {
475
- return new Map(this.dependencyGraph);
476
- }
477
- }