@lolyjs/core 0.1.0-alpha.9 → 0.2.0-alpha.0

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
@@ -1,13 +1,13 @@
1
- # Loly Framework EXPERIMENTAL
1
+ # Loly Framework
2
2
 
3
3
  <div align="center">
4
4
 
5
- **A modern, production-ready React framework with file-based routing, SSR, SSG, and built-in security**
5
+ **A modern, full-stack React framework with native WebSocket support, route-level middlewares, and enterprise-grade features**
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/@lolyjs/core?style=flat-square)](https://www.npmjs.com/package/@lolyjs/core)
8
8
  [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg?style=flat-square)](https://opensource.org/licenses/ISC)
9
9
 
10
- *Built with React 19, Express, Rspack, and TypeScript*
10
+ *Built with React 19, Express, Rspack, Socket.IO, and TypeScript*
11
11
 
12
12
  </div>
13
13
 
@@ -15,15 +15,16 @@
15
15
 
16
16
  ## Overview
17
17
 
18
- Loly is a full-stack React framework that combines the simplicity of file-based routing with powerful server-side rendering, static site generation, and enterprise-grade security features.
18
+ Loly is a full-stack React framework that combines the simplicity of file-based routing with powerful server-side rendering, static site generation, and unique features like native WebSocket support and route-level middlewares.
19
19
 
20
- ### Why Loly?
20
+ ### What Makes Loly Different?
21
21
 
22
- - **Fast** - Lightning-fast bundling with Rspack and optimized SSR streaming
23
- - 🔒 **Secure** - Built-in rate limiting, CORS, CSP, input validation, and sanitization
24
- - 🎯 **Developer-Friendly** - File-based routing, hot reload, and full TypeScript support
25
- - 🚀 **Production-Ready** - Structured logging, error handling, and optimized builds
26
- - 💾 **Smart Caching** - LRU cache with path indexing for optimal performance
22
+ - 🔌 **Native WebSocket Support** - Built-in Socket.IO integration with automatic namespace routing
23
+ - 🎯 **Route-Level Middlewares** - Define middlewares directly in your routes for pages and APIs
24
+ - 📁 **Separation of Concerns** - Server logic in `server.hook.ts` separate from React components
25
+ - 🚀 **Hybrid Rendering** - SSR, SSG, and CSR with streaming support
26
+ - 🛡️ **Security First** - Built-in rate limiting, validation, sanitization, and security headers
27
+ - ⚡ **Performance** - Fast bundling with Rspack and optimized code splitting
27
28
 
28
29
  ---
29
30
 
@@ -37,15 +38,7 @@ npm install @lolyjs/core react react-dom
37
38
  pnpm add @lolyjs/core react react-dom
38
39
  ```
39
40
 
40
- ### Create Your First App
41
-
42
- ```bash
43
- # Create app directory
44
- mkdir my-app && cd my-app
45
-
46
- # Create your first page
47
- mkdir -p app
48
- ```
41
+ ### Create Your First Page
49
42
 
50
43
  ```tsx
51
44
  // app/page.tsx
@@ -54,35 +47,161 @@ export default function Home() {
54
47
  }
55
48
  ```
56
49
 
50
+ ### Add Server-Side Data
51
+
57
52
  ```tsx
58
- // bootstrap.tsx
59
- import { bootstrapClient } from "@lolyjs/core/runtime";
60
- import routes from "@lolyjs/core/runtime";
53
+ // app/page/server.hook.ts
54
+ import type { ServerLoader } from "@lolyjs/core";
61
55
 
62
- bootstrapClient(routes);
56
+ export const getServerSideProps: ServerLoader = async (ctx) => {
57
+ const data = await fetchData();
58
+
59
+ return {
60
+ props: { data },
61
+ metadata: {
62
+ title: "Home Page",
63
+ description: "Welcome to Loly",
64
+ },
65
+ };
66
+ };
63
67
  ```
64
68
 
65
- ```json
66
- // package.json
67
- {
68
- "scripts": {
69
- "dev": "loly dev",
70
- "build": "loly build",
71
- "start": "loly start"
72
- }
69
+ ```tsx
70
+ // app/page.tsx
71
+ import { usePageProps } from "@lolyjs/core/hooks";
72
+
73
+ export default function Home() {
74
+ const { props } = usePageProps();
75
+ return <h1>{props.data}</h1>;
73
76
  }
74
77
  ```
75
78
 
79
+ ### Start Development Server
80
+
76
81
  ```bash
77
- npm run dev
82
+ npx loly dev
78
83
  # Server runs on http://localhost:3000
79
84
  ```
80
85
 
81
86
  ---
82
87
 
83
- ## Core Concepts
88
+ ## Key Features
89
+
90
+ ### 🔌 Native WebSocket Support
91
+
92
+ Loly includes built-in WebSocket support with automatic namespace routing. Define WebSocket events using the same file-based routing pattern as pages and APIs:
93
+
94
+ ```tsx
95
+ // app/wss/chat/events.ts
96
+ import type { WssContext } from "@lolyjs/core";
97
+
98
+ export const events = [
99
+ {
100
+ name: "connection",
101
+ handler: (ctx: WssContext) => {
102
+ console.log("Client connected:", ctx.socket.id);
103
+ },
104
+ },
105
+ {
106
+ name: "message",
107
+ handler: (ctx: WssContext) => {
108
+ const { data, actions } = ctx;
109
+ // Broadcast to all clients
110
+ actions.broadcast("message", {
111
+ text: data.text,
112
+ from: ctx.socket.id,
113
+ });
114
+ },
115
+ },
116
+ ];
117
+ ```
118
+
119
+ **Client-side:**
120
+
121
+ ```tsx
122
+ import { lolySocket } from "@lolyjs/core/sockets";
123
+
124
+ const socket = lolySocket("/chat");
125
+
126
+ socket.on("message", (data) => {
127
+ console.log("Received:", data);
128
+ });
129
+
130
+ socket.emit("message", { text: "Hello!" });
131
+ ```
132
+
133
+ **Key Benefits:**
134
+ - Automatic namespace creation from file structure
135
+ - Same routing pattern as pages and APIs
136
+ - Built-in broadcasting helpers (`emit`, `broadcast`, `emitTo`, `emitToClient`)
137
+ - No manual configuration required
138
+
139
+ ### 🎯 Route-Level Middlewares
140
+
141
+ Define middlewares directly in your routes for fine-grained control:
142
+
143
+ **For Pages:**
84
144
 
85
- ### File-Based Routing
145
+ ```tsx
146
+ // app/dashboard/server.hook.ts
147
+ import type { RouteMiddleware, ServerLoader } from "@lolyjs/core";
148
+
149
+ export const beforeServerData: RouteMiddleware[] = [
150
+ async (ctx, next) => {
151
+ // Authentication
152
+ const token = ctx.req.headers.authorization;
153
+ if (!token) {
154
+ ctx.res.status(401).json({ error: "Unauthorized" });
155
+ return;
156
+ }
157
+ ctx.locals.user = await verifyToken(token);
158
+ await next();
159
+ },
160
+ ];
161
+
162
+ export const getServerSideProps: ServerLoader = async (ctx) => {
163
+ const user = ctx.locals.user; // Available from middleware
164
+ return { props: { user } };
165
+ };
166
+ ```
167
+
168
+ **For API Routes:**
169
+
170
+ ```tsx
171
+ // app/api/protected/route.ts
172
+ import type { ApiMiddleware, ApiContext } from "@lolyjs/core";
173
+
174
+ // Global middleware for all methods
175
+ export const beforeApi: ApiMiddleware[] = [
176
+ async (ctx, next) => {
177
+ // Authentication
178
+ const user = await verifyUser(ctx.req);
179
+ ctx.locals.user = user;
180
+ await next();
181
+ },
182
+ ];
183
+
184
+ // Method-specific middleware
185
+ export const beforePOST: ApiMiddleware[] = [
186
+ async (ctx, next) => {
187
+ // Validation specific to POST
188
+ await next();
189
+ },
190
+ ];
191
+
192
+ export async function GET(ctx: ApiContext) {
193
+ const user = ctx.locals.user;
194
+ return ctx.Response({ user });
195
+ }
196
+ ```
197
+
198
+ **Key Benefits:**
199
+ - Middlewares execute before loaders/handlers
200
+ - Share data via `ctx.locals`
201
+ - Method-specific middlewares for APIs
202
+ - Clean separation of concerns
203
+
204
+ ### 📁 File-Based Routing
86
205
 
87
206
  Routes are automatically created from your file structure:
88
207
 
@@ -93,99 +212,209 @@ Routes are automatically created from your file structure:
93
212
  | `app/blog/[slug]/page.tsx` | `/blog/:slug` |
94
213
  | `app/post/[...path]/page.tsx` | `/post/*` (catch-all) |
95
214
 
96
- ### Server-Side Data Fetching
215
+ **Nested Layouts:**
216
+
217
+ **⚠️ Important**: Layouts should NOT include `<html>` or `<body>` tags. The framework automatically handles the base HTML structure. Layouts should only contain content that goes inside the body.
218
+
219
+ ```tsx
220
+ // app/layout.tsx (Root layout)
221
+ export default function RootLayout({ children }) {
222
+ return (
223
+ <div>
224
+ <nav>Navigation</nav>
225
+ {children}
226
+ <footer>Footer</footer>
227
+ </div>
228
+ );
229
+ }
230
+ ```
231
+
232
+ ```tsx
233
+ // app/blog/layout.tsx (Nested layout)
234
+ export default function BlogLayout({ children }) {
235
+ return (
236
+ <div>
237
+ <aside>Sidebar</aside>
238
+ <main>{children}</main>
239
+ </div>
240
+ );
241
+ }
242
+ ```
243
+
244
+ ### 🚀 Hybrid Rendering
245
+
246
+ Choose the best rendering strategy for each page:
247
+
248
+ **SSR (Server-Side Rendering):**
97
249
 
98
- Use `server.hook.ts` to fetch data on the server:
250
+ ```tsx
251
+ // app/posts/server.hook.ts
252
+ export const dynamic = "force-dynamic" as const;
253
+
254
+ export const getServerSideProps: ServerLoader = async (ctx) => {
255
+ const posts = await fetchFreshPosts();
256
+ return { props: { posts } };
257
+ };
258
+ ```
259
+
260
+ **SSG (Static Site Generation):**
99
261
 
100
262
  ```tsx
101
263
  // app/blog/[slug]/server.hook.ts
102
- import type { ServerLoader } from "@lolyjs/core";
264
+ export const dynamic = "force-static" as const;
265
+
266
+ export const generateStaticParams: GenerateStaticParams = async () => {
267
+ const posts = await getAllPosts();
268
+ return posts.map(post => ({ slug: post.slug }));
269
+ };
103
270
 
104
271
  export const getServerSideProps: ServerLoader = async (ctx) => {
105
- const { slug } = ctx.params;
106
- const post = await fetchPost(slug);
107
-
108
- return {
109
- props: { post },
110
- metadata: {
111
- title: post.title,
112
- description: post.excerpt,
113
- },
114
- };
272
+ const post = await getPost(ctx.params.slug);
273
+ return { props: { post } };
115
274
  };
116
275
  ```
117
276
 
277
+ **CSR (Client-Side Rendering):**
278
+
118
279
  ```tsx
119
- // app/blog/[slug]/page.tsx
120
- import { usePageProps } from "@lolyjs/core/hooks";
280
+ // app/dashboard/page.tsx (No server.hook.ts)
281
+ import { useState, useEffect } from "react";
121
282
 
122
- export default function BlogPost() {
123
- const { props } = usePageProps();
124
- const { post } = props;
283
+ export default function Dashboard() {
284
+ const [data, setData] = useState(null);
125
285
 
126
- return (
127
- <article>
128
- <h1>{post.title}</h1>
129
- <div>{post.content}</div>
130
- </article>
131
- );
286
+ useEffect(() => {
287
+ fetchData().then(setData);
288
+ }, []);
289
+
290
+ return <div>{data}</div>;
132
291
  }
133
292
  ```
134
293
 
135
- ### Client-Side Navigation
294
+ ### 🔌 API Routes
136
295
 
137
- Fast page transitions without full reloads:
296
+ Create RESTful APIs with flexible middleware support:
138
297
 
139
298
  ```tsx
140
- import { Link } from "@lolyjs/core/components";
299
+ // app/api/posts/route.ts
300
+ import type { ApiContext } from "@lolyjs/core";
301
+ import { validate } from "@lolyjs/core";
302
+ import { z } from "zod";
141
303
 
142
- export default function Navigation() {
143
- return (
144
- <nav>
145
- <Link href="/">Home</Link>
146
- <Link href="/about">About</Link>
147
- </nav>
148
- );
304
+ const postSchema = z.object({
305
+ title: z.string().min(1),
306
+ content: z.string().min(1),
307
+ });
308
+
309
+ export async function GET(ctx: ApiContext) {
310
+ const posts = await getPosts();
311
+ return ctx.Response({ posts });
312
+ }
313
+
314
+ export async function POST(ctx: ApiContext) {
315
+ const data = validate(postSchema, ctx.req.body);
316
+ const post = await createPost(data);
317
+ return ctx.Response({ post }, 201);
149
318
  }
150
319
  ```
151
320
 
152
- ### Cache Revalidation
321
+ ### 🛡️ Built-in Security
153
322
 
154
- Invalidate and refresh route data:
323
+ **Rate Limiting:**
155
324
 
156
325
  ```tsx
157
- import { revalidatePath, revalidate } from "@lolyjs/core/client-cache";
326
+ // loly.config.ts
327
+ import { ServerConfig } from "@lolyjs/core";
158
328
 
159
- // Revalidate a specific route
160
- revalidatePath('/posts');
329
+ export const config = (env: string): ServerConfig => {
330
+ return {
331
+ rateLimit: {
332
+ windowMs: 15 * 60 * 1000, // 15 minutes
333
+ max: 1000,
334
+ strictMax: 5,
335
+ strictPatterns: ["/api/auth/**"],
336
+ },
337
+ };
338
+ };
339
+ ```
161
340
 
162
- // Revalidate and refresh current page (similar to Next.js router.refresh())
163
- await revalidate();
341
+ **Validation with Zod:**
342
+
343
+ ```tsx
344
+ import { validate, ValidationError } from "@lolyjs/core";
345
+ import { z } from "zod";
346
+
347
+ const schema = z.object({
348
+ email: z.string().email(),
349
+ age: z.number().int().min(0).max(150),
350
+ });
351
+
352
+ try {
353
+ const data = validate(schema, req.body);
354
+ } catch (error) {
355
+ if (error instanceof ValidationError) {
356
+ return Response({ errors: error.format() }, 400);
357
+ }
358
+ }
164
359
  ```
165
360
 
166
- Components using `usePageProps()` automatically update when you call `revalidate()`.
361
+ **Automatic Sanitization:**
167
362
 
168
- ---
363
+ Route parameters and query strings are automatically sanitized to prevent XSS attacks.
364
+
365
+ **Security Headers:**
169
366
 
170
- ## Features
367
+ Helmet is configured by default with CSP (Content Security Policy) and nonce support.
171
368
 
172
- ### Core Features
369
+ ### 📝 Structured Logging
173
370
 
174
- - ✅ **File-based Routing** - Automatic route generation
175
- - **Server-Side Rendering (SSR)** - React 19 streaming
176
- - ✅ **Static Site Generation (SSG)** - Pre-render at build time
177
- - ✅ **API Routes** - RESTful APIs alongside pages
178
- - ✅ **Nested Layouts** - Shared UI components
179
- - ✅ **Client-Side Navigation** - Fast page transitions
180
- - ✅ **Smart Caching** - LRU cache with path indexing
371
+ ```tsx
372
+ import { getRequestLogger, createModuleLogger } from "@lolyjs/core";
181
373
 
182
- ### Security Features
374
+ // Request logger (automatic request ID)
375
+ export const getServerSideProps: ServerLoader = async (ctx) => {
376
+ const logger = getRequestLogger(ctx.req);
377
+ logger.info("Processing request", { userId: ctx.locals.user?.id });
378
+ return { props: {} };
379
+ };
183
380
 
184
- - 🔒 **Rate Limiting** - Configurable with strict patterns
185
- - 🔒 **CORS Protection** - Secure cross-origin sharing
186
- - 🔒 **Content Security Policy** - XSS protection with nonce
187
- - 🔒 **Input Validation** - Zod-based validation
188
- - 🔒 **Input Sanitization** - Automatic XSS protection
381
+ // Module logger
382
+ const logger = createModuleLogger("my-module");
383
+ logger.info("Module initialized");
384
+ logger.error("Error occurred", error);
385
+ ```
386
+
387
+ ---
388
+
389
+ ## Project Structure
390
+
391
+ ```
392
+ your-app/
393
+ ├── app/
394
+ │ ├── layout.tsx # Root layout
395
+ │ ├── page.tsx # Home page (/)
396
+ │ ├── server.hook.ts # Server logic for home
397
+ │ ├── _not-found.tsx # Custom 404
398
+ │ ├── _error.tsx # Custom error page
399
+ │ ├── blog/
400
+ │ │ ├── layout.tsx # Blog layout
401
+ │ │ ├── page.tsx # /blog
402
+ │ │ └── [slug]/
403
+ │ │ ├── page.tsx # /blog/:slug
404
+ │ │ └── server.hook.ts # Server logic
405
+ │ ├── api/
406
+ │ │ └── posts/
407
+ │ │ └── route.ts # /api/posts
408
+ │ └── wss/
409
+ │ └── chat/
410
+ │ └── events.ts # WebSocket namespace /chat
411
+ ├── components/ # React components
412
+ ├── lib/ # Utilities
413
+ ├── public/ # Static files
414
+ ├── loly.config.ts # Framework configuration
415
+ ├── init.server.ts # Server initialization (DB, services, etc.)
416
+ └── package.json
417
+ ```
189
418
 
190
419
  ---
191
420
 
@@ -224,258 +453,187 @@ export const getServerSideProps: ServerLoader = async (ctx) => {
224
453
  };
225
454
  ```
226
455
 
227
- ### Static Site Generation
228
-
229
- ```tsx
230
- import type { ServerLoader, GenerateStaticParams } from "@lolyjs/core";
231
-
232
- export const dynamic = "force-static" as const;
233
-
234
- export const generateStaticParams: GenerateStaticParams = async () => {
235
- const posts = await fetchAllPosts();
236
- return posts.map((post) => ({ slug: post.slug }));
237
- };
238
-
239
- export const getServerSideProps: ServerLoader = async (ctx) => {
240
- const { slug } = ctx.params;
241
- const post = await fetchPost(slug);
242
- return { props: { post } };
243
- };
244
- ```
245
-
246
- ### API Routes
456
+ ### API Route Handler
247
457
 
248
458
  ```tsx
249
459
  import type { ApiContext } from "@lolyjs/core";
250
- import { validate } from "@lolyjs/core";
251
- import { z } from "zod";
252
-
253
- const schema = z.object({
254
- title: z.string().min(1),
255
- content: z.string().min(1),
256
- });
257
460
 
258
461
  export async function GET(ctx: ApiContext) {
259
- const posts = await fetchPosts();
260
- return ctx.Response({ posts });
462
+ return ctx.Response({ data: "value" });
261
463
  }
262
464
 
263
465
  export async function POST(ctx: ApiContext) {
264
- const body = validate(schema, ctx.req.body);
265
- const post = await createPost(body);
266
- return ctx.Response(post, 201);
466
+ return ctx.Response({ created: true }, 201);
267
467
  }
268
- ```
269
468
 
270
- ### Middleware
271
-
272
- ```tsx
273
- import type { RouteMiddleware } from "@lolyjs/core";
274
-
275
- export const requireAuth: RouteMiddleware = async (ctx, next) => {
276
- const user = await getUser(ctx.req);
277
- if (!user) {
278
- ctx.res.redirect("/login");
279
- return;
280
- }
281
- ctx.locals.user = user;
282
- await next();
283
- };
284
- ```
285
-
286
- ```tsx
287
- // app/dashboard/server.hook.ts
288
- import { requireAuth } from "../middleware/auth";
289
-
290
- export const middlewares = [requireAuth];
291
-
292
- export const getServerSideProps: ServerLoader = async (ctx) => {
293
- const user = ctx.locals.user; // Available from middleware
294
- return { props: { user } };
295
- };
469
+ export async function DELETE(ctx: ApiContext) {
470
+ return ctx.Response({ deleted: true }, 204);
471
+ }
296
472
  ```
297
473
 
298
- ### Cache Management
474
+ ### WebSocket Event Handler
299
475
 
300
476
  ```tsx
301
- import { revalidatePath, revalidate } from "@lolyjs/core/client-cache";
302
-
303
- // Revalidate a specific route (removes from cache)
304
- revalidatePath('/posts');
305
-
306
- // Revalidate with query params
307
- revalidatePath('/posts?page=2');
477
+ import type { WssContext } from "@lolyjs/core";
308
478
 
309
- // Revalidate current page and refresh components
310
- await revalidate();
479
+ export const events = [
480
+ {
481
+ name: "connection",
482
+ handler: (ctx: WssContext) => {
483
+ // Handle connection
484
+ },
485
+ },
486
+ {
487
+ name: "custom-event",
488
+ handler: (ctx: WssContext) => {
489
+ const { socket, data, actions } = ctx;
490
+
491
+ // Emit to all clients
492
+ actions.emit("response", { message: "Hello" });
493
+
494
+ // Broadcast to all except sender
495
+ actions.broadcast("notification", data);
496
+
497
+ // Emit to specific socket
498
+ actions.emitTo(socketId, "private", data);
499
+ },
500
+ },
501
+ ];
311
502
  ```
312
503
 
313
504
  ### Client Hooks
314
505
 
315
506
  ```tsx
316
507
  import { usePageProps } from "@lolyjs/core/hooks";
508
+ import { revalidate } from "@lolyjs/core/client-cache";
317
509
 
318
510
  export default function Page() {
319
511
  const { params, props } = usePageProps();
320
- // Automatically updates when revalidate() is called
512
+
513
+ const handleRefresh = async () => {
514
+ await revalidate(); // Refresh current page data
515
+ };
516
+
321
517
  return <div>{/* Your UI */}</div>;
322
518
  }
323
519
  ```
324
520
 
521
+ ### Components
522
+
523
+ ```tsx
524
+ import { Link } from "@lolyjs/core/components";
525
+
526
+ export default function Navigation() {
527
+ return (
528
+ <nav>
529
+ <Link href="/">Home</Link>
530
+ <Link href="/about">About</Link>
531
+ <Link href="/blog/[slug]" params={{ slug: "my-post" }}>
532
+ My Post
533
+ </Link>
534
+ </nav>
535
+ );
536
+ }
537
+ ```
538
+
325
539
  ---
326
540
 
327
541
  ## Configuration
328
542
 
329
- Create `loly.config.ts` in your project root:
543
+ ### Framework Configuration
544
+
545
+ Create `loly.config.ts` in your project root to configure the framework:
330
546
 
331
547
  ```tsx
332
- export const config = (env: string) => {
548
+ import { FrameworkConfig } from "@lolyjs/core";
549
+
550
+ export default {
551
+ directories: {
552
+ app: "app",
553
+ build: ".loly",
554
+ static: "public",
555
+ },
556
+ server: {
557
+ port: 3000,
558
+ host: "localhost",
559
+ },
560
+ routing: {
561
+ trailingSlash: "ignore",
562
+ caseSensitive: false,
563
+ basePath: "",
564
+ },
565
+ rendering: {
566
+ framework: "react",
567
+ streaming: true,
568
+ ssr: true,
569
+ ssg: true,
570
+ },
571
+ } satisfies FrameworkConfig;
572
+ ```
573
+
574
+ ### Server Configuration
575
+
576
+ Configure server settings (CORS, rate limiting, etc.) in `loly.config.ts` by exporting a `config` function:
577
+
578
+ ```tsx
579
+ // loly.config.ts
580
+ import { ServerConfig } from "@lolyjs/core";
581
+
582
+ export const config = (env: string): ServerConfig => {
333
583
  return {
334
- // Server
335
- server: {
336
- port: 3000,
337
- host: env === "production" ? "0.0.0.0" : "localhost",
338
- },
339
-
340
- // Security
341
- security: {
342
- contentSecurityPolicy: {
343
- directives: {
344
- defaultSrc: ["'self'"],
345
- scriptSrc: ["'self'"],
346
- styleSrc: ["'self'", "'unsafe-inline'"],
347
- },
348
- },
349
- },
350
-
351
- // Rate limiting
584
+ bodyLimit: "1mb",
585
+ corsOrigin: env === "production"
586
+ ? ["https://yourdomain.com"]
587
+ : "*",
352
588
  rateLimit: {
353
589
  windowMs: 15 * 60 * 1000,
354
- max: 100,
590
+ max: 1000,
355
591
  strictMax: 5,
356
592
  strictPatterns: ["/api/auth/**"],
357
593
  },
358
-
359
- // CORS
360
- corsOrigin: env === "production"
361
- ? ["https://yourdomain.com"]
362
- : true,
363
594
  };
364
595
  };
365
596
  ```
366
597
 
367
- ---
598
+ ### Server Initialization
368
599
 
369
- ## Project Structure
370
-
371
- ```
372
- your-app/
373
- ├── app/
374
- │ ├── layout.tsx # Root layout
375
- │ ├── page.tsx # Home page (/)
376
- │ ├── _not-found.tsx # Custom 404 (optional)
377
- │ ├── _error.tsx # Custom error (optional)
378
- │ ├── about/
379
- │ │ └── page.tsx # /about
380
- │ ├── blog/
381
- │ │ ├── layout.tsx # Blog layout
382
- │ │ └── [slug]/
383
- │ │ ├── page.tsx # /blog/:slug
384
- │ │ └── server.hook.ts # Server-side data
385
- │ └── api/
386
- │ └── posts/
387
- │ └── route.ts # /api/posts
388
- ├── bootstrap.tsx # Client entry
389
- ├── init.server.ts # Server init (optional)
390
- ├── loly.config.ts # Config (optional)
391
- └── package.json
392
- ```
393
-
394
- ---
395
-
396
- ## Advanced
397
-
398
- ### Layouts
399
-
400
- ```tsx
401
- // app/layout.tsx (Root)
402
- export default function RootLayout({ children }) {
403
- return (
404
- <div className="app">
405
- <nav>Navigation</nav>
406
- <main>{children}</main>
407
- <footer>Footer</footer>
408
- </div>
409
- );
410
- }
411
- ```
600
+ Create `init.server.ts` in your project root to initialize services when Express starts (database connections, external services, etc.):
412
601
 
413
602
  ```tsx
414
- // app/blog/layout.tsx (Nested)
415
- export default function BlogLayout({ children }) {
416
- return (
417
- <div className="blog">
418
- <aside>Sidebar</aside>
419
- <main>{children}</main>
420
- </div>
421
- );
422
- }
423
- ```
424
-
425
- ### Themes
426
-
427
- ```tsx
428
- import { ThemeProvider } from "@lolyjs/core/themes";
429
-
430
- export default function RootLayout({ children, theme }) {
431
- return (
432
- <ThemeProvider initialTheme={theme}>
433
- {children}
434
- </ThemeProvider>
435
- );
603
+ // init.server.ts
604
+ import { InitServerData } from "@lolyjs/core";
605
+
606
+ export async function init({
607
+ serverContext,
608
+ }: {
609
+ serverContext: InitServerData;
610
+ }) {
611
+ // Initialize database connection
612
+ await connectToDatabase();
613
+
614
+ // Setup external services
615
+ await setupExternalServices();
616
+
617
+ // Any other initialization logic
618
+ console.log("Server initialized successfully");
436
619
  }
437
620
  ```
438
621
 
439
- ### Validation & Sanitization
440
-
441
- ```tsx
442
- import { validate, safeValidate, sanitizeString } from "@lolyjs/core";
443
- import { z } from "zod";
622
+ **Note**: `init.server.ts` is for initializing your application services, not for configuring Loly Framework. Framework configuration goes in `loly.config.ts`.
444
623
 
445
- const schema = z.object({
446
- name: z.string().min(1).max(100),
447
- email: z.string().email(),
448
- });
449
-
450
- // Throw on error
451
- const data = validate(schema, req.body);
452
-
453
- // Return result object
454
- const result = safeValidate(schema, req.body);
455
- if (!result.success) {
456
- return Response({ errors: result.error }, 400);
457
- }
458
-
459
- // Sanitize strings
460
- const clean = sanitizeString(userInput);
461
- ```
624
+ ---
462
625
 
463
- ### Logging
626
+ ## CLI Commands
464
627
 
465
- ```tsx
466
- import { getRequestLogger, createModuleLogger } from "@lolyjs/core";
628
+ ```bash
629
+ # Development server
630
+ npx loly dev
467
631
 
468
- // In server hooks or API routes
469
- export const getServerSideProps: ServerLoader = async (ctx) => {
470
- const logger = getRequestLogger(ctx.req);
471
- logger.info("Processing request", { userId: ctx.locals.user?.id });
472
- return { props: {} };
473
- };
632
+ # Build for production
633
+ npx loly build
474
634
 
475
- // Module logger
476
- const logger = createModuleLogger("my-module");
477
- logger.debug("Debug message");
478
- logger.error("Error", error);
635
+ # Start production server
636
+ npx loly start
479
637
  ```
480
638
 
481
639
  ---
@@ -489,16 +647,11 @@ import type {
489
647
  ServerContext,
490
648
  ServerLoader,
491
649
  ApiContext,
650
+ WssContext,
492
651
  RouteMiddleware,
493
652
  ApiMiddleware,
653
+ GenerateStaticParams,
494
654
  } from "@lolyjs/core";
495
-
496
- // Fully typed server loader
497
- export const getServerSideProps: ServerLoader = async (ctx) => {
498
- // ctx is fully typed
499
- const { params, req, res, locals } = ctx;
500
- // ...
501
- };
502
655
  ```
503
656
 
504
657
  ---
@@ -514,41 +667,20 @@ npm run build
514
667
  This generates:
515
668
  - Client bundle (`.loly/client`)
516
669
  - Static pages if using SSG (`.loly/ssg`)
517
- - Server code
518
-
519
- ### Start
520
-
521
- ```bash
522
- npm run start
523
- ```
670
+ - Server code (`.loly/server`)
524
671
 
525
672
  ### Environment Variables
526
673
 
527
674
  ```bash
528
675
  PORT=3000
529
676
  HOST=0.0.0.0
530
- LOG_LEVEL=info
531
- LOG_REQUESTS=false
532
- ```
533
-
534
- ---
535
-
536
- ## CLI Commands
537
-
538
- ```bash
539
- # Development
540
- loly dev [--port 3000] [--appDir app]
541
-
542
- # Build
543
- loly build [--appDir app]
544
-
545
- # Start production server
546
- loly start [--port 3000] [--appDir app]
677
+ NODE_ENV=production
678
+ PUBLIC_WS_BASE_URL=http://localhost:3000
547
679
  ```
548
680
 
549
681
  ---
550
682
 
551
- ## All Exports
683
+ ## Exports
552
684
 
553
685
  ```tsx
554
686
  // Server
@@ -559,6 +691,7 @@ import type {
559
691
  ServerContext,
560
692
  ServerLoader,
561
693
  ApiContext,
694
+ WssContext,
562
695
  RouteMiddleware,
563
696
  ApiMiddleware,
564
697
  GenerateStaticParams,
@@ -569,16 +702,24 @@ import { validate, safeValidate, ValidationError } from "@lolyjs/core";
569
702
 
570
703
  // Security
571
704
  import { sanitizeString, sanitizeObject } from "@lolyjs/core";
572
- import { strictRateLimiter, lenientRateLimiter } from "@lolyjs/core";
705
+ import {
706
+ createRateLimiter,
707
+ defaultRateLimiter,
708
+ strictRateLimiter
709
+ } from "@lolyjs/core";
573
710
 
574
711
  // Logging
575
- import { logger, createModuleLogger, getRequestLogger } from "@lolyjs/core";
712
+ import {
713
+ logger,
714
+ createModuleLogger,
715
+ getRequestLogger
716
+ } from "@lolyjs/core";
576
717
 
577
718
  // Client
578
719
  import { Link } from "@lolyjs/core/components";
579
720
  import { usePageProps } from "@lolyjs/core/hooks";
580
- import { ThemeProvider } from "@lolyjs/core/themes";
581
- import { revalidatePath, revalidate } from "@lolyjs/core/client-cache";
721
+ import { lolySocket } from "@lolyjs/core/sockets";
722
+ import { revalidate, revalidatePath } from "@lolyjs/core/client-cache";
582
723
  ```
583
724
 
584
725
  ---
@@ -594,6 +735,7 @@ ISC
594
735
  - [React](https://react.dev/) - UI library
595
736
  - [Express](https://expressjs.com/) - Web framework
596
737
  - [Rspack](https://rspack.dev/) - Fast bundler
738
+ - [Socket.IO](https://socket.io/) - WebSocket library
597
739
  - [Pino](https://getpino.io/) - Fast logger
598
740
  - [Zod](https://zod.dev/) - Schema validation
599
741
  - [Helmet](https://helmetjs.github.io/) - Security headers