tywrap 0.1.2 → 0.2.1

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 (93) hide show
  1. package/README.md +11 -4
  2. package/dist/index.d.ts +20 -4
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +22 -2
  5. package/dist/index.js.map +1 -1
  6. package/dist/runtime/base.d.ts +17 -7
  7. package/dist/runtime/base.d.ts.map +1 -1
  8. package/dist/runtime/base.js +18 -1
  9. package/dist/runtime/base.js.map +1 -1
  10. package/dist/runtime/bounded-context.d.ts +252 -0
  11. package/dist/runtime/bounded-context.d.ts.map +1 -0
  12. package/dist/runtime/bounded-context.js +454 -0
  13. package/dist/runtime/bounded-context.js.map +1 -0
  14. package/dist/runtime/bridge-protocol.d.ts +167 -0
  15. package/dist/runtime/bridge-protocol.d.ts.map +1 -0
  16. package/dist/runtime/bridge-protocol.js +247 -0
  17. package/dist/runtime/bridge-protocol.js.map +1 -0
  18. package/dist/runtime/disposable.d.ts +40 -0
  19. package/dist/runtime/disposable.d.ts.map +1 -0
  20. package/dist/runtime/disposable.js +49 -0
  21. package/dist/runtime/disposable.js.map +1 -0
  22. package/dist/runtime/http-io.d.ts +91 -0
  23. package/dist/runtime/http-io.d.ts.map +1 -0
  24. package/dist/runtime/http-io.js +195 -0
  25. package/dist/runtime/http-io.js.map +1 -0
  26. package/dist/runtime/http.d.ts +47 -13
  27. package/dist/runtime/http.d.ts.map +1 -1
  28. package/dist/runtime/http.js +55 -74
  29. package/dist/runtime/http.js.map +1 -1
  30. package/dist/runtime/node.d.ts +142 -28
  31. package/dist/runtime/node.d.ts.map +1 -1
  32. package/dist/runtime/node.js +321 -168
  33. package/dist/runtime/node.js.map +1 -1
  34. package/dist/runtime/optimized-node.d.ts +19 -125
  35. package/dist/runtime/optimized-node.d.ts.map +1 -1
  36. package/dist/runtime/optimized-node.js +19 -550
  37. package/dist/runtime/optimized-node.js.map +1 -1
  38. package/dist/runtime/pooled-transport.d.ts +131 -0
  39. package/dist/runtime/pooled-transport.d.ts.map +1 -0
  40. package/dist/runtime/pooled-transport.js +175 -0
  41. package/dist/runtime/pooled-transport.js.map +1 -0
  42. package/dist/runtime/process-io.d.ts +204 -0
  43. package/dist/runtime/process-io.d.ts.map +1 -0
  44. package/dist/runtime/process-io.js +695 -0
  45. package/dist/runtime/process-io.js.map +1 -0
  46. package/dist/runtime/pyodide-io.d.ts +155 -0
  47. package/dist/runtime/pyodide-io.d.ts.map +1 -0
  48. package/dist/runtime/pyodide-io.js +397 -0
  49. package/dist/runtime/pyodide-io.js.map +1 -0
  50. package/dist/runtime/pyodide.d.ts +51 -19
  51. package/dist/runtime/pyodide.d.ts.map +1 -1
  52. package/dist/runtime/pyodide.js +57 -186
  53. package/dist/runtime/pyodide.js.map +1 -1
  54. package/dist/runtime/safe-codec.d.ts +81 -0
  55. package/dist/runtime/safe-codec.d.ts.map +1 -0
  56. package/dist/runtime/safe-codec.js +345 -0
  57. package/dist/runtime/safe-codec.js.map +1 -0
  58. package/dist/runtime/transport.d.ts +186 -0
  59. package/dist/runtime/transport.d.ts.map +1 -0
  60. package/dist/runtime/transport.js +86 -0
  61. package/dist/runtime/transport.js.map +1 -0
  62. package/dist/runtime/validators.d.ts +131 -0
  63. package/dist/runtime/validators.d.ts.map +1 -0
  64. package/dist/runtime/validators.js +219 -0
  65. package/dist/runtime/validators.js.map +1 -0
  66. package/dist/runtime/worker-pool.d.ts +196 -0
  67. package/dist/runtime/worker-pool.d.ts.map +1 -0
  68. package/dist/runtime/worker-pool.js +371 -0
  69. package/dist/runtime/worker-pool.js.map +1 -0
  70. package/dist/utils/codec.d.ts.map +1 -1
  71. package/dist/utils/codec.js +120 -1
  72. package/dist/utils/codec.js.map +1 -1
  73. package/package.json +2 -2
  74. package/runtime/python_bridge.py +30 -3
  75. package/runtime/safe_codec.py +344 -0
  76. package/src/index.ts +48 -5
  77. package/src/runtime/base.ts +18 -26
  78. package/src/runtime/bounded-context.ts +608 -0
  79. package/src/runtime/bridge-protocol.ts +319 -0
  80. package/src/runtime/disposable.ts +65 -0
  81. package/src/runtime/http-io.ts +244 -0
  82. package/src/runtime/http.ts +71 -117
  83. package/src/runtime/node.ts +460 -217
  84. package/src/runtime/optimized-node.ts +19 -761
  85. package/src/runtime/pooled-transport.ts +252 -0
  86. package/src/runtime/process-io.ts +902 -0
  87. package/src/runtime/pyodide-io.ts +485 -0
  88. package/src/runtime/pyodide.ts +75 -215
  89. package/src/runtime/safe-codec.ts +443 -0
  90. package/src/runtime/transport.ts +273 -0
  91. package/src/runtime/validators.ts +241 -0
  92. package/src/runtime/worker-pool.ts +498 -0
  93. package/src/utils/codec.ts +126 -1
