tywrap 0.2.1 → 0.3.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 (138) hide show
  1. package/README.md +65 -31
  2. package/dist/cli.js +49 -2
  3. package/dist/cli.js.map +1 -1
  4. package/dist/config/index.d.ts +11 -7
  5. package/dist/config/index.d.ts.map +1 -1
  6. package/dist/config/index.js +43 -7
  7. package/dist/config/index.js.map +1 -1
  8. package/dist/core/analyzer.d.ts +1 -0
  9. package/dist/core/analyzer.d.ts.map +1 -1
  10. package/dist/core/analyzer.js +47 -12
  11. package/dist/core/analyzer.js.map +1 -1
  12. package/dist/core/annotation-parser.d.ts +8 -0
  13. package/dist/core/annotation-parser.d.ts.map +1 -0
  14. package/dist/core/annotation-parser.js +409 -0
  15. package/dist/core/annotation-parser.js.map +1 -0
  16. package/dist/core/discovery.d.ts +1 -0
  17. package/dist/core/discovery.d.ts.map +1 -1
  18. package/dist/core/discovery.js +9 -9
  19. package/dist/core/discovery.js.map +1 -1
  20. package/dist/core/generator.d.ts +9 -1
  21. package/dist/core/generator.d.ts.map +1 -1
  22. package/dist/core/generator.js +738 -104
  23. package/dist/core/generator.js.map +1 -1
  24. package/dist/core/mapper.d.ts +9 -7
  25. package/dist/core/mapper.d.ts.map +1 -1
  26. package/dist/core/mapper.js +129 -44
  27. package/dist/core/mapper.js.map +1 -1
  28. package/dist/index.d.ts +7 -7
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +4 -4
  31. package/dist/index.js.map +1 -1
  32. package/dist/runtime/bridge-core.d.ts +1 -0
  33. package/dist/runtime/bridge-core.d.ts.map +1 -1
  34. package/dist/runtime/bridge-core.js +52 -17
  35. package/dist/runtime/bridge-core.js.map +1 -1
  36. package/dist/runtime/bridge-protocol.d.ts +17 -0
  37. package/dist/runtime/bridge-protocol.d.ts.map +1 -1
  38. package/dist/runtime/bridge-protocol.js +97 -0
  39. package/dist/runtime/bridge-protocol.js.map +1 -1
  40. package/dist/runtime/errors.d.ts +10 -0
  41. package/dist/runtime/errors.d.ts.map +1 -1
  42. package/dist/runtime/errors.js +9 -0
  43. package/dist/runtime/errors.js.map +1 -1
  44. package/dist/runtime/http-io.d.ts.map +1 -1
  45. package/dist/runtime/http-io.js.map +1 -1
  46. package/dist/runtime/http.d.ts +1 -0
  47. package/dist/runtime/http.d.ts.map +1 -1
  48. package/dist/runtime/http.js +6 -0
  49. package/dist/runtime/http.js.map +1 -1
  50. package/dist/runtime/node.d.ts.map +1 -1
  51. package/dist/runtime/node.js +170 -32
  52. package/dist/runtime/node.js.map +1 -1
  53. package/dist/runtime/optimized-node.d.ts +1 -1
  54. package/dist/runtime/optimized-node.d.ts.map +1 -1
  55. package/dist/runtime/optimized-node.js +1 -1
  56. package/dist/runtime/optimized-node.js.map +1 -1
  57. package/dist/runtime/pooled-transport.d.ts +4 -0
  58. package/dist/runtime/pooled-transport.d.ts.map +1 -1
  59. package/dist/runtime/pooled-transport.js +2 -0
  60. package/dist/runtime/pooled-transport.js.map +1 -1
  61. package/dist/runtime/process-io.d.ts +5 -0
  62. package/dist/runtime/process-io.d.ts.map +1 -1
  63. package/dist/runtime/process-io.js +64 -30
  64. package/dist/runtime/process-io.js.map +1 -1
  65. package/dist/runtime/pyodide-io.d.ts +4 -5
  66. package/dist/runtime/pyodide-io.d.ts.map +1 -1
  67. package/dist/runtime/pyodide-io.js +99 -37
  68. package/dist/runtime/pyodide-io.js.map +1 -1
  69. package/dist/runtime/safe-codec.d.ts +25 -3
  70. package/dist/runtime/safe-codec.d.ts.map +1 -1
  71. package/dist/runtime/safe-codec.js +231 -57
  72. package/dist/runtime/safe-codec.js.map +1 -1
  73. package/dist/runtime/transport.d.ts +6 -1
  74. package/dist/runtime/transport.d.ts.map +1 -1
  75. package/dist/runtime/transport.js.map +1 -1
  76. package/dist/runtime/validators.d.ts +0 -11
  77. package/dist/runtime/validators.d.ts.map +1 -1
  78. package/dist/runtime/validators.js +13 -3
  79. package/dist/runtime/validators.js.map +1 -1
  80. package/dist/runtime/worker-pool.d.ts +12 -0
  81. package/dist/runtime/worker-pool.d.ts.map +1 -1
  82. package/dist/runtime/worker-pool.js +53 -5
  83. package/dist/runtime/worker-pool.js.map +1 -1
  84. package/dist/types/index.d.ts +51 -1
  85. package/dist/types/index.d.ts.map +1 -1
  86. package/dist/tywrap.d.ts.map +1 -1
  87. package/dist/tywrap.js +227 -328
  88. package/dist/tywrap.js.map +1 -1
  89. package/dist/utils/cache.d.ts.map +1 -1
  90. package/dist/utils/cache.js +53 -5
  91. package/dist/utils/cache.js.map +1 -1
  92. package/dist/utils/codec.d.ts.map +1 -1
  93. package/dist/utils/codec.js +10 -16
  94. package/dist/utils/codec.js.map +1 -1
  95. package/dist/utils/ir-cache.d.ts +28 -0
  96. package/dist/utils/ir-cache.d.ts.map +1 -0
  97. package/dist/utils/ir-cache.js +29 -0
  98. package/dist/utils/ir-cache.js.map +1 -0
  99. package/dist/utils/parallel-processor.d.ts +1 -1
  100. package/dist/utils/parallel-processor.d.ts.map +1 -1
  101. package/dist/utils/parallel-processor.js +111 -40
  102. package/dist/utils/parallel-processor.js.map +1 -1
  103. package/dist/utils/runtime.d.ts +5 -1
  104. package/dist/utils/runtime.d.ts.map +1 -1
  105. package/dist/utils/runtime.js +112 -18
  106. package/dist/utils/runtime.js.map +1 -1
  107. package/package.json +40 -18
  108. package/runtime/python_bridge.py +78 -12
  109. package/src/cli.ts +61 -2
  110. package/src/config/index.ts +63 -17
  111. package/src/core/analyzer.ts +52 -15
  112. package/src/core/annotation-parser.ts +500 -0
  113. package/src/core/discovery.ts +26 -12
  114. package/src/core/generator.ts +956 -116
  115. package/src/core/mapper.ts +158 -51
  116. package/src/index.ts +34 -5
  117. package/src/runtime/bridge-core.ts +58 -18
  118. package/src/runtime/bridge-protocol.ts +164 -0
  119. package/src/runtime/errors.ts +14 -0
  120. package/src/runtime/http-io.ts +2 -1
  121. package/src/runtime/http.ts +7 -0
  122. package/src/runtime/node.ts +264 -44
  123. package/src/runtime/optimized-node.ts +4 -1
  124. package/src/runtime/pooled-transport.ts +12 -1
  125. package/src/runtime/process-io.ts +83 -55
  126. package/src/runtime/pyodide-io.ts +110 -44
  127. package/src/runtime/safe-codec.ts +290 -82
  128. package/src/runtime/transport.ts +6 -1
  129. package/src/runtime/validators.ts +14 -3
  130. package/src/runtime/worker-pool.ts +67 -6
  131. package/src/types/global.d.ts +11 -1
  132. package/src/types/index.ts +63 -0
  133. package/src/tywrap.ts +279 -358
  134. package/src/utils/cache.ts +59 -5
  135. package/src/utils/codec.ts +11 -15
  136. package/src/utils/ir-cache.ts +51 -0
  137. package/src/utils/parallel-processor.ts +119 -43
  138. package/src/utils/runtime.ts +129 -18
