@lolyjs/core 0.2.0-alpha.16 → 0.2.0-alpha.17

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
@@ -101,33 +101,54 @@ npx loly dev
101
101
 
102
102
  ## Key Features
103
103
 
104
- ### 🔌 Native WebSocket Support
104
+ ### 🔌 Native WebSocket Support (Realtime v1)
105
105
 
106
- Loly includes built-in WebSocket support with automatic namespace routing. Define WebSocket events using the same file-based routing pattern as pages and APIs:
106
+ Loly includes production-ready WebSocket support with automatic namespace routing, authentication, validation, rate limiting, and multi-instance scaling. Define WebSocket events using the new `defineWssRoute()` API:
107
107
 
108
108
  ```tsx
109
109
  // app/wss/chat/events.ts
110
- import type { WssContext } from "@lolyjs/core";
110
+ import { defineWssRoute } from "@lolyjs/core";
111
+ import { z } from "zod";
111
112
 
112
- export const events = [
113
- {
114
- name: "connection",
115
- handler: (ctx: WssContext) => {
116
- console.log("Client connected:", ctx.socket.id);
117
- },
113
+ export default defineWssRoute({
114
+ // Authentication hook
115
+ auth: async (ctx) => {
116
+ const token = ctx.req.headers.authorization;
117
+ return await verifyToken(token); // Returns user or null
118
+ },
119
+
120
+ // Connection hook
121
+ onConnect: (ctx) => {
122
+ console.log("User connected:", ctx.user?.id);
118
123
  },
119
- {
120
- name: "message",
121
- handler: (ctx: WssContext) => {
122
- const { data, actions } = ctx;
123
- // Broadcast to all clients
124
- actions.broadcast("message", {
125
- text: data.text,
126
- from: ctx.socket.id,
127
- });
124
+
125
+ // Event handlers with validation, guards, and rate limiting
126
+ events: {
127
+ message: {
128
+ // Schema validation (Zod/Valibot)
129
+ schema: z.object({
130
+ text: z.string().min(1).max(500),
131
+ }),
132
+
133
+ // Guard (permissions check)
134
+ guard: ({ user }) => !!user, // Require authentication
135
+
136
+ // Per-event rate limiting
137
+ rateLimit: {
138
+ eventsPerSecond: 10,
139
+ burst: 20,
140
+ },
141
+
142
+ // Handler
143
+ handler: (ctx) => {
144
+ ctx.actions.broadcast("message", {
145
+ text: ctx.data.text,
146
+ from: ctx.user?.id,
147
+ });
148
+ },
128
149
  },
129
150
  },
130
- ];
151
+ });
131
152
  ```
132
153
 
133
154
  **Client-side:**
@@ -144,12 +165,19 @@ socket.on("message", (data) => {
144
165
  socket.emit("message", { text: "Hello!" });
145
166
  ```
146
167
 
147
- **Key Benefits:**
168
+ **Key Features:**
148
169
 
149
- - Automatic namespace creation from file structure
150
- - Same routing pattern as pages and APIs
151
- - Built-in broadcasting helpers (`emit`, `broadcast`, `emitTo`, `emitToClient`)
152
- - No manual configuration required
170
+ - **Production-ready**: Auth, validation, rate limiting, logging
171
+ - **Multi-instance**: Redis adapter for horizontal scaling
172
+ - **State Store**: Shared state across instances (memory/Redis)
173
+ - **Presence**: User-to-socket mapping for targeted messaging
174
+ - ✅ **Type-safe**: Full TypeScript support
175
+ - ✅ **Automatic namespace creation** from file structure
176
+ - ✅ **Same routing pattern** as pages and APIs
177
+ - ✅ **Built-in helpers**: `emit`, `broadcast`, `toUser()`, `toRoom()`, `join()`, `leave()`
178
+ - ✅ **No manual configuration required** (works out of the box for localhost)
179
+
180
+ **📖 For complete documentation, see [REALTIME.md](./docs/REALTIME.md)**
153
181
 
154
182
  ### 🎯 Route-Level Middlewares
155
183
 
@@ -747,34 +775,43 @@ export async function DELETE(ctx: ApiContext) {
747
775
  }
748
776
  ```
749
777
 
750
- ### WebSocket Event Handler
778
+ ### WebSocket Event Handler (New API - Realtime v1)
751
779
 
752
780
  ```tsx
753
- import type { WssContext } from "@lolyjs/core";
781
+ import { defineWssRoute } from "@lolyjs/core";
782
+ import { z } from "zod";
754
783
 
755
- export const events = [
756
- {
757
- name: "connection",
758
- handler: (ctx: WssContext) => {
759
- // Handle connection
760
- },
784
+ export default defineWssRoute({
785
+ auth: async (ctx) => {
786
+ // Authenticate user
787
+ return await getUserFromToken(ctx.req.headers.authorization);
761
788
  },
762
- {
763
- name: "custom-event",
764
- handler: (ctx: WssContext) => {
765
- const { socket, data, actions } = ctx;
766
789
 
767
- // Emit to all clients
768
- actions.emit("response", { message: "Hello" });
790
+ onConnect: (ctx) => {
791
+ console.log("User connected:", ctx.user?.id);
792
+ },
769
793
 
770
- // Broadcast to all except sender
771
- actions.broadcast("notification", data);
794
+ events: {
795
+ "custom-event": {
796
+ schema: z.object({ message: z.string() }),
797
+ guard: ({ user }) => !!user,
798
+ handler: (ctx) => {
799
+ // Emit to all clients
800
+ ctx.actions.emit("response", { message: "Hello" });
772
801
 
773
- // Emit to specific socket
774
- actions.emitTo(socketId, "private", data);
802
+ // Broadcast to all except sender
803
+ ctx.actions.broadcast("notification", ctx.data);
804
+
805
+ // Send to specific user
806
+ ctx.actions.toUser(userId).emit("private", ctx.data);
807
+
808
+ // Send to room
809
+ ctx.actions.toRoom("room-name").emit("room-message", ctx.data);
810
+ },
775
811
  },
776
812
  },
777
- ];
813
+ });
814
+ ```
778
815
  ```
779
816
 
780
817
  ### Client Cache
@@ -846,26 +883,43 @@ export default {
846
883
 
847
884
  ### Server Configuration
848
885
 
849
- Configure server settings (CORS, rate limiting, etc.) in `loly.config.ts` by exporting a `config` function:
886
+ Configure server settings (CORS, rate limiting, WebSocket, etc.) in `loly.config.ts` by exporting a `config` function:
850
887
 
851
888
  ```tsx
852
889
  // loly.config.ts
853
890
  import { ServerConfig } from "@lolyjs/core";
854
891
 
855
892
  export const config = (env: string): ServerConfig => {
893
+ const isDev = env === "development";
894
+
856
895
  return {
857
896
  bodyLimit: "1mb",
858
- corsOrigin: env === "production" ? ["https://yourdomain.com"] : "*",
897
+ corsOrigin: isDev ? "*" : ["https://yourdomain.com"],
859
898
  rateLimit: {
860
899
  windowMs: 15 * 60 * 1000,
861
900
  max: 1000,
862
901
  strictMax: 5,
863
902
  strictPatterns: ["/api/auth/**"],
864
903
  },
904
+ // Realtime (WebSocket) configuration
905
+ realtime: {
906
+ enabled: true,
907
+ // For production, configure allowed origins
908
+ // For development, localhost is auto-allowed
909
+ allowedOrigins: isDev ? undefined : ["https://yourdomain.com"],
910
+ // Optional: Configure Redis for multi-instance scaling
911
+ // scale: {
912
+ // mode: "cluster",
913
+ // adapter: { url: "redis://localhost:6379" },
914
+ // stateStore: { name: "redis", url: "redis://localhost:6379" },
915
+ // },
916
+ },
865
917
  };
866
918
  };
867
919
  ```
868
920
 
921
+ **Note:** For local development, Realtime works out of the box without any configuration. The framework automatically allows `localhost` connections. Only configure `allowedOrigins` when deploying to production.
922
+
869
923
  ### Server Initialization
870
924
 
871
925
  Create `init.server.ts` in your project root to initialize services when Express starts (database connections, external services, etc.):
@@ -947,9 +1001,13 @@ This generates:
947
1001
  PORT=3000
948
1002
  HOST=0.0.0.0
949
1003
  NODE_ENV=production
1004
+ # PUBLIC_WS_BASE_URL is optional - defaults to window.location.origin
1005
+ # Only set if WebSocket server is on a different domain
950
1006
  PUBLIC_WS_BASE_URL=http://localhost:3000
951
1007
  ```
952
1008
 
1009
+ **Note:** For WebSocket connections, `PUBLIC_WS_BASE_URL` is optional. By default, `lolySocket` uses `window.location.origin`, so you only need to set it if your WebSocket server is on a different domain than your web app.
1010
+
953
1011
  ---
954
1012
 
955
1013
  ## Exports