tywrap 0.4.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 (75) hide show
  1. package/README.md +1 -0
  2. package/dist/index.d.ts +3 -3
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +6 -6
  5. package/dist/index.js.map +1 -1
  6. package/dist/runtime/base.d.ts +7 -7
  7. package/dist/runtime/base.js +8 -8
  8. package/dist/runtime/bounded-context.d.ts +15 -31
  9. package/dist/runtime/bounded-context.d.ts.map +1 -1
  10. package/dist/runtime/bounded-context.js +16 -15
  11. package/dist/runtime/bounded-context.js.map +1 -1
  12. package/dist/runtime/http.d.ts +24 -5
  13. package/dist/runtime/http.d.ts.map +1 -1
  14. package/dist/runtime/http.js +57 -12
  15. package/dist/runtime/http.js.map +1 -1
  16. package/dist/runtime/node.d.ts +33 -7
  17. package/dist/runtime/node.d.ts.map +1 -1
  18. package/dist/runtime/node.js +139 -151
  19. package/dist/runtime/node.js.map +1 -1
  20. package/dist/runtime/pooled-transport.d.ts +2 -18
  21. package/dist/runtime/pooled-transport.d.ts.map +1 -1
  22. package/dist/runtime/pooled-transport.js +2 -29
  23. package/dist/runtime/pooled-transport.js.map +1 -1
  24. package/dist/runtime/process-io.d.ts +2 -18
  25. package/dist/runtime/process-io.d.ts.map +1 -1
  26. package/dist/runtime/process-io.js +3 -30
  27. package/dist/runtime/process-io.js.map +1 -1
  28. package/dist/runtime/pyodide-bootstrap-core.generated.d.ts +12 -0
  29. package/dist/runtime/pyodide-bootstrap-core.generated.d.ts.map +1 -0
  30. package/dist/runtime/pyodide-bootstrap-core.generated.js +12 -0
  31. package/dist/runtime/pyodide-bootstrap-core.generated.js.map +1 -0
  32. package/dist/runtime/pyodide-io.d.ts +7 -37
  33. package/dist/runtime/pyodide-io.d.ts.map +1 -1
  34. package/dist/runtime/pyodide-io.js +90 -214
  35. package/dist/runtime/pyodide-io.js.map +1 -1
  36. package/dist/runtime/pyodide.d.ts +25 -5
  37. package/dist/runtime/pyodide.d.ts.map +1 -1
  38. package/dist/runtime/pyodide.js +59 -10
  39. package/dist/runtime/pyodide.js.map +1 -1
  40. package/dist/runtime/{bridge-protocol.d.ts → rpc-client.d.ts} +59 -46
  41. package/dist/runtime/rpc-client.d.ts.map +1 -0
  42. package/dist/runtime/{bridge-protocol.js → rpc-client.js} +60 -42
  43. package/dist/runtime/rpc-client.js.map +1 -0
  44. package/dist/runtime/worker-pool.d.ts +2 -19
  45. package/dist/runtime/worker-pool.d.ts.map +1 -1
  46. package/dist/runtime/worker-pool.js +2 -30
  47. package/dist/runtime/worker-pool.js.map +1 -1
  48. package/dist/types/index.d.ts +8 -3
  49. package/dist/types/index.d.ts.map +1 -1
  50. package/package.json +10 -10
  51. package/runtime/__pycache__/safe_codec.cpython-311.pyc +0 -0
  52. package/runtime/__pycache__/tywrap_bridge_core.cpython-311.pyc +0 -0
  53. package/runtime/python_bridge.py +85 -702
  54. package/runtime/safe_codec.py +10 -2
  55. package/runtime/tywrap_bridge_core.py +875 -0
  56. package/src/index.ts +6 -6
  57. package/src/runtime/base.ts +8 -8
  58. package/src/runtime/bounded-context.ts +17 -56
  59. package/src/runtime/http.ts +85 -13
  60. package/src/runtime/node.ts +165 -217
  61. package/src/runtime/pooled-transport.ts +2 -57
  62. package/src/runtime/process-io.ts +3 -55
  63. package/src/runtime/pyodide-bootstrap-core.generated.ts +12 -0
  64. package/src/runtime/pyodide-io.ts +92 -243
  65. package/src/runtime/pyodide.ts +87 -10
  66. package/src/runtime/{bridge-protocol.ts → rpc-client.ts} +76 -49
  67. package/src/runtime/worker-pool.ts +2 -58
  68. package/src/types/index.ts +20 -4
  69. package/dist/runtime/bridge-core.d.ts +0 -65
  70. package/dist/runtime/bridge-core.d.ts.map +0 -1
  71. package/dist/runtime/bridge-core.js +0 -379
  72. package/dist/runtime/bridge-core.js.map +0 -1
  73. package/dist/runtime/bridge-protocol.d.ts.map +0 -1
  74. package/dist/runtime/bridge-protocol.js.map +0 -1
  75. package/src/runtime/bridge-core.ts +0 -494