@@ -39,6 +39,13 @@ export interface WorkerPoolOptions {
39
39
  * Use this for per-worker warmup (e.g., importing modules, running setup).
40
40
  */
41
41
  onWorkerReady?: (worker: PooledWorker) => Promise<void>;
42
+
43
+ /**
44
+ * Optional callback used only for background replacement workers after a
45
+ * fatal timeout/crash. This lets callers publish a replacement only after it
46
+ * is proven ready, without charging hidden work to normal request startup.
47
+ */
48
+ onReplacementWorkerReady?: (worker: PooledWorker) => Promise<void>;
42
49
  }
43
50
 
44
51
  /**
@@ -99,8 +106,12 @@ interface QueuedWaiter {
99
106
  * ```
100
107
  */
101
108
  export class WorkerPool extends BoundedContext {
102
- private readonly options: Omit<Required<WorkerPoolOptions>, 'onWorkerReady'> & {
109
+ private readonly options: Omit<
110
+ Required<WorkerPoolOptions>,
111
+ 'onWorkerReady' | 'onReplacementWorkerReady'
112
+ > & {
103
113
  onWorkerReady?: (worker: PooledWorker) => Promise<void>;
114
+ onReplacementWorkerReady?: (worker: PooledWorker) => Promise<void>;
104
115
  };
105
116
  private readonly workers: PooledWorker[] = [];
106
117
  private readonly waitQueue: QueuedWaiter[] = [];
@@ -135,6 +146,7 @@ export class WorkerPool extends BoundedContext {
135
146
  queueTimeoutMs: options.queueTimeoutMs ?? 30000,
136
147
  maxConcurrentPerWorker: options.maxConcurrentPerWorker ?? 1,
137
148
  onWorkerReady: options.onWorkerReady,
149
+ onReplacementWorkerReady: options.onReplacementWorkerReady,
138
150
  };
139
151
  }
140
152
 
@@ -319,8 +331,14 @@ export class WorkerPool extends BoundedContext {
319
331
  * - Process exited unexpectedly
320
332
  * - Pipe errors (EPIPE)
321
333
  * - Connection reset errors (ECONNRESET)
334
+ * - Request timeouts (the worker may still be busy with an uncancellable request)
322
335
  */
323
336
  private isFatalWorkerError(error: unknown): boolean {
337
+ // If a request times out, the underlying transport may still be executing it.
338
+ // Quarantine this worker so subsequent requests don't get stuck behind it.
339
+ if (error instanceof BridgeTimeoutError) {
340
+ return true;
341
+ }
324
342
  if (error instanceof BridgeProtocolError) {
325
343
  const msg = error.message.toLowerCase();
326
344
  return (
@@ -347,6 +365,7 @@ export class WorkerPool extends BoundedContext {
347
365
  worker.transport.dispose().catch(() => {
348
366
  // Ignore disposal errors for dead workers
349
367
  });
368
+ this.scheduleReplacementWorker();
350
369
  }
351
370
  }
352
371
 
@@ -386,13 +405,40 @@ export class WorkerPool extends BoundedContext {
386
405
  return this.workers.find(w => w.inFlightCount < this.options.maxConcurrentPerWorker);
387
406
  }
388
407
 
408
+ /**
409
+ * Replace a removed worker in the background so the next caller does not pay
410
+ * the full worker cold-start penalty after a timeout or crash.
411
+ */
412
+ private scheduleReplacementWorker(): void {
413
+ if (this.state !== 'ready') {
414
+ return;
415
+ }
416
+ if (this.workers.length + this.pendingCreations >= this.options.maxWorkers) {
417
+ return;
418
+ }
419
+
420
+ this.pendingCreations++;
421
+ const replacementReady = this.options.onReplacementWorkerReady ?? this.options.onWorkerReady;
422
+ this.createWorker(replacementReady)
423
+ .then(worker => {
424
+ // Reuse release() to wake queued callers if one is waiting.
425
+ this.release(worker);
426
+ })
427
+ .catch(() => {
428
+ // Ignore background replacement failures. A later acquire() can retry.
429
+ })
430
+ .finally(() => {
431
+ this.pendingCreations = Math.max(0, this.pendingCreations - 1);
432
+ });
433
+ }
434
+
389
435
  /**
390
436
  * Create a new worker and add it to the pool.
391
437
  *
392
438
  * If onWorkerReady is configured, calls it after the transport is initialized.
393
439
  * This is useful for per-worker warmup (importing modules, running setup).
394
440
  */
395
- private async createWorker(): Promise<PooledWorker> {
441
+ private async createWorker(onWorkerReady = this.options.onWorkerReady): Promise<PooledWorker> {
396
442
  const transport = this.options.createTransport();
397
443
 
398
444
  // Initialize the transport
@@ -403,13 +449,28 @@ export class WorkerPool extends BoundedContext {
403
449
  inFlightCount: 0,
404
450
  };
405
451
 
406
- this.workers.push(worker);
452
+ try {
453
+ // Call onWorkerReady callback if provided
454
+ if (onWorkerReady) {
455
+ await onWorkerReady(worker);
456
+ }
457
+ } catch (error) {
458
+ // Ensure partially initialized workers do not leak when warmup fails.
459
+ await transport.dispose().catch(() => {
460
+ // Ignore disposal failures during warmup failure handling.
461
+ });
462
+ throw error;
463
+ }
407
464
 
408
- // Call onWorkerReady callback if provided
409
- if (this.options.onWorkerReady) {
410
- await this.options.onWorkerReady(worker);
465
+ if (this.state === 'disposing' || this.state === 'disposed') {
466
+ await transport.dispose().catch(() => {
467
+ // Ignore disposal failures if the pool was torn down mid-creation.
468
+ });
469
+ throw new BridgeExecutionError('Pool disposed during worker creation');
411
470
  }
412
471
 
472
+ this.workers.push(worker);
473
+
413
474
  return worker;
414
475
  }
415
476
 
@@ -8,13 +8,20 @@ declare namespace DenoTypes {
8
8
  deno: string;
9
9
  }
10
10
 
11
+ interface CommandOptions {
12
+ args?: string[];
13
+ cwd?: string;
14
+ env?: Record<string, string>;
15
+ signal?: AbortSignal;
16
+ }
17
+
11
18
  interface DenoGlobal {
12
19
  version: Version;
13
20
  readTextFile(path: string): Promise<string>;
14
21
  writeTextFile(path: string, content: string): Promise<void>;
15
22
  Command: new (
16
23
  cmd: string,
17
- options?: { args?: string[] }
24
+ options?: CommandOptions
18
25
  ) => {
19
26
  output(): Promise<{ code: number; stdout: Uint8Array; stderr: Uint8Array }>;
20
27
  };
@@ -37,12 +44,15 @@ declare namespace BunTypes {
37
44
  options?: {
38
45
  stdout?: string;
39
46
  stderr?: string;
47
+ cwd?: string;
48
+ env?: Record<string, string>;
40
49
  }
41
50
  ): {
42
51
  stdout: ReadableStream;
43
52
  stderr: ReadableStream;
44
53
  exitCode?: number;
45
54
  exited: Promise<void>;
55
+ kill(signal?: number | string): void;
46
56
  };
47
57
  }
48
58
  }
@@ -8,6 +8,7 @@ export interface PythonModule {
8
8
  version?: string;
9
9
  functions: PythonFunction[];
10
10
  classes: PythonClass[];
11
+ typeAliases?: PythonTypeAlias[];
11
12
  imports: PythonImport[];
12
13
  exports: string[];
13
14
  }
@@ -19,6 +20,7 @@ export interface PythonFunction {
19
20
  decorators: string[];
20
21
  isAsync: boolean;
21
22
  isGenerator: boolean;
23
+ typeParameters?: PythonGenericParameter[];
22
24
  returnType: PythonType;
23
25
  parameters: Parameter[];
24
26
  }
@@ -31,6 +33,23 @@ export interface PythonClass {
31
33
  docstring?: string;
32
34
  decorators: string[];
33
35
  kind?: 'class' | 'protocol' | 'typed_dict' | 'namedtuple' | 'dataclass' | 'pydantic';
36
+ typeParameters?: PythonGenericParameter[];
37
+ }
38
+
39
+ export interface PythonTypeAlias {
40
+ name: string;
41
+ type: PythonType;
42
+ typeParameters?: PythonGenericParameter[];
43
+ }
44
+
45
+ export type PythonGenericParameterKind = 'typevar' | 'paramspec' | 'typevartuple';
46
+
47
+ export interface PythonGenericParameter {
48
+ name: string;
49
+ kind: PythonGenericParameterKind;
50
+ bound?: PythonType;
51
+ constraints?: PythonType[];
52
+ variance?: 'covariant' | 'contravariant' | 'invariant';
34
53
  }
35
54
 
36
55
  export interface PythonImport {
@@ -47,12 +66,15 @@ export interface Parameter {
47
66
  defaultValue?: unknown;
48
67
  varArgs: boolean;
49
68
  kwArgs: boolean;
69
+ positionalOnly?: boolean;
70
+ keywordOnly?: boolean;
50
71
  }
51
72
 
52
73
  export interface Property {
53
74
  name: string;
54
75
  type: PythonType;
55
76
  readonly: boolean;
77
+ optional?: boolean;
56
78
  setter?: boolean;
57
79
  getter?: boolean;
58
80
  }
@@ -76,6 +98,11 @@ export type PythonType =
76
98
  | AnnotatedType
77
99
  | CustomType
78
100
  | TypeVarType
101
+ | ParamSpecType
102
+ | ParamSpecArgsType
103
+ | ParamSpecKwargsType
104
+ | TypeVarTupleType
105
+ | UnpackType
79
106
  | FinalType
80
107
  | ClassVarType;
81
108
 
@@ -103,6 +130,7 @@ export interface OptionalType {
103
130
  export interface GenericType {
104
131
  kind: 'generic';
105
132
  name: string;
133
+ module?: string;
106
134
  typeArgs: PythonType[];
107
135
  }
108
136
 
@@ -115,6 +143,7 @@ export interface CustomType {
115
143
  export interface CallableType {
116
144
  kind: 'callable';
117
145
  parameters: PythonType[];
146
+ parameterSpec?: ParamSpecType;
118
147
  returnType: PythonType;
119
148
  }
120
149
 
@@ -137,6 +166,31 @@ export interface TypeVarType {
137
166
  variance?: 'covariant' | 'contravariant' | 'invariant';
138
167
  }
139
168
 
169
+ export interface ParamSpecType {
170
+ kind: 'paramspec';
171
+ name: string;
172
+ }
173
+
174
+ export interface ParamSpecArgsType {
175
+ kind: 'paramspec_args';
176
+ name: string;
177
+ }
178
+
179
+ export interface ParamSpecKwargsType {
180
+ kind: 'paramspec_kwargs';
181
+ name: string;
182
+ }
183
+
184
+ export interface TypeVarTupleType {
185
+ kind: 'typevartuple';
186
+ name: string;
187
+ }
188
+
189
+ export interface UnpackType {
190
+ kind: 'unpack';
191
+ type: PythonType;
192
+ }
193
+
140
194
  export interface FinalType {
141
195
  kind: 'final';
142
196
  type: PythonType;
@@ -242,6 +296,11 @@ export type RuntimeStrategy = 'pyodide' | 'node' | 'http' | 'auto';
242
296
 
243
297
  export interface TywrapOptions {
244
298
  pythonModules: Record<string, PythonModuleConfig>;
299
+ /**
300
+ * Additional Python import paths to prepend to PYTHONPATH during code generation
301
+ * and discovery (IR extraction). Useful for local modules not installed in site-packages.
302
+ */
303
+ pythonImportPath?: string[];
245
304
  output: OutputConfig;
246
305
  runtime: RuntimeConfig;
247
306
  performance: PerformanceConfig;
@@ -255,6 +314,10 @@ export interface PythonModuleConfig {
255
314
  runtime: RuntimeStrategy;
256
315
  functions?: string[];
257
316
  classes?: string[];
317
+ /** Exclude specific exports by exact name. */
318
+ exclude?: string[];
319
+ /** Exclude exports matching one or more regex patterns (JavaScript RegExp source). */
320
+ excludePatterns?: string[];
258
321
  alias?: string;
259
322
  typeHints: 'strict' | 'loose' | 'ignore';
260
323
  watch?: boolean;