diff --git a/.gitignore b/.gitignore index c4bf5729..6d6749c5 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,9 @@ yarn-debug.log* yarn-error.log* pnpm-debug.log* +# Plans (local only) +docs/plans/ + # Build outputs /dist/ /coverage/ diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 2afb6b5e..018f10a7 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -4,6 +4,7 @@ export default defineConfig({ title: 'tywrap', description: 'Generate type-safe TypeScript wrappers for any Python library — Node.js, Deno, Bun, and browsers via Pyodide.', base: '/tywrap/', + appearance: 'force-dark', cleanUrls: true, srcExclude: ['**/plans/**', 'release.md', 'codec-roadmap.md'], diff --git a/docs/.vitepress/theme/components/Hero3D.vue b/docs/.vitepress/theme/components/Hero3D.vue new file mode 100644 index 00000000..2ec79875 --- /dev/null +++ b/docs/.vitepress/theme/components/Hero3D.vue @@ -0,0 +1,315 @@ + + + + + + + + + + + + + Wrap Python in + TypeScript Safety + + + + Seamlessly integrate the unmatched computational power of Python's elite ecosystem—like PyTorch, SciPy, pandas, and NumPy—directly within your TypeScript applications, fully protected by a robust type system. + + + + + Get Started + + + View Documentation + + + + + + + diff --git a/docs/.vitepress/theme/composables/useThreeScene.ts b/docs/.vitepress/theme/composables/useThreeScene.ts new file mode 100644 index 00000000..5757b61f --- /dev/null +++ b/docs/.vitepress/theme/composables/useThreeScene.ts @@ -0,0 +1,405 @@ +/** + * useThreeScene — Vue composable that builds and animates the tywrap hero 3D scene. + * + * Reimagined with a high-end, cinematic procedural particle network + * inspired by the "Hermes 4" glowing graph aesthetic representing + * Python-to-TypeScript data flow. + */ + +import * as THREE from 'three' +import { + EffectComposer, + EffectPass, + RenderPass, + BloomEffect, + VignetteEffect, +} from 'postprocessing' + +export interface ThreeSceneOptions { + canvas: HTMLCanvasElement + width: number + height: number +} + +export interface ThreeSceneReturn { + scene: THREE.Scene + camera: THREE.PerspectiveCamera + renderer: THREE.WebGLRenderer + start: () => void + dispose: () => void + resize: (w: number, h: number) => void + onScroll: (scrollY: number) => void +} + +// --------------------------------------------------------------------------- +// Simplex Noise 3D GLSL +// --------------------------------------------------------------------------- +const snoise3D = ` +// GLSL textureless classic 3D noise "cnoise", +// with an RSL-style periodic variant "pnoise". +// Author: Stefan Gustavson (stefan.gustavson@liu.se) +// Version: 2011-08-22 +vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);} +vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;} +float snoise(vec3 v){ + const vec2 C = vec2(1.0/6.0, 1.0/3.0) ; + const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); + vec3 i = floor(v + dot(v, C.yyy) ); + vec3 x0 = v - i + dot(i, C.xxx) ; + vec3 g = step(x0.yzx, x0.xyz); + vec3 l = 1.0 - g; + vec3 i1 = min( g.xyz, l.zxy ); + vec3 i2 = max( g.xyz, l.zxy ); + vec3 x1 = x0 - i1 + 1.0 * C.xxx; + vec3 x2 = x0 - i2 + 2.0 * C.xxx; + vec3 x3 = x0 - 1.0 + 3.0 * C.xxx; + i = mod(i, 289.0 ); + vec4 p = permute( permute( permute( + i.z + vec4(0.0, i1.z, i2.z, 1.0 )) + + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) + + i.x + vec4(0.0, i1.x, i2.x, 1.0 )); + float n_ = 1.0/7.0; + vec3 ns = n_ * D.wyz - D.xzx; + vec4 j = p - 49.0 * floor(p * ns.z *ns.z); + vec4 x_ = floor(j * ns.z); + vec4 y_ = floor(j - 7.0 * x_ ); + vec4 x = x_ *ns.x + ns.yyyy; + vec4 y = y_ *ns.x + ns.yyyy; + vec4 h = 1.0 - abs(x) - abs(y); + vec4 b0 = vec4( x.xy, y.xy ); + vec4 b1 = vec4( x.zw, y.zw ); + vec4 s0 = floor(b0)*2.0 + 1.0; + vec4 s1 = floor(b1)*2.0 + 1.0; + vec4 sh = -step(h, vec4(0.0)); + vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ; + vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ; + vec3 p0 = vec3(a0.xy,h.x); + vec3 p1 = vec3(a0.zw,h.y); + vec3 p2 = vec3(a1.xy,h.z); + vec3 p3 = vec3(a1.zw,h.w); + vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; + vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); + m = m * m; + return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), + dot(p2,x2), dot(p3,x3) ) ); +} +` + +const particlesVertexShader = ` +uniform float uTime; +uniform float uScrollCurrent; + +attribute float aSize; +attribute vec3 aColor; + +varying vec3 vColor; +varying float vAlpha; + +${snoise3D} + +void main() { + vColor = aColor; + vec3 pos = position; + + // Fluid curling motion using fbm + float noise1 = snoise(vec3(pos.x * 0.2, pos.y * 0.2, uTime * 0.2)); + float noise2 = snoise(vec3(pos.y * 0.2, pos.z * 0.2, uTime * 0.25)); + float noise3 = snoise(vec3(pos.z * 0.2, pos.x * 0.2, uTime * 0.3)); + + // The core pulses and shifts organically + pos.x += noise1 * 2.0; + pos.y += noise2 * 2.0; + pos.z += noise3 * 2.0; + + // Parallax / Scroll effect: pull the cloud up as we scroll down + pos.y += uScrollCurrent * 0.005; + + vec4 viewPosition = viewMatrix * modelMatrix * vec4(pos, 1.0); + gl_Position = projectionMatrix * viewPosition; + + // Massively increase point size for visibility and glow + gl_PointSize = aSize * (800.0 / -viewPosition.z); + + // Twinkling organic pulse + vAlpha = 0.5 + 0.5 * snoise(vec3(pos.x * 0.5, pos.y * 0.5, uTime * 1.5)); +} +` + +const particlesFragmentShader = ` +varying vec3 vColor; +varying float vAlpha; + +void main() { + float dist = length(gl_PointCoord - vec2(0.5)); + if(dist > 0.5) discard; + + // Soft, additive smoke/plasma blur + float strength = pow(1.0 - (dist * 2.0), 1.5); + gl_FragColor = vec4(vColor, strength * vAlpha * 0.6); +} +` + +export function useThreeScene(options: ThreeSceneOptions): ThreeSceneReturn { + const { canvas, width, height } = options + + const scene = new THREE.Scene() + const clock = new THREE.Clock() + + const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000) + // Shift right so the mass is on the right of the screen + camera.position.set(0, 0, 25) + + const mouse = new THREE.Vector2() + const targetMouse = new THREE.Vector2() + const scrollState = { target: 0, current: 0 } + + function onMouseMove(event: MouseEvent) { + targetMouse.x = (event.clientX / window.innerWidth) * 2 - 1 + targetMouse.y = -(event.clientY / window.innerHeight) * 2 + 1 + } + function onScroll(scrollY: number) { scrollState.target = scrollY } + + const dpr = Math.min(Math.max(1, window.devicePixelRatio), 2) + const renderer = new THREE.WebGLRenderer({ + canvas, + antialias: false, + alpha: false, + powerPreference: 'high-performance', + }) + // Pitch black for high contrast Hermes 4 look + renderer.setClearColor(new THREE.Color('#020205'), 1) + renderer.setPixelRatio(dpr) + renderer.setSize(width, height) + renderer.toneMapping = THREE.ACESFilmicToneMapping + renderer.toneMappingExposure = 1.5 + + const composer = new EffectComposer(renderer, { + frameBufferType: THREE.HalfFloatType, + }) + composer.addPass(new RenderPass(scene, camera)) + + const bloomEffect = new BloomEffect({ + luminanceThreshold: 0.1, + mipmapBlur: true, + intensity: 4.0, // HUGE bloom for plasma look + }) + const vignetteEffect = new VignetteEffect({ + eskil: false, + offset: 0.3, + darkness: 0.9, + }) + composer.addPass(new EffectPass(camera, bloomEffect, vignetteEffect)) + + const coreGroup = new THREE.Group() + // Move the core to the right half of the screen + coreGroup.position.x = 8 + scene.add(coreGroup) + + // --- Massive Plasma Core Particles --- + // Reduce particle count on mobile / low-end devices to avoid frame drops + const isMobile = width < 768 + const PARTICLE_COUNT = isMobile ? 5000 : 15000 + const positions = new Float32Array(PARTICLE_COUNT * 3) + const colors = new Float32Array(PARTICLE_COUNT * 3) + const sizes = new Float32Array(PARTICLE_COUNT) + + // Tywrap branding: Amber to Blue, with green accents + const colorAmber = new THREE.Color(0xf59e0b) + const colorSapphire = new THREE.Color(0x3b82f6) + const colorNeonGreen = new THREE.Color(0x10b981) + + for (let i = 0; i < PARTICLE_COUNT; i++) { + // Generate particles in a dense sphere / elliptical core + const radius = Math.pow(Math.random(), 2.0) * 12 // Denser at center + const theta = Math.random() * Math.PI * 2 + const phi = Math.acos((Math.random() * 2) - 1) + + const x = radius * Math.sin(phi) * Math.cos(theta) * 1.5 // stretch X + const y = radius * Math.sin(phi) * Math.sin(theta) + const z = radius * Math.cos(phi) * 0.5 // compress Z + + positions[i * 3 + 0] = x + positions[i * 3 + 1] = y + positions[i * 3 + 2] = z + + let c: THREE.Color + const rand = Math.random() + if (rand < 0.4) c = colorSapphire.clone() + else if (rand < 0.8) c = colorAmber.clone() + else c = colorNeonGreen.clone() + + // Blend them together based on position + if (x > 0) c.lerp(colorSapphire, 0.5) + else c.lerp(colorAmber, 0.5) + + colors[i * 3 + 0] = c.r + colors[i * 3 + 1] = c.g + colors[i * 3 + 2] = c.b + + // Dynamic sizes: core is dense with small particles, outer is large wisps + sizes[i] = Math.random() * 4.0 + 1.0 + } + + const geometry = new THREE.BufferGeometry() + geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)) + geometry.setAttribute('aColor', new THREE.BufferAttribute(colors, 3)) + geometry.setAttribute('aSize', new THREE.BufferAttribute(sizes, 1)) + + const material = new THREE.ShaderMaterial({ + vertexShader: particlesVertexShader, + fragmentShader: particlesFragmentShader, + transparent: true, + depthWrite: false, + blending: THREE.NormalBlending, + uniforms: { + uTime: { value: 0 }, + uScrollCurrent: { value: 0 } + } + }) + + const particlesMesh = new THREE.Points(geometry, material) + coreGroup.add(particlesMesh) + + // --- Core Glowing Nodes (Larger accent points) --- + const nodeGeometry = new THREE.SphereGeometry(0.5, 32, 32) // significantly smaller + const nodeMaterial = new THREE.MeshBasicMaterial({ + color: 0xffffff, + transparent: true, + opacity: 0.5, // decreased opacity + depthWrite: false, + blending: THREE.AdditiveBlending, + }) + + const nodes = new THREE.InstancedMesh(nodeGeometry, nodeMaterial, 7) + const dummy = new THREE.Object3D() + for(let i=0; i<7; i++) { + dummy.position.set( + (Math.random() - 0.5) * 15, + (Math.random() - 0.5) * 10, + (Math.random() - 0.5) * 10 + ); + const s = Math.random() * 1.5 + 0.5; + dummy.scale.set(s,s,s) + dummy.updateMatrix() + nodes.setMatrixAt(i, dummy.matrix) + + // Extinguish base colors with multiplier + let nodeCol = i % 2 === 0 ? colorSapphire.clone() : colorAmber.clone(); + nodes.setColorAt(i, nodeCol) // Removed harsh multiplier + } + nodes.instanceMatrix.needsUpdate = true + if (nodes.instanceColor) nodes.instanceColor.needsUpdate = true + coreGroup.add(nodes) + + // --- Neural Edges (Axons / Light Streams) --- + const lineCount = 40 + const linesGroup = new THREE.Group() + coreGroup.add(linesGroup) + + const lineMaterials: THREE.MeshBasicMaterial[] = [] + + for(let i=0; i { + mat.opacity = 0.3 + 0.3 * Math.sin(elapsed * 2.0 + i) + }) + + composer.render() + } + + function start() { + if (started) return + started = true + window.addEventListener('mousemove', onMouseMove) + clock.start() + animate() + } + + function dispose() { + if (rafId !== null) cancelAnimationFrame(rafId) + window.removeEventListener('mousemove', onMouseMove) + + // Dispose all scene resources + geometry.dispose() + material.dispose() + nodeGeometry.dispose() + nodeMaterial.dispose() + + // Dispose tube geometries and materials + linesGroup.traverse((obj) => { + if (obj instanceof THREE.Mesh) { + obj.geometry.dispose() + ;(obj.material as THREE.Material).dispose() + } + }) + + composer.dispose() + renderer.dispose() + } + + function resize(w: number, h: number) { + camera.aspect = w / h + camera.updateProjectionMatrix() + renderer.setSize(w, h) + composer.setSize(w, h) + } + + return { scene, camera, renderer, start, dispose, resize, onScroll } +} + diff --git a/docs/.vitepress/theme/custom.css b/docs/.vitepress/theme/custom.css new file mode 100644 index 00000000..f441f866 --- /dev/null +++ b/docs/.vitepress/theme/custom.css @@ -0,0 +1,33 @@ +/* Custom theme overrides for tywrap docs */ + +/* Hide VitePress default hero — replaced by Hero3D component */ +.VPHome > .VPHero { + display: none; +} + +/* Ensure home page has transparent background for the 3D canvas */ +.VPHome { + background-color: transparent !important; +} + +/* Features section inherits transparent theme on home */ +.VPHome .VPFeatures { + background-color: transparent !important; +} + +/* Ensure the main markdown content container is transparent */ +.VPHome .VPHomeContent { + background-color: transparent !important; +} + +/* Restrict features/content to the left to match the hero text layout. + Targets internal VitePress classes (.VPFeatures .container, .VPHomeContent .container) + verified against VitePress 1.x — may need updating if VitePress changes its markup. */ +@media (min-width: 960px) { + .VPHome .VPFeatures .container, + .VPHome .VPHomeContent .container { + max-width: 60%; + margin-left: 0; + padding-left: max(24px, calc((100vw - var(--vp-layout-max-width, 1152px)) / 2)); + } +} diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts new file mode 100644 index 00000000..04cdf1cf --- /dev/null +++ b/docs/.vitepress/theme/index.ts @@ -0,0 +1,17 @@ +import DefaultTheme from 'vitepress/theme' +import type { Theme } from 'vitepress' +import { h } from 'vue' +import { useRoute } from 'vitepress' +import Hero3D from './components/Hero3D.vue' +import './custom.css' + +export default { + extends: DefaultTheme, + Layout() { + const route = useRoute() + const isHome = route.path === '/' || route.path.replace(/\/$/, '') === '/tywrap' + return h(DefaultTheme.Layout, null, { + ...(isHome ? { 'layout-top': () => h(Hero3D) } : {}), + }) + }, +} satisfies Theme diff --git a/docs/index.md b/docs/index.md index 4197b601..73790a76 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,18 +1,6 @@ --- layout: home -hero: - name: tywrap - text: TypeScript wrappers for Python libraries - tagline: Auto-generate type-safe TypeScript bindings for any Python library — works in Node.js, Bun, Deno, and browsers via Pyodide. - actions: - - theme: brand - text: Get Started - link: /guide/getting-started - - theme: alt - text: View on GitHub - link: https://github.com/bbopen/tywrap - features: - icon: 🔒 title: Full Type Safety diff --git a/docs/plans/2026-01-22-tywrapped-ecosystem-vision.md b/docs/plans/2026-01-22-tywrapped-ecosystem-vision.md deleted file mode 100644 index ced24aed..00000000 --- a/docs/plans/2026-01-22-tywrapped-ecosystem-vision.md +++ /dev/null @@ -1,482 +0,0 @@ -# RFC: @tywrapped Ecosystem - -| | | -|---|---| -| **Status** | Draft | -| **Created** | 2026-01-22 | -| **Author** | @bbopen | - -## Summary - -Create a **curated ecosystem of pre-wrapped Python libraries** published to npm under the `@tywrapped` organization, making world-class Python libraries accessible to JavaScript/TypeScript developers who don't know Python. - -## Motivation - -JavaScript/TypeScript developers often need access to Python libraries that are best-in-class for their domain (PyTorch, NumPy, SciPy, scikit-learn). Currently, they must either: -1. Learn Python and manage a Python environment -2. Use inferior JS alternatives -3. Set up tywrap themselves - -This RFC proposes a community-governed ecosystem of pre-built, tested, and versioned wrappers that "just work" via `npm install`. - -## Goals - -1. **Adoption friction** - Lower the barrier by providing ready-to-use npm packages instead of requiring users to run tywrap themselves -2. **Quality assurance** - Ensure high-quality, tested wrappers through governance and CI gates -3. **Discovery/Marketing** - Make tywrap visible in the npm ecosystem through packages like `@tywrapped/pytorch` -4. **Ecosystem sustainability** - Create a network effect where these become the "blessed" way to use Python libs from JS - -## Target Audience - -**JS/TS developers who don't know Python** and need access to libraries that are: -- Best-in-class for their domain (not just "best in Python") -- Have no well-maintained JS equivalent -- The primary project doesn't provide a JS SDK - -## Unique Value Proposition - -Tywrap is unique in the ecosystem: - -| Project | Approach | Types | Existing Libraries | -|---------|----------|-------|-------------------| -| **tywrap** | Analyzes Python source → generates TS wrappers | Auto-generated | Yes, directly wraps numpy/pandas/etc. | -| **PyBridge** | Manual bridge | Manual TS interfaces | Requires wrapper functions | -| **pymport** | Embeds Python in-process | Minimal | Weak typing | -| **node-calls-python** | In-process execution | Basic conversions | No real TS types | - -**Tywrap's unique combination:** -1. Auto-generates TypeScript types from Python source analysis -2. Wraps existing libraries directly (no wrapper functions needed) -3. Multi-runtime (Node.js subprocess AND browser via Pyodide) -4. Rich data science type mappings (numpy, pandas, torch, scipy, sklearn) - ---- - -## Governance Model - -### Organization Structure - -- **Central branded org**: `@tywrapped` on npm, `tywrapped` on GitHub -- **Nonprofit/community-driven** structure that can receive sponsorship -- **Technical committee** provides governance and quality oversight -- Trust established via the central org, not individual maintainers - -### Maintainer Model - -- Trusted maintainers assigned to specific libraries -- Maintainers approve releases for their libraries -- Outreach to upstream Python projects welcomed but not required - -### Upstream Relationship - -| Level | Description | -|-------|-------------| -| **Awareness** | Default - notify upstream, they know we exist | -| **Collaboration** | Optional - they contribute to wrapper design | -| **Ownership** | Optional - upstream joins tywrap community as maintainer | - -Upstream can "come to us" rather than requiring us to convince them. - ---- - -## Package Naming - -**Pattern**: `@tywrapped/` - -Examples: -- `@tywrapped/numpy` -- `@tywrapped/pytorch` -- `@tywrapped/scipy` -- `@tywrapped/beautifulsoup4` - -**Rationale**: -- Scoped under org we control - prevents squatting -- Follows `@types/*` precedent (DefinitelyTyped) -- Clear this is community wrapper, not official -- Package naming, trademark strategy, and disclaimers must be reviewed by legal counsel before any public launch - -**Required disclaimer in each package (draft, subject to legal review)**: -> This is a community-maintained wrapper and is not affiliated with or endorsed by [upstream project]. - ---- - -## Versioning Strategy - -**Pattern**: Mirror upstream by default, compound version for tywrap patches. - -| Scenario | Version | -|----------|---------| -| Wraps numpy 1.26.0 | `@tywrapped/numpy@1.26.0` | -| Tywrap fix for same numpy | `@tywrapped/numpy@1.26.0-tywrap.1` | -| New numpy release | `@tywrapped/numpy@1.26.1` (resets suffix) | - -**Republish policy**: Every tywrap core release can trigger regeneration and republish of all active packages with a compound version so users on supported upstream versions benefit from improvements. - -Operational scalability (suggested defaults): -- Canary wave: 5-10% of packages first; require green CI and no runtime regressions before expanding. -- Publish gating: require unit + integration smoke tests; fail-closed on flaky/timeout tests. -- Batching: cap concurrent publishes (e.g., 3-5) to avoid CI bottlenecks and npm rate limits. -- Rate limits: retry with exponential backoff (e.g., 5 retries, 30s → 5m); pause the queue on repeated 429s. -- Rollback: move dist-tags back to the last known-good build; open an incident issue and stop the wave. - -**Version support breadth**: Separate indicator from quality tiers. Packages aim to match upstream's official support matrix (e.g., if numpy supports 1.24+, so does `@tywrapped/numpy`). - -**Breaking changes**: Mirror upstream directly. No buffering, no parallel support periods. The wrapper is a transparent pass-through - breaking changes in numpy mean breaking changes in `@tywrapped/numpy`. Document this clearly so users understand they're accepting upstream's upgrade path. - -### Enterprise stability options - -- Optional channels: `stable`/`lts` dist-tags for org-blessed "slow lane" packages when demand justifies it; explicitly time-box support windows. -- Migration guides: require a short migration note for any breaking change (template + checklist) before publish. -- Pinning guidance (enterprise): prefer exact versions + lockfiles for production; treat `^` upgrades as opt-in. -- Trade-off: strict mirroring maximizes transparency and reduces maintainer load; stability channels increase operational overhead but improve adoption. - ---- - -## Template Repository Structure - -### Standardized Core (Fixed) - -- GitHub Actions workflows (CI/CD) -- npm publish pipeline -- Health badge generation -- Dependabot configuration -- `tywrap.config.ts` structure -- CONTRIBUTING guidelines -- License (MIT) -- Disclaimer template - -### Flexible Edges (Customizable) - -- Library-specific integration tests -- Usage examples -- Extended documentation -- Custom TypeScript utilities on top of generated code - ---- - -## CI/CD Pipeline - -### Triggers - -Releases triggered by **either**: -1. **Upstream Python release** - Dependabot detects new version -2. **Tywrap release** - New tywrap improves codegen - -### Pipeline Flow - -```text -1. Trigger (Dependabot or tywrap release) - ↓ -2. CI regenerates wrappers - ↓ -3. Run test suite (based on health tier) - ↓ -4. If tests pass → Create PR / request approval - ↓ -5. Library maintainer reviews/approves - ↓ -6. Publish to npm -``` - -### Human Gate - -Library maintainer must approve before publish - automation does the heavy lifting, humans ensure quality. - -Fallbacks when maintainers are unavailable (suggested defaults): -- Minimum 2 maintainers per package (primary + backup). -- Patch releases: auto-approve after 72 hours with no response (logged), unless a maintainer vetoes. -- Security fixes: emergency override by technical committee (2-of-3) with mandatory postmortem. -- All overrides require a public audit trail (issue + PR link + rationale). - ---- - -## Package Health Indicators - -Two separate badge systems for orthogonal concerns: - -### Quality Tier (testing depth) - -| Tier | Badge | Requirement | -|------|-------|-------------| -| 🥉 Bronze | Generation + types | Wrappers generate, TypeScript compiles | -| 🥈 Silver | Smoke tests | Basic "does it call Python and return" tests | -| 🥇 Gold | Integration tests | Extensive API surface coverage | -| 💎 Platinum | Upstream parity | Ported subset of Python test suite | - -### Support Breadth (version coverage) - -| Level | Indicator | Meaning | -|-------|-----------|---------| -| Basic | `latest` | Only current upstream version | -| Standard | `latest-1` | Latest + previous major | -| Full | `upstream` | Matches upstream's support matrix | -| Extended | `upstream-lts` | Upstream support + extended LTS | - -#### Implementation notes - -Recommended approach: single package name + npm dist-tags. - -- One package per library (e.g., `@tywrapped/numpy`) publishes multiple tested wrapper versions (mirroring upstream + `-tywrap.N` patches). -- Use dist-tags to make supported tracks easy to install: - - `latest` → newest supported upstream release line - - `latest-1` → previous major (or previous supported line) - - `upstream` → "full matrix" track (only if you actually publish/test it) - - `upstream-lts` → extended support line (explicitly time-boxed) -- Wrapper repo `package.json` stays straightforward: name is stable, version mirrors upstream with optional `-tywrap.N`, and `publishConfig.access` is public. - -Publish/tag flow (sketch): - -```bash -npm publish --tag latest -npm dist-tag add @tywrapped/numpy@1.26.0-tywrap.3 latest -npm dist-tag add @tywrapped/numpy@1.25.4-tywrap.2 latest-1 -``` - -CI matrix outline (sketch): - -```yaml -strategy: - matrix: - python: ["3.10", "3.11", "3.12"] - upstream: ["numpy==1.24.*", "numpy==1.25.*", "numpy==1.26.*"] -``` - -Publish script logic (sketch): -1. Install target upstream version(s) into an isolated env (per matrix entry). -2. Generate wrappers (tywrap) and run the test tier for that package. -3. Compute package version (`-tywrap.N`) and publish. -4. Apply/update dist-tags for the supported tracks. - -User-facing installs: -- Default: `npm i @tywrapped/numpy` (uses `latest`) -- Specific track: `npm i @tywrapped/numpy@latest-1` -- Exact pin: `npm i @tywrapped/numpy@1.26.0-tywrap.3` - -Alternative (if tags become unwieldy): separate packages per track (e.g., `@tywrapped/numpy-1.24`), at the cost of more maintenance and user confusion. - -### Display Format - -README example: - -```markdown -@tywrapped/numpy -Quality: 🥇 Gold | Support: upstream (1.24+) | Runtimes: node, browser -``` - -### Progression Path - -- All packages start at Bronze quality, Basic support -- Maintainers level up through added tests and version support -- LLM-assisted tooling can help port Python tests to TypeScript -- Health indicators create clear upgrade path and contribution opportunities - -**Visibility**: Prominent in README, npm description, and searchable in package registry. - ---- - -## Runtime Selection & Browser Support - -### Auto-detection with Explicit Override - -**Default behavior (zero config):** -- Browser environment → Pyodide runtime -- Node.js environment → Subprocess runtime (safest, no native deps) - -**Override for power users (current tywrap API):** - -```typescript -import { setRuntimeBridge, NodeBridge } from 'tywrap'; - -setRuntimeBridge(new NodeBridge()); // Explicit Node.js subprocess runtime -``` - -### Runtime Packages - -Scope policy: -- `@tywrapped/*` are public library wrappers (e.g., `@tywrapped/numpy`, exports `setRuntime` and wrapper APIs) -- `@tywrap/*` are runtime and infrastructure packages (e.g., `@tywrap/runtime-subprocess`, `@tywrap/runtime-inprocess`, `@tywrap/runtime-pyodide`) and export runtime implementations like `InProcessBridge` - -- `@tywrap/runtime-subprocess` - Bundled by default, no native dependencies -- `@tywrap/runtime-inprocess` - Optional, requires node-calls-python native addon -- `@tywrap/runtime-pyodide` - Auto-loaded in browser environments - -### Browser Support - -Opt-in per package, clearly marked. - -Packages declare supported runtimes in metadata: -- `runtimes: ["node"]` - Node.js only (heavy libraries like PyTorch) -- `runtimes: ["node", "browser"]` - Both supported - -Visible in README badge, npm package metadata, and package documentation. - -#### Validation & enforcement (template repo) - -- Automated validation: add a `browser-compat` CI job that runs bundler builds (esbuild/rollup/webpack) and Playwright smoke tests for packages that declare `runtimes: ["browser"]` (or `["node", "browser"]`). -- Enforcement at package boundary: - - Build-time: fail CI if declared runtimes don't have matching artifacts/entry points. - - Runtime: add a guard in Node-only entry points that throws a clear error when imported in the browser. -- Publish-time checks: lint `package.json` `runtimes`, generate README badges, and fail publish if declared runtime artifacts are missing. -- Pyodide handling: for browser-enabled packages, lazy-load Pyodide/WASM and keep heavy Python deps optional; include a headless-browser smoke test that imports the package and initializes the runtime. - ---- - -## Initial Launch Libraries - -Criteria for selection: -- High demand from JS developers -- No good existing JS alternative -- Reasonable API surface -- Good upstream test coverage - -**Candidates**: - -| Library | Domain | JS Alternative? | Priority | -|---------|--------|-----------------|----------| -| NumPy | Numerical computing | Limited (ndarray-js) | High | -| SciPy | Scientific computing | None comprehensive | High | -| PyTorch | ML/Deep learning | TensorFlow.js (different) | High | -| scikit-learn | Classical ML | None good | Medium | -| BeautifulSoup4 | HTML parsing | Cheerio (different API) | Medium | -| Pandas | Data manipulation | Danfo.js (less mature) | Medium | - ---- - -## Funding & Brand Protection - -### Funding Model: Open Collective - -- Transparent finances - all income and expenses visible to community -- Fiscal host handles legal and tax compliance -- Proven model used by webpack, Babel, Vue, and similar projects -- Funds distributed to maintainers based on contribution -- Sponsors get visibility and community goodwill - -**Fund allocation** (suggested starting point): -- 60% - Maintainer stipends (proportional to package maintenance) -- 20% - Infrastructure costs (CI, hosting, tooling) -- 15% - Bounties for new packages, test improvements, documentation -- 5% - Reserve for legal/unexpected expenses - -#### Governance and Fund Allocation - -- Decision body: technical committee + maintainers (define this in a governance charter). -- Maintainer stipends (60%): split using a simple, auditable formula (e.g., base stipend per maintained package + a variable component tied to monthly activity); publish the formula. -- Bounties (15%): bounties are proposed via issues, approved by the committee within a fixed SLA (e.g., 7 days), and paid on merge + release of the deliverable. -- Reserve (5%): only tapped for legal/incidents; require explicit approval (e.g., 2-of-3 committee vote) and a public rationale. -- Transparency: publish quarterly reports + a public ledger of disbursements; define a dispute-resolution path (mediation → committee decision → community escalation). - -### Trademark: Register Early - -- File trademark application before public launch -- Estimated cost (fees-only): ~$250-400 per class (USPTO filing fees); budget ~$1,200-3,000+ per class including search + attorney filing/prosecution and possible office actions -- Protects brand from squatting -- Establishes legitimacy for enterprise adoption -- Required classes: software development tools, software distribution - -**Brand assets to protect**: -- "tywrapped" name -- `@tywrapped` npm scope (already protected by npm org ownership) -- Logo (when created) -- Domain name (tywrapped.org or similar) - ---- - -## Runtime Abstraction Feasibility - -### Current Architecture (Already Pluggable!) - -The tywrap codebase already has a clean transport abstraction: - -```text -┌─────────────────────────────────────────┐ -│ Transport Interface │ -│ init() | send() | dispose() | isReady │ -└─────────────────┬───────────────────────┘ - │ implements - ┌───────────┼───────────┐ - ▼ ▼ ▼ - ProcessIO HttpIO PyodideIO - (subprocess) (HTTP) (WASM) -``` - -**Key interface** (`src/runtime/transport.ts`): - -```typescript -interface Transport extends Disposable { - init(): Promise; - send(message: string, timeoutMs: number, signal?: AbortSignal): Promise; - readonly isReady: boolean; -} -``` - -`BridgeProtocol` is already transport-agnostic - it accepts any `Transport` implementation. - -#### Large-data handling notes - -The current `Transport` interface is string-based, which is great for simplicity but has practical limits for multi-GB tensors/DataFrames. - -High-level strategies (future work): -- Binary channel: add optional `sendBinary(...)` support (or extend `send(...)` to accept `ArrayBuffer`) and use `Transferable` objects where available. -- Out-of-band transfer: for large payloads, send references (file paths, HTTP URLs, object-store URIs) instead of inlining data. -- Streaming/chunking: add `streamStart`/`streamChunk`/`streamEnd` semantics with backpressure and `AbortSignal`. -- Capability negotiation: have transports advertise features (binary/streaming) via a meta call (e.g., `getBridgeInfo()` capabilities). - -### Adding node-calls-python Support - -**Recommended approach: JSON passthrough** - -1. Create `NodeCallsPythonIO implements Transport` -2. Keep the JSON protocol (parse incoming JSON, call Python via node-calls-python, serialize result) -3. Estimated ~200-300 lines of code -4. Small JSON overhead, but simpler integration and maintains consistency - -### Implementation Estimate - -| Task | Effort | -|------|--------| -| `NodeCallsPythonIO` transport class | 2-3 days | -| Bridge adapter for node-calls-python | 1 day | -| Integration tests | 1-2 days | -| Documentation | 0.5 day | -| **Total** | **~1-2 weeks (best-case)** | - -Best-case assumes a stable `Transport` interface and no first-time runtime-integration surprises; budget extra buffer for native addon quirks, OS differences, and CI iteration. - -### Conclusion - -**No architectural changes needed.** The runtime abstraction already exists. Adding new runtimes is a matter of implementing the `Transport` interface. - ---- - -## Next Steps - -### Phase 1: Foundation (before launch) - -1. Register "tywrapped" trademark -2. Set up GitHub org (`tywrapped`) and npm org (`@tywrapped`) -3. Create Open Collective account -4. Secure domain (tywrapped.org or similar) - -### Phase 2: Template & Tooling - -1. Create template repository with standardized CI/CD -2. Build badge generation tooling for health indicators -3. Document governance charter and contribution guidelines - -### Phase 3: Proof of Concept - -1. Build `@tywrapped/numpy` as first package -2. Validate CI/CD pipeline with real upstream updates -3. Gather feedback, iterate on template - -### Phase 4: Expansion - -1. Add 2-3 more high-priority packages (scipy, scikit-learn) -2. Recruit initial maintainers -3. Announce publicly, begin community building - -### Optional (parallel track) - -- Prototype `NodeCallsPythonIO` transport for performance optimization -- Explore LLM-assisted test porting from Python to TypeScript diff --git a/docs/plans/2026-03-10-tywrap-promotion.md b/docs/plans/2026-03-10-tywrap-promotion.md deleted file mode 100644 index 0efe3d95..00000000 --- a/docs/plans/2026-03-10-tywrap-promotion.md +++ /dev/null @@ -1,1595 +0,0 @@ -# tywrap Promotion & Professionalization Implementation Plan - -> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. - -**Goal:** Make tywrap discoverable, professional, and easy to maintain — through repository metadata fixes, a VitePress documentation site with full runtime guides (Node, Bun, Deno, Browser, HTTP), AI agent discoverability files, and GitHub automation. - -**Architecture:** Phase 1 (metadata + README) is pure file edits with no runtime impact. Phase 2 (VitePress) builds a separate static site in `docs/.vitepress/` deployed via GitHub Actions to GitHub Pages. Phase 3 (automation) adds GitHub Actions workflows and a config file — zero changes to library code. - -**Tech Stack:** VitePress (docs), GitHub Actions (CI/Pages/automation), actions/labeler@v6, actions/stale@v10, release-please, Renovate, Codecov - ---- - -## Manual Pre-Steps (GitHub UI — do these first, no code required) - -Before coding, do these in the GitHub repo UI at `github.com/bbopen/tywrap`: - -1. **Settings > About** (gear icon on repo page): - - Description: `Generate type-safe TypeScript wrappers for any Python library — Node.js, Deno, Bun, and browsers via Pyodide.` - - Topics: `typescript`, `python`, `bridge`, `interop`, `type-safety`, `codegen`, `code-generation`, `ast`, `pyodide`, `node`, `bun`, `deno`, `ffi`, `bindings`, `numpy`, `pandas`, `developer-tools`, `python-interop` - -2. **Social preview:** go to `https://socialify.git.ci/bbopen/tywrap`, download the PNG, upload it via Settings > General > Social preview - -3. **Discussions:** Settings > General > Features > check "Discussions". Create categories: Q&A, Show and Tell, Ideas. Pin a welcome post. - -4. **Labels:** Add these via Issues > Labels > New label: - - `good first issue` (green `#7057ff`) — note: GitHub may already have this - - `help wanted` (green `#008672`) — may already exist - - `area:runtime` (blue `#0075ca`) - - `area:codegen` (orange `#e4e669`) - - `area:types` (purple `#d93f0b`) - - `area:docs` (yellow `#fef2c0`) - - `area:ci` (gray `#cccccc`) - - `breaking` (red `#b60205`) - -5. **Tag 2-3 open issues** with `good first issue` and `help wanted`. - -6. **GitHub Pages:** Settings > Pages > Source: set to "GitHub Actions" (not a branch). Do this before the docs workflow runs. - -7. **Codecov:** Sign up at codecov.io with your GitHub account, authorize the `bbopen/tywrap` repo, copy the `CODECOV_TOKEN`, add it as a repo secret: Settings > Secrets > Actions > New repository secret, name `CODECOV_TOKEN`. - ---- - -## Phase 1: Quick Wins — Package Metadata & README - -### Task 1: Clean up `package.json` metadata - -**Files:** -- Modify: `package.json` - -**Step 1: Edit `package.json`** - -Change `description` from: -``` -"TypeScript wrapper for Python libraries with full type safety (EXPERIMENTAL - v0.2.1)" -``` -to: -``` -"Generate type-safe TypeScript wrappers for any Python library — Node.js, Deno, Bun, and browsers via Pyodide." -``` - -Change `homepage` from: -``` -"https://github.com/bbopen/tywrap#readme" -``` -to: -``` -"https://bbopen.github.io/tywrap" -``` - -Add these keywords to the existing `keywords` array (keep existing, add new ones): -```json -"codegen", "ffi", "bindings", "wrapper", "numpy", "pandas", "scipy", "python-wrapper", "rpc", "subprocess" -``` - -Final keywords array should be: -```json -"keywords": [ - "typescript", "python", "bridge", "type-safety", "type-safe", - "interop", "pyodide", "ast", "code-generation", "codegen", - "node", "deno", "bun", "ffi", "bindings", "wrapper", - "numpy", "pandas", "scipy", "python-wrapper", "rpc", "subprocess" -] -``` - -**Step 2: Verify** -```bash -npm pack --dry-run 2>&1 | head -20 -``` -Expected: shows updated description in output (no EXPERIMENTAL text). - -**Step 3: Commit** -```bash -git add package.json -git commit -m "chore: clean up package.json metadata for npm discoverability" -``` - ---- - -### Task 2: Clean up `tywrap_ir/pyproject.toml` - -**Files:** -- Modify: `tywrap_ir/pyproject.toml` - -**Step 1: Edit `tywrap_ir/pyproject.toml`** - -Update `keywords`: -```toml -keywords = ["tywrap", "typescript", "python", "bridge", "code-generation", "ast", "ir", "interop", "type-safe", "bindings", "ffi", "codegen"] -``` - -Add new classifiers (append after existing ones, before the closing `]`): -```toml - "Topic :: Software Development :: Compilers", - "Topic :: Utilities", - "Environment :: Console", -``` - -Update `[project.urls]` Documentation: -```toml -Documentation = "https://bbopen.github.io/tywrap" -``` - -**Step 2: Verify** -```bash -cd tywrap_ir && python -c "import tomllib; d = tomllib.load(open('pyproject.toml','rb')); print(d['project']['classifiers'])" -``` -Expected: list including `"Topic :: Software Development :: Compilers"`. - -**Step 3: Commit** -```bash -git add tywrap_ir/pyproject.toml -git commit -m "chore: update tywrap-ir PyPI classifiers and docs URL" -``` - ---- - -### Task 3: Update `README.md` - -**Files:** -- Modify: `README.md` - -**Step 1: Add two new badges** — insert after the existing 4 badges (after the CI badge line): -```markdown -[](https://www.npmjs.com/package/tywrap) -[](CONTRIBUTING.md) -``` - -**Step 2: Add a Documentation badge/link** — add after the existing badge block: -```markdown -[](https://bbopen.github.io/tywrap) -``` - -**Step 3: Update the ⚠️ experimental notice** — replace existing notice: -```markdown -> **⚠️ Experimental Software (v0.2.1)** - APIs may change between versions. Not recommended for production use until v1.0.0. -``` -with: -```markdown -> **⚠️ Experimental** — APIs may change before v1.0.0. See [CHANGELOG](./CHANGELOG.md) for breaking changes. -``` - -**Step 4: Add "Why tywrap?" section** — insert after the `## Features` section and before `## Requirements`: - -```markdown -## Why tywrap? - -| Feature | tywrap | pythonia | node-calls-python | pymport | -|---------|--------|----------|-------------------|---------| -| Auto-generated TypeScript types | Yes | No | No | No | -| Browser / WASM (Pyodide) | Yes | No | No | No | -| numpy / pandas type mappings | Yes | No | No | No | -| Node.js + Bun + Deno | All three | Node only | Node only | Node only | -| Apache Arrow binary transport | Yes | No | No | No | -``` - -**Step 5: Add star CTA** — insert immediately after the `## Quick Start` closing code block: -```markdown -> If tywrap saves you time, a ⭐ on GitHub helps others find it. -``` - -**Step 6: Commit** -```bash -git add README.md -git commit -m "docs: add Why tywrap comparison table, badges, and star CTA" -``` - ---- - -### Task 4: Create `AGENTS.md` and `CLAUDE.md` - -**Files:** -- Create: `AGENTS.md` -- Create: `CLAUDE.md` - -**Step 1: Create `AGENTS.md`** at the repo root with this content: - -```markdown -# AGENTS.md - -> This file helps AI coding assistants (Cursor, Copilot, Claude Code, Devin, Jules, Aider, Warp) -> understand the tywrap codebase and contribute effectively. - -## Project Overview - -tywrap generates type-safe TypeScript wrappers for Python libraries. It has two components: -- **`tywrap`** (npm) — TypeScript/Node.js/Bun/Deno runtime bridges and CLI code generator -- **`tywrap-ir`** (PyPI) — Python AST analyzer that extracts typed IR from Python source - -The generated wrappers let you call Python functions from TypeScript with full type safety, -using either a subprocess bridge (Node/Bun/Deno), in-browser WebAssembly (Pyodide), or HTTP. - -## Build & Test Commands - -```bash -# Setup -npm ci # Install Node.js dependencies -pip install -e tywrap_ir/ # Install Python component (editable) - -# Build & type-check -npm run build # Compile TypeScript → dist/ -npm run typecheck # tsc --noEmit (no output) -npm run lint # ESLint on src/ and test/ - -# Test -npm test # Full Vitest suite -npm run test:types # TSD type-level tests (test-d/) -npm run test:bun # Bun-specific tests (requires Bun installed) -npm run test:coverage # Coverage report → coverage/lcov.info - -# Python integration tests -npm run test:python:suite:core # Core Python libs (stdlib, etc.) -npm run test:python:suite:data # Data libs (numpy, pandas, pyarrow) -``` - -## Repository Structure - -``` -src/ - cli.ts # CLI entry point (tywrap init / generate) - runtime/ - node.ts # NodeBridge — subprocess-based (Node, Bun, Deno) - pyodide.ts # PyodideBridge — browser WebAssembly - http.ts # HttpBridge — remote Python server - utils/ - runtime.ts # detectRuntime(), isBun(), isDeno(), etc. -tywrap_ir/ # Python package: AST analysis → typed IR - tywrap_ir/ - __main__.py # CLI entry: tywrap-ir -runtime/ - python_bridge.py # Python subprocess server (JSONL protocol) -test/ # Vitest tests mirroring src/ structure -test-d/ # TSD type tests -docs/ # Documentation (migrated to VitePress) -examples/ - living-app/ # End-to-end example with pandas + Arrow -generated/ # Generated wrapper output (gitignored in user projects) -dist/ # Compiled TypeScript output (do not edit) -``` - -## Code Conventions - -- TypeScript strict mode; avoid `any` — use `unknown` and type guards instead -- Runtime bridges: `src/runtime/`; corresponding tests: `test/runtime_*.test.ts` -- Python code: `tywrap_ir/`; follow PEP 8, use type hints -- Generated files (`generated/`, `dist/`) — never edit manually -- `TYWRAP_PERF_BUDGETS=1` enables performance budget assertions in tests -- `NODE_OPTIONS=--expose-gc` required for GC-sensitive tests - -## Commit Conventions - -Conventional commits with scope: -- `feat(runtime): add warmup support` -- `fix(codec): handle oversized Arrow payloads` -- `test(pyodide): add retry regression` -- `docs: update Bun runtime guide` -- `chore: bump vitest` - -## PR Guidelines - -1. Include tests for new runtime behavior (see `test/runtime_*.test.ts` patterns) -2. Run `npm run check:all` before pushing — this runs format, lint, build, type tests, and unit tests -3. Wait for CI to be green including the `required` job -4. Resolve all CodeRabbit review threads before merging - -## Environment Variables (for testing) - -| Var | Purpose | -|-----|---------| -| `TYWRAP_PERF_BUDGETS=1` | Enable perf budget assertions | -| `NODE_OPTIONS=--expose-gc` | Enable GC for memory tests | -| `TYWRAP_CODEC_PYTHON=python` | Python path for codec tests | -| `TYWRAP_CODEC_MAX_BYTES` | Max response size (default 1MB) | -| `TYWRAP_CODEC_FALLBACK=json` | Disable Arrow, use JSON only | -``` - -**Step 2: Create `CLAUDE.md`** — identical content to `AGENTS.md`. Run: -```bash -cp AGENTS.md CLAUDE.md -``` - -**Step 3: Commit** -```bash -git add AGENTS.md CLAUDE.md -git commit -m "docs: add AGENTS.md and CLAUDE.md for AI coding agent discoverability" -``` - ---- - -## Phase 2: VitePress Documentation Site - -### Task 5: Install VitePress and add npm scripts - -**Files:** -- Modify: `package.json` - -**Step 1: Install VitePress** -```bash -npm install --save-dev vitepress -``` - -**Step 2: Add scripts** — add to the `scripts` block in `package.json`: -```json -"docs:dev": "vitepress dev docs", -"docs:build": "vitepress build docs", -"docs:preview": "vitepress preview docs" -``` - -**Step 3: Verify VitePress installed** -```bash -npx vitepress --version -``` -Expected: prints a version like `1.x.x`. - -**Step 4: Commit** -```bash -git add package.json package-lock.json -git commit -m "chore: install vitepress and add docs scripts" -``` - ---- - -### Task 6: Create VitePress config - -**Files:** -- Create: `docs/.vitepress/config.ts` - -**Step 1: Create the config file** - -```typescript -import { defineConfig } from 'vitepress' - -export default defineConfig({ - title: 'tywrap', - description: 'Generate type-safe TypeScript wrappers for any Python library — Node.js, Deno, Bun, and browsers via Pyodide.', - base: '/tywrap/', - cleanUrls: true, - - head: [ - ['link', { rel: 'icon', href: '/tywrap/favicon.ico' }], - ], - - themeConfig: { - logo: '/logo.svg', - siteTitle: 'tywrap', - - nav: [ - { text: 'Guide', link: '/guide/getting-started' }, - { text: 'Reference', link: '/reference/cli' }, - { text: 'Examples', link: '/examples/' }, - { - text: 'GitHub', - link: 'https://github.com/bbopen/tywrap', - target: '_blank', - }, - ], - - sidebar: [ - { - text: 'Guide', - items: [ - { text: 'Getting Started', link: '/guide/getting-started' }, - { text: 'Configuration', link: '/guide/configuration' }, - { - text: 'Runtime Bridges', - collapsed: false, - items: [ - { text: 'Comparison', link: '/guide/runtimes/comparison' }, - { text: 'Node.js', link: '/guide/runtimes/node' }, - { text: 'Bun', link: '/guide/runtimes/bun' }, - { text: 'Deno', link: '/guide/runtimes/deno' }, - { text: 'Browser (Pyodide)', link: '/guide/runtimes/browser' }, - { text: 'HTTP Bridge', link: '/guide/runtimes/http' }, - ], - }, - ], - }, - { - text: 'Reference', - items: [ - { text: 'CLI', link: '/reference/cli' }, - { text: 'Environment Variables', link: '/reference/env-vars' }, - { text: 'Type Mapping', link: '/reference/type-mapping' }, - { text: 'API', link: '/reference/api/' }, - ], - }, - { - text: 'Examples', - items: [ - { text: 'Quick Examples', link: '/examples/' }, - ], - }, - { - text: 'Help', - items: [ - { text: 'Troubleshooting', link: '/troubleshooting/' }, - ], - }, - ], - - search: { - provider: 'local', - }, - - socialLinks: [ - { icon: 'github', link: 'https://github.com/bbopen/tywrap' }, - { icon: 'npm', link: 'https://www.npmjs.com/package/tywrap' }, - ], - - editLink: { - pattern: 'https://github.com/bbopen/tywrap/edit/main/docs/:path', - text: 'Edit this page on GitHub', - }, - - footer: { - message: 'Released under the MIT License.', - copyright: 'Copyright © tywrap contributors', - }, - }, -}) -``` - -**Step 2: Commit** -```bash -git add docs/.vitepress/config.ts -git commit -m "docs: add vitepress config with full sidebar" -``` - ---- - -### Task 7: Create VitePress landing page - -**Files:** -- Create: `docs/index.md` - -**Step 1: Create `docs/index.md`** (VitePress home page format): - -```markdown ---- -layout: home - -hero: - name: tywrap - text: TypeScript wrappers for Python libraries - tagline: Auto-generate type-safe TypeScript bindings for any Python library — works in Node.js, Bun, Deno, and browsers via Pyodide. - actions: - - theme: brand - text: Get Started - link: /guide/getting-started - - theme: alt - text: View on GitHub - link: https://github.com/bbopen/tywrap - -features: - - icon: 🔒 - title: Full Type Safety - details: TypeScript definitions generated directly from Python source analysis via AST — no manual type writing. - - icon: 🌐 - title: Multi-Runtime - details: One API across Node.js, Bun, Deno (subprocess), and browsers (Pyodide WebAssembly). - - icon: ⚡ - title: Rich Data Types - details: First-class support for numpy, pandas, scipy, torch, and sklearn with Apache Arrow binary transport. - - icon: 🛠 - title: Zero-Config CLI - details: Run `npx tywrap generate` and get production-ready TypeScript wrappers with a single command. ---- - -## Quick Start - -```bash -npm install tywrap -pip install tywrap-ir -npx tywrap init -npx tywrap generate -``` - -```typescript -import { NodeBridge } from 'tywrap/node'; -import { setRuntimeBridge } from 'tywrap/runtime'; -import * as math from './generated/math.generated.js'; - -setRuntimeBridge(new NodeBridge({ pythonPath: 'python3' })); -const result = await math.sqrt(16); // 4 — fully typed -``` - -> ⚠️ **Experimental** — APIs may change before v1.0.0. See [CHANGELOG](https://github.com/bbopen/tywrap/blob/main/CHANGELOG.md) for breaking changes. - -> If tywrap saves you time, a ⭐ on [GitHub](https://github.com/bbopen/tywrap) helps others find it. -``` - -**Step 2: Commit** -```bash -git add docs/index.md -git commit -m "docs: add vitepress landing page" -``` - ---- - -### Task 8: Migrate existing docs into VitePress structure - -The existing docs are already at the right paths for VitePress (`docs/getting-started.md`, `docs/configuration.md`, etc.). VitePress will serve them from `docs/` automatically based on the sidebar config. You need to: - -1. **Rename/move paths to match config:** The config uses `/guide/getting-started` etc., but existing files are at `docs/getting-started.md`. Move them: - -```bash -mkdir -p docs/guide docs/guide/runtimes docs/reference docs/examples -mv docs/getting-started.md docs/guide/getting-started.md -mv docs/configuration.md docs/guide/configuration.md -mv docs/runtimes/nodejs.md docs/guide/runtimes/node.md -mv docs/runtimes/browser.md docs/guide/runtimes/browser.md -mv docs/type-mapping-matrix.md docs/reference/type-mapping.md -mv docs/api docs/reference/api -mv docs/examples/README.md docs/examples/index.md -mv docs/troubleshooting/README.md docs/troubleshooting/index.md -``` - -2. **Update cross-links** inside the moved files. Search for old relative paths like `../configuration.md` and update to the new paths. Use: -```bash -grep -r "\.\./\|getting-started\|configuration\|type-mapping-matrix" docs/guide/ docs/reference/ -``` -Then edit each file to fix broken relative links. - -**Step 2: Verify locally** -```bash -npm run docs:dev -``` -Open `http://localhost:5173/tywrap/guide/getting-started` — content should render with sidebar. - -**Step 3: Commit** -```bash -git add docs/ -git commit -m "docs: migrate existing docs into vitepress guide/reference structure" -``` - ---- - -### Task 9: New Bun runtime guide - -**Files:** -- Create: `docs/guide/runtimes/bun.md` - -**Step 1: Create the file** - -```markdown -# Bun Runtime Guide - -tywrap works with [Bun](https://bun.sh/) 1.1+ using the same `NodeBridge` as Node.js. No separate bridge or `npm:` prefix is needed. - -## Installation - -```bash -bun add tywrap -pip install tywrap-ir -``` - -## Basic Setup - -```typescript -import { NodeBridge } from 'tywrap'; // main export, no /node needed -import { setRuntimeBridge } from 'tywrap/runtime'; - -setRuntimeBridge(new NodeBridge({ - pythonPath: 'python3', - virtualEnv: '.venv', // optional: path to your venv - timeoutMs: 30000, -})); -``` - -> **Note:** With Bun you import from `'tywrap'` directly. This differs from Node.js where you may use `'tywrap/node'`. - -## bunfig.toml - -Tywrap ships with a `bunfig.toml` you can reference for build and run settings: - -```toml -[build] -target = "bun" -format = "esm" -splitting = true -sourcemap = "external" -external = ["pyodide"] - -[dev] -hot = true -``` - -## Configuration Options - -`NodeBridge` accepts the same options under Bun as under Node.js. See the [Node.js guide](./node) for the full option reference. - -Key options for Bun: - -| Option | Default | Description | -|--------|---------|-------------| -| `pythonPath` | auto-detect | Path to `python3` executable | -| `virtualEnv` | — | Path to virtual environment directory | -| `timeoutMs` | 30000 | Request timeout in milliseconds | -| `inheritProcessEnv` | `false` | Set `true` to pass full `process.env` to subprocess | - -## Environment Variables - -The same `TYWRAP_*` env vars work under Bun. See the [environment variables reference](/reference/env-vars). - -## Running Bun-Specific Tests - -```bash -bun run vitest --run test/runtime_bun.test.ts -``` - -Or via npm script: - -```bash -npm run test:bun -``` - -## Virtual Environments - -```typescript -setRuntimeBridge(new NodeBridge({ - pythonPath: '.venv/bin/python', - virtualEnv: '.venv', -})); -``` - -## Troubleshooting - -**`python3: command not found`** — Set `pythonPath` explicitly or ensure Python is in `PATH`. - -**Subprocess times out** — Increase `timeoutMs` or check that `pip install tywrap-ir` was run in the correct environment. - -See the [Node.js troubleshooting guide](./node#troubleshooting) for additional patterns — they apply equally to Bun. -``` - -**Step 2: Commit** -```bash -git add docs/guide/runtimes/bun.md -git commit -m "docs: add Bun runtime guide" -``` - ---- - -### Task 10: New Deno runtime guide - -**Files:** -- Create: `docs/guide/runtimes/deno.md` - -**Step 1: Create the file** - -```markdown -# Deno Runtime Guide - -tywrap works with [Deno](https://deno.land/) 1.46+ using the same `NodeBridge` as Node.js. Deno requires the `npm:` prefix for npm imports. - -## ⚠️ Deno Deploy Limitation - -**Deno Deploy does NOT support subprocess execution.** Because tywrap's `NodeBridge` spawns a Python subprocess, it cannot run in Deno Deploy. - -**Alternatives for Deno Deploy:** -- Use [`PyodideBridge`](./browser) — runs Python in-browser via WebAssembly (no subprocess) -- Use [`HttpBridge`](./http) — connects to a remote Python server over HTTP - -## Installation - -```bash -deno add npm:tywrap -pip install tywrap-ir -``` - -Or import directly: -```typescript -import { NodeBridge } from 'npm:tywrap'; -``` - -## Required Permissions - -Deno requires explicit permission flags for subprocess execution: - -```bash -deno run \ - --allow-run=python3 \ - --allow-read \ - --allow-env \ - your-script.ts -``` - -| Flag | Reason | -|------|--------| -| `--allow-run=python3` | Spawn the Python subprocess | -| `--allow-read` | Read Python scripts and config files | -| `--allow-env` | Read `TYWRAP_*` and `PATH` environment variables | - -## Basic Setup - -```typescript -import { NodeBridge } from 'npm:tywrap'; -import { setRuntimeBridge } from 'npm:tywrap/runtime'; - -setRuntimeBridge(new NodeBridge({ - pythonPath: 'python3', - timeoutMs: 30000, -})); -``` - -## Type Checking - -```bash -deno check src/index.ts -``` - -## Configuration Options - -See the [Node.js guide](./node) for the full `NodeBridgeOptions` reference — all options work identically in Deno. - -## Environment Variables - -The same `TYWRAP_*` env vars work under Deno. See the [environment variables reference](/reference/env-vars). - -## When to Use Each Bridge in Deno - -| Scenario | Bridge | Notes | -|----------|--------|-------| -| Local Deno script | `NodeBridge` | Needs `--allow-run` | -| Deno Deploy | `PyodideBridge` | WebAssembly, no subprocess | -| Deno Deploy + heavy libs | `HttpBridge` | Python runs on a separate server | - -## Troubleshooting - -**`PermissionDenied: Requires run access to "python3"`** — Add `--allow-run=python3` to your `deno run` command. - -**`NotSupported: Subprocess access is not allowed`** — You are running in Deno Deploy. Switch to `PyodideBridge` or `HttpBridge`. - -See the [Node.js troubleshooting guide](./node#troubleshooting) for additional patterns. -``` - -**Step 2: Commit** -```bash -git add docs/guide/runtimes/deno.md -git commit -m "docs: add Deno runtime guide with Deploy limitation warning" -``` - ---- - -### Task 11: New HTTP bridge guide - -**Files:** -- Create: `docs/guide/runtimes/http.md` - -**Step 1: Create the file** - -```markdown -# HTTP Bridge Guide - -`HttpBridge` connects to a Python server over HTTP. Use it when Python must run in a separate process or on a separate machine — including Deno Deploy, edge functions, or distributed architectures. - -## When to Use HttpBridge - -- You cannot spawn subprocesses (Deno Deploy, Cloudflare Workers, serverless) -- Python must run on a dedicated server for resource reasons -- You want to share one Python server across multiple TypeScript clients - -## Installation - -```bash -npm install tywrap -pip install tywrap-ir -``` - -## TypeScript Setup - -```typescript -import { HttpBridge } from 'tywrap/http'; -import { setRuntimeBridge } from 'tywrap/runtime'; - -setRuntimeBridge(new HttpBridge({ - baseURL: 'http://localhost:8080', - timeoutMs: 30000, - headers: { - 'Authorization': 'Bearer your-token', // optional - }, -})); -``` - -## Options - -| Option | Required | Default | Description | -|--------|----------|---------|-------------| -| `baseURL` | Yes | — | Base URL of the Python bridge server | -| `headers` | No | `{}` | Additional HTTP headers (auth, etc.) | -| `timeoutMs` | No | `30000` | Request timeout in milliseconds | -| `codec` | No | Arrow | Codec options — see [configuration](/guide/configuration) | - -## Running the Python Server - -Start the Python bridge server using the bundled script: - -```bash -pip install tywrap-ir flask # flask is needed for HTTP server mode -python -m tywrap_ir.server --port 8080 -``` - -Or point tywrap to your own HTTP server that implements the JSONL-over-HTTP protocol. - -> **Note:** HTTP transport is stateless — each call is an independent POST request. There is no persistent connection or session state. - -## Protocol - -Each call sends a POST to `{baseURL}/call` with a JSON body: -```json -{ "module": "math", "function": "sqrt", "args": [16], "kwargs": {} } -``` -Response: -```json -{ "result": 4.0 } -``` - -## Apache Arrow - -Arrow binary transport works over HTTP. Enable it by registering a decoder: - -```typescript -import { registerArrowDecoder } from 'tywrap'; -import { tableFromIPC } from 'apache-arrow'; -registerArrowDecoder(bytes => tableFromIPC(bytes)); -``` - -## Environment Variables - -| Var | Purpose | -|-----|---------| -| `TYWRAP_CODEC_FALLBACK=json` | Disable Arrow, use JSON only | -| `TYWRAP_CODEC_MAX_BYTES` | Cap max response size | - -See [environment variables reference](/reference/env-vars). - -## Security - -- Always use HTTPS in production -- Set `Authorization` headers for server access control -- Consider rate-limiting the Python server endpoint -``` - -**Step 2: Commit** -```bash -git add docs/guide/runtimes/http.md -git commit -m "docs: add HTTP bridge guide" -``` - ---- - -### Task 12: Runtime comparison page - -**Files:** -- Create: `docs/guide/runtimes/comparison.md` - -**Step 1: Create the file** - -```markdown -# Runtime Comparison - -tywrap supports five runtime configurations. Choose based on your environment and requirements. - -## Feature Matrix - -| Feature | Node.js | Bun | Deno (local) | Browser (Pyodide) | HTTP | -|---------|:-------:|:---:|:------------:|:-----------------:|:----:| -| Python subprocess | ✅ | ✅ | ✅ | ❌ | ❌ | -| Deno Deploy / serverless | ❌ | ❌ | ❌ | ✅ | ✅ | -| Apache Arrow transport | ✅ | ✅ | ✅ | ✅ | ✅ | -| Virtual environment support | ✅ | ✅ | ✅ | ❌ | Server-side | -| Process pooling (experimental) | ✅ | ✅ | ✅ | ❌ | ❌ | -| numpy / pandas support | ✅ | ✅ | ✅ | Limited | ✅ | -| Tested in CI | ✅ | ✅ | Mocked | ✅ | ✅ | - -## Import Paths - -| Runtime | Import | -|---------|--------| -| Node.js | `import { NodeBridge } from 'tywrap/node'` | -| Bun | `import { NodeBridge } from 'tywrap'` | -| Deno | `import { NodeBridge } from 'npm:tywrap'` | -| Browser | `import { PyodideBridge } from 'tywrap/pyodide'` | -| HTTP | `import { HttpBridge } from 'tywrap/http'` | - -## Decision Guide - -``` -Do you need to run Python in a subprocess? -├── Yes → Does your environment allow subprocess execution? -│ ├── Yes (Node.js or Bun) → Use NodeBridge -│ ├── Yes (Deno local) → Use NodeBridge with --allow-run -│ └── No (Deno Deploy, serverless) → Continue below -└── No (browser, edge, serverless) → - ├── Can you load ~50MB WASM? → Use PyodideBridge (Pyodide) - └── No / need heavy Python libs → Use HttpBridge -``` - -## Bridge Classes - -| Bridge | Package export | Guide | -|--------|---------------|-------| -| `NodeBridge` | `tywrap/node` or `tywrap` | [Node.js](./node) · [Bun](./bun) · [Deno](./deno) | -| `PyodideBridge` | `tywrap/pyodide` | [Browser](./browser) | -| `HttpBridge` | `tywrap/http` | [HTTP](./http) | -``` - -**Step 2: Commit** -```bash -git add docs/guide/runtimes/comparison.md -git commit -m "docs: add runtime comparison page" -``` - ---- - -### Task 13: New CLI reference page - -**Files:** -- Create: `docs/reference/cli.md` - -**Step 1: Create the file** - -```markdown -# CLI Reference - -The `tywrap` CLI generates TypeScript wrappers from Python source. - -## Installation - -```bash -npm install tywrap # local -npm install -g tywrap # global -``` - -Or use without installing: -```bash -npx tywrap -``` - -## Commands - -### `tywrap init` - -Creates a `tywrap.config.ts` (or `.json`) in the current directory and adds `tywrap:generate` and `tywrap:check` scripts to `package.json` if it exists. - -```bash -npx tywrap init -``` - -### `tywrap generate` - -Reads `tywrap.config.ts` (or the path specified by `--config`) and generates TypeScript wrappers for all configured Python modules. - -```bash -npx tywrap generate -npx tywrap generate --config path/to/tywrap.config.json -``` - -| Flag | Description | -|------|-------------| -| `--config ` | Path to config file (default: `tywrap.config.ts`) | -| `--check` | Verify generated files match current Python source (useful in CI) | -| `--verbose` | Print detailed output | - -### `tywrap generate --check` - -Runs generation and compares output to the files already on disk. Exits with code 1 if anything would change. Use in CI to prevent stale generated files from being committed: - -```bash -npx tywrap generate --check -``` - -Add to CI: -```yaml -- name: Check generated wrappers are up to date - run: npx tywrap generate --check -``` - -## Config File - -```typescript -// tywrap.config.ts -import { defineConfig } from 'tywrap'; - -export default defineConfig({ - pythonModules: { - 'numpy': { alias: 'np' }, - 'pandas': { classes: ['DataFrame'], functions: ['read_csv'] }, - 'math': { functions: ['sqrt', 'pi'] }, - }, - output: { - dir: './src/generated', - format: 'esm', - declaration: true, - }, -}); -``` - -See the [Configuration guide](/guide/configuration) for all options. -``` - -**Step 2: Commit** -```bash -git add docs/reference/cli.md -git commit -m "docs: add CLI reference page" -``` - ---- - -### Task 14: New environment variables reference page - -**Files:** -- Create: `docs/reference/env-vars.md` - -**Step 1: Create the file** - -```markdown -# Environment Variables - -All `TYWRAP_*` environment variables are read at runtime by the bridge. - -## Reference - -| Variable | Runtimes | Default | Description | -|----------|----------|---------|-------------| -| `TYWRAP_PYTHON_PATH` | Node, Bun, Deno | auto-detect | Path to the Python executable (e.g. `/usr/bin/python3`) | -| `TYWRAP_VIRTUAL_ENV` | Node, Bun, Deno | — | Path to a virtual environment directory (activates it for the subprocess) | -| `TYWRAP_CODEC_FALLBACK` | Node, HTTP | `arrow` | Set to `json` to disable Apache Arrow and use JSON-only transport | -| `TYWRAP_CODEC_MAX_BYTES` | Node, HTTP | `1048576` (1MB) | Maximum serialized response size in bytes. Requests exceeding this fail with an explicit error instead of silently truncating. | -| `TYWRAP_REQUEST_MAX_BYTES` | Node, HTTP | — | Maximum serialized request size in bytes. Unbounded by default. | -| `TYWRAP_TORCH_ALLOW_COPY` | Node, HTTP | `false` | Set to `true` to allow implicit GPU→CPU tensor copies when serializing `torch.Tensor`. | -| `TYWRAP_PERF_BUDGETS` | Test suite | — | Set to `1` to enable performance budget assertions in the test suite. | - -## Usage Examples - -### Explicit Python path -```bash -TYWRAP_PYTHON_PATH=/opt/homebrew/bin/python3 node dist/app.js -``` - -### Virtual environment -```bash -TYWRAP_VIRTUAL_ENV=.venv node dist/app.js -``` - -### JSON-only transport (disable Arrow) -```bash -TYWRAP_CODEC_FALLBACK=json node dist/app.js -``` - -### Cap response size at 10MB -```bash -TYWRAP_CODEC_MAX_BYTES=10485760 node dist/app.js -``` - -## Precedence - -Environment variables are fallbacks — options passed directly to the bridge constructor take precedence: - -```typescript -// Constructor option wins over TYWRAP_PYTHON_PATH -new NodeBridge({ pythonPath: '/my/python' }) -``` - -## subprocess env inheritance - -By default, `NodeBridge` inherits only `PATH`, `PYTHON*`, and `TYWRAP_*` variables from `process.env` to keep the subprocess environment minimal and reproducible. To pass the full environment: - -```typescript -new NodeBridge({ inheritProcessEnv: true }) -``` -``` - -**Step 2: Commit** -```bash -git add docs/reference/env-vars.md -git commit -m "docs: add environment variables reference page" -``` - ---- - -### Task 15: Create `llms.txt` and `llms-full.txt` - -**Files:** -- Create: `docs/public/llms.txt` -- Create: `docs/public/llms-full.txt` - -VitePress serves everything in `docs/public/` at the site root. These files will be live at `https://bbopen.github.io/tywrap/llms.txt`. - -**Step 1: Create `docs/public/llms.txt`** - -```markdown -# tywrap - -> TypeScript wrapper generator for Python libraries. Auto-generates type-safe TypeScript -> bindings from Python source via AST analysis. Supports Node.js, Bun, Deno (subprocess), -> and browsers (Pyodide WebAssembly). Apache Arrow binary transport for numpy/pandas data. -> Experimental (v0.2.1) — APIs may change before v1.0.0. - -## Docs - -- [Getting Started](https://bbopen.github.io/tywrap/guide/getting-started): Installation, first wrapper, quick start -- [Configuration](https://bbopen.github.io/tywrap/guide/configuration): tywrap.config.ts / .json all options -- [Runtime Comparison](https://bbopen.github.io/tywrap/guide/runtimes/comparison): Which bridge to use (Node/Bun/Deno/Pyodide/HTTP) -- [Node.js Runtime](https://bbopen.github.io/tywrap/guide/runtimes/node): NodeBridge setup, virtual envs, pooling, Docker -- [Bun Runtime](https://bbopen.github.io/tywrap/guide/runtimes/bun): NodeBridge on Bun, bunfig.toml -- [Deno Runtime](https://bbopen.github.io/tywrap/guide/runtimes/deno): npm: prefix, --allow-run, Deploy limitation -- [Browser Runtime (Pyodide)](https://bbopen.github.io/tywrap/guide/runtimes/browser): WebAssembly Python, CDN setup -- [HTTP Bridge](https://bbopen.github.io/tywrap/guide/runtimes/http): Remote Python server, Deno Deploy -- [CLI Reference](https://bbopen.github.io/tywrap/reference/cli): tywrap init, generate, generate --check -- [Environment Variables](https://bbopen.github.io/tywrap/reference/env-vars): All TYWRAP_* variables -- [Type Mapping](https://bbopen.github.io/tywrap/reference/type-mapping): Python→TypeScript type conversions -- [Examples](https://bbopen.github.io/tywrap/examples/): Copy-pasteable runtime examples - -## Optional - -- [Troubleshooting](https://bbopen.github.io/tywrap/troubleshooting/): Common errors and fixes -- [Changelog](https://github.com/bbopen/tywrap/blob/main/CHANGELOG.md): Version history and breaking changes -- [Contributing](https://github.com/bbopen/tywrap/blob/main/CONTRIBUTING.md): Development setup, PR guidelines -- [AGENTS.md](https://github.com/bbopen/tywrap/blob/main/AGENTS.md): AI coding agent instructions -``` - -**Step 2: Create `docs/public/llms-full.txt`** - -This is a concatenation of all doc pages into a single file for agent frameworks that want full context in one fetch. Create a build script or assemble it manually from all the guide/reference/examples markdown files. For now, create a placeholder that documents the intent: - -```markdown -# tywrap — Full Documentation - -> This file contains the complete tywrap documentation concatenated for use by -> AI agent frameworks. See llms.txt for the structured index. - -[Paste full content of each doc page here, separated by --- dividers] -``` - -Note: Once the VitePress site is built, automate this by adding a `docs:build:llms` script that concatenates `docs/guide/**/*.md` + `docs/reference/**/*.md` into `docs/public/llms-full.txt` as part of `docs:build`. - -**Step 3: Commit** -```bash -git add docs/public/ -git commit -m "docs: add llms.txt and llms-full.txt for AI agent discoverability" -``` - ---- - -### Task 16: GitHub Pages deploy workflow - -**Files:** -- Create: `.github/workflows/docs.yml` - -**Step 1: Enable GitHub Pages** (manual UI step if not done yet): -Settings > Pages > Source: "GitHub Actions" - -**Step 2: Create the workflow** - -```yaml -name: Deploy Docs to GitHub Pages - -on: - push: - branches: [main] - workflow_dispatch: - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: read - pages: write - id-token: write - -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -concurrency: - group: pages - cancel-in-progress: false - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 # VitePress uses git history for "last updated" timestamps - - - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: npm - - - uses: actions/configure-pages@v4 - - - name: Install dependencies - run: npm ci --prefer-offline --no-audit - - - name: Build VitePress site - run: npm run docs:build - - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - path: docs/.vitepress/dist - - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - needs: build - runs-on: ubuntu-latest - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 -``` - -**Step 3: Push and verify** -```bash -git add .github/workflows/docs.yml -git commit -m "ci: add github pages docs deploy workflow" -git push -``` -Then check GitHub Actions tab — the `Deploy Docs to GitHub Pages` job should run and deploy the site. After it completes, visit `https://bbopen.github.io/tywrap/`. - ---- - -## Phase 3: Automation - -### Task 17: Add Codecov to CI - -**Files:** -- Modify: `.github/workflows/ci.yml` - -**Step 1: Add the Codecov upload step** - -In `.github/workflows/ci.yml`, find the `test` job. After the `Run tests` step, add: - -```yaml - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v5 - if: matrix.node-version == 22 && matrix.python-version == '3.11' - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ./coverage/lcov.info - fail_ci_if_error: false -``` - -The `if` condition uploads only once (from the Node 22 / Python 3.11 matrix combination) to avoid duplicate uploads. - -**Step 2: Ensure test:coverage generates lcov** - -The `test:coverage` script uses `@vitest/coverage-v8` (already in devDependencies). Verify `vitest.config.ts` outputs lcov: - -```bash -cat vitest.config.ts -``` - -If `coverage.reporter` does not include `'lcov'`, add it: -```typescript -coverage: { - reporter: ['text', 'lcov'], - // ... -} -``` - -**Step 3: Change test job to also collect coverage** - -In the `test` job's `Run tests` step, change: -```yaml -run: npm test -``` -to: -```yaml -run: npm run test:coverage -``` -(Only needed if you want coverage reported on every CI run. Otherwise keep `npm test` and add a separate coverage job.) - -**Step 4: Add coverage badge to README** - -In `README.md`, add to the badge row: -```markdown -[](https://codecov.io/gh/bbopen/tywrap) -``` - -**Step 5: Commit** -```bash -git add .github/workflows/ci.yml README.md vitest.config.ts -git commit -m "ci: add codecov coverage upload" -``` - ---- - -### Task 18: PR auto-labeling - -**Files:** -- Create: `.github/labeler.yml` -- Create: `.github/workflows/labeler.yml` - -**Step 1: Create `.github/labeler.yml`** - -```yaml -area:runtime: - - changed-files: - - any-glob-to-any-file: - - 'src/runtime/**' - - 'test/runtime*' - - 'runtime/**' - -area:codegen: - - changed-files: - - any-glob-to-any-file: - - 'src/codegen/**' - - 'src/cli.ts' - - 'tywrap_ir/**' - - 'test/python/**' - - 'test/cli*' - -area:types: - - changed-files: - - any-glob-to-any-file: - - 'src/types/**' - - 'test-d/**' - - 'tsd.json' - -area:docs: - - changed-files: - - any-glob-to-any-file: - - 'docs/**' - - '*.md' - - 'CONTRIBUTING.md' - - 'AGENTS.md' - - 'CLAUDE.md' - -area:ci: - - changed-files: - - any-glob-to-any-file: - - '.github/**' - - 'scripts/**' -``` - -**Step 2: Create `.github/workflows/labeler.yml`** - -```yaml -name: PR Labeler - -on: - pull_request_target: - types: [opened, synchronize, reopened] - -jobs: - labeler: - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write - steps: - - uses: actions/labeler@v6 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} -``` - -**Step 3: Commit** -```bash -git add .github/labeler.yml .github/workflows/labeler.yml -git commit -m "ci: add file-based pr auto-labeling" -``` - ---- - -### Task 19: Stale issue management - -**Files:** -- Create: `.github/workflows/stale.yml` - -**Step 1: Create `.github/workflows/stale.yml`** - -```yaml -name: Stale Issues and PRs - -on: - schedule: - - cron: '30 1 * * *' # 1:30am UTC daily - workflow_dispatch: - -permissions: - issues: write - pull-requests: write - -jobs: - stale: - runs-on: ubuntu-latest - steps: - - uses: actions/stale@v9 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - - # Issues - days-before-issue-stale: 90 - days-before-issue-close: 30 - stale-issue-label: 'stale' - stale-issue-message: > - This issue has been inactive for 90 days. It will be closed in 30 days - unless there is new activity. If this is still relevant, please comment - or add a reaction to keep it open. - close-issue-message: > - Closed due to inactivity. If this issue is still relevant, please open - a new one with updated context. - exempt-issue-labels: 'good first issue,help wanted,pinned,security' - - # PRs - days-before-pr-stale: 60 - days-before-pr-close: 14 - stale-pr-label: 'stale' - stale-pr-message: > - This PR has been inactive for 60 days. It will be closed in 14 days - unless there is new activity. If you intend to continue this work, - please rebase and push a new commit. - close-pr-message: > - Closed due to inactivity. Feel free to reopen if you resume work on this. - exempt-pr-labels: 'pinned,security' -``` - -**Step 2: Commit** -```bash -git add .github/workflows/stale.yml -git commit -m "ci: add stale issue and pr management workflow" -``` - ---- - -### Task 20: Renovate config - -**Files:** -- Create: `renovate.json` - -**Step 1: Create `renovate.json`** at repo root: - -```json -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": ["config:recommended"], - "packageRules": [ - { - "matchUpdateTypes": ["patch", "minor"], - "matchDepTypes": ["devDependencies"], - "automerge": true, - "automergeType": "pr", - "platformAutomerge": true - }, - { - "matchPackagePatterns": ["^@types/"], - "groupName": "TypeScript type definitions", - "automerge": true, - "automergeType": "pr" - }, - { - "matchDepTypes": ["dependencies"], - "automerge": false - } - ], - "schedule": ["before 6am on Monday"], - "prConcurrentLimit": 5, - "prHourlyLimit": 2 -} -``` - -**Step 2: Install Renovate GitHub App** - -Visit `https://github.com/apps/renovate` and install it for the `bbopen/tywrap` repository. Renovate will open a "Configure Renovate" PR within 24 hours. - -**Step 3: Commit** -```bash -git add renovate.json -git commit -m "chore: add renovate config for automated dependency updates" -``` - ---- - -### Task 21: release-please workflow - -**Files:** -- Create: `.github/workflows/release.yml` - -**Step 1: Create `.github/workflows/release.yml`** - -```yaml -name: Release Please - -on: - push: - branches: [main] - workflow_dispatch: - -permissions: - contents: write - pull-requests: write - -jobs: - release-please: - runs-on: ubuntu-latest - outputs: - release_created: ${{ steps.release.outputs.release_created }} - tag_name: ${{ steps.release.outputs.tag_name }} - steps: - - uses: googleapis/release-please-action@v4 - id: release - with: - release-type: node - token: ${{ secrets.GITHUB_TOKEN }} - - publish-npm: - needs: release-please - if: ${{ needs.release-please.outputs.release_created }} - runs-on: ubuntu-latest - environment: npm - permissions: - contents: read - id-token: write - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ needs.release-please.outputs.tag_name }} - - - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: npm - registry-url: 'https://registry.npmjs.org' - - - run: npm ci --prefer-offline --no-audit - - run: npm run build - - run: npm run test - env: - NODE_OPTIONS: --expose-gc - TYWRAP_PERF_BUDGETS: '1' - - - name: Publish to npm - run: npm publish --provenance --access public - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} -``` - -> **Note:** This workflow replaces the tag-based trigger in the existing `publish.yml`. Once `release.yml` is working, disable or delete the old `publish.yml` to avoid double-publishing. Keep `publish-pypi.yml` as-is and trigger it manually after a release, or wire it to listen for the same release tag. - -**Step 2: Commit** -```bash -git add .github/workflows/release.yml -git commit -m "ci: add release-please workflow for automated changelog and npm publish" -``` - ---- - -## Final Verification - -After all tasks are complete: - -```bash -# 1. Docs build locally -npm run docs:dev -# open http://localhost:5173/tywrap/ — verify all pages load, no 404s - -# 2. Package metadata -npm pack --dry-run 2>&1 | grep '"description"' -# expected: no EXPERIMENTAL text - -# 3. AGENTS.md present -cat AGENTS.md | head -5 -# expected: # AGENTS.md header - -# 4. After pushing to main: -# - GitHub Actions "Deploy Docs to GitHub Pages" job passes -# - curl https://bbopen.github.io/tywrap/llms.txt → returns markdown -# - curl https://bbopen.github.io/tywrap/guide/runtimes/bun → 200 OK - -# 5. CI coverage (after CODECOV_TOKEN secret is set): -# codecov.io/gh/bbopen/tywrap shows a coverage report - -# 6. Labeler test: open a PR touching src/runtime/ → area:runtime label appears - -# 7. Renovate: within 24h of installing the GitHub App, a "Configure Renovate" PR opens -``` diff --git a/docs/public/llms-full.txt b/docs/public/llms-full.txt index 1af48241..24dbfea9 100644 --- a/docs/public/llms-full.txt +++ b/docs/public/llms-full.txt @@ -7,18 +7,6 @@ --- layout: home -hero: - name: tywrap - text: TypeScript wrappers for Python libraries - tagline: Auto-generate type-safe TypeScript bindings for any Python library — works in Node.js, Bun, Deno, and browsers via Pyodide. - actions: - - theme: brand - text: Get Started - link: /guide/getting-started - - theme: alt - text: View on GitHub - link: https://github.com/bbopen/tywrap - features: - icon: 🔒 title: Full Type Safety diff --git a/package-lock.json b/package-lock.json index 7534ad89..af97a0ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "devDependencies": { "@types/bun": "^1.3.10", "@types/node": "^25.5.0", + "@types/three": "^0.183.1", "@types/yargs": "^17.0.35", "@typescript-eslint/eslint-plugin": "^8.57.1", "@typescript-eslint/parser": "^8.5.0", @@ -34,7 +35,9 @@ "eslint-plugin-import": "^2.29.1", "eslint-plugin-security": "^3.0.1", "fast-check": "^4.6.0", + "postprocessing": "^6.38.3", "prettier": "^3.8.1", + "three": "^0.183.2", "tsd": "^0.33.0", "vite": "^7.3.0", "vitepress": "^1.6.4", @@ -402,6 +405,13 @@ "node": ">=18" } }, + "node_modules/@dimforge/rapier3d-compat": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", + "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/@docsearch/css": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.8.2.tgz", @@ -1726,6 +1736,13 @@ "node": ">=14.17" } }, + "node_modules/@tweenjs/tween.js": { + "version": "23.1.3", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", + "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", + "dev": true, + "license": "MIT" + }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", @@ -1880,6 +1897,29 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/stats.js": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz", + "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/three": { + "version": "0.183.1", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.183.1.tgz", + "integrity": "sha512-f2Pu5Hrepfgavttdye3PsH5RWyY/AvdZQwIVhrc4uNtvF7nOWJacQKcoVJn0S4f0yYbmAE6AR+ve7xDcuYtMGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@dimforge/rapier3d-compat": "~0.12.0", + "@tweenjs/tween.js": "~23.1.3", + "@types/stats.js": "*", + "@types/webxr": ">=0.5.17", + "@webgpu/types": "*", + "fflate": "~0.8.2", + "meshoptimizer": "~1.0.1" + } + }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -1894,6 +1934,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/webxr": { + "version": "0.5.24", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz", + "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/yargs": { "version": "17.0.35", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", @@ -2856,6 +2903,13 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/@webgpu/types": { + "version": "0.1.69", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.69.tgz", + "integrity": "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -4741,6 +4795,13 @@ "reusify": "^1.0.4" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "license": "MIT" + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -6188,6 +6249,13 @@ "node": ">= 8" } }, + "node_modules/meshoptimizer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.0.1.tgz", + "integrity": "sha512-Vix+QlA1YYT3FwmBBZ+49cE5y/b+pRrcXKqGpS5ouh33d3lSp2PoTpCw19E0cKDFWalembrHnIaZetf27a+W2g==", + "dev": true, + "license": "MIT" + }, "node_modules/micromark-util-character": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", @@ -6802,6 +6870,16 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postprocessing": { + "version": "6.38.3", + "resolved": "https://registry.npmjs.org/postprocessing/-/postprocessing-6.38.3.tgz", + "integrity": "sha512-5qCFp8j62nWL6sSVv/RKuHscQUIV+VMMgWeHLYZQEBpAk7G+r3jA3bSKON7gZjiuxdZ/F4PXj2Jc1oPh/7Eg+g==", + "dev": true, + "license": "Zlib", + "peerDependencies": { + "three": ">= 0.157.0 < 0.184.0" + } + }, "node_modules/preact": { "version": "10.29.0", "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.0.tgz", @@ -7939,6 +8017,13 @@ "node": ">=12.17" } }, + "node_modules/three": { + "version": "0.183.2", + "resolved": "https://registry.npmjs.org/three/-/three-0.183.2.tgz", + "integrity": "sha512-di3BsL2FEQ1PA7Hcvn4fyJOlxRRgFYBpMTcyOgkwJIaDOdJMebEFPA+t98EvjuljDx4hNulAGwF6KIjtwI5jgQ==", + "dev": true, + "license": "MIT" + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", diff --git a/package.json b/package.json index 93a71a20..64e9337a 100644 --- a/package.json +++ b/package.json @@ -126,6 +126,7 @@ "devDependencies": { "@types/bun": "^1.3.10", "@types/node": "^25.5.0", + "@types/three": "^0.183.1", "@types/yargs": "^17.0.35", "@typescript-eslint/eslint-plugin": "^8.57.1", "@typescript-eslint/parser": "^8.5.0", @@ -141,7 +142,9 @@ "tsd": "^0.33.0", "vite": "^7.3.0", "vitepress": "^1.6.4", - "vitest": "^4.0.16" + "vitest": "^4.0.16", + "three": "^0.183.2", + "postprocessing": "^6.38.3" }, "peerDependencies": { "pyodide": ">=0.28.0"
+ Seamlessly integrate the unmatched computational power of Python's elite ecosystem—like PyTorch, SciPy, pandas, and NumPy—directly within your TypeScript applications, fully protected by a robust type system. +