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.
Files changed (163) hide show
  1. package/README.md +65 -31
  2. package/dist/cli.js +49 -2
  3. package/dist/cli.js.map +1 -1
  4. package/dist/config/index.d.ts +11 -7
  5. package/dist/config/index.d.ts.map +1 -1
  6. package/dist/config/index.js +43 -7
  7. package/dist/config/index.js.map +1 -1
  8. package/dist/core/analyzer.d.ts +1 -0
  9. package/dist/core/analyzer.d.ts.map +1 -1
  10. package/dist/core/analyzer.js +47 -12
  11. package/dist/core/analyzer.js.map +1 -1
  12. package/dist/core/annotation-parser.d.ts +8 -0
  13. package/dist/core/annotation-parser.d.ts.map +1 -0
  14. package/dist/core/annotation-parser.js +409 -0
  15. package/dist/core/annotation-parser.js.map +1 -0
  16. package/dist/core/discovery.d.ts +1 -0
  17. package/dist/core/discovery.d.ts.map +1 -1
  18. package/dist/core/discovery.js +9 -9
  19. package/dist/core/discovery.js.map +1 -1
  20. package/dist/core/generator.d.ts +9 -1
  21. package/dist/core/generator.d.ts.map +1 -1
  22. package/dist/core/generator.js +738 -104
  23. package/dist/core/generator.js.map +1 -1
  24. package/dist/core/mapper.d.ts +9 -7
  25. package/dist/core/mapper.d.ts.map +1 -1
  26. package/dist/core/mapper.js +129 -44
  27. package/dist/core/mapper.js.map +1 -1
  28. package/dist/index.d.ts +23 -7
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +23 -3
  31. package/dist/index.js.map +1 -1
  32. package/dist/runtime/base.d.ts +17 -7
  33. package/dist/runtime/base.d.ts.map +1 -1
  34. package/dist/runtime/base.js +18 -1
  35. package/dist/runtime/base.js.map +1 -1
  36. package/dist/runtime/bounded-context.d.ts +252 -0
  37. package/dist/runtime/bounded-context.d.ts.map +1 -0
  38. package/dist/runtime/bounded-context.js +454 -0
  39. package/dist/runtime/bounded-context.js.map +1 -0
  40. package/dist/runtime/bridge-core.d.ts +1 -0
  41. package/dist/runtime/bridge-core.d.ts.map +1 -1
  42. package/dist/runtime/bridge-core.js +52 -17
  43. package/dist/runtime/bridge-core.js.map +1 -1
  44. package/dist/runtime/bridge-protocol.d.ts +184 -0
  45. package/dist/runtime/bridge-protocol.d.ts.map +1 -0
  46. package/dist/runtime/bridge-protocol.js +344 -0
  47. package/dist/runtime/bridge-protocol.js.map +1 -0
  48. package/dist/runtime/disposable.d.ts +40 -0
  49. package/dist/runtime/disposable.d.ts.map +1 -0
  50. package/dist/runtime/disposable.js +49 -0
  51. package/dist/runtime/disposable.js.map +1 -0
  52. package/dist/runtime/errors.d.ts +10 -0
  53. package/dist/runtime/errors.d.ts.map +1 -1
  54. package/dist/runtime/errors.js +9 -0
  55. package/dist/runtime/errors.js.map +1 -1
  56. package/dist/runtime/http-io.d.ts +91 -0
  57. package/dist/runtime/http-io.d.ts.map +1 -0
  58. package/dist/runtime/http-io.js +195 -0
  59. package/dist/runtime/http-io.js.map +1 -0
  60. package/dist/runtime/http.d.ts +48 -13
  61. package/dist/runtime/http.d.ts.map +1 -1
  62. package/dist/runtime/http.js +60 -73
  63. package/dist/runtime/http.js.map +1 -1
  64. package/dist/runtime/node.d.ts +97 -130
  65. package/dist/runtime/node.d.ts.map +1 -1
  66. package/dist/runtime/node.js +392 -521
  67. package/dist/runtime/node.js.map +1 -1
  68. package/dist/runtime/optimized-node.d.ts +1 -1
  69. package/dist/runtime/optimized-node.d.ts.map +1 -1
  70. package/dist/runtime/optimized-node.js +1 -1
  71. package/dist/runtime/optimized-node.js.map +1 -1
  72. package/dist/runtime/pooled-transport.d.ts +135 -0
  73. package/dist/runtime/pooled-transport.d.ts.map +1 -0
  74. package/dist/runtime/pooled-transport.js +177 -0
  75. package/dist/runtime/pooled-transport.js.map +1 -0
  76. package/dist/runtime/process-io.d.ts +209 -0
  77. package/dist/runtime/process-io.d.ts.map +1 -0
  78. package/dist/runtime/process-io.js +729 -0
  79. package/dist/runtime/process-io.js.map +1 -0
  80. package/dist/runtime/pyodide-io.d.ts +154 -0
  81. package/dist/runtime/pyodide-io.d.ts.map +1 -0
  82. package/dist/runtime/pyodide-io.js +459 -0
  83. package/dist/runtime/pyodide-io.js.map +1 -0
  84. package/dist/runtime/pyodide.d.ts +51 -19
  85. package/dist/runtime/pyodide.d.ts.map +1 -1
  86. package/dist/runtime/pyodide.js +57 -186
  87. package/dist/runtime/pyodide.js.map +1 -1
  88. package/dist/runtime/safe-codec.d.ts +103 -0
  89. package/dist/runtime/safe-codec.d.ts.map +1 -0
  90. package/dist/runtime/safe-codec.js +519 -0
  91. package/dist/runtime/safe-codec.js.map +1 -0
  92. package/dist/runtime/transport.d.ts +191 -0
  93. package/dist/runtime/transport.d.ts.map +1 -0
  94. package/dist/runtime/transport.js +86 -0
  95. package/dist/runtime/transport.js.map +1 -0
  96. package/dist/runtime/validators.d.ts +120 -0
  97. package/dist/runtime/validators.d.ts.map +1 -0
  98. package/dist/runtime/validators.js +229 -0
  99. package/dist/runtime/validators.js.map +1 -0
  100. package/dist/runtime/worker-pool.d.ts +208 -0
  101. package/dist/runtime/worker-pool.d.ts.map +1 -0
  102. package/dist/runtime/worker-pool.js +419 -0
  103. package/dist/runtime/worker-pool.js.map +1 -0
  104. package/dist/types/index.d.ts +51 -1
  105. package/dist/types/index.d.ts.map +1 -1
  106. package/dist/tywrap.d.ts.map +1 -1
  107. package/dist/tywrap.js +227 -328
  108. package/dist/tywrap.js.map +1 -1
  109. package/dist/utils/cache.d.ts.map +1 -1
  110. package/dist/utils/cache.js +53 -5
  111. package/dist/utils/cache.js.map +1 -1
  112. package/dist/utils/codec.d.ts.map +1 -1
  113. package/dist/utils/codec.js +114 -1
  114. package/dist/utils/codec.js.map +1 -1
  115. package/dist/utils/ir-cache.d.ts +28 -0
  116. package/dist/utils/ir-cache.d.ts.map +1 -0
  117. package/dist/utils/ir-cache.js +29 -0
  118. package/dist/utils/ir-cache.js.map +1 -0
  119. package/dist/utils/parallel-processor.d.ts +1 -1
  120. package/dist/utils/parallel-processor.d.ts.map +1 -1
  121. package/dist/utils/parallel-processor.js +111 -40
  122. package/dist/utils/parallel-processor.js.map +1 -1
  123. package/dist/utils/runtime.d.ts +5 -1
  124. package/dist/utils/runtime.d.ts.map +1 -1
  125. package/dist/utils/runtime.js +112 -18
  126. package/dist/utils/runtime.js.map +1 -1
  127. package/package.json +40 -18
  128. package/runtime/python_bridge.py +108 -15
  129. package/runtime/safe_codec.py +344 -0
  130. package/src/cli.ts +61 -2
  131. package/src/config/index.ts +63 -17
  132. package/src/core/analyzer.ts +52 -15
  133. package/src/core/annotation-parser.ts +500 -0
  134. package/src/core/discovery.ts +26 -12
  135. package/src/core/generator.ts +956 -116
  136. package/src/core/mapper.ts +158 -51
  137. package/src/index.ts +78 -6
  138. package/src/runtime/base.ts +18 -26
  139. package/src/runtime/bounded-context.ts +608 -0
  140. package/src/runtime/bridge-core.ts +58 -18
  141. package/src/runtime/bridge-protocol.ts +483 -0
  142. package/src/runtime/disposable.ts +65 -0
  143. package/src/runtime/errors.ts +14 -0
  144. package/src/runtime/http-io.ts +245 -0
  145. package/src/runtime/http.ts +76 -115
  146. package/src/runtime/node.ts +563 -676
  147. package/src/runtime/optimized-node.ts +4 -1
  148. package/src/runtime/pooled-transport.ts +263 -0
  149. package/src/runtime/process-io.ts +930 -0
  150. package/src/runtime/pyodide-io.ts +551 -0
  151. package/src/runtime/pyodide.ts +75 -215
  152. package/src/runtime/safe-codec.ts +651 -0
  153. package/src/runtime/transport.ts +278 -0
  154. package/src/runtime/validators.ts +252 -0
  155. package/src/runtime/worker-pool.ts +559 -0
  156. package/src/types/global.d.ts +11 -1
  157. package/src/types/index.ts +63 -0
  158. package/src/tywrap.ts +279 -358
  159. package/src/utils/cache.ts +59 -5
  160. package/src/utils/codec.ts +122 -1
  161. package/src/utils/ir-cache.ts +51 -0
  162. package/src/utils/parallel-processor.ts +119 -43
  163. 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
