tywrap 0.2.0 → 0.3.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 +65 -31
- package/dist/cli.js +49 -2
- 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 +43 -7
- 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 +47 -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/index.d.ts +23 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -3
- package/dist/index.js.map +1 -1
- package/dist/runtime/base.d.ts +17 -7
- package/dist/runtime/base.d.ts.map +1 -1
- package/dist/runtime/base.js +18 -1
- package/dist/runtime/base.js.map +1 -1
- package/dist/runtime/bounded-context.d.ts +252 -0
- package/dist/runtime/bounded-context.d.ts.map +1 -0
- package/dist/runtime/bounded-context.js +454 -0
- package/dist/runtime/bounded-context.js.map +1 -0
- 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 +184 -0
- package/dist/runtime/bridge-protocol.d.ts.map +1 -0
- package/dist/runtime/bridge-protocol.js +344 -0
- package/dist/runtime/bridge-protocol.js.map +1 -0
- package/dist/runtime/disposable.d.ts +40 -0
- package/dist/runtime/disposable.d.ts.map +1 -0
- package/dist/runtime/disposable.js +49 -0
- package/dist/runtime/disposable.js.map +1 -0
- 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 +91 -0
- package/dist/runtime/http-io.d.ts.map +1 -0
- package/dist/runtime/http-io.js +195 -0
- package/dist/runtime/http-io.js.map +1 -0
- package/dist/runtime/http.d.ts +48 -13
- package/dist/runtime/http.d.ts.map +1 -1
- package/dist/runtime/http.js +60 -73
- package/dist/runtime/http.js.map +1 -1
- package/dist/runtime/node.d.ts +97 -130
- package/dist/runtime/node.d.ts.map +1 -1
- package/dist/runtime/node.js +392 -521
- package/dist/runtime/node.js.map +1 -1
- package/dist/runtime/optimized-node.d.ts +1 -1
- package/dist/runtime/optimized-node.d.ts.map +1 -1
- package/dist/runtime/optimized-node.js +1 -1
- package/dist/runtime/optimized-node.js.map +1 -1
- package/dist/runtime/pooled-transport.d.ts +135 -0
- package/dist/runtime/pooled-transport.d.ts.map +1 -0
- package/dist/runtime/pooled-transport.js +177 -0
- package/dist/runtime/pooled-transport.js.map +1 -0
- package/dist/runtime/process-io.d.ts +209 -0
- package/dist/runtime/process-io.d.ts.map +1 -0
- package/dist/runtime/process-io.js +729 -0
- package/dist/runtime/process-io.js.map +1 -0
- package/dist/runtime/pyodide-io.d.ts +154 -0
- package/dist/runtime/pyodide-io.d.ts.map +1 -0
- package/dist/runtime/pyodide-io.js +459 -0
- package/dist/runtime/pyodide-io.js.map +1 -0
- package/dist/runtime/pyodide.d.ts +51 -19
- package/dist/runtime/pyodide.d.ts.map +1 -1
- package/dist/runtime/pyodide.js +57 -186
- package/dist/runtime/pyodide.js.map +1 -1
- package/dist/runtime/safe-codec.d.ts +103 -0
- package/dist/runtime/safe-codec.d.ts.map +1 -0
- package/dist/runtime/safe-codec.js +519 -0
- package/dist/runtime/safe-codec.js.map +1 -0
- package/dist/runtime/transport.d.ts +191 -0
- package/dist/runtime/transport.d.ts.map +1 -0
- package/dist/runtime/transport.js +86 -0
- package/dist/runtime/transport.js.map +1 -0
- package/dist/runtime/validators.d.ts +120 -0
- package/dist/runtime/validators.d.ts.map +1 -0
- package/dist/runtime/validators.js +229 -0
- package/dist/runtime/validators.js.map +1 -0
- package/dist/runtime/worker-pool.d.ts +208 -0
- package/dist/runtime/worker-pool.d.ts.map +1 -0
- package/dist/runtime/worker-pool.js +419 -0
- package/dist/runtime/worker-pool.js.map +1 -0
- package/dist/types/index.d.ts +51 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/tywrap.d.ts.map +1 -1
- package/dist/tywrap.js +227 -328
- 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 +114 -1
- 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 +40 -18
- package/runtime/python_bridge.py +108 -15
- package/runtime/safe_codec.py +344 -0
- package/src/cli.ts +61 -2
- package/src/config/index.ts +63 -17
- package/src/core/analyzer.ts +52 -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/index.ts +78 -6
- package/src/runtime/base.ts +18 -26
- package/src/runtime/bounded-context.ts +608 -0
- package/src/runtime/bridge-core.ts +58 -18
- package/src/runtime/bridge-protocol.ts +483 -0
- package/src/runtime/disposable.ts +65 -0
- package/src/runtime/errors.ts +14 -0
- package/src/runtime/http-io.ts +245 -0
- package/src/runtime/http.ts +76 -115
- package/src/runtime/node.ts +563 -676
- package/src/runtime/optimized-node.ts +4 -1
- package/src/runtime/pooled-transport.ts +263 -0
- package/src/runtime/process-io.ts +930 -0
- package/src/runtime/pyodide-io.ts +551 -0
- package/src/runtime/pyodide.ts +75 -215
- package/src/runtime/safe-codec.ts +651 -0
- package/src/runtime/transport.ts +278 -0
- package/src/runtime/validators.ts +252 -0
- package/src/runtime/worker-pool.ts +559 -0
- package/src/types/global.d.ts +11 -1
- package/src/types/index.ts +63 -0
- package/src/tywrap.ts +279 -358
- package/src/utils/cache.ts +59 -5
- package/src/utils/codec.ts +122 -1
- package/src/utils/ir-cache.ts +51 -0
- package/src/utils/parallel-processor.ts +119 -43
- package/src/utils/runtime.ts +129 -18
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP Transport for BridgeProtocol.
|
|
3
|
+
*
|
|
4
|
+
* Provides stateless HTTP POST-based communication with a Python server.
|
|
5
|
+
* Each request is independent - no connection state is maintained.
|
|
6
|
+
*
|
|
7
|
+
* @see https://github.com/bbopen/tywrap/issues/149
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
BridgeDisposedError,
|
|
12
|
+
BridgeExecutionError,
|
|
13
|
+
BridgeProtocolError,
|
|
14
|
+
BridgeTimeoutError,
|
|
15
|
+
} from './errors.js';
|
|
16
|
+
import type { Transport } from './transport.js';
|
|
17
|
+
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// OPTIONS
|
|
20
|
+
// =============================================================================
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Configuration options for HttpIO transport.
|
|
24
|
+
*/
|
|
25
|
+
export interface HttpIOOptions {
|
|
26
|
+
/** Base URL for the Python server (e.g., 'http://localhost:8000') */
|
|
27
|
+
baseURL: string;
|
|
28
|
+
|
|
29
|
+
/** Additional headers to include in each request */
|
|
30
|
+
headers?: Record<string, string>;
|
|
31
|
+
|
|
32
|
+
/** Default timeout in milliseconds. Default: 30000 (30 seconds) */
|
|
33
|
+
defaultTimeoutMs?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// =============================================================================
|
|
37
|
+
// HTTP TRANSPORT
|
|
38
|
+
// =============================================================================
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* HTTP-based transport for BridgeProtocol.
|
|
42
|
+
*
|
|
43
|
+
* This transport sends protocol messages as HTTP POST requests to a Python
|
|
44
|
+
* server. It is stateless - each request is independent and the transport
|
|
45
|
+
* is always ready after construction.
|
|
46
|
+
*
|
|
47
|
+
* Features:
|
|
48
|
+
* - Stateless design (init/dispose are no-ops)
|
|
49
|
+
* - Timeout handling via AbortController
|
|
50
|
+
* - External signal support for cancellation
|
|
51
|
+
* - Proper error classification
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```typescript
|
|
55
|
+
* const transport = new HttpIO({ baseURL: 'http://localhost:8000' });
|
|
56
|
+
* await transport.init(); // No-op but follows Transport contract
|
|
57
|
+
*
|
|
58
|
+
* const response = await transport.send(
|
|
59
|
+
* JSON.stringify({ id: '1', type: 'call', module: 'math', functionName: 'sqrt', args: [16] }),
|
|
60
|
+
* 5000
|
|
61
|
+
* );
|
|
62
|
+
*
|
|
63
|
+
* await transport.dispose(); // Marks as disposed
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export class HttpIO implements Transport {
|
|
67
|
+
private readonly baseURL: string;
|
|
68
|
+
private readonly headers: Record<string, string>;
|
|
69
|
+
private readonly defaultTimeoutMs: number;
|
|
70
|
+
private _isDisposed = false;
|
|
71
|
+
|
|
72
|
+
constructor(options: HttpIOOptions) {
|
|
73
|
+
// Normalize URL - remove trailing slash
|
|
74
|
+
this.baseURL = options.baseURL.replace(/\/$/, '');
|
|
75
|
+
this.headers = {
|
|
76
|
+
'Content-Type': 'application/json',
|
|
77
|
+
...options.headers,
|
|
78
|
+
};
|
|
79
|
+
this.defaultTimeoutMs = options.defaultTimeoutMs ?? 30000;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ===========================================================================
|
|
83
|
+
// LIFECYCLE
|
|
84
|
+
// ===========================================================================
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Initialize the transport.
|
|
88
|
+
*
|
|
89
|
+
* HTTP is stateless, so this is a no-op. The transport is ready
|
|
90
|
+
* immediately after construction.
|
|
91
|
+
*/
|
|
92
|
+
async init(): Promise<void> {
|
|
93
|
+
// HTTP is stateless - nothing to initialize
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Dispose the transport.
|
|
98
|
+
*
|
|
99
|
+
* Marks the transport as disposed. Subsequent send() calls will fail.
|
|
100
|
+
*/
|
|
101
|
+
async dispose(): Promise<void> {
|
|
102
|
+
this._isDisposed = true;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Whether the transport is ready to send messages.
|
|
107
|
+
*
|
|
108
|
+
* Returns true unless dispose() has been called.
|
|
109
|
+
*/
|
|
110
|
+
get isReady(): boolean {
|
|
111
|
+
return !this._isDisposed;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ===========================================================================
|
|
115
|
+
// SEND
|
|
116
|
+
// ===========================================================================
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Send a message to the Python server and wait for the response.
|
|
120
|
+
*
|
|
121
|
+
* @param message - The JSON-encoded protocol message to send
|
|
122
|
+
* @param timeoutMs - Timeout in milliseconds (0 = no timeout, negative = use default)
|
|
123
|
+
* @param signal - Optional AbortSignal for external cancellation
|
|
124
|
+
* @returns The raw response string (JSON-encoded ProtocolResponse)
|
|
125
|
+
*
|
|
126
|
+
* @throws BridgeDisposedError if the transport has been disposed
|
|
127
|
+
* @throws BridgeTimeoutError if the operation times out or is aborted
|
|
128
|
+
* @throws BridgeExecutionError if the server returns a non-2xx status
|
|
129
|
+
* @throws BridgeProtocolError if the response cannot be read
|
|
130
|
+
*/
|
|
131
|
+
async send(message: string, timeoutMs: number, signal?: AbortSignal): Promise<string> {
|
|
132
|
+
if (this._isDisposed) {
|
|
133
|
+
throw new BridgeDisposedError('Transport has been disposed');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Determine effective timeout
|
|
137
|
+
// 0 = no timeout (per interface contract), negative = use default
|
|
138
|
+
const effectiveTimeout =
|
|
139
|
+
timeoutMs === 0 ? 0 : timeoutMs > 0 ? timeoutMs : this.defaultTimeoutMs;
|
|
140
|
+
|
|
141
|
+
// Create abort controller for timeout
|
|
142
|
+
const controller = new AbortController();
|
|
143
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
144
|
+
|
|
145
|
+
// Set up timeout if enabled
|
|
146
|
+
if (effectiveTimeout > 0) {
|
|
147
|
+
timeoutId = setTimeout(() => {
|
|
148
|
+
controller.abort();
|
|
149
|
+
}, effectiveTimeout);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Handle external signal - abort our controller when external aborts
|
|
153
|
+
const externalAbortHandler = (): void => {
|
|
154
|
+
controller.abort();
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
if (signal) {
|
|
158
|
+
// Check if already aborted
|
|
159
|
+
if (signal.aborted) {
|
|
160
|
+
if (timeoutId !== undefined) {
|
|
161
|
+
clearTimeout(timeoutId);
|
|
162
|
+
}
|
|
163
|
+
throw new BridgeTimeoutError('Operation aborted');
|
|
164
|
+
}
|
|
165
|
+
signal.addEventListener('abort', externalAbortHandler, { once: true });
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
const response = await fetch(this.baseURL, {
|
|
170
|
+
method: 'POST',
|
|
171
|
+
headers: this.headers,
|
|
172
|
+
body: message,
|
|
173
|
+
signal: controller.signal,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Handle non-2xx status codes
|
|
177
|
+
if (!response.ok) {
|
|
178
|
+
const errorBody = await this.safeReadText(response);
|
|
179
|
+
throw new BridgeExecutionError(
|
|
180
|
+
`HTTP ${response.status}: ${errorBody || response.statusText}`,
|
|
181
|
+
{ code: `HTTP_${response.status}` }
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Read response body
|
|
186
|
+
const responseText = await response.text();
|
|
187
|
+
return responseText;
|
|
188
|
+
} catch (error) {
|
|
189
|
+
// Handle abort errors (timeout or external signal)
|
|
190
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
191
|
+
// Determine if it was timeout or external abort
|
|
192
|
+
if (signal?.aborted) {
|
|
193
|
+
throw new BridgeTimeoutError('Operation aborted');
|
|
194
|
+
}
|
|
195
|
+
throw new BridgeTimeoutError(`Request timed out after ${effectiveTimeout}ms`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Re-throw bridge errors as-is
|
|
199
|
+
if (
|
|
200
|
+
error instanceof BridgeTimeoutError ||
|
|
201
|
+
error instanceof BridgeExecutionError ||
|
|
202
|
+
error instanceof BridgeProtocolError
|
|
203
|
+
) {
|
|
204
|
+
throw error;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Wrap network errors
|
|
208
|
+
if (error instanceof TypeError) {
|
|
209
|
+
// fetch throws TypeError for network failures
|
|
210
|
+
throw new BridgeExecutionError(`Network error: ${error.message}`, { cause: error });
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Wrap unknown errors
|
|
214
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
215
|
+
throw new BridgeExecutionError(`Request failed: ${errorMessage}`, {
|
|
216
|
+
cause: error instanceof Error ? error : undefined,
|
|
217
|
+
});
|
|
218
|
+
} finally {
|
|
219
|
+
// Clean up timeout
|
|
220
|
+
if (timeoutId !== undefined) {
|
|
221
|
+
clearTimeout(timeoutId);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Clean up external signal listener
|
|
225
|
+
if (signal) {
|
|
226
|
+
signal.removeEventListener('abort', externalAbortHandler);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ===========================================================================
|
|
232
|
+
// HELPERS
|
|
233
|
+
// ===========================================================================
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Safely read response body as text, returning empty string on failure.
|
|
237
|
+
*/
|
|
238
|
+
private async safeReadText(response: Response): Promise<string> {
|
|
239
|
+
try {
|
|
240
|
+
return await response.text();
|
|
241
|
+
} catch {
|
|
242
|
+
return '';
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
package/src/runtime/http.ts
CHANGED
|
@@ -1,132 +1,93 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* HTTP runtime bridge
|
|
2
|
+
* HTTP runtime bridge for BridgeProtocol.
|
|
3
|
+
*
|
|
4
|
+
* HttpBridge extends BridgeProtocol and uses HttpIO transport for
|
|
5
|
+
* stateless HTTP POST-based communication with a Python server.
|
|
6
|
+
*
|
|
7
|
+
* @see https://github.com/bbopen/tywrap/issues/149
|
|
3
8
|
*/
|
|
4
9
|
|
|
5
|
-
import {
|
|
10
|
+
import { BridgeProtocol, type BridgeProtocolOptions } from './bridge-protocol.js';
|
|
11
|
+
import { HttpIO } from './http-io.js';
|
|
12
|
+
import type { CodecOptions } from './safe-codec.js';
|
|
13
|
+
import { autoRegisterArrowDecoder } from '../utils/codec.js';
|
|
6
14
|
|
|
7
|
-
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// OPTIONS
|
|
17
|
+
// =============================================================================
|
|
8
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Configuration options for HttpBridge.
|
|
21
|
+
*/
|
|
9
22
|
export interface HttpBridgeOptions {
|
|
23
|
+
/** Base URL for the Python server (e.g., 'http://localhost:8000') */
|
|
10
24
|
baseURL: string;
|
|
11
|
-
headers?: Record<string, string>;
|
|
12
|
-
timeoutMs?: number;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
interface HttpCallPayload {
|
|
16
|
-
module: string;
|
|
17
|
-
functionName: string;
|
|
18
|
-
args: unknown[];
|
|
19
|
-
kwargs?: Record<string, unknown>;
|
|
20
|
-
}
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
className: string;
|
|
25
|
-
args: unknown[];
|
|
26
|
-
kwargs?: Record<string, unknown>;
|
|
27
|
-
}
|
|
26
|
+
/** Additional headers to include in each request */
|
|
27
|
+
headers?: Record<string, string>;
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
methodName: string;
|
|
32
|
-
args: unknown[];
|
|
33
|
-
kwargs?: Record<string, unknown>;
|
|
34
|
-
}
|
|
29
|
+
/** Timeout in ms for requests. Default: 30000 (30 seconds) */
|
|
30
|
+
timeoutMs?: number;
|
|
35
31
|
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
/** Codec options for validation/serialization */
|
|
33
|
+
codec?: CodecOptions;
|
|
38
34
|
}
|
|
39
35
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
private readonly timeoutMs: number;
|
|
44
|
-
|
|
45
|
-
constructor(options: HttpBridgeOptions = { baseURL: 'http://localhost:8000' }) {
|
|
46
|
-
super();
|
|
47
|
-
this.baseURL = options.baseURL.replace(/\/$/, '');
|
|
48
|
-
this.headers = { 'content-type': 'application/json', ...(options.headers ?? {}) };
|
|
49
|
-
this.timeoutMs = options.timeoutMs ?? 30000;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async call<T = unknown>(
|
|
53
|
-
module: string,
|
|
54
|
-
functionName: string,
|
|
55
|
-
args: unknown[],
|
|
56
|
-
kwargs?: Record<string, unknown>
|
|
57
|
-
): Promise<T> {
|
|
58
|
-
const payload: HttpCallPayload = { module, functionName, args, kwargs };
|
|
59
|
-
const res = await this.post(`${this.baseURL}/call`, payload);
|
|
60
|
-
return (await decodeValueAsync(res)) as T;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async instantiate<T = unknown>(
|
|
64
|
-
module: string,
|
|
65
|
-
className: string,
|
|
66
|
-
args: unknown[],
|
|
67
|
-
kwargs?: Record<string, unknown>
|
|
68
|
-
): Promise<T> {
|
|
69
|
-
const payload: HttpInstantiatePayload = { module, className, args, kwargs };
|
|
70
|
-
const res = await this.post(`${this.baseURL}/instantiate`, payload);
|
|
71
|
-
return (await decodeValueAsync(res)) as T;
|
|
72
|
-
}
|
|
36
|
+
// =============================================================================
|
|
37
|
+
// HTTP BRIDGE
|
|
38
|
+
// =============================================================================
|
|
73
39
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
clearTimeout(timer);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
40
|
+
/**
|
|
41
|
+
* HTTP-based runtime bridge for executing Python code.
|
|
42
|
+
*
|
|
43
|
+
* HttpBridge provides a stateless HTTP transport for communication with
|
|
44
|
+
* a Python server. Each request is independent - no connection state is
|
|
45
|
+
* maintained between calls.
|
|
46
|
+
*
|
|
47
|
+
* Features:
|
|
48
|
+
* - Stateless HTTP POST communication
|
|
49
|
+
* - Timeout handling via AbortController
|
|
50
|
+
* - Full SafeCodec validation (NaN/Infinity rejection, key validation)
|
|
51
|
+
* - Automatic Arrow decoding for DataFrames/ndarrays
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```typescript
|
|
55
|
+
* const bridge = new HttpBridge({ baseURL: 'http://localhost:8000' });
|
|
56
|
+
* await bridge.init();
|
|
57
|
+
*
|
|
58
|
+
* const result = await bridge.call('math', 'sqrt', [16]);
|
|
59
|
+
* console.log(result); // 4.0
|
|
60
|
+
*
|
|
61
|
+
* await bridge.dispose();
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export class HttpBridge extends BridgeProtocol {
|
|
65
|
+
/**
|
|
66
|
+
* Create a new HttpBridge instance.
|
|
67
|
+
*
|
|
68
|
+
* @param options - Configuration options for the bridge
|
|
69
|
+
*/
|
|
70
|
+
constructor(options: HttpBridgeOptions) {
|
|
71
|
+
// Create HTTP transport
|
|
72
|
+
const transport = new HttpIO({
|
|
73
|
+
baseURL: options.baseURL,
|
|
74
|
+
headers: options.headers,
|
|
75
|
+
defaultTimeoutMs: options.timeoutMs,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Initialize BridgeProtocol with transport and codec options
|
|
79
|
+
const protocolOptions: BridgeProtocolOptions = {
|
|
80
|
+
transport,
|
|
81
|
+
codec: options.codec,
|
|
82
|
+
defaultTimeoutMs: options.timeoutMs,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
super(protocolOptions);
|
|
123
86
|
}
|
|
124
|
-
}
|
|
125
87
|
|
|
126
|
-
async
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return '';
|
|
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();
|
|
131
92
|
}
|
|
132
93
|
}
|