tywrap 0.7.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 (165) hide show
  1. package/README.md +36 -11
  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/emit-call.d.ts.map +1 -1
  11. package/dist/core/emit-call.js +1 -1
  12. package/dist/core/emit-call.js.map +1 -1
  13. package/dist/core/generator.d.ts +23 -4
  14. package/dist/core/generator.d.ts.map +1 -1
  15. package/dist/core/generator.js +243 -170
  16. package/dist/core/generator.js.map +1 -1
  17. package/dist/core/mapper.d.ts +3 -2
  18. package/dist/core/mapper.d.ts.map +1 -1
  19. package/dist/core/mapper.js +5 -5
  20. package/dist/core/mapper.js.map +1 -1
  21. package/dist/dev.d.ts.map +1 -1
  22. package/dist/dev.js +1 -3
  23. package/dist/dev.js.map +1 -1
  24. package/dist/index.d.ts +7 -5
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +6 -4
  27. package/dist/index.js.map +1 -1
  28. package/dist/runtime/base-bridge.d.ts +3 -7
  29. package/dist/runtime/base-bridge.d.ts.map +1 -1
  30. package/dist/runtime/base-bridge.js +4 -17
  31. package/dist/runtime/base-bridge.js.map +1 -1
  32. package/dist/runtime/bounded-context.d.ts +3 -22
  33. package/dist/runtime/bounded-context.d.ts.map +1 -1
  34. package/dist/runtime/bounded-context.js +13 -53
  35. package/dist/runtime/bounded-context.js.map +1 -1
  36. package/dist/runtime/bridge-codec.d.ts +1 -1
  37. package/dist/runtime/bridge-codec.d.ts.map +1 -1
  38. package/dist/runtime/bridge-codec.js +78 -55
  39. package/dist/runtime/bridge-codec.js.map +1 -1
  40. package/dist/runtime/errors.d.ts +16 -0
  41. package/dist/runtime/errors.d.ts.map +1 -1
  42. package/dist/runtime/errors.js +17 -0
  43. package/dist/runtime/errors.js.map +1 -1
  44. package/dist/runtime/frame-codec.d.ts +111 -0
  45. package/dist/runtime/frame-codec.d.ts.map +1 -0
  46. package/dist/runtime/frame-codec.js +352 -0
  47. package/dist/runtime/frame-codec.js.map +1 -0
  48. package/dist/runtime/http-transport.d.ts +1 -1
  49. package/dist/runtime/http-transport.d.ts.map +1 -1
  50. package/dist/runtime/http-transport.js +1 -1
  51. package/dist/runtime/http-transport.js.map +1 -1
  52. package/dist/runtime/index.d.ts +2 -1
  53. package/dist/runtime/index.d.ts.map +1 -1
  54. package/dist/runtime/index.js +2 -1
  55. package/dist/runtime/index.js.map +1 -1
  56. package/dist/runtime/node.d.ts +7 -23
  57. package/dist/runtime/node.d.ts.map +1 -1
  58. package/dist/runtime/node.js +5 -78
  59. package/dist/runtime/node.js.map +1 -1
  60. package/dist/runtime/pooled-transport.d.ts +120 -59
  61. package/dist/runtime/pooled-transport.d.ts.map +1 -1
  62. package/dist/runtime/pooled-transport.js +345 -78
  63. package/dist/runtime/pooled-transport.js.map +1 -1
  64. package/dist/runtime/pyodide-bootstrap-core.generated.d.ts.map +1 -1
  65. package/dist/runtime/pyodide-bootstrap-core.generated.js +1 -1
  66. package/dist/runtime/pyodide-bootstrap-core.generated.js.map +1 -1
  67. package/dist/runtime/pyodide-transport.d.ts +1 -1
  68. package/dist/runtime/pyodide-transport.d.ts.map +1 -1
  69. package/dist/runtime/pyodide-transport.js +2 -3
  70. package/dist/runtime/pyodide-transport.js.map +1 -1
  71. package/dist/runtime/rpc-client.d.ts +7 -36
  72. package/dist/runtime/rpc-client.d.ts.map +1 -1
  73. package/dist/runtime/rpc-client.js +37 -72
  74. package/dist/runtime/rpc-client.js.map +1 -1
  75. package/dist/runtime/subprocess-transport.d.ts +134 -8
  76. package/dist/runtime/subprocess-transport.d.ts.map +1 -1
  77. package/dist/runtime/subprocess-transport.js +490 -65
  78. package/dist/runtime/subprocess-transport.js.map +1 -1
  79. package/dist/runtime/timed-out-request-tracker.d.ts +2 -1
  80. package/dist/runtime/timed-out-request-tracker.d.ts.map +1 -1
  81. package/dist/runtime/transport.d.ts +90 -18
  82. package/dist/runtime/transport.d.ts.map +1 -1
  83. package/dist/runtime/transport.js +21 -1
  84. package/dist/runtime/transport.js.map +1 -1
  85. package/dist/runtime/validators.d.ts +49 -0
  86. package/dist/runtime/validators.d.ts.map +1 -1
  87. package/dist/runtime/validators.js +152 -0
  88. package/dist/runtime/validators.js.map +1 -1
  89. package/dist/types/index.d.ts +14 -15
  90. package/dist/types/index.d.ts.map +1 -1
  91. package/dist/tywrap.d.ts +3 -2
  92. package/dist/tywrap.d.ts.map +1 -1
  93. package/dist/tywrap.js +140 -21
  94. package/dist/tywrap.js.map +1 -1
  95. package/dist/utils/cache.d.ts +3 -16
  96. package/dist/utils/cache.d.ts.map +1 -1
  97. package/dist/utils/codec.d.ts +1 -0
  98. package/dist/utils/codec.d.ts.map +1 -1
  99. package/dist/utils/codec.js +165 -17
  100. package/dist/utils/codec.js.map +1 -1
  101. package/dist/utils/ir-cache.d.ts +2 -1
  102. package/dist/utils/ir-cache.d.ts.map +1 -1
  103. package/dist/utils/runtime.d.ts +0 -29
  104. package/dist/utils/runtime.d.ts.map +1 -1
  105. package/dist/utils/runtime.js +16 -107
  106. package/dist/utils/runtime.js.map +1 -1
  107. package/dist/version.js +1 -1
  108. package/package.json +7 -6
  109. package/runtime/frame_codec.py +430 -0
  110. package/runtime/python_bridge.py +213 -54
  111. package/runtime/tywrap_bridge_core.py +217 -147
  112. package/src/config/index.ts +11 -0
  113. package/src/core/annotation-parser.ts +9 -5
  114. package/src/core/emit-call.ts +1 -7
  115. package/src/core/generator.ts +315 -205
  116. package/src/core/mapper.ts +8 -8
  117. package/src/dev.ts +1 -3
  118. package/src/index.ts +7 -3
  119. package/src/runtime/base-bridge.ts +5 -30
  120. package/src/runtime/bounded-context.ts +12 -67
  121. package/src/runtime/bridge-codec.ts +94 -65
  122. package/src/runtime/errors.ts +21 -0
  123. package/src/runtime/frame-codec.ts +469 -0
  124. package/src/runtime/http-transport.ts +6 -1
  125. package/src/runtime/index.ts +7 -6
  126. package/src/runtime/node.ts +17 -104
  127. package/src/runtime/pooled-transport.ts +424 -90
  128. package/src/runtime/pyodide-bootstrap-core.generated.ts +1 -1
  129. package/src/runtime/pyodide-transport.ts +7 -3
  130. package/src/runtime/rpc-client.ts +58 -93
  131. package/src/runtime/subprocess-transport.ts +585 -80
  132. package/src/runtime/timed-out-request-tracker.ts +1 -1
  133. package/src/runtime/transport.ts +112 -22
  134. package/src/runtime/validators.ts +204 -0
  135. package/src/types/index.ts +21 -35
  136. package/src/tywrap.ts +157 -30
  137. package/src/utils/cache.ts +3 -3
  138. package/src/utils/codec.ts +205 -16
  139. package/src/utils/ir-cache.ts +1 -1
  140. package/src/utils/runtime.ts +17 -128
  141. package/src/version.ts +1 -1
  142. package/dist/core/discovery.d.ts +0 -103
  143. package/dist/core/discovery.d.ts.map +0 -1
  144. package/dist/core/discovery.js +0 -380
  145. package/dist/core/discovery.js.map +0 -1
  146. package/dist/core/validation.d.ts +0 -102
  147. package/dist/core/validation.d.ts.map +0 -1
  148. package/dist/core/validation.js +0 -490
  149. package/dist/core/validation.js.map +0 -1
  150. package/dist/runtime/base.d.ts +0 -22
  151. package/dist/runtime/base.d.ts.map +0 -1
  152. package/dist/runtime/base.js +0 -23
  153. package/dist/runtime/base.js.map +0 -1
  154. package/dist/runtime/transport-pool.d.ts +0 -196
  155. package/dist/runtime/transport-pool.d.ts.map +0 -1
  156. package/dist/runtime/transport-pool.js +0 -418
  157. package/dist/runtime/transport-pool.js.map +0 -1
  158. package/runtime/__pycache__/_tywrap_member_fixtures.cpython-311.pyc +0 -0
  159. package/runtime/__pycache__/safe_codec.cpython-311.pyc +0 -0
  160. package/runtime/__pycache__/tywrap_bridge_core.cpython-311.pyc +0 -0
  161. package/runtime/safe_codec.py +0 -352
  162. package/src/core/discovery.ts +0 -477
  163. package/src/core/validation.ts +0 -729
  164. package/src/runtime/base.ts +0 -24
  165. package/src/runtime/transport-pool.ts +0 -538
@@ -16,7 +16,6 @@ import { createRequire } from 'node:module';
16
16
  import { autoRegisterArrowDecoder } from '../utils/codec.js';
17
17
  import { getDefaultPythonPath } from '../utils/python.js';
18
18
  import { getVenvBinDir, getVenvPythonExe } from '../utils/runtime.js';
19
- import { globalCache } from '../utils/cache.js';
20
19
 
21
20
  import { BasePythonBridge } from './base-bridge.js';
22
21
  import { RpcClient } from './rpc-client.js';
@@ -24,7 +23,7 @@ import { BridgeCodecError, BridgeExecutionError, BridgeProtocolError } from './e
24
23
  import { SubprocessTransport } from './subprocess-transport.js';
25
24
  import { PooledTransport } from './pooled-transport.js';
26
25
  import type { CodecOptions } from './bridge-codec.js';
27
- import type { TransportLease } from './transport-pool.js';
26
+ import type { TransportLease } from './pooled-transport.js';
28
27
 
29
28
  // =============================================================================
30
29
  // OPTIONS
@@ -40,7 +39,12 @@ export interface NodeBridgeOptions {
40
39
  /** Maximum number of Python processes to spawn. Default: 1 (single-process mode) */
41
40
  maxProcesses?: number;
42
41
 
43
- /** Maximum concurrent requests per process. Default: 10 */
42
+ /**
43
+ * Maximum concurrent requests per process. Default: 1.
44
+ *
45
+ * Python bridge workers process JSONL requests serially, so the default lets
46
+ * the pool use another process for concurrent calls when one is available.
47
+ */
44
48
  maxConcurrentPerProcess?: number;
45
49
 
46
50
  /** Path to Python executable. Auto-detected if not specified. */
@@ -64,9 +68,6 @@ export interface NodeBridgeOptions {
64
68
  /** Inherit all environment variables from parent process. Default: false */
65
69
  inheritProcessEnv?: boolean;
66
70
 
67
- /** Enable result caching for pure functions. Default: false */
68
- enableCache?: boolean;
69
-
70
71
  /** Optional extra environment variables to pass to the Python subprocess. */
71
72
  env?: Record<string, string | undefined>;
72
73
 
@@ -118,7 +119,6 @@ interface ResolvedOptions {
118
119
  timeoutMs: number;
119
120
  queueTimeoutMs: number;
120
121
  inheritProcessEnv: boolean;
121
- enableCache: boolean;
122
122
  env: Record<string, string | undefined>;
123
123
  codec?: CodecOptions;
124
124
  warmupCommands: WarmupCommand[];
@@ -278,7 +278,6 @@ function normalizeWarmupCommands(commands: NodeBridgeOptions['warmupCommands']):
278
278
  * - Virtual environment support
279
279
  * - Full BridgeCodec validation (NaN/Infinity rejection, key validation)
280
280
  * - Automatic Arrow decoding for DataFrames/ndarrays
281
- * - Optional result caching for pure functions
282
281
  * - Process warmup commands
283
282
  *
284
283
  * @example
@@ -299,7 +298,6 @@ function normalizeWarmupCommands(commands: NodeBridgeOptions['warmupCommands']):
299
298
  * const pooledBridge = new NodeBridge({
300
299
  * maxProcesses: 4,
301
300
  * maxConcurrentPerProcess: 2,
302
- * enableCache: true,
303
301
  * });
304
302
  * await pooledBridge.init();
305
303
  * ```
@@ -329,7 +327,7 @@ export class NodeBridge extends BasePythonBridge {
329
327
  const resolvedOptions: ResolvedOptions = {
330
328
  minProcesses,
331
329
  maxProcesses,
332
- maxConcurrentPerProcess: options.maxConcurrentPerProcess ?? 10,
330
+ maxConcurrentPerProcess: options.maxConcurrentPerProcess ?? 1,
333
331
  pythonPath: options.pythonPath ?? venv?.pythonPath ?? getDefaultPythonPath(),
334
332
  scriptPath: resolvedScriptPath,
335
333
  virtualEnv,
@@ -337,7 +335,6 @@ export class NodeBridge extends BasePythonBridge {
337
335
  timeoutMs: options.timeoutMs ?? 30000,
338
336
  queueTimeoutMs: options.queueTimeoutMs ?? 30000,
339
337
  inheritProcessEnv: options.inheritProcessEnv ?? false,
340
- enableCache: options.enableCache ?? false,
341
338
  env: options.env ?? {},
342
339
  codec: options.codec,
343
340
  warmupCommands,
@@ -367,6 +364,9 @@ export class NodeBridge extends BasePythonBridge {
367
364
  bridgeScript: resolvedOptions.scriptPath,
368
365
  env: processEnv,
369
366
  cwd: resolvedOptions.cwd,
367
+ // Bound chunked-response reassembly to the codec's logical payload cap
368
+ // so a huge response fails loud early instead of buffering to OOM.
369
+ maxReassemblyBytes: resolvedOptions.codec?.maxPayloadBytes,
370
370
  }),
371
371
  maxWorkers: resolvedOptions.maxProcesses,
372
372
  minWorkers: resolvedOptions.minProcesses,
@@ -437,57 +437,12 @@ export class NodeBridge extends BasePythonBridge {
437
437
 
438
438
  /**
439
439
  * Expose the held RpcClient to BasePythonBridge's shared delegating methods
440
- * (instantiate/callMethod/disposeInstance/getBridgeInfo). call() is
441
- * overridden below to layer caching on top.
440
+ * (call/getBridgeInfo).
442
441
  */
443
442
  protected getRpcClient(): RpcClient {
444
443
  return this.rpc;
445
444
  }
446
445
 
447
- /**
448
- * Call a Python function, with optional result caching.
449
- *
450
- * Overrides BasePythonBridge.call() to layer the cache lookup/writeback on
451
- * top of the shared delegation. Cache lookup stays FIRST so cache hits return
452
- * without forcing init, preserving the pre-composition behavior.
453
- */
454
- override async call<T = unknown>(
455
- module: string,
456
- functionName: string,
457
- args: unknown[],
458
- kwargs?: Record<string, unknown>
459
- ): Promise<T> {
460
- // Check cache if enabled
461
- if (this.resolvedOptions.enableCache) {
462
- const cacheKey = this.safeCacheKey('runtime_call', module, functionName, args, kwargs);
463
- if (cacheKey) {
464
- const cached = await globalCache.get<T>(cacheKey);
465
- if (cached !== null) {
466
- return cached;
467
- }
468
- }
469
-
470
- // Execute and cache if pure function
471
- const startTime = performance.now();
472
- await this.ensureReady();
473
- const result = await this.rpc.call<T>(module, functionName, args, kwargs);
474
- const duration = performance.now() - startTime;
475
-
476
- if (cacheKey && this.isPureFunctionCandidate(functionName, args)) {
477
- await globalCache.set(cacheKey, result, {
478
- computeTime: duration,
479
- dependencies: [module],
480
- });
481
- }
482
-
483
- return result;
484
- }
485
-
486
- // No caching - direct call
487
- await this.ensureReady();
488
- return this.rpc.call<T>(module, functionName, args, kwargs);
489
- }
490
-
491
446
  // ===========================================================================
492
447
  // POOL STATISTICS
493
448
  // ===========================================================================
@@ -541,52 +496,6 @@ export class NodeBridge extends BasePythonBridge {
541
496
  busyWorkers: poolStats.totalInFlight,
542
497
  };
543
498
  }
544
-
545
- // ===========================================================================
546
- // PRIVATE HELPERS
547
- // ===========================================================================
548
-
549
- /**
550
- * Generate a cache key, returning null if generation fails.
551
- */
552
- private safeCacheKey(prefix: string, ...inputs: unknown[]): string | null {
553
- try {
554
- return globalCache.generateKey(prefix, ...inputs);
555
- } catch {
556
- return null;
557
- }
558
- }
559
-
560
- /**
561
- * Heuristic to determine if function result should be cached.
562
- */
563
- private isPureFunctionCandidate(functionName: string, args: unknown[]): boolean {
564
- const pureFunctionPatterns = [
565
- /^(get|fetch|read|load|find|search|query|select)_/i,
566
- /^(compute|calculate|process|transform|convert)_/i,
567
- /^(encode|decode|serialize|deserialize)_/i,
568
- ];
569
-
570
- const impureFunctionPatterns = [
571
- /^(set|save|write|update|insert|delete|create|modify)_/i,
572
- /^(send|post|put|patch)_/i,
573
- /random|uuid|timestamp|now|current/i,
574
- ];
575
-
576
- if (impureFunctionPatterns.some(pattern => pattern.test(functionName))) {
577
- return false;
578
- }
579
-
580
- if (pureFunctionPatterns.some(pattern => pattern.test(functionName))) {
581
- return true;
582
- }
583
-
584
- const hasComplexArgs = args.some(
585
- arg => arg !== null && typeof arg === 'object' && !(arg instanceof Date)
586
- );
587
-
588
- return !hasComplexArgs && args.length <= 3;
589
- }
590
499
  }
591
500
 
592
501
  // =============================================================================
@@ -652,7 +561,11 @@ function createWorkerReadyCallback(
652
561
 
653
562
  // Readiness probe (mirrors getBridgeInfo's meta request, per-worker).
654
563
  try {
655
- await rpc.sendOn(worker.transport, { method: 'meta', params: {} }, { timeoutMs: readyTimeoutMs });
564
+ await rpc.sendOn(
565
+ worker.transport,
566
+ { method: 'meta', params: {} },
567
+ { timeoutMs: readyTimeoutMs }
568
+ );
656
569
  } catch (error) {
657
570
  throw wrapWarmupError('Worker warmup check', error);
658
571
  }