Skip to content
thirawat27Public

About

Ruvyxa is a production-minded web framework built around clarity, speed, and control. It keeps routing, server logic, validation, builds, and runtime output in one predictable workflow.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

20 stars

Watchers

2 watching

Forks

Latest commit

 

History

609 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Ruvyxa

Ruvyxa

Robust Universal Validation & Yielding eXperience Application

A React framework whose compiler, dev server, and production host are one Rust binary.
File-system routes, server rendering, actions, eleven deploy targets — or the whole site inside your own Rust service.

License Node 24.19+ Compiler written in Rust Runtime written in TypeScript npm version


Documentation

Guide Description
Documentation Home Full manual — English and Thai editions
Introduction What Ruvyxa is and when to use it
Create Your First App Scaffold, run, and build a first vertical slice
Project Structure app/, public/, config, and generated output
Routing & Rendering File-system routes, layouts, SSR/SSG/ISR/CSR/PPR
Data, Actions & API Loaders, cache(), server actions, API routes
UI, Navigation & Assets Components, metadata, images, fonts
Configuration ruvyxa.config.ts and environment variables
Request pipeline Headers, redirects, rewrites, proxy, route files
Integrations @ruvyxa/auth, database, realtime
CLI Scripts, scaffolding, and the build loop
Architecture Graph, bundler, dev server, diagnostics
Development & Testing Develop, test, and package the framework
Security Boundary enforcement and env safety
Observability & Performance Logging, timing, caching, budgets
Deploy, Run & Operate Adapters and platform-specific configuration
Troubleshooting Diagnostic catalog and upgrade notes
Public API Reference Complete @ruvyxa/react + @ruvyxa/core API
Documentation Scope What the manual covers and its sources
Release Readiness Pre-release verification playbook
Platform Adapter Guide Writing a custom deployment adapter
Practical Recipes Task-oriented end-to-end examples

A full Thai edition lives beside the English one under docs/th/. Repository-level documents: ARCHITECTURE.md, CHANGELOG.md, CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md, and AGENTS.md for agent/contributor rules.


Why Ruvyxa

The conventions are the familiar ones — an app/ directory, page.tsx, layout.tsx, route.ts, server actions, and SSR, SSG, ISR, CSR, or PPR chosen per route — because the conventions are not the point. What is different is what runs them, and what that makes possible.

  • The toolchain is one prebuilt binary. The TypeScript/JSX compiler, resolver, linker, minifier, source maps, route graph, dev server, and production host are Rust, shipped as a native CLI that npm install resolves for your platform. There is no separate bundler to install or configure and no plugin chain. The benchmarks below are a consequence of that, not the reason for it.
  • Your Rust backend can serve the site. ruvyxa adds axum writes a Cargo.toml and a src/main.rs built on the ruvyxa-axum crate, which serves the static build from an axum::Router beside your own routes: one executable, no JavaScript runtime on the host, no second process, no reverse proxy joining the two, and the same security headers on your API as on the pages. It covers the static target — SSG and CSR routes; a route that renders on the server stays with ruvyxa start or a deploy adapter. See one binary for the site and the API.
  • The deploy target is not hidden. Eleven adapters emit each platform's native artifact, and the deployed build is held to the same conformance fixtures as the native host — headers, cache policy, endpoints, stored documents — so a project moves between Node, Bun, Deno, a static host, an edge worker, and a serverless function without a preferred vendor and without a behaviour that exists on one host only.
  • Two hosts, one contract. ruvyxa dev and ruvyxa start run on the Rust host; every deployed build runs on createHandler. A rule that applied on one and not the other is the shape of defect this project has fixed most often, so those rules are now fixtures both hosts replay in their own tests — not a comment saying the two agree.

If you write React and deploy to a JavaScript platform, the first and third are what you get. If you write Rust and want a React front end without a second deployment, the second is the reason this project is in your world at all. The rest of this section is the feature catalog.

