Hi Gergely,
Thanks for trying out the WASM runtime in the playground!
Here is my take on how we can achieve the flexibility you are looking for without falling into the trap of dual-track maintenance.
1. TypeScript-Only Backend
Why restrict this architectural shift strictly to TypeScript? Because we have strictly typed Python throughout Framework M, our primitives are language-agnostic data structures. In theory, any target runtime should be possible once we decouple the engine from the delivery layer.
2. Zero Python Dependency Option
This is the most challenging piece of the puzzle. It is practically impossible to manually write, maintain, and sync the full framework logic across multiple different runtimes in parallel. I will only take this route if we write the core logic once and automate the downstream translations.
Instead of writing runtime adapters, my plan is to keep the core engine entirely in Python. We will expose the internal APIs that define our primitives:
DocType / Meta
Controller / Services
Repositories
Permissions, etc.
Once these primitives are cleanly exposed, we can write CLI extensions that read, interpret, and compile them into a given runtime (like NestJS for TypeScript). Instead of forcing a Python-like GenericRepository onto a Node environment, the compiler will generate native, idiomatic boilerplate specific to that runtime.
3. Schema & Validation Strategy
Instead of baking a specific validation tool into the core framework, we will choose and emit these tools dynamically based on the requirements of the target code generation. If we are compiling to a NestJS/TS target, the generator will output the appropriate Zod or Standard Schema constructs automatically.
4. Implementation & Runtime (The Envisioned Workflow)
I disagree slightly on the feasibility of continuous AI-assisted porting. Because Python will always receive priority features, performance adapters, and updates, relying on AI to continuously translate the core framework introduces too much non-deterministic maintenance overhead in CI/CD pipelines.
Instead, my envisioned workflow utilizes structured abstract syntax tree (AST) generation to handle updates cleanly:
[ Python Core / Meta ]
│
▼ (CLI Generation / ts_morph)
[ Pure TypeScript / NestJS Output ]
│
▼ (Merge into Dev Target)
[ Developer Overrides & Custom Logic ]
Proposed Codegen Lifecycle:
- Source of Truth: Everything is defined, typed, and authored in Code-First Python (Permissions, JSONSchema, Contexts, Endpoints, Workers, Jobs).
- Compilation: We use tools like
ts_morph (leveraging our existing Python wrappers) to parse the Python metadata and programmatically generate clean TypeScript/NestJS files with ~80% accuracy.
- Distribution: The CLI dumps out the generated code into the target repository. The developer can deploy this code directly.
- Service Continuity: Infrastructure connections remain unchanged. Because services like SpiceDB (permissions), NATS (event bus), Redis, or the primary database use the exact same schema regardless of runtime, hydrating and migrating them happens seamlessly via the primary workflow anyway.
- Conflict Resolution: The remaining ~20% of developer-tweaked logic is funneled into designated placeholder files (e.g., using a
.base.ts split-file pattern) to prevent future schema regenerations from triggering nasty Git merge conflicts.
This approach gives developers a completely zero-Python production environment (perfect for running light on Cloudflare workerd), while keeping the framework single-sourced and maintainable for us.
Strategic Considerations & Trade-Offs
While this multi-runtime compiler approach is a fascinating architectural puzzle, it isn't an immediate framework priority. If anyone from the community wants to pick this up, we will happily and officially support it by formally declaring and locking down our Primitives API.
As we look at this long-term, there are a few foundational questions we have to answer:
- What are the actual trade-offs of the runtime environment? No complex, multi-service application runs entirely on the edge as-is. Instead of chasing full-framework feature parity, shouldn't the goal be allowing devs to isolate and export only the specific parts fit for the edge? For example, exporting a lightweight worker, job, or a highly validated entry controller to a specialized runtime, while keeping the heavy application logic intact.
- Why complicate feature parity? Ports and Adapters already give us a programmatic blueprint to interact with and decompose the monolith when needed.
- Is there a deterministic bridge? If there is a "magic bullet" engine out there that can fully, deterministically translate our Python source-of-truth into an auto-generated, QA-tested runtime, I’d love to explore it. Otherwise, manual or AI-driven synchronization is too fragile.
Core Focus: ERPNext Feature Parity (Without the Technical Debt)
Right now, my engineering focus is strictly locked onto achieving high-quality ERPNext feature parity, while fundamentally solving the architectural problems that plague the traditional ecosystem:
- True Macroservices Mode: Building genuinely modular apps (Frontend = Shell + Micro-frontends; Backend = Main App + Libs). If a module like CRM needs to duplicate a customer record to decouple from Core, it should happen seamlessly—allowing the app to run as a single-process monolith by default, or split cleanly into macroservices when scale demands it.
- High-Write Tree Architectures: Moving beyond just
NestedSet. I have already added AdjacencyList (leveraging Recursive CTEs) to handle high-write tree structures cleanly and efficiently.
- Next-Gen Document Infrastructure: Implementing bulletproof GitOps-based print format templates, unified reporting engines, and robust read-model-based data architectures.
- Flawless Form Entry: Elevating data-entry ergonomics with high-fidelity forms, strict link-field validation, auto-alignment, and elegant workflow engine integrations (Invoices, Approvals, etc.).
- Global Accessibility: Expanding core localization with native RTL language re-arrangement layouts and exploring frictionless, headless localization contribution flows.
- AI-Driven Studio Architecture: Revamping the Studio out of the manual "user-clicks-to-create-doctype" paradigm and moving toward an AI-assisted context engine. You feed it an initial form; it maps the full end-to-end scaffolding, summarizes the conversational context, and assists you deterministically across later application versions, while keeping manual controls as a zero-cost fallback.
- Headless Localization / Studio Sync: Designing a decoupled Studio interface that can act as a lightweight translation portal for non-technical contributors. By utilizing GitLab/OAuth2 integrations, translators can sync JSON files and open Merge Requests directly without needing the full framework running locally.
- High-Availability (HA) Scheduling: Architecting a resilient, distributed cron and background task scheduler that scales smoothly across multi-node deployment topologies without double-execution risks.
- Permission-Driven UX & Entitlements: Dynamically tailoring the frontend navigation layouts, entry landing pages, and functional system entitlements based strictly on the user's granular security context and roles.
- Tenant Onboarding & Management: Building smooth, automated multi-tenant provisioning, lifecycle management, and resource allocation primitives directly into the core orchestration tooling.
- Enterprise-Grade Backup & Recovery: Implementing robust document backup and point-in-time recovery (PITR) workflows by leveraging Postgres backup best practices alongside NATS-driven event streams for data durability.
I'd love to know your thoughts on this compiler/generation approach versus a native runtime port, and how you see edge-delivery playing into modular architectures!
Revant.
Edit 1: I'd just use pyodide on workerd and run the app directly as it is showcased in playground. As long as app logic is using standard adapters and services they are override-able and whichever adapter breaks can be overridden via developer's app for the case. No need for transpiling then.