tywrap 0.1.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 (137) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +319 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +263 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/config/index.d.ts +41 -0
  8. package/dist/config/index.d.ts.map +1 -0
  9. package/dist/config/index.js +234 -0
  10. package/dist/config/index.js.map +1 -0
  11. package/dist/core/analyzer.d.ts +63 -0
  12. package/dist/core/analyzer.d.ts.map +1 -0
  13. package/dist/core/analyzer.js +676 -0
  14. package/dist/core/analyzer.js.map +1 -0
  15. package/dist/core/discovery.d.ts +85 -0
  16. package/dist/core/discovery.d.ts.map +1 -0
  17. package/dist/core/discovery.js +344 -0
  18. package/dist/core/discovery.js.map +1 -0
  19. package/dist/core/generator.d.ts +37 -0
  20. package/dist/core/generator.d.ts.map +1 -0
  21. package/dist/core/generator.js +368 -0
  22. package/dist/core/generator.js.map +1 -0
  23. package/dist/core/mapper.d.ts +40 -0
  24. package/dist/core/mapper.d.ts.map +1 -0
  25. package/dist/core/mapper.js +401 -0
  26. package/dist/core/mapper.js.map +1 -0
  27. package/dist/core/validation.d.ts +79 -0
  28. package/dist/core/validation.d.ts.map +1 -0
  29. package/dist/core/validation.js +486 -0
  30. package/dist/core/validation.js.map +1 -0
  31. package/dist/index.d.ts +26 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +29 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/runtime/base.d.ts +12 -0
  36. package/dist/runtime/base.d.ts.map +1 -0
  37. package/dist/runtime/base.js +6 -0
  38. package/dist/runtime/base.js.map +1 -0
  39. package/dist/runtime/errors.d.ts +17 -0
  40. package/dist/runtime/errors.d.ts.map +1 -0
  41. package/dist/runtime/errors.js +21 -0
  42. package/dist/runtime/errors.js.map +1 -0
  43. package/dist/runtime/http.d.ts +22 -0
  44. package/dist/runtime/http.d.ts.map +1 -0
  45. package/dist/runtime/http.js +79 -0
  46. package/dist/runtime/http.js.map +1 -0
  47. package/dist/runtime/index.d.ts +8 -0
  48. package/dist/runtime/index.d.ts.map +1 -0
  49. package/dist/runtime/index.js +17 -0
  50. package/dist/runtime/index.js.map +1 -0
  51. package/dist/runtime/node.d.ts +48 -0
  52. package/dist/runtime/node.d.ts.map +1 -0
  53. package/dist/runtime/node.js +328 -0
  54. package/dist/runtime/node.js.map +1 -0
  55. package/dist/runtime/optimized-node.d.ts +131 -0
  56. package/dist/runtime/optimized-node.d.ts.map +1 -0
  57. package/dist/runtime/optimized-node.js +622 -0
  58. package/dist/runtime/optimized-node.js.map +1 -0
  59. package/dist/runtime/protocol.d.ts +3 -0
  60. package/dist/runtime/protocol.d.ts.map +1 -0
  61. package/dist/runtime/protocol.js +3 -0
  62. package/dist/runtime/protocol.js.map +1 -0
  63. package/dist/runtime/pyodide.d.ts +27 -0
  64. package/dist/runtime/pyodide.d.ts.map +1 -0
  65. package/dist/runtime/pyodide.js +191 -0
  66. package/dist/runtime/pyodide.js.map +1 -0
  67. package/dist/tools/python_suite.js +173 -0
  68. package/dist/tools/python_suite.js.map +1 -0
  69. package/dist/types/index.d.ts +299 -0
  70. package/dist/types/index.d.ts.map +1 -0
  71. package/dist/types/index.js +5 -0
  72. package/dist/types/index.js.map +1 -0
  73. package/dist/tywrap.d.ts +29 -0
  74. package/dist/tywrap.d.ts.map +1 -0
  75. package/dist/tywrap.js +589 -0
  76. package/dist/tywrap.js.map +1 -0
  77. package/dist/utils/bundle-optimizer.d.ts +182 -0
  78. package/dist/utils/bundle-optimizer.d.ts.map +1 -0
  79. package/dist/utils/bundle-optimizer.js +680 -0
  80. package/dist/utils/bundle-optimizer.js.map +1 -0
  81. package/dist/utils/cache.d.ts +149 -0
  82. package/dist/utils/cache.d.ts.map +1 -0
  83. package/dist/utils/cache.js +492 -0
  84. package/dist/utils/cache.js.map +1 -0
  85. package/dist/utils/codec.d.ts +94 -0
  86. package/dist/utils/codec.d.ts.map +1 -0
  87. package/dist/utils/codec.js +150 -0
  88. package/dist/utils/codec.js.map +1 -0
  89. package/dist/utils/logger.d.ts +42 -0
  90. package/dist/utils/logger.d.ts.map +1 -0
  91. package/dist/utils/logger.js +140 -0
  92. package/dist/utils/logger.js.map +1 -0
  93. package/dist/utils/memory-profiler.d.ts +123 -0
  94. package/dist/utils/memory-profiler.d.ts.map +1 -0
  95. package/dist/utils/memory-profiler.js +426 -0
  96. package/dist/utils/memory-profiler.js.map +1 -0
  97. package/dist/utils/parallel-processor.d.ts +146 -0
  98. package/dist/utils/parallel-processor.d.ts.map +1 -0
  99. package/dist/utils/parallel-processor.js +636 -0
  100. package/dist/utils/parallel-processor.js.map +1 -0
  101. package/dist/utils/python.d.ts +8 -0
  102. package/dist/utils/python.d.ts.map +1 -0
  103. package/dist/utils/python.js +56 -0
  104. package/dist/utils/python.js.map +1 -0
  105. package/dist/utils/runtime.d.ts +153 -0
  106. package/dist/utils/runtime.d.ts.map +1 -0
  107. package/dist/utils/runtime.js +519 -0
  108. package/dist/utils/runtime.js.map +1 -0
  109. package/package.json +136 -0
  110. package/runtime/python_bridge.py +476 -0
  111. package/src/cli.ts +331 -0
  112. package/src/config/index.ts +289 -0
  113. package/src/core/analyzer.ts +788 -0
  114. package/src/core/discovery.ts +415 -0
  115. package/src/core/generator.ts +447 -0
  116. package/src/core/mapper.ts +504 -0
  117. package/src/core/validation.ts +693 -0
  118. package/src/index.ts +83 -0
  119. package/src/runtime/base.ts +32 -0
  120. package/src/runtime/errors.ts +19 -0
  121. package/src/runtime/http.ts +132 -0
  122. package/src/runtime/index.ts +24 -0
  123. package/src/runtime/node.ts +431 -0
  124. package/src/runtime/optimized-node.ts +838 -0
  125. package/src/runtime/protocol.ts +2 -0
  126. package/src/runtime/pyodide.ts +228 -0
  127. package/src/types/global.d.ts +60 -0
  128. package/src/types/index.ts +410 -0
  129. package/src/tywrap.ts +669 -0
  130. package/src/utils/bundle-optimizer.ts +876 -0
  131. package/src/utils/cache.ts +634 -0
  132. package/src/utils/codec.ts +275 -0
  133. package/src/utils/logger.ts +201 -0
  134. package/src/utils/memory-profiler.ts +582 -0
  135. package/src/utils/parallel-processor.ts +879 -0
  136. package/src/utils/python.ts +79 -0
  137. package/src/utils/runtime.ts +616 -0