Rust core

  • Ruvyxa Bundler — TypeScript/JSX/Markdown/MDX compilation, module resolution, tree-shaking, Oxc-backed minification, and source map generation in one self-contained binary.
  • Radix-trie routing — O(path-depth) route resolution regardless of the number of registered routes. Duplicate and ambiguous routes are rejected at graph validation time.
  • Persistent JavaScript worker pool — eliminates 100–500 ms per-request subprocess overhead for SSR. Shared across requests with layout nesting and route-level hydration bundles.
  • LRU render cache — SSR pages and client bundles cached in-memory (capacity 1024 dev / 512 prod, TTL 5 min dev / 30 min prod), invalidated automatically on file change. Configurable via RUVYXA_RENDER_CACHE_SIZE (0 disables it; environment values are capped at 16,384). Backed by RwLock for concurrent readers.
  • Parallel production bundling — route graphs are prepared once with bounded concurrency, then emitted once with shared-route modules in deterministic route order. Lightweight route plans and final/shared artifacts are content-validated, with each shared dependency fingerprinted once per build. Cold shared output reuses prepared modules; warm builds reuse the validated registry.
  • Bounded build reuse — Node transforms, native dependency closures, and native Markdown/MDX output reuse content-keyed results. Prerendering loads its asset index once and shares immutable CSS across the bounded worker pool.
  • Async I/O — file serving uses tokio::fs to avoid blocking the async runtime under concurrent load.
  • Persistent incremental module graph — production builds persist content-verified dependency edges and reuse unchanged client resolution work on warm builds. The graph is namespaced by the evaluated config dependency hash; React-compiler transforms bypass edge reuse so their output stays correct. Compiled output remains content-addressed under the configured build cache.
  • Typed artifact task graph — resolve, transform, analysis, chunk-plan, emit, source-map, and manifest records share explicit dependency edges and generation-scoped completion. Records are persisted atomically beside the build cache; corrupt or incompatible metadata rebuilds normally, and RUVYXA_DISABLE_ARTIFACT_CACHE=1 provides a correctness-preserving temporary bypass.
  • Coordinated cache pressure — compiler memory, resolver derivations, and artifact metadata use one soft/hard hysteresis policy (RUVYXA_BUILD_CACHE_MEMORY_MB, 256 MiB default). Worker caches apply the same policy under RUVYXA_MEMORY_LIMIT_MB (512 MiB default), skip pinned build keys, stop speculative warmups at the hard limit, and expose pressure/eviction counters.
  • Config-declared request pipeline — headers(), redirects(), rewrites(), and proxy on one config object, evaluated by the native host and every deployed build from one shared table. AST-based import/export extraction and CommonJS detection for npm dependencies.
  • Gzip + Brotli compression — all responses compressed automatically via tower-http middleware.
  • ETag / 304 support — static assets include BLAKE3-256-based ETags for efficient browser caching. Bundle names are BLAKE3-content-addressed for deterministic cache busting.

Dev server & HMR

  • Hot Module Replacement — style and component updates streamed over WebSocket without full-page reloads. CSS collection, minification, and HMR are handled natively by the dev server.
  • Debug overlay — in-browser error overlay during development with source-mapped stack traces.
  • Dev/prod parity — dev and start share routing, rendering, static asset, security-header, and compression semantics.
  • Port conflict detection — auto-scans 100 subsequent ports with process-owner identification (Windows netstat/tasklist, Unix lsof).

Rendering strategies

  • SSR-first React — pages render on the server with layout nesting, route-level client bundles for hydration, and the persistent worker pool.
  • Five rendering strategies — SSR (default), SSG, ISR, CSR, and PPR. Configurable per-route via ruvyxa.config.ts or inline exports (revalidate, ppr, getStaticParams, 'use client').
  • Partial Pre-rendering (PPR) — static shell with streamed dynamic slots via React <Suspense> boundaries and onShellReady streaming.
  • Incremental Static Regeneration (ISR) — stale-while-revalidate with configurable TTL.
  • On-demand revalidation — revalidatePath() from ruvyxa/server queues one URL for a fresh render on its next request from any API route or server action. For SSR and CSR the cached document is dropped; for SSG, ISR, and PPR the next request also bypasses the per-rendered HTML. The invalidation arrives with the write that caused it, so a client following the action cannot outrun the cache clear. Works in dev, start, and serverless.
  • getStaticParams — generate static paths at build time for dynamic SSG routes.
  • Deferred and zero-JS hydration — route exports accept hydrate = 'visible', 'idle', or false. Deferred routes do not preload their React bundle; one small shared loader imports it only when the route becomes visible or the browser is idle. false emits no client bundle at all.
  • Simple SSG parameters — export staticParams directly for known values, or return scalar values from getStaticParams when a route has one dynamic segment. Parameter discovery supports opt-in, dependency-aware persistent caching.
  • CDN-ready code splitting — route-level, shared, or vendor chunk splitting via build.split with tree-shaking applied per-split.

File-system routing

  • App directory router — app/ discovers page.tsx, page.md, page.mdx, route.ts, layout.tsx, server.ts, and action.ts automatically.
  • Dynamic segments — [param], [...catchAll], and [[...optionalCatchAll]] with full param access injected into loaders and page components.
  • Route groups — (group) directories for logical organization without affecting the URL.
  • Parallel route slots — @slot/ directories for parallel-rendered route segments.
  • API routes — named HTTP-method exports (GET, POST, PUT, DELETE, PATCH) in route.ts files, with binary-safe response streaming through bounded worker IPC.
  • Duplicate & ambiguous route rejection — the graph validator catches conflicts before they reach production.

