kilatjs 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,425 +14,646 @@
14
14
 
15
15
  KilatJS is a server-first web framework inspired by the simplicity of PHP and the power of modern component libraries. It rejects the complexity of SPAs, hydration, and client-side routing in favor of **real HTML rendered on the server**.
16
16
 
17
- **🔥 Powered by Bun's Native APIs - No Abstractions, Pure Performance:**
18
- - 🚀 **Bun.FileSystemRouter** - Zero-config file-based routing at native speed
19
- - ⚡ **Bun.serve()** - HTTP/2 server with built-in WebSocket support
20
- - 🔧 **Bun.file()** - Streaming file operations with automatic MIME detection
21
- - 📦 **Bun.build()** - Native TypeScript/JSX bundling (no Webpack/Vite needed)
22
- - 🔄 **Bun.spawn()** - Process management for build tools and external commands
23
- - 💾 **Bun.write()** - Optimized file writing with atomic operations
24
- - 🔍 **Bun.glob()** - Pattern matching for static site generation
25
-
26
17
  ### One-Line Positioning
27
18
 
28
19
  > **"Request → Load → Render HTML → Response. That's it."**
29
20
 
30
21
  ---
31
22
 
32
- ## 🔥 Why Bun Native?
23
+ ## *Quick Start
33
24
 
34
- KilatJS leverages Bun's runtime directly instead of abstracting it away:
25
+ ### Installation
35
26
 
36
- ```typescript
37
- // Direct Bun.FileSystemRouter usage
38
- const router = new Bun.FileSystemRouter({
39
- style: "nextjs",
40
- dir: "./routes"
41
- });
27
+ ```bash
28
+ # Create a new project
29
+ mkdir my-app && cd my-app
30
+ bun init
42
31
 
43
- // Native Bun.serve() with streaming
44
- export default {
45
- port: 3000,
46
- fetch: async (request) => {
47
- // Zero-copy request handling
48
- return new Response(Bun.file("./static/index.html"));
49
- }
50
- };
32
+ # Install KilatJS
33
+ bun add kilatjs react react-dom
51
34
 
52
- // Built-in TypeScript compilation
53
- const result = await Bun.build({
54
- entrypoints: ["./src/index.tsx"],
55
- outdir: "./dist",
56
- target: "bun"
57
- });
35
+ # Install dev dependencies
36
+ bun add -d @types/react @types/react-dom typescript tailwindcss
58
37
  ```
59
38
 
60
- **Performance Benefits:**
61
- - ⚡ **3x faster** cold starts vs Node.js frameworks
62
- - 🚀 **Native TypeScript** - no transpilation overhead
63
- - 💾 **Zero-copy streaming** for static files
64
- - 🔧 **Built-in bundling** eliminates build tool complexity
39
+ ### Create Your First App
65
40
 
66
- ---
41
+ **1. Create config file `kilat.config.ts`:**
67
42
 
68
- ## ✅ Core Principles
43
+ ```ts
44
+ import { defineConfig } from "kilatjs";
69
45
 
70
- 1. **HTML is the protocol** — Every response is complete, semantic HTML
71
- 2. **Server owns all truth** — No client-side state management nightmares
72
- 3. **Every meaningful change = HTTP request** — Forms, mutations, navigation
73
- 4. **UI frameworks render, never orchestrate** — React/Solid/Vue for templating only
74
- 5. **JavaScript is optional, not required** — Sites work without JS
46
+ export default defineConfig({
47
+ appDir: "./src",
48
+ outDir: "./dist",
49
+ port: 3000,
50
+ tailwind: {
51
+ enabled: true,
52
+ inputPath: "./input.css",
53
+ cssPath: "./styles.css",
54
+ },
55
+ });
56
+ ```
75
57
 
76
- ---
58
+ **2. Create input CSS `input.css`:**
77
59
 
78
- ## ❌ Strict Non-Goals
60
+ ```css
61
+ @import "tailwindcss";
62
+ ```
79
63
 
80
- KilatJS intentionally avoids:
64
+ **3. Create your first route `src/routes/index.tsx`:**
81
65
 
82
- - ❌ Client-Side Rendering (CSR)
83
- - Hydration / Islands architecture
84
- - Client-side routing
85
- - Framework hooks (`useLoaderData`, `useRoute`, etc.)
86
- - ❌ Middleware chains
87
- - ❌ Dependency injection
88
- - ❌ SPA build assumptions
89
- - ❌ Vite as a hard dependency
66
+ ```tsx
67
+ export const meta = {
68
+ title: "Welcome to KilatJS",
69
+ description: "A Bun-native, HTML-first framework",
70
+ };
90
71
 
91
- ---
72
+ export async function load() {
73
+ return {
74
+ message: "Hello from the server!",
75
+ time: new Date().toLocaleTimeString(),
76
+ };
77
+ }
92
78
 
93
- ## 🚀 Quick Start
79
+ export default function HomePage({ data }) {
80
+ return (
81
+ <div className="min-h-screen bg-gray-100 flex items-center justify-center">
82
+ <div className="bg-white p-8 rounded-lg shadow-lg">
83
+ <h1 className="text-3xl font-bold text-gray-900">{data.message}</h1>
84
+ <p className="text-gray-600 mt-2">Server time: {data.time}</p>
85
+ </div>
86
+ </div>
87
+ );
88
+ }
89
+ ```
94
90
 
95
- ### 1. Install dependencies
91
+ **4. Run the dev server:**
96
92
 
97
93
  ```bash
98
- bun install
94
+ bunx kilat dev
99
95
  ```
100
96
 
101
- ### 2. Run the example
97
+ Visit [http://localhost:3000](http://localhost:3000) 🎉
98
+
99
+ ---
100
+
101
+ ## 📖 CLI Commands
102
+
103
+ | Command | Description |
104
+ |---------|-------------|
105
+ | `kilat dev` | Start development server with HMR + Live Reload |
106
+ | `kilat build` | Build for production |
107
+ | `kilat serve` | Run production server (`bun dist/server.js`) |
108
+ | `kilat preview` | Preview static files |
109
+
110
+ ### Development Output
102
111
 
103
- ```bash
104
- bun run dev
112
+ ```
113
+ KilatJS Dev Server
114
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
115
+ ➜ http://localhost:3000
116
+ ➜ HMR + Live Reload enabled
117
+ ➜ Edit files and see changes instantly
118
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
105
119
  ```
106
120
 
107
- Visit [http://localhost:3000](http://localhost:3000) to see the example site.
121
+ ### Build Output
122
+
123
+ ```
124
+ 🔨 KilatJS Production Build
125
+
126
+ 🎨 Building Tailwind CSS...
127
+
128
+ 📄 Routes Analysis:
129
+ ─────────────────────────────────────────────────────────
130
+ 📄 / SSR index.tsx
131
+ 📄 /about SSR about.tsx
132
+ ⚡ /api/posts API api/posts.ts
133
+ 🔄 /blog/[slug] Dynamic SSR blog/[slug].tsx
134
+ ─────────────────────────────────────────────────────────
135
+ Total: 15 routes (10 SSR, 3 Dynamic, 2 API)
136
+
137
+ ✅ Build Complete!
138
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
139
+ Output: ./dist
140
+ Start: bun ./dist/server.js
141
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
142
+ ```
108
143
 
109
144
  ---
110
145
 
111
146
  ## 📁 Project Structure
112
147
 
113
148
  ```
114
- kilatjs/
149
+ my-app/
150
+ ├── kilat.config.ts # Configuration
151
+ ├── input.css # Tailwind input
152
+ ├── styles.css # Generated CSS
115
153
  ├── src/
116
- │ ├── index.ts # Main entry point
117
- ├── core/
118
- │ ├── router.ts # File-based router
119
- │ └── types.ts # TypeScript types
120
- ├── adapters/
121
- │ └── react.ts # React SSR adapter
122
- └── server/
123
- │ └── server.ts # Bun HTTP server
124
- ├── example/
125
- ├── server.ts # Example server entry
126
- │ ├── build.ts # Static build script
127
- │ ├── styles.css # CSS styles
128
- │ ├── components/
129
- │ │ └── Layout.tsx # Shared layout
130
- │ └── routes/
131
- │ ├── index.tsx # Home page
132
- │ ├── about.tsx # About page
133
- │ ├── contact.tsx # Contact form
134
- │ ├── contact/
135
- │ │ └── success.tsx
136
- │ └── blog/
137
- │ ├── index.tsx # Blog listing
138
- │ └── [slug].tsx # Dynamic blog posts
139
- └── README.md
154
+ │ ├── components/ # Shared components
155
+ │ └── Layout.tsx
156
+ └── routes/ # File-based routing
157
+ ├── index.tsx # /
158
+ ├── about.tsx # → /about
159
+ ├── blog/
160
+ ├── index.tsx # → /blog
161
+ └── [slug].tsx # /blog/:slug
162
+ │ └── api/
163
+ └── posts.ts # /api/posts
164
+ └── dist/ # Production build
140
165
  ```
141
166
 
142
167
  ---
143
168
 
144
169
  ## 📄 Route Contract
145
170
 
146
- Each route file may export **at most**:
171
+ Each route file can export:
147
172
 
148
173
  ```tsx
149
- // Rendering mode: "static" (build-time) or "ssr" (request-time)
150
- export const mode = "ssr";
151
-
152
- // UI framework: "react" | "solid" | "vue" (default: react)
153
- export const ui = "react";
154
-
155
174
  // SEO meta tags
156
175
  export const meta = {
157
176
  title: "Page Title",
158
- description: "Page description for SEO",
177
+ description: "Page description",
159
178
  robots: "index,follow",
160
179
  ogTitle: "Open Graph Title",
161
- ogDescription: "Open Graph description",
162
- ogImage: "https://example.com/image.jpg"
180
+ ogDescription: "OG description",
181
+ ogImage: "https://example.com/image.jpg",
163
182
  };
164
183
 
165
- // For static generation of dynamic routes
166
- export function getStaticPaths() {
167
- return ["/blog/post-1", "/blog/post-2"];
168
- }
169
-
170
- // Server-only data loading
184
+ // Server-side data loading
171
185
  export async function load(ctx) {
172
- const data = await fetchFromDatabase();
173
- return { posts: data };
186
+ const data = await fetchData();
187
+ return { items: data };
174
188
  }
175
189
 
176
- // HTTP action handlers
190
+ // HTTP method handlers (POST, PUT, DELETE, etc.)
177
191
  export async function POST(ctx) {
178
192
  const formData = await ctx.request.formData();
179
193
  // Process form...
180
194
  return Response.redirect("/success", 302);
181
195
  }
182
196
 
183
- // Pure render function
197
+ // Page component (receives data from load())
184
198
  export default function Page({ data, params, state }) {
185
- return <div>{data.posts.map(p => <h2>{p.title}</h2>)}</div>;
199
+ return <div>{data.items.map(item => <p>{item.name}</p>)}</div>;
186
200
  }
187
201
  ```
188
202
 
189
203
  ---
190
204
 
191
- ## 🔄 Actions (PRG Pattern)
205
+ ## 🔧 Tutorial: Building a Blog
192
206
 
193
- All meaningful state changes happen via HTTP requests:
207
+ ### Step 1: Create Layout Component
208
+
209
+ **`src/components/Layout.tsx`:**
194
210
 
195
211
  ```tsx
196
- // routes/contact.tsx
197
- export async function POST({ request }) {
198
- const formData = await request.formData();
199
- const email = formData.get("email");
200
-
201
- await saveToDatabase({ email });
202
-
203
- // PRG: Post-Redirect-Get pattern
204
- return Response.redirect("/contact/success", 302);
212
+ interface LayoutProps {
213
+ children: React.ReactNode;
214
+ title?: string;
205
215
  }
206
216
 
207
- export default function ContactPage() {
217
+ export function Layout({ children, title = "My Blog" }: LayoutProps) {
208
218
  return (
209
- <form method="POST" action="/contact">
210
- <input type="email" name="email" required />
211
- <button type="submit">Subscribe</button>
212
- </form>
219
+ <div className="min-h-screen bg-gray-50">
220
+ <header className="bg-white shadow">
221
+ <nav className="max-w-4xl mx-auto px-4 py-4">
222
+ <a href="/" className="text-xl font-bold">My Blog</a>
223
+ <div className="float-right space-x-4">
224
+ <a href="/" className="text-gray-600 hover:text-gray-900">Home</a>
225
+ <a href="/blog" className="text-gray-600 hover:text-gray-900">Blog</a>
226
+ <a href="/about" className="text-gray-600 hover:text-gray-900">About</a>
227
+ </div>
228
+ </nav>
229
+ </header>
230
+ <main className="max-w-4xl mx-auto px-4 py-8">
231
+ {children}
232
+ </main>
233
+ </div>
213
234
  );
214
235
  }
215
236
  ```
216
237
 
217
- **No JavaScript required.** Forms work natively.
238
+ ### Step 2: Create Blog List Page
218
239
 
219
- ---
240
+ **`src/routes/blog/index.tsx`:**
220
241
 
221
- ## 🎨 Styling (Tailwind-Ready)
242
+ ```tsx
243
+ import { Layout } from "../../components/Layout";
222
244
 
223
- KilatJS supports Tailwind CSS via CLI (no Vite required):
245
+ export const meta = {
246
+ title: "Blog - My Site",
247
+ description: "Read our latest articles",
248
+ };
224
249
 
225
- ```bash
226
- # Generate CSS
227
- npx tailwindcss -i ./input.css -o ./styles.css --watch
250
+ export async function load() {
251
+ // In real app, fetch from database
252
+ const posts = [
253
+ { id: 1, slug: "hello-world", title: "Hello World", excerpt: "My first post..." },
254
+ { id: 2, slug: "getting-started", title: "Getting Started", excerpt: "Learn how to..." },
255
+ ];
256
+ return { posts };
257
+ }
258
+
259
+ export default function BlogPage({ data }) {
260
+ return (
261
+ <Layout>
262
+ <h1 className="text-3xl font-bold mb-8">Blog</h1>
263
+ <div className="space-y-6">
264
+ {data.posts.map(post => (
265
+ <article key={post.id} className="bg-white p-6 rounded-lg shadow">
266
+ <h2 className="text-xl font-semibold">
267
+ <a href={`/blog/${post.slug}`} className="hover:text-blue-600">
268
+ {post.title}
269
+ </a>
270
+ </h2>
271
+ <p className="text-gray-600 mt-2">{post.excerpt}</p>
272
+ </article>
273
+ ))}
274
+ </div>
275
+ </Layout>
276
+ );
277
+ }
228
278
  ```
229
279
 
230
- Configure in your `kilat.config.ts`:
280
+ ### Step 3: Create Dynamic Blog Post Page
231
281
 
232
- ```ts
233
- import { createKilat } from 'kilatjs';
282
+ **`src/routes/blog/[slug].tsx`:**
234
283
 
235
- createKilat({
236
- routesDir: "./routes",
237
- tailwind: {
238
- enabled: true,
239
- cssPath: "./styles.css"
284
+ ```tsx
285
+ import { Layout } from "../../components/Layout";
286
+
287
+ export const meta = {
288
+ title: "Blog Post",
289
+ description: "Read this article",
290
+ };
291
+
292
+ export async function load({ params }) {
293
+ // params.slug contains the URL parameter
294
+ const post = await getPostBySlug(params.slug);
295
+
296
+ if (!post) {
297
+ throw new Response("Not Found", { status: 404 });
240
298
  }
241
- });
299
+
300
+ return { post };
301
+ }
302
+
303
+ async function getPostBySlug(slug: string) {
304
+ // In real app, fetch from database
305
+ const posts = {
306
+ "hello-world": { title: "Hello World", content: "This is my first post!" },
307
+ "getting-started": { title: "Getting Started", content: "Let me show you how..." },
308
+ };
309
+ return posts[slug] || null;
310
+ }
311
+
312
+ export default function BlogPostPage({ data, params }) {
313
+ return (
314
+ <Layout>
315
+ <article>
316
+ <h1 className="text-4xl font-bold mb-4">{data.post.title}</h1>
317
+ <div className="prose max-w-none">
318
+ {data.post.content}
319
+ </div>
320
+ </article>
321
+ <a href="/blog" className="text-blue-600 mt-8 inline-block">← Back to Blog</a>
322
+ </Layout>
323
+ );
324
+ }
242
325
  ```
243
326
 
244
- ---
327
+ ### Step 4: Create API Route
245
328
 
246
- ## 📊 Rendering Modes
329
+ **`src/routes/api/posts.ts`:**
247
330
 
248
- ### Static Generation
331
+ ```ts
332
+ import { RouteContext } from "kilatjs";
249
333
 
250
- - Build-time HTML
251
- - Best for SEO & caching
252
- - Requires explicit path enumeration for dynamic routes
334
+ const posts = [
335
+ { id: 1, title: "Hello World", slug: "hello-world" },
336
+ { id: 2, title: "Getting Started", slug: "getting-started" },
337
+ ];
253
338
 
254
- ```tsx
255
- export const mode = "static";
339
+ // GET /api/posts
340
+ export async function GET(ctx: RouteContext) {
341
+ const search = ctx.query.get("search")?.toLowerCase();
342
+
343
+ let results = posts;
344
+ if (search) {
345
+ results = posts.filter(p => p.title.toLowerCase().includes(search));
346
+ }
347
+
348
+ return new Response(JSON.stringify({ data: results }), {
349
+ headers: { "Content-Type": "application/json" },
350
+ });
351
+ }
256
352
 
257
- export function getStaticPaths() {
258
- return ["/blog/post-1", "/blog/post-2"];
353
+ // POST /api/posts
354
+ export async function POST(ctx: RouteContext) {
355
+ const body = await ctx.request.json();
356
+
357
+ const newPost = {
358
+ id: posts.length + 1,
359
+ title: body.title,
360
+ slug: body.title.toLowerCase().replace(/\s+/g, "-"),
361
+ };
362
+
363
+ posts.push(newPost);
364
+
365
+ return new Response(JSON.stringify({ data: newPost }), {
366
+ status: 201,
367
+ headers: { "Content-Type": "application/json" },
368
+ });
259
369
  }
260
370
  ```
261
371
 
262
- ### Server-Side Rendering (SSR)
372
+ ### Step 5: Create Contact Form with PRG Pattern
263
373
 
264
- - Request-time HTML
265
- - PHP-style execution
266
- - Infinite routes allowed
374
+ **`src/routes/contact.tsx`:**
267
375
 
268
376
  ```tsx
269
- export const mode = "ssr"; // or omit (default)
377
+ import { Layout } from "../components/Layout";
378
+
379
+ export const meta = {
380
+ title: "Contact Us",
381
+ description: "Get in touch with us",
382
+ };
383
+
384
+ // Handle form submission
385
+ export async function POST({ request }) {
386
+ const formData = await request.formData();
387
+ const name = formData.get("name");
388
+ const email = formData.get("email");
389
+ const message = formData.get("message");
390
+
391
+ // Save to database, send email, etc.
392
+ console.log("Contact form:", { name, email, message });
393
+
394
+ // PRG: Post-Redirect-Get pattern
395
+ return Response.redirect("/contact/success", 302);
396
+ }
397
+
398
+ export default function ContactPage() {
399
+ return (
400
+ <Layout>
401
+ <h1 className="text-3xl font-bold mb-8">Contact Us</h1>
402
+ <form method="POST" className="max-w-md space-y-4">
403
+ <div>
404
+ <label className="block text-sm font-medium mb-1">Name</label>
405
+ <input
406
+ type="text"
407
+ name="name"
408
+ required
409
+ className="w-full px-3 py-2 border rounded-lg"
410
+ />
411
+ </div>
412
+ <div>
413
+ <label className="block text-sm font-medium mb-1">Email</label>
414
+ <input
415
+ type="email"
416
+ name="email"
417
+ required
418
+ className="w-full px-3 py-2 border rounded-lg"
419
+ />
420
+ </div>
421
+ <div>
422
+ <label className="block text-sm font-medium mb-1">Message</label>
423
+ <textarea
424
+ name="message"
425
+ rows={4}
426
+ required
427
+ className="w-full px-3 py-2 border rounded-lg"
428
+ />
429
+ </div>
430
+ <button
431
+ type="submit"
432
+ className="bg-blue-600 text-white px-6 py-2 rounded-lg hover:bg-blue-700"
433
+ >
434
+ Send Message
435
+ </button>
436
+ </form>
437
+ </Layout>
438
+ );
439
+ }
270
440
  ```
271
441
 
272
- ---
442
+ **`src/routes/contact/success.tsx`:**
273
443
 
274
- ## 🔐 Authentication
444
+ ```tsx
445
+ import { Layout } from "../../components/Layout";
275
446
 
276
- Identity is proven **per request** via cookies/headers:
447
+ export const meta = {
448
+ title: "Message Sent",
449
+ };
450
+
451
+ export default function ContactSuccessPage() {
452
+ return (
453
+ <Layout>
454
+ <div className="text-center py-12">
455
+ <h1 className="text-3xl font-bold text-green-600 mb-4">✓ Message Sent!</h1>
456
+ <p className="text-gray-600 mb-8">Thank you for contacting us. We'll get back to you soon.</p>
457
+ <a href="/" className="text-blue-600 hover:underline">← Back to Home</a>
458
+ </div>
459
+ </Layout>
460
+ );
461
+ }
462
+ ```
463
+
464
+ ---
465
+
466
+ ## 🔐 Authentication Example
277
467
 
278
468
  ```tsx
469
+ // src/routes/dashboard.tsx
279
470
  export async function load({ request }) {
280
- const session = await getSession(request.headers.get("cookie"));
471
+ const cookie = request.headers.get("cookie");
472
+ const session = await getSession(cookie);
281
473
 
282
474
  if (!session) {
283
- throw Response.redirect("/login");
475
+ // Redirect to login if not authenticated
476
+ throw Response.redirect("/login", 302);
284
477
  }
285
478
 
286
479
  return { user: session.user };
287
480
  }
288
- ```
289
481
 
290
- - Never trust localStorage for auth
291
- - Server validates every request
292
- - Cookies are the source of truth
482
+ export default function DashboardPage({ data }) {
483
+ return (
484
+ <div>
485
+ <h1>Welcome, {data.user.name}!</h1>
486
+ <form method="POST" action="/logout">
487
+ <button type="submit">Logout</button>
488
+ </form>
489
+ </div>
490
+ );
491
+ }
492
+ ```
293
493
 
294
494
  ---
295
495
 
296
- ## 🏃 Running Your App
297
-
298
- ### Development
496
+ ## 🛠️ Configuration Reference
299
497
 
300
498
  ```ts
301
- // Named import
302
- import { createKilat } from 'kilatjs';
303
-
304
- // Or default import
305
- import Kilat from 'kilatjs';
306
-
307
- const server = createKilat({
308
- routesDir: "./routes",
309
- port: 3000,
310
- dev: true,
499
+ // kilat.config.ts
500
+ import { defineConfig } from "kilatjs";
501
+
502
+ export default defineConfig({
503
+ appDir: "./src", // Source directory (auto-detects routes/ or pages/)
504
+ outDir: "./dist", // Production build output
505
+ port: 3000, // Server port
506
+ hostname: "localhost", // Server hostname
507
+ publicDir: "./public", // Static assets directory
311
508
  tailwind: {
312
- enabled: false,
313
- cssPath: "./styles.css"
314
- }
509
+ enabled: true, // Enable Tailwind CSS
510
+ inputPath: "./input.css",
511
+ cssPath: "./styles.css",
512
+ },
315
513
  });
316
-
317
- server.start();
318
514
  ```
319
515
 
320
- ### Static Build
516
+ ---
517
+
518
+ ## 🔥 Why Bun Native?
321
519
 
322
- ```ts
323
- import { buildStatic } from 'kilatjs';
520
+ KilatJS leverages Bun's runtime directly:
324
521
 
325
- buildStatic({
326
- routesDir: "./routes",
327
- outDir: "./dist"
328
- });
329
- ```
522
+ - ⚡ **Bun.FileSystemRouter** - Zero-config file-based routing
523
+ - 🚀 **Bun.serve()** - HTTP server with WebSocket support
524
+ - 📦 **Bun.build()** - Native TypeScript/JSX bundling
525
+ - 💾 **Bun.file()** - Streaming file operations
526
+ - 🔍 **Bun.Glob** - Pattern matching for routes
330
527
 
331
- ---
528
+ **Performance:**
529
+ - 3x faster cold starts vs Node.js
530
+ - Native TypeScript - no transpilation
531
+ - Zero-copy streaming for static files
332
532
 
333
- ## 🧩 UI Framework Support
533
+ ---
334
534
 
335
- | Framework | Status | Notes |
336
- |-----------|--------|-------|
337
- | React | ✅ Default | Full SSR support |
338
- | Solid | 🚧 Planned | Coming soon |
339
- | Vue | 🚧 Planned | Coming soon |
535
+ ## Core Principles
340
536
 
341
- **Rule:** One renderer per route. No mixing.
537
+ 1. **HTML is the protocol** Every response is complete, semantic HTML
538
+ 2. **Server owns all truth** — No client-side state management
539
+ 3. **Every change = HTTP request** — Forms, mutations, navigation
540
+ 4. **UI frameworks render only** — React for templating, not orchestration
541
+ 5. **JavaScript is optional** — Sites work without JS
342
542
 
343
543
  ---
344
544
 
345
- ## 🎯 SEO Guarantee
545
+ ## Client-Side Interactivity
346
546
 
347
- > **If a route exists, it is SEO-friendly.**
547
+ KilatJS is server-first, but you can add client-side interactivity using these patterns:
348
548
 
349
- Because:
350
- - HTML is always complete
351
- - `<head>` is server-rendered
352
- - No JS required for content
353
- - Meta tags are first-class
549
+ ### 1. `clientScript` Export (Recommended)
354
550
 
355
- ---
356
-
357
- ## 📜 Philosophy
551
+ Export a function that runs in the browser. TypeScript/LSP fully supports it!
358
552
 
359
- KilatJS rejects modern web complexity:
553
+ ```tsx
554
+ // src/routes/counter.tsx
360
555
 
361
- | We Don't Do | We Do |
362
- |-------------|-------|
363
- | Client-side rendering | Server-rendered HTML |
364
- | Hydration/Islands | Static markup |
365
- | Client-side router | HTTP navigation |
366
- | Framework hooks | Props from loader |
367
- | State sync | HTTP mutations |
556
+ // Client-side script - auto-injected, LSP checks the code!
557
+ export function clientScript() {
558
+ let count = 0;
559
+ const countEl = document.getElementById("count")!;
560
+
561
+ document.getElementById("decrement")!.onclick = () => {
562
+ countEl.textContent = String(--count);
563
+ };
564
+ document.getElementById("increment")!.onclick = () => {
565
+ countEl.textContent = String(++count);
566
+ };
567
+ }
368
568
 
369
- ---
569
+ export default function CounterPage() {
570
+ return (
571
+ <div>
572
+ <button id="decrement">-</button>
573
+ <span id="count">0</span>
574
+ <button id="increment">+</button>
575
+ </div>
576
+ );
577
+ }
578
+ ```
370
579
 
371
- ## 🛠️ API Reference
580
+ The framework automatically injects the script - no manual `<script>` tags needed!
372
581
 
373
- ### `createKilat(config)`
582
+ ### 2. Alpine.js
374
583
 
375
- Creates a Kilat server instance.
584
+ Lightweight reactivity with declarative HTML attributes:
376
585
 
377
- ```ts
378
- interface KilatConfig {
379
- routesDir: string; // Path to routes directory
380
- outDir: string; // Output for static builds
381
- port?: number; // Server port (default: 3000)
382
- hostname?: string; // Server hostname (default: "localhost")
383
- dev?: boolean; // Development mode
384
- publicDir?: string; // Static assets directory
385
- tailwind?: {
386
- enabled: boolean;
387
- cssPath: string;
388
- configPath?: string;
389
- };
586
+ ```tsx
587
+ export default function AlpinePage() {
588
+ return (
589
+ <div x-data="{ count: 0 }">
590
+ <button x-on:click="count--">-</button>
591
+ <span x-text="count">0</span>
592
+ <button x-on:click="count++">+</button>
593
+ </div>
594
+ );
390
595
  }
391
596
  ```
392
597
 
393
- ### `RouteContext`
598
+ ### 3. HTMX
394
599
 
395
- Passed to `load()` and action handlers:
600
+ Server-driven UI updates without JavaScript:
396
601
 
397
- ```ts
398
- interface RouteContext {
399
- request: Request; // Web Request object
400
- params: Record<string, string>; // URL parameters
401
- query: URLSearchParams; // Query string
402
- state: ServerGlobalState; // Request-scoped state
403
- }
602
+ ```html
603
+ <!-- Can use .html files directly in routes/ -->
604
+ <button
605
+ hx-get="/api/greeting"
606
+ hx-target="#result"
607
+ hx-swap="innerHTML">
608
+ Load
609
+ </button>
610
+ <div id="result"></div>
404
611
  ```
405
612
 
406
- ### `PageProps`
613
+ KilatJS supports `.html` files as routes - perfect for HTMX pages!
407
614
 
408
- Passed to page components:
615
+ ### 4. Native HTML Forms
409
616
 
410
- ```ts
411
- interface PageProps {
412
- data: any; // From load()
413
- params: Record<string, string>; // URL parameters
414
- state: ServerGlobalState; // Request-scoped state
415
- }
617
+ Best for mutations - no JS required:
618
+
619
+ ```tsx
620
+ <form method="POST" action="/contact">
621
+ <input name="email" type="email" />
622
+ <button type="submit">Submit</button>
623
+ </form>
416
624
  ```
417
625
 
418
626
  ---
419
627
 
420
- ## 📝 License
628
+ ## What KilatJS Doesn't Do
421
629
 
422
- MIT
630
+ - ❌ Client-Side Rendering (CSR)
631
+ - ❌ React Hydration / Islands
632
+ - ❌ Client-side routing
633
+ - ❌ React hooks (`useState`, `useEffect`) - No client React runtime
634
+ - ❌ SPA patterns
423
635
 
424
- ---
636
+ ### Why No React Hooks?
425
637
 
426
- ## 🤝 Contributing
638
+ KilatJS uses `renderToStaticMarkup` which outputs pure HTML without React runtime. This means:
427
639
 
428
- KilatJS is in early development. Contributions welcome!
640
+ - Faster page loads (no React bundle)
641
+ - ✅ Better SEO (real HTML)
642
+ - ✅ Works without JavaScript
643
+ - ❌ No `useState`, `useEffect`, `onClick`
429
644
 
430
- 1. Fork the repository
431
- 2. Create a feature branch
432
- 3. Submit a pull request
645
+ **For interactivity, use `clientScript`, Alpine.js, HTMX, or vanilla JS instead.**
646
+
647
+ > If you need React hooks with SSR, consider Next.js or Remix which have built-in hydration.
648
+
649
+ ---
650
+
651
+ ## 📝 License
652
+
653
+ MIT
433
654
 
434
655
  ---
435
656
 
436
657
  <p align="center">
437
- <strong>Built with 💜 for the real web using Bun's native APIs.</strong>
438
- </p>
658
+ <strong>Built with 💜 for the real web using Bun.</strong>
659
+ </p>