package/src/index.ts CHANGED
@@ -9,14 +9,14 @@ import { tywrap } from './tywrap.js';
9
9
 
10
10
  export type { TywrapConfig, ResolvedTywrapConfig } from './config/index.js';
11
11
  export { defineConfig, resolveConfig } from './config/index.js';
12
- // BoundedContext - unified abstraction for cross-boundary concerns
12
+ // DisposableBase - lifecycle/resource base for cross-boundary components
13
13
  export {
14
- BoundedContext,
14
+ DisposableBase,
15
15
  type ContextState,
16
16
  type ExecuteOptions,
17
17
  } from './runtime/bounded-context.js';
18
- // BridgeProtocol - unified BoundedContext + SafeCodec + Transport
19
- export { BridgeProtocol, type BridgeProtocolOptions } from './runtime/bridge-protocol.js';
18
+ // RpcClient - the single correlated-RPC client (codec + transport), held by bridges
19
+ export { RpcClient, type RpcClientOptions } from './runtime/rpc-client.js';
20
20
  // SafeCodec - validation and serialization for JS<->Python boundary
21
21
  export { SafeCodec, type CodecOptions } from './runtime/safe-codec.js';
22
22
  // Transport - abstract I/O channel interface
@@ -63,7 +63,7 @@ export {
63
63
  } from './runtime/validators.js';
64
64
 
65
65
  /**
66
- * @deprecated Use BoundedContext instead. RuntimeBridge will be removed in the next major version.
66
+ * @deprecated Use DisposableBase instead. RuntimeBridge will be removed in the next major version.
67
67
  */
68
68
  export { RuntimeBridge } from './runtime/base.js';
69
69
 
@@ -77,7 +77,7 @@ export {
77
77
  } from './runtime/errors.js';
78
78
  export { getRuntimeBridge, setRuntimeBridge, clearRuntimeBridge } from './runtime/index.js';
79
79
 
80
- // Runtime-specific exports (Bridges using new BridgeProtocol architecture)
80
+ // Runtime-specific exports (Bridge facades that HOLD an RpcClient)
81
81
  export { NodeBridge, type NodeBridgeOptions } from './runtime/node.js';
82
82
  export { PyodideBridge, type PyodideBridgeOptions } from './runtime/pyodide.js';
83
83
  export { HttpBridge, type HttpBridgeOptions } from './runtime/http.js';
@@ -1,24 +1,24 @@
1
1
  /**
2
2
  * Base runtime bridge
3
3
  *
4
- * @deprecated Use BoundedContext instead. RuntimeBridge will be removed in the next major version.
4
+ * @deprecated Use DisposableBase instead. RuntimeBridge will be removed in the next major version.
5
5
  */
6
6
 
7
- import { BoundedContext } from './bounded-context.js';
7
+ import { DisposableBase } from './bounded-context.js';
8
8
 
9
9
  /**
10
- * @deprecated Use BoundedContext instead. RuntimeBridge will be removed in the next major version.
10
+ * @deprecated Use DisposableBase instead. RuntimeBridge will be removed in the next major version.
11
11
  *
12
- * All bridges now extend BoundedContext which provides:
12
+ * All bridges now build on DisposableBase which provides:
13
13
  * - Lifecycle management (init/dispose state machine)
14
14
  * - Validation helpers
15
15
  * - Error classification
16
16
  * - Bounded execution (timeout, retry)
17
17
  * - Resource ownership tracking
18
18
  *
19
- * @see BoundedContext
19
+ * @see DisposableBase
20
20
  */
21
- export abstract class RuntimeBridge extends BoundedContext {}
21
+ export abstract class RuntimeBridge extends DisposableBase {}
22
22
 
23
- // Re-export BoundedContext for backwards compatibility
24
- export { BoundedContext };
23
+ // Re-export DisposableBase for backwards compatibility
24
+ export { DisposableBase };
@@ -1,19 +1,20 @@
1
1
  /**
2
- * BoundedContext - Unified abstraction for cross-boundary concerns.
2
+ * DisposableBase - Lifecycle-and-resource base for cross-boundary components.
3
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
4
+ * Provides ONLY: init/dispose state machine, validation helpers, error
5
+ * classification, bounded execution (timeout/retry/abort), and resource
6
+ * ownership tracking. It carries ZERO RPC methods by design — the
7
+ * cross-boundary RPC contract (call/instantiate/callMethod/disposeInstance)
8
+ * is PythonRuntime, implemented only by the bridge facades, never by a base
9
+ * class or a transport. This separation is what keeps transports (ProcessIO,
10
+ * PooledTransport, WorkerPool, PyodideIO) and RpcClient from having to stub
11
+ * RPC methods they do not implement.
10
12
  *
11
- * All runtime bridges (NodeBridge, PyodideBridge, HttpBridge) extend this class.
13
+ * Reused by RpcClient and all transports.
12
14
  *
13
15
  * @see https://github.com/bbopen/tywrap/issues/149
14
16
  */