@@ -0,0 +1,415 @@
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
+ export interface ModuleInfo {
16
+ name: string;
17
+ path: string;
18
+ version?: string;
19
+ isPackage: boolean;
20
+ dependencies: string[];
21
+ }
22
+
23
+ export interface DiscoveryOptions {
24
+ pythonPath?: string;
25
+ virtualEnv?: string;
26
+ searchPaths?: string[];
27
+ excludePatterns?: string[];
28
+ includeStdLib?: boolean;
29
+ }
30
+
31
+ export class ModuleDiscovery {
32
+ private options: DiscoveryOptions;
33
+ private moduleCache = new Map<string, ModuleInfo>();
34
+ private dependencyGraph = new Map<string, Set<string>>();
35
+ private pythonExecPromise?: Promise<string>;
36
+
37
+ constructor(options: DiscoveryOptions = {}) {
38
+ this.options = {
39
+ includeStdLib: false,
40
+ excludePatterns: ['__pycache__', '*.pyc', '.git', '.svn'],
41
+ ...options,
42
+ };
43
+ }
44
+
45
+ private async getPythonExecutable(): Promise<string> {
46
+ this.pythonExecPromise ??= resolvePythonExecutable({
47
+ pythonPath: this.options.pythonPath,
48
+ virtualEnv: this.options.virtualEnv,
49
+ });
50
+ return this.pythonExecPromise;
51
+ }
52
+
53
+ /**
54
+ * Find Python modules from various sources
55
+ */
56
+ async findPythonModules(paths: string[]): Promise<ModuleInfo[]> {
57
+ const modules: ModuleInfo[] = [];
58
+ const seen = new Set<string>();
59
+
60
+ for (const path of paths) {
61
+ try {
62
+ const foundModules = await this.scanPath(path);
63
+ for (const module of foundModules) {
64
+ if (!seen.has(module.name)) {
65
+ modules.push(module);
66
+ seen.add(module.name);
67
+ this.moduleCache.set(module.name, module);
68
+ }
69
+ }
70
+ } catch (error) {
71
+ log.warn('Failed to scan path', { path, error: String(error) });
72
+ }
73
+ }
74
+
75
+ return modules;
76
+ }
77
+
78
+ /**
79
+ * Resolve Python module path from module name
80
+ */
81
+ async resolvePythonPath(moduleName: string): Promise<string | null> {
82
+ // Check cache first
83
+ const cached = this.moduleCache.get(moduleName);
84
+ if (cached) {
85
+ return cached.path;
86
+ }
87
+
88
+ // Try to resolve using Python's module finder
89
+ if (processUtils.isAvailable()) {
90
+ try {
91
+ const pythonPath = await this.getPythonExecutable();
92
+ const result = await processUtils.exec(pythonPath, [
93
+ '-c',
94
+ `import ${moduleName}; print(${moduleName}.__file__ if hasattr(${moduleName}, '__file__') else '${moduleName}.__path__[0]' if hasattr(${moduleName}, '__path__') else 'builtin')`,
95
+ ]);
96
+
97
+ if (result.code === 0 && result.stdout.trim() !== 'builtin') {
98
+ const modulePath = result.stdout.trim();
99
+
100
+ // Cache the result
101
+ this.moduleCache.set(moduleName, {
102
+ name: moduleName,
103
+ path: modulePath,
104
+ isPackage: modulePath.endsWith('__init__.py'),
105
+ dependencies: [],
106
+ });
107
+
108
+ return modulePath;
109
+ }
110
+ } catch (error) {
111
+ log.warn('Failed to resolve module', { moduleName, error: String(error) });
112
+ }
113
+ }
114
+
115
+ // Fallback: search in known Python paths
116
+ const searchPaths = await this.getPythonSearchPaths();
117
+
118
+ for (const searchPath of searchPaths) {
119
+ const candidates = [
120
+ pathUtils.join(searchPath, `${moduleName}.py`),
121
+ pathUtils.join(searchPath, moduleName, '__init__.py'),
122
+ ];
123
+
124
+ for (const candidate of candidates) {
125
+ try {
126
+ if (fsUtils.isAvailable()) {
127
+ await fsUtils.readFile(candidate);
128
+ return candidate; // File exists
129
+ }
130
+ } catch {
131
+ // File doesn't exist, continue
132
+ }
133
+ }
134
+ }
135
+
136
+ return null;
137
+ }
138
+
139
+ /**
140
+ * Check if a file is a valid Python file
141
+ */
142
+ isValidPythonFile(path: string): boolean {
143
+ if (!path.endsWith('.py')) {
144
+ return false;
145
+ }
146
+
147
+ // Check against exclude patterns
148
+ for (const pattern of this.options.excludePatterns ?? []) {
149
+ if (pattern.startsWith('*') && path.endsWith(pattern.slice(1))) {
150
+ return false;
151
+ }
152
+ if (path.includes(pattern)) {
153
+ return false;
154
+ }
155
+ }
156
+
157
+ return true;
158
+ }
159
+
160
+ /**
161
+ * Build dependency graph for modules
162
+ */
163
+ async buildDependencyGraph(modules: ModuleInfo[]): Promise<Map<string, Set<string>>> {
164
+ this.dependencyGraph.clear();
165
+
166
+ for (const module of modules) {
167
+ const dependencies = await this.extractDependencies(module.path);
168
+ this.dependencyGraph.set(module.name, new Set(dependencies));
169
+
170
+ // Update module info
171
+ module.dependencies = dependencies;
172
+ }
173
+
174
+ return new Map(this.dependencyGraph);
175
+ }
176
+
177
+ /**
178
+ * Extract dependencies from Python source file
179
+ */
180
+ async extractDependencies(filePath: string): Promise<string[]> {
181
+ if (!fsUtils.isAvailable()) {
182
+ return [];
183
+ }
184
+
185
+ try {
186
+ const source = await fsUtils.readFile(filePath);
187
+ return this.parseDependenciesFromSource(source);
188
+ } catch (error) {
189
+ log.warn('Failed to read file', { filePath, error: String(error) });
190
+ return [];
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Parse import statements from Python source code
196
+ */
197
+ parseDependenciesFromSource(source: string): string[] {
198
+ const dependencies: string[] = [];
199
+ const lines = source.split('\n');
200
+
201
+ for (const line of lines) {
202
+ const trimmed = line.trim();
203
+
204
+ // Skip comments and empty lines
205
+ if (trimmed.startsWith('#') || trimmed === '') {
206
+ continue;
207
+ }
208
+
209
+ // Match import statements
210
+ const importMatch = trimmed.match(/^import\s+([a-zA-Z_][a-zA-Z0-9_\.]*)/);
211
+ if (importMatch?.[1]) {
212
+ const fullModuleName = importMatch[1];
213
+ const moduleName = fullModuleName.split('.')[0]; // Get top-level module
214
+ if (moduleName) {
215
+ dependencies.push(moduleName);
216
+ }
217
+ continue;
218
+ }
219
+
220
+ // Match from ... import statements
221
+ const fromMatch = trimmed.match(/^from\s+([a-zA-Z_][a-zA-Z0-9_\.]*)\s+import/);
222
+ if (fromMatch?.[1]) {
223
+ const fullModuleName = fromMatch[1];
224
+ const moduleName = fullModuleName.split('.')[0]; // Get top-level module
225
+ if (moduleName) {
226
+ dependencies.push(moduleName);
227
+ }
228
+ continue;
229
+ }
230
+ }
231
+
232
+ return [...new Set(dependencies)]; // Remove duplicates
233
+ }
234
+
235
+ /**
236
+ * Detect circular dependencies
237
+ */
238
+ detectCircularDependencies(): string[][] {
239
+ const cycles: string[][] = [];
240
+ const visited = new Set<string>();
241
+ const recursionStack = new Set<string>();
242
+
243
+ const dfs = (module: string, path: string[]): void => {
244
+ if (recursionStack.has(module)) {
245
+ // Found cycle
246
+ const cycleStart = path.indexOf(module);
247
+ cycles.push([...path.slice(cycleStart), module]);
248
+ return;
249
+ }
250
+
251
+ if (visited.has(module)) {
252
+ return;
253
+ }
254
+
255
+ visited.add(module);
256
+ recursionStack.add(module);
257
+
258
+ const dependencies = this.dependencyGraph.get(module);
259
+ if (dependencies) {
260
+ for (const dep of dependencies) {
261
+ dfs(dep, [...path, module]);
262
+ }
263
+ }
264
+
265
+ recursionStack.delete(module);
266
+ };
267
+
268
+ for (const module of this.dependencyGraph.keys()) {
269
+ if (!visited.has(module)) {
270
+ dfs(module, []);
271
+ }
272
+ }
273
+
274
+ return cycles;
275
+ }
276
+
277
+ /**
278
+ * Get Python search paths
279
+ */
280
+ private async getPythonSearchPaths(): Promise<string[]> {
281
+ const paths: string[] = [];
282
+
283
+ // Add explicit search paths
284
+ if (this.options.searchPaths) {
285
+ paths.push(...this.options.searchPaths);
286
+ }
287
+
288
+ // Add virtual environment paths
289
+ if (this.options.virtualEnv) {
290
+ paths.push(
291
+ pathUtils.join(this.options.virtualEnv, 'lib', 'python*', 'site-packages'),
292
+ pathUtils.join(this.options.virtualEnv, 'Lib', 'site-packages') // Windows
293
+ );
294
+ }
295
+
296
+ // Get Python's sys.path if available
297
+ if (processUtils.isAvailable()) {
298
+ try {
299
+ const pythonPath = await this.getPythonExecutable();
300
+ const result = await processUtils.exec(pythonPath, [
301
+ '-c',
302
+ 'import sys; print("\\n".join(sys.path))',
303
+ ]);
304
+
305
+ if (result.code === 0) {
306
+ const sysPaths = result.stdout
307
+ .trim()
308
+ .split('\n')
309
+ .filter(p => p.trim());
310
+ paths.push(...sysPaths);
311
+ }
312
+ } catch (error) {
313
+ log.warn('Failed to get Python sys.path', { error: String(error) });
314
+ }
315
+ }
316
+
317
+ return [...new Set(paths)]; // Remove duplicates
318
+ }
319
+
320
+ /**
321
+ * Scan a directory path for Python modules
322
+ */
323
+ private async scanPath(path: string): Promise<ModuleInfo[]> {
324
+ const modules: ModuleInfo[] = [];
325
+
326
+ if (!fsUtils.isAvailable()) {
327
+ return modules;
328
+ }
329
+
330
+ try {
331
+ // For now, just create a stub - full directory scanning would need more complex logic
332
+ if (this.isValidPythonFile(path)) {
333
+ // Single file
334
+ const moduleName = this.extractModuleNameFromPath(path);
335
+ const dependencies = await this.extractDependencies(path);
336
+
337
+ modules.push({
338
+ name: moduleName,
339
+ path,
340
+ isPackage: false,
341
+ dependencies,
342
+ });
343
+ }
344
+ } catch (error) {
345
+ log.warn('Failed to scan path', { path, error: String(error) });
346
+ }
347
+
348
+ return modules;
349
+ }
350
+
351
+ /**
352
+ * Extract module name from file path
353
+ */
354
+ private extractModuleNameFromPath(path: string): string {
355
+ // Normalize backslashes to forward slashes for Windows compatibility
356
+ const normalizedPath = path.replace(/\\/g, '/');
357
+ const parts = normalizedPath.split('/');
358
+ const filename = parts[parts.length - 1];
359
+
360
+ if (filename === '__init__.py') {
361
+ // Package module
362
+ return parts[parts.length - 2] ?? 'unknown';
363
+ }
364
+
365
+ // Regular module
366
+ return filename?.replace('.py', '') ?? 'unknown';
367
+ }
368
+
369
+ /**
370
+ * Get module version information
371
+ */
372
+ async getModuleVersion(moduleName: string): Promise<string | undefined> {
373
+ if (!processUtils.isAvailable()) {
374
+ return undefined;
375
+ }
376
+
377
+ try {
378
+ const pythonPath = await this.getPythonExecutable();
379
+ const result = await processUtils.exec(pythonPath, [
380
+ '-c',
381
+ `import ${moduleName}; print(getattr(${moduleName}, '__version__', 'unknown'))`,
382
+ ]);
383
+
384
+ if (result.code === 0 && result.stdout.trim() !== 'unknown') {
385
+ return result.stdout.trim();
386
+ }
387
+ } catch (error) {
388
+ log.warn('Failed to get module version', { moduleName, error: String(error) });
389
+ }
390
+
391
+ return undefined;
392
+ }
393
+
394
+ /**
395
+ * Clear module cache
396
+ */
397
+ clearCache(): void {
398
+ this.moduleCache.clear();
399
+ this.dependencyGraph.clear();
400
+ }
401
+
402
+ /**
403
+ * Get cached module information
404
+ */
405
+ getCachedModule(moduleName: string): ModuleInfo | undefined {
406
+ return this.moduleCache.get(moduleName);
407
+ }
408
+
409
+ /**
410
+ * Get dependency graph
411
+ */
412
+ getDependencyGraph(): Map<string, Set<string>> {
413
+ return new Map(this.dependencyGraph);
414
+ }
415
+ }