tywrap 0.2.0 → 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.
- package/README.md +1 -1
- package/dist/index.d.ts +20 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -2
- 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-protocol.d.ts +167 -0
- package/dist/runtime/bridge-protocol.d.ts.map +1 -0
- package/dist/runtime/bridge-protocol.js +247 -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/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 +47 -13
- package/dist/runtime/http.d.ts.map +1 -1
- package/dist/runtime/http.js +55 -74
- 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 +256 -523
- package/dist/runtime/node.js.map +1 -1
- package/dist/runtime/pooled-transport.d.ts +131 -0
- package/dist/runtime/pooled-transport.d.ts.map +1 -0
- package/dist/runtime/pooled-transport.js +175 -0
- package/dist/runtime/pooled-transport.js.map +1 -0
- package/dist/runtime/process-io.d.ts +204 -0
- package/dist/runtime/process-io.d.ts.map +1 -0
- package/dist/runtime/process-io.js +695 -0
- package/dist/runtime/process-io.js.map +1 -0
- package/dist/runtime/pyodide-io.d.ts +155 -0
- package/dist/runtime/pyodide-io.d.ts.map +1 -0
- package/dist/runtime/pyodide-io.js +397 -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 +81 -0
- package/dist/runtime/safe-codec.d.ts.map +1 -0
- package/dist/runtime/safe-codec.js +345 -0
- package/dist/runtime/safe-codec.js.map +1 -0
- package/dist/runtime/transport.d.ts +186 -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 +131 -0
- package/dist/runtime/validators.d.ts.map +1 -0
- package/dist/runtime/validators.js +219 -0
- package/dist/runtime/validators.js.map +1 -0
- package/dist/runtime/worker-pool.d.ts +196 -0
- package/dist/runtime/worker-pool.d.ts.map +1 -0
- package/dist/runtime/worker-pool.js +371 -0
- package/dist/runtime/worker-pool.js.map +1 -0
- package/dist/utils/codec.d.ts.map +1 -1
- package/dist/utils/codec.js +120 -1
- package/dist/utils/codec.js.map +1 -1
- package/package.json +2 -2
- package/runtime/python_bridge.py +30 -3
- package/runtime/safe_codec.py +344 -0
- package/src/index.ts +48 -5
- package/src/runtime/base.ts +18 -26
- package/src/runtime/bounded-context.ts +608 -0
- package/src/runtime/bridge-protocol.ts +319 -0
- package/src/runtime/disposable.ts +65 -0
- package/src/runtime/http-io.ts +244 -0
- package/src/runtime/http.ts +71 -117
- package/src/runtime/node.ts +358 -691
- package/src/runtime/pooled-transport.ts +252 -0
- package/src/runtime/process-io.ts +902 -0
- package/src/runtime/pyodide-io.ts +485 -0
- package/src/runtime/pyodide.ts +75 -215
- package/src/runtime/safe-codec.ts +443 -0
- package/src/runtime/transport.ts +273 -0
- package/src/runtime/validators.ts +241 -0
- package/src/runtime/worker-pool.ts +498 -0
- package/src/utils/codec.ts +126 -1
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BoundedContext - Unified abstraction for cross-boundary concerns.
|
|
3
|
+
*
|
|
4
|
+
* This base class provides consistent handling of:
|
|
5
|
+
* - Lifecycle management (init/dispose state machine)
|
|
6
|
+
* - Validation helpers
|
|
7
|
+
* - Error classification
|
|
8
|
+
* - Bounded execution (timeout, retry)
|
|
9
|
+
* - Resource ownership tracking
|
|
10
|
+
*
|
|
11
|
+
* All runtime bridges (NodeBridge, PyodideBridge, HttpBridge) extend this class.
|
|
12
|
+
*
|
|
13
|
+
* @see https://github.com/bbopen/tywrap/issues/149
|
|
14
|
+
*/
|
|
15
|
+
import { BridgeDisposedError, BridgeError, BridgeExecutionError, BridgeProtocolError, BridgeTimeoutError, } from './errors.js';
|
|
16
|
+
import { disposeAll } from './disposable.js';
|
|
17
|
+
import { assertFiniteNumber, assertNonEmptyString, assertPositive, assertString, ValidationError, } from './validators.js';
|
|
18
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
19
|
+
// BOUNDED CONTEXT BASE CLASS
|
|
20
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
21
|
+
/**
|
|
22
|
+
* Abstract base class for runtime bridges with unified boundary management.
|
|
23
|
+
*
|
|
24
|
+
* Provides lifecycle management, validation, error classification,
|
|
25
|
+
* bounded execution, and resource tracking.
|
|
26
|
+
*/
|
|
27
|
+
export class BoundedContext {
|
|
28
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
29
|
+
// STATE
|
|
30
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
31
|
+
_state = 'idle';
|
|
32
|
+
_initPromise;
|
|
33
|
+
_resources = new Set();
|
|
34
|
+
/**
|
|
35
|
+
* Current lifecycle state of the context.
|
|
36
|
+
*/
|
|
37
|
+
get state() {
|
|
38
|
+
return this._state;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Whether the context is ready for operations.
|
|
42
|
+
*/
|
|
43
|
+
get isReady() {
|
|
44
|
+
return this._state === 'ready';
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Whether the context has been disposed.
|
|
48
|
+
*/
|
|
49
|
+
get isDisposed() {
|
|
50
|
+
return this._state === 'disposed';
|
|
51
|
+
}
|
|
52
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
53
|
+
// LIFECYCLE (addresses #142, #148)
|
|
54
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
55
|
+
/**
|
|
56
|
+
* Initialize the context.
|
|
57
|
+
*
|
|
58
|
+
* This method:
|
|
59
|
+
* - Is idempotent (safe to call multiple times)
|
|
60
|
+
* - Deduplicates concurrent calls (returns the same promise)
|
|
61
|
+
* - Allows retry after failure (resets to idle state)
|
|
62
|
+
* - Throws BridgeDisposedError if already disposed
|
|
63
|
+
*
|
|
64
|
+
* @throws BridgeDisposedError if the context has been disposed
|
|
65
|
+
* @throws BridgeError subclass if initialization fails
|
|
66
|
+
*/
|
|
67
|
+
async init() {
|
|
68
|
+
// Guard against disposed or disposing states
|
|
69
|
+
if (this._state === 'disposed' || this._state === 'disposing') {
|
|
70
|
+
throw new BridgeDisposedError('Context has been disposed');
|
|
71
|
+
}
|
|
72
|
+
if (this._state === 'ready') {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (this._initPromise) {
|
|
76
|
+
return this._initPromise;
|
|
77
|
+
}
|
|
78
|
+
this._state = 'initializing';
|
|
79
|
+
this._initPromise = this.doInit()
|
|
80
|
+
.then(() => {
|
|
81
|
+
// Guard against dispose() being called during init
|
|
82
|
+
if (this._state === 'initializing') {
|
|
83
|
+
this._state = 'ready';
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
.catch(err => {
|
|
87
|
+
// Only allow retry by resetting to idle if still initializing
|
|
88
|
+
// (don't revive a disposed/disposing context)
|
|
89
|
+
if (this._state === 'initializing') {
|
|
90
|
+
this._state = 'idle';
|
|
91
|
+
this._initPromise = undefined;
|
|
92
|
+
}
|
|
93
|
+
throw this.classifyError(err);
|
|
94
|
+
});
|
|
95
|
+
return this._initPromise;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Dispose the context and all tracked resources.
|
|
99
|
+
*
|
|
100
|
+
* This method:
|
|
101
|
+
* - Is idempotent (safe to call multiple times)
|
|
102
|
+
* - Disposes all tracked resources before calling doDispose()
|
|
103
|
+
* - Collects errors from resource disposal and reports them
|
|
104
|
+
* - Transitions to disposed state even if disposal errors occur
|
|
105
|
+
*
|
|
106
|
+
* @throws AggregateError if multiple disposal errors occur
|
|
107
|
+
* @throws Error if a single disposal error occurs
|
|
108
|
+
*/
|
|
109
|
+
async dispose() {
|
|
110
|
+
if (this._state === 'disposed') {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (this._state === 'disposing') {
|
|
114
|
+
// Another dispose is in progress; don't overlap
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
this._state = 'disposing';
|
|
118
|
+
// Dispose all tracked resources
|
|
119
|
+
const resourceErrors = await disposeAll(this._resources);
|
|
120
|
+
this._resources.clear();
|
|
121
|
+
// Call subclass dispose logic
|
|
122
|
+
let doDisposeError;
|
|
123
|
+
try {
|
|
124
|
+
await this.doDispose();
|
|
125
|
+
}
|
|
126
|
+
catch (e) {
|
|
127
|
+
doDisposeError = e instanceof Error ? e : new Error(String(e));
|
|
128
|
+
}
|
|
129
|
+
// Always transition to disposed
|
|
130
|
+
this._state = 'disposed';
|
|
131
|
+
this._initPromise = undefined;
|
|
132
|
+
// Report any errors
|
|
133
|
+
const allErrors = doDisposeError ? [...resourceErrors, doDisposeError] : resourceErrors;
|
|
134
|
+
if (allErrors.length === 1) {
|
|
135
|
+
throw allErrors[0];
|
|
136
|
+
}
|
|
137
|
+
if (allErrors.length > 1) {
|
|
138
|
+
throw new AggregateError(allErrors, 'Multiple errors during dispose');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
142
|
+
// VALIDATION (addresses #141, #145, #146, #147)
|
|
143
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
144
|
+
/**
|
|
145
|
+
* Validate that a value is a finite number.
|
|
146
|
+
* Wraps validation errors in appropriate BridgeError.
|
|
147
|
+
*
|
|
148
|
+
* @param value - The value to validate
|
|
149
|
+
* @param name - Parameter name for error messages
|
|
150
|
+
* @returns The validated number
|
|
151
|
+
* @throws BridgeError if validation fails
|
|
152
|
+
*/
|
|
153
|
+
validateNumeric(value, name) {
|
|
154
|
+
try {
|
|
155
|
+
return assertFiniteNumber(value, name);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
throw this.classifyError(error);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Validate that a value is a positive number.
|
|
163
|
+
* Wraps validation errors in appropriate BridgeError.
|
|
164
|
+
*
|
|
165
|
+
* @param value - The value to validate
|
|
166
|
+
* @param name - Parameter name for error messages
|
|
167
|
+
* @returns The validated number
|
|
168
|
+
* @throws BridgeError if validation fails
|
|
169
|
+
*/
|
|
170
|
+
validatePositive(value, name) {
|
|
171
|
+
try {
|
|
172
|
+
return assertPositive(value, name);
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
throw this.classifyError(error);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Validate that a value is a string.
|
|
180
|
+
* Wraps validation errors in appropriate BridgeError.
|
|
181
|
+
*
|
|
182
|
+
* @param value - The value to validate
|
|
183
|
+
* @param name - Parameter name for error messages
|
|
184
|
+
* @returns The validated string
|
|
185
|
+
* @throws BridgeError if validation fails
|
|
186
|
+
*/
|
|
187
|
+
validateString(value, name) {
|
|
188
|
+
try {
|
|
189
|
+
return assertString(value, name);
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
throw this.classifyError(error);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Validate that a value is a non-empty string.
|
|
197
|
+
* Wraps validation errors in appropriate BridgeError.
|
|
198
|
+
*
|
|
199
|
+
* @param value - The value to validate
|
|
200
|
+
* @param name - Parameter name for error messages
|
|
201
|
+
* @returns The validated string
|
|
202
|
+
* @throws BridgeError if validation fails
|
|
203
|
+
*/
|
|
204
|
+
validateNonEmptyString(value, name) {
|
|
205
|
+
try {
|
|
206
|
+
return assertNonEmptyString(value, name);
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
throw this.classifyError(error);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Validate input before processing.
|
|
214
|
+
* Override in subclasses for domain-specific input validation.
|
|
215
|
+
*
|
|
216
|
+
* @param input - The input to validate
|
|
217
|
+
* @returns The validated input
|
|
218
|
+
*/
|
|
219
|
+
validateInput(input) {
|
|
220
|
+
return input;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Validate output before returning.
|
|
224
|
+
* Override in subclasses for domain-specific output validation.
|
|
225
|
+
*
|
|
226
|
+
* @param output - The output to validate
|
|
227
|
+
* @returns The validated output
|
|
228
|
+
*/
|
|
229
|
+
validateOutput(output) {
|
|
230
|
+
return output;
|
|
231
|
+
}
|
|
232
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
233
|
+
// ERROR CLASSIFICATION (addresses #143)
|
|
234
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
235
|
+
/**
|
|
236
|
+
* Classify an error into the appropriate BridgeError subtype.
|
|
237
|
+
*
|
|
238
|
+
* This method:
|
|
239
|
+
* - Passes through existing BridgeError instances
|
|
240
|
+
* - Uses context state to determine appropriate error type
|
|
241
|
+
* - Pattern-matches error messages for classification
|
|
242
|
+
*
|
|
243
|
+
* @param error - The error to classify
|
|
244
|
+
* @returns A BridgeError instance
|
|
245
|
+
*/
|
|
246
|
+
classifyError(error) {
|
|
247
|
+
// Pass through existing BridgeErrors
|
|
248
|
+
if (error instanceof BridgeError) {
|
|
249
|
+
return error;
|
|
250
|
+
}
|
|
251
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
252
|
+
const cause = error instanceof Error ? error : undefined;
|
|
253
|
+
// Context state takes precedence
|
|
254
|
+
if (this._state === 'disposed') {
|
|
255
|
+
return new BridgeDisposedError(message, { cause });
|
|
256
|
+
}
|
|
257
|
+
// Pattern matching for classification
|
|
258
|
+
const lowerMessage = message.toLowerCase();
|
|
259
|
+
// Timeout patterns
|
|
260
|
+
if (lowerMessage.includes('timeout') ||
|
|
261
|
+
lowerMessage.includes('etimedout') ||
|
|
262
|
+
lowerMessage.includes('timed out') ||
|
|
263
|
+
lowerMessage.includes('aborted')) {
|
|
264
|
+
return new BridgeTimeoutError(message, { cause });
|
|
265
|
+
}
|
|
266
|
+
// Protocol patterns
|
|
267
|
+
if (lowerMessage.includes('protocol') ||
|
|
268
|
+
lowerMessage.includes('invalid json') ||
|
|
269
|
+
lowerMessage.includes('parse error') ||
|
|
270
|
+
lowerMessage.includes('unexpected token') ||
|
|
271
|
+
lowerMessage.includes('not found') ||
|
|
272
|
+
error instanceof ValidationError) {
|
|
273
|
+
return new BridgeProtocolError(message, { cause });
|
|
274
|
+
}
|
|
275
|
+
// Default to execution error
|
|
276
|
+
return new BridgeExecutionError(message, { cause });
|
|
277
|
+
}
|
|
278
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
279
|
+
// BOUNDED EXECUTION (addresses #141, #147, #148)
|
|
280
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
281
|
+
/**
|
|
282
|
+
* Execute an operation with bounded execution guarantees.
|
|
283
|
+
*
|
|
284
|
+
* This method:
|
|
285
|
+
* - Auto-initializes if not ready
|
|
286
|
+
* - Enforces timeout limits
|
|
287
|
+
* - Supports retry on retryable errors
|
|
288
|
+
* - Respects abort signals
|
|
289
|
+
* - Validates results if a validator is provided
|
|
290
|
+
*
|
|
291
|
+
* @param operation - The async operation to execute
|
|
292
|
+
* @param options - Execution options (timeout, retries, etc.)
|
|
293
|
+
* @returns The operation result
|
|
294
|
+
* @throws BridgeTimeoutError if the operation times out
|
|
295
|
+
* @throws BridgeDisposedError if the context is disposed
|
|
296
|
+
* @throws BridgeError for other failures
|
|
297
|
+
*/
|
|
298
|
+
async execute(operation, options = {}) {
|
|
299
|
+
// Auto-initialize if needed
|
|
300
|
+
if (this._state !== 'ready') {
|
|
301
|
+
await this.init();
|
|
302
|
+
}
|
|
303
|
+
// Check disposed state after potential init
|
|
304
|
+
if (this._state === 'disposed') {
|
|
305
|
+
throw new BridgeDisposedError('Context disposed');
|
|
306
|
+
}
|
|
307
|
+
const { timeoutMs = 30000, retries = 0, retryDelayMs = 100, validate, signal } = options;
|
|
308
|
+
let lastError;
|
|
309
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
310
|
+
// Check for abort before each attempt
|
|
311
|
+
if (signal?.aborted) {
|
|
312
|
+
throw new BridgeTimeoutError('Operation aborted');
|
|
313
|
+
}
|
|
314
|
+
try {
|
|
315
|
+
const result = await this.withTimeout(operation(), timeoutMs, signal);
|
|
316
|
+
return validate ? validate(result) : result;
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
lastError = this.classifyError(error);
|
|
320
|
+
// Retry if appropriate
|
|
321
|
+
if (attempt < retries && this.isRetryable(lastError)) {
|
|
322
|
+
await this.delay(retryDelayMs * (attempt + 1));
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
throw lastError;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
// Should not reach here, but TypeScript doesn't know that
|
|
329
|
+
/* istanbul ignore next */
|
|
330
|
+
throw lastError ?? new BridgeExecutionError('Unexpected execution flow');
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Wrap a promise with a timeout.
|
|
334
|
+
*
|
|
335
|
+
* @param promise - The promise to wrap
|
|
336
|
+
* @param ms - Timeout in milliseconds (0 or negative disables timeout)
|
|
337
|
+
* @param signal - Optional abort signal
|
|
338
|
+
* @returns The promise result
|
|
339
|
+
* @throws BridgeTimeoutError if the timeout expires
|
|
340
|
+
*/
|
|
341
|
+
async withTimeout(promise, ms, signal) {
|
|
342
|
+
// Check if already aborted
|
|
343
|
+
if (signal?.aborted) {
|
|
344
|
+
throw new BridgeTimeoutError('Operation aborted');
|
|
345
|
+
}
|
|
346
|
+
// No timeout if ms is 0 or negative, but still honor abort signal
|
|
347
|
+
if (ms <= 0 || !Number.isFinite(ms)) {
|
|
348
|
+
if (!signal) {
|
|
349
|
+
return promise;
|
|
350
|
+
}
|
|
351
|
+
// Wrap promise to honor abort signal even without timeout
|
|
352
|
+
return new Promise((resolve, reject) => {
|
|
353
|
+
const abortHandler = () => {
|
|
354
|
+
reject(new BridgeTimeoutError('Operation aborted'));
|
|
355
|
+
};
|
|
356
|
+
signal.addEventListener('abort', abortHandler, { once: true });
|
|
357
|
+
promise
|
|
358
|
+
.then(result => {
|
|
359
|
+
signal.removeEventListener('abort', abortHandler);
|
|
360
|
+
resolve(result);
|
|
361
|
+
})
|
|
362
|
+
.catch(error => {
|
|
363
|
+
signal.removeEventListener('abort', abortHandler);
|
|
364
|
+
reject(error);
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
return new Promise((resolve, reject) => {
|
|
369
|
+
const timer = setTimeout(() => {
|
|
370
|
+
reject(new BridgeTimeoutError(`Operation timed out after ${ms}ms`));
|
|
371
|
+
}, ms);
|
|
372
|
+
// Handle external abort signal
|
|
373
|
+
const abortHandler = () => {
|
|
374
|
+
clearTimeout(timer);
|
|
375
|
+
reject(new BridgeTimeoutError('Operation aborted'));
|
|
376
|
+
};
|
|
377
|
+
if (signal) {
|
|
378
|
+
signal.addEventListener('abort', abortHandler, { once: true });
|
|
379
|
+
}
|
|
380
|
+
promise
|
|
381
|
+
.then(result => {
|
|
382
|
+
clearTimeout(timer);
|
|
383
|
+
signal?.removeEventListener('abort', abortHandler);
|
|
384
|
+
resolve(result);
|
|
385
|
+
})
|
|
386
|
+
.catch(error => {
|
|
387
|
+
clearTimeout(timer);
|
|
388
|
+
signal?.removeEventListener('abort', abortHandler);
|
|
389
|
+
reject(error);
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Determine if an error is retryable.
|
|
395
|
+
* Override in subclasses to customize retry logic.
|
|
396
|
+
*
|
|
397
|
+
* @param error - The error to check
|
|
398
|
+
* @returns True if the operation should be retried
|
|
399
|
+
*/
|
|
400
|
+
isRetryable(error) {
|
|
401
|
+
// Timeout and connection errors are typically retryable
|
|
402
|
+
if (error instanceof BridgeTimeoutError) {
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
const message = error.message.toLowerCase();
|
|
406
|
+
return (message.includes('econnreset') ||
|
|
407
|
+
message.includes('econnrefused') ||
|
|
408
|
+
message.includes('epipe') ||
|
|
409
|
+
message.includes('connection reset'));
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Delay for a specified duration.
|
|
413
|
+
*
|
|
414
|
+
* @param ms - Milliseconds to delay
|
|
415
|
+
*/
|
|
416
|
+
delay(ms) {
|
|
417
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
418
|
+
}
|
|
419
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
420
|
+
// RESOURCE OWNERSHIP (addresses #144, #148)
|
|
421
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
422
|
+
/**
|
|
423
|
+
* Track a disposable resource for automatic cleanup.
|
|
424
|
+
*
|
|
425
|
+
* When the context is disposed, all tracked resources will be
|
|
426
|
+
* disposed automatically.
|
|
427
|
+
*
|
|
428
|
+
* @param resource - The resource to track
|
|
429
|
+
* @returns The same resource (for chaining)
|
|
430
|
+
*/
|
|
431
|
+
trackResource(resource) {
|
|
432
|
+
this._resources.add(resource);
|
|
433
|
+
return resource;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Stop tracking a resource.
|
|
437
|
+
*
|
|
438
|
+
* Use this when a resource is disposed manually and should
|
|
439
|
+
* not be disposed again during context disposal.
|
|
440
|
+
*
|
|
441
|
+
* @param resource - The resource to untrack
|
|
442
|
+
* @returns True if the resource was being tracked
|
|
443
|
+
*/
|
|
444
|
+
untrackResource(resource) {
|
|
445
|
+
return this._resources.delete(resource);
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Number of currently tracked resources.
|
|
449
|
+
*/
|
|
450
|
+
get resourceCount() {
|
|
451
|
+
return this._resources.size;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
//# sourceMappingURL=bounded-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bounded-context.js","sourceRoot":"","sources":["../../src/runtime/bounded-context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,167 @@
|
|
|
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, type ExecuteOptions } from './bounded-context.js';
|
|
19
|
+
import { SafeCodec, type CodecOptions } from './safe-codec.js';
|
|
20
|
+
import { type Transport, type ProtocolMessage } from './transport.js';
|
|
21
|
+
/**
|
|
22
|
+
* Configuration options for BridgeProtocol.
|
|
23
|
+
*/
|
|
24
|
+
export interface BridgeProtocolOptions {
|
|
25
|
+
/** The transport to use for communication */
|
|
26
|
+
transport: Transport;
|
|
27
|
+
/** Codec options for validation/serialization */
|
|
28
|
+
codec?: CodecOptions;
|
|
29
|
+
/** Default timeout for operations in ms. Default: 30000 (30s) */
|
|
30
|
+
defaultTimeoutMs?: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* BridgeProtocol combines BoundedContext + SafeCodec + Transport
|
|
34
|
+
* into a unified abstraction for all JS<->Python communication.
|
|
35
|
+
*
|
|
36
|
+
* This class provides:
|
|
37
|
+
* - Automatic transport lifecycle management
|
|
38
|
+
* - Request encoding with guardrails (special float rejection, key validation)
|
|
39
|
+
* - Response decoding with Arrow support
|
|
40
|
+
* - Full RuntimeExecution interface implementation
|
|
41
|
+
*
|
|
42
|
+
* Subclasses should:
|
|
43
|
+
* 1. Create their transport in their constructor
|
|
44
|
+
* 2. Pass it to super() via BridgeProtocolOptions
|
|
45
|
+
* 3. Optionally override doInit() and doDispose() for additional setup/teardown
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* class NodeBridge extends BridgeProtocol {
|
|
50
|
+
* constructor(options: NodeBridgeOptions) {
|
|
51
|
+
* const transport = new ProcessIO(options);
|
|
52
|
+
* super({ transport, defaultTimeoutMs: options.timeout });
|
|
53
|
+
* }
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare class BridgeProtocol extends BoundedContext {
|
|
58
|
+
/** Codec instance for validation and serialization */
|
|
59
|
+
protected readonly codec: SafeCodec;
|
|
60
|
+
/** Transport instance for message passing */
|
|
61
|
+
protected readonly transport: Transport;
|
|
62
|
+
/** Default timeout for operations in milliseconds */
|
|
63
|
+
protected readonly defaultTimeoutMs: number;
|
|
64
|
+
/** Counter for generating unique request IDs */
|
|
65
|
+
private requestId;
|
|
66
|
+
/**
|
|
67
|
+
* Create a new BridgeProtocol instance.
|
|
68
|
+
*
|
|
69
|
+
* @param options - Configuration options including transport and codec settings
|
|
70
|
+
*/
|
|
71
|
+
constructor(options: BridgeProtocolOptions);
|
|
72
|
+
/**
|
|
73
|
+
* Initialize the protocol.
|
|
74
|
+
*
|
|
75
|
+
* Initializes the underlying transport. Subclasses can override this
|
|
76
|
+
* to add additional initialization logic, but must call super.doInit().
|
|
77
|
+
*/
|
|
78
|
+
protected doInit(): Promise<void>;
|
|
79
|
+
/**
|
|
80
|
+
* Dispose the protocol.
|
|
81
|
+
*
|
|
82
|
+
* The transport is tracked as a resource and will be disposed automatically
|
|
83
|
+
* by BoundedContext. Subclasses can override this to add additional cleanup,
|
|
84
|
+
* but should not need to dispose the transport manually.
|
|
85
|
+
*/
|
|
86
|
+
protected doDispose(): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Send a protocol message and receive a typed response.
|
|
89
|
+
*
|
|
90
|
+
* This method:
|
|
91
|
+
* 1. Generates a unique request ID
|
|
92
|
+
* 2. Encodes the request with validation (special float rejection, etc.)
|
|
93
|
+
* 3. Sends via transport with timeout/abort support
|
|
94
|
+
* 4. Decodes and validates the response
|
|
95
|
+
*
|
|
96
|
+
* @param message - The protocol message (without id field)
|
|
97
|
+
* @param options - Execution options (timeout, retries, validation)
|
|
98
|
+
* @returns The typed response from Python
|
|
99
|
+
*
|
|
100
|
+
* @throws BridgeProtocolError if encoding/decoding fails
|
|
101
|
+
* @throws BridgeExecutionError if Python returns an error
|
|
102
|
+
* @throws BridgeTimeoutError if the operation times out
|
|
103
|
+
*/
|
|
104
|
+
protected sendMessage<T>(message: Omit<ProtocolMessage, 'id' | 'protocol'>, options?: ExecuteOptions<T>): Promise<T>;
|
|
105
|
+
/**
|
|
106
|
+
* Async version that uses decodeResponseAsync for Arrow support.
|
|
107
|
+
*
|
|
108
|
+
* Use this method when the response may contain encoded DataFrames,
|
|
109
|
+
* ndarrays, or other Arrow-encoded data structures.
|
|
110
|
+
*
|
|
111
|
+
* @param message - The protocol message (without id field)
|
|
112
|
+
* @param options - Execution options (timeout, retries, validation)
|
|
113
|
+
* @returns The typed response from Python with Arrow decoding applied
|
|
114
|
+
*
|
|
115
|
+
* @throws BridgeProtocolError if encoding/decoding fails
|
|
116
|
+
* @throws BridgeExecutionError if Python returns an error
|
|
117
|
+
* @throws BridgeTimeoutError if the operation times out
|
|
118
|
+
*/
|
|
119
|
+
protected sendMessageAsync<T>(message: Omit<ProtocolMessage, 'id' | 'protocol'>, options?: ExecuteOptions<T>): Promise<T>;
|
|
120
|
+
/**
|
|
121
|
+
* Generate a unique request ID.
|
|
122
|
+
*
|
|
123
|
+
* Returns a monotonically increasing integer that ensures uniqueness
|
|
124
|
+
* within a process lifetime.
|
|
125
|
+
*/
|
|
126
|
+
private generateId;
|
|
127
|
+
/**
|
|
128
|
+
* Call a Python function.
|
|
129
|
+
*
|
|
130
|
+
* @param module - Python module path (e.g., 'numpy', 'mypackage.submodule')
|
|
131
|
+
* @param functionName - Function name to call
|
|
132
|
+
* @param args - Positional arguments
|
|
133
|
+
* @param kwargs - Keyword arguments
|
|
134
|
+
* @returns The function result
|
|
135
|
+
*/
|
|
136
|
+
call<T = unknown>(module: string, functionName: string, args: unknown[], kwargs?: Record<string, unknown>): Promise<T>;
|
|
137
|
+
/**
|
|
138
|
+
* Instantiate a Python class.
|
|
139
|
+
*
|
|
140
|
+
* @param module - Python module path containing the class
|
|
141
|
+
* @param className - Class name to instantiate
|
|
142
|
+
* @param args - Positional constructor arguments
|
|
143
|
+
* @param kwargs - Keyword constructor arguments
|
|
144
|
+
* @returns A handle to the created instance
|
|
145
|
+
*/
|
|
146
|
+
instantiate<T = unknown>(module: string, className: string, args: unknown[], kwargs?: Record<string, unknown>): Promise<T>;
|
|
147
|
+
/**
|
|
148
|
+
* Call a method on a Python instance.
|
|
149
|
+
*
|
|
150
|
+
* @param handle - Instance handle returned from instantiate()
|
|
151
|
+
* @param methodName - Method name to call
|
|
152
|
+
* @param args - Positional arguments
|
|
153
|
+
* @param kwargs - Keyword arguments
|
|
154
|
+
* @returns The method result
|
|
155
|
+
*/
|
|
156
|
+
callMethod<T = unknown>(handle: string, methodName: string, args: unknown[], kwargs?: Record<string, unknown>): Promise<T>;
|
|
157
|
+
/**
|
|
158
|
+
* Dispose a Python instance.
|
|
159
|
+
*
|
|
160
|
+
* Releases the instance handle on the Python side, allowing
|
|
161
|
+
* the object to be garbage collected.
|
|
162
|
+
*
|
|
163
|
+
* @param handle - Instance handle to dispose
|
|
164
|
+
*/
|
|
165
|
+
disposeInstance(handle: string): Promise<void>;
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=bridge-protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-protocol.d.ts","sourceRoot":"","sources":["../../src/runtime/bridge-protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAE,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAe,KAAK,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAMnF;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,6CAA6C;IAC7C,SAAS,EAAE,SAAS,CAAC;IAErB,iDAAiD;IACjD,KAAK,CAAC,EAAE,YAAY,CAAC;IAErB,iEAAiE;IACjE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAMD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,cAAe,SAAQ,cAAc;IAChD,sDAAsD;IACtD,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAEpC,6CAA6C;IAC7C,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAExC,qDAAqD;IACrD,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAE5C,gDAAgD;IAChD,OAAO,CAAC,SAAS,CAAK;IAEtB;;;;OAIG;gBACS,OAAO,EAAE,qBAAqB;IAc1C;;;;;OAKG;cACa,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAIvC;;;;;;OAMG;cACa,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAS1C;;;;;;;;;;;;;;;;OAgBG;cACa,WAAW,CAAC,CAAC,EAC3B,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,IAAI,GAAG,UAAU,CAAC,EACjD,OAAO,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,GAC1B,OAAO,CAAC,CAAC,CAAC;IAuBb;;;;;;;;;;;;;OAaG;cACa,gBAAgB,CAAC,CAAC,EAChC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,IAAI,GAAG,UAAU,CAAC,EACjD,OAAO,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,GAC1B,OAAO,CAAC,CAAC,CAAC;IAuBb;;;;;OAKG;IACH,OAAO,CAAC,UAAU;IAQlB;;;;;;;;OAQG;IACG,IAAI,CAAC,CAAC,GAAG,OAAO,EACpB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,OAAO,EAAE,EACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,CAAC,CAAC;IAYb;;;;;;;;OAQG;IACG,WAAW,CAAC,CAAC,GAAG,OAAO,EAC3B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,OAAO,EAAE,EACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,CAAC,CAAC;IAYb;;;;;;;;OAQG;IACG,UAAU,CAAC,CAAC,GAAG,OAAO,EAC1B,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,OAAO,EAAE,EACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,CAAC,CAAC;IAYb;;;;;;;OAOG;IACG,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAQrD"}
|