tywrap 0.3.0 → 0.5.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 (101) hide show
  1. package/README.md +40 -5
  2. package/dist/cli.js +24 -6
  3. package/dist/cli.js.map +1 -1
  4. package/dist/config/index.d.ts.map +1 -1
  5. package/dist/config/index.js +19 -13
  6. package/dist/config/index.js.map +1 -1
  7. package/dist/core/analyzer.d.ts.map +1 -1
  8. package/dist/core/analyzer.js +0 -1
  9. package/dist/core/analyzer.js.map +1 -1
  10. package/dist/dev.d.ts +57 -0
  11. package/dist/dev.d.ts.map +1 -0
  12. package/dist/dev.js +743 -0
  13. package/dist/dev.js.map +1 -0
  14. package/dist/index.d.ts +5 -4
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +6 -6
  17. package/dist/index.js.map +1 -1
  18. package/dist/runtime/base.d.ts +7 -7
  19. package/dist/runtime/base.js +8 -8
  20. package/dist/runtime/bounded-context.d.ts +15 -31
  21. package/dist/runtime/bounded-context.d.ts.map +1 -1
  22. package/dist/runtime/bounded-context.js +16 -15
  23. package/dist/runtime/bounded-context.js.map +1 -1
  24. package/dist/runtime/http.d.ts +24 -5
  25. package/dist/runtime/http.d.ts.map +1 -1
  26. package/dist/runtime/http.js +57 -12
  27. package/dist/runtime/http.js.map +1 -1
  28. package/dist/runtime/node.d.ts +33 -7
  29. package/dist/runtime/node.d.ts.map +1 -1
  30. package/dist/runtime/node.js +144 -120
  31. package/dist/runtime/node.js.map +1 -1
  32. package/dist/runtime/optimized-node.d.ts +5 -4
  33. package/dist/runtime/optimized-node.d.ts.map +1 -1
  34. package/dist/runtime/optimized-node.js +5 -4
  35. package/dist/runtime/optimized-node.js.map +1 -1
  36. package/dist/runtime/pooled-transport.d.ts +2 -18
  37. package/dist/runtime/pooled-transport.d.ts.map +1 -1
  38. package/dist/runtime/pooled-transport.js +2 -29
  39. package/dist/runtime/pooled-transport.js.map +1 -1
  40. package/dist/runtime/process-io.d.ts +2 -18
  41. package/dist/runtime/process-io.d.ts.map +1 -1
  42. package/dist/runtime/process-io.js +3 -30
  43. package/dist/runtime/process-io.js.map +1 -1
  44. package/dist/runtime/pyodide-bootstrap-core.generated.d.ts +12 -0
  45. package/dist/runtime/pyodide-bootstrap-core.generated.d.ts.map +1 -0
  46. package/dist/runtime/pyodide-bootstrap-core.generated.js +12 -0
  47. package/dist/runtime/pyodide-bootstrap-core.generated.js.map +1 -0
  48. package/dist/runtime/pyodide-io.d.ts +7 -37
  49. package/dist/runtime/pyodide-io.d.ts.map +1 -1
  50. package/dist/runtime/pyodide-io.js +90 -214
  51. package/dist/runtime/pyodide-io.js.map +1 -1
  52. package/dist/runtime/pyodide.d.ts +25 -5
  53. package/dist/runtime/pyodide.d.ts.map +1 -1
  54. package/dist/runtime/pyodide.js +59 -10
  55. package/dist/runtime/pyodide.js.map +1 -1
  56. package/dist/runtime/{bridge-protocol.d.ts → rpc-client.d.ts} +59 -46
  57. package/dist/runtime/rpc-client.d.ts.map +1 -0
  58. package/dist/runtime/{bridge-protocol.js → rpc-client.js} +60 -42
  59. package/dist/runtime/rpc-client.js.map +1 -0
  60. package/dist/runtime/worker-pool.d.ts +8 -20
  61. package/dist/runtime/worker-pool.d.ts.map +1 -1
  62. package/dist/runtime/worker-pool.js +58 -59
  63. package/dist/runtime/worker-pool.js.map +1 -1
  64. package/dist/types/index.d.ts +8 -10
  65. package/dist/types/index.d.ts.map +1 -1
  66. package/dist/tywrap.d.ts +6 -0
  67. package/dist/tywrap.d.ts.map +1 -1
  68. package/dist/tywrap.js +10 -3
  69. package/dist/tywrap.js.map +1 -1
  70. package/package.json +22 -18
  71. package/runtime/__pycache__/safe_codec.cpython-311.pyc +0 -0
  72. package/runtime/__pycache__/tywrap_bridge_core.cpython-311.pyc +0 -0
  73. package/runtime/python_bridge.py +85 -702
  74. package/runtime/safe_codec.py +10 -2
  75. package/runtime/tywrap_bridge_core.py +875 -0
  76. package/src/cli.ts +27 -6
  77. package/src/config/index.ts +28 -15
  78. package/src/core/analyzer.ts +1 -2
  79. package/src/dev.ts +983 -0
  80. package/src/index.ts +7 -7
  81. package/src/runtime/base.ts +8 -8
  82. package/src/runtime/bounded-context.ts +17 -56
  83. package/src/runtime/http.ts +85 -13
  84. package/src/runtime/node.ts +172 -178
  85. package/src/runtime/optimized-node.ts +5 -4
  86. package/src/runtime/pooled-transport.ts +2 -57
  87. package/src/runtime/process-io.ts +3 -55
  88. package/src/runtime/pyodide-bootstrap-core.generated.ts +12 -0
  89. package/src/runtime/pyodide-io.ts +92 -243
  90. package/src/runtime/pyodide.ts +87 -10
  91. package/src/runtime/{bridge-protocol.ts → rpc-client.ts} +76 -49
  92. package/src/runtime/worker-pool.ts +67 -88
  93. package/src/types/index.ts +20 -12
  94. package/src/tywrap.ts +17 -3
  95. package/dist/runtime/bridge-core.d.ts +0 -65
  96. package/dist/runtime/bridge-core.d.ts.map +0 -1
  97. package/dist/runtime/bridge-core.js +0 -379
  98. package/dist/runtime/bridge-core.js.map +0 -1
  99. package/dist/runtime/bridge-protocol.d.ts.map +0 -1
  100. package/dist/runtime/bridge-protocol.js.map +0 -1
  101. package/src/runtime/bridge-core.ts +0 -494
