@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/dist/index.d.mts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import http from 'http';
|
|
2
|
-
export { a as ApiContext, A as ApiMiddleware, G as GenerateStaticParams, L as LoaderResult, M as MetadataLoader, R as RouteMiddleware, S as ServerContext, b as ServerLoader, W as
|
|
2
|
+
export { a as ApiContext, A as ApiMiddleware, G as GenerateStaticParams, L as LoaderResult, M as MetadataLoader, R as RouteMiddleware, S as ServerContext, b as ServerLoader, W as WssActions } from './index.types-DMOO-uvF.mjs';
|
|
3
|
+
import { Server, Socket } from 'socket.io';
|
|
3
4
|
export { c as bootstrapClient } from './bootstrap-BfGTMUkj.mjs';
|
|
4
5
|
import { ZodSchema, z } from 'zod';
|
|
5
6
|
import * as express_rate_limit from 'express-rate-limit';
|
|
6
7
|
import pino, { Logger as Logger$1 } from 'pino';
|
|
7
8
|
import { Request, Response } from 'express';
|
|
8
|
-
import 'socket.io';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Framework configuration interface.
|
|
@@ -121,6 +121,271 @@ interface InitServerData {
|
|
|
121
121
|
server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Authentication context provided to the auth hook.
|
|
126
|
+
* Contains request metadata needed for authentication.
|
|
127
|
+
*/
|
|
128
|
+
interface AuthContext {
|
|
129
|
+
/** Request headers */
|
|
130
|
+
req: {
|
|
131
|
+
headers: Record<string, string | string[] | undefined>;
|
|
132
|
+
ip?: string;
|
|
133
|
+
url?: string;
|
|
134
|
+
cookies?: Record<string, string>;
|
|
135
|
+
};
|
|
136
|
+
/** Socket.IO socket instance */
|
|
137
|
+
socket: Socket;
|
|
138
|
+
/** Namespace path (e.g., "/chat") */
|
|
139
|
+
namespace: string;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Rate limit configuration for events.
|
|
143
|
+
*/
|
|
144
|
+
interface RateLimitCfg {
|
|
145
|
+
/** Maximum events per second */
|
|
146
|
+
eventsPerSecond: number;
|
|
147
|
+
/** Burst capacity (optional, defaults to eventsPerSecond * 2) */
|
|
148
|
+
burst?: number;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Guard function type. Returns true to allow, false to block.
|
|
152
|
+
*/
|
|
153
|
+
type GuardFn<TUser = any> = (ctx: {
|
|
154
|
+
user: TUser | null;
|
|
155
|
+
req: AuthContext["req"];
|
|
156
|
+
socket: Socket;
|
|
157
|
+
namespace: string;
|
|
158
|
+
}) => boolean | Promise<boolean>;
|
|
159
|
+
/**
|
|
160
|
+
* Authentication function type.
|
|
161
|
+
* Returns the authenticated user or null.
|
|
162
|
+
*/
|
|
163
|
+
type AuthFn<TUser = any> = (ctx: AuthContext) => TUser | null | Promise<TUser | null>;
|
|
164
|
+
/**
|
|
165
|
+
* Schema validation adapter.
|
|
166
|
+
* Must support Zod-like interface: schema.parse(data) or schema.safeParse(data)
|
|
167
|
+
*/
|
|
168
|
+
type Schema = {
|
|
169
|
+
parse?: (data: any) => any;
|
|
170
|
+
safeParse?: (data: any) => {
|
|
171
|
+
success: boolean;
|
|
172
|
+
error?: any;
|
|
173
|
+
data?: any;
|
|
174
|
+
};
|
|
175
|
+
} & Record<string, any>;
|
|
176
|
+
/**
|
|
177
|
+
* Realtime state store interface.
|
|
178
|
+
* Provides key-value storage, lists, sets, and atomic operations.
|
|
179
|
+
*/
|
|
180
|
+
interface RealtimeStateStore {
|
|
181
|
+
/** Get a value by key */
|
|
182
|
+
get<T = any>(key: string): Promise<T | null>;
|
|
183
|
+
/** Set a value with optional TTL */
|
|
184
|
+
set<T = any>(key: string, value: T, opts?: {
|
|
185
|
+
ttlMs?: number;
|
|
186
|
+
}): Promise<void>;
|
|
187
|
+
/** Delete a key */
|
|
188
|
+
del(key: string): Promise<void>;
|
|
189
|
+
/** Increment a numeric value */
|
|
190
|
+
incr(key: string, by?: number): Promise<number>;
|
|
191
|
+
/** Decrement a numeric value */
|
|
192
|
+
decr(key: string, by?: number): Promise<number>;
|
|
193
|
+
/** Push to a list (left push) */
|
|
194
|
+
listPush(key: string, value: any, opts?: {
|
|
195
|
+
maxLen?: number;
|
|
196
|
+
}): Promise<void>;
|
|
197
|
+
/** Get range from a list */
|
|
198
|
+
listRange<T = any>(key: string, start: number, end: number): Promise<T[]>;
|
|
199
|
+
/** Add member to a set */
|
|
200
|
+
setAdd(key: string, member: string): Promise<void>;
|
|
201
|
+
/** Remove member from a set */
|
|
202
|
+
setRem(key: string, member: string): Promise<void>;
|
|
203
|
+
/** Get all members of a set */
|
|
204
|
+
setMembers(key: string): Promise<string[]>;
|
|
205
|
+
/** Optional: Acquire a distributed lock (returns unlock function) */
|
|
206
|
+
lock?(key: string, ttlMs: number): Promise<() => Promise<void>>;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Logger interface for realtime events.
|
|
210
|
+
*/
|
|
211
|
+
interface RealtimeLogger {
|
|
212
|
+
debug(message: string, meta?: Record<string, any>): void;
|
|
213
|
+
info(message: string, meta?: Record<string, any>): void;
|
|
214
|
+
warn(message: string, meta?: Record<string, any>): void;
|
|
215
|
+
error(message: string, meta?: Record<string, any>): void;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Metrics interface (optional for v1).
|
|
219
|
+
*/
|
|
220
|
+
interface RealtimeMetrics {
|
|
221
|
+
incrementCounter(name: string, labels?: Record<string, string>): void;
|
|
222
|
+
recordLatency(name: string, ms: number, labels?: Record<string, string>): void;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Extended WssActions with full RFC support.
|
|
226
|
+
*/
|
|
227
|
+
interface WssActions {
|
|
228
|
+
/**
|
|
229
|
+
* Emit to current socket only (reply)
|
|
230
|
+
*/
|
|
231
|
+
reply(event: string, payload?: any): void;
|
|
232
|
+
/**
|
|
233
|
+
* Emit to all sockets in current namespace
|
|
234
|
+
*/
|
|
235
|
+
emit(event: string, payload?: any): void;
|
|
236
|
+
/**
|
|
237
|
+
* Emit to everyone except current socket
|
|
238
|
+
*/
|
|
239
|
+
broadcast(event: string, payload?: any, opts?: {
|
|
240
|
+
excludeSelf?: boolean;
|
|
241
|
+
}): void;
|
|
242
|
+
/**
|
|
243
|
+
* Join a room
|
|
244
|
+
*/
|
|
245
|
+
join(room: string): Promise<void>;
|
|
246
|
+
/**
|
|
247
|
+
* Leave a room
|
|
248
|
+
*/
|
|
249
|
+
leave(room: string): Promise<void>;
|
|
250
|
+
/**
|
|
251
|
+
* Emit to a specific room
|
|
252
|
+
*/
|
|
253
|
+
toRoom(room: string): {
|
|
254
|
+
emit(event: string, payload?: any): void;
|
|
255
|
+
};
|
|
256
|
+
/**
|
|
257
|
+
* Emit to a specific user (by userId)
|
|
258
|
+
* Uses presence mapping to find user's sockets
|
|
259
|
+
*/
|
|
260
|
+
toUser(userId: string): {
|
|
261
|
+
emit(event: string, payload?: any): void;
|
|
262
|
+
};
|
|
263
|
+
/**
|
|
264
|
+
* Emit error event (reserved event: __loly:error)
|
|
265
|
+
*/
|
|
266
|
+
error(code: string, message: string, details?: any): void;
|
|
267
|
+
/**
|
|
268
|
+
* Legacy: Emit to a specific socket by Socket.IO socket ID
|
|
269
|
+
* @deprecated Use toUser() for user targeting
|
|
270
|
+
*/
|
|
271
|
+
emitTo?: (socketId: string, event: string, ...args: any[]) => void;
|
|
272
|
+
/**
|
|
273
|
+
* Legacy: Emit to a specific client by custom clientId
|
|
274
|
+
* @deprecated Use toUser() for user targeting
|
|
275
|
+
*/
|
|
276
|
+
emitToClient?: (clientId: string, event: string, ...args: any[]) => void;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Extended WssContext with full RFC support.
|
|
280
|
+
*/
|
|
281
|
+
interface WssContext<TData = any, TUser = any> {
|
|
282
|
+
/** Socket.IO server instance */
|
|
283
|
+
io: Server;
|
|
284
|
+
/** Socket.IO socket instance */
|
|
285
|
+
socket: Socket;
|
|
286
|
+
/** Request metadata */
|
|
287
|
+
req: {
|
|
288
|
+
headers: Record<string, string | string[] | undefined>;
|
|
289
|
+
ip?: string;
|
|
290
|
+
url?: string;
|
|
291
|
+
cookies?: Record<string, string>;
|
|
292
|
+
};
|
|
293
|
+
/** Authenticated user (set by auth hook) */
|
|
294
|
+
user: TUser | null;
|
|
295
|
+
/** Incoming payload for current event */
|
|
296
|
+
data: TData;
|
|
297
|
+
/** Route params (from dynamic routes) */
|
|
298
|
+
params: Record<string, string>;
|
|
299
|
+
/** Route pathname */
|
|
300
|
+
pathname: string;
|
|
301
|
+
/** Framework utilities */
|
|
302
|
+
actions: WssActions;
|
|
303
|
+
/** State store for shared state */
|
|
304
|
+
state: RealtimeStateStore;
|
|
305
|
+
/** Logger with WSS context */
|
|
306
|
+
log: RealtimeLogger;
|
|
307
|
+
/** Metrics (optional) */
|
|
308
|
+
metrics?: RealtimeMetrics;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* WSS event handler type.
|
|
312
|
+
*/
|
|
313
|
+
type WssHandler<TData = any, TUser = any> = (ctx: WssContext<TData, TUser>) => void | Promise<void>;
|
|
314
|
+
/**
|
|
315
|
+
* WSS event definition.
|
|
316
|
+
* Can be a simple handler or an object with validation, guards, and rate limiting.
|
|
317
|
+
*/
|
|
318
|
+
type WssEventDefinition<TData = any, TUser = any> = WssHandler<TData, TUser> | {
|
|
319
|
+
/** Schema for validation (Zod/Valibot compatible) */
|
|
320
|
+
schema?: Schema;
|
|
321
|
+
/** Per-event rate limit config */
|
|
322
|
+
rateLimit?: RateLimitCfg;
|
|
323
|
+
/** Guard function (auth/roles/permissions) */
|
|
324
|
+
guard?: GuardFn<TUser>;
|
|
325
|
+
/** Event handler */
|
|
326
|
+
handler: WssHandler<TData, TUser>;
|
|
327
|
+
};
|
|
328
|
+
/**
|
|
329
|
+
* WSS route definition (result of defineWssRoute).
|
|
330
|
+
*/
|
|
331
|
+
interface WssRouteDefinition<TUser = any> {
|
|
332
|
+
/** Namespace (optional, inferred from folder name) */
|
|
333
|
+
namespace?: string;
|
|
334
|
+
/** Authentication hook */
|
|
335
|
+
auth?: AuthFn<TUser>;
|
|
336
|
+
/** Connection hook */
|
|
337
|
+
onConnect?: WssHandler<any, TUser>;
|
|
338
|
+
/** Disconnection hook */
|
|
339
|
+
onDisconnect?: (ctx: WssContext<any, TUser>, reason?: string) => void | Promise<void>;
|
|
340
|
+
/** Event handlers */
|
|
341
|
+
events: Record<string, WssEventDefinition<any, TUser>>;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Realtime/WebSocket configuration
|
|
346
|
+
*/
|
|
347
|
+
interface RealtimeConfig {
|
|
348
|
+
/** Enable realtime features */
|
|
349
|
+
enabled?: boolean;
|
|
350
|
+
/** Socket.IO server settings */
|
|
351
|
+
path?: string;
|
|
352
|
+
transports?: ("websocket" | "polling")[];
|
|
353
|
+
pingIntervalMs?: number;
|
|
354
|
+
pingTimeoutMs?: number;
|
|
355
|
+
maxPayloadBytes?: number;
|
|
356
|
+
/** Security */
|
|
357
|
+
allowedOrigins?: string | string[];
|
|
358
|
+
cors?: {
|
|
359
|
+
credentials?: boolean;
|
|
360
|
+
allowedHeaders?: string[];
|
|
361
|
+
};
|
|
362
|
+
/** Scaling configuration */
|
|
363
|
+
scale?: {
|
|
364
|
+
mode?: "single" | "cluster";
|
|
365
|
+
adapter?: {
|
|
366
|
+
name: "redis";
|
|
367
|
+
url: string;
|
|
368
|
+
pubClientName?: string;
|
|
369
|
+
subClientName?: string;
|
|
370
|
+
};
|
|
371
|
+
stateStore?: {
|
|
372
|
+
name: "memory" | "redis";
|
|
373
|
+
url?: string;
|
|
374
|
+
prefix?: string;
|
|
375
|
+
};
|
|
376
|
+
};
|
|
377
|
+
/** Rate limiting */
|
|
378
|
+
limits?: {
|
|
379
|
+
connectionsPerIp?: number;
|
|
380
|
+
eventsPerSecond?: number;
|
|
381
|
+
burst?: number;
|
|
382
|
+
};
|
|
383
|
+
/** Logging */
|
|
384
|
+
logging?: {
|
|
385
|
+
level?: "debug" | "info" | "warn" | "error";
|
|
386
|
+
pretty?: boolean;
|
|
387
|
+
};
|
|
388
|
+
}
|
|
124
389
|
interface ServerConfig {
|
|
125
390
|
bodyLimit?: string;
|
|
126
391
|
corsOrigin?: string | string[] | boolean | ((origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void) => void);
|
|
@@ -138,6 +403,8 @@ interface ServerConfig {
|
|
|
138
403
|
includeSubDomains?: boolean;
|
|
139
404
|
};
|
|
140
405
|
};
|
|
406
|
+
/** Realtime/WebSocket configuration */
|
|
407
|
+
realtime?: RealtimeConfig;
|
|
141
408
|
}
|
|
142
409
|
|
|
143
410
|
interface BuildAppOptions {
|
|
@@ -147,6 +414,43 @@ interface BuildAppOptions {
|
|
|
147
414
|
}
|
|
148
415
|
declare function buildApp(options?: BuildAppOptions): Promise<void>;
|
|
149
416
|
|
|
417
|
+
/**
|
|
418
|
+
* Defines a WebSocket route with authentication, hooks, and event handlers.
|
|
419
|
+
*
|
|
420
|
+
* This is the new, recommended way to define WSS routes. It provides:
|
|
421
|
+
* - Type safety
|
|
422
|
+
* - Built-in validation
|
|
423
|
+
* - Auth hooks
|
|
424
|
+
* - Connection/disconnection hooks
|
|
425
|
+
* - Per-event validation, guards, and rate limiting
|
|
426
|
+
*
|
|
427
|
+
* @example
|
|
428
|
+
* ```ts
|
|
429
|
+
* import { defineWssRoute } from "@lolyjs/core";
|
|
430
|
+
* import { z } from "zod";
|
|
431
|
+
*
|
|
432
|
+
* export default defineWssRoute({
|
|
433
|
+
* auth: async (ctx) => {
|
|
434
|
+
* const token = ctx.req.headers.authorization;
|
|
435
|
+
* return await verifyToken(token);
|
|
436
|
+
* },
|
|
437
|
+
* onConnect: (ctx) => {
|
|
438
|
+
* console.log("User connected:", ctx.user?.id);
|
|
439
|
+
* },
|
|
440
|
+
* events: {
|
|
441
|
+
* message: {
|
|
442
|
+
* schema: z.object({ text: z.string() }),
|
|
443
|
+
* guard: ({ user }) => !!user,
|
|
444
|
+
* handler: (ctx) => {
|
|
445
|
+
* ctx.actions.broadcast("message", ctx.data);
|
|
446
|
+
* }
|
|
447
|
+
* }
|
|
448
|
+
* }
|
|
449
|
+
* });
|
|
450
|
+
* ```
|
|
451
|
+
*/
|
|
452
|
+
declare function defineWssRoute<TUser = any>(definition: WssRouteDefinition<TUser>): WssRouteDefinition<TUser>;
|
|
453
|
+
|
|
150
454
|
declare function withCache(fn: any, options: any): any;
|
|
151
455
|
|
|
152
456
|
/**
|
|
@@ -376,4 +680,4 @@ declare function requestLoggerMiddleware(options?: {
|
|
|
376
680
|
*/
|
|
377
681
|
declare function getRequestLogger(req: Request): Logger;
|
|
378
682
|
|
|
379
|
-
export { DEFAULT_CONFIG, type FrameworkConfig, type InitServerData, type LogLevel, Logger, type LoggerContext, type LoggerOptions, type ServerConfig, ValidationError, buildApp, commonSchemas, createModuleLogger, createRateLimiter, defaultRateLimiter, generateRequestId, getAppDir, getBuildDir, getLogger, getRequestLogger, getStaticDir, lenientRateLimiter, loadConfig, logger, requestLoggerMiddleware, resetLogger, safeValidate, sanitizeObject, sanitizeParams, sanitizeQuery, sanitizeString, setLogger, startDevServer, startProdServer, strictRateLimiter, validate, withCache };
|
|
683
|
+
export { type AuthContext, type AuthFn, DEFAULT_CONFIG, type FrameworkConfig, type GuardFn, type InitServerData, type LogLevel, Logger, type LoggerContext, type LoggerOptions, type RateLimitCfg, type RealtimeConfig, type RealtimeLogger, type RealtimeMetrics, type RealtimeStateStore, type Schema, type ServerConfig, ValidationError, type WssContext, type WssEventDefinition, type WssHandler, type WssRouteDefinition, buildApp, commonSchemas, createModuleLogger, createRateLimiter, defaultRateLimiter, defineWssRoute, generateRequestId, getAppDir, getBuildDir, getLogger, getRequestLogger, getStaticDir, lenientRateLimiter, loadConfig, logger, requestLoggerMiddleware, resetLogger, safeValidate, sanitizeObject, sanitizeParams, sanitizeQuery, sanitizeString, setLogger, startDevServer, startProdServer, strictRateLimiter, validate, withCache };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import http from 'http';
|
|
2
|
-
export { a as ApiContext, A as ApiMiddleware, G as GenerateStaticParams, L as LoaderResult, M as MetadataLoader, R as RouteMiddleware, S as ServerContext, b as ServerLoader, W as
|
|
2
|
+
export { a as ApiContext, A as ApiMiddleware, G as GenerateStaticParams, L as LoaderResult, M as MetadataLoader, R as RouteMiddleware, S as ServerContext, b as ServerLoader, W as WssActions } from './index.types-DMOO-uvF.js';
|
|
3
|
+
import { Server, Socket } from 'socket.io';
|
|
3
4
|
export { c as bootstrapClient } from './bootstrap-BfGTMUkj.js';
|
|
4
5
|
import { ZodSchema, z } from 'zod';
|
|
5
6
|
import * as express_rate_limit from 'express-rate-limit';
|
|
6
7
|
import pino, { Logger as Logger$1 } from 'pino';
|
|
7
8
|
import { Request, Response } from 'express';
|
|
8
|
-
import 'socket.io';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Framework configuration interface.
|
|
@@ -121,6 +121,271 @@ interface InitServerData {
|
|
|
121
121
|
server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Authentication context provided to the auth hook.
|
|
126
|
+
* Contains request metadata needed for authentication.
|
|
127
|
+
*/
|
|
128
|
+
interface AuthContext {
|
|
129
|
+
/** Request headers */
|
|
130
|
+
req: {
|
|
131
|
+
headers: Record<string, string | string[] | undefined>;
|
|
132
|
+
ip?: string;
|
|
133
|
+
url?: string;
|
|
134
|
+
cookies?: Record<string, string>;
|
|
135
|
+
};
|
|
136
|
+
/** Socket.IO socket instance */
|
|
137
|
+
socket: Socket;
|
|
138
|
+
/** Namespace path (e.g., "/chat") */
|
|
139
|
+
namespace: string;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Rate limit configuration for events.
|
|
143
|
+
*/
|
|
144
|
+
interface RateLimitCfg {
|
|
145
|
+
/** Maximum events per second */
|
|
146
|
+
eventsPerSecond: number;
|
|
147
|
+
/** Burst capacity (optional, defaults to eventsPerSecond * 2) */
|
|
148
|
+
burst?: number;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Guard function type. Returns true to allow, false to block.
|
|
152
|
+
*/
|
|
153
|
+
type GuardFn<TUser = any> = (ctx: {
|
|
154
|
+
user: TUser | null;
|
|
155
|
+
req: AuthContext["req"];
|
|
156
|
+
socket: Socket;
|
|
157
|
+
namespace: string;
|
|
158
|
+
}) => boolean | Promise<boolean>;
|
|
159
|
+
/**
|
|
160
|
+
* Authentication function type.
|
|
161
|
+
* Returns the authenticated user or null.
|
|
162
|
+
*/
|
|
163
|
+
type AuthFn<TUser = any> = (ctx: AuthContext) => TUser | null | Promise<TUser | null>;
|
|
164
|
+
/**
|
|
165
|
+
* Schema validation adapter.
|
|
166
|
+
* Must support Zod-like interface: schema.parse(data) or schema.safeParse(data)
|
|
167
|
+
*/
|
|
168
|
+
type Schema = {
|
|
169
|
+
parse?: (data: any) => any;
|
|
170
|
+
safeParse?: (data: any) => {
|
|
171
|
+
success: boolean;
|
|
172
|
+
error?: any;
|
|
173
|
+
data?: any;
|
|
174
|
+
};
|
|
175
|
+
} & Record<string, any>;
|
|
176
|
+
/**
|
|
177
|
+
* Realtime state store interface.
|
|
178
|
+
* Provides key-value storage, lists, sets, and atomic operations.
|
|
179
|
+
*/
|
|
180
|
+
interface RealtimeStateStore {
|
|
181
|
+
/** Get a value by key */
|
|
182
|
+
get<T = any>(key: string): Promise<T | null>;
|
|
183
|
+
/** Set a value with optional TTL */
|
|
184
|
+
set<T = any>(key: string, value: T, opts?: {
|
|
185
|
+
ttlMs?: number;
|
|
186
|
+
}): Promise<void>;
|
|
187
|
+
/** Delete a key */
|
|
188
|
+
del(key: string): Promise<void>;
|
|
189
|
+
/** Increment a numeric value */
|
|
190
|
+
incr(key: string, by?: number): Promise<number>;
|
|
191
|
+
/** Decrement a numeric value */
|
|
192
|
+
decr(key: string, by?: number): Promise<number>;
|
|
193
|
+
/** Push to a list (left push) */
|
|
194
|
+
listPush(key: string, value: any, opts?: {
|
|
195
|
+
maxLen?: number;
|
|
196
|
+
}): Promise<void>;
|
|
197
|
+
/** Get range from a list */
|
|
198
|
+
listRange<T = any>(key: string, start: number, end: number): Promise<T[]>;
|
|
199
|
+
/** Add member to a set */
|
|
200
|
+
setAdd(key: string, member: string): Promise<void>;
|
|
201
|
+
/** Remove member from a set */
|
|
202
|
+
setRem(key: string, member: string): Promise<void>;
|
|
203
|
+
/** Get all members of a set */
|
|
204
|
+
setMembers(key: string): Promise<string[]>;
|
|
205
|
+
/** Optional: Acquire a distributed lock (returns unlock function) */
|
|
206
|
+
lock?(key: string, ttlMs: number): Promise<() => Promise<void>>;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Logger interface for realtime events.
|
|
210
|
+
*/
|
|
211
|
+
interface RealtimeLogger {
|
|
212
|
+
debug(message: string, meta?: Record<string, any>): void;
|
|
213
|
+
info(message: string, meta?: Record<string, any>): void;
|
|
214
|
+
warn(message: string, meta?: Record<string, any>): void;
|
|
215
|
+
error(message: string, meta?: Record<string, any>): void;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Metrics interface (optional for v1).
|
|
219
|
+
*/
|
|
220
|
+
interface RealtimeMetrics {
|
|
221
|
+
incrementCounter(name: string, labels?: Record<string, string>): void;
|
|
222
|
+
recordLatency(name: string, ms: number, labels?: Record<string, string>): void;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Extended WssActions with full RFC support.
|
|
226
|
+
*/
|
|
227
|
+
interface WssActions {
|
|
228
|
+
/**
|
|
229
|
+
* Emit to current socket only (reply)
|
|
230
|
+
*/
|
|
231
|
+
reply(event: string, payload?: any): void;
|
|
232
|
+
/**
|
|
233
|
+
* Emit to all sockets in current namespace
|
|
234
|
+
*/
|
|
235
|
+
emit(event: string, payload?: any): void;
|
|
236
|
+
/**
|
|
237
|
+
* Emit to everyone except current socket
|
|
238
|
+
*/
|
|
239
|
+
broadcast(event: string, payload?: any, opts?: {
|
|
240
|
+
excludeSelf?: boolean;
|
|
241
|
+
}): void;
|
|
242
|
+
/**
|
|
243
|
+
* Join a room
|
|
244
|
+
*/
|
|
245
|
+
join(room: string): Promise<void>;
|
|
246
|
+
/**
|
|
247
|
+
* Leave a room
|
|
248
|
+
*/
|
|
249
|
+
leave(room: string): Promise<void>;
|
|
250
|
+
/**
|
|
251
|
+
* Emit to a specific room
|
|
252
|
+
*/
|
|
253
|
+
toRoom(room: string): {
|
|
254
|
+
emit(event: string, payload?: any): void;
|
|
255
|
+
};
|
|
256
|
+
/**
|
|
257
|
+
* Emit to a specific user (by userId)
|
|
258
|
+
* Uses presence mapping to find user's sockets
|
|
259
|
+
*/
|
|
260
|
+
toUser(userId: string): {
|
|
261
|
+
emit(event: string, payload?: any): void;
|
|
262
|
+
};
|
|
263
|
+
/**
|
|
264
|
+
* Emit error event (reserved event: __loly:error)
|
|
265
|
+
*/
|
|
266
|
+
error(code: string, message: string, details?: any): void;
|
|
267
|
+
/**
|
|
268
|
+
* Legacy: Emit to a specific socket by Socket.IO socket ID
|
|
269
|
+
* @deprecated Use toUser() for user targeting
|
|
270
|
+
*/
|
|
271
|
+
emitTo?: (socketId: string, event: string, ...args: any[]) => void;
|
|
272
|
+
/**
|
|
273
|
+
* Legacy: Emit to a specific client by custom clientId
|
|
274
|
+
* @deprecated Use toUser() for user targeting
|
|
275
|
+
*/
|
|
276
|
+
emitToClient?: (clientId: string, event: string, ...args: any[]) => void;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Extended WssContext with full RFC support.
|
|
280
|
+
*/
|
|
281
|
+
interface WssContext<TData = any, TUser = any> {
|
|
282
|
+
/** Socket.IO server instance */
|
|
283
|
+
io: Server;
|
|
284
|
+
/** Socket.IO socket instance */
|
|
285
|
+
socket: Socket;
|
|
286
|
+
/** Request metadata */
|
|
287
|
+
req: {
|
|
288
|
+
headers: Record<string, string | string[] | undefined>;
|
|
289
|
+
ip?: string;
|
|
290
|
+
url?: string;
|
|
291
|
+
cookies?: Record<string, string>;
|
|
292
|
+
};
|
|
293
|
+
/** Authenticated user (set by auth hook) */
|
|
294
|
+
user: TUser | null;
|
|
295
|
+
/** Incoming payload for current event */
|
|
296
|
+
data: TData;
|
|
297
|
+
/** Route params (from dynamic routes) */
|
|
298
|
+
params: Record<string, string>;
|
|
299
|
+
/** Route pathname */
|
|
300
|
+
pathname: string;
|
|
301
|
+
/** Framework utilities */
|
|
302
|
+
actions: WssActions;
|
|
303
|
+
/** State store for shared state */
|
|
304
|
+
state: RealtimeStateStore;
|
|
305
|
+
/** Logger with WSS context */
|
|
306
|
+
log: RealtimeLogger;
|
|
307
|
+
/** Metrics (optional) */
|
|
308
|
+
metrics?: RealtimeMetrics;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* WSS event handler type.
|
|
312
|
+
*/
|
|
313
|
+
type WssHandler<TData = any, TUser = any> = (ctx: WssContext<TData, TUser>) => void | Promise<void>;
|
|
314
|
+
/**
|
|
315
|
+
* WSS event definition.
|
|
316
|
+
* Can be a simple handler or an object with validation, guards, and rate limiting.
|
|
317
|
+
*/
|
|
318
|
+
type WssEventDefinition<TData = any, TUser = any> = WssHandler<TData, TUser> | {
|
|
319
|
+
/** Schema for validation (Zod/Valibot compatible) */
|
|
320
|
+
schema?: Schema;
|
|
321
|
+
/** Per-event rate limit config */
|
|
322
|
+
rateLimit?: RateLimitCfg;
|
|
323
|
+
/** Guard function (auth/roles/permissions) */
|
|
324
|
+
guard?: GuardFn<TUser>;
|
|
325
|
+
/** Event handler */
|
|
326
|
+
handler: WssHandler<TData, TUser>;
|
|
327
|
+
};
|
|
328
|
+
/**
|
|
329
|
+
* WSS route definition (result of defineWssRoute).
|
|
330
|
+
*/
|
|
331
|
+
interface WssRouteDefinition<TUser = any> {
|
|
332
|
+
/** Namespace (optional, inferred from folder name) */
|
|
333
|
+
namespace?: string;
|
|
334
|
+
/** Authentication hook */
|
|
335
|
+
auth?: AuthFn<TUser>;
|
|
336
|
+
/** Connection hook */
|
|
337
|
+
onConnect?: WssHandler<any, TUser>;
|
|
338
|
+
/** Disconnection hook */
|
|
339
|
+
onDisconnect?: (ctx: WssContext<any, TUser>, reason?: string) => void | Promise<void>;
|
|
340
|
+
/** Event handlers */
|
|
341
|
+
events: Record<string, WssEventDefinition<any, TUser>>;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Realtime/WebSocket configuration
|
|
346
|
+
*/
|
|
347
|
+
interface RealtimeConfig {
|
|
348
|
+
/** Enable realtime features */
|
|
349
|
+
enabled?: boolean;
|
|
350
|
+
/** Socket.IO server settings */
|
|
351
|
+
path?: string;
|
|
352
|
+
transports?: ("websocket" | "polling")[];
|
|
353
|
+
pingIntervalMs?: number;
|
|
354
|
+
pingTimeoutMs?: number;
|
|
355
|
+
maxPayloadBytes?: number;
|
|
356
|
+
/** Security */
|
|
357
|
+
allowedOrigins?: string | string[];
|
|
358
|
+
cors?: {
|
|
359
|
+
credentials?: boolean;
|
|
360
|
+
allowedHeaders?: string[];
|
|
361
|
+
};
|
|
362
|
+
/** Scaling configuration */
|
|
363
|
+
scale?: {
|
|
364
|
+
mode?: "single" | "cluster";
|
|
365
|
+
adapter?: {
|
|
366
|
+
name: "redis";
|
|
367
|
+
url: string;
|
|
368
|
+
pubClientName?: string;
|
|
369
|
+
subClientName?: string;
|
|
370
|
+
};
|
|
371
|
+
stateStore?: {
|
|
372
|
+
name: "memory" | "redis";
|
|
373
|
+
url?: string;
|
|
374
|
+
prefix?: string;
|
|
375
|
+
};
|
|
376
|
+
};
|
|
377
|
+
/** Rate limiting */
|
|
378
|
+
limits?: {
|
|
379
|
+
connectionsPerIp?: number;
|
|
380
|
+
eventsPerSecond?: number;
|
|
381
|
+
burst?: number;
|
|
382
|
+
};
|
|
383
|
+
/** Logging */
|
|
384
|
+
logging?: {
|
|
385
|
+
level?: "debug" | "info" | "warn" | "error";
|
|
386
|
+
pretty?: boolean;
|
|
387
|
+
};
|
|
388
|
+
}
|
|
124
389
|
interface ServerConfig {
|
|
125
390
|
bodyLimit?: string;
|
|
126
391
|
corsOrigin?: string | string[] | boolean | ((origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void) => void);
|
|
@@ -138,6 +403,8 @@ interface ServerConfig {
|
|
|
138
403
|
includeSubDomains?: boolean;
|
|
139
404
|
};
|
|
140
405
|
};
|
|
406
|
+
/** Realtime/WebSocket configuration */
|
|
407
|
+
realtime?: RealtimeConfig;
|
|
141
408
|
}
|
|
142
409
|
|
|
143
410
|
interface BuildAppOptions {
|
|
@@ -147,6 +414,43 @@ interface BuildAppOptions {
|
|
|
147
414
|
}
|
|
148
415
|
declare function buildApp(options?: BuildAppOptions): Promise<void>;
|
|
149
416
|
|
|
417
|
+
/**
|
|
418
|
+
* Defines a WebSocket route with authentication, hooks, and event handlers.
|
|
419
|
+
*
|
|
420
|
+
* This is the new, recommended way to define WSS routes. It provides:
|
|
421
|
+
* - Type safety
|
|
422
|
+
* - Built-in validation
|
|
423
|
+
* - Auth hooks
|
|
424
|
+
* - Connection/disconnection hooks
|
|
425
|
+
* - Per-event validation, guards, and rate limiting
|
|
426
|
+
*
|
|
427
|
+
* @example
|
|
428
|
+
* ```ts
|
|
429
|
+
* import { defineWssRoute } from "@lolyjs/core";
|
|
430
|
+
* import { z } from "zod";
|
|
431
|
+
*
|
|
432
|
+
* export default defineWssRoute({
|
|
433
|
+
* auth: async (ctx) => {
|
|
434
|
+
* const token = ctx.req.headers.authorization;
|
|
435
|
+
* return await verifyToken(token);
|
|
436
|
+
* },
|
|
437
|
+
* onConnect: (ctx) => {
|
|
438
|
+
* console.log("User connected:", ctx.user?.id);
|
|
439
|
+
* },
|
|
440
|
+
* events: {
|
|
441
|
+
* message: {
|
|
442
|
+
* schema: z.object({ text: z.string() }),
|
|
443
|
+
* guard: ({ user }) => !!user,
|
|
444
|
+
* handler: (ctx) => {
|
|
445
|
+
* ctx.actions.broadcast("message", ctx.data);
|
|
446
|
+
* }
|
|
447
|
+
* }
|
|
448
|
+
* }
|
|
449
|
+
* });
|
|
450
|
+
* ```
|
|
451
|
+
*/
|
|
452
|
+
declare function defineWssRoute<TUser = any>(definition: WssRouteDefinition<TUser>): WssRouteDefinition<TUser>;
|
|
453
|
+
|
|
150
454
|
declare function withCache(fn: any, options: any): any;
|
|
151
455
|
|
|
152
456
|
/**
|
|
@@ -376,4 +680,4 @@ declare function requestLoggerMiddleware(options?: {
|
|
|
376
680
|
*/
|
|
377
681
|
declare function getRequestLogger(req: Request): Logger;
|
|
378
682
|
|
|
379
|
-
export { DEFAULT_CONFIG, type FrameworkConfig, type InitServerData, type LogLevel, Logger, type LoggerContext, type LoggerOptions, type ServerConfig, ValidationError, buildApp, commonSchemas, createModuleLogger, createRateLimiter, defaultRateLimiter, generateRequestId, getAppDir, getBuildDir, getLogger, getRequestLogger, getStaticDir, lenientRateLimiter, loadConfig, logger, requestLoggerMiddleware, resetLogger, safeValidate, sanitizeObject, sanitizeParams, sanitizeQuery, sanitizeString, setLogger, startDevServer, startProdServer, strictRateLimiter, validate, withCache };
|
|
683
|
+
export { type AuthContext, type AuthFn, DEFAULT_CONFIG, type FrameworkConfig, type GuardFn, type InitServerData, type LogLevel, Logger, type LoggerContext, type LoggerOptions, type RateLimitCfg, type RealtimeConfig, type RealtimeLogger, type RealtimeMetrics, type RealtimeStateStore, type Schema, type ServerConfig, ValidationError, type WssContext, type WssEventDefinition, type WssHandler, type WssRouteDefinition, buildApp, commonSchemas, createModuleLogger, createRateLimiter, defaultRateLimiter, defineWssRoute, generateRequestId, getAppDir, getBuildDir, getLogger, getRequestLogger, getStaticDir, lenientRateLimiter, loadConfig, logger, requestLoggerMiddleware, resetLogger, safeValidate, sanitizeObject, sanitizeParams, sanitizeQuery, sanitizeString, setLogger, startDevServer, startProdServer, strictRateLimiter, validate, withCache };
|