diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e76b069..6f8ca97 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,7 +3,7 @@ Thanks for your interest in contributing! - Development setup: - - Node 20+ and Python 3.12+ + - Node 20+ and Python 3.10+ (CI tests 3.10, 3.11, 3.12) - npm ci - npm run check:all - Common tasks: diff --git a/README.md b/README.md index f6abd24..7ed5486 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ You can cap payload sizes with `TYWRAP_CODEC_MAX_BYTES` (responses) and `TYWRAP_ ```typescript import { PyodideBridge } from 'tywrap/pyodide'; const bridge = new PyodideBridge({ - indexURL: 'https://cdn.jsdelivr.net/pyodide/v0.24.1/full/' + indexURL: 'https://cdn.jsdelivr.net/pyodide/v0.28.0/full/' }); await bridge.init(); ``` diff --git a/ROADMAP.md b/ROADMAP.md deleted file mode 100644 index 4194c6a..0000000 --- a/ROADMAP.md +++ /dev/null @@ -1,8 +0,0 @@ -# Roadmap - -## Runtime Bridges - -- [ ] Unify NodeBridge and OptimizedNodeBridge behind a shared JSONL transport core. -- [ ] Preserve NodeBridge semantics as the baseline (correctness-first, explicit errors). -- [ ] Make performance features opt-in (process pooling, caching) so defaults stay safe. -- [ ] When parity is achieved, keep NodeBridge as a thin alias over the unified core. diff --git a/docs/configuration.md b/docs/configuration.md index 2a90604..36b76ac 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -163,7 +163,7 @@ export default defineConfig({ { "runtime": { "pyodide": { - "indexURL": "https://cdn.jsdelivr.net/pyodide/", + "indexURL": "https://cdn.jsdelivr.net/pyodide/v0.28.0/full/", "packages": ["numpy", "scipy", "matplotlib"] } } @@ -266,8 +266,6 @@ Use presets to opt into richer mappings for common ecosystems. } ``` -### Development Options - | Option | Type | Default | Description | |--------|------|---------|-------------| | `hotReload` | `boolean` | `false` | Enable hot reloading | diff --git a/docs/getting-started.md b/docs/getting-started.md index ff3513c..a9c3785 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -221,7 +221,7 @@ async function demo() { }, "runtime": { "pyodide": { - "indexURL": "https://cdn.jsdelivr.net/pyodide/", + "indexURL": "https://cdn.jsdelivr.net/pyodide/v0.28.0/full/", "packages": ["numpy", "scipy"] } } diff --git a/docs/runtimes/browser.md b/docs/runtimes/browser.md index 284305b..20035c1 100644 --- a/docs/runtimes/browser.md +++ b/docs/runtimes/browser.md @@ -25,7 +25,7 @@ npm install tywrap pyodide }, "runtime": { "pyodide": { - "indexURL": "https://cdn.jsdelivr.net/pyodide/", + "indexURL": "https://cdn.jsdelivr.net/pyodide/v0.28.0/full/", "packages": ["numpy", "matplotlib", "scipy"] } } @@ -39,7 +39,7 @@ import { setRuntimeBridge } from 'tywrap/runtime'; import { array } from './generated/numpy.generated.js'; const bridge = new PyodideBridge({ - indexURL: 'https://cdn.jsdelivr.net/pyodide/', + indexURL: 'https://cdn.jsdelivr.net/pyodide/v0.28.0/full/', packages: ['numpy'] }); diff --git a/docs/type-mapping-matrix.md b/docs/type-mapping-matrix.md index 819d2c9..47e5a98 100644 --- a/docs/type-mapping-matrix.md +++ b/docs/type-mapping-matrix.md @@ -208,7 +208,33 @@ Enable presets via `types.presets` in your config to opt into additional mapping | Python Type | TypeScript Type | Notes | |-------------|----------------|-------| -| `torch.Tensor` | `{ data: unknown, shape: number[], dtype?: string, device?: string }` | Tensor metadata + decoded data | +| `torch.Tensor` | Nested ndarray envelope with tensor metadata | See structure below | + +Torch tensors are wrapped in a special envelope containing an ndarray: +```typescript +{ + __tywrap__: 'torch.tensor', + encoding: 'ndarray', + value: { + __tywrap__: 'ndarray', + encoding: 'json' | 'arrow', + value: number[] | Uint8Array, + shape: number[], + dtype: string + }, + device: string // e.g., 'cpu' +} +``` + +### pydantic preset + +| Python Type | TypeScript Type | Notes | +|-------------|----------------|-------| +| `pydantic.BaseModel` | Serialized dict | Via `model_dump(by_alias=True, mode='json')` | +| Fields with aliases | Uses alias name | `by_alias=True` default | + +Pydantic v2 models are serialized using `model_dump()` with `by_alias=True` and `mode='json'` +to ensure JSON-safe output. Nested models are recursively serialized. ### sklearn preset @@ -322,4 +348,4 @@ Planned improvements to the type mapping system: --- -*This document is automatically updated as the type mapping system evolves. Last updated: 2024.* +*Last updated: January 2026.* diff --git a/src/runtime/pyodide-io.ts b/src/runtime/pyodide-io.ts index 582a35a..291966a 100644 --- a/src/runtime/pyodide-io.ts +++ b/src/runtime/pyodide-io.ts @@ -55,7 +55,7 @@ interface PyodideInstance { // ============================================================================= /** Default Pyodide CDN URL */ -const DEFAULT_INDEX_URL = 'https://cdn.jsdelivr.net/pyodide/v0.24.1/full/'; +const DEFAULT_INDEX_URL = 'https://cdn.jsdelivr.net/pyodide/v0.28.0/full/'; /** * Bootstrap Python code that sets up the dispatch function. diff --git a/test/runtime_config.test.ts b/test/runtime_config.test.ts index ebc5ec6..3bbdd4e 100644 --- a/test/runtime_config.test.ts +++ b/test/runtime_config.test.ts @@ -137,7 +137,7 @@ describe('Runtime Configuration', () => { const indexURL = (transport as any).indexURL; const packages = (transport as any).packages; - expect(indexURL).toBe('https://cdn.jsdelivr.net/pyodide/v0.24.1/full/'); + expect(indexURL).toBe('https://cdn.jsdelivr.net/pyodide/v0.28.0/full/'); expect(packages).toEqual([]); }); @@ -184,7 +184,7 @@ describe('Runtime Configuration', () => { it('should validate CDN URLs', () => { const validURLs = [ 'https://cdn.jsdelivr.net/pyodide/', - 'https://unpkg.com/pyodide@0.24.1/', + 'https://unpkg.com/pyodide@0.28.0/', 'https://custom-cdn.example.com/pyodide/', 'http://localhost:8080/pyodide/', // For development ]; diff --git a/test/runtime_pyodide.test.ts b/test/runtime_pyodide.test.ts index 1b533ee..981e6e0 100644 --- a/test/runtime_pyodide.test.ts +++ b/test/runtime_pyodide.test.ts @@ -110,7 +110,7 @@ describe('Pyodide Runtime Bridge', () => { const result = await bridge.call('math', 'sqrt', [16]); expect(result).toBe(4); expect(mockLoadPyodide).toHaveBeenCalledWith({ - indexURL: 'https://cdn.jsdelivr.net/pyodide/v0.24.1/full/', + indexURL: 'https://cdn.jsdelivr.net/pyodide/v0.28.0/full/', }); }); @@ -133,7 +133,7 @@ describe('Pyodide Runtime Bridge', () => { it('should initialize with pre-loaded packages', async () => { const packages = ['numpy', 'pandas']; bridge = new PyodideBridge({ - indexURL: 'https://cdn.jsdelivr.net/pyodide/v0.24.1/full/', + indexURL: 'https://cdn.jsdelivr.net/pyodide/v0.28.0/full/', packages, }); @@ -675,8 +675,8 @@ describe('Pyodide Runtime Bridge', () => { describe('CDN and Loading Configurations', () => { it('should handle different CDN URLs', async () => { const customCDNs = [ - 'https://cdn.jsdelivr.net/pyodide/v0.24.1/', - 'https://unpkg.com/pyodide@0.24.1/', + 'https://cdn.jsdelivr.net/pyodide/v0.28.0/', + 'https://unpkg.com/pyodide@0.28.0/', 'https://custom-cdn.example.com/pyodide/', ];