Content & images

  • Built-in content routes — page.md and page.mdx support nested YAML frontmatter, stable heading exports, GFM tables/tasks/references/footnotes, multiline ESM, JSX member components, expressions, component overrides, and SSG. Same dev/prod pipeline as TSX routes.
  • Fast WebP image pipeline — production builds replace copied PNG/JPEG assets with cached, parallel-encoded WebP output for low CLS.

CSS pipeline

  • Dependency-driven CSS imports — application modules import .css from anywhere; no separate import manifest required.
  • CSS entries for globals — unimported global stylesheets via css.entries in config.
  • SCSS/Sass built in — import .scss and .sass files directly, including partials referenced with Sass @use, @forward, or @import.
  • CSS Modules — .module.css, .module.scss, and .module.sass imports expose deterministic, project-scoped class maps to React components while emitting the matching collected CSS.
  • CSS-in-JS compatible — React style objects and <style> elements work natively.
  • CSS caching & minification — production builds minify collected styles with cached results.
  • Tailwind CSS auto-detection — detects @import "tailwindcss" in stylesheets and invokes @tailwindcss/cli with --minify in production. LESS imports produce a clear diagnostic.

Data loading & cache

  • Co-located data fetching — server-only server.ts files beside routes with loader() and cache() utilities.
  • Real TTL caching — human-readable durations ("30s", "5m", "1h", "1d") with invalidateCache(key) or invalidateCache() (clear all) from server actions. Stale-while- revalidate keeps responses fast during background refresh. cacheStats() provides runtime observability ({ size, maxEntries }).

Server actions

  • Type-safe server actions — action.input() with validation parser and .handler() with typed input and cache invalidation callback.
  • Content type support — application/json and application/x-www-form-urlencoded.
  • Module isolation — actions run in isolated contexts with bounded resource usage.

React primitives

  • Error boundary — <RuvyxaErrorBoundary> with typed fallback({ error, resetError }) for per-route error isolation. resetError() clears state for retry without full-page reload.
  • Hydration — hydrate() attaches React to server-rendered DOM with automatic error reporting and fallback rendering when hydration fails.
  • Image components — <Image> (responsive, fill, priority, loader, unoptimized, fetchPriority) and <Picture> for art-direction with multi-source support.
  • SEO, GEO, and AEO primitives — <Seo> with typed canonical, robots, Open Graph, Twitter Card, Article/Breadcrumb JSON-LD, plus a visible <Answer> primitive with citations and Question/Answer microdata. Production builds also emit standards-compliant, automatically sharded sitemap.xml and configurable robots.txt, while public files or exact routes can take ownership.
  • Client loader hook — useRuvyxaLoader loads client-side data and returns { data, loading, error, refetch } with built-in race-condition handling and mount-safety checks. See the client loader guide.
  • Typed routes — set typedRoutes: true and dev/build/check generate .ruvyxa/types/routes.d.ts; <Link href> and the imperative router are then narrowed to the routes the project actually has, with route(url) for URLs computed at runtime. Opt-in — until the file exists, the type collapses back to plain string.
  • <Script> — third-party scripts with beforeInteractive, afterInteractive, and lazyOnload strategies; an external URL is fetched once per page.
  • Instrumentation — an optional instrumentation.ts whose register() runs once per server process, for installing OpenTelemetry, error reporters, or metrics exporters.

Security

  • Server/client boundary enforcement — server-only, client-only, and server/ imports are validated at build time. Private environment variables never leak to client bundles; only RUVYXA_PUBLIC_-prefixed variables are accessible on the client.
  • Server action guards — same-origin checks, Fetch Metadata guards, 1 MB body limit (security.actionLimit), 10 MB API body limit (security.apiLimit), and per-client/action rate limiting (600 req/min default via security.actionRateLimit).
  • Security headers — native, standalone, and serverless responses receive the same seven safe defaults while explicit application headers win. Static/Cloudflare _headers output carries the same policy; CSP and HSTS remain explicit because safe values are application-specific.
  • Config safety — unknown configuration keys fail intentionally; typos never silently change deployment behavior.

Middleware & request pipeline

  • Tower-based middleware — composable CORS, timing, logging, rate limiting, and custom headers via ruvyxa.config.ts. Route-scoped middleware targets specific path patterns.
  • Route rules — headers(), redirects(), and rewrites() with path-to-regexp sources and has/missing conditions, applied in a fixed order ahead of routing on both hosts.
  • proxy — one handler for the paths proxy.matcher names: answer with a Response, or continue with a rewritten Request. Runs in the project worker under ruvyxa dev/start and in-process in every deployed build.
  • File conventions — instrumentation.ts for startup work, route.ts for any generated file (security.txt, feeds, OpenAPI), content: true for the content engine's artifacts. See the Request pipeline.
  • Official state packages — @ruvyxa/database provides a typed adapter facade, @ruvyxa/auth provides secure provider-driven sessions, and @ruvyxa/realtime connects opted-in server actions to the native self-hosted WebSocket transport with explicit deployment guards.

