tywrap 0.8.0 → 0.9.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.
- package/README.md +22 -7
- package/SECURITY.md +39 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +8 -0
- package/dist/config/index.js.map +1 -1
- package/dist/core/annotation-parser.d.ts +2 -1
- package/dist/core/annotation-parser.d.ts.map +1 -1
- package/dist/core/annotation-parser.js +6 -3
- package/dist/core/annotation-parser.js.map +1 -1
- package/dist/core/generator.d.ts +23 -4
- package/dist/core/generator.d.ts.map +1 -1
- package/dist/core/generator.js +243 -170
- package/dist/core/generator.js.map +1 -1
- package/dist/core/mapper.d.ts +3 -2
- package/dist/core/mapper.d.ts.map +1 -1
- package/dist/core/mapper.js +5 -5
- package/dist/core/mapper.js.map +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/runtime/base-bridge.d.ts +3 -7
- package/dist/runtime/base-bridge.d.ts.map +1 -1
- package/dist/runtime/base-bridge.js +4 -17
- package/dist/runtime/base-bridge.js.map +1 -1
- package/dist/runtime/bounded-context.d.ts +3 -22
- package/dist/runtime/bounded-context.d.ts.map +1 -1
- package/dist/runtime/bounded-context.js +13 -53
- package/dist/runtime/bounded-context.js.map +1 -1
- package/dist/runtime/bridge-codec.d.ts +1 -1
- package/dist/runtime/bridge-codec.d.ts.map +1 -1
- package/dist/runtime/bridge-codec.js +78 -55
- package/dist/runtime/bridge-codec.js.map +1 -1
- package/dist/runtime/errors.d.ts +16 -0
- package/dist/runtime/errors.d.ts.map +1 -1
- package/dist/runtime/errors.js +17 -0
- package/dist/runtime/errors.js.map +1 -1
- package/dist/runtime/http-transport.d.ts +1 -1
- package/dist/runtime/http-transport.d.ts.map +1 -1
- package/dist/runtime/http-transport.js +1 -1
- package/dist/runtime/http-transport.js.map +1 -1
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/node.d.ts +7 -36
- package/dist/runtime/node.d.ts.map +1 -1
- package/dist/runtime/node.js +2 -80
- package/dist/runtime/node.js.map +1 -1
- package/dist/runtime/pooled-transport.d.ts +120 -59
- package/dist/runtime/pooled-transport.d.ts.map +1 -1
- package/dist/runtime/pooled-transport.js +345 -78
- package/dist/runtime/pooled-transport.js.map +1 -1
- package/dist/runtime/pyodide-bootstrap-core.generated.d.ts.map +1 -1
- package/dist/runtime/pyodide-bootstrap-core.generated.js +1 -1
- package/dist/runtime/pyodide-bootstrap-core.generated.js.map +1 -1
- package/dist/runtime/pyodide-transport.d.ts +1 -1
- package/dist/runtime/pyodide-transport.d.ts.map +1 -1
- package/dist/runtime/pyodide-transport.js +2 -3
- package/dist/runtime/pyodide-transport.js.map +1 -1
- package/dist/runtime/rpc-client.d.ts +7 -36
- package/dist/runtime/rpc-client.d.ts.map +1 -1
- package/dist/runtime/rpc-client.js +20 -102
- package/dist/runtime/rpc-client.js.map +1 -1
- package/dist/runtime/subprocess-transport.d.ts +30 -69
- package/dist/runtime/subprocess-transport.d.ts.map +1 -1
- package/dist/runtime/subprocess-transport.js +179 -236
- package/dist/runtime/subprocess-transport.js.map +1 -1
- package/dist/runtime/timed-out-request-tracker.d.ts +2 -1
- package/dist/runtime/timed-out-request-tracker.d.ts.map +1 -1
- package/dist/runtime/transport.d.ts +9 -19
- package/dist/runtime/transport.d.ts.map +1 -1
- package/dist/runtime/transport.js +1 -1
- package/dist/runtime/transport.js.map +1 -1
- package/dist/runtime/validators.d.ts +49 -0
- package/dist/runtime/validators.d.ts.map +1 -1
- package/dist/runtime/validators.js +152 -0
- package/dist/runtime/validators.js.map +1 -1
- package/dist/types/index.d.ts +14 -39
- package/dist/types/index.d.ts.map +1 -1
- package/dist/tywrap.d.ts +3 -2
- package/dist/tywrap.d.ts.map +1 -1
- package/dist/tywrap.js +140 -13
- package/dist/tywrap.js.map +1 -1
- package/dist/utils/cache.d.ts +3 -16
- package/dist/utils/cache.d.ts.map +1 -1
- package/dist/utils/codec.d.ts +1 -0
- package/dist/utils/codec.d.ts.map +1 -1
- package/dist/utils/codec.js +13 -13
- package/dist/utils/codec.js.map +1 -1
- package/dist/utils/ir-cache.d.ts +2 -1
- package/dist/utils/ir-cache.d.ts.map +1 -1
- package/dist/utils/runtime.d.ts +0 -29
- package/dist/utils/runtime.d.ts.map +1 -1
- package/dist/utils/runtime.js +16 -107
- package/dist/utils/runtime.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +7 -6
- package/runtime/frame_codec.py +7 -1
- package/runtime/python_bridge.py +85 -125
- package/runtime/tywrap_bridge_core.py +133 -150
- package/src/config/index.ts +11 -0
- package/src/core/annotation-parser.ts +7 -4
- package/src/core/generator.ts +315 -205
- package/src/core/mapper.ts +8 -8
- package/src/index.ts +7 -4
- package/src/runtime/base-bridge.ts +5 -30
- package/src/runtime/bounded-context.ts +12 -67
- package/src/runtime/bridge-codec.ts +94 -65
- package/src/runtime/errors.ts +21 -0
- package/src/runtime/http-transport.ts +6 -1
- package/src/runtime/index.ts +6 -0
- package/src/runtime/node.ts +9 -120
- package/src/runtime/pooled-transport.ts +424 -90
- package/src/runtime/pyodide-bootstrap-core.generated.ts +1 -1
- package/src/runtime/pyodide-transport.ts +7 -3
- package/src/runtime/rpc-client.ts +37 -139
- package/src/runtime/subprocess-transport.ts +210 -285
- package/src/runtime/timed-out-request-tracker.ts +1 -1
- package/src/runtime/transport.ts +15 -23
- package/src/runtime/validators.ts +204 -0
- package/src/types/index.ts +21 -60
- package/src/tywrap.ts +157 -22
- package/src/utils/cache.ts +3 -3
- package/src/utils/codec.ts +21 -13
- package/src/utils/ir-cache.ts +1 -1
- package/src/utils/runtime.ts +17 -128
- package/src/version.ts +1 -1
- package/dist/core/discovery.d.ts +0 -103
- package/dist/core/discovery.d.ts.map +0 -1
- package/dist/core/discovery.js +0 -380
- package/dist/core/discovery.js.map +0 -1
- package/dist/core/validation.d.ts +0 -102
- package/dist/core/validation.d.ts.map +0 -1
- package/dist/core/validation.js +0 -490
- package/dist/core/validation.js.map +0 -1
- package/dist/runtime/base.d.ts +0 -22
- package/dist/runtime/base.d.ts.map +0 -1
- package/dist/runtime/base.js +0 -23
- package/dist/runtime/base.js.map +0 -1
- package/dist/runtime/transport-pool.d.ts +0 -196
- package/dist/runtime/transport-pool.d.ts.map +0 -1
- package/dist/runtime/transport-pool.js +0 -418
- package/dist/runtime/transport-pool.js.map +0 -1
- package/runtime/__pycache__/_tywrap_conformance_chunking_fixtures.cpython-311.pyc +0 -0
- package/runtime/__pycache__/_tywrap_member_fixtures.cpython-311.pyc +0 -0
- package/runtime/__pycache__/_tywrap_w4_chunking_fixture.cpython-311.pyc +0 -0
- package/runtime/__pycache__/_tywrap_w5_request_chunking_fixture.cpython-311.pyc +0 -0
- package/runtime/__pycache__/_tywrap_w6_pool_chunking_fixture.cpython-311.pyc +0 -0
- package/runtime/__pycache__/frame_codec.cpython-311.pyc +0 -0
- package/runtime/__pycache__/safe_codec.cpython-311.pyc +0 -0
- package/runtime/__pycache__/tywrap_bridge_core.cpython-311.pyc +0 -0
- package/runtime/safe_codec.py +0 -352
- package/src/core/discovery.ts +0 -477
- package/src/core/validation.ts +0 -729
- package/src/runtime/base.ts +0 -24
- package/src/runtime/transport-pool.ts +0 -538
package/src/runtime/transport.ts
CHANGED
|
@@ -57,9 +57,6 @@ export const FRAME_PROTOCOL_VERSION = Number.parseInt(FRAME_PROTOCOL_ID.split('/
|
|
|
57
57
|
*
|
|
58
58
|
* Each method corresponds to a BridgeProtocol operation:
|
|
59
59
|
* - `call`: Invoke a module-level function
|
|
60
|
-
* - `instantiate`: Create a new class instance
|
|
61
|
-
* - `call_method`: Invoke a method on an existing instance
|
|
62
|
-
* - `dispose_instance`: Release an instance handle
|
|
63
60
|
* - `meta`: Get bridge metadata
|
|
64
61
|
*/
|
|
65
62
|
export interface ProtocolMessage {
|
|
@@ -70,25 +67,16 @@ export interface ProtocolMessage {
|
|
|
70
67
|
protocol: typeof PROTOCOL_ID;
|
|
71
68
|
|
|
72
69
|
/** The method to invoke */
|
|
73
|
-
method: 'call' | '
|
|
70
|
+
method: 'call' | 'meta';
|
|
74
71
|
|
|
75
72
|
/** Method parameters */
|
|
76
73
|
params: {
|
|
77
|
-
/** Python module path (for call
|
|
74
|
+
/** Python module path (for call) */
|
|
78
75
|
module?: string;
|
|
79
76
|
|
|
80
77
|
/** Function name (for call) */
|
|
81
78
|
functionName?: string;
|
|
82
79
|
|
|
83
|
-
/** Class name (for instantiate) */
|
|
84
|
-
className?: string;
|
|
85
|
-
|
|
86
|
-
/** Instance handle (for call_method and dispose_instance) */
|
|
87
|
-
handle?: string;
|
|
88
|
-
|
|
89
|
-
/** Method name (for call_method) */
|
|
90
|
-
methodName?: string;
|
|
91
|
-
|
|
92
80
|
/** Positional arguments */
|
|
93
81
|
args?: unknown[];
|
|
94
82
|
|
|
@@ -212,10 +200,8 @@ export interface ChunkFrame {
|
|
|
212
200
|
* authoritative for transport-level flags; the meta report is authoritative for
|
|
213
201
|
* library availability.
|
|
214
202
|
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
* (`enableChunking`) — static and lifecycle-independent, like `supportsArrow`;
|
|
218
|
-
* the per-bridge negotiated fact lives on {@link BridgeInfo}'s `transport` block.
|
|
203
|
+
* `supportsChunking` is always true for the subprocess backend because the
|
|
204
|
+
* packaged Python bridge and JS transport always speak `tywrap-frame/1`.
|
|
219
205
|
* HTTP and Pyodide stay `false`. `supportsStreaming` is `false` on every backend
|
|
220
206
|
* (not implemented in 0.8.0). See docs/transport-capabilities.md for the full
|
|
221
207
|
* matrix.
|
|
@@ -240,8 +226,8 @@ export interface TransportCapabilities {
|
|
|
240
226
|
|
|
241
227
|
/**
|
|
242
228
|
* Whether the transport splits a single logical message across multiple wire
|
|
243
|
-
* frames.
|
|
244
|
-
* `false
|
|
229
|
+
* frames. This is always `true` for the packaged subprocess transport and
|
|
230
|
+
* `false` for HTTP and Pyodide.
|
|
245
231
|
*/
|
|
246
232
|
readonly supportsChunking: boolean;
|
|
247
233
|
|
|
@@ -255,7 +241,8 @@ export interface TransportCapabilities {
|
|
|
255
241
|
* Maximum size, in bytes, of a single wire frame the transport will accept.
|
|
256
242
|
* `Number.POSITIVE_INFINITY` means the transport imposes no frame ceiling of
|
|
257
243
|
* its own (a higher layer — e.g. the codec's payload limit — may still cap the
|
|
258
|
-
* size). For the subprocess backend this is the
|
|
244
|
+
* size). For the subprocess backend this is the always-on framing ceiling for
|
|
245
|
+
* each JSONL wire frame.
|
|
259
246
|
*/
|
|
260
247
|
readonly maxFrameBytes: number;
|
|
261
248
|
}
|
|
@@ -326,7 +313,12 @@ export interface Transport extends Disposable {
|
|
|
326
313
|
* @throws BridgeProtocolError if the message format is invalid
|
|
327
314
|
* @throws BridgeError for other transport-level failures
|
|
328
315
|
*/
|
|
329
|
-
send(
|
|
316
|
+
send(
|
|
317
|
+
message: string,
|
|
318
|
+
timeoutMs: number,
|
|
319
|
+
signal?: AbortSignal,
|
|
320
|
+
requestId?: number
|
|
321
|
+
): Promise<string>;
|
|
330
322
|
|
|
331
323
|
/**
|
|
332
324
|
* Whether the transport is ready to send messages.
|
|
@@ -418,7 +410,7 @@ export function isProtocolMessage(value: unknown): value is ProtocolMessage {
|
|
|
418
410
|
typeof msg.id === 'number' &&
|
|
419
411
|
msg.protocol === PROTOCOL_ID &&
|
|
420
412
|
typeof msg.method === 'string' &&
|
|
421
|
-
|
|
413
|
+
(msg.method === 'call' || msg.method === 'meta') &&
|
|
422
414
|
typeof msg.params === 'object' &&
|
|
423
415
|
msg.params !== null
|
|
424
416
|
);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BridgeValidationError } from './errors.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Pure validation functions for runtime value checking.
|
|
3
5
|
*
|
|
@@ -15,6 +17,208 @@ export class ValidationError extends Error {
|
|
|
15
17
|
}
|
|
16
18
|
}
|
|
17
19
|
|
|
20
|
+
/** A serializable return-contract description emitted by the code generator. */
|
|
21
|
+
export type ReturnSchema =
|
|
22
|
+
| { kind: 'any' }
|
|
23
|
+
| {
|
|
24
|
+
kind: 'primitive';
|
|
25
|
+
type: 'number' | 'string' | 'boolean' | 'null' | 'undefined' | 'Uint8Array' | 'object';
|
|
26
|
+
}
|
|
27
|
+
| { kind: 'literal'; value: string | number | boolean | null }
|
|
28
|
+
| { kind: 'array'; element: ReturnSchema }
|
|
29
|
+
| { kind: 'tuple'; elements: ReturnSchema[] }
|
|
30
|
+
| {
|
|
31
|
+
kind: 'record';
|
|
32
|
+
fields?: Record<string, { schema: ReturnSchema; optional?: boolean }>;
|
|
33
|
+
values?: ReturnSchema;
|
|
34
|
+
}
|
|
35
|
+
| { kind: 'union'; options: ReturnSchema[] }
|
|
36
|
+
| { kind: 'ref'; name: string }
|
|
37
|
+
| { kind: 'marker'; marker: 'dataframe' | 'series' | 'ndarray'; dims?: number; dtype?: string };
|
|
38
|
+
|
|
39
|
+
export type ReturnValidator<T = unknown> = (result: T) => T;
|
|
40
|
+
|
|
41
|
+
export interface DecodedShapeMetadata {
|
|
42
|
+
marker: 'dataframe' | 'series' | 'ndarray';
|
|
43
|
+
dims?: number;
|
|
44
|
+
dtype?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const decodedShapeMetadata = new WeakMap<object, DecodedShapeMetadata>();
|
|
48
|
+
|
|
49
|
+
/** @internal Preserve wire provenance after codec decoding changes the JS shape. */
|
|
50
|
+
export function tagDecodedShape<T>(value: T, metadata: DecodedShapeMetadata): T {
|
|
51
|
+
if (value !== null && (typeof value === 'object' || typeof value === 'function')) {
|
|
52
|
+
decodedShapeMetadata.set(value as object, metadata);
|
|
53
|
+
}
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function isObjectLike(value: unknown): value is object {
|
|
58
|
+
return value !== null && (typeof value === 'object' || typeof value === 'function');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** A short, bounded description suitable for an error message. */
|
|
62
|
+
export function describeReceivedShape(value: unknown): string {
|
|
63
|
+
if (value === null) {
|
|
64
|
+
return 'null';
|
|
65
|
+
}
|
|
66
|
+
if (value === undefined) {
|
|
67
|
+
return 'undefined';
|
|
68
|
+
}
|
|
69
|
+
if (value instanceof Uint8Array) {
|
|
70
|
+
return `Uint8Array(${value.byteLength})`;
|
|
71
|
+
}
|
|
72
|
+
if (Array.isArray(value)) {
|
|
73
|
+
return `array(${value.length})`;
|
|
74
|
+
}
|
|
75
|
+
if (typeof value === 'object') {
|
|
76
|
+
const marker = decodedShapeMetadata.get(value);
|
|
77
|
+
if (marker) {
|
|
78
|
+
const details = [marker.dims === undefined ? undefined : `${marker.dims}d`, marker.dtype]
|
|
79
|
+
.filter(Boolean)
|
|
80
|
+
.join(', ');
|
|
81
|
+
return `${marker.marker}${details ? ` (${details})` : ''}`;
|
|
82
|
+
}
|
|
83
|
+
const name = (value as { constructor?: { name?: unknown } }).constructor?.name;
|
|
84
|
+
return typeof name === 'string' && name !== 'Object' ? name : 'object';
|
|
85
|
+
}
|
|
86
|
+
return typeof value;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function renderSchema(schema: ReturnSchema): string {
|
|
90
|
+
switch (schema.kind) {
|
|
91
|
+
case 'any':
|
|
92
|
+
return 'unknown';
|
|
93
|
+
case 'primitive':
|
|
94
|
+
return schema.type;
|
|
95
|
+
case 'literal':
|
|
96
|
+
return JSON.stringify(schema.value);
|
|
97
|
+
case 'array':
|
|
98
|
+
return `${renderSchema(schema.element)}[]`;
|
|
99
|
+
case 'tuple':
|
|
100
|
+
return `[${schema.elements.map(renderSchema).join(', ')}]`;
|
|
101
|
+
case 'record':
|
|
102
|
+
return 'record';
|
|
103
|
+
case 'union':
|
|
104
|
+
return schema.options.map(renderSchema).join(' | ');
|
|
105
|
+
case 'ref':
|
|
106
|
+
return schema.name;
|
|
107
|
+
case 'marker':
|
|
108
|
+
return schema.marker;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
interface CheckState {
|
|
113
|
+
readonly definitions: Readonly<Record<string, ReturnSchema>>;
|
|
114
|
+
readonly pairs: WeakMap<object, Set<string>>;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function check(schema: ReturnSchema, value: unknown, state: CheckState): boolean {
|
|
118
|
+
if (schema.kind === 'any') {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (schema.kind === 'ref') {
|
|
123
|
+
const definition = state.definitions[schema.name];
|
|
124
|
+
if (!definition) {
|
|
125
|
+
return true;
|
|
126
|
+
} // unresolved/erased types deliberately degrade to unknown.
|
|
127
|
+
if (isObjectLike(value)) {
|
|
128
|
+
const seen = state.pairs.get(value) ?? new Set<string>();
|
|
129
|
+
if (seen.has(schema.name)) {
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
seen.add(schema.name);
|
|
133
|
+
state.pairs.set(value, seen);
|
|
134
|
+
}
|
|
135
|
+
return check(definition, value, state);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
switch (schema.kind) {
|
|
139
|
+
case 'primitive':
|
|
140
|
+
if (schema.type === 'null') {
|
|
141
|
+
return value === null;
|
|
142
|
+
}
|
|
143
|
+
if (schema.type === 'undefined') {
|
|
144
|
+
return value === undefined;
|
|
145
|
+
}
|
|
146
|
+
if (schema.type === 'Uint8Array') {
|
|
147
|
+
return value instanceof Uint8Array;
|
|
148
|
+
}
|
|
149
|
+
if (schema.type === 'object') {
|
|
150
|
+
return isPlainObject(value);
|
|
151
|
+
}
|
|
152
|
+
return typeof value === schema.type;
|
|
153
|
+
case 'literal':
|
|
154
|
+
return Object.is(value, schema.value);
|
|
155
|
+
case 'array':
|
|
156
|
+
return Array.isArray(value) && value.every(item => check(schema.element, item, state));
|
|
157
|
+
case 'tuple':
|
|
158
|
+
return (
|
|
159
|
+
Array.isArray(value) &&
|
|
160
|
+
value.length === schema.elements.length &&
|
|
161
|
+
schema.elements.every((entry, index) => check(entry, value[index], state))
|
|
162
|
+
);
|
|
163
|
+
case 'record': {
|
|
164
|
+
if (!isPlainObject(value)) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
if (schema.fields) {
|
|
168
|
+
for (const [key, field] of Object.entries(schema.fields)) {
|
|
169
|
+
if (!(key in value)) {
|
|
170
|
+
if (field.optional) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
if (!check(field.schema, value[key], state)) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return (
|
|
181
|
+
!schema.values ||
|
|
182
|
+
Object.values(value).every(item => check(schema.values as ReturnSchema, item, state))
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
case 'union':
|
|
186
|
+
return schema.options.some(option => check(option, value, state));
|
|
187
|
+
case 'marker': {
|
|
188
|
+
const metadata = isObjectLike(value) ? decodedShapeMetadata.get(value) : undefined;
|
|
189
|
+
if (metadata?.marker !== schema.marker) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
return (
|
|
193
|
+
(schema.dims === undefined || metadata.dims === schema.dims) &&
|
|
194
|
+
(schema.dtype === undefined || metadata.dtype === schema.dtype)
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Construct a cycle-safe structural validator for one generated callable.
|
|
202
|
+
* Unknown/Any/void schemas intentionally validate nothing.
|
|
203
|
+
*/
|
|
204
|
+
export function createReturnValidator<T = unknown>(
|
|
205
|
+
schema: ReturnSchema,
|
|
206
|
+
callSite: string,
|
|
207
|
+
definitions: Readonly<Record<string, ReturnSchema>> = {}
|
|
208
|
+
): ReturnValidator<T> {
|
|
209
|
+
const declaredType = renderSchema(schema);
|
|
210
|
+
return (result: T): T => {
|
|
211
|
+
if (!check(schema, result, { definitions, pairs: new WeakMap<object, Set<string>>() })) {
|
|
212
|
+
throw new BridgeValidationError({
|
|
213
|
+
declaredType,
|
|
214
|
+
receivedShape: describeReceivedShape(result),
|
|
215
|
+
callSite,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
return result;
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
18
222
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
19
223
|
// TYPE GUARDS
|
|
20
224
|
// ═══════════════════════════════════════════════════════════════════════════
|
package/src/types/index.ts
CHANGED
|
@@ -13,6 +13,13 @@ export interface PythonModule {
|
|
|
13
13
|
exports: string[];
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/** A pinned, versioned JSON IR artifact emitted alongside generated wrappers. */
|
|
17
|
+
export interface IrContract {
|
|
18
|
+
ir_version: string;
|
|
19
|
+
module: string;
|
|
20
|
+
[key: string]: unknown;
|
|
21
|
+
}
|
|
22
|
+
|
|
16
23
|
/**
|
|
17
24
|
* How a callable is bound on its owning class.
|
|
18
25
|
*
|
|
@@ -261,7 +268,8 @@ export interface TSPrimitiveType {
|
|
|
261
268
|
| 'void'
|
|
262
269
|
| 'unknown'
|
|
263
270
|
| 'never'
|
|
264
|
-
| 'object'
|
|
271
|
+
| 'object'
|
|
272
|
+
| 'Uint8Array';
|
|
265
273
|
}
|
|
266
274
|
|
|
267
275
|
export interface TSArrayType {
|
|
@@ -314,6 +322,7 @@ export interface TSParameter {
|
|
|
314
322
|
export interface TSGenericType {
|
|
315
323
|
kind: 'generic';
|
|
316
324
|
name: string;
|
|
325
|
+
module?: string;
|
|
317
326
|
typeArgs: TypescriptType[];
|
|
318
327
|
}
|
|
319
328
|
|
|
@@ -338,6 +347,11 @@ export interface TywrapOptions {
|
|
|
338
347
|
* and discovery (IR extraction). Useful for local modules not installed in site-packages.
|
|
339
348
|
*/
|
|
340
349
|
pythonImportPath?: string[];
|
|
350
|
+
/**
|
|
351
|
+
* Read one pinned IR contract instead of spawning Python. A string applies to
|
|
352
|
+
* every configured module; a record selects a contract path per module.
|
|
353
|
+
*/
|
|
354
|
+
contractInput?: string | Record<string, string>;
|
|
341
355
|
output: OutputConfig;
|
|
342
356
|
runtime: RuntimeConfig;
|
|
343
357
|
performance: PerformanceConfig;
|
|
@@ -404,6 +418,8 @@ export interface PerformanceConfig {
|
|
|
404
418
|
compression: 'auto' | 'gzip' | 'brotli' | 'none';
|
|
405
419
|
}
|
|
406
420
|
|
|
421
|
+
// `numpy` is intentionally accepted as a no-op in 0.9: decoded ndarrays are JS
|
|
422
|
+
// arrays, and a faithful static shape awaits #268 return validation.
|
|
407
423
|
export type TypePreset = 'numpy' | 'pandas' | 'pydantic' | 'stdlib' | 'scipy' | 'torch' | 'sklearn';
|
|
408
424
|
|
|
409
425
|
export interface TypeMappingConfig {
|
|
@@ -413,25 +429,6 @@ export interface TypeMappingConfig {
|
|
|
413
429
|
/** Known bridge backends. Each speaks the identical "tywrap/1" protocol. */
|
|
414
430
|
export type BridgeBackend = 'python-subprocess' | 'pyodide' | 'http';
|
|
415
431
|
|
|
416
|
-
/**
|
|
417
|
-
* Optional chunked-transport negotiation block in {@link BridgeInfo}.
|
|
418
|
-
*
|
|
419
|
-
* Reported by a bridge that understands the `tywrap-frame/1` framing protocol
|
|
420
|
-
* (subprocess only, 0.8.0). It lets the JS side learn, via the `meta` probe,
|
|
421
|
-
* whether the bridge can reassemble chunked frames and the maximum single-frame
|
|
422
|
-
* size it will accept. Absent on old bridges (and on HTTP/Pyodide, which stay
|
|
423
|
-
* single-frame in 0.8.0) — absence means "no chunking", which is backward
|
|
424
|
-
* compatible. See docs/transport-framing.md.
|
|
425
|
-
*/
|
|
426
|
-
export interface BridgeTransportInfo {
|
|
427
|
-
/** Framing protocol the bridge speaks (e.g. `'tywrap-frame/1'`). */
|
|
428
|
-
frameProtocol: string;
|
|
429
|
-
/** Whether the bridge can fragment/reassemble chunked frames. */
|
|
430
|
-
supportsChunking: boolean;
|
|
431
|
-
/** Maximum size, in bytes, of a single wire frame the bridge will accept. */
|
|
432
|
-
maxFrameBytes: number;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
432
|
export interface BridgeInfo {
|
|
436
433
|
protocol: string;
|
|
437
434
|
protocolVersion: number;
|
|
@@ -445,12 +442,6 @@ export interface BridgeInfo {
|
|
|
445
442
|
torchAvailable: boolean;
|
|
446
443
|
sklearnAvailable: boolean;
|
|
447
444
|
instances: number;
|
|
448
|
-
/**
|
|
449
|
-
* Optional chunked-transport negotiation block. Present only when the bridge
|
|
450
|
-
* advertises `tywrap-frame/1` framing; absent on old bridges and on
|
|
451
|
-
* HTTP/Pyodide (single-frame in 0.8.0). See {@link BridgeTransportInfo}.
|
|
452
|
-
*/
|
|
453
|
-
transport?: BridgeTransportInfo;
|
|
454
445
|
}
|
|
455
446
|
|
|
456
447
|
// Analysis and generation results
|
|
@@ -499,7 +490,7 @@ export interface GenerationMetadata {
|
|
|
499
490
|
optimizations: string[];
|
|
500
491
|
}
|
|
501
492
|
|
|
502
|
-
// PythonRuntime — the
|
|
493
|
+
// PythonRuntime — the call-only cross-boundary RPC method generated wrappers call.
|
|
503
494
|
// This is the contract that, after the composition rework, is implemented ONLY
|
|
504
495
|
// by the bridge facades (NodeBridge/HttpBridge/PyodideBridge); the facades
|
|
505
496
|
// satisfy it by delegating to an owned RpcClient. It deliberately carries NO
|
|
@@ -511,45 +502,15 @@ export interface PythonRuntime {
|
|
|
511
502
|
module: string,
|
|
512
503
|
functionName: string,
|
|
513
504
|
args: unknown[],
|
|
514
|
-
kwargs?: Record<string, unknown
|
|
505
|
+
kwargs?: Record<string, unknown>,
|
|
506
|
+
validate?: (result: T) => void
|
|
515
507
|
): Promise<T>;
|
|
516
|
-
|
|
517
|
-
instantiate<T = unknown>(
|
|
518
|
-
module: string,
|
|
519
|
-
className: string,
|
|
520
|
-
args: unknown[],
|
|
521
|
-
kwargs?: Record<string, unknown>
|
|
522
|
-
): Promise<T>;
|
|
523
|
-
|
|
524
|
-
callMethod<T = unknown>(
|
|
525
|
-
handle: string,
|
|
526
|
-
methodName: string,
|
|
527
|
-
args: unknown[],
|
|
528
|
-
kwargs?: Record<string, unknown>
|
|
529
|
-
): Promise<T>;
|
|
530
|
-
|
|
531
|
-
disposeInstance(handle: string): Promise<void>;
|
|
532
508
|
}
|
|
533
509
|
|
|
534
|
-
// Runtime bridge interface — the
|
|
510
|
+
// Runtime bridge interface — the call RPC method plus lifecycle dispose().
|
|
535
511
|
// Kept as PythonRuntime + dispose() so getRuntimeBridge() and every existing
|
|
536
512
|
// `RuntimeExecution` reference (registry, dev.ts) compile with zero churn. The
|
|
537
513
|
// RuntimeExecution -> PythonRuntime symbol rename is deferred to the T9 pass.
|
|
538
514
|
export interface RuntimeExecution extends PythonRuntime {
|
|
539
515
|
dispose(): Promise<void>;
|
|
540
516
|
}
|
|
541
|
-
|
|
542
|
-
// Utility types
|
|
543
|
-
export type DeepReadonly<T> = {
|
|
544
|
-
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
|
|
545
|
-
};
|
|
546
|
-
|
|
547
|
-
export type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
|
548
|
-
|
|
549
|
-
export type RequiredKeys<T> = {
|
|
550
|
-
[K in keyof T]-?: {} extends Pick<T, K> ? never : K;
|
|
551
|
-
}[keyof T];
|
|
552
|
-
|
|
553
|
-
export type OptionalKeys<T> = {
|
|
554
|
-
[K in keyof T]-?: {} extends Pick<T, K> ? K : never;
|
|
555
|
-
}[keyof T];
|