tywrap 0.6.1 → 0.8.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 (110) hide show
  1. package/README.md +15 -5
  2. package/dist/core/annotation-parser.d.ts.map +1 -1
  3. package/dist/core/annotation-parser.js.map +1 -1
  4. package/dist/core/emit-call.d.ts.map +1 -1
  5. package/dist/core/emit-call.js +1 -1
  6. package/dist/core/emit-call.js.map +1 -1
  7. package/dist/core/generator.d.ts.map +1 -1
  8. package/dist/core/generator.js +40 -9
  9. package/dist/core/generator.js.map +1 -1
  10. package/dist/dev.d.ts.map +1 -1
  11. package/dist/dev.js +1 -3
  12. package/dist/dev.js.map +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/runtime/base-bridge.d.ts +57 -0
  17. package/dist/runtime/base-bridge.d.ts.map +1 -0
  18. package/dist/runtime/base-bridge.js +72 -0
  19. package/dist/runtime/base-bridge.js.map +1 -0
  20. package/dist/runtime/frame-codec.d.ts +111 -0
  21. package/dist/runtime/frame-codec.d.ts.map +1 -0
  22. package/dist/runtime/frame-codec.js +352 -0
  23. package/dist/runtime/frame-codec.js.map +1 -0
  24. package/dist/runtime/http-transport.d.ts +11 -1
  25. package/dist/runtime/http-transport.d.ts.map +1 -1
  26. package/dist/runtime/http-transport.js +19 -0
  27. package/dist/runtime/http-transport.js.map +1 -1
  28. package/dist/runtime/http.d.ts +5 -12
  29. package/dist/runtime/http.d.ts.map +1 -1
  30. package/dist/runtime/http.js +6 -29
  31. package/dist/runtime/http.js.map +1 -1
  32. package/dist/runtime/index.d.ts +2 -2
  33. package/dist/runtime/index.d.ts.map +1 -1
  34. package/dist/runtime/index.js +1 -1
  35. package/dist/runtime/index.js.map +1 -1
  36. package/dist/runtime/node.d.ts +25 -19
  37. package/dist/runtime/node.d.ts.map +1 -1
  38. package/dist/runtime/node.js +19 -34
  39. package/dist/runtime/node.js.map +1 -1
  40. package/dist/runtime/pooled-transport.d.ts +21 -2
  41. package/dist/runtime/pooled-transport.d.ts.map +1 -1
  42. package/dist/runtime/pooled-transport.js +16 -0
  43. package/dist/runtime/pooled-transport.js.map +1 -1
  44. package/dist/runtime/pyodide-bootstrap-core.generated.d.ts.map +1 -1
  45. package/dist/runtime/pyodide-bootstrap-core.generated.js +1 -1
  46. package/dist/runtime/pyodide-bootstrap-core.generated.js.map +1 -1
  47. package/dist/runtime/pyodide-transport.d.ts +12 -1
  48. package/dist/runtime/pyodide-transport.d.ts.map +1 -1
  49. package/dist/runtime/pyodide-transport.js +20 -0
  50. package/dist/runtime/pyodide-transport.js.map +1 -1
  51. package/dist/runtime/pyodide.d.ts +5 -12
  52. package/dist/runtime/pyodide.d.ts.map +1 -1
  53. package/dist/runtime/pyodide.js +6 -29
  54. package/dist/runtime/pyodide.js.map +1 -1
  55. package/dist/runtime/rpc-client.d.ts +14 -1
  56. package/dist/runtime/rpc-client.d.ts.map +1 -1
  57. package/dist/runtime/rpc-client.js +68 -6
  58. package/dist/runtime/rpc-client.js.map +1 -1
  59. package/dist/runtime/subprocess-transport.d.ts +177 -3
  60. package/dist/runtime/subprocess-transport.d.ts.map +1 -1
  61. package/dist/runtime/subprocess-transport.js +526 -26
  62. package/dist/runtime/subprocess-transport.js.map +1 -1
  63. package/dist/runtime/transport.d.ts +141 -0
  64. package/dist/runtime/transport.d.ts.map +1 -1
  65. package/dist/runtime/transport.js +21 -0
  66. package/dist/runtime/transport.js.map +1 -1
  67. package/dist/types/index.d.ts +59 -0
  68. package/dist/types/index.d.ts.map +1 -1
  69. package/dist/tywrap.d.ts.map +1 -1
  70. package/dist/tywrap.js +204 -149
  71. package/dist/tywrap.js.map +1 -1
  72. package/dist/utils/codec.d.ts +2 -0
  73. package/dist/utils/codec.d.ts.map +1 -1
  74. package/dist/utils/codec.js +205 -6
  75. package/dist/utils/codec.js.map +1 -1
  76. package/dist/version.js +1 -1
  77. package/package.json +7 -1
  78. package/runtime/__pycache__/_tywrap_conformance_chunking_fixtures.cpython-311.pyc +0 -0
  79. package/runtime/__pycache__/_tywrap_member_fixtures.cpython-311.pyc +0 -0
  80. package/runtime/__pycache__/_tywrap_w4_chunking_fixture.cpython-311.pyc +0 -0
  81. package/runtime/__pycache__/_tywrap_w5_request_chunking_fixture.cpython-311.pyc +0 -0
  82. package/runtime/__pycache__/_tywrap_w6_pool_chunking_fixture.cpython-311.pyc +0 -0
  83. package/runtime/__pycache__/frame_codec.cpython-311.pyc +0 -0
  84. package/runtime/__pycache__/safe_codec.cpython-311.pyc +0 -0
  85. package/runtime/__pycache__/tywrap_bridge_core.cpython-311.pyc +0 -0
  86. package/runtime/frame_codec.py +424 -0
  87. package/runtime/python_bridge.py +241 -42
  88. package/runtime/tywrap_bridge_core.py +152 -13
  89. package/src/core/annotation-parser.ts +2 -1
  90. package/src/core/emit-call.ts +1 -7
  91. package/src/core/generator.ts +50 -11
  92. package/src/dev.ts +1 -3
  93. package/src/index.ts +1 -0
  94. package/src/runtime/base-bridge.ts +106 -0
  95. package/src/runtime/frame-codec.ts +469 -0
  96. package/src/runtime/http-transport.ts +21 -1
  97. package/src/runtime/http.ts +7 -51
  98. package/src/runtime/index.ts +2 -6
  99. package/src/runtime/node.ts +42 -53
  100. package/src/runtime/pooled-transport.ts +25 -2
  101. package/src/runtime/pyodide-bootstrap-core.generated.ts +1 -1
  102. package/src/runtime/pyodide-transport.ts +22 -0
  103. package/src/runtime/pyodide.ts +7 -52
  104. package/src/runtime/rpc-client.ts +91 -7
  105. package/src/runtime/subprocess-transport.ts +629 -30
  106. package/src/runtime/transport.ts +169 -0
  107. package/src/types/index.ts +62 -0
  108. package/src/tywrap.ts +265 -162
  109. package/src/utils/codec.ts +245 -7
  110. package/src/version.ts +1 -1
