Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
node-version: 24
cache: npm
- name: Setup Python
uses: actions/setup-python@v6
Expand All @@ -45,7 +45,7 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: [20, 22]
node-version: [20, 24]
python-version: ['3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v6
Expand All @@ -68,11 +68,11 @@ jobs:
env:
NODE_OPTIONS: --expose-gc
TYWRAP_PERF_BUDGETS: '1'
run: ${{ (matrix.node-version == 22 && matrix.python-version == '3.11') && 'npm run test:coverage' || 'npm test' }}
run: ${{ (matrix.node-version == 24 && matrix.python-version == '3.11') && 'npm run test:coverage' || 'npm test' }}

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v6
if: matrix.node-version == 22 && matrix.python-version == '3.11'
if: matrix.node-version == 24 && matrix.python-version == '3.11'
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/lcov.info
Expand Down Expand Up @@ -124,7 +124,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
node-version: 24
cache: npm
- name: Setup Python
uses: actions/setup-python@v6
Expand Down Expand Up @@ -179,7 +179,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
node-version: 24
cache: npm
- name: Setup Python
uses: actions/setup-python@v6
Expand Down Expand Up @@ -209,7 +209,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
node-version: 24
cache: npm
- name: Setup Python
uses: actions/setup-python@v6
Expand Down Expand Up @@ -239,7 +239,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
node-version: 24
cache: npm
- name: Setup Python
uses: actions/setup-python@v6
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:

- uses: actions/setup-node@v6
with:
node-version: 22
node-version: 24
cache: npm

- uses: actions/configure-pages@v6
Expand Down
8 changes: 1 addition & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ jobs:

- uses: actions/setup-node@v6
with:
node-version: 22
node-version: 24
cache: npm

- name: Skip if version is already published
Expand Down Expand Up @@ -212,12 +212,6 @@ jobs:
NODE_OPTIONS: --expose-gc
TYWRAP_PERF_BUDGETS: '1'

- name: Switch to Node 24 for npm trusted publishing
if: ${{ steps.npm_registry.outputs.already_published != 'true' }}
uses: actions/setup-node@v6
with:
node-version: 24

- name: Publish to npm
if: ${{ steps.npm_registry.outputs.already_published != 'true' }}
env:
Expand Down
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Changelog

## [0.4.0](https://github.com/bbopen/tywrap/compare/v0.3.1...v0.4.0) (2026-04-12)

### Features

* add development hot reload helpers through `tywrap/dev`
* add Node watch sessions that regenerate wrappers and swap the active bridge
* pass the resolved config into bridge recreation during reloads

### Bug Fixes

* add structured generation failures and CLI handling for fatal vs stale output states
* harden Node worker warmup, worker-pool publishing, and timeout recovery behavior
* reject legacy config-based reload fields and point users to the new dev helpers
* update docs to describe the real hot reload support matrix across Node, Pyodide, and HTTP

## [0.3.1](https://github.com/bbopen/tywrap/compare/v0.3.0...v0.3.1) (2026-04-11)


Expand Down
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ TypeScript wrapper for Python libraries with full type safety.

- **Full Type Safety** - TypeScript definitions generated from Python source
analysis
- **Development Hot Reload** - Real Node watch sessions regenerate wrappers and
swap the active bridge without re-importing generated modules
- **Generic-Aware Declarations** - Preserves simple `TypeVar` and callable
`ParamSpec` generics in generated `.ts` and `.d.ts` output
- **Multi-Runtime** - Node.js (subprocess) and browsers (Pyodide)
Expand Down Expand Up @@ -62,6 +64,10 @@ For CI (or to verify a dependency upgrade didn’t change the generated surface)
npx tywrap generate --check
```

For local Node development, `tywrap/dev` gives you real hot reload: watch local
Python package trees, regenerate wrappers, and swap the active bridge while
keeping the last known good state if regeneration fails.

```typescript
import { NodeBridge } from 'tywrap/node';
import { setRuntimeBridge } from 'tywrap/runtime';
Expand Down Expand Up @@ -105,6 +111,31 @@ need the full environment. Large JSONL responses are capped by `maxLineLength`
You can cap payload sizes with `TYWRAP_CODEC_MAX_BYTES` (responses) and
`TYWRAP_REQUEST_MAX_BYTES` (requests) to keep JSONL traffic bounded.

## Development Hot Reload

```typescript
import { startNodeWatchSession } from 'tywrap/dev';
import { NodeBridge } from 'tywrap/node';

const session = await startNodeWatchSession({
configFile: './tywrap.config.ts',
createBridge: async config =>
new NodeBridge({
pythonPath: config.runtime.node?.pythonPath ?? 'python3',
timeoutMs: config.runtime.node?.timeout ?? 30000,
}),
});
```

- **Node**: full watch + wrapper regeneration + bridge swap
- **Pyodide**: use `createBridgeReloader(...)` from `tywrap/dev` for manual
bridge replacement
- **HTTP**: restart or redeploy the remote server outside tywrap

The Node watch session manages local package trees, refreshes nested directory
watchers when package layouts change, and keeps the last known good generated
output and bridge active if regeneration returns structured failures.

### Browser (Pyodide)

```typescript
Expand Down
4 changes: 2 additions & 2 deletions docs/guide/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,9 @@ sparse matrix classes (csr/csc/coo) to structured sparse objects.
| `batching` | `boolean` | `false` | Batch multiple operations |
| `compression` | `'auto' \| 'gzip' \| 'brotli' \| 'none'` | `'none'` | Output compression |

## Development Reload Helpers
## Development Hot Reload Helpers

Development reload is no longer configured inside `tywrap.config.*`.
Development hot reload is no longer configured inside `tywrap.config.*`.

Use `tywrap/dev` instead:

Expand Down
10 changes: 6 additions & 4 deletions docs/guide/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,10 @@ async function demo() {
}
```

## Development Workflow
## Development Workflow (Hot Reload)

Use `tywrap/dev` for wrapper regeneration plus bridge replacement:
Use `tywrap/dev` for development hot reload: wrapper regeneration plus bridge
replacement.

```typescript
import { startNodeWatchSession } from 'tywrap/dev';
Expand All @@ -295,8 +296,9 @@ const session = await startNodeWatchSession({
});
```

For Pyodide, use `createBridgeReloader(...)` from `tywrap/dev` for manual bridge
replacement. For HTTP, restart or redeploy the remote server outside tywrap.
For Pyodide, use `createBridgeReloader(...)` from `tywrap/dev` for manual
bridge replacement. For HTTP, restart or redeploy the remote server outside
tywrap.

`startNodeWatchSession(...)` watches local package directories as directory
trees, refreshes those trees when nested directories change, and keeps the last
Expand Down
1 change: 1 addition & 0 deletions docs/guide/runtimes/comparison.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ tywrap supports five runtime configurations. Choose based on your environment.
| Apache Arrow transport | ✅ | ✅ | ✅ | ✅ | ✅ |
| Virtual environment support | ✅ | ✅ | ✅ | ❌ | Server-side |
| Process pooling (experimental) | ✅ | ✅ | ✅ | ❌ | ❌ |
| Development hot reload | ✅ | ❌ | ❌ | Manual bridge reload | External |
| Tested in CI | ✅ | ✅ | Mocked | ✅ | ✅ |

## Import Paths
Expand Down
6 changes: 3 additions & 3 deletions docs/guide/runtimes/node.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ The Node.js runtime:
Both bridges share the same JSONL core for protocol validation, timeouts, and
stderr buffering.

## Development Reload
## Development Hot Reload

Node wrapper regeneration plus bridge replacement lives in `tywrap/dev`, not in
`tywrap.config.*`.
Node hot reload in tywrap means wrapper regeneration plus bridge replacement.
It lives in `tywrap/dev`, not in `tywrap.config.*`.

```typescript
import { startNodeWatchSession } from 'tywrap/dev';
Expand Down
20 changes: 20 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,26 @@ setRuntimeBridge(new NodeBridge({ pythonPath: 'python3' }));
const result = await math.sqrt(16); // 4 — fully typed
```

## Development Hot Reload

```typescript
import { startNodeWatchSession } from 'tywrap/dev';
import { NodeBridge } from 'tywrap/node';

const session = await startNodeWatchSession({
configFile: './tywrap.config.ts',
createBridge: async config =>
new NodeBridge({
pythonPath: config.runtime.node?.pythonPath ?? 'python3',
timeoutMs: config.runtime.node?.timeout ?? 30000,
}),
});
```

Use `reloadNow()` for an explicit rebuild or `close()` to stop watching. Node
gets full hot reload, Pyodide gets manual bridge replacement through
`createBridgeReloader(...)`, and HTTP reload remains external to tywrap.

> ⚠️ **Experimental** — APIs may change before v1.0.0. See [Releases](https://github.com/bbopen/tywrap/releases) for breaking changes.

> If tywrap saves you time, a ⭐ on [GitHub](https://github.com/bbopen/tywrap) helps others find it.
45 changes: 34 additions & 11 deletions docs/public/llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,26 @@ setRuntimeBridge(new NodeBridge({ pythonPath: 'python3' }));
const result = await math.sqrt(16); // 4 — fully typed
```

## Development Hot Reload

```typescript
import { startNodeWatchSession } from 'tywrap/dev';
import { NodeBridge } from 'tywrap/node';

const session = await startNodeWatchSession({
configFile: './tywrap.config.ts',
createBridge: async config =>
new NodeBridge({
pythonPath: config.runtime.node?.pythonPath ?? 'python3',
timeoutMs: config.runtime.node?.timeout ?? 30000,
}),
});
```

Use `reloadNow()` for an explicit rebuild or `close()` to stop watching. Node
gets full hot reload, Pyodide gets manual bridge replacement through
`createBridgeReloader(...)`, and HTTP reload remains external to tywrap.

> ⚠️ **Experimental** — APIs may change before v1.0.0. See [Releases](https://github.com/bbopen/tywrap/releases) for breaking changes.

> If tywrap saves you time, a ⭐ on [GitHub](https://github.com/bbopen/tywrap) helps others find it.
Expand Down Expand Up @@ -316,9 +336,10 @@ async function demo() {
}
```

## Development Workflow
## Development Workflow (Hot Reload)

Use `tywrap/dev` for wrapper regeneration plus bridge replacement:
Use `tywrap/dev` for development hot reload: wrapper regeneration plus bridge
replacement.

```typescript
import { startNodeWatchSession } from 'tywrap/dev';
Expand All @@ -334,8 +355,9 @@ const session = await startNodeWatchSession({
});
```

For Pyodide, use `createBridgeReloader(...)` from `tywrap/dev` for manual bridge
replacement. For HTTP, restart or redeploy the remote server outside tywrap.
For Pyodide, use `createBridgeReloader(...)` from `tywrap/dev` for manual
bridge replacement. For HTTP, restart or redeploy the remote server outside
tywrap.

`startNodeWatchSession(...)` watches local package directories as directory
trees, refreshes those trees when nested directories change, and keeps the last
Expand Down Expand Up @@ -726,9 +748,9 @@ sparse matrix classes (csr/csc/coo) to structured sparse objects.
| `batching` | `boolean` | `false` | Batch multiple operations |
| `compression` | `'auto' \| 'gzip' \| 'brotli' \| 'none'` | `'none'` | Output compression |

## Development Reload Helpers
## Development Hot Reload Helpers

Development reload is no longer configured inside `tywrap.config.*`.
Development hot reload is no longer configured inside `tywrap.config.*`.

Use `tywrap/dev` instead:

Expand Down Expand Up @@ -1032,6 +1054,7 @@ tywrap supports five runtime configurations. Choose based on your environment.
| Apache Arrow transport | ✅ | ✅ | ✅ | ✅ | ✅ |
| Virtual environment support | ✅ | ✅ | ✅ | ❌ | Server-side |
| Process pooling (experimental) | ✅ | ✅ | ✅ | ❌ | ❌ |
| Development hot reload | ✅ | ❌ | ❌ | Manual bridge reload | External |
| Tested in CI | ✅ | ✅ | Mocked | ✅ | ✅ |

## Import Paths
Expand Down Expand Up @@ -1097,10 +1120,10 @@ The Node.js runtime:
Both bridges share the same JSONL core for protocol validation, timeouts, and
stderr buffering.

## Development Reload
## Development Hot Reload

Node wrapper regeneration plus bridge replacement lives in `tywrap/dev`, not in
`tywrap.config.*`.
Node hot reload in tywrap means wrapper regeneration plus bridge replacement.
It lives in `tywrap/dev`, not in `tywrap.config.*`.

```typescript
import { startNodeWatchSession } from 'tywrap/dev';
Expand Down Expand Up @@ -2778,8 +2801,8 @@ import {
import { createBridgeReloader, startNodeWatchSession } from 'tywrap/dev';
```

- `startNodeWatchSession(...)` is the Node-only helper for wrapper regeneration
plus bridge swap.
- `startNodeWatchSession(...)` is the Node-only development hot reload helper
for wrapper regeneration plus bridge swap.
- `startNodeWatchSession(...)` passes the resolved config for that reload cycle
into `createBridge(config)`.
- `startNodeWatchSession(...)` watches local package trees by attaching one
Expand Down
4 changes: 2 additions & 2 deletions docs/reference/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,8 @@ import {
import { createBridgeReloader, startNodeWatchSession } from 'tywrap/dev';
```

- `startNodeWatchSession(...)` is the Node-only helper for wrapper regeneration
plus bridge swap.
- `startNodeWatchSession(...)` is the Node-only development hot reload helper
for wrapper regeneration plus bridge swap.
- `startNodeWatchSession(...)` passes the resolved config for that reload cycle
into `createBridge(config)`.
- `startNodeWatchSession(...)` watches local package trees by attaching one
Expand Down
4 changes: 2 additions & 2 deletions docs/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
5. npm publishing uses npm trusted publishing from GitHub Actions. Keep the npm
package connected to this repository as a trusted publisher, and do not add a
publish token to the workflow for the normal release path. The release job
validates the tagged package on Node 22, then switches to Node 24 for the
final `npm publish --provenance` step so npm uses an OIDC-capable CLI.
validates the tagged package on Node 24 and publishes with
`npm publish --provenance` from that same runtime.

6. For the normal `release-please` path, keep the repository Actions setting
`Allow GitHub Actions to create and approve pull requests` enabled.
Expand Down
Loading
Loading