@lolyjs/core 0.2.0-alpha.16 → 0.2.0-alpha.18
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 +103 -45
- package/dist/cli.cjs +12744 -1856
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +12754 -1856
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +11658 -684
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +307 -3
- package/dist/index.d.ts +307 -3
- package/dist/index.js +11917 -934
- package/dist/index.js.map +1 -1
- package/dist/{index.types-BPX6IVAC.d.mts → index.types-DMOO-uvF.d.mts} +40 -17
- package/dist/{index.types-BPX6IVAC.d.ts → index.types-DMOO-uvF.d.ts} +40 -17
- package/dist/react/cache.d.mts +1 -2
- package/dist/react/cache.d.ts +1 -2
- package/dist/react/sockets.cjs +11 -10
- package/dist/react/sockets.cjs.map +1 -1
- package/dist/react/sockets.js +11 -10
- package/dist/react/sockets.js.map +1 -1
- package/package.json +6 -1
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
|
|
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
|
|
110
|
+
import { defineWssRoute } from "@lolyjs/core";
|
|
111
|
+
import { z } from "zod";
|
|
111
112
|
|
|
112
|
-
export
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
text:
|
|
126
|
-
|
|
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
|
|
168
|
+
**Key Features:**
|
|
148
169
|
|
|
149
|
-
-
|
|
150
|
-
-
|
|
151
|
-
-
|
|
152
|
-
-
|
|
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
|
|
781
|
+
import { defineWssRoute } from "@lolyjs/core";
|
|
782
|
+
import { z } from "zod";
|
|
754
783
|
|
|
755
|
-
export
|
|
756
|
-
{
|
|
757
|
-
|
|
758
|
-
|
|
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
|
-
|
|
768
|
-
|
|
790
|
+
onConnect: (ctx) => {
|
|
791
|
+
console.log("User connected:", ctx.user?.id);
|
|
792
|
+
},
|
|
769
793
|
|
|
770
|
-
|
|
771
|
-
|
|
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
|
-
|
|
774
|
-
|
|
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:
|
|
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
|