tywrap 0.8.0 → 0.10.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 +42 -22
- package/SECURITY.md +38 -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/generator.d.ts +23 -4
- package/dist/core/generator.d.ts.map +1 -1
- package/dist/core/generator.js +254 -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/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 +3 -3
- package/dist/runtime/bridge-codec.d.ts.map +1 -1
- package/dist/runtime/bridge-codec.js +129 -62
- 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/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 +1 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/node.d.ts +7 -36
- package/dist/runtime/node.d.ts.map +1 -1
- package/dist/runtime/node.js +2 -80
- 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 +20 -102
- package/dist/runtime/rpc-client.js.map +1 -1
- package/dist/runtime/subprocess-transport.d.ts +30 -69
- package/dist/runtime/subprocess-transport.d.ts.map +1 -1
- package/dist/runtime/subprocess-transport.js +179 -236
- 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 +9 -19
- package/dist/runtime/transport.d.ts.map +1 -1
- package/dist/runtime/transport.js +1 -1
- package/dist/runtime/transport.js.map +1 -1
- package/dist/runtime/validators.d.ts +50 -0
- package/dist/runtime/validators.d.ts.map +1 -1
- package/dist/runtime/validators.js +154 -0
- package/dist/runtime/validators.js.map +1 -1
- package/dist/types/index.d.ts +14 -39
- 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 -13
- 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 +17 -0
- package/dist/utils/codec.d.ts.map +1 -1
- package/dist/utils/codec.js +500 -85
- 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.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +7 -6
- package/runtime/frame_codec.py +7 -1
- package/runtime/python_bridge.py +85 -125
- package/runtime/tywrap_bridge_core.py +515 -187
- package/src/config/index.ts +11 -0
- package/src/core/annotation-parser.ts +7 -4
- package/src/core/generator.ts +330 -205
- package/src/core/mapper.ts +8 -8
- package/src/index.ts +7 -4
- package/src/runtime/base-bridge.ts +5 -30
- package/src/runtime/bounded-context.ts +12 -67
- package/src/runtime/bridge-codec.ts +158 -74
- package/src/runtime/errors.ts +21 -0
- package/src/runtime/http-transport.ts +6 -1
- package/src/runtime/index.ts +6 -0
- package/src/runtime/node.ts +9 -120
- 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 +37 -139
- package/src/runtime/subprocess-transport.ts +210 -285
- package/src/runtime/timed-out-request-tracker.ts +1 -1
- package/src/runtime/transport.ts +15 -23
- package/src/runtime/validators.ts +212 -0
- package/src/types/index.ts +21 -60
- package/src/tywrap.ts +157 -22
- package/src/utils/cache.ts +3 -3
- package/src/utils/codec.ts +749 -112
- 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_conformance_chunking_fixtures.cpython-311.pyc +0 -0
- package/runtime/__pycache__/_tywrap_member_fixtures.cpython-311.pyc +0 -0
- package/runtime/__pycache__/_tywrap_w4_chunking_fixture.cpython-311.pyc +0 -0
- package/runtime/__pycache__/_tywrap_w5_request_chunking_fixture.cpython-311.pyc +0 -0
- package/runtime/__pycache__/_tywrap_w6_pool_chunking_fixture.cpython-311.pyc +0 -0
- package/runtime/__pycache__/frame_codec.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,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 ??
|
|
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
|
-
* (
|
|
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
|
// =============================================================================
|