@@ -18,7 +18,7 @@
18
18
  * @see https://github.com/bbopen/tywrap/issues/149
19
19
  */
20
20
 
21
- import type { BridgeInfo } from '../types/index.js';
21
+ import type { BridgeBackend, BridgeInfo, BridgeTransportInfo } from '../types/index.js';
22
22
 
23
23
  import { DisposableBase, type ExecuteOptions } from './bounded-context.js';
24
24
  import { BridgeProtocolError } from './errors.js';
@@ -27,6 +27,7 @@ import {
27
27
  PROTOCOL_ID,
28
28
  TYWRAP_PROTOCOL_VERSION,
29
29
  type Transport,
30
+ type TransportCapabilities,
30
31
  type ProtocolMessage,
31
32
  } from './transport.js';
32
33
 
@@ -74,8 +75,12 @@ function validateBridgeInfoPayload(value: unknown): BridgeInfo {
74
75
  torchAvailable?: unknown;
75
76
  sklearnAvailable?: unknown;
76
77
  instances?: unknown;
78
+ transport?: unknown;
77
79
  }
78
80
 
81
+ /** Honest set of backend identities a `meta` payload may report. */
82
+ const KNOWN_BRIDGES: readonly BridgeBackend[] = ['python-subprocess', 'pyodide', 'http'];
83
+
79
84
  const formatValue = (val: unknown): string => {
80
85
  try {
81
86
  const serialized = JSON.stringify(val);
@@ -101,10 +106,16 @@ function validateBridgeInfoPayload(value: unknown): BridgeInfo {
101
106
  );
102
107
  }
103
108
 
109
+ // Accept the honest BridgeBackend union (subprocess/pyodide/http). All
110
+ // backends speak the identical "tywrap/1" protocol; relaxing this from the
111
+ // old hardcoded 'python-subprocess' lets the Pyodide/HTTP facades route
112
+ // getBridgeInfo() through this same validator without being rejected.
104
113
  const bridge = obj.bridge;
105
- if (bridge !== 'python-subprocess') {
114
+ if (typeof bridge !== 'string' || !KNOWN_BRIDGES.includes(bridge as BridgeBackend)) {
106
115
  throw new BridgeProtocolError(
107
- `Invalid bridge info payload: bridge expected "python-subprocess", got ${formatValue(bridge)}`
116
+ `Invalid bridge info payload: bridge expected one of ${KNOWN_BRIDGES.map(b => `"${b}"`).join(
117
+ ', '
118
+ )}, got ${formatValue(bridge)}`
108
119
  );
109
120
  }
110
121
 
@@ -115,10 +126,14 @@ function validateBridgeInfoPayload(value: unknown): BridgeInfo {
115
126
  );
116
127
  }
117
128
 
129
+ // pid is OPTIONAL across backends: subprocess reports a real OS pid (positive
130
+ // integer); in-WASM Pyodide (and HTTP) have no local process and report null.
131
+ // Accept a positive integer OR null; reject any other shape (e.g. 0, negative,
132
+ // non-integer, string).
118
133
  const pid = obj.pid;
119
- if (typeof pid !== 'number' || !Number.isInteger(pid) || pid <= 0) {
134
+ if (pid !== null && (typeof pid !== 'number' || !Number.isInteger(pid) || pid <= 0)) {
120
135
  throw new BridgeProtocolError(
121
- `Invalid bridge info payload: pid expected positive integer, got ${formatValue(pid)}`
136
+ `Invalid bridge info payload: pid expected positive integer or null, got ${formatValue(pid)}`
122
137
  );
123
138
  }
124
139
 
@@ -164,10 +179,59 @@ function validateBridgeInfoPayload(value: unknown): BridgeInfo {
164
179
  );
165
180
  }
166
181
 
167
- return {
182
+ // OPTIONAL chunked-transport negotiation block. Absent on old bridges and on
183
+ // HTTP/Pyodide (single-frame in 0.8.0) — absence is backward compatible. When
184
+ // present, validate every field and CARRY IT THROUGH so negotiation data
185
+ // survives the rebuild below (the old validator silently dropped unknown
186
+ // fields, which would have discarded this block).
187
+ let transport: BridgeTransportInfo | undefined;
188
+ const rawTransport = obj.transport;
189
+ if (rawTransport !== undefined) {
190
+ if (!rawTransport || typeof rawTransport !== 'object' || Array.isArray(rawTransport)) {
191
+ const kind =
192
+ rawTransport === null
193
+ ? 'null'
194
+ : Array.isArray(rawTransport)
195
+ ? 'array'
196
+ : typeof rawTransport;
197
+ throw new BridgeProtocolError(
198
+ `Invalid bridge info payload: transport expected object, got ${kind}`
199
+ );
200
+ }
201
+ const t = rawTransport as {
202
+ frameProtocol?: unknown;
203
+ supportsChunking?: unknown;
204
+ maxFrameBytes?: unknown;
205
+ };
206
+ const frameProtocol = t.frameProtocol;
207
+ if (typeof frameProtocol !== 'string' || frameProtocol.length === 0) {
208
+ throw new BridgeProtocolError(
209
+ `Invalid bridge info payload: transport.frameProtocol expected non-empty string, got ${formatValue(frameProtocol)}`
210
+ );
211
+ }
212
+ const supportsChunking = t.supportsChunking;
213
+ if (typeof supportsChunking !== 'boolean') {
214
+ throw new BridgeProtocolError(
215
+ `Invalid bridge info payload: transport.supportsChunking expected boolean, got ${formatValue(supportsChunking)}`
216
+ );
217
+ }
218
+ const maxFrameBytes = t.maxFrameBytes;
219
+ if (
220
+ typeof maxFrameBytes !== 'number' ||
221
+ !Number.isInteger(maxFrameBytes) ||
222
+ maxFrameBytes <= 0
223
+ ) {
224
+ throw new BridgeProtocolError(
225
+ `Invalid bridge info payload: transport.maxFrameBytes expected positive integer, got ${formatValue(maxFrameBytes)}`
226
+ );
227
+ }
228
+ transport = { frameProtocol, supportsChunking, maxFrameBytes };
229
+ }
230
+
231
+ const info: BridgeInfo = {
168
232
  protocol: PROTOCOL_ID,
169
233
  protocolVersion: TYWRAP_PROTOCOL_VERSION,
170
- bridge: 'python-subprocess',
234
+ bridge: bridge as BridgeBackend,
171
235
  pythonVersion,
172
236
  pid,
173
237
  codecFallback,
@@ -177,6 +241,10 @@ function validateBridgeInfoPayload(value: unknown): BridgeInfo {
177
241
  sklearnAvailable,
178
242
  instances,
179
243
  };
244
+ if (transport !== undefined) {
245
+ info.transport = transport;
246
+ }
247
+ return info;
180
248
  }
181
249
 
182
250
  // =============================================================================
@@ -518,4 +586,20 @@ export class RpcClient extends DisposableBase {
518
586
  this.bridgeInfoCache = info;
519
587
  return info;
520
588
  }
589
+
590
+ /**
591
+ * Report the underlying transport's static capability descriptor.
592
+ *
593
+ * This returns the transport-level flags (Arrow/binary carriage, framing,
594
+ * chunking/streaming, max frame size) WITHOUT any network round-trip — the
595
+ * descriptor is authoritative for what the wire channel can do. It is
596
+ * deliberately distinct from {@link getBridgeInfo}, which reports the *Python
597
+ * environment* (which optional libraries are importable). Callers that need
598
+ * both — "can this transport carry Arrow AND does this Python have pyarrow?" —
599
+ * should consult both: the transport descriptor for the channel, the bridge
600
+ * info for library availability.
601
+ */
602
+ capabilities(): TransportCapabilities {
603
+ return this.transport.capabilities();
604
+ }
521
605
  }