CLI & diagnostics

  • 13 verified commands — dev, build, check, start, preview, routes, analyze, adds, doctor, clean, trace, bench, and test:parity.
  • build — production output supports --target node, bun, edge, or static, plus --adapter <name> to run a deploy adapter without editing config and --server-only for API-only artifacts. Pre-renders SSG, ISR, PPR, and CSR pages at build time via parallel worker pool (MAX_PRERENDER_PARALLELISM: 2).
  • check — type checking, production build, dev/prod route parity, and page smoke rendering in one command.
  • analyze — human, JSON, SARIF 2.1.0, or a self-contained interactive HTML validation report for routes, imports, and server/client boundaries (--format sarif --output reports/ruvyxa.sarif; --html).
  • adds — scaffold an additive, framework-native form, data-table, or auth flow, or an axum host: a Cargo.toml and src/main.rs that serve the static build from a Rust binary.
  • doctor — project health plus deploy-target inspection: adapter runtime, platform, capabilities, and every route unsupported by the selected adapter (--json for CI).
  • bench — benchmark route discovery, analysis, validation, and production builds.
  • test:parity — compare dev/prod routes and smoke-render page routes.
  • First-Class Diagnostics — Over 60 RUV#### error codes spanning Boundary Violations (RUV1000s), Server/Render Errors (RUV1100s), Build/Compilation (RUV1300s), and Official Packages (RUV3000s). Every diagnostic carries its own explanation and suggested fix at the point it is raised; see Troubleshooting for the common symptoms and their evidence-backed fixes.

Deploy Anywhere (11 Adapters)

  • Four starters — npm create ruvyxa@latest defaults to the focused minimal app, with blog, crud, and api available through --template. Run it with no arguments on a real terminal and it prompts for a project name and offers an arrow-key template menu (j/k also work); the scaffold summary prints the actual generated file tree, colored by role.
  • 11 Deployment Adapters — Zero-config native output for Vercel, Netlify, Cloudflare, Node.js, Bun, Deno, Static, AWS Amplify, Firebase, Railway, and Render. See the Deployment Guide for platform-specific configurations.
  • Or your own Rust binary — the static target's publish directory is served by the ruvyxa-axum crate from an axum::Router, with the _headers policy, the project's 404.html, and the shared content-type table, so a Rust service deploys the site the way it deploys itself.

Requirements

Version floors are declared once, in the manifests, and this table points at them rather than restating numbers that go stale:

Use case Requirement Declared in
Building an app Node.js (Bun can run SSR instead). No Rust toolchain needed — the CLI ships prebuilt. engines.node in each published package.json
Supported CLI platforms win32-x64, win32-arm64, linux-x64, linux-arm64, darwin-arm64 optionalDependencies of ruvyxa
Building the framework Node.js, pnpm, and a Rust toolchain (edition 2024) .nvmrc, packageManager, rust-version
Serving from your own Rust binary Node.js to run ruvyxa build; a Rust toolchain for your application; nothing on the host at runtime rust-version in the workspace Cargo.toml

ruvyxa doctor prints the versions it actually resolved on your machine and says which ones are too old, which is the answer that matters more than any number written here.

The ruvyxa npm package resolves one @ruvyxa/cli-<platform> optional dependency that carries the prebuilt native binary, so npm install is all that is required to get a working ruvyxa command.


Quick Start

npm create ruvyxa@latest my-app
cd my-app
npm install
npm run dev

Choose a focused starter when you want more than the minimal route:

npm create ruvyxa@latest my-blog -- --template blog
npm create ruvyxa@latest my-admin -- --template crud
npm create ruvyxa@latest my-api -- --template api

Open http://localhost:3000.

pnpm, yarn, bun, and Deno tasks work too. When no runtime is configured, Ruvyxa prefers Node, then falls back to Bun and Deno. Set runtime: 'bun' or runtime: 'deno', or pass --runtime bun|deno, when that runtime should execute config, SSR, API routes, actions, adapters, and the project worker. Deno local tooling runs with the permissions required by trusted project config (-A --no-prompt). A scaffold can be run with deno install && deno task dev. The generated app keeps the first screen focused:

my-app/
├── app/
│   ├── globals.css
│   ├── layout.tsx
│   └── page.tsx
├── public/
│   └── ruvyxa.png
├── package.json
├── ruvyxa.config.ts
└── tsconfig.json

In practice you rarely type ruvyxa directly — every starter wires the CLI into package.json scripts, so the day-to-day loop is plain npm/pnpm/yarn/bun scripts:

Script Runs When you use it
npm run dev ruvyxa dev Local development with HMR
npm run build ruvyxa build Production build into .ruvyxa/
npm start ruvyxa start Serve the production build
npm run preview ruvyxa preview Check the production build locally
npm run check ruvyxa check Pre-deploy gate: typecheck + build + parity
npm run routes ruvyxa routes See what the router discovered
npm run analyze ruvyxa analyze Boundary/import validation, CI-friendly
npm run doctor ruvyxa doctor Environment and deploy-target health
npm run adds -- form ruvyxa adds form Scaffold a framework-native flow
npm run trace -- / ruvyxa trace / Inspect one route manifest entry
npm run clean ruvyxa clean Remove .ruvyxa/

Arguments after npm run <script> need the -- separator (npm run trace -- /blog/[slug]); pnpm and bun pass them through directly. ruvyxa has no --version flag — run ruvyxa doctor, which prints the resolved version alongside the rest of the project report.

With a Rust backend, add the axum host to the same project and build for the static target:

npm run adds -- axum                 # Cargo.toml and src/main.rs beside package.json
npm install @ruvyxa/adapter-static   # then `adapter: staticOutput()` in ruvyxa.config.ts
npm run build                        # writes .ruvyxa/static
cargo run                            # serves it, and /api/health, on port 3000

The result is one binary with nothing else installed on the host. What it can and cannot serve is in one binary for the site and the API.

First time using Ruvyxa? Check out the Tutorial: Build a Mini Blog in the Getting Started guide for a step-by-step introduction to Routing, Server Components, and Dynamic Routes.

For a fuller integration app with dynamic routes, API routes, server actions, and all rendering strategies, see examples/demo.


Benchmarks

Measured on 2026-08-21 with the repository harness against minimal starters. These figures are valid only for the exact versions, machine, and run conditions shown below; re-run the harness when comparing newer releases.

Metric (lower is better) Ruvyxa 1.1.0 Next.js 16.3.0 Astro 7.2.4
Production build (cold-cache median) 1.131 s 7.667 s 2.309 s
Dev server → first rendered response 1.115 s 3.310 s 5.855 s
Prod server start → first response 0.845 s 1.074 s 2.813 s
Client JS shipped (minimal page) 235 KB ¹ 568 KB 0 KB ²
Throughput (higher is better) Ruvyxa 1.1.0 Next.js 16.3.0 Astro 7.2.4
Requests/second (/, prod server) ³ 44,528 3,647 3,611
Latency p50 / p99 0 / 1 ms 6 / 12 ms 6 / 10 ms

In this run, Ruvyxa's median cold build completed 6.8× / 2.0× faster than Next.js / Astro respectively, dev-server readiness was 3.0× / 5.3× faster, production-server readiness 1.3× / 3.3× faster, and the measured production throughput was 12.2× / 12.3× higher. These are local results for this minimal-starter workload, not a universal performance ranking.

First-run warm-up is real and large enough to swamp a careless reading: Next.js's first cold build measured 19.8 s against a 7.6 s median for its other two runs, and Astro's first spiked to 20.2 s against a ~2.2 s median. The reported medians are unaffected because a median discards one outlier; Ruvyxa's three cold builds were 1.175 s / 1.128 s / 1.131 s, which is the spread worth noting on its own. Re-run the harness on an otherwise idle machine before reading anything into small differences, and treat gaps under ~100 ms as noise.

¹ For the interactive React starter page. A content page can opt out entirely with export const hydrate = false — its HTML then ships 0 KB of JavaScript and no client bundle is emitted. ² Astro's minimal starter is a zero-JS static page by design (no React hydration), so it has no client bundle and its preview server serves static files only. ³ The harness runs autocannon once for 10 seconds with 25 connections against the final production server of each framework; throughput and latency are not medians.

Methodology — measured on Windows 11 Home, AMD Ryzen 7 8845HS, 31 GB RAM. Each framework used a freshly scaffolded minimal starter in an isolated benchmark root with RUNS=3 and the same scripts/bench-frameworks.mjs harness. The harness reports timings only, not framework versions; capture those from each starter's own lockfile at the moment you run it, because that is the only record that will still be accurate later. Build = build script wall time. Dev/prod readiness = time from process spawn to first HTTP 200 on /. Cold-cache runs remove .ruvyxa/.next/dist/.astro/Vite caches before each run; dependencies remain installed during the harness. The Ruvyxa column was packed from this repository with pnpm pack:smoke and installed from local tarballs together with the matching Windows native CLI package, rather than installed from the npm registry; the Ruvyxa and Next starters ran the same React. The machine was not otherwise idle during this run, so the absolute readiness and build timings run slightly high; the three frameworks were measured back to back under the same conditions, so the comparison between them holds. To reproduce, prepare the three isolated starters, use local tarball overrides for an unpublished Ruvyxa release candidate, then run BENCH_ROOT=<benchmark-root> RUNS=3 node scripts/bench-frameworks.mjs.


From Source

./setup.sh
cargo run -p ruvyxa_cli -- dev --root examples/demo

On Windows, use setup.bat instead of ./setup.sh. The setup script installs locked dependencies, builds workspace packages, and compiles the Ruvyxa CLI.