@@ -1,22 +1,24 @@
1
1
  /**
2
- * BridgeProtocol - Unified abstraction for JS<->Python communication.
2
+ * RpcClient - the single correlated-RPC client for JS<->Python communication.
3
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)
4
+ * It HOLDS a Transport and a codec (SafeCodec) and is, in turn, HELD by the
5
+ * bridge facades (NodeBridge/HttpBridge/PyodideBridge) via composition — it is
6
+ * NOT a base class bridges extend. It owns the one place where the wire frame
7
+ * is built and correlated: id generation, {id, protocol} stamping, codec
8
+ * encode/decode, and transport.send. It composes DisposableBase to obtain its
9
+ * lifecycle (init/dispose) and bounded execution (timeout/retry/abort), but it
10
+ * carries no PythonRuntime contract obligation — the facade implements
11
+ * PythonRuntime and delegates the four RPC methods to this client.
10
12
  *
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
13
+ * Why composition (not inheritance): inheritance previously forced the RPC
14
+ * contract onto a lifecycle base, leaking throwing RPC stubs into transports.
15
+ * Holding one RpcClient keeps exactly one encode/decode/correlation site and
16
+ * lets transports stay pure byte-movers.
15
17
  *
16
18
  * @see https://github.com/bbopen/tywrap/issues/149
17
19
  */
18
20
  import type { BridgeInfo } from '../types/index.js';
19
- import { BoundedContext, type ExecuteOptions } from './bounded-context.js';
21
+ import { DisposableBase, type ExecuteOptions } from './bounded-context.js';
20
22
  import { SafeCodec, type CodecOptions } from './safe-codec.js';
21
23
  import { type Transport, type ProtocolMessage } from './transport.js';
