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.
- package/README.md +36 -11
- package/SECURITY.md +39 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +8 -0
- package/dist/config/index.js.map +1 -1
- package/dist/core/annotation-parser.d.ts +2 -1
- package/dist/core/annotation-parser.d.ts.map +1 -1
- package/dist/core/annotation-parser.js +6 -3
- package/dist/core/annotation-parser.js.map +1 -1
- package/dist/core/emit-call.d.ts.map +1 -1
- package/dist/core/emit-call.js +1 -1
- package/dist/core/emit-call.js.map +1 -1
- package/dist/core/generator.d.ts +23 -4
- package/dist/core/generator.d.ts.map +1 -1
- package/dist/core/generator.js +243 -170
- package/dist/core/generator.js.map +1 -1
- package/dist/core/mapper.d.ts +3 -2
- package/dist/core/mapper.d.ts.map +1 -1
- package/dist/core/mapper.js +5 -5
- package/dist/core/mapper.js.map +1 -1
- package/dist/dev.d.ts.map +1 -1
- package/dist/dev.js +1 -3
- package/dist/dev.js.map +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/runtime/base-bridge.d.ts +3 -7
- package/dist/runtime/base-bridge.d.ts.map +1 -1
- package/dist/runtime/base-bridge.js +4 -17
- package/dist/runtime/base-bridge.js.map +1 -1
- package/dist/runtime/bounded-context.d.ts +3 -22
- package/dist/runtime/bounded-context.d.ts.map +1 -1
- package/dist/runtime/bounded-context.js +13 -53
- package/dist/runtime/bounded-context.js.map +1 -1
- package/dist/runtime/bridge-codec.d.ts +1 -1
- package/dist/runtime/bridge-codec.d.ts.map +1 -1
- package/dist/runtime/bridge-codec.js +78 -55
- package/dist/runtime/bridge-codec.js.map +1 -1
- package/dist/runtime/errors.d.ts +16 -0
- package/dist/runtime/errors.d.ts.map +1 -1
- package/dist/runtime/errors.js +17 -0
- package/dist/runtime/errors.js.map +1 -1
- package/dist/runtime/frame-codec.d.ts +111 -0
- package/dist/runtime/frame-codec.d.ts.map +1 -0
- package/dist/runtime/frame-codec.js +352 -0
- package/dist/runtime/frame-codec.js.map +1 -0
- package/dist/runtime/http-transport.d.ts +1 -1
- package/dist/runtime/http-transport.d.ts.map +1 -1
- package/dist/runtime/http-transport.js +1 -1
- package/dist/runtime/http-transport.js.map +1 -1
- package/dist/runtime/index.d.ts +2 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +2 -1
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/node.d.ts +7 -23
- package/dist/runtime/node.d.ts.map +1 -1
- package/dist/runtime/node.js +5 -78
- package/dist/runtime/node.js.map +1 -1
- package/dist/runtime/pooled-transport.d.ts +120 -59
- package/dist/runtime/pooled-transport.d.ts.map +1 -1
- package/dist/runtime/pooled-transport.js +345 -78
- package/dist/runtime/pooled-transport.js.map +1 -1
- package/dist/runtime/pyodide-bootstrap-core.generated.d.ts.map +1 -1
- package/dist/runtime/pyodide-bootstrap-core.generated.js +1 -1
- package/dist/runtime/pyodide-bootstrap-core.generated.js.map +1 -1
- package/dist/runtime/pyodide-transport.d.ts +1 -1
- package/dist/runtime/pyodide-transport.d.ts.map +1 -1
- package/dist/runtime/pyodide-transport.js +2 -3
- package/dist/runtime/pyodide-transport.js.map +1 -1
- package/dist/runtime/rpc-client.d.ts +7 -36
- package/dist/runtime/rpc-client.d.ts.map +1 -1
- package/dist/runtime/rpc-client.js +37 -72
- package/dist/runtime/rpc-client.js.map +1 -1
- package/dist/runtime/subprocess-transport.d.ts +134 -8
- package/dist/runtime/subprocess-transport.d.ts.map +1 -1
- package/dist/runtime/subprocess-transport.js +490 -65
- package/dist/runtime/subprocess-transport.js.map +1 -1
- package/dist/runtime/timed-out-request-tracker.d.ts +2 -1
- package/dist/runtime/timed-out-request-tracker.d.ts.map +1 -1
- package/dist/runtime/transport.d.ts +90 -18
- package/dist/runtime/transport.d.ts.map +1 -1
- package/dist/runtime/transport.js +21 -1
- package/dist/runtime/transport.js.map +1 -1
- package/dist/runtime/validators.d.ts +49 -0
- package/dist/runtime/validators.d.ts.map +1 -1
- package/dist/runtime/validators.js +152 -0
- package/dist/runtime/validators.js.map +1 -1
- package/dist/types/index.d.ts +14 -15
- package/dist/types/index.d.ts.map +1 -1
- package/dist/tywrap.d.ts +3 -2
- package/dist/tywrap.d.ts.map +1 -1
- package/dist/tywrap.js +140 -21
- package/dist/tywrap.js.map +1 -1
- package/dist/utils/cache.d.ts +3 -16
- package/dist/utils/cache.d.ts.map +1 -1
- package/dist/utils/codec.d.ts +1 -0
- package/dist/utils/codec.d.ts.map +1 -1
- package/dist/utils/codec.js +165 -17
- package/dist/utils/codec.js.map +1 -1
- package/dist/utils/ir-cache.d.ts +2 -1
- package/dist/utils/ir-cache.d.ts.map +1 -1
- package/dist/utils/runtime.d.ts +0 -29
- package/dist/utils/runtime.d.ts.map +1 -1
- package/dist/utils/runtime.js +16 -107
- package/dist/utils/runtime.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +7 -6
- package/runtime/frame_codec.py +430 -0
- package/runtime/python_bridge.py +213 -54
- package/runtime/tywrap_bridge_core.py +217 -147
- package/src/config/index.ts +11 -0
- package/src/core/annotation-parser.ts +9 -5
- package/src/core/emit-call.ts +1 -7
- package/src/core/generator.ts +315 -205
- package/src/core/mapper.ts +8 -8
- package/src/dev.ts +1 -3
- package/src/index.ts +7 -3
- package/src/runtime/base-bridge.ts +5 -30
- package/src/runtime/bounded-context.ts +12 -67
- package/src/runtime/bridge-codec.ts +94 -65
- package/src/runtime/errors.ts +21 -0
- package/src/runtime/frame-codec.ts +469 -0
- package/src/runtime/http-transport.ts +6 -1
- package/src/runtime/index.ts +7 -6
- package/src/runtime/node.ts +17 -104
- package/src/runtime/pooled-transport.ts +424 -90
- package/src/runtime/pyodide-bootstrap-core.generated.ts +1 -1
- package/src/runtime/pyodide-transport.ts +7 -3
- package/src/runtime/rpc-client.ts +58 -93
- package/src/runtime/subprocess-transport.ts +585 -80
- package/src/runtime/timed-out-request-tracker.ts +1 -1
- package/src/runtime/transport.ts +112 -22
- package/src/runtime/validators.ts +204 -0
- package/src/types/index.ts +21 -35
- package/src/tywrap.ts +157 -30
- package/src/utils/cache.ts +3 -3
- package/src/utils/codec.ts +205 -16
- package/src/utils/ir-cache.ts +1 -1
- package/src/utils/runtime.ts +17 -128
- package/src/version.ts +1 -1
- package/dist/core/discovery.d.ts +0 -103
- package/dist/core/discovery.d.ts.map +0 -1
- package/dist/core/discovery.js +0 -380
- package/dist/core/discovery.js.map +0 -1
- package/dist/core/validation.d.ts +0 -102
- package/dist/core/validation.d.ts.map +0 -1
- package/dist/core/validation.js +0 -490
- package/dist/core/validation.js.map +0 -1
- package/dist/runtime/base.d.ts +0 -22
- package/dist/runtime/base.d.ts.map +0 -1
- package/dist/runtime/base.js +0 -23
- package/dist/runtime/base.js.map +0 -1
- package/dist/runtime/transport-pool.d.ts +0 -196
- package/dist/runtime/transport-pool.d.ts.map +0 -1
- package/dist/runtime/transport-pool.js +0 -418
- package/dist/runtime/transport-pool.js.map +0 -1
- package/runtime/__pycache__/_tywrap_member_fixtures.cpython-311.pyc +0 -0
- package/runtime/__pycache__/safe_codec.cpython-311.pyc +0 -0
- package/runtime/__pycache__/tywrap_bridge_core.cpython-311.pyc +0 -0
- package/runtime/safe_codec.py +0 -352
- package/src/core/discovery.ts +0 -477
- package/src/core/validation.ts +0 -729
- package/src/runtime/base.ts +0 -24
- package/src/runtime/transport-pool.ts +0 -538
package/src/runtime/node.ts
CHANGED
|
@@ -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
|
|
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
|
-
/**
|
|
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 ??
|
|
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
|
-
* (
|
|
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(
|
|
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
|
}
|