tywrap 0.2.1 → 0.4.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 +100 -32
- package/dist/cli.js +70 -5
- package/dist/cli.js.map +1 -1
- package/dist/config/index.d.ts +11 -7
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +62 -20
- package/dist/config/index.js.map +1 -1
- package/dist/core/analyzer.d.ts +1 -0
- package/dist/core/analyzer.d.ts.map +1 -1
- package/dist/core/analyzer.js +46 -12
- package/dist/core/analyzer.js.map +1 -1
- package/dist/core/annotation-parser.d.ts +8 -0
- package/dist/core/annotation-parser.d.ts.map +1 -0
- package/dist/core/annotation-parser.js +409 -0
- package/dist/core/annotation-parser.js.map +1 -0
- package/dist/core/discovery.d.ts +1 -0
- package/dist/core/discovery.d.ts.map +1 -1
- package/dist/core/discovery.js +9 -9
- package/dist/core/discovery.js.map +1 -1
- package/dist/core/generator.d.ts +9 -1
- package/dist/core/generator.d.ts.map +1 -1
- package/dist/core/generator.js +738 -104
- package/dist/core/generator.js.map +1 -1
- package/dist/core/mapper.d.ts +9 -7
- package/dist/core/mapper.d.ts.map +1 -1
- package/dist/core/mapper.js +129 -44
- package/dist/core/mapper.js.map +1 -1
- package/dist/dev.d.ts +57 -0
- package/dist/dev.d.ts.map +1 -0
- package/dist/dev.js +743 -0
- package/dist/dev.js.map +1 -0
- package/dist/index.d.ts +8 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/runtime/bridge-core.d.ts +1 -0
- package/dist/runtime/bridge-core.d.ts.map +1 -1
- package/dist/runtime/bridge-core.js +52 -17
- package/dist/runtime/bridge-core.js.map +1 -1
- package/dist/runtime/bridge-protocol.d.ts +17 -0
- package/dist/runtime/bridge-protocol.d.ts.map +1 -1
- package/dist/runtime/bridge-protocol.js +97 -0
- package/dist/runtime/bridge-protocol.js.map +1 -1
- package/dist/runtime/errors.d.ts +10 -0
- package/dist/runtime/errors.d.ts.map +1 -1
- package/dist/runtime/errors.js +9 -0
- package/dist/runtime/errors.js.map +1 -1
- package/dist/runtime/http-io.d.ts.map +1 -1
- package/dist/runtime/http-io.js.map +1 -1
- package/dist/runtime/http.d.ts +1 -0
- package/dist/runtime/http.d.ts.map +1 -1
- package/dist/runtime/http.js +6 -0
- package/dist/runtime/http.js.map +1 -1
- package/dist/runtime/node.d.ts.map +1 -1
- package/dist/runtime/node.js +206 -32
- package/dist/runtime/node.js.map +1 -1
- package/dist/runtime/optimized-node.d.ts +6 -5
- package/dist/runtime/optimized-node.d.ts.map +1 -1
- package/dist/runtime/optimized-node.js +6 -5
- package/dist/runtime/optimized-node.js.map +1 -1
- package/dist/runtime/pooled-transport.d.ts +4 -0
- package/dist/runtime/pooled-transport.d.ts.map +1 -1
- package/dist/runtime/pooled-transport.js +2 -0
- package/dist/runtime/pooled-transport.js.map +1 -1
- package/dist/runtime/process-io.d.ts +5 -0
- package/dist/runtime/process-io.d.ts.map +1 -1
- package/dist/runtime/process-io.js +64 -30
- package/dist/runtime/process-io.js.map +1 -1
- package/dist/runtime/pyodide-io.d.ts +4 -5
- package/dist/runtime/pyodide-io.d.ts.map +1 -1
- package/dist/runtime/pyodide-io.js +99 -37
- package/dist/runtime/pyodide-io.js.map +1 -1
- package/dist/runtime/safe-codec.d.ts +25 -3
- package/dist/runtime/safe-codec.d.ts.map +1 -1
- package/dist/runtime/safe-codec.js +231 -57
- package/dist/runtime/safe-codec.js.map +1 -1
- package/dist/runtime/transport.d.ts +6 -1
- package/dist/runtime/transport.d.ts.map +1 -1
- package/dist/runtime/transport.js.map +1 -1
- package/dist/runtime/validators.d.ts +0 -11
- package/dist/runtime/validators.d.ts.map +1 -1
- package/dist/runtime/validators.js +13 -3
- package/dist/runtime/validators.js.map +1 -1
- package/dist/runtime/worker-pool.d.ts +17 -0
- package/dist/runtime/worker-pool.d.ts.map +1 -1
- package/dist/runtime/worker-pool.js +97 -22
- package/dist/runtime/worker-pool.js.map +1 -1
- package/dist/types/index.d.ts +51 -8
- package/dist/types/index.d.ts.map +1 -1
- package/dist/tywrap.d.ts +6 -0
- package/dist/tywrap.d.ts.map +1 -1
- package/dist/tywrap.js +237 -331
- package/dist/tywrap.js.map +1 -1
- package/dist/utils/cache.d.ts.map +1 -1
- package/dist/utils/cache.js +53 -5
- package/dist/utils/cache.js.map +1 -1
- package/dist/utils/codec.d.ts.map +1 -1
- package/dist/utils/codec.js +10 -16
- package/dist/utils/codec.js.map +1 -1
- package/dist/utils/ir-cache.d.ts +28 -0
- package/dist/utils/ir-cache.d.ts.map +1 -0
- package/dist/utils/ir-cache.js +29 -0
- package/dist/utils/ir-cache.js.map +1 -0
- package/dist/utils/parallel-processor.d.ts +1 -1
- package/dist/utils/parallel-processor.d.ts.map +1 -1
- package/dist/utils/parallel-processor.js +111 -40
- package/dist/utils/parallel-processor.js.map +1 -1
- package/dist/utils/runtime.d.ts +5 -1
- package/dist/utils/runtime.d.ts.map +1 -1
- package/dist/utils/runtime.js +112 -18
- package/dist/utils/runtime.js.map +1 -1
- package/package.json +47 -21
- package/runtime/python_bridge.py +78 -12
- package/src/cli.ts +85 -5
- package/src/config/index.ts +91 -32
- package/src/core/analyzer.ts +51 -15
- package/src/core/annotation-parser.ts +500 -0
- package/src/core/discovery.ts +26 -12
- package/src/core/generator.ts +956 -116
- package/src/core/mapper.ts +158 -51
- package/src/dev.ts +983 -0
- package/src/index.ts +35 -6
- package/src/runtime/bridge-core.ts +58 -18
- package/src/runtime/bridge-protocol.ts +164 -0
- package/src/runtime/errors.ts +14 -0
- package/src/runtime/http-io.ts +2 -1
- package/src/runtime/http.ts +7 -0
- package/src/runtime/node.ts +312 -46
- package/src/runtime/optimized-node.ts +9 -5
- package/src/runtime/pooled-transport.ts +12 -1
- package/src/runtime/process-io.ts +83 -55
- package/src/runtime/pyodide-io.ts +110 -44
- package/src/runtime/safe-codec.ts +290 -82
- package/src/runtime/transport.ts +6 -1
- package/src/runtime/validators.ts +14 -3
- package/src/runtime/worker-pool.ts +120 -24
- package/src/types/global.d.ts +11 -1
- package/src/types/index.ts +63 -8
- package/src/tywrap.ts +296 -361
- package/src/utils/cache.ts +59 -5
- package/src/utils/codec.ts +11 -15
- package/src/utils/ir-cache.ts +51 -0
- package/src/utils/parallel-processor.ts +119 -43
- package/src/utils/runtime.ts +129 -18
package/src/index.ts
CHANGED
|
@@ -7,17 +7,31 @@
|
|
|
7
7
|
|
|
8
8
|
import { tywrap } from './tywrap.js';
|
|
9
9
|
|
|
10
|
-
export type { TywrapConfig } from './config/index.js';
|
|
10
|
+
export type { TywrapConfig, ResolvedTywrapConfig } from './config/index.js';
|
|
11
11
|
export { defineConfig, resolveConfig } from './config/index.js';
|
|
12
12
|
// BoundedContext - unified abstraction for cross-boundary concerns
|
|
13
|
-
export {
|
|
13
|
+
export {
|
|
14
|
+
BoundedContext,
|
|
15
|
+
type ContextState,
|
|
16
|
+
type ExecuteOptions,
|
|
17
|
+
} from './runtime/bounded-context.js';
|
|
14
18
|
// BridgeProtocol - unified BoundedContext + SafeCodec + Transport
|
|
15
19
|
export { BridgeProtocol, type BridgeProtocolOptions } from './runtime/bridge-protocol.js';
|
|
16
20
|
// SafeCodec - validation and serialization for JS<->Python boundary
|
|
17
21
|
export { SafeCodec, type CodecOptions } from './runtime/safe-codec.js';
|
|
18
22
|
// Transport - abstract I/O channel interface
|
|
19
|
-
export type {
|
|
20
|
-
|
|
23
|
+
export type {
|
|
24
|
+
Transport,
|
|
25
|
+
TransportOptions,
|
|
26
|
+
ProtocolMessage,
|
|
27
|
+
ProtocolResponse,
|
|
28
|
+
} from './runtime/transport.js';
|
|
29
|
+
export {
|
|
30
|
+
PROTOCOL_ID,
|
|
31
|
+
isTransport,
|
|
32
|
+
isProtocolMessage,
|
|
33
|
+
isProtocolResponse,
|
|
34
|
+
} from './runtime/transport.js';
|
|
21
35
|
// Transport implementations
|
|
22
36
|
export { ProcessIO, type ProcessIOOptions } from './runtime/process-io.js';
|
|
23
37
|
export { HttpIO, type HttpIOOptions } from './runtime/http-io.js';
|
|
@@ -55,6 +69,7 @@ export { RuntimeBridge } from './runtime/base.js';
|
|
|
55
69
|
|
|
56
70
|
export {
|
|
57
71
|
BridgeError,
|
|
72
|
+
BridgeCodecError,
|
|
58
73
|
BridgeProtocolError,
|
|
59
74
|
BridgeTimeoutError,
|
|
60
75
|
BridgeDisposedError,
|
|
@@ -72,13 +87,27 @@ export type {
|
|
|
72
87
|
PythonModule,
|
|
73
88
|
PythonFunction,
|
|
74
89
|
PythonClass,
|
|
90
|
+
PythonTypeAlias,
|
|
75
91
|
PythonType,
|
|
92
|
+
PythonGenericParameter,
|
|
93
|
+
PythonGenericParameterKind,
|
|
76
94
|
PrimitiveType,
|
|
77
95
|
CollectionType,
|
|
78
96
|
UnionType,
|
|
79
97
|
OptionalType,
|
|
80
98
|
CustomType,
|
|
81
99
|
GenericType,
|
|
100
|
+
CallableType,
|
|
101
|
+
LiteralType,
|
|
102
|
+
AnnotatedType,
|
|
103
|
+
TypeVarType,
|
|
104
|
+
ParamSpecType,
|
|
105
|
+
ParamSpecArgsType,
|
|
106
|
+
ParamSpecKwargsType,
|
|
107
|
+
TypeVarTupleType,
|
|
108
|
+
UnpackType,
|
|
109
|
+
FinalType,
|
|
110
|
+
ClassVarType,
|
|
82
111
|
Parameter,
|
|
83
112
|
Property,
|
|
84
113
|
PythonImport,
|
|
@@ -92,7 +121,6 @@ export type {
|
|
|
92
121
|
NodeConfig,
|
|
93
122
|
HttpConfig,
|
|
94
123
|
PerformanceConfig,
|
|
95
|
-
DevelopmentConfig,
|
|
96
124
|
TypeMappingConfig,
|
|
97
125
|
TypePreset,
|
|
98
126
|
BridgeInfo,
|
|
@@ -106,6 +134,7 @@ export type {
|
|
|
106
134
|
// Main API
|
|
107
135
|
export { tywrap } from './tywrap.js';
|
|
108
136
|
export { generate } from './tywrap.js';
|
|
137
|
+
export type { GenerateFailure, GenerateResult, GenerateRunOptions } from './tywrap.js';
|
|
109
138
|
|
|
110
139
|
// Runtime detection utilities
|
|
111
140
|
export { detectRuntime, isNodejs, isDeno, isBun, isBrowser } from './utils/runtime.js';
|
|
@@ -118,7 +147,7 @@ export {
|
|
|
118
147
|
} from './utils/codec.js';
|
|
119
148
|
|
|
120
149
|
// Version info
|
|
121
|
-
export const VERSION = '0.
|
|
150
|
+
export const VERSION = '0.3.0';
|
|
122
151
|
|
|
123
152
|
/**
|
|
124
153
|
* Quick setup function for getting started
|
|
@@ -252,17 +252,34 @@ export class BridgeCore {
|
|
|
252
252
|
return;
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
255
|
+
const hasResult = Object.prototype.hasOwnProperty.call(msg, 'result');
|
|
256
|
+
const hasError = Object.prototype.hasOwnProperty.call(msg, 'error');
|
|
257
|
+
|
|
258
|
+
if (hasResult && hasError) {
|
|
259
|
+
this.handleProtocolError('Response cannot include both "result" and "error"', line);
|
|
260
|
+
return;
|
|
258
261
|
}
|
|
259
262
|
|
|
260
|
-
if (
|
|
263
|
+
if (hasError) {
|
|
261
264
|
const errorPayload = this.normalizeErrorPayload(msg.error);
|
|
265
|
+
if (!errorPayload) {
|
|
266
|
+
const reason = this.describeInvalidErrorPayload(msg.error);
|
|
267
|
+
this.handleProtocolError(`Invalid response "error" payload: ${reason}`, line);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
this.pending.delete(msg.id);
|
|
271
|
+
if (pending.timer) {
|
|
272
|
+
clearTimeout(pending.timer);
|
|
273
|
+
}
|
|
262
274
|
pending.reject(this.errorFrom(errorPayload));
|
|
263
275
|
return;
|
|
264
276
|
}
|
|
265
277
|
|
|
278
|
+
this.pending.delete(msg.id);
|
|
279
|
+
if (pending.timer) {
|
|
280
|
+
clearTimeout(pending.timer);
|
|
281
|
+
}
|
|
282
|
+
|
|
266
283
|
Promise.resolve(this.options.decodeValue(msg.result))
|
|
267
284
|
.then(decoded => pending.resolve(decoded))
|
|
268
285
|
.catch(err => {
|
|
@@ -280,21 +297,44 @@ export class BridgeCore {
|
|
|
280
297
|
return error;
|
|
281
298
|
}
|
|
282
299
|
|
|
283
|
-
private normalizeErrorPayload(
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
300
|
+
private normalizeErrorPayload(
|
|
301
|
+
err: unknown
|
|
302
|
+
): { type: string; message: string; traceback?: string } | null {
|
|
303
|
+
if (!err || typeof err !== 'object' || Array.isArray(err)) {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
const candidate = err as { type?: unknown; message?: unknown; traceback?: unknown };
|
|
307
|
+
if (typeof candidate.type !== 'string' || typeof candidate.message !== 'string') {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
if (candidate.traceback !== undefined && typeof candidate.traceback !== 'string') {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
const normalized: { type: string; message: string; traceback?: string } = {
|
|
314
|
+
type: candidate.type,
|
|
315
|
+
message: candidate.message,
|
|
316
|
+
};
|
|
317
|
+
if (typeof candidate.traceback === 'string') {
|
|
318
|
+
normalized.traceback = candidate.traceback;
|
|
319
|
+
}
|
|
320
|
+
return normalized;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
private describeInvalidErrorPayload(err: unknown): string {
|
|
324
|
+
if (!err || typeof err !== 'object' || Array.isArray(err)) {
|
|
325
|
+
return 'expected an object with string "type" and "message" fields';
|
|
326
|
+
}
|
|
327
|
+
const candidate = err as { type?: unknown; message?: unknown; traceback?: unknown };
|
|
328
|
+
if (typeof candidate.type !== 'string') {
|
|
329
|
+
return '"type" must be a string';
|
|
330
|
+
}
|
|
331
|
+
if (typeof candidate.message !== 'string') {
|
|
332
|
+
return '"message" must be a string';
|
|
333
|
+
}
|
|
334
|
+
if (candidate.traceback !== undefined && typeof candidate.traceback !== 'string') {
|
|
335
|
+
return '"traceback" must be a string when provided';
|
|
296
336
|
}
|
|
297
|
-
return
|
|
337
|
+
return 'expected an object with string "type" and "message" fields';
|
|
298
338
|
}
|
|
299
339
|
|
|
300
340
|
private failRequest(id: number, error: Error): void {
|
|
@@ -16,14 +16,26 @@
|
|
|
16
16
|
* @see https://github.com/bbopen/tywrap/issues/149
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
import type { BridgeInfo } from '../types/index.js';
|
|
20
|
+
|
|
19
21
|
import { BoundedContext, type ExecuteOptions } from './bounded-context.js';
|
|
22
|
+
import { BridgeProtocolError } from './errors.js';
|
|
20
23
|
import { SafeCodec, type CodecOptions } from './safe-codec.js';
|
|
24
|
+
import { TYWRAP_PROTOCOL_VERSION } from './protocol.js';
|
|
21
25
|
import { PROTOCOL_ID, type Transport, type ProtocolMessage } from './transport.js';
|
|
22
26
|
|
|
23
27
|
// =============================================================================
|
|
24
28
|
// TYPES
|
|
25
29
|
// =============================================================================
|
|
26
30
|
|
|
31
|
+
export interface GetBridgeInfoOptions {
|
|
32
|
+
/**
|
|
33
|
+
* If true, bypasses the cached info and queries the bridge again.
|
|
34
|
+
* This is useful when you want up-to-date instance counts or diagnostics.
|
|
35
|
+
*/
|
|
36
|
+
refresh?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
27
39
|
/**
|
|
28
40
|
* Configuration options for BridgeProtocol.
|
|
29
41
|
*/
|
|
@@ -38,6 +50,129 @@ export interface BridgeProtocolOptions {
|
|
|
38
50
|
defaultTimeoutMs?: number;
|
|
39
51
|
}
|
|
40
52
|
|
|
53
|
+
function validateBridgeInfoPayload(value: unknown): BridgeInfo {
|
|
54
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
55
|
+
const kind = value === null ? 'null' : Array.isArray(value) ? 'array' : typeof value;
|
|
56
|
+
throw new BridgeProtocolError(`Invalid bridge info payload: expected object, got ${kind}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface BridgeInfoWire {
|
|
60
|
+
protocol?: unknown;
|
|
61
|
+
protocolVersion?: unknown;
|
|
62
|
+
bridge?: unknown;
|
|
63
|
+
pythonVersion?: unknown;
|
|
64
|
+
pid?: unknown;
|
|
65
|
+
codecFallback?: unknown;
|
|
66
|
+
arrowAvailable?: unknown;
|
|
67
|
+
scipyAvailable?: unknown;
|
|
68
|
+
torchAvailable?: unknown;
|
|
69
|
+
sklearnAvailable?: unknown;
|
|
70
|
+
instances?: unknown;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const formatValue = (val: unknown): string => {
|
|
74
|
+
try {
|
|
75
|
+
const serialized = JSON.stringify(val);
|
|
76
|
+
return serialized ?? String(val);
|
|
77
|
+
} catch {
|
|
78
|
+
return String(val);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const obj = value as BridgeInfoWire;
|
|
83
|
+
|
|
84
|
+
const protocol = obj.protocol;
|
|
85
|
+
if (protocol !== PROTOCOL_ID) {
|
|
86
|
+
throw new BridgeProtocolError(
|
|
87
|
+
`Invalid bridge info payload: protocol expected "${PROTOCOL_ID}", got ${formatValue(protocol)}`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const protocolVersion = obj.protocolVersion;
|
|
92
|
+
if (protocolVersion !== TYWRAP_PROTOCOL_VERSION) {
|
|
93
|
+
throw new BridgeProtocolError(
|
|
94
|
+
`Invalid bridge info payload: protocolVersion expected ${TYWRAP_PROTOCOL_VERSION}, got ${formatValue(protocolVersion)}`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const bridge = obj.bridge;
|
|
99
|
+
if (bridge !== 'python-subprocess') {
|
|
100
|
+
throw new BridgeProtocolError(
|
|
101
|
+
`Invalid bridge info payload: bridge expected "python-subprocess", got ${formatValue(bridge)}`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const pythonVersion = obj.pythonVersion;
|
|
106
|
+
if (typeof pythonVersion !== 'string' || pythonVersion.length === 0) {
|
|
107
|
+
throw new BridgeProtocolError(
|
|
108
|
+
`Invalid bridge info payload: pythonVersion expected non-empty string, got ${formatValue(pythonVersion)}`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const pid = obj.pid;
|
|
113
|
+
if (typeof pid !== 'number' || !Number.isInteger(pid) || pid <= 0) {
|
|
114
|
+
throw new BridgeProtocolError(
|
|
115
|
+
`Invalid bridge info payload: pid expected positive integer, got ${formatValue(pid)}`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const codecFallback = obj.codecFallback;
|
|
120
|
+
if (codecFallback !== 'json' && codecFallback !== 'none') {
|
|
121
|
+
throw new BridgeProtocolError(
|
|
122
|
+
`Invalid bridge info payload: codecFallback expected "json" or "none", got ${formatValue(codecFallback)}`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const arrowAvailable = obj.arrowAvailable;
|
|
127
|
+
if (typeof arrowAvailable !== 'boolean') {
|
|
128
|
+
throw new BridgeProtocolError(
|
|
129
|
+
`Invalid bridge info payload: arrowAvailable expected boolean, got ${formatValue(arrowAvailable)}`
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const scipyAvailable = obj.scipyAvailable;
|
|
134
|
+
if (typeof scipyAvailable !== 'boolean') {
|
|
135
|
+
throw new BridgeProtocolError(
|
|
136
|
+
`Invalid bridge info payload: scipyAvailable expected boolean, got ${formatValue(scipyAvailable)}`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const torchAvailable = obj.torchAvailable;
|
|
141
|
+
if (typeof torchAvailable !== 'boolean') {
|
|
142
|
+
throw new BridgeProtocolError(
|
|
143
|
+
`Invalid bridge info payload: torchAvailable expected boolean, got ${formatValue(torchAvailable)}`
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const sklearnAvailable = obj.sklearnAvailable;
|
|
148
|
+
if (typeof sklearnAvailable !== 'boolean') {
|
|
149
|
+
throw new BridgeProtocolError(
|
|
150
|
+
`Invalid bridge info payload: sklearnAvailable expected boolean, got ${formatValue(sklearnAvailable)}`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const instances = obj.instances;
|
|
155
|
+
if (typeof instances !== 'number' || !Number.isInteger(instances) || instances < 0) {
|
|
156
|
+
throw new BridgeProtocolError(
|
|
157
|
+
`Invalid bridge info payload: instances expected non-negative integer, got ${formatValue(instances)}`
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
protocol: PROTOCOL_ID,
|
|
163
|
+
protocolVersion: TYWRAP_PROTOCOL_VERSION,
|
|
164
|
+
bridge: 'python-subprocess',
|
|
165
|
+
pythonVersion,
|
|
166
|
+
pid,
|
|
167
|
+
codecFallback,
|
|
168
|
+
arrowAvailable,
|
|
169
|
+
scipyAvailable,
|
|
170
|
+
torchAvailable,
|
|
171
|
+
sklearnAvailable,
|
|
172
|
+
instances,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
41
176
|
// =============================================================================
|
|
42
177
|
// BRIDGE PROTOCOL BASE CLASS
|
|
43
178
|
// =============================================================================
|
|
@@ -80,6 +215,9 @@ export class BridgeProtocol extends BoundedContext {
|
|
|
80
215
|
/** Counter for generating unique request IDs */
|
|
81
216
|
private requestId = 0;
|
|
82
217
|
|
|
218
|
+
/** Cached bridge diagnostics info (populated by getBridgeInfo). */
|
|
219
|
+
private bridgeInfoCache?: BridgeInfo;
|
|
220
|
+
|
|
83
221
|
/**
|
|
84
222
|
* Create a new BridgeProtocol instance.
|
|
85
223
|
*
|
|
@@ -117,6 +255,7 @@ export class BridgeProtocol extends BoundedContext {
|
|
|
117
255
|
* but should not need to dispose the transport manually.
|
|
118
256
|
*/
|
|
119
257
|
protected async doDispose(): Promise<void> {
|
|
258
|
+
this.bridgeInfoCache = undefined;
|
|
120
259
|
// Transport is tracked and will be disposed by BoundedContext
|
|
121
260
|
// Subclasses can override to add additional cleanup
|
|
122
261
|
}
|
|
@@ -316,4 +455,29 @@ export class BridgeProtocol extends BoundedContext {
|
|
|
316
455
|
},
|
|
317
456
|
});
|
|
318
457
|
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Fetch bridge diagnostics and feature availability.
|
|
461
|
+
*
|
|
462
|
+
* The Python bridge supports a `meta` method that returns protocol and environment info
|
|
463
|
+
* (including optional codec availability and current instance count).
|
|
464
|
+
*/
|
|
465
|
+
async getBridgeInfo(options: GetBridgeInfoOptions = {}): Promise<BridgeInfo> {
|
|
466
|
+
if (!options.refresh && this.bridgeInfoCache) {
|
|
467
|
+
return this.bridgeInfoCache;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const info = await this.sendMessage<BridgeInfo>(
|
|
471
|
+
{
|
|
472
|
+
method: 'meta',
|
|
473
|
+
params: {},
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
validate: validateBridgeInfoPayload,
|
|
477
|
+
}
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
this.bridgeInfoCache = info;
|
|
481
|
+
return info;
|
|
482
|
+
}
|
|
319
483
|
}
|
package/src/runtime/errors.ts
CHANGED
|
@@ -14,6 +14,20 @@ export class BridgeProtocolError extends BridgeError {}
|
|
|
14
14
|
export class BridgeTimeoutError extends BridgeError {}
|
|
15
15
|
export class BridgeDisposedError extends BridgeError {}
|
|
16
16
|
|
|
17
|
+
export class BridgeCodecError extends BridgeError {
|
|
18
|
+
codecPhase?: string;
|
|
19
|
+
valueType?: string;
|
|
20
|
+
|
|
21
|
+
constructor(
|
|
22
|
+
message: string,
|
|
23
|
+
options?: { code?: string; cause?: unknown; codecPhase?: string; valueType?: string }
|
|
24
|
+
) {
|
|
25
|
+
super(message, { code: options?.code, cause: options?.cause });
|
|
26
|
+
this.codecPhase = options?.codecPhase;
|
|
27
|
+
this.valueType = options?.valueType;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
17
31
|
export class BridgeExecutionError extends BridgeError {
|
|
18
32
|
traceback?: string;
|
|
19
33
|
}
|
package/src/runtime/http-io.ts
CHANGED
|
@@ -135,7 +135,8 @@ export class HttpIO implements Transport {
|
|
|
135
135
|
|
|
136
136
|
// Determine effective timeout
|
|
137
137
|
// 0 = no timeout (per interface contract), negative = use default
|
|
138
|
-
const effectiveTimeout =
|
|
138
|
+
const effectiveTimeout =
|
|
139
|
+
timeoutMs === 0 ? 0 : timeoutMs > 0 ? timeoutMs : this.defaultTimeoutMs;
|
|
139
140
|
|
|
140
141
|
// Create abort controller for timeout
|
|
141
142
|
const controller = new AbortController();
|
package/src/runtime/http.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { BridgeProtocol, type BridgeProtocolOptions } from './bridge-protocol.js';
|
|
11
11
|
import { HttpIO } from './http-io.js';
|
|
12
12
|
import type { CodecOptions } from './safe-codec.js';
|
|
13
|
+
import { autoRegisterArrowDecoder } from '../utils/codec.js';
|
|
13
14
|
|
|
14
15
|
// =============================================================================
|
|
15
16
|
// OPTIONS
|
|
@@ -83,4 +84,10 @@ export class HttpBridge extends BridgeProtocol {
|
|
|
83
84
|
|
|
84
85
|
super(protocolOptions);
|
|
85
86
|
}
|
|
87
|
+
|
|
88
|
+
protected async doInit(): Promise<void> {
|
|
89
|
+
// Best-effort: keep apache-arrow optional and avoid breaking non-Node runtimes.
|
|
90
|
+
await autoRegisterArrowDecoder();
|
|
91
|
+
await super.doInit();
|
|
92
|
+
}
|
|
86
93
|
}
|