22
24
  export interface GetBridgeInfoOptions {
@@ -27,9 +29,9 @@ export interface GetBridgeInfoOptions {
27
29
  refresh?: boolean;
28
30
  }
29
31
  /**
30
- * Configuration options for BridgeProtocol.
32
+ * Configuration options for RpcClient.
31
33
  */
32
- export interface BridgeProtocolOptions {
34
+ export interface RpcClientOptions {
33
35
  /** The transport to use for communication */
34
36
  transport: Transport;
35
37
  /** Codec options for validation/serialization */
@@ -38,60 +40,51 @@ export interface BridgeProtocolOptions {
38
40
  defaultTimeoutMs?: number;
39
41
  }
40
42
  /**
41
- * BridgeProtocol combines BoundedContext + SafeCodec + Transport
42
- * into a unified abstraction for all JS<->Python communication.
43
- *
44
- * This class provides:
45
- * - Automatic transport lifecycle management
46
- * - Request encoding with guardrails (special float rejection, key validation)
47
- * - Response decoding with Arrow support
48
- * - Full RuntimeExecution interface implementation
49
- *
50
- * Subclasses should:
51
- * 1. Create their transport in their constructor
52
- * 2. Pass it to super() via BridgeProtocolOptions
53
- * 3. Optionally override doInit() and doDispose() for additional setup/teardown
43
+ * RpcClient holds a SafeCodec + Transport and composes DisposableBase for
44
+ * lifecycle/bounded-execution. It is the one correlated-RPC client; bridge
45
+ * facades HOLD an instance and delegate their PythonRuntime methods to it.
54
46
  *
55
47
  * @example
56
48
  * ```typescript
57
- * class NodeBridge extends BridgeProtocol {
49
+ * class NodeBridge extends DisposableBase implements PythonRuntime {
50
+ * private readonly rpc: RpcClient;
58
51
  * constructor(options: NodeBridgeOptions) {
52
+ * super();
59
53
  * const transport = new ProcessIO(options);
60
- * super({ transport, defaultTimeoutMs: options.timeout });
54
+ * this.rpc = new RpcClient({ transport, defaultTimeoutMs: options.timeout });
55
+ * this.trackResource(this.rpc);
61
56
  * }
57
+ * // call/instantiate/callMethod/disposeInstance delegate to this.rpc.*
62
58
  * }
63
59
  * ```
64
60
  */
65
- export declare class BridgeProtocol extends BoundedContext {
61
+ export declare class RpcClient extends DisposableBase {
66
62
  /** Codec instance for validation and serialization */
67
- protected readonly codec: SafeCodec;
63
+ readonly codec: SafeCodec;
68
64
  /** Transport instance for message passing */
69
- protected readonly transport: Transport;
65
+ readonly transport: Transport;
70
66
  /** Default timeout for operations in milliseconds */
71
- protected readonly defaultTimeoutMs: number;
67
+ readonly defaultTimeoutMs: number;
72
68
  /** Counter for generating unique request IDs */
73
69
  private requestId;
74
70
  /** Cached bridge diagnostics info (populated by getBridgeInfo). */
75
71
  private bridgeInfoCache?;
76
72
  /**
77
- * Create a new BridgeProtocol instance.
73
+ * Create a new RpcClient instance.
78
74
  *
79
75
  * @param options - Configuration options including transport and codec settings
80
76
  */
81
- constructor(options: BridgeProtocolOptions);
77
+ constructor(options: RpcClientOptions);
82
78
  /**
83
- * Initialize the protocol.
84
- *
85
- * Initializes the underlying transport. Subclasses can override this
86
- * to add additional initialization logic, but must call super.doInit().
79
+ * Initialize the client by initializing the underlying transport.
80
+ * Driven by the holding facade's lifecycle (facade.init() -> rpc.init()).
87
81
  */
88
82
  protected doInit(): Promise<void>;
89
83
  /**
90
- * Dispose the protocol.
84
+ * Dispose the client.
91
85
  *
92
- * The transport is tracked as a resource and will be disposed automatically
93
- * by BoundedContext. Subclasses can override this to add additional cleanup,
94
- * but should not need to dispose the transport manually.
86
+ * The transport is tracked as a resource and is disposed automatically by
87
+ * DisposableBase. Here we only clear the cached bridge info.
95
88
  */
96
89
  protected doDispose(): Promise<void>;
97
90
  /**
@@ -111,7 +104,7 @@ export declare class BridgeProtocol extends BoundedContext {
111
104
  * @throws BridgeExecutionError if Python returns an error
112
105
  * @throws BridgeTimeoutError if the operation times out
113
106
  */
114
- protected sendMessage<T>(message: Omit<ProtocolMessage, 'id' | 'protocol'>, options?: ExecuteOptions<T>): Promise<T>;
107
+ sendMessage<T>(message: Omit<ProtocolMessage, 'id' | 'protocol'>, options?: ExecuteOptions<T>): Promise<T>;
115
108
  /**
116
109
  * Async version that uses decodeResponseAsync for Arrow support.
117
110
  *
@@ -126,7 +119,27 @@ export declare class BridgeProtocol extends BoundedContext {
126
119
  * @throws BridgeExecutionError if Python returns an error
127
120
  * @throws BridgeTimeoutError if the operation times out
128
121
  */
129
- protected sendMessageAsync<T>(message: Omit<ProtocolMessage, 'id' | 'protocol'>, options?: ExecuteOptions<T>): Promise<T>;
122
+ sendMessageAsync<T>(message: Omit<ProtocolMessage, 'id' | 'protocol'>, options?: ExecuteOptions<T>): Promise<T>;
123
+ /**
124
+ * Send a message over an EXPLICIT transport (not this.transport) with the
125
+ * same id-generation + codec encode/decode as the normal send path, but
126
+ * WITHOUT this.execute() and WITHOUT auto-init.
127
+ *
128
+ * This is the warmup path: NodeBridge runs warmup commands inside
129
+ * transport.init() (worker spawn -> onWorkerReady), which happens DURING
130
+ * rpc.init(). Routing those through this.execute() would auto-init this same
131
+ * client and re-await the in-flight init() that is itself waiting on warmup,
132
+ * deadlocking. So sendOn deliberately skips lifecycle: it only unifies the
133
+ * id counter + codec, never the state machine.
134
+ *
135
+ * @param transport - The specific worker transport to send on
136
+ * @param message - The protocol message (without id/protocol; stamped here)
137
+ * @param opts - Optional timeout / abort signal
138
+ */
139
+ sendOn<T>(transport: Transport, message: Omit<ProtocolMessage, 'id' | 'protocol'>, opts?: {
140
+ timeoutMs?: number;
141
+ signal?: AbortSignal;
142
+ }): Promise<T>;
130
143
  /**
131
144
  * Generate a unique request ID.
132
145
  *
@@ -181,4 +194,4 @@ export declare class BridgeProtocol extends BoundedContext {
181
194
  */
182
195
  getBridgeInfo(options?: GetBridgeInfoOptions): Promise<BridgeInfo>;
183
196
  }
184
- //# sourceMappingURL=bridge-protocol.d.ts.map
197
+ //# sourceMappingURL=rpc-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc-client.d.ts","sourceRoot":"","sources":["../../src/runtime/rpc-client.ts"],"names":[],"mappings":"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"}
@@ -1,21 +1,23 @@
1
1
  /**
2
- * BridgeProtocol - Unified abstraction for JS<->Python communication.
2
+ * RpcClient - the single correlated-RPC client for JS<->Python communication.
3
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)
4
+ * It HOLDS a Transport and a codec (SafeCodec) and is, in turn, HELD by the
5
+ * bridge facades (NodeBridge/HttpBridge/PyodideBridge) via composition — it is
6
+ * NOT a base class bridges extend. It owns the one place where the wire frame
7
+ * is built and correlated: id generation, {id, protocol} stamping, codec
8
+ * encode/decode, and transport.send. It composes DisposableBase to obtain its
9
+ * lifecycle (init/dispose) and bounded execution (timeout/retry/abort), but it
10
+ * carries no PythonRuntime contract obligation — the facade implements
11
+ * PythonRuntime and delegates the four RPC methods to this client.
10
12
  *
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
13
+ * Why composition (not inheritance): inheritance previously forced the RPC
14
+ * contract onto a lifecycle base, leaking throwing RPC stubs into transports.
15
+ * Holding one RpcClient keeps exactly one encode/decode/correlation site and
16
+ * lets transports stay pure byte-movers.
15
17
  *
16
18
  * @see https://github.com/bbopen/tywrap/issues/149
17
19
  */
18
- import { BoundedContext } from './bounded-context.js';
20
+ import { DisposableBase } from './bounded-context.js';
19
21
  import { BridgeProtocolError } from './errors.js';
20
22
  import { SafeCodec } from './safe-codec.js';
21
23
  import { TYWRAP_PROTOCOL_VERSION } from './protocol.js';
@@ -94,34 +96,28 @@ function validateBridgeInfoPayload(value) {
94
96
  };
95
97
  }
96
98
  // =============================================================================
97
- // BRIDGE PROTOCOL BASE CLASS
99
+ // RPC CLIENT
98
100
  // =============================================================================
99
101
  /**
100
- * BridgeProtocol combines BoundedContext + SafeCodec + Transport
101
- * into a unified abstraction for all JS<->Python communication.
102
- *
103
- * This class provides:
104
- * - Automatic transport lifecycle management
105
- * - Request encoding with guardrails (special float rejection, key validation)
106
- * - Response decoding with Arrow support
107
- * - Full RuntimeExecution interface implementation
108
- *
109
- * Subclasses should:
110
- * 1. Create their transport in their constructor
111
- * 2. Pass it to super() via BridgeProtocolOptions
112
- * 3. Optionally override doInit() and doDispose() for additional setup/teardown
102
+ * RpcClient holds a SafeCodec + Transport and composes DisposableBase for
103
+ * lifecycle/bounded-execution. It is the one correlated-RPC client; bridge
104
+ * facades HOLD an instance and delegate their PythonRuntime methods to it.
113
105
  *
114
106
  * @example
115
107
  * ```typescript
116
- * class NodeBridge extends BridgeProtocol {
108
+ * class NodeBridge extends DisposableBase implements PythonRuntime {
109
+ * private readonly rpc: RpcClient;
117
110
  * constructor(options: NodeBridgeOptions) {
111
+ * super();
118
112
  * const transport = new ProcessIO(options);
119
- * super({ transport, defaultTimeoutMs: options.timeout });
113
+ * this.rpc = new RpcClient({ transport, defaultTimeoutMs: options.timeout });
114
+ * this.trackResource(this.rpc);
120
115
  * }
116
+ * // call/instantiate/callMethod/disposeInstance delegate to this.rpc.*
121
117
  * }
122
118
  * ```
123
119
  */
124
- export class BridgeProtocol extends BoundedContext {
120
+ export class RpcClient extends DisposableBase {
125
121
  /** Codec instance for validation and serialization */
126
122
  codec;
127
123
  /** Transport instance for message passing */
@@ -133,7 +129,7 @@ export class BridgeProtocol extends BoundedContext {
133
129
  /** Cached bridge diagnostics info (populated by getBridgeInfo). */
134
130
  bridgeInfoCache;
135
131
  /**
136
- * Create a new BridgeProtocol instance.
132
+ * Create a new RpcClient instance.
137
133
  *
138
134
  * @param options - Configuration options including transport and codec settings
139
135
  */
@@ -149,25 +145,21 @@ export class BridgeProtocol extends BoundedContext {
149
145
  // LIFECYCLE
150
146
  // ===========================================================================
151
147
  /**
152
- * Initialize the protocol.
153
- *
154
- * Initializes the underlying transport. Subclasses can override this
155
- * to add additional initialization logic, but must call super.doInit().
148
+ * Initialize the client by initializing the underlying transport.
149
+ * Driven by the holding facade's lifecycle (facade.init() -> rpc.init()).
156
150
  */
157
151
  async doInit() {
158
152
  await this.transport.init();
159
153
  }
160
154
  /**
161
- * Dispose the protocol.
155
+ * Dispose the client.
162
156
  *
163
- * The transport is tracked as a resource and will be disposed automatically
164
- * by BoundedContext. Subclasses can override this to add additional cleanup,
165
- * but should not need to dispose the transport manually.
157
+ * The transport is tracked as a resource and is disposed automatically by
158
+ * DisposableBase. Here we only clear the cached bridge info.
166
159
  */
167
160
  async doDispose() {
168
161
  this.bridgeInfoCache = undefined;
169
- // Transport is tracked and will be disposed by BoundedContext
170
- // Subclasses can override to add additional cleanup
162
+ // Transport is tracked and will be disposed by DisposableBase.
171
163
  }
172
164
  // ===========================================================================
173
165
  // MESSAGE SENDING
@@ -233,6 +225,32 @@ export class BridgeProtocol extends BoundedContext {
233
225
  return this.codec.decodeResponseAsync(responseStr);
234
226
  }, options);
235
227
  }
228
+ /**
229
+ * Send a message over an EXPLICIT transport (not this.transport) with the
230
+ * same id-generation + codec encode/decode as the normal send path, but
231
+ * WITHOUT this.execute() and WITHOUT auto-init.
232
+ *
233
+ * This is the warmup path: NodeBridge runs warmup commands inside
234
+ * transport.init() (worker spawn -> onWorkerReady), which happens DURING
235
+ * rpc.init(). Routing those through this.execute() would auto-init this same
236
+ * client and re-await the in-flight init() that is itself waiting on warmup,
237
+ * deadlocking. So sendOn deliberately skips lifecycle: it only unifies the
238
+ * id counter + codec, never the state machine.
239
+ *
240
+ * @param transport - The specific worker transport to send on
241
+ * @param message - The protocol message (without id/protocol; stamped here)
242
+ * @param opts - Optional timeout / abort signal
243
+ */
244
+ async sendOn(transport, message, opts) {
245
+ const fullMessage = {
246
+ ...message,
247
+ id: this.generateId(),
248
+ protocol: PROTOCOL_ID,
249
+ };
250
+ const encoded = this.codec.encodeRequest(fullMessage);
251
+ const responseStr = await transport.send(encoded, opts?.timeoutMs ?? this.defaultTimeoutMs, opts?.signal);
252
+ return this.codec.decodeResponseAsync(responseStr);
253
+ }
236
254
  /**
237
255
  * Generate a unique request ID.
238
256
  *
@@ -243,7 +261,7 @@ export class BridgeProtocol extends BoundedContext {
243
261
  return ++this.requestId;
244
262
  }
245
263
  // ===========================================================================
246
- // RUNTIME EXECUTION INTERFACE
264
+ // RPC METHODS (delegated to by the facade's PythonRuntime implementation)
247
265
  // ===========================================================================
248
266
  /**
249
267
  * Call a Python function.
@@ -341,4 +359,4 @@ export class BridgeProtocol extends BoundedContext {
341
359
  return info;
342
360
  }
343
361
  }
344
- //# sourceMappingURL=bridge-protocol.js.map
362
+ //# sourceMappingURL=rpc-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc-client.js","sourceRoot":"","sources":["../../src/runtime/rpc-client.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * @see https://github.com/bbopen/tywrap/issues/149
8
8
  */
9
- import { BoundedContext } from './bounded-context.js';
9
+ import { DisposableBase } from './bounded-context.js';
10
10
  import type { Transport } from './transport.js';
11
11
  /**
12
12
  * Configuration options for the WorkerPool.
@@ -71,7 +71,7 @@ export interface PooledWorker {
71
71
  * await pool.dispose();
72
72
  * ```
73
73
  */
74
- export declare class WorkerPool extends BoundedContext {
74
+ export declare class WorkerPool extends DisposableBase {
75
75
  private readonly options;
76
76
  private readonly workers;
77
77
  private readonly waitQueue;
@@ -145,7 +145,6 @@ export declare class WorkerPool extends BoundedContext {
145
145
  * - Process exited unexpectedly
146
146
  * - Pipe errors (EPIPE)
147
147
  * - Connection reset errors (ECONNRESET)
148
- * - Request timeouts (the worker may still be busy with an uncancellable request)
149
148
  */
150
149
  private isFatalWorkerError;
151
150
  /**
@@ -171,11 +170,17 @@ export declare class WorkerPool extends BoundedContext {
171
170
  * Find an available worker with capacity for another request.
172
171
  */
173
172
  private findAvailableWorker;
173
+ private getMinimumWorkerDeficit;
174
+ private isShuttingDown;
175
+ private fillToMinimumWorkers;
176
+ private spawnWorkers;
174
177
  /**
175
178
  * Replace a removed worker in the background so the next caller does not pay
176
179
  * the full worker cold-start penalty after a timeout or crash.
177
180
  */
178
181
  private scheduleReplacementWorker;
182
+ private spawnWorkerToPool;
183
+ private publishAvailableWorker;
179
184
  /**
180
185
  * Create a new worker and add it to the pool.
181
186
  *
@@ -187,22 +192,5 @@ export declare class WorkerPool extends BoundedContext {
187
192
  * Wait in queue for a worker to become available.
188
193
  */
189
194
  private waitForWorker;
190
- /**
191
- * Not implemented - WorkerPool does not execute Python calls directly.
192
- * Use the BridgeProtocol layer with a pooled worker's transport.
193
- */
194
- call<T = unknown>(_module: string, _functionName: string, _args: unknown[], _kwargs?: Record<string, unknown>): Promise<T>;
195
- /**
196
- * Not implemented - WorkerPool does not execute Python calls directly.
197
- */
198
- instantiate<T = unknown>(_module: string, _className: string, _args: unknown[], _kwargs?: Record<string, unknown>): Promise<T>;
199
- /**
200
- * Not implemented - WorkerPool does not execute Python calls directly.
201
- */
202
- callMethod<T = unknown>(_handle: string, _methodName: string, _args: unknown[], _kwargs?: Record<string, unknown>): Promise<T>;
203
- /**
204
- * Not implemented - WorkerPool does not execute Python calls directly.
205
- */
206
- disposeInstance(_handle: string): Promise<void>;
207
195
  }
208
196
  //# sourceMappingURL=worker-pool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"worker-pool.d.ts","sourceRoot":"","sources":["../../src/runtime/worker-pool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"worker-pool.d.ts","sourceRoot":"","sources":["../../src/runtime/worker-pool.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAMhD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,4CAA4C;IAC5C,eAAe,EAAE,MAAM,SAAS,CAAC;IAEjC,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;IAEnB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,wDAAwD;IACxD,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,yDAAyD;IACzD,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAExD;;;;OAIG;IACH,wBAAwB,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACpE;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,wCAAwC;IACxC,SAAS,EAAE,SAAS,CAAC;IAErB,kEAAkE;IAClE,aAAa,EAAE,MAAM,CAAC;CACvB;AAoBD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,UAAW,SAAQ,cAAc;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAMtB;IACF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsB;IAChD,0EAA0E;IAC1E,OAAO,CAAC,gBAAgB,CAAK;IAE7B;;;;OAIG;gBACS,OAAO,EAAE,iBAAiB;IA+BtC;;;;;OAKG;cACa,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAKvC;;;;;;OAMG;cACa,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAgC1C;;;;;;;;;;;OAWG;IACG,OAAO,IAAI,OAAO,CAAC,YAAY,CAAC;IAqCtC;;;;;;;OAOG;IACH,OAAO,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAWnC;;;;;;;;;;;;;;;OAeG;IACG,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAyBzE;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IAa1B;;;;;OAKG;IACH,OAAO,CAAC,YAAY;IAkBpB;;OAEG;IACH,IAAI,WAAW,IAAI,MAAM,CAExB;IAED;;OAEG;IACH,IAAI,WAAW,IAAI,MAAM,CAExB;IAED;;OAEG;IACH,IAAI,aAAa,IAAI,MAAM,CAE1B;IAMD;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAI3B,OAAO,CAAC,uBAAuB;IAI/B,OAAO,CAAC,cAAc;YAIR,oBAAoB;YAIpB,YAAY;IAY1B;;;OAGG;IACH,OAAO,CAAC,yBAAyB;YAmBnB,iBAAiB;IAc/B,OAAO,CAAC,sBAAsB;IAe9B;;;;;OAKG;YACW,YAAY;IAoC1B;;OAEG;IACH,OAAO,CAAC,aAAa;CAuBtB"}
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * @see https://github.com/bbopen/tywrap/issues/149
8
8
  */
9
- import { BoundedContext } from './bounded-context.js';
9
+ import { DisposableBase } from './bounded-context.js';
10
10
  import { BridgeTimeoutError, BridgeExecutionError, BridgeProtocolError } from './errors.js';
11
11
  // =============================================================================
12
12
  // WORKER POOL
@@ -39,7 +39,7 @@ import { BridgeTimeoutError, BridgeExecutionError, BridgeProtocolError } from '.
39
39
  * await pool.dispose();
40
40
  * ```
41
41
  */
42
- export class WorkerPool extends BoundedContext {
42
+ export class WorkerPool extends DisposableBase {
43
43
  options;
44
44
  workers = [];
45
45
  waitQueue = [];
@@ -84,13 +84,7 @@ export class WorkerPool extends BoundedContext {
84
84
  */
85
85
  async doInit() {
86
86
  // Pre-spawn minimum workers if configured
87
- if (this.options.minWorkers > 0) {
88
- const spawns = [];
89
- for (let i = 0; i < this.options.minWorkers; i++) {
90
- spawns.push(this.createWorker());
91
- }
92
- await Promise.all(spawns);
93
- }
87
+ await this.fillToMinimumWorkers();
94
88
  }
95
89
  /**
96
90
  * Dispose the pool and all workers.
@@ -142,7 +136,7 @@ export class WorkerPool extends BoundedContext {
142
136
  */
143
137
  async acquire() {
144
138
  // Check for disposed state
145
- if (this.isDisposed) {
139
+ if (this.isDisposed || this.state === 'disposing') {
146
140
  throw new BridgeExecutionError('Pool has been disposed');
147
141
  }
148
142
  // Find an available worker (one with capacity)
@@ -159,7 +153,12 @@ export class WorkerPool extends BoundedContext {
159
153
  this.pendingCreations++;
160
154
  try {
161
155
  const newWorker = await this.createWorker();
156
+ if (this.isShuttingDown()) {
157
+ this.removeWorker(newWorker);
158
+ throw new BridgeExecutionError('Pool has been disposed');
159
+ }
162
160
  newWorker.inFlightCount++;
161
+ this.publishAvailableWorker(newWorker);
163
162
  return newWorker;
164
163
  }
165
164
  finally {
@@ -184,15 +183,7 @@ export class WorkerPool extends BoundedContext {
184
183
  }
185
184
  // Decrement in-flight count (minimum 0)
186
185
  worker.inFlightCount = Math.max(0, worker.inFlightCount - 1);
187
- // If there are waiters and this worker has capacity, fulfill the first waiter
188
- if (this.waitQueue.length > 0 && worker.inFlightCount < this.options.maxConcurrentPerWorker) {
189
- const waiter = this.waitQueue.shift();
190
- if (waiter) {
191
- clearTimeout(waiter.timer);
192
- worker.inFlightCount++;
193
- waiter.resolve(worker);
194
- }
195
- }
186
+ this.publishAvailableWorker(worker);
196
187
  }
197
188
  /**
198
189
  * Execute a function with an acquired worker, automatically releasing afterward.
@@ -242,14 +233,8 @@ export class WorkerPool extends BoundedContext {
242
233
  * - Process exited unexpectedly
243
234
  * - Pipe errors (EPIPE)
244
235
  * - Connection reset errors (ECONNRESET)
245
- * - Request timeouts (the worker may still be busy with an uncancellable request)
246
236
  */
247
237
  isFatalWorkerError(error) {
248
- // If a request times out, the underlying transport may still be executing it.
249
- // Quarantine this worker so subsequent requests don't get stuck behind it.
250
- if (error instanceof BridgeTimeoutError) {
251
- return true;
252
- }
253
238
  if (error instanceof BridgeProtocolError) {
254
239
  const msg = error.message.toLowerCase();
255
240
  return (msg.includes('not running') ||
@@ -273,7 +258,9 @@ export class WorkerPool extends BoundedContext {
273
258
  worker.transport.dispose().catch(() => {
274
259
  // Ignore disposal errors for dead workers
275
260
  });
276
- this.scheduleReplacementWorker();
261
+ if (this.state === 'ready') {
262
+ this.scheduleReplacementWorker();
263
+ }
277
264
  }
278
265
  }
279
266
  // ===========================================================================
@@ -306,6 +293,27 @@ export class WorkerPool extends BoundedContext {
306
293
  findAvailableWorker() {
307
294
  return this.workers.find(w => w.inFlightCount < this.options.maxConcurrentPerWorker);
308
295
  }
296
+ getMinimumWorkerDeficit() {
297
+ return Math.max(0, this.options.minWorkers - (this.workers.length + this.pendingCreations));
298
+ }
299
+ isShuttingDown() {
300
+ return this.isDisposed || this.state === 'disposing';
301
+ }
302
+ async fillToMinimumWorkers() {
303
+ await this.spawnWorkers(this.getMinimumWorkerDeficit());
304
+ }
305
+ async spawnWorkers(count) {
306
+ if (count === 0) {
307
+ return;
308
+ }
309
+ this.pendingCreations += count;
310
+ try {
311
+ await Promise.all(Array.from({ length: count }, () => this.spawnWorkerToPool()));
312
+ }
313
+ finally {
314
+ this.pendingCreations = Math.max(0, this.pendingCreations - count);
315
+ }
316
+ }
309
317
  /**
310
318
  * Replace a removed worker in the background so the next caller does not pay
311
319
  * the full worker cold-start penalty after a timeout or crash.
@@ -319,11 +327,7 @@ export class WorkerPool extends BoundedContext {
319
327
  }
320
328
  this.pendingCreations++;
321
329
  const replacementReady = this.options.onReplacementWorkerReady ?? this.options.onWorkerReady;
322
- this.createWorker(replacementReady)
323
- .then(worker => {
324
- // Reuse release() to wake queued callers if one is waiting.
325
- this.release(worker);
326
- })
330
+ this.spawnWorkerToPool(replacementReady)
327
331
  .catch(() => {
328
332
  // Ignore background replacement failures. A later acquire() can retry.
329
333
  })
@@ -331,6 +335,29 @@ export class WorkerPool extends BoundedContext {
331
335
  this.pendingCreations = Math.max(0, this.pendingCreations - 1);
332
336
  });
333
337
  }
338
+ async spawnWorkerToPool(onWorkerReady = this.options.onWorkerReady) {
339
+ if (this.isShuttingDown()) {
340
+ return;
341
+ }
342
+ const worker = await this.createWorker(onWorkerReady);
343
+ if (this.isShuttingDown()) {
344
+ this.removeWorker(worker);
345
+ return;
346
+ }
347
+ this.publishAvailableWorker(worker);
348
+ }
349
+ publishAvailableWorker(worker) {
350
+ while (this.waitQueue.length > 0 &&
351
+ worker.inFlightCount < this.options.maxConcurrentPerWorker) {
352
+ const waiter = this.waitQueue.shift();
353
+ if (!waiter) {
354
+ return;
355
+ }
356
+ clearTimeout(waiter.timer);
357
+ worker.inFlightCount++;
358
+ waiter.resolve(worker);
359
+ }
360
+ }
334
361
  /**
335
362
  * Create a new worker and add it to the pool.
336
363
  *
@@ -387,33 +414,5 @@ export class WorkerPool extends BoundedContext {
387
414
  this.waitQueue.push({ resolve, reject, timer });
388
415
  });
389
416
  }
390
- // ===========================================================================
391
- // RUNTIME EXECUTION (Not implemented - WorkerPool is just for worker management)
392
- // ===========================================================================
393
- /**
394
- * Not implemented - WorkerPool does not execute Python calls directly.
395
- * Use the BridgeProtocol layer with a pooled worker's transport.
396
- */
397
- async call(_module, _functionName, _args, _kwargs) {
398
- throw new BridgeExecutionError('WorkerPool does not implement call() - use withWorker() to get a transport');
399
- }
400
- /**
401
- * Not implemented - WorkerPool does not execute Python calls directly.
402
- */
403
- async instantiate(_module, _className, _args, _kwargs) {
404
- throw new BridgeExecutionError('WorkerPool does not implement instantiate() - use withWorker() to get a transport');
405
- }
406
- /**
407
- * Not implemented - WorkerPool does not execute Python calls directly.
408
- */
409
- async callMethod(_handle, _methodName, _args, _kwargs) {
410
- throw new BridgeExecutionError('WorkerPool does not implement callMethod() - use withWorker() to get a transport');
411
- }
412
- /**
413
- * Not implemented - WorkerPool does not execute Python calls directly.
414
- */
415
- async disposeInstance(_handle) {
416
- throw new BridgeExecutionError('WorkerPool does not implement disposeInstance() - use withWorker() to get a transport');
417
- }
418
417
  }
419
418
  //# sourceMappingURL=worker-pool.js.map