Khung fullstack tối giản — một runtime TypeScript (chạy trên node:http zero-dep) — dựa
trên triết lý RCA — Resolved Cell Architecture: logic chỉ phụ thuộc HỢP ĐỒNG; mọi quyết định
vận hành (render, backend data) là kết quả được GIẢI bởi engine, không viết tay.
Đổi backend data
memory ↔ sqlite ↔ postgreschỉ một dòng trongapp/backend/data.ts, đổi renderstatic ↔ islandqua profile — cell & frontend không đổi một dòng.
npm i @nmvuong92/fluxe react react-dom zodimport { defineCell, withInput, makeServer } from "@nmvuong92/fluxe";
import { useQuery, useMutation, Link, Nav, ThemeToggle } from "@nmvuong92/fluxe/react";
import { rpc } from "@nmvuong92/fluxe/client";| Import | Nội dung |
|---|---|
@nmvuong92/fluxe |
engine: defineCell, makeServer, resolver, auth, validate, seo, broker, ratelimit, contract builder f… (KHÔNG có driver data — backend là của bạn ở app/backend/data.ts) |
@nmvuong92/fluxe/react |
useQuery, useMutation, Link, Nav, ThemeToggle, useTheme, DebugBar |
@nmvuong92/fluxe/client |
rpc, RpcError, mutate, revalidate, subscribe |
@nmvuong92/fluxe/jobs |
queue/dead-letter (cần --experimental-sqlite) |
npm install
npm run fx -- build # resolve + prerender + bundle client
npm run fx -- dev # http://localhost:5180
npm run test:all # typecheck + 144 unit + integration (selftest2) — TẤT CẢ XANHfx: resolve · prerender · build · dev · test · jobs.
app/ ← DEV sở hữu (sửa thoải mái) — Contract Plane
cells/ trang/feature (route + loader + view + action/head/layout/guard)
layouts/ layout dùng chung (nested)
backend/ BACKEND của bạn (Express/Fastify + fluxe)
server.ts entry: mount fluxe (catch-all) + route riêng của bạn
data.ts TẦNG DATA: interface domain + chọn driver (memory/sqlite/postgres); export backend
profiles.ts profile resolve render mode (static/island) per môi trường
contract.ts contract builder `f` → types suy ra qua Infer<>/Resolvers<>
env.ts env có kiểu, validate fail-fast lúc boot
app.ts registry cell — sinh tự động (fx sync), đừng sửa
src/ ← ENGINE (không đụng) — Resolution Plane
core/ resolver · router · errors · auth · validate · contract · layouts ·
broker · presence · jobs · ratelimit · observe · panel · seo · cli · ...
server_factory.ts runtime ráp cell + giải manifest
Quy tắc: backend là tầng data của bạn ở app/backend/data.ts (interface domain + chọn driver),
inject qua makeServer(…, { backend }). Engine không bao giờ import ngược vào app/.
- Server — chạy zero-config (
makeServer, node:http) HOẶC nhúng vào Express/Fastify qua adapter (@nmvuong92/fluxe/express|fastify) - Render — static (0 JS) · island hydrate · SPA nav (Inertia) · static-prerender · API mode
?json=1 - Routing — động
[param]→ctx.input· nested layouts · SEO (head/canonical/OG/JSON-LD per cell,/sitemap.xml,/robots.txt) - Bảo mật (đầy đủ) — input validation (Zod) · auth password scrypt · RBAC · CSRF double-submit · rate-limit token-bucket · error handling không-leak + structured
- Data — backend user-owned ở
app/backend/data.ts(bạn tự định nghĩa interface + implement bằngnode:sqlite/pg/ORM trực tiếp), inject quamakeServer(…, { backend })· engine 0 driver · contract builderf→ types suy ra qua inference - Mutations DX —
RpcErrorcó cấu trúc ·mutate()optimistic + rollback · lỗi validation field-level - Realtime (Trục 4g) — SSE channel + pub/sub broker · live-update on action · presence (multi-tab)
- Async — job queue bền (SQLite, retry → dead-letter)
- Observability — request log + dashboard
/_fluxe(RCA Resolution + Recent requests) - Config — env có kiểu, fail-fast lúc boot
- DX —
fxCLI · mockBackendtest cực dễ · typecheck gate
Backend là tầng data của bạn — định nghĩa interface domain ở app/backend/data.ts + chọn driver
TS in-process dưới đây, inject qua makeServer(…, { backend }). Cell chỉ thấy interface:
| Driver | Bạn tự implement bằng |
|---|---|
memory |
object in-RAM — mặc định dev |
sqlite |
node:sqlite built-in (DatabaseSync), 0 dep, persist ra file (cần --experimental-sqlite) |
postgres |
npm i pg, dùng Pool/Client trực tiếp (DATABASE_URL) |
// app/backend/data.ts — engine không biết gì
import { DatabaseSync } from "node:sqlite";
export interface Todo { id: string; title: string; done: boolean }
export interface Backend {
name: string;
listTodos(): Promise<Todo[]>;
addTodo(title: string): Promise<Todo>;
toggleTodo(id: string): Promise<Todo[]>;
}
export function memoryBackend(): Backend {
let items: Todo[] = [];
let seq = 0;
return {
name: "memory",
async listTodos() { return items; },
async addTodo(title) { const t = { id: String(++seq), title, done: false }; items.push(t); return t; },
async toggleTodo(id) { items = items.map((t) => t.id === id ? { ...t, done: !t.done } : t); return items; },
};
}
export function sqliteBackend(path = ":memory:"): Backend {
const db = new DatabaseSync(path);
db.exec(`CREATE TABLE IF NOT EXISTS todos (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, done INTEGER DEFAULT 0)`);
// … CRUD bằng db.prepare(...)
return { name: "sqlite" } as any;
}
export const backend: Backend = process.env.FLUXE_SQLITE_PATH
? sqliteBackend(process.env.FLUXE_SQLITE_PATH) // đổi 1 dòng = đổi nơi lưu
: memoryBackend();Nguồn khác (REST/ORM/Redis)? Tự implement Backend là xong — engine không quan tâm bên dưới.
Toàn bộ định hướng, các trục chiến lược, tenets và nguyên lý RCA: xem idea.md.
Spec + plan: docs/superpowers/.
Apache-2.0 © 2026 nmvuong92. See LICENSE and NOTICE. Copyright is held solely by the author, who reserves the right to offer the software under separate commercial terms.