+ }
@@ -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 { decodeValueAsync } from '../utils/codec.js';
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
- import { RuntimeBridge } from './base.js';
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
- interface HttpInstantiatePayload {
23
- module: string;
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
- interface HttpCallMethodPayload {
30
- handle: string;
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
- interface HttpDisposePayload {
37
- handle: string;
32
+ /** Codec options for validation/serialization */
33
+ codec?: CodecOptions;
38
34
  }
39
35
 
40
- export class HttpBridge extends RuntimeBridge {
41
- private readonly baseURL: string;
42
- private readonly headers: Record<string, string>;
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
- async callMethod<T = unknown>(
75
- handle: string,
76
- methodName: string,
77
- args: unknown[],
78
- kwargs?: Record<string, unknown>
79
- ): Promise<T> {
80
- const payload: HttpCallMethodPayload = { handle, methodName, args, kwargs };
81
- const res = await this.post(`${this.baseURL}/call_method`, payload);
82
- return (await decodeValueAsync(res)) as T;
83
- }
84
-
85
- async disposeInstance(handle: string): Promise<void> {
86
- const payload: HttpDisposePayload = { handle };
87
- await this.post(`${this.baseURL}/dispose_instance`, payload);
88
- }
89
-
90
- async dispose(): Promise<void> {
91
- // stateless
92
- }
93
-
94
- private async post(url: string, body: unknown): Promise<unknown> {
95
- const controller = typeof AbortController !== 'undefined' ? new AbortController() : undefined;
96
- const timer = controller ? setTimeout(() => controller.abort(), this.timeoutMs) : undefined;
97
- try {
98
- const resp = await fetch(url, {
99
- method: 'POST',
100
- headers: this.headers,
101
- body: JSON.stringify(body),
102
- signal: controller?.signal,
103
- });
104
- if (!resp.ok) {
105
- const text = await safeText(resp);
106
- throw new Error(`HTTP ${resp.status}: ${text || resp.statusText}`);
107
- }
108
- const ct = resp.headers.get('content-type') ?? '';
109
- if (ct.includes('application/json')) {
110
- return (await resp.json()) as unknown;
111
- }
112
- const text = await resp.text();
113
- try {
114
- return JSON.parse(text) as unknown;
115
- } catch {
116
- return text as unknown;
117
- }
118
- } finally {
119
- if (timer) {
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 function safeText(resp: Response): Promise<string> {
127
- try {
128
- return await resp.text();
129
- } catch {
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
  }