15
17
 
16
- import type { RuntimeExecution } from '../types/index.js';
17
18
  import {
18
19
  BridgeDisposedError,
19
20
  BridgeError,
@@ -36,7 +37,7 @@ import {
36
37
  // ═══════════════════════════════════════════════════════════════════════════
37
38
 
38
39
  /**
39
- * Lifecycle states for a BoundedContext.
40
+ * Lifecycle states for a DisposableBase.
40
41
  *
41
42
  * State transitions:
42
43
  * - idle → initializing → ready (on successful init)
@@ -63,16 +64,15 @@ export interface ExecuteOptions<T = unknown> {
63
64
  }
64
65
 
65
66
  // ═══════════════════════════════════════════════════════════════════════════
66
- // BOUNDED CONTEXT BASE CLASS
67
+ // DISPOSABLE BASE CLASS
67
68
  // ═══════════════════════════════════════════════════════════════════════════
68
69
 
69
70
  /**
70
- * Abstract base class for runtime bridges with unified boundary management.
71
- *
72
- * Provides lifecycle management, validation, error classification,
73
- * bounded execution, and resource tracking.
71
+ * Abstract lifecycle/resource base. Provides lifecycle management, validation,
72
+ * error classification, bounded execution, and resource tracking — and nothing
73
+ * about RPC. See the file header for the DisposableBase vs PythonRuntime split.
74
74
  */
75
- export abstract class BoundedContext implements RuntimeExecution {
75
+ export abstract class DisposableBase {
76
76
  // ═══════════════════════════════════════════════════════════════════════════
77
77
  // STATE
78
78
  // ═══════════════════════════════════════════════════════════════════════════
@@ -103,7 +103,7 @@ export abstract class BoundedContext implements RuntimeExecution {
103
103
  }
104
104
 
105
105
  // ═══════════════════════════════════════════════════════════════════════════
106
- // LIFECYCLE (addresses #142, #148)
106
+ // LIFECYCLE
107
107
  // ═══════════════════════════════════════════════════════════════════════════
108
108
 
109
109
  /**
@@ -566,43 +566,4 @@ export abstract class BoundedContext implements RuntimeExecution {
566
566
  protected get resourceCount(): number {
567
567
  return this._resources.size;
568
568
  }
569
-
570
- // ═══════════════════════════════════════════════════════════════════════════
571
- // ABSTRACT METHODS (RuntimeExecution interface)
572
- // ═══════════════════════════════════════════════════════════════════════════
573
-
574
- /**
575
- * Call a Python function.
576
- */
577
- abstract call<T = unknown>(
578
- module: string,
579
- functionName: string,
580
- args: unknown[],
581
- kwargs?: Record<string, unknown>
582
- ): Promise<T>;
583
-
584
- /**
585
- * Instantiate a Python class.
586
- */
587
- abstract instantiate<T = unknown>(
588
- module: string,
589
- className: string,
590
- args: unknown[],
591
- kwargs?: Record<string, unknown>
592
- ): Promise<T>;
593
-
594
- /**
595
- * Call a method on a Python instance.
596
- */
597
- abstract callMethod<T = unknown>(
598
- handle: string,
599
- methodName: string,
600
- args: unknown[],
601
- kwargs?: Record<string, unknown>
602
- ): Promise<T>;
603
-
604
- /**
605
- * Dispose a Python instance.
606
- */
607
- abstract disposeInstance(handle: string): Promise<void>;
608
569
  }
@@ -1,16 +1,20 @@
1
1
  /**
2
- * HTTP runtime bridge for BridgeProtocol.
2
+ * HTTP runtime bridge.
3
3
  *
4
- * HttpBridge extends BridgeProtocol and uses HttpIO transport for
5
- * stateless HTTP POST-based communication with a Python server.
4
+ * HttpBridge is a thin facade: it extends DisposableBase (lifecycle/resources)
5
+ * and implements PythonRuntime by HOLDING an RpcClient over an HttpIO transport
6
+ * for stateless HTTP POST-based communication with a Python server.
6
7
  *
7
8
  * @see https://github.com/bbopen/tywrap/issues/149
8
9
  */
9
10
 
10
- import { BridgeProtocol, type BridgeProtocolOptions } from './bridge-protocol.js';
11
+ import type { PythonRuntime, BridgeInfo } from '../types/index.js';
12
+ import { autoRegisterArrowDecoder } from '../utils/codec.js';
13
+
14
+ import { DisposableBase } from './bounded-context.js';
15
+ import { RpcClient, type GetBridgeInfoOptions } from './rpc-client.js';
11
16
  import { HttpIO } from './http-io.js';
12
17
  import type { CodecOptions } from './safe-codec.js';
13
- import { autoRegisterArrowDecoder } from '../utils/codec.js';
14
18
 
15
19
  // =============================================================================
16
20
  // OPTIONS
@@ -61,33 +65,101 @@ export interface HttpBridgeOptions {
61
65
  * await bridge.dispose();
62
66
  * ```
63
67
  */
64
- export class HttpBridge extends BridgeProtocol {
68
+ export class HttpBridge extends DisposableBase implements PythonRuntime {
69
+ private readonly rpc: RpcClient;
70
+
65
71
  /**
66
72
  * Create a new HttpBridge instance.
67
73
  *
68
74
  * @param options - Configuration options for the bridge
69
75
  */
70
76
  constructor(options: HttpBridgeOptions) {
71
- // Create HTTP transport
77
+ super();
78
+
72
79
  const transport = new HttpIO({
73
80
  baseURL: options.baseURL,
74
81
  headers: options.headers,
75
82
  defaultTimeoutMs: options.timeoutMs,
76
83
  });
77
84
 
78
- // Initialize BridgeProtocol with transport and codec options
79
- const protocolOptions: BridgeProtocolOptions = {
85
+ this.rpc = new RpcClient({
80
86
  transport,
81
87
  codec: options.codec,
82
88
  defaultTimeoutMs: options.timeoutMs,
83
- };
84
-
85
- super(protocolOptions);
89
+ });
90
+ // One disposal chain: facade -> rpc -> transport.
91
+ this.trackResource(this.rpc);
86
92
  }
87
93
 
94
+ // ===========================================================================
95
+ // LIFECYCLE
96
+ // ===========================================================================
97
+
88
98
  protected async doInit(): Promise<void> {
89
99
  // Best-effort: keep apache-arrow optional and avoid breaking non-Node runtimes.
90
100
  await autoRegisterArrowDecoder();
91
- await super.doInit();
101
+ await this.rpc.init();
102
+ }
103
+
104
+ /**
105
+ * No facade-specific teardown: the RpcClient (and its transport) is tracked
106
+ * as a resource and disposed automatically by DisposableBase.
107
+ */
108
+ protected async doDispose(): Promise<void> {
109
+ // Intentionally empty; tracked resources handle disposal.
110
+ }
111
+
112
+ // ===========================================================================
113
+ // RPC METHODS (delegate to the held RpcClient)
114
+ // ===========================================================================
115
+
116
+ async call<T = unknown>(
117
+ module: string,
118
+ functionName: string,
119
+ args: unknown[],
120
+ kwargs?: Record<string, unknown>
121
+ ): Promise<T> {
122
+ await this.ensureReady();
123
+ return this.rpc.call<T>(module, functionName, args, kwargs);
124
+ }
125
+
126
+ async instantiate<T = unknown>(
127
+ module: string,
128
+ className: string,
129
+ args: unknown[],
130
+ kwargs?: Record<string, unknown>
131
+ ): Promise<T> {
132
+ await this.ensureReady();
133
+ return this.rpc.instantiate<T>(module, className, args, kwargs);
134
+ }
135
+
136
+ async callMethod<T = unknown>(
137
+ handle: string,
138
+ methodName: string,
139
+ args: unknown[],
140
+ kwargs?: Record<string, unknown>
141
+ ): Promise<T> {
142
+ await this.ensureReady();
143
+ return this.rpc.callMethod<T>(handle, methodName, args, kwargs);
144
+ }
145
+
146
+ async disposeInstance(handle: string): Promise<void> {
147
+ await this.ensureReady();
148
+ return this.rpc.disposeInstance(handle);
149
+ }
150
+
151
+ async getBridgeInfo(options?: GetBridgeInfoOptions): Promise<BridgeInfo> {
152
+ await this.ensureReady();
153
+ return this.rpc.getBridgeInfo(options);
154
+ }
155
+
156
+ /**
157
+ * Ensure the facade is initialized before delegating an RPC, replicating the
158
+ * auto-init that the bounded execute path provided pre-composition.
159
+ */
160
+ private async ensureReady(): Promise<void> {
161
+ if (!this.isReady) {
162
+ await this.init();
163
+ }
92
164
  }
93
165
  }