@@ -0,0 +1,247 @@
1
+ /**
2
+ * BridgeProtocol - Unified abstraction for JS<->Python communication.
3
+ *
4
+ * Combines BoundedContext + SafeCodec + Transport into a single base class
5
+ * that handles all cross-boundary concerns:
6
+ * - Lifecycle management (init/dispose state machine)
7
+ * - Request/response encoding with validation
8
+ * - Transport-agnostic message passing
9
+ * - Bounded execution (timeout, retry, abort)
10
+ *
11
+ * Subclasses (NodeBridge, HttpBridge, PyodideBridge) only need to:
12
+ * 1. Create their transport in their constructor
13
+ * 2. Pass it to super() via BridgeProtocolOptions
14
+ * 3. Optionally override doInit() and doDispose() for additional setup/teardown
15
+ *
16
+ * @see https://github.com/bbopen/tywrap/issues/149
17
+ */
18
+ import { BoundedContext } from './bounded-context.js';
19
+ import { SafeCodec } from './safe-codec.js';
20
+ import { PROTOCOL_ID } from './transport.js';
21
+ // =============================================================================
22
+ // BRIDGE PROTOCOL BASE CLASS
23
+ // =============================================================================
24
+ /**
25
+ * BridgeProtocol combines BoundedContext + SafeCodec + Transport
26
+ * into a unified abstraction for all JS<->Python communication.
27
+ *
28
+ * This class provides:
29
+ * - Automatic transport lifecycle management
30
+ * - Request encoding with guardrails (special float rejection, key validation)
31
+ * - Response decoding with Arrow support
32
+ * - Full RuntimeExecution interface implementation
33
+ *
34
+ * Subclasses should:
35
+ * 1. Create their transport in their constructor
36
+ * 2. Pass it to super() via BridgeProtocolOptions
37
+ * 3. Optionally override doInit() and doDispose() for additional setup/teardown
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * class NodeBridge extends BridgeProtocol {
42
+ * constructor(options: NodeBridgeOptions) {
43
+ * const transport = new ProcessIO(options);
44
+ * super({ transport, defaultTimeoutMs: options.timeout });
45
+ * }
46
+ * }
47
+ * ```
48
+ */
49
+ export class BridgeProtocol extends BoundedContext {
50
+ /** Codec instance for validation and serialization */
51
+ codec;
52
+ /** Transport instance for message passing */
53
+ transport;
54
+ /** Default timeout for operations in milliseconds */
55
+ defaultTimeoutMs;
56
+ /** Counter for generating unique request IDs */
57
+ requestId = 0;
58
+ /**
59
+ * Create a new BridgeProtocol instance.
60
+ *
61
+ * @param options - Configuration options including transport and codec settings
62
+ */
63
+ constructor(options) {
64
+ super();
65
+ this.codec = new SafeCodec(options.codec);
66
+ this.transport = options.transport;
67
+ this.defaultTimeoutMs = options.defaultTimeoutMs ?? 30000;
68
+ // Track the transport for automatic cleanup during dispose
69
+ this.trackResource(this.transport);
70
+ }
71
+ // ===========================================================================
72
+ // LIFECYCLE
73
+ // ===========================================================================
74
+ /**
75
+ * Initialize the protocol.
76
+ *
77
+ * Initializes the underlying transport. Subclasses can override this
78
+ * to add additional initialization logic, but must call super.doInit().
79
+ */
80
+ async doInit() {
81
+ await this.transport.init();
82
+ }
83
+ /**
84
+ * Dispose the protocol.
85
+ *
86
+ * The transport is tracked as a resource and will be disposed automatically
87
+ * by BoundedContext. Subclasses can override this to add additional cleanup,
88
+ * but should not need to dispose the transport manually.
89
+ */
90
+ async doDispose() {
91
+ // Transport is tracked and will be disposed by BoundedContext
92
+ // Subclasses can override to add additional cleanup
93
+ }
94
+ // ===========================================================================
95
+ // MESSAGE SENDING
96
+ // ===========================================================================
97
+ /**
98
+ * Send a protocol message and receive a typed response.
99
+ *
100
+ * This method:
101
+ * 1. Generates a unique request ID
102
+ * 2. Encodes the request with validation (special float rejection, etc.)
103
+ * 3. Sends via transport with timeout/abort support
104
+ * 4. Decodes and validates the response
105
+ *
106
+ * @param message - The protocol message (without id field)
107
+ * @param options - Execution options (timeout, retries, validation)
108
+ * @returns The typed response from Python
109
+ *
110
+ * @throws BridgeProtocolError if encoding/decoding fails
111
+ * @throws BridgeExecutionError if Python returns an error
112
+ * @throws BridgeTimeoutError if the operation times out
113
+ */
114
+ async sendMessage(message, options) {
115
+ const fullMessage = {
116
+ ...message,
117
+ id: this.generateId(),
118
+ protocol: PROTOCOL_ID,
119
+ };
120
+ return this.execute(async () => {
121
+ // 1. Encode request (validates args)
122
+ const encoded = this.codec.encodeRequest(fullMessage);
123
+ // 2. Send via transport
124
+ const responseStr = await this.transport.send(encoded, options?.timeoutMs ?? this.defaultTimeoutMs, options?.signal);
125
+ // 3. Decode response (validates result)
126
+ return this.codec.decodeResponse(responseStr);
127
+ }, options);
128
+ }
129
+ /**
130
+ * Async version that uses decodeResponseAsync for Arrow support.
131
+ *
132
+ * Use this method when the response may contain encoded DataFrames,
133
+ * ndarrays, or other Arrow-encoded data structures.
134
+ *
135
+ * @param message - The protocol message (without id field)
136
+ * @param options - Execution options (timeout, retries, validation)
137
+ * @returns The typed response from Python with Arrow decoding applied
138
+ *
139
+ * @throws BridgeProtocolError if encoding/decoding fails
140
+ * @throws BridgeExecutionError if Python returns an error
141
+ * @throws BridgeTimeoutError if the operation times out
142
+ */
143
+ async sendMessageAsync(message, options) {
144
+ const fullMessage = {
145
+ ...message,
146
+ id: this.generateId(),
147
+ protocol: PROTOCOL_ID,
148
+ };
149
+ return this.execute(async () => {
150
+ // 1. Encode request (validates args)
151
+ const encoded = this.codec.encodeRequest(fullMessage);
152
+ // 2. Send via transport
153
+ const responseStr = await this.transport.send(encoded, options?.timeoutMs ?? this.defaultTimeoutMs, options?.signal);
154
+ // 3. Decode response with Arrow support
155
+ return this.codec.decodeResponseAsync(responseStr);
156
+ }, options);
157
+ }
158
+ /**
159
+ * Generate a unique request ID.
160
+ *
161
+ * Returns a monotonically increasing integer that ensures uniqueness
162
+ * within a process lifetime.
163
+ */
164
+ generateId() {
165
+ return ++this.requestId;
166
+ }
167
+ // ===========================================================================
168
+ // RUNTIME EXECUTION INTERFACE
169
+ // ===========================================================================
170
+ /**
171
+ * Call a Python function.
172
+ *
173
+ * @param module - Python module path (e.g., 'numpy', 'mypackage.submodule')
174
+ * @param functionName - Function name to call
175
+ * @param args - Positional arguments
176
+ * @param kwargs - Keyword arguments
177
+ * @returns The function result
178
+ */
179
+ async call(module, functionName, args, kwargs) {
180
+ return this.sendMessageAsync({
181
+ method: 'call',
182
+ params: {
183
+ module,
184
+ functionName,
185
+ args,
186
+ kwargs,
187
+ },
188
+ });
189
+ }
190
+ /**
191
+ * Instantiate a Python class.
192
+ *
193
+ * @param module - Python module path containing the class
194
+ * @param className - Class name to instantiate
195
+ * @param args - Positional constructor arguments
196
+ * @param kwargs - Keyword constructor arguments
197
+ * @returns A handle to the created instance
198
+ */
199
+ async instantiate(module, className, args, kwargs) {
200
+ return this.sendMessageAsync({
201
+ method: 'instantiate',
202
+ params: {
203
+ module,
204
+ className,
205
+ args,
206
+ kwargs,
207
+ },
208
+ });
209
+ }
210
+ /**
211
+ * Call a method on a Python instance.
212
+ *
213
+ * @param handle - Instance handle returned from instantiate()
214
+ * @param methodName - Method name to call
215
+ * @param args - Positional arguments
216
+ * @param kwargs - Keyword arguments
217
+ * @returns The method result
218
+ */
219
+ async callMethod(handle, methodName, args, kwargs) {
220
+ return this.sendMessageAsync({
221
+ method: 'call_method',
222
+ params: {
223
+ handle,
224
+ methodName,
225
+ args,
226
+ kwargs,
227
+ },
228
+ });
229
+ }
230
+ /**
231
+ * Dispose a Python instance.
232
+ *
233
+ * Releases the instance handle on the Python side, allowing
234
+ * the object to be garbage collected.
235
+ *
236
+ * @param handle - Instance handle to dispose
237
+ */
238
+ async disposeInstance(handle) {
239
+ await this.sendMessageAsync({
240
+ method: 'dispose_instance',
241
+ params: {
242
+ handle,
243
+ },
244
+ });
245
+ }
246
+ }
247
+ //# sourceMappingURL=bridge-protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-protocol.js","sourceRoot":"","sources":["../../src/runtime/bridge-protocol.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Disposable interface for resources that need cleanup.
3
+ *
4
+ * This interface enables the BoundedContext resource tracking system
5
+ * to automatically dispose of owned resources when the context is disposed.
6
+ */
7
+ /**
8
+ * Interface for resources that require explicit cleanup.
9
+ */
10
+ export interface Disposable {
11
+ /**
12
+ * Release resources held by this object.
13
+ * This method should be idempotent (safe to call multiple times).
14
+ */
15
+ dispose(): Promise<void>;
16
+ }
17
+ /**
18
+ * Type guard to check if a value implements the Disposable interface.
19
+ *
20
+ * @param value - The value to check
21
+ * @returns True if the value has a dispose method
22
+ */
23
+ export declare function isDisposable(value: unknown): value is Disposable;
24
+ /**
25
+ * Safely dispose a value if it implements Disposable.
26
+ * Does nothing if the value is not disposable.
27
+ *
28
+ * @param value - The value to dispose
29
+ * @returns Promise that resolves when disposal is complete
30
+ */
31
+ export declare function safeDispose(value: unknown): Promise<void>;
32
+ /**
33
+ * Dispose multiple resources, collecting any errors.
34
+ * All resources will be attempted even if some fail.
35
+ *
36
+ * @param resources - Iterable of Disposable resources
37
+ * @returns Array of errors that occurred during disposal (empty if all succeeded)
38
+ */
39
+ export declare function disposeAll(resources: Iterable<Disposable>): Promise<Error[]>;
40
+ //# sourceMappingURL=disposable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"disposable.d.ts","sourceRoot":"","sources":["../../src/runtime/disposable.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB;;;OAGG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAMhE;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAI/D;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,SAAS,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAYlF"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Disposable interface for resources that need cleanup.
3
+ *
4
+ * This interface enables the BoundedContext resource tracking system
5
+ * to automatically dispose of owned resources when the context is disposed.
6
+ */
7
+ /**
8
+ * Type guard to check if a value implements the Disposable interface.
9
+ *
10
+ * @param value - The value to check
11
+ * @returns True if the value has a dispose method
12
+ */
13
+ export function isDisposable(value) {
14
+ return (value !== null &&
15
+ typeof value === 'object' &&
16
+ typeof value.dispose === 'function');
17
+ }
18
+ /**
19
+ * Safely dispose a value if it implements Disposable.
20
+ * Does nothing if the value is not disposable.
21
+ *
22
+ * @param value - The value to dispose
23
+ * @returns Promise that resolves when disposal is complete
24
+ */
25
+ export async function safeDispose(value) {
26
+ if (isDisposable(value)) {
27
+ await value.dispose();
28
+ }
29
+ }
30
+ /**
31
+ * Dispose multiple resources, collecting any errors.
32
+ * All resources will be attempted even if some fail.
33
+ *
34
+ * @param resources - Iterable of Disposable resources
35
+ * @returns Array of errors that occurred during disposal (empty if all succeeded)
36
+ */
37
+ export async function disposeAll(resources) {
38
+ const errors = [];
39
+ for (const resource of resources) {
40
+ try {
41
+ await resource.dispose();
42
+ }
43
+ catch (e) {
44
+ errors.push(e instanceof Error ? e : new Error(String(e)));
45
+ }
46
+ }
47
+ return errors;
48
+ }
49
+ //# sourceMappingURL=disposable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"disposable.js","sourceRoot":"","sources":["../../src/runtime/disposable.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,OAAO,CACL,KAAK,KAAK,IAAI;QACd,OAAO,KAAK,KAAK,QAAQ;QACzB,OAAQ,KAAoB,CAAC,OAAO,KAAK,UAAU,CACpD,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,KAAc;IAC9C,IAAI,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC;IACxB,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,SAA+B;IAC9D,MAAM,MAAM,GAAY,EAAE,CAAC;IAE3B,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,91 @@
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
+ import type { Transport } from './transport.js';
10
+ /**
11
+ * Configuration options for HttpIO transport.
12
+ */
13
+ export interface HttpIOOptions {
14
+ /** Base URL for the Python server (e.g., 'http://localhost:8000') */
15
+ baseURL: string;
16
+ /** Additional headers to include in each request */
17
+ headers?: Record<string, string>;
18
+ /** Default timeout in milliseconds. Default: 30000 (30 seconds) */
19
+ defaultTimeoutMs?: number;
20
+ }
21
+ /**
22
+ * HTTP-based transport for BridgeProtocol.
23
+ *
24
+ * This transport sends protocol messages as HTTP POST requests to a Python
25
+ * server. It is stateless - each request is independent and the transport
26
+ * is always ready after construction.
27
+ *
28
+ * Features:
29
+ * - Stateless design (init/dispose are no-ops)
30
+ * - Timeout handling via AbortController
31
+ * - External signal support for cancellation
32
+ * - Proper error classification
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * const transport = new HttpIO({ baseURL: 'http://localhost:8000' });
37
+ * await transport.init(); // No-op but follows Transport contract
38
+ *
39
+ * const response = await transport.send(
40
+ * JSON.stringify({ id: '1', type: 'call', module: 'math', functionName: 'sqrt', args: [16] }),
41
+ * 5000
42
+ * );
43
+ *
44
+ * await transport.dispose(); // Marks as disposed
45
+ * ```
46
+ */
47
+ export declare class HttpIO implements Transport {
48
+ private readonly baseURL;
49
+ private readonly headers;
50
+ private readonly defaultTimeoutMs;
51
+ private _isDisposed;
52
+ constructor(options: HttpIOOptions);
53
+ /**
54
+ * Initialize the transport.
55
+ *
56
+ * HTTP is stateless, so this is a no-op. The transport is ready
57
+ * immediately after construction.
58
+ */
59
+ init(): Promise<void>;
60
+ /**
61
+ * Dispose the transport.
62
+ *
63
+ * Marks the transport as disposed. Subsequent send() calls will fail.
64
+ */
65
+ dispose(): Promise<void>;
66
+ /**
67
+ * Whether the transport is ready to send messages.
68
+ *
69
+ * Returns true unless dispose() has been called.
70
+ */
71
+ get isReady(): boolean;
72
+ /**
73
+ * Send a message to the Python server and wait for the response.
74
+ *
75
+ * @param message - The JSON-encoded protocol message to send
76
+ * @param timeoutMs - Timeout in milliseconds (0 = no timeout, negative = use default)
77
+ * @param signal - Optional AbortSignal for external cancellation
78
+ * @returns The raw response string (JSON-encoded ProtocolResponse)
79
+ *
80
+ * @throws BridgeDisposedError if the transport has been disposed
81
+ * @throws BridgeTimeoutError if the operation times out or is aborted
82
+ * @throws BridgeExecutionError if the server returns a non-2xx status
83
+ * @throws BridgeProtocolError if the response cannot be read
84
+ */
85
+ send(message: string, timeoutMs: number, signal?: AbortSignal): Promise<string>;
86
+ /**
87
+ * Safely read response body as text, returning empty string on failure.
88
+ */
89
+ private safeReadText;
90
+ }
91
+ //# sourceMappingURL=http-io.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-io.d.ts","sourceRoot":"","sources":["../../src/runtime/http-io.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAMhD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAC;IAEhB,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC,mEAAmE;IACnE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,MAAO,YAAW,SAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IACjD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,EAAE,aAAa;IAclC;;;;;OAKG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B;;;;OAIG;IACH,IAAI,OAAO,IAAI,OAAO,CAErB;IAMD;;;;;;;;;;;;OAYG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAuGrF;;OAEG;YACW,YAAY;CAO3B"}
@@ -0,0 +1,195 @@
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
+ import { BridgeDisposedError, BridgeExecutionError, BridgeProtocolError, BridgeTimeoutError, } from './errors.js';
10
+ // =============================================================================
11
+ // HTTP TRANSPORT
12
+ // =============================================================================
13
+ /**
14
+ * HTTP-based transport for BridgeProtocol.
15
+ *
16
+ * This transport sends protocol messages as HTTP POST requests to a Python
17
+ * server. It is stateless - each request is independent and the transport
18
+ * is always ready after construction.
19
+ *
20
+ * Features:
21
+ * - Stateless design (init/dispose are no-ops)
22
+ * - Timeout handling via AbortController
23
+ * - External signal support for cancellation
24
+ * - Proper error classification
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const transport = new HttpIO({ baseURL: 'http://localhost:8000' });
29
+ * await transport.init(); // No-op but follows Transport contract
30
+ *
31
+ * const response = await transport.send(
32
+ * JSON.stringify({ id: '1', type: 'call', module: 'math', functionName: 'sqrt', args: [16] }),
33
+ * 5000
34
+ * );
35
+ *
36
+ * await transport.dispose(); // Marks as disposed
37
+ * ```
38
+ */
39
+ export class HttpIO {
40
+ baseURL;
41
+ headers;
42
+ defaultTimeoutMs;
43
+ _isDisposed = false;
44
+ constructor(options) {
45
+ // Normalize URL - remove trailing slash
46
+ this.baseURL = options.baseURL.replace(/\/$/, '');
47
+ this.headers = {
48
+ 'Content-Type': 'application/json',
49
+ ...options.headers,
50
+ };
51
+ this.defaultTimeoutMs = options.defaultTimeoutMs ?? 30000;
52
+ }
53
+ // ===========================================================================
54
+ // LIFECYCLE
55
+ // ===========================================================================
56
+ /**
57
+ * Initialize the transport.
58
+ *
59
+ * HTTP is stateless, so this is a no-op. The transport is ready
60
+ * immediately after construction.
61
+ */
62
+ async init() {
63
+ // HTTP is stateless - nothing to initialize
64
+ }
65
+ /**
66
+ * Dispose the transport.
67
+ *
68
+ * Marks the transport as disposed. Subsequent send() calls will fail.
69
+ */
70
+ async dispose() {
71
+ this._isDisposed = true;
72
+ }
73
+ /**
74
+ * Whether the transport is ready to send messages.
75
+ *
76
+ * Returns true unless dispose() has been called.
77
+ */
78
+ get isReady() {
79
+ return !this._isDisposed;
80
+ }
81
+ // ===========================================================================
82
+ // SEND
83
+ // ===========================================================================
84
+ /**
85
+ * Send a message to the Python server and wait for the response.
86
+ *
87
+ * @param message - The JSON-encoded protocol message to send
88
+ * @param timeoutMs - Timeout in milliseconds (0 = no timeout, negative = use default)
89
+ * @param signal - Optional AbortSignal for external cancellation
90
+ * @returns The raw response string (JSON-encoded ProtocolResponse)
91
+ *
92
+ * @throws BridgeDisposedError if the transport has been disposed
93
+ * @throws BridgeTimeoutError if the operation times out or is aborted
94
+ * @throws BridgeExecutionError if the server returns a non-2xx status
95
+ * @throws BridgeProtocolError if the response cannot be read
96
+ */
97
+ async send(message, timeoutMs, signal) {
98
+ if (this._isDisposed) {
99
+ throw new BridgeDisposedError('Transport has been disposed');
100
+ }
101
+ // Determine effective timeout
102
+ // 0 = no timeout (per interface contract), negative = use default
103
+ const effectiveTimeout = timeoutMs === 0 ? 0 : timeoutMs > 0 ? timeoutMs : this.defaultTimeoutMs;
104
+ // Create abort controller for timeout
105
+ const controller = new AbortController();
106
+ let timeoutId;
107
+ // Set up timeout if enabled
108
+ if (effectiveTimeout > 0) {
109
+ timeoutId = setTimeout(() => {
110
+ controller.abort();
111
+ }, effectiveTimeout);
112
+ }
113
+ // Handle external signal - abort our controller when external aborts
114
+ const externalAbortHandler = () => {
115
+ controller.abort();
116
+ };
117
+ if (signal) {
118
+ // Check if already aborted
119
+ if (signal.aborted) {
120
+ if (timeoutId !== undefined) {
121
+ clearTimeout(timeoutId);
122
+ }
123
+ throw new BridgeTimeoutError('Operation aborted');
124
+ }
125
+ signal.addEventListener('abort', externalAbortHandler, { once: true });
126
+ }
127
+ try {
128
+ const response = await fetch(this.baseURL, {
129
+ method: 'POST',
130
+ headers: this.headers,
131
+ body: message,
132
+ signal: controller.signal,
133
+ });
134
+ // Handle non-2xx status codes
135
+ if (!response.ok) {
136
+ const errorBody = await this.safeReadText(response);
137
+ throw new BridgeExecutionError(`HTTP ${response.status}: ${errorBody || response.statusText}`, { code: `HTTP_${response.status}` });
138
+ }
139
+ // Read response body
140
+ const responseText = await response.text();
141
+ return responseText;
142
+ }
143
+ catch (error) {
144
+ // Handle abort errors (timeout or external signal)
145
+ if (error instanceof Error && error.name === 'AbortError') {
146
+ // Determine if it was timeout or external abort
147
+ if (signal?.aborted) {
148
+ throw new BridgeTimeoutError('Operation aborted');
149
+ }
150
+ throw new BridgeTimeoutError(`Request timed out after ${effectiveTimeout}ms`);
151
+ }
152
+ // Re-throw bridge errors as-is
153
+ if (error instanceof BridgeTimeoutError ||
154
+ error instanceof BridgeExecutionError ||
155
+ error instanceof BridgeProtocolError) {
156
+ throw error;
157
+ }
158
+ // Wrap network errors
159
+ if (error instanceof TypeError) {
160
+ // fetch throws TypeError for network failures
161
+ throw new BridgeExecutionError(`Network error: ${error.message}`, { cause: error });
162
+ }
163
+ // Wrap unknown errors
164
+ const errorMessage = error instanceof Error ? error.message : String(error);
165
+ throw new BridgeExecutionError(`Request failed: ${errorMessage}`, {
166
+ cause: error instanceof Error ? error : undefined,
167
+ });
168
+ }
169
+ finally {
170
+ // Clean up timeout
171
+ if (timeoutId !== undefined) {
172
+ clearTimeout(timeoutId);
173
+ }
174
+ // Clean up external signal listener
175
+ if (signal) {
176
+ signal.removeEventListener('abort', externalAbortHandler);
177
+ }
178
+ }
179
+ }
180
+ // ===========================================================================
181
+ // HELPERS
182
+ // ===========================================================================
183
+ /**
184
+ * Safely read response body as text, returning empty string on failure.
185
+ */
186
+ async safeReadText(response) {
187
+ try {
188
+ return await response.text();
189
+ }
190
+ catch {
191
+ return '';
192
+ }
193
+ }
194
+ }
195
+ //# sourceMappingURL=http-io.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-io.js","sourceRoot":"","sources":["../../src/runtime/http-io.ts"],"names":[],"mappings":"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"}