|
1 | 1 | # Changelog |
2 | 2 |
|
| 3 | +## [0.9.0](https://github.com/bbopen/tywrap/compare/v0.8.0...v0.9.0) (2026-07-11) |
| 4 | + |
| 5 | +Typed value-RPC, one job done well. 0.9.0 removes the stateful instance API whose handles silently broke under pooling, collapses the transport stack it no longer needs, and makes the generated types tell the truth: a type you see in a generated wrapper is now either backed by a declaration and a codec, or it is `unknown` — and what comes back over the wire is validated against it at runtime. Generated wrappers must be regenerated: the IR schema is now `0.4.0` on both sides, and a version mismatch fails generation with a clear message. |
| 6 | + |
| 7 | +### Breaking |
| 8 | + |
| 9 | +- **The stateful instance API is gone ([#264](https://github.com/bbopen/tywrap/issues/264), [#265](https://github.com/bbopen/tywrap/issues/265), [#266](https://github.com/bbopen/tywrap/issues/266)).** A handle lived in one pool worker while calls routed to any worker, so instance methods returned wrong results under `maxProcesses > 1`. The server no longer accepts `instantiate`/`call_method`/`dispose_instance` (structured unknown-method error), the client and wire protocol are call-only (`call | meta`), and generated classes expose only static and classmethod members routed through ordinary calls. Classes that lose members carry a migration note pointing at value-returning module functions. |
| 10 | +- **Generated types degrade honestly ([#267](https://github.com/bbopen/tywrap/issues/267)).** A return type tywrap cannot resolve to a local declaration or a codec-backed value is emitted as `unknown` instead of a bare, undeclared TypeScript name — and every degrade feeds the generation report, so `--fail-on-warn` builds fail on them by design. Two long-catalogued type lies are fixed with the same stroke: Python `bytes` maps to `Uint8Array` (what the decoder actually delivers), and `set[T]` maps to `T[]` (sets arrive as arrays; sending a `Set` was already a loud error). Iterator-protocol returns (`Generator`, `Iterator`, `AsyncGenerator`, `AsyncIterator`) degrade to `unknown` for the same reason: the bridge rejects iterator objects loudly at serialization, so those annotations could never carry a value — while `Iterable[T]`/`Sequence[T]` keep mapping to types an actually-decoded list satisfies. |
| 11 | +- **Returns are validated at runtime ([#268](https://github.com/bbopen/tywrap/issues/268)).** Every generated module-function and static-method call checks the decoded result against its declared type before the promise resolves; a Python function annotated `-> int` that returns a string now throws `BridgeValidationError` (with the declared type, the received shape, and the call site) instead of surfacing as a mistyped `Promise<number>`. Columnar values are checked by provenance — marker, dims, dtype — so DataFrame/ndarray returns validate by shape and the Arrow fast path stays walk-free. `unknown`, `void`, and `Any` returns validate nothing, by design. |
| 12 | +- **The IR is a pinned, versioned contract ([#269](https://github.com/bbopen/tywrap/issues/269)).** Generation writes `<module>.contract.json` next to the generated code — byte-stable across machines and Python processes — so `generate --check` flags drift; `contractInput` regenerates from the pinned file without spawning Python; and `IR_VERSION` is `0.4.0` on both sides, checked at generation time with an actionable mismatch error. |
| 13 | +- **Call results are never cached, and requests get exactly one attempt.** The heuristic `enableCache` result cache is removed (a "pure function" guess that could serve stale results), along with the request retry machinery (retries could reuse an in-flight request id and cross-wire responses). Timeouts and errors surface to the caller unchanged — once. |
| 14 | +- **The codec rejects what JSON would mangle.** `Map` and `Set` values in requests fail loudly with a conversion hint instead of serializing as `{}`; `bytesHandling: 'passthrough'` is removed; non-finite numbers keep failing as before. |
| 15 | +- **Pool and transport defaults tightened.** `maxConcurrentPerProcess` defaults to `1` (the Python bridge is a serial loop; concurrency comes from more workers, and pipelining is explicit opt-in). Pool limits are validated at `NodeBridge` construction (synchronous throw) rather than first use. The `TYWRAP_TRANSPORT_CHUNKING`/`TYWRAP_TRANSPORT_FRAME_PROTOCOL`/`TYWRAP_TRANSPORT_MAX_FRAME_BYTES` negotiation env vars are gone: the npm package ships its own Python bridge, so version skew is impossible and `tywrap-frame/1` framing is simply always on for the subprocess transport. |
| 16 | + |
| 17 | +### Features |
| 18 | + |
| 19 | +- **Security model documented.** `SECURITY.md` states the bridge trust model — the two bounds are the `TYWRAP_ALLOWED_MODULES` allowlist and the on-by-default private-attribute block (`TYWRAP_ALLOW_PRIVATE_ATTRS` opts out) — plus the server-author contract for network exposure and private vulnerability reporting. ([#262](https://github.com/bbopen/tywrap/issues/262), [#263](https://github.com/bbopen/tywrap/issues/263)) |
| 20 | +- **Docs describe what the types actually cover** — annotation-derived typing with `unknown` fallbacks, not "full type safety"; Deno marked experimental and untested in CI. ([#261](https://github.com/bbopen/tywrap/issues/261)) |
| 21 | + |
| 22 | +### Internal |
| 23 | + |
| 24 | +- The subprocess transport stack collapsed from six layers to a merged pool/transport with atomic threshold restarts: a restarting worker can never receive dispatches, cancelled requests never count toward restart thresholds, and disposal fences out racing restarts so no orphan Python process survives. |
| 25 | +- Python-side codec unified: `safe_codec.py` merged into `tywrap_bridge_core.py`; plain values no longer cold-import the scientific stack (first-call serialize cost 1.6 s → microseconds); duplicate first frames surface the duplicate-sequence protocol error instead of silently resetting reassembly. |
| 26 | +- The test suite was rebuilt around discrimination: an adversarial cross-library menagerie (`test/menagerie/`) catalogues every round-trip behavior as honest/loud/known, ~2,600 lines of vacuous or tautological tests were removed, and the resurrected adversarial suite caught real cross-PR interactions before they shipped. |
| 27 | +- `tywrap-ir` is published as `0.3.0` (IR schema `0.4.0`). |
| 28 | + |
3 | 29 | ## [0.8.0](https://github.com/bbopen/tywrap/compare/v0.7.0...v0.8.0) (2026-06-01) |
4 | 30 |
|
5 | 31 | The large-payload transport release — the second half of the scientific data plane. A result that exceeds a single JSONL line no longer has to fit on one line: the subprocess bridge splits it into frames and reassembles it byte-for-byte. The wire protocol is unchanged (`tywrap/1`); framing is a separate, additive `tywrap-frame/1` protocol negotiated at startup, so a 0.7.x bridge and a 0.8.0 client still talk, and an oversize payload sent to a bridge that cannot chunk fails loudly instead of silently truncating. |
|
0 commit comments