Build and test all packages:

cargo test --workspace
pnpm -r build
pnpm -r check
pnpm -r test

Standalone JavaScript and TypeScript tests live under tests/ and are routed by each package's test script. See Development & Testing for the verification layout.

The full pre-submit gate used in this repository:

cargo fmt --all -- --check
cargo clippy --workspace --locked -- -D warnings
cargo test --workspace --locked
pnpm -r build && pnpm -r check && pnpm -r test
pnpm format:check
pnpm release:validate
pnpm pack:smoke
cargo run -p ruvyxa_cli -- check --root examples/demo

Repository layout

ruvyxa/
├── crates/            # Rust workspace (8 crates)
│   ├── ruvyxa_cli/        # commands, config loading, build orchestration
│   ├── ruvyxa_bundler/    # TS/JSX compile, resolve, link, minify, source maps
│   ├── ruvyxa_dev_server/ # axum server, HMR, worker pool, router, caches
│   ├── ruvyxa_graph/      # route discovery, validation, render strategies
│   ├── ruvyxa_middleware/ # Tower layers, route rules, worker host
│   ├── ruvyxa_diagnostics/# RUV#### structured errors
│   ├── ruvyxa_tui/        # terminal layout, progress, mascot, and theme primitives
│   └── ruvyxa_axum/       # `ruvyxa-axum` on crates.io: serve the static build from axum
├── packages/          # npm packages (see the table below)
├── templates/         # create-ruvyxa starters
├── examples/demo/     # 23-route integration fixture
├── tests/             # Node tests, organized by package
├── docs/{en,th}/      # user-facing manual, both editions
└── scripts/           # release validation, packing, benchmarks

App Directory

Routes are discovered from app/. Every page.tsx must export a default component; every route.ts exports named HTTP-method handlers.

The folder name is the complete route contract: use [slug] for one segment, [...path] for a required string[], and [[...path]] for an optional string[]. Route groups ((...)) organize files without adding a segment. There is no separate :param or *param route syntax to configure. Directories starting with _ or @ are ignored.

export default function Home() {
  return <main>Hello Ruvyxa</main>
}

Data Loading

Co-locate server-only data fetching beside routes via server.ts:

import { loader, cache } from 'ruvyxa/server'

export const getPost = loader(async ({ params, cache, request }) => {
  return cache(`post:${params.slug}`)
    .ttl('5m')
    .get(async () => {
      return db.posts.findBySlug(params.slug)
    })
})

The cache() utility provides real in-memory TTL caching with human-readable durations ("30s", "5m", "1h", "1d"). Call invalidateCache(key) or invalidateCache() (clear all) from server actions.


Server Actions

Co-locate validated mutations beside routes via action.ts:

import { action } from 'ruvyxa/server'

export const createTodo = action
  .input({
    parse(value: unknown) {
      if (!value || typeof value !== 'object' || !('title' in value))
        throw new Error('Title is required')
      return { title: String(value.title).trim() }
    },
  })
  .handler(async ({ input, invalidate }) => {
    invalidate('todos')
    return { title: input.title, completed: false }
  })

Supported content types: application/json, application/x-www-form-urlencoded.

Security defaults: body size limit (1 MB), API body limit (10 MB), same-origin check, Fetch Metadata guards, per-client/action rate limiting (600 req/min), module isolation.


Middleware

Ruvyxa ships a tower-based middleware system configurable via ruvyxa.config.ts, and a proxy that runs ahead of the routes its matcher names:

import { config } from 'ruvyxa/config'

export default config({
  middleware: { builtin: { timing: true, log: true } },
  proxy: {
    matcher: '/api/:path*',
    handler(request) {
      return request.headers.has('authorization')
        ? undefined
        : new Response('Unauthorized', { status: 401 })
    },
  },
})

Built-in middleware stays native Tower code. proxy.matcher is compiled natively on both hosts; proxy.handler runs in the persistent project worker under ruvyxa dev/start, bounded by middleware.timeoutMs, and in-process in every deployed build.


Configuration

CSS imports are dependency-driven, so application modules may import .css from anywhere inside the project. Use css.entries below for global files or directories that are not imported; React style objects and <style> elements continue to work for runtime CSS-in-JS.

import { config } from 'ruvyxa/config'

export default config({
  appDir: 'app',
  outDir: '.ruvyxa',
  css: {
    entries: ['styles/theme.css'],
  },
  server: {
    host: 'localhost',
    port: 3000,
  },
  build: {
    minify: true,
    map: false,
    treeShake: true,
    split: 'route',
    jsx: 'automatic',
    target: 'es2022',
    workers: 4,
    manifest: false,
    warm: true,
  },
  render: {
    strategy: 'ssr',
    revalidate: 60,
  },
  cache: {
    routes: true,
    css: true,
    dir: '.ruvyxa/cache/bundler',
  },
  debug: {
    overlay: true,
    traces: true,
  },
  image: {
    optimize: true,
    quality: 82,
    lossless: false,
    variantWidths: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
    workers: 0,
  },
  security: {
    actionLimit: 1024 * 1024,
    apiLimit: 10 * 1024 * 1024,
    actionRateLimit: { max: 600, window: 60 },
    sameOrigin: true,
    fetchMeta: true,
    trustedProxyIps: [],
    headers: true,
  },
  middleware: {
    builtin: {
      timing: true,
      log: true,
      cors: {
        origins: ['http://localhost:5173'],
        methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
        credentials: true,
      },
    },
  },
})

Rendering Strategies

Strategy Export Behavior
SSR default Rendered per request (default)
SSG staticParams / config Pre-rendered at build time, served as HTML
ISR export const revalidate Stale-while-revalidate with configurable TTL
CSR 'use client' directive Minimal shell, full render in browser
PPR export const ppr = true Static shell + streamed dynamic slots

Dynamic routes can export a staticParams array for known values or use getStaticParams(context) for asynchronous discovery. getStaticParams may return { params, cache: '10m' } to persist the result until its TTL expires; changes to the route or imported dependencies invalidate it early. See the rendering guide for scalar shorthand, context, and cache examples.


CLI

Fourteen commands. Project commands accept --root <dir> (default .) and --runtime node|bun|deno.

Command Purpose Additional flags
ruvyxa dev Development server with HMR and file watching --host, --port
ruvyxa build Build production output to .ruvyxa/ --target node|bun|deno|edge|static, --adapter, --server-only
ruvyxa check Production readiness gate: typecheck, build, dev/prod parity, page smoke render —
ruvyxa start Serve production output with the same runtime semantics as dev --host, --port
ruvyxa preview Preview an existing production build locally (same server as start) --host, --port
ruvyxa routes Print the discovered route table --json
ruvyxa analyze Validate routes, imports, and server/client boundaries --format auto|human|json|sarif|html, --output, --html
ruvyxa adds <flow…> Scaffold a form, data-table, or auth flow --force
ruvyxa doctor Project health plus deploy-target inspection --target, --adapter, --json
ruvyxa trace <route> Inspect one route manifest entry —
ruvyxa bench Benchmark route discovery, analysis, validation, and production builds --runtime, --samples <n> (default 3), --json, --baseline
ruvyxa test:parity Compare dev/prod routes and smoke-render page routes —
ruvyxa clean Remove .ruvyxa/ build output —

trace takes a route manifest path, not a request URL: use ruvyxa trace "/blog/[slug]", not ruvyxa trace /blog/hello. It prints the route id, file, layout chain, server/client modules, runtime, and render strategy as JSON. On Windows, quote paths containing [ ], and note that Git Bash rewrites a leading / into a Windows path — use PowerShell, cmd, or MSYS_NO_PATHCONV=1.

Command and flag spellings are normalized before parsing, so --root=x, --server_only, test-parity, and an em-dashed —root all resolve to their canonical form. Run ruvyxa help <command> for the built-in reference. There is no --version flag; ruvyxa doctor reports the resolved version.

Use ruvyxa bench --baseline --json for the production baseline. Each sample copies project inputs into an ignored temporary workspace and measures cold/warm builds, first-route rendering, and CSS/client/server/leaf edit classes. Results use the stable ruvyxa.build-bench contract with a separate schemaVersion: 1, plus peak resident memory and HMR reload-fallback counts. The command refuses to publish timings unless cold and warm builds have the same semantic artifact set; cache/timing telemetry is excluded from that comparison, while deployed code, assets, and manifests remain part of it. The ordinary bench --json array is unchanged for existing consumers.

Verified end to end

Every command in the table above was run on 2026-08-24 against examples/demo — 30 routes: 27 pages and 3 API routes — with a --release binary built from this working tree. Scaffolding commands ran against a throwaway copy of templates/minimal so the fixture stayed clean. Timings are one machine's, not a specification; re-run them rather than quoting them.

Command Observed result
routes 30 routes discovered (27 pages, 3 API), strategy resolved per route
doctor 0 diagnostics, adapter ruvyxa-native, every route supported by the target, 1.17s
analyze 0 diagnostics; 38 client modules and 4 server modules reported
trace route id, file, layout chain, runtime, and render strategy as JSON
build cold build of all 30 routes into .ruvyxa/ in 8.15s, 247 kB shared by every page
start ready in 425ms; GET /, GET /api/health, and GET /blog/hello-world all → 200
preview ready and serving the same production build → 200
dev ready in 482ms with HMR enabled; GET / → 200
check typecheck + build + parity + smoke render over 30 routes, 16.01s, exit 0
test:parity parity passed for 30 routes in 14.40s, exit 0
bench 6 scenarios; cold build 8.27s against warm 657ms — 92% of it saved by the cache
adds form wrote page.tsx + action.ts; data-table wrote the generic client component
clean removed .ruvyxa/ in 141ms

Architecture

┌───────────────────────────────────────────────────────────────┐
│                      ruvyxa (npm package)                     │
│   CLI launcher → Ruvyxa CLI Rust binary (ruvyxa_cli)          │
│   Runtime: worker-pool.mjs (persistent Node/Bun/Deno IPC)     │
└─────────────────────┬─────────────────────────────────────────┘
                      │
┌─────────────────────┴─────────────────────────────────────────┐
│                    Rust Workspace (crates/)                   │
├─────────────────────┬─────────────────────────────────────────┤
│ ruvyxa_bundler      │ compiler, minifier, linker, resolver,   │
│                     │ source maps, boundary checks            │
│ ruvyxa_cli          │ CLI commands, config loading, build     │
│                     │ orchestration, production output        │
│ ruvyxa_dev_server   │ axum server, websocket HMR, worker      │
│                     │ pool, radix router, render cache        │
│ ruvyxa_middleware   │ Tower layers, route rules, worker host  │
│ ruvyxa_graph        │ route discovery, import graph, render   │
│                     │ strategy detection, validation          │
│ ruvyxa_diagnostics  │ structured errors with RUV#### codes    │
│ ruvyxa_axum         │ ruvyxa-axum on crates.io: the static    │
│                     │ build served from your own axum app     │
└─────────────────────┴─────────────────────────────────────────┘

Performance features:

  • Persistent JavaScript worker pool (eliminates 100-500ms/request subprocess overhead)
  • Radix-trie route matching (O(depth) instead of O(n))
  • LRU render cache with TTL (sub-ms repeated page loads)
  • Bounded, binary-safe API response streaming across worker IPC
  • Async file I/O via tokio::fs (no thread starvation)
  • SSR via renderToString with layout nesting
  • Gzip + Brotli compression (tower-http)
  • ETag / 304 Not Modified (BLAKE3-256 hashing)
  • RwLock-based runtime cache (concurrent readers)
  • Route-level client bundle splitting with tree-shaking

Build Output

.ruvyxa/
├── server/        # Production route source (copied from app/, components/, server/)
├── client/        # BLAKE3-hashed client bundles + manifest.json
├── assets/        # Public assets + converted WebP images and image manifest
├── prerender/     # Pre-rendered SSG/ISR/PPR/CSR HTML files + manifest.json
├── manifest.json  # Route manifest with paths, layouts, module references
└── build.json     # Build metadata, security defaults, build settings, render summary

Hybrid adapters add platform deployment directories containing a compiled .mjs static route registry. Serverless and edge handlers execute that bundle directly; raw TS/TSX source is not used as a deployment entrypoint.


Packages

Package Description
ruvyxa CLI, runtime bridge, and public framework entrypoints
create-ruvyxa Minimal app scaffolder (4 starters)
@ruvyxa/core Typed config, server APIs, cache helpers, responses, and adapter contracts
@ruvyxa/react React components, hooks, SEO, error boundary, hydration
@ruvyxa/auth Provider-driven authentication (credentials, GitHub, Google, magic link)
@ruvyxa/database Typed database adapter facade (Prisma, DynamoDB)
@ruvyxa/realtime WebSocket transport for opted-in server actions
@ruvyxa/testing Dependency-free loader, action, and cache test doubles
@ruvyxa/adapter-node Node deployment adapter
@ruvyxa/adapter-vercel Vercel serverless adapter
@ruvyxa/adapter-cloudflare Cloudflare edge adapter
@ruvyxa/adapter-netlify Netlify functions adapter
@ruvyxa/adapter-bun Bun runtime adapter
@ruvyxa/adapter-deno Deno self-hosted runtime adapter
@ruvyxa/adapter-static Static output adapter
@ruvyxa/adapter-railway Railway standalone service adapter
@ruvyxa/adapter-render Render Web Service and Blueprint adapter
@ruvyxa/adapter-firebase Firebase Hosting and Cloud Functions v2 adapter
@ruvyxa/adapter-aws AWS Amplify Hosting static and compute adapter

ruvyxa also resolves one of five @ruvyxa/cli-<platform> packages as an optional dependency. They carry the prebuilt native CLI for a single target and are not imported directly.

One crate is published outside npm: ruvyxa-axum on crates.io, which serves a static build from an axum::Router and is what ruvyxa adds axum writes into a project's Cargo.toml. It depends on nothing else in the workspace.


Contributing

See CONTRIBUTING.md for local setup, verification commands, and release rules.


License

Apache 2.0 Copyright (c) 2026 Thirawat Sinlapasomsak

About

Ruvyxa is a production-minded web framework built around clarity, speed, and control. It keeps routing, server logic, validation, builds, and runtime output in one predictable workflow.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

20 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages