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
@@ -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,29 +68,12 @@ 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
 
73
74
  /** Codec options for validation/serialization */
74
75
  codec?: CodecOptions;
75
76
 
76
- /**
77
- * Negotiate the chunked transport (`tywrap-frame/1`) so a large result that
78
- * would exceed a single JSONL line is split into frames and transparently
79
- * reassembled. Default: `true`.
80
- *
81
- * Negotiation degrades safely: small payloads are unaffected, and a bridge
82
- * that does not advertise chunking still fails loud on an oversize payload
83
- * (never a silent single-frame fallback). Chunking only engages above the
84
- * frame ceiling, so raising the codec payload cap (`codec`) is what unlocks
85
- * genuinely large results — flipping this alone changes nothing for typical
86
- * small-payload traffic.
87
- */
88
- enableChunking?: boolean;
89
-
90
77
  /** Commands to run on each process at startup for warming up. */
91
78
  warmupCommands?: Array<
92
79
  { module: string; functionName: string; args?: unknown[] } | { method: string; params: unknown } // Legacy shape preserved so runtime can surface a migration error
@@ -132,8 +119,6 @@ interface ResolvedOptions {
132
119
  timeoutMs: number;
133
120
  queueTimeoutMs: number;
134
121
  inheritProcessEnv: boolean;
135
- enableCache: boolean;
136
- enableChunking: boolean;
137
122
  env: Record<string, string | undefined>;
138
123
  codec?: CodecOptions;
139
124
  warmupCommands: WarmupCommand[];
@@ -293,7 +278,6 @@ function normalizeWarmupCommands(commands: NodeBridgeOptions['warmupCommands']):
293
278
  * - Virtual environment support
294
279
  * - Full BridgeCodec validation (NaN/Infinity rejection, key validation)
295
280
  * - Automatic Arrow decoding for DataFrames/ndarrays
296
- * - Optional result caching for pure functions
297
281
  * - Process warmup commands
298
282
  *
299
283
  * @example
@@ -314,7 +298,6 @@ function normalizeWarmupCommands(commands: NodeBridgeOptions['warmupCommands']):
314
298
  * const pooledBridge = new NodeBridge({
315
299
  * maxProcesses: 4,
316
300
  * maxConcurrentPerProcess: 2,
317
- * enableCache: true,
318
301
  * });
319
302
  * await pooledBridge.init();
320
303
  * ```
@@ -344,7 +327,7 @@ export class NodeBridge extends BasePythonBridge {
344
327
  const resolvedOptions: ResolvedOptions = {
345
328
  minProcesses,
346
329
  maxProcesses,
347
- maxConcurrentPerProcess: options.maxConcurrentPerProcess ?? 10,
330
+ maxConcurrentPerProcess: options.maxConcurrentPerProcess ?? 1,
348
331
  pythonPath: options.pythonPath ?? venv?.pythonPath ?? getDefaultPythonPath(),
349
332
  scriptPath: resolvedScriptPath,
350
333
  virtualEnv,
@@ -352,8 +335,6 @@ export class NodeBridge extends BasePythonBridge {
352
335
  timeoutMs: options.timeoutMs ?? 30000,
353
336
  queueTimeoutMs: options.queueTimeoutMs ?? 30000,
354
337
  inheritProcessEnv: options.inheritProcessEnv ?? false,
355
- enableCache: options.enableCache ?? false,
356
- enableChunking: options.enableChunking ?? true,
357
338
  env: options.env ?? {},
358
339
  codec: options.codec,
359
340
  warmupCommands,
@@ -383,7 +364,6 @@ export class NodeBridge extends BasePythonBridge {
383
364
  bridgeScript: resolvedOptions.scriptPath,
384
365
  env: processEnv,
385
366
  cwd: resolvedOptions.cwd,
386
- enableChunking: resolvedOptions.enableChunking,
387
367
  // Bound chunked-response reassembly to the codec's logical payload cap
388
368
  // so a huge response fails loud early instead of buffering to OOM.
389
369
  maxReassemblyBytes: resolvedOptions.codec?.maxPayloadBytes,
@@ -457,57 +437,12 @@ export class NodeBridge extends BasePythonBridge {
457
437
 
458
438
  /**
459
439
  * Expose the held RpcClient to BasePythonBridge's shared delegating methods
460
- * (instantiate/callMethod/disposeInstance/getBridgeInfo). call() is
461
- * overridden below to layer caching on top.
440
+ * (call/getBridgeInfo).
462
441
  */
463
442
  protected getRpcClient(): RpcClient {
464
443
  return this.rpc;
465
444
  }
466
445
 
467
- /**
468
- * Call a Python function, with optional result caching.
469
- *
470
- * Overrides BasePythonBridge.call() to layer the cache lookup/writeback on
471
- * top of the shared delegation. Cache lookup stays FIRST so cache hits return
472
- * without forcing init, preserving the pre-composition behavior.
473
- */
474
- override async call<T = unknown>(
475
- module: string,
476
- functionName: string,
477
- args: unknown[],
478
- kwargs?: Record<string, unknown>
479
- ): Promise<T> {
480
- // Check cache if enabled
481
- if (this.resolvedOptions.enableCache) {
482
- const cacheKey = this.safeCacheKey('runtime_call', module, functionName, args, kwargs);
483
- if (cacheKey) {
484
- const cached = await globalCache.get<T>(cacheKey);
485
- if (cached !== null) {
486
- return cached;
487
- }
488
- }
489
-
490
- // Execute and cache if pure function
491
- const startTime = performance.now();
492
- await this.ensureReady();
493
- const result = await this.rpc.call<T>(module, functionName, args, kwargs);
494
- const duration = performance.now() - startTime;
495
-
496
- if (cacheKey && this.isPureFunctionCandidate(functionName, args)) {
497
- await globalCache.set(cacheKey, result, {
498
- computeTime: duration,
499
- dependencies: [module],
500
- });
501
- }
502
-
503
- return result;
504
- }
505
-
506
- // No caching - direct call
507
- await this.ensureReady();
508
- return this.rpc.call<T>(module, functionName, args, kwargs);
509
- }
510
-
511
446
  // ===========================================================================
512
447
  // POOL STATISTICS
513
448
  // ===========================================================================
@@ -561,52 +496,6 @@ export class NodeBridge extends BasePythonBridge {
561
496
  busyWorkers: poolStats.totalInFlight,
562
497
  };
563
498
  }
564
-
565
- // ===========================================================================
566
- // PRIVATE HELPERS
567
- // ===========================================================================
568
-
569
- /**
570
- * Generate a cache key, returning null if generation fails.
571
- */
572
- private safeCacheKey(prefix: string, ...inputs: unknown[]): string | null {
573
- try {
574
- return globalCache.generateKey(prefix, ...inputs);
575
- } catch {
576
- return null;
577
- }
578
- }
579
-
580
- /**
581
- * Heuristic to determine if function result should be cached.
582
- */
583
- private isPureFunctionCandidate(functionName: string, args: unknown[]): boolean {
584
- const pureFunctionPatterns = [
585
- /^(get|fetch|read|load|find|search|query|select)_/i,
586
- /^(compute|calculate|process|transform|convert)_/i,
587
- /^(encode|decode|serialize|deserialize)_/i,
588
- ];
589
-
590
- const impureFunctionPatterns = [
591
- /^(set|save|write|update|insert|delete|create|modify)_/i,
592
- /^(send|post|put|patch)_/i,
593
- /random|uuid|timestamp|now|current/i,
594
- ];
595
-
596
- if (impureFunctionPatterns.some(pattern => pattern.test(functionName))) {
597
- return false;
598
- }
599
-
600
- if (pureFunctionPatterns.some(pattern => pattern.test(functionName))) {
601
- return true;
602
- }
603
-
604
- const hasComplexArgs = args.some(
605
- arg => arg !== null && typeof arg === 'object' && !(arg instanceof Date)
606
- );
607
-
608
- return !hasComplexArgs && args.length <= 3;
609
- }
610
499
  }
611
500
 
612
501
  // =============================================================================