@fluxstack/live 0.1.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/dist/build/index.d.ts +27 -0
- package/dist/build/index.js +151 -0
- package/dist/build/index.js.map +1 -0
- package/dist/index.d.ts +1400 -0
- package/dist/index.js +3334 -0
- package/dist/index.js.map +1 -0
- package/package.json +50 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1400 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Credentials sent by the client during WebSocket authentication.
|
|
5
|
+
* Extensible to support any auth strategy (JWT, API key, crypto, etc.)
|
|
6
|
+
*/
|
|
7
|
+
interface LiveAuthCredentials {
|
|
8
|
+
/** JWT or opaque token */
|
|
9
|
+
token?: string;
|
|
10
|
+
/** Public key (for crypto-auth) */
|
|
11
|
+
publicKey?: string;
|
|
12
|
+
/** Signature (for crypto-auth) */
|
|
13
|
+
signature?: string;
|
|
14
|
+
/** Signature timestamp */
|
|
15
|
+
timestamp?: number;
|
|
16
|
+
/** Anti-replay nonce */
|
|
17
|
+
nonce?: string;
|
|
18
|
+
/** Additional fields for custom providers */
|
|
19
|
+
[key: string]: unknown;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Authenticated user information.
|
|
23
|
+
* Returned by LiveAuthProvider after validation.
|
|
24
|
+
*/
|
|
25
|
+
interface LiveAuthUser {
|
|
26
|
+
/** Unique user identifier */
|
|
27
|
+
id: string;
|
|
28
|
+
/** Roles assigned to the user (e.g., 'admin', 'moderator') */
|
|
29
|
+
roles?: string[];
|
|
30
|
+
/** Granular permissions (e.g., 'chat.write', 'chat.admin') */
|
|
31
|
+
permissions?: string[];
|
|
32
|
+
/** Additional fields (name, email, etc.) */
|
|
33
|
+
[key: string]: unknown;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Auth context available inside LiveComponent via this.$auth.
|
|
37
|
+
* Provides type-safe helpers for checking roles and permissions.
|
|
38
|
+
*/
|
|
39
|
+
interface LiveAuthContext {
|
|
40
|
+
/** Whether the user is authenticated */
|
|
41
|
+
readonly authenticated: boolean;
|
|
42
|
+
/** User data (undefined if not authenticated) */
|
|
43
|
+
readonly user?: LiveAuthUser;
|
|
44
|
+
/** Original token used for authentication */
|
|
45
|
+
readonly token?: string;
|
|
46
|
+
/** Timestamp of when authentication occurred */
|
|
47
|
+
readonly authenticatedAt?: number;
|
|
48
|
+
/** Check if user has a specific role */
|
|
49
|
+
hasRole(role: string): boolean;
|
|
50
|
+
/** Check if user has ANY of the roles */
|
|
51
|
+
hasAnyRole(roles: string[]): boolean;
|
|
52
|
+
/** Check if user has ALL roles */
|
|
53
|
+
hasAllRoles(roles: string[]): boolean;
|
|
54
|
+
/** Check if user has a specific permission */
|
|
55
|
+
hasPermission(permission: string): boolean;
|
|
56
|
+
/** Check if user has ALL permissions */
|
|
57
|
+
hasAllPermissions(permissions: string[]): boolean;
|
|
58
|
+
/** Check if user has ANY of the permissions */
|
|
59
|
+
hasAnyPermission(permissions: string[]): boolean;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Interface for authentication strategy implementations.
|
|
63
|
+
* Each provider implements its own validation logic.
|
|
64
|
+
*
|
|
65
|
+
* Examples: JWTAuthProvider, CryptoAuthProvider, SessionAuthProvider
|
|
66
|
+
*/
|
|
67
|
+
interface LiveAuthProvider {
|
|
68
|
+
/** Unique provider name (e.g., 'jwt', 'crypto', 'session') */
|
|
69
|
+
readonly name: string;
|
|
70
|
+
/**
|
|
71
|
+
* Validate credentials and return auth context.
|
|
72
|
+
* Returns null if credentials are invalid.
|
|
73
|
+
*/
|
|
74
|
+
authenticate(credentials: LiveAuthCredentials): Promise<LiveAuthContext | null>;
|
|
75
|
+
/**
|
|
76
|
+
* (Optional) Custom per-action authorization.
|
|
77
|
+
* Returns true if the user can execute the action.
|
|
78
|
+
*/
|
|
79
|
+
authorizeAction?(context: LiveAuthContext, componentName: string, action: string): Promise<boolean>;
|
|
80
|
+
/**
|
|
81
|
+
* (Optional) Custom per-room authorization.
|
|
82
|
+
* Returns true if the user can join the room.
|
|
83
|
+
*/
|
|
84
|
+
authorizeRoom?(context: LiveAuthContext, roomId: string): Promise<boolean>;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Declarative auth configuration for a LiveComponent.
|
|
88
|
+
* Defined as a static property on the class.
|
|
89
|
+
*/
|
|
90
|
+
interface LiveComponentAuth {
|
|
91
|
+
/** Whether authentication is required to mount the component. Default: false */
|
|
92
|
+
required?: boolean;
|
|
93
|
+
/** Required roles (OR logic - any role suffices) */
|
|
94
|
+
roles?: string[];
|
|
95
|
+
/** Required permissions (AND logic - all must be present) */
|
|
96
|
+
permissions?: string[];
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Per-action auth configuration.
|
|
100
|
+
*/
|
|
101
|
+
interface LiveActionAuth {
|
|
102
|
+
/** Required roles for this action (OR logic) */
|
|
103
|
+
roles?: string[];
|
|
104
|
+
/** Required permissions for this action (AND logic) */
|
|
105
|
+
permissions?: string[];
|
|
106
|
+
}
|
|
107
|
+
/** Map of action name -> auth configuration */
|
|
108
|
+
type LiveActionAuthMap = Record<string, LiveActionAuth>;
|
|
109
|
+
/**
|
|
110
|
+
* Result of an authorization check.
|
|
111
|
+
*/
|
|
112
|
+
interface LiveAuthResult {
|
|
113
|
+
/** Whether authorization was successful */
|
|
114
|
+
allowed: boolean;
|
|
115
|
+
/** Denial reason (if allowed === false) */
|
|
116
|
+
reason?: string;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Minimal WebSocket interface that any server framework can implement.
|
|
121
|
+
* Compatible with Bun ServerWebSocket, Elysia WS, `ws` package, etc.
|
|
122
|
+
*/
|
|
123
|
+
interface GenericWebSocket {
|
|
124
|
+
/** Send data to the client */
|
|
125
|
+
send(data: string | ArrayBuffer | Uint8Array, compress?: boolean): void | number;
|
|
126
|
+
/** Close the connection */
|
|
127
|
+
close(code?: number, reason?: string): void;
|
|
128
|
+
/** Connection data storage */
|
|
129
|
+
data: LiveWSData;
|
|
130
|
+
/** Remote address of the client */
|
|
131
|
+
readonly remoteAddress: string;
|
|
132
|
+
/** WebSocket ready state: 0=CONNECTING, 1=OPEN, 2=CLOSING, 3=CLOSED */
|
|
133
|
+
readonly readyState: 0 | 1 | 2 | 3;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Data stored on each WebSocket connection.
|
|
137
|
+
* Attached to ws.data by the transport adapter on open.
|
|
138
|
+
*/
|
|
139
|
+
interface LiveWSData {
|
|
140
|
+
connectionId: string;
|
|
141
|
+
components: Map<string, any>;
|
|
142
|
+
subscriptions: Set<string>;
|
|
143
|
+
connectedAt: Date;
|
|
144
|
+
userId?: string;
|
|
145
|
+
/** Auth context for the connection */
|
|
146
|
+
authContext?: LiveAuthContext;
|
|
147
|
+
}
|
|
148
|
+
/** @deprecated Use GenericWebSocket instead */
|
|
149
|
+
type FluxStackWebSocket = GenericWebSocket;
|
|
150
|
+
/** @deprecated Use LiveWSData instead */
|
|
151
|
+
type FluxStackWSData = LiveWSData;
|
|
152
|
+
/**
|
|
153
|
+
* The transport layer that each server adapter must implement.
|
|
154
|
+
* LiveServer calls these methods to register WS and HTTP handlers.
|
|
155
|
+
*/
|
|
156
|
+
interface LiveTransport {
|
|
157
|
+
/** Register the main WebSocket endpoint for Live Components */
|
|
158
|
+
registerWebSocket(config: WebSocketConfig): void | Promise<void>;
|
|
159
|
+
/** Register HTTP monitoring/debug routes */
|
|
160
|
+
registerHttpRoutes(routes: HttpRouteDefinition[]): void | Promise<void>;
|
|
161
|
+
/** Optional startup hook */
|
|
162
|
+
start?(): void | Promise<void>;
|
|
163
|
+
/** Optional shutdown hook */
|
|
164
|
+
shutdown?(): void | Promise<void>;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Configuration for the Live Components WebSocket endpoint.
|
|
168
|
+
* The transport adapter wires these callbacks to its own WS implementation.
|
|
169
|
+
*/
|
|
170
|
+
interface WebSocketConfig {
|
|
171
|
+
/** Path for the WebSocket endpoint (e.g., '/api/live/ws') */
|
|
172
|
+
path: string;
|
|
173
|
+
/** Called when a new client connects */
|
|
174
|
+
onOpen(ws: GenericWebSocket): void | Promise<void>;
|
|
175
|
+
/** Called when a message is received (JSON or binary) */
|
|
176
|
+
onMessage(ws: GenericWebSocket, message: unknown, isBinary: boolean): void | Promise<void>;
|
|
177
|
+
/** Called when a client disconnects */
|
|
178
|
+
onClose(ws: GenericWebSocket, code: number, reason: string): void | Promise<void>;
|
|
179
|
+
/** Called on WebSocket error */
|
|
180
|
+
onError?(ws: GenericWebSocket, error: Error): void;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Framework-agnostic HTTP route definition.
|
|
184
|
+
* Each transport adapter maps these to its own router.
|
|
185
|
+
*/
|
|
186
|
+
interface HttpRouteDefinition {
|
|
187
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
188
|
+
path: string;
|
|
189
|
+
handler: (request: HttpRequest) => HttpResponse | Promise<HttpResponse>;
|
|
190
|
+
metadata?: {
|
|
191
|
+
summary?: string;
|
|
192
|
+
description?: string;
|
|
193
|
+
tags?: string[];
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Normalized HTTP request object passed to route handlers.
|
|
198
|
+
*/
|
|
199
|
+
interface HttpRequest {
|
|
200
|
+
params: Record<string, string>;
|
|
201
|
+
query: Record<string, string | undefined>;
|
|
202
|
+
body: unknown;
|
|
203
|
+
headers: Record<string, string | undefined>;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Normalized HTTP response returned by route handlers.
|
|
207
|
+
*/
|
|
208
|
+
interface HttpResponse {
|
|
209
|
+
status?: number;
|
|
210
|
+
body: unknown;
|
|
211
|
+
headers?: Record<string, string>;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
type EventHandler<T = any> = (data: T) => void;
|
|
215
|
+
interface RoomSubscription {
|
|
216
|
+
roomType: string;
|
|
217
|
+
roomId: string;
|
|
218
|
+
event: string;
|
|
219
|
+
handler: EventHandler;
|
|
220
|
+
componentId: string;
|
|
221
|
+
}
|
|
222
|
+
declare function createTypedRoomEventBus<TRoomEvents extends Record<string, Record<string, any>>>(): {
|
|
223
|
+
on<K extends keyof TRoomEvents, E extends keyof TRoomEvents[K]>(roomType: K, roomId: string, event: E, componentId: string, handler: EventHandler<TRoomEvents[K][E]>): () => void;
|
|
224
|
+
emit<K extends keyof TRoomEvents, E extends keyof TRoomEvents[K]>(roomType: K, roomId: string, event: E, data: TRoomEvents[K][E], excludeComponentId?: string): number;
|
|
225
|
+
unsubscribeAll(componentId: string): number;
|
|
226
|
+
clearRoom<K extends keyof TRoomEvents>(roomType: K, roomId: string): number;
|
|
227
|
+
getStats(): {
|
|
228
|
+
totalSubscriptions: number;
|
|
229
|
+
rooms: Record<string, {
|
|
230
|
+
events: Record<string, number>;
|
|
231
|
+
}>;
|
|
232
|
+
};
|
|
233
|
+
};
|
|
234
|
+
declare class RoomEventBus {
|
|
235
|
+
private subscriptions;
|
|
236
|
+
private getKey;
|
|
237
|
+
on(roomType: string, roomId: string, event: string, componentId: string, handler: EventHandler): () => void;
|
|
238
|
+
emit(roomType: string, roomId: string, event: string, data: any, excludeComponentId?: string): number;
|
|
239
|
+
unsubscribeAll(componentId: string): number;
|
|
240
|
+
clearRoom(roomType: string, roomId: string): number;
|
|
241
|
+
getStats(): {
|
|
242
|
+
totalSubscriptions: number;
|
|
243
|
+
rooms: Record<string, {
|
|
244
|
+
events: Record<string, number>;
|
|
245
|
+
}>;
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
declare class LiveRoomManager {
|
|
250
|
+
private roomEvents;
|
|
251
|
+
private rooms;
|
|
252
|
+
private componentRooms;
|
|
253
|
+
constructor(roomEvents: RoomEventBus);
|
|
254
|
+
/**
|
|
255
|
+
* Component joins a room
|
|
256
|
+
*/
|
|
257
|
+
joinRoom<TState = any>(componentId: string, roomId: string, ws: GenericWebSocket, initialState?: TState): {
|
|
258
|
+
state: TState;
|
|
259
|
+
};
|
|
260
|
+
/**
|
|
261
|
+
* Component leaves a room
|
|
262
|
+
*/
|
|
263
|
+
leaveRoom(componentId: string, roomId: string): void;
|
|
264
|
+
/**
|
|
265
|
+
* Component disconnects - leave all rooms
|
|
266
|
+
*/
|
|
267
|
+
cleanupComponent(componentId: string): void;
|
|
268
|
+
/**
|
|
269
|
+
* Emit event to all members in a room
|
|
270
|
+
*/
|
|
271
|
+
emitToRoom(roomId: string, event: string, data: any, excludeComponentId?: string): number;
|
|
272
|
+
/**
|
|
273
|
+
* Update room state
|
|
274
|
+
*/
|
|
275
|
+
setRoomState(roomId: string, updates: any, excludeComponentId?: string): void;
|
|
276
|
+
/**
|
|
277
|
+
* Get room state
|
|
278
|
+
*/
|
|
279
|
+
getRoomState<TState = any>(roomId: string): TState;
|
|
280
|
+
/**
|
|
281
|
+
* Broadcast to all members in a room
|
|
282
|
+
*/
|
|
283
|
+
private broadcastToRoom;
|
|
284
|
+
/**
|
|
285
|
+
* Check if component is in a room
|
|
286
|
+
*/
|
|
287
|
+
isInRoom(componentId: string, roomId: string): boolean;
|
|
288
|
+
/**
|
|
289
|
+
* Get rooms for a component
|
|
290
|
+
*/
|
|
291
|
+
getComponentRooms(componentId: string): string[];
|
|
292
|
+
/**
|
|
293
|
+
* Get statistics
|
|
294
|
+
*/
|
|
295
|
+
getStats(): {
|
|
296
|
+
totalRooms: number;
|
|
297
|
+
rooms: Record<string, {
|
|
298
|
+
members: number;
|
|
299
|
+
createdAt: number;
|
|
300
|
+
lastActivity: number;
|
|
301
|
+
}>;
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
type DebugEventType = 'COMPONENT_MOUNT' | 'COMPONENT_UNMOUNT' | 'COMPONENT_REHYDRATE' | 'STATE_CHANGE' | 'ACTION_CALL' | 'ACTION_RESULT' | 'ACTION_ERROR' | 'ROOM_JOIN' | 'ROOM_LEAVE' | 'ROOM_EMIT' | 'ROOM_EVENT_RECEIVED' | 'WS_CONNECT' | 'WS_DISCONNECT' | 'ERROR' | 'LOG';
|
|
306
|
+
interface DebugEvent {
|
|
307
|
+
id: string;
|
|
308
|
+
timestamp: number;
|
|
309
|
+
type: DebugEventType;
|
|
310
|
+
componentId: string | null;
|
|
311
|
+
componentName: string | null;
|
|
312
|
+
data: Record<string, unknown>;
|
|
313
|
+
}
|
|
314
|
+
interface ComponentSnapshot {
|
|
315
|
+
componentId: string;
|
|
316
|
+
componentName: string;
|
|
317
|
+
/** Developer-defined label for easier identification in the debugger */
|
|
318
|
+
debugLabel?: string;
|
|
319
|
+
state: Record<string, unknown>;
|
|
320
|
+
rooms: string[];
|
|
321
|
+
mountedAt: number;
|
|
322
|
+
lastActivity: number;
|
|
323
|
+
actionCount: number;
|
|
324
|
+
stateChangeCount: number;
|
|
325
|
+
errorCount: number;
|
|
326
|
+
}
|
|
327
|
+
interface DebugSnapshot {
|
|
328
|
+
components: ComponentSnapshot[];
|
|
329
|
+
connections: number;
|
|
330
|
+
uptime: number;
|
|
331
|
+
totalEvents: number;
|
|
332
|
+
}
|
|
333
|
+
interface DebugWsMessage {
|
|
334
|
+
type: 'DEBUG_EVENT' | 'DEBUG_SNAPSHOT' | 'DEBUG_WELCOME' | 'DEBUG_DISABLED';
|
|
335
|
+
event?: DebugEvent;
|
|
336
|
+
snapshot?: DebugSnapshot;
|
|
337
|
+
enabled?: boolean;
|
|
338
|
+
timestamp: number;
|
|
339
|
+
}
|
|
340
|
+
declare class LiveDebugger {
|
|
341
|
+
private events;
|
|
342
|
+
private componentSnapshots;
|
|
343
|
+
private debugClients;
|
|
344
|
+
private _enabled;
|
|
345
|
+
private startTime;
|
|
346
|
+
private eventCounter;
|
|
347
|
+
constructor(enabled?: boolean);
|
|
348
|
+
get enabled(): boolean;
|
|
349
|
+
set enabled(value: boolean);
|
|
350
|
+
emit(type: DebugEventType | string, componentId: string | null, componentName: string | null, data?: Record<string, unknown>): void;
|
|
351
|
+
trackComponentMount(componentId: string, componentName: string, initialState: Record<string, unknown>, room?: string, debugLabel?: string): void;
|
|
352
|
+
trackComponentUnmount(componentId: string): void;
|
|
353
|
+
trackStateChange(componentId: string, delta: Record<string, unknown>, fullState: Record<string, unknown>, source?: 'proxy' | 'setState' | 'rehydrate'): void;
|
|
354
|
+
trackActionCall(componentId: string, action: string, payload: unknown): void;
|
|
355
|
+
trackActionResult(componentId: string, action: string, result: unknown, duration: number): void;
|
|
356
|
+
trackActionError(componentId: string, action: string, error: string, duration: number): void;
|
|
357
|
+
trackRoomJoin(componentId: string, roomId: string): void;
|
|
358
|
+
trackRoomLeave(componentId: string, roomId: string): void;
|
|
359
|
+
trackRoomEmit(componentId: string, roomId: string, event: string, data: unknown): void;
|
|
360
|
+
trackConnection(connectionId: string): void;
|
|
361
|
+
trackDisconnection(connectionId: string, componentCount: number): void;
|
|
362
|
+
trackError(componentId: string | null, error: string, context?: Record<string, unknown>): void;
|
|
363
|
+
registerDebugClient(ws: GenericWebSocket): void;
|
|
364
|
+
unregisterDebugClient(ws: GenericWebSocket): void;
|
|
365
|
+
getSnapshot(): DebugSnapshot;
|
|
366
|
+
getComponentState(componentId: string): ComponentSnapshot | null;
|
|
367
|
+
getEvents(filter?: {
|
|
368
|
+
componentId?: string;
|
|
369
|
+
type?: DebugEventType;
|
|
370
|
+
limit?: number;
|
|
371
|
+
}): DebugEvent[];
|
|
372
|
+
clearEvents(): void;
|
|
373
|
+
private broadcastEvent;
|
|
374
|
+
private sanitizeData;
|
|
375
|
+
private sanitizeState;
|
|
376
|
+
private updateSnapshot;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
declare class LiveAuthManager {
|
|
380
|
+
private providers;
|
|
381
|
+
private defaultProviderName?;
|
|
382
|
+
/**
|
|
383
|
+
* Register an auth provider.
|
|
384
|
+
*/
|
|
385
|
+
register(provider: LiveAuthProvider): void;
|
|
386
|
+
/**
|
|
387
|
+
* Remove an auth provider.
|
|
388
|
+
*/
|
|
389
|
+
unregister(name: string): void;
|
|
390
|
+
/**
|
|
391
|
+
* Set the default auth provider.
|
|
392
|
+
*/
|
|
393
|
+
setDefault(name: string): void;
|
|
394
|
+
/**
|
|
395
|
+
* Returns true if at least one provider is registered.
|
|
396
|
+
*/
|
|
397
|
+
hasProviders(): boolean;
|
|
398
|
+
/**
|
|
399
|
+
* Returns the default provider or undefined.
|
|
400
|
+
*/
|
|
401
|
+
getDefaultProvider(): LiveAuthProvider | undefined;
|
|
402
|
+
/**
|
|
403
|
+
* Authenticate credentials using the specified provider, or try all providers.
|
|
404
|
+
* Returns ANONYMOUS_CONTEXT if no credentials or no providers.
|
|
405
|
+
*/
|
|
406
|
+
authenticate(credentials: LiveAuthCredentials, providerName?: string): Promise<LiveAuthContext>;
|
|
407
|
+
/**
|
|
408
|
+
* Verify auth context meets component requirements.
|
|
409
|
+
*/
|
|
410
|
+
authorizeComponent(authContext: LiveAuthContext, authConfig: LiveComponentAuth | undefined): LiveAuthResult;
|
|
411
|
+
/**
|
|
412
|
+
* Verify auth context allows executing a specific action.
|
|
413
|
+
*/
|
|
414
|
+
authorizeAction(authContext: LiveAuthContext, componentName: string, action: string, actionAuth: LiveActionAuth | undefined, providerName?: string): Promise<LiveAuthResult>;
|
|
415
|
+
/**
|
|
416
|
+
* Verify auth context allows joining a room.
|
|
417
|
+
*/
|
|
418
|
+
authorizeRoom(authContext: LiveAuthContext, roomId: string, providerName?: string): Promise<LiveAuthResult>;
|
|
419
|
+
/**
|
|
420
|
+
* Get info about registered providers.
|
|
421
|
+
*/
|
|
422
|
+
getInfo(): {
|
|
423
|
+
providers: string[];
|
|
424
|
+
defaultProvider?: string;
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
interface SignedState {
|
|
429
|
+
data: string;
|
|
430
|
+
signature: string;
|
|
431
|
+
timestamp: number;
|
|
432
|
+
version: number;
|
|
433
|
+
componentId: string;
|
|
434
|
+
nonce?: string;
|
|
435
|
+
compressed?: boolean;
|
|
436
|
+
encrypted?: boolean;
|
|
437
|
+
}
|
|
438
|
+
interface StateSignatureConfig {
|
|
439
|
+
/** HMAC secret for signing. Defaults to env LIVE_STATE_SECRET or a random key. */
|
|
440
|
+
secret?: string;
|
|
441
|
+
/** Enable key rotation */
|
|
442
|
+
rotationEnabled?: boolean;
|
|
443
|
+
/** Key rotation interval in ms */
|
|
444
|
+
rotationInterval?: number;
|
|
445
|
+
/** Enable compression */
|
|
446
|
+
compressionEnabled?: boolean;
|
|
447
|
+
/** Enable encryption */
|
|
448
|
+
encryptionEnabled?: boolean;
|
|
449
|
+
/** Enable anti-replay nonces */
|
|
450
|
+
nonceEnabled?: boolean;
|
|
451
|
+
/** Maximum state age in ms */
|
|
452
|
+
maxStateAge?: number;
|
|
453
|
+
/** Enable state backups */
|
|
454
|
+
backupEnabled?: boolean;
|
|
455
|
+
/** Max state backups to keep */
|
|
456
|
+
maxBackups?: number;
|
|
457
|
+
}
|
|
458
|
+
declare class StateSignatureManager {
|
|
459
|
+
private secret;
|
|
460
|
+
private previousSecrets;
|
|
461
|
+
private rotationTimer?;
|
|
462
|
+
private usedNonces;
|
|
463
|
+
private nonceCleanupTimer?;
|
|
464
|
+
private stateBackups;
|
|
465
|
+
private config;
|
|
466
|
+
constructor(config?: StateSignatureConfig);
|
|
467
|
+
signState(componentId: string, state: Record<string, unknown>, version: number, options?: {
|
|
468
|
+
compress?: boolean;
|
|
469
|
+
backup?: boolean;
|
|
470
|
+
}): Promise<SignedState>;
|
|
471
|
+
validateState(signedState: SignedState): Promise<{
|
|
472
|
+
valid: boolean;
|
|
473
|
+
error?: string;
|
|
474
|
+
}>;
|
|
475
|
+
extractData(signedState: SignedState): Promise<Record<string, unknown>>;
|
|
476
|
+
getBackups(componentId: string): SignedState[];
|
|
477
|
+
getLatestBackup(componentId: string): SignedState | null;
|
|
478
|
+
private backupState;
|
|
479
|
+
private computeSignature;
|
|
480
|
+
private computeSignatureWithKey;
|
|
481
|
+
private timingSafeEqual;
|
|
482
|
+
private deriveEncryptionKey;
|
|
483
|
+
private setupKeyRotation;
|
|
484
|
+
private cleanupNonces;
|
|
485
|
+
shutdown(): void;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
interface ComponentPerformanceMetrics {
|
|
489
|
+
componentId: string;
|
|
490
|
+
componentName: string;
|
|
491
|
+
mountTime: number;
|
|
492
|
+
actionTimes: Map<string, number[]>;
|
|
493
|
+
renderTimes: number[];
|
|
494
|
+
stateChanges: number;
|
|
495
|
+
errorCount: number;
|
|
496
|
+
lastActivity: number;
|
|
497
|
+
}
|
|
498
|
+
interface PerformanceAlert {
|
|
499
|
+
type: 'slow_action' | 'slow_render' | 'high_error_rate' | 'memory_warning';
|
|
500
|
+
componentId: string;
|
|
501
|
+
componentName: string;
|
|
502
|
+
message: string;
|
|
503
|
+
value: number;
|
|
504
|
+
threshold: number;
|
|
505
|
+
timestamp: number;
|
|
506
|
+
}
|
|
507
|
+
interface PerformanceConfig {
|
|
508
|
+
/** Threshold for slow action warnings (ms) */
|
|
509
|
+
slowActionThreshold?: number;
|
|
510
|
+
/** Threshold for slow render warnings (ms) */
|
|
511
|
+
slowRenderThreshold?: number;
|
|
512
|
+
/** Max errors before warning */
|
|
513
|
+
highErrorRateThreshold?: number;
|
|
514
|
+
/** Enable performance alerts */
|
|
515
|
+
alertsEnabled?: boolean;
|
|
516
|
+
}
|
|
517
|
+
declare class PerformanceMonitor extends EventEmitter {
|
|
518
|
+
private components;
|
|
519
|
+
private alerts;
|
|
520
|
+
private config;
|
|
521
|
+
constructor(config?: PerformanceConfig);
|
|
522
|
+
initializeComponent(componentId: string, componentName: string): void;
|
|
523
|
+
recordRenderTime(componentId: string, time: number): void;
|
|
524
|
+
recordActionTime(componentId: string, action: string, time: number, error?: Error): void;
|
|
525
|
+
recordStateChange(componentId: string): void;
|
|
526
|
+
removeComponent(componentId: string): void;
|
|
527
|
+
getComponentMetrics(componentId: string): ComponentPerformanceMetrics | null;
|
|
528
|
+
getAllMetrics(): ComponentPerformanceMetrics[];
|
|
529
|
+
getAlerts(limit?: number): PerformanceAlert[];
|
|
530
|
+
clearAlerts(): void;
|
|
531
|
+
getStats(): {
|
|
532
|
+
totalComponents: number;
|
|
533
|
+
totalAlerts: number;
|
|
534
|
+
components: {
|
|
535
|
+
id: string;
|
|
536
|
+
name: string;
|
|
537
|
+
renderCount: number;
|
|
538
|
+
avgRenderTime: number;
|
|
539
|
+
actionCount: number;
|
|
540
|
+
stateChanges: number;
|
|
541
|
+
errorCount: number;
|
|
542
|
+
}[];
|
|
543
|
+
};
|
|
544
|
+
private addAlert;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
interface LiveMessage {
|
|
548
|
+
type: 'COMPONENT_MOUNT' | 'COMPONENT_UNMOUNT' | 'COMPONENT_REHYDRATE' | 'COMPONENT_ACTION' | 'CALL_ACTION' | 'ACTION_RESPONSE' | 'PROPERTY_UPDATE' | 'STATE_UPDATE' | 'STATE_DELTA' | 'STATE_REHYDRATED' | 'ERROR' | 'BROADCAST' | 'FILE_UPLOAD_START' | 'FILE_UPLOAD_CHUNK' | 'FILE_UPLOAD_COMPLETE' | 'COMPONENT_PING' | 'COMPONENT_PONG' | 'AUTH' | 'ROOM_JOIN' | 'ROOM_LEAVE' | 'ROOM_EMIT' | 'ROOM_STATE_SET' | 'ROOM_STATE_GET';
|
|
549
|
+
componentId: string;
|
|
550
|
+
action?: string;
|
|
551
|
+
property?: string;
|
|
552
|
+
payload?: any;
|
|
553
|
+
timestamp?: number;
|
|
554
|
+
userId?: string;
|
|
555
|
+
room?: string;
|
|
556
|
+
requestId?: string;
|
|
557
|
+
responseId?: string;
|
|
558
|
+
expectResponse?: boolean;
|
|
559
|
+
}
|
|
560
|
+
interface WebSocketResponse {
|
|
561
|
+
type: 'MESSAGE_RESPONSE' | 'CONNECTION_ESTABLISHED' | 'ERROR' | 'BROADCAST' | 'ACTION_RESPONSE' | 'COMPONENT_MOUNTED' | 'COMPONENT_REHYDRATED' | 'STATE_UPDATE' | 'STATE_DELTA' | 'STATE_REHYDRATED' | 'FILE_UPLOAD_PROGRESS' | 'FILE_UPLOAD_COMPLETE' | 'FILE_UPLOAD_ERROR' | 'FILE_UPLOAD_START_RESPONSE' | 'COMPONENT_PONG' | 'AUTH_RESPONSE' | 'ROOM_EVENT' | 'ROOM_STATE' | 'ROOM_SYSTEM' | 'ROOM_JOINED' | 'ROOM_LEFT';
|
|
562
|
+
originalType?: string;
|
|
563
|
+
componentId?: string;
|
|
564
|
+
success?: boolean;
|
|
565
|
+
result?: any;
|
|
566
|
+
requestId?: string;
|
|
567
|
+
responseId?: string;
|
|
568
|
+
error?: string;
|
|
569
|
+
timestamp?: number;
|
|
570
|
+
connectionId?: string;
|
|
571
|
+
payload?: any;
|
|
572
|
+
uploadId?: string;
|
|
573
|
+
chunkIndex?: number;
|
|
574
|
+
totalChunks?: number;
|
|
575
|
+
bytesUploaded?: number;
|
|
576
|
+
totalBytes?: number;
|
|
577
|
+
progress?: number;
|
|
578
|
+
filename?: string;
|
|
579
|
+
fileUrl?: string;
|
|
580
|
+
signedState?: any;
|
|
581
|
+
oldComponentId?: string;
|
|
582
|
+
newComponentId?: string;
|
|
583
|
+
}
|
|
584
|
+
interface RoomMessage {
|
|
585
|
+
type: 'ROOM_JOIN' | 'ROOM_LEAVE' | 'ROOM_EMIT' | 'ROOM_STATE_SET' | 'ROOM_STATE_GET';
|
|
586
|
+
componentId: string;
|
|
587
|
+
roomId: string;
|
|
588
|
+
event?: string;
|
|
589
|
+
data?: any;
|
|
590
|
+
requestId?: string;
|
|
591
|
+
timestamp: number;
|
|
592
|
+
}
|
|
593
|
+
interface ComponentState {
|
|
594
|
+
[key: string]: any;
|
|
595
|
+
}
|
|
596
|
+
interface BroadcastMessage {
|
|
597
|
+
type: string;
|
|
598
|
+
payload: any;
|
|
599
|
+
room?: string;
|
|
600
|
+
excludeUser?: string;
|
|
601
|
+
}
|
|
602
|
+
interface LiveComponentInstance<TState = ComponentState, TActions = Record<string, Function>> {
|
|
603
|
+
id: string;
|
|
604
|
+
state: TState;
|
|
605
|
+
call: <T extends keyof TActions>(action: T, ...args: any[]) => Promise<any>;
|
|
606
|
+
set: <K extends keyof TState>(property: K, value: TState[K]) => void;
|
|
607
|
+
loading: boolean;
|
|
608
|
+
errors: Record<string, string>;
|
|
609
|
+
connected: boolean;
|
|
610
|
+
room?: string;
|
|
611
|
+
}
|
|
612
|
+
interface WebSocketMessage {
|
|
613
|
+
type: string;
|
|
614
|
+
componentId?: string;
|
|
615
|
+
action?: string;
|
|
616
|
+
payload?: any;
|
|
617
|
+
timestamp?: number;
|
|
618
|
+
userId?: string;
|
|
619
|
+
room?: string;
|
|
620
|
+
requestId?: string;
|
|
621
|
+
responseId?: string;
|
|
622
|
+
expectResponse?: boolean;
|
|
623
|
+
}
|
|
624
|
+
interface HybridState<T> {
|
|
625
|
+
data: T;
|
|
626
|
+
validation: StateValidation;
|
|
627
|
+
conflicts: StateConflict[];
|
|
628
|
+
status: 'synced' | 'conflict' | 'disconnected';
|
|
629
|
+
}
|
|
630
|
+
interface StateValidation {
|
|
631
|
+
checksum: string;
|
|
632
|
+
version: number;
|
|
633
|
+
source: 'client' | 'server' | 'mount';
|
|
634
|
+
timestamp: number;
|
|
635
|
+
}
|
|
636
|
+
interface StateConflict {
|
|
637
|
+
property: string;
|
|
638
|
+
clientValue: any;
|
|
639
|
+
serverValue: any;
|
|
640
|
+
timestamp: number;
|
|
641
|
+
resolved: boolean;
|
|
642
|
+
}
|
|
643
|
+
interface HybridComponentOptions {
|
|
644
|
+
fallbackToLocal?: boolean;
|
|
645
|
+
room?: string;
|
|
646
|
+
userId?: string;
|
|
647
|
+
autoMount?: boolean;
|
|
648
|
+
debug?: boolean;
|
|
649
|
+
onConnect?: () => void;
|
|
650
|
+
onMount?: () => void;
|
|
651
|
+
onRehydrate?: () => void;
|
|
652
|
+
onDisconnect?: () => void;
|
|
653
|
+
onError?: (error: string) => void;
|
|
654
|
+
onStateChange?: (newState: any, oldState: any) => void;
|
|
655
|
+
}
|
|
656
|
+
interface ServerRoomHandle<TState = any, TEvents extends Record<string, any> = Record<string, any>> {
|
|
657
|
+
readonly id: string;
|
|
658
|
+
readonly state: TState;
|
|
659
|
+
join: (initialState?: TState) => void;
|
|
660
|
+
leave: () => void;
|
|
661
|
+
emit: <K extends keyof TEvents>(event: K, data: TEvents[K]) => number;
|
|
662
|
+
on: <K extends keyof TEvents>(event: K, handler: (data: TEvents[K]) => void) => () => void;
|
|
663
|
+
setState: (updates: Partial<TState>) => void;
|
|
664
|
+
}
|
|
665
|
+
interface ServerRoomProxy<TState = any, TEvents extends Record<string, any> = Record<string, any>> {
|
|
666
|
+
(roomId: string): ServerRoomHandle<TState, TEvents>;
|
|
667
|
+
readonly id: string | undefined;
|
|
668
|
+
readonly state: TState;
|
|
669
|
+
join: (initialState?: TState) => void;
|
|
670
|
+
leave: () => void;
|
|
671
|
+
emit: <K extends keyof TEvents>(event: K, data: TEvents[K]) => number;
|
|
672
|
+
on: <K extends keyof TEvents>(event: K, handler: (data: TEvents[K]) => void) => () => void;
|
|
673
|
+
setState: (updates: Partial<TState>) => void;
|
|
674
|
+
}
|
|
675
|
+
interface FileChunkData {
|
|
676
|
+
uploadId: string;
|
|
677
|
+
filename: string;
|
|
678
|
+
fileType: string;
|
|
679
|
+
fileSize: number;
|
|
680
|
+
chunkIndex: number;
|
|
681
|
+
totalChunks: number;
|
|
682
|
+
chunkSize: number;
|
|
683
|
+
data: string;
|
|
684
|
+
hash?: string;
|
|
685
|
+
}
|
|
686
|
+
interface FileUploadStartMessage {
|
|
687
|
+
type: 'FILE_UPLOAD_START';
|
|
688
|
+
componentId: string;
|
|
689
|
+
uploadId: string;
|
|
690
|
+
filename: string;
|
|
691
|
+
fileType: string;
|
|
692
|
+
fileSize: number;
|
|
693
|
+
chunkSize?: number;
|
|
694
|
+
requestId?: string;
|
|
695
|
+
}
|
|
696
|
+
interface FileUploadChunkMessage {
|
|
697
|
+
type: 'FILE_UPLOAD_CHUNK';
|
|
698
|
+
componentId: string;
|
|
699
|
+
uploadId: string;
|
|
700
|
+
chunkIndex: number;
|
|
701
|
+
totalChunks: number;
|
|
702
|
+
data: string | Buffer;
|
|
703
|
+
hash?: string;
|
|
704
|
+
requestId?: string;
|
|
705
|
+
}
|
|
706
|
+
interface BinaryChunkHeader {
|
|
707
|
+
type: 'FILE_UPLOAD_CHUNK';
|
|
708
|
+
componentId: string;
|
|
709
|
+
uploadId: string;
|
|
710
|
+
chunkIndex: number;
|
|
711
|
+
totalChunks: number;
|
|
712
|
+
requestId?: string;
|
|
713
|
+
}
|
|
714
|
+
interface FileUploadCompleteMessage {
|
|
715
|
+
type: 'FILE_UPLOAD_COMPLETE';
|
|
716
|
+
componentId: string;
|
|
717
|
+
uploadId: string;
|
|
718
|
+
requestId?: string;
|
|
719
|
+
}
|
|
720
|
+
interface FileUploadProgressResponse {
|
|
721
|
+
type: 'FILE_UPLOAD_PROGRESS';
|
|
722
|
+
componentId: string;
|
|
723
|
+
uploadId: string;
|
|
724
|
+
chunkIndex: number;
|
|
725
|
+
totalChunks: number;
|
|
726
|
+
bytesUploaded: number;
|
|
727
|
+
totalBytes: number;
|
|
728
|
+
progress: number;
|
|
729
|
+
requestId?: string;
|
|
730
|
+
timestamp: number;
|
|
731
|
+
}
|
|
732
|
+
interface FileUploadCompleteResponse {
|
|
733
|
+
type: 'FILE_UPLOAD_COMPLETE';
|
|
734
|
+
componentId: string;
|
|
735
|
+
uploadId: string;
|
|
736
|
+
success: boolean;
|
|
737
|
+
filename?: string;
|
|
738
|
+
fileUrl?: string;
|
|
739
|
+
error?: string;
|
|
740
|
+
requestId?: string;
|
|
741
|
+
timestamp: number;
|
|
742
|
+
}
|
|
743
|
+
interface ActiveUpload {
|
|
744
|
+
uploadId: string;
|
|
745
|
+
componentId: string;
|
|
746
|
+
filename: string;
|
|
747
|
+
fileType: string;
|
|
748
|
+
fileSize: number;
|
|
749
|
+
totalChunks: number;
|
|
750
|
+
receivedChunks: Map<number, string | Buffer>;
|
|
751
|
+
bytesReceived: number;
|
|
752
|
+
startTime: number;
|
|
753
|
+
lastChunkTime: number;
|
|
754
|
+
tempFilePath?: string;
|
|
755
|
+
}
|
|
756
|
+
interface ComponentDefinition<TState = ComponentState> {
|
|
757
|
+
name: string;
|
|
758
|
+
initialState: TState;
|
|
759
|
+
component: new (initialState: TState, ws: any, options?: {
|
|
760
|
+
room?: string;
|
|
761
|
+
userId?: string;
|
|
762
|
+
}) => any;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
interface FileUploadConfig {
|
|
766
|
+
maxUploadSize?: number;
|
|
767
|
+
chunkTimeout?: number;
|
|
768
|
+
maxBytesPerUser?: number;
|
|
769
|
+
quotaResetInterval?: number;
|
|
770
|
+
allowedTypes?: string[];
|
|
771
|
+
blockedExtensions?: string[];
|
|
772
|
+
uploadsDir?: string;
|
|
773
|
+
/** Custom file assembly handler - if not provided, uses default fs assembly */
|
|
774
|
+
assembleFile?: (upload: ActiveUpload) => Promise<string>;
|
|
775
|
+
}
|
|
776
|
+
declare class FileUploadManager {
|
|
777
|
+
private activeUploads;
|
|
778
|
+
private readonly maxUploadSize;
|
|
779
|
+
private readonly chunkTimeout;
|
|
780
|
+
private userUploadBytes;
|
|
781
|
+
private readonly maxBytesPerUser;
|
|
782
|
+
private readonly quotaResetInterval;
|
|
783
|
+
private readonly allowedTypes;
|
|
784
|
+
private readonly blockedExtensions;
|
|
785
|
+
private readonly uploadsDir;
|
|
786
|
+
private readonly customAssembleFile?;
|
|
787
|
+
private cleanupTimer?;
|
|
788
|
+
private quotaTimer?;
|
|
789
|
+
constructor(config?: FileUploadConfig);
|
|
790
|
+
startUpload(message: FileUploadStartMessage, userId?: string): Promise<{
|
|
791
|
+
success: boolean;
|
|
792
|
+
error?: string;
|
|
793
|
+
}>;
|
|
794
|
+
receiveChunk(message: FileUploadChunkMessage, binaryData?: Buffer | null): Promise<FileUploadProgressResponse | null>;
|
|
795
|
+
completeUpload(message: FileUploadCompleteMessage): Promise<FileUploadCompleteResponse>;
|
|
796
|
+
private defaultAssembleFile;
|
|
797
|
+
private validateContentMagicBytes;
|
|
798
|
+
private cleanupStaleUploads;
|
|
799
|
+
private resetUploadQuotas;
|
|
800
|
+
getUserUploadUsage(userId: string): {
|
|
801
|
+
used: number;
|
|
802
|
+
limit: number;
|
|
803
|
+
remaining: number;
|
|
804
|
+
};
|
|
805
|
+
getUploadStatus(uploadId: string): ActiveUpload | null;
|
|
806
|
+
getStats(): {
|
|
807
|
+
activeUploads: number;
|
|
808
|
+
maxUploadSize: number;
|
|
809
|
+
allowedTypes: string[];
|
|
810
|
+
};
|
|
811
|
+
shutdown(): void;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
interface ConnectionConfig {
|
|
815
|
+
maxConnections: number;
|
|
816
|
+
connectionTimeout: number;
|
|
817
|
+
heartbeatInterval: number;
|
|
818
|
+
reconnectAttempts: number;
|
|
819
|
+
reconnectDelay: number;
|
|
820
|
+
maxReconnectDelay: number;
|
|
821
|
+
jitterFactor: number;
|
|
822
|
+
loadBalancing: 'round-robin' | 'least-connections' | 'random';
|
|
823
|
+
healthCheckInterval: number;
|
|
824
|
+
messageQueueSize: number;
|
|
825
|
+
offlineQueueEnabled: boolean;
|
|
826
|
+
}
|
|
827
|
+
interface ConnectionMetrics {
|
|
828
|
+
id: string;
|
|
829
|
+
connectedAt: Date;
|
|
830
|
+
lastActivity: Date;
|
|
831
|
+
messagesSent: number;
|
|
832
|
+
messagesReceived: number;
|
|
833
|
+
bytesTransferred: number;
|
|
834
|
+
latency: number;
|
|
835
|
+
status: 'connecting' | 'connected' | 'disconnecting' | 'disconnected' | 'error';
|
|
836
|
+
errorCount: number;
|
|
837
|
+
reconnectCount: number;
|
|
838
|
+
}
|
|
839
|
+
interface ConnectionHealth {
|
|
840
|
+
id: string;
|
|
841
|
+
status: 'healthy' | 'degraded' | 'unhealthy';
|
|
842
|
+
lastCheck: Date;
|
|
843
|
+
issues: string[];
|
|
844
|
+
metrics: ConnectionMetrics;
|
|
845
|
+
}
|
|
846
|
+
declare class WebSocketConnectionManager extends EventEmitter {
|
|
847
|
+
private connections;
|
|
848
|
+
private connectionMetrics;
|
|
849
|
+
private connectionPools;
|
|
850
|
+
private messageQueues;
|
|
851
|
+
private healthCheckTimer?;
|
|
852
|
+
private heartbeatTimer?;
|
|
853
|
+
private config;
|
|
854
|
+
private loadBalancerIndex;
|
|
855
|
+
constructor(config?: Partial<ConnectionConfig>);
|
|
856
|
+
registerConnection(ws: GenericWebSocket, connectionId: string, poolId?: string): void;
|
|
857
|
+
addToPool(connectionId: string, poolId: string): void;
|
|
858
|
+
removeFromPool(connectionId: string, poolId: string): void;
|
|
859
|
+
cleanupConnection(connectionId: string): void;
|
|
860
|
+
getConnectionMetrics(connectionId: string): ConnectionMetrics | null;
|
|
861
|
+
getAllConnectionMetrics(): ConnectionMetrics[];
|
|
862
|
+
getSystemStats(): {
|
|
863
|
+
totalConnections: number;
|
|
864
|
+
activeConnections: number;
|
|
865
|
+
totalPools: number;
|
|
866
|
+
totalQueuedMessages: number;
|
|
867
|
+
maxConnections: number;
|
|
868
|
+
connectionUtilization: number;
|
|
869
|
+
};
|
|
870
|
+
private setupHealthMonitoring;
|
|
871
|
+
private setupHeartbeat;
|
|
872
|
+
private performHealthChecks;
|
|
873
|
+
shutdown(): void;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
interface LiveRoomManagerInterface {
|
|
877
|
+
joinRoom<TState = any>(componentId: string, roomId: string, ws: any, initialState?: TState): {
|
|
878
|
+
state: TState;
|
|
879
|
+
};
|
|
880
|
+
leaveRoom(componentId: string, roomId: string): void;
|
|
881
|
+
cleanupComponent(componentId: string): void;
|
|
882
|
+
emitToRoom(roomId: string, event: string, data: any, excludeComponentId?: string): number;
|
|
883
|
+
setRoomState(roomId: string, updates: any, excludeComponentId?: string): void;
|
|
884
|
+
getRoomState<TState = any>(roomId: string): TState;
|
|
885
|
+
isInRoom(componentId: string, roomId: string): boolean;
|
|
886
|
+
getComponentRooms(componentId: string): string[];
|
|
887
|
+
getStats(): any;
|
|
888
|
+
}
|
|
889
|
+
interface LiveDebuggerInterface {
|
|
890
|
+
enabled: boolean;
|
|
891
|
+
trackStateChange(componentId: string, delta: Record<string, unknown>, fullState: Record<string, unknown>, source?: string): void;
|
|
892
|
+
trackActionCall(componentId: string, action: string, payload: unknown): void;
|
|
893
|
+
trackActionResult(componentId: string, action: string, result: unknown, duration: number): void;
|
|
894
|
+
trackActionError(componentId: string, action: string, error: string, duration: number): void;
|
|
895
|
+
trackRoomEmit(componentId: string, roomId: string, event: string, data: unknown): void;
|
|
896
|
+
}
|
|
897
|
+
interface LiveComponentContext {
|
|
898
|
+
roomEvents: RoomEventBus;
|
|
899
|
+
roomManager: LiveRoomManagerInterface;
|
|
900
|
+
debugger?: LiveDebuggerInterface;
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Set the global Live Component context.
|
|
904
|
+
* Called once by LiveServer.start() before any components are mounted.
|
|
905
|
+
*/
|
|
906
|
+
declare function setLiveComponentContext(ctx: LiveComponentContext): void;
|
|
907
|
+
/**
|
|
908
|
+
* Get the global Live Component context.
|
|
909
|
+
* Throws if LiveServer.start() hasn't been called yet.
|
|
910
|
+
*/
|
|
911
|
+
declare function getLiveComponentContext(): LiveComponentContext;
|
|
912
|
+
|
|
913
|
+
/** @internal Symbol key for singleton emit override */
|
|
914
|
+
declare const EMIT_OVERRIDE_KEY: unique symbol;
|
|
915
|
+
declare abstract class LiveComponent<TState = ComponentState, TPrivate extends Record<string, any> = Record<string, any>> {
|
|
916
|
+
/** Component name for registry lookup - must be defined in subclasses */
|
|
917
|
+
static componentName: string;
|
|
918
|
+
/** Default state - must be defined in subclasses */
|
|
919
|
+
static defaultState: any;
|
|
920
|
+
/**
|
|
921
|
+
* Per-component logging control. Silent by default.
|
|
922
|
+
*
|
|
923
|
+
* @example
|
|
924
|
+
* static logging = true // all categories
|
|
925
|
+
* static logging = ['lifecycle', 'messages'] // specific categories
|
|
926
|
+
*/
|
|
927
|
+
static logging?: boolean | readonly ('lifecycle' | 'messages' | 'state' | 'performance' | 'rooms' | 'websocket')[];
|
|
928
|
+
/**
|
|
929
|
+
* Component-level auth configuration.
|
|
930
|
+
*/
|
|
931
|
+
static auth?: LiveComponentAuth;
|
|
932
|
+
/**
|
|
933
|
+
* Per-action auth configuration.
|
|
934
|
+
*/
|
|
935
|
+
static actionAuth?: LiveActionAuthMap;
|
|
936
|
+
/**
|
|
937
|
+
* Data that survives HMR reloads.
|
|
938
|
+
*/
|
|
939
|
+
static persistent?: Record<string, any>;
|
|
940
|
+
/**
|
|
941
|
+
* When true, only ONE server-side instance exists for this component.
|
|
942
|
+
* All clients share the same state.
|
|
943
|
+
*/
|
|
944
|
+
static singleton?: boolean;
|
|
945
|
+
readonly id: string;
|
|
946
|
+
private _state;
|
|
947
|
+
state: TState;
|
|
948
|
+
protected ws: GenericWebSocket;
|
|
949
|
+
room?: string;
|
|
950
|
+
userId?: string;
|
|
951
|
+
broadcastToRoom: (message: BroadcastMessage) => void;
|
|
952
|
+
private _privateState;
|
|
953
|
+
private _authContext;
|
|
954
|
+
private roomEventUnsubscribers;
|
|
955
|
+
private joinedRooms;
|
|
956
|
+
protected roomType: string;
|
|
957
|
+
private roomHandles;
|
|
958
|
+
private _inStateChange;
|
|
959
|
+
[EMIT_OVERRIDE_KEY]: ((type: string, payload: any) => void) | null;
|
|
960
|
+
constructor(initialState: Partial<TState>, ws: GenericWebSocket, options?: {
|
|
961
|
+
room?: string;
|
|
962
|
+
userId?: string;
|
|
963
|
+
});
|
|
964
|
+
private createDirectStateAccessors;
|
|
965
|
+
private createStateProxy;
|
|
966
|
+
get $private(): TPrivate;
|
|
967
|
+
get $room(): ServerRoomProxy;
|
|
968
|
+
/**
|
|
969
|
+
* List of room IDs this component is participating in
|
|
970
|
+
*/
|
|
971
|
+
get $rooms(): string[];
|
|
972
|
+
get $auth(): LiveAuthContext;
|
|
973
|
+
/** @internal */
|
|
974
|
+
setAuthContext(context: LiveAuthContext): void;
|
|
975
|
+
get $persistent(): Record<string, any>;
|
|
976
|
+
protected onConnect(): void;
|
|
977
|
+
protected onMount(): void | Promise<void>;
|
|
978
|
+
protected onDisconnect(): void;
|
|
979
|
+
protected onDestroy(): void;
|
|
980
|
+
protected onStateChange(changes: Partial<TState>): void;
|
|
981
|
+
protected onRoomJoin(roomId: string): void;
|
|
982
|
+
protected onRoomLeave(roomId: string): void;
|
|
983
|
+
protected onRehydrate(previousState: TState): void;
|
|
984
|
+
protected onAction(action: string, payload: any): void | false | Promise<void | false>;
|
|
985
|
+
protected onClientJoin(connectionId: string, connectionCount: number): void;
|
|
986
|
+
protected onClientLeave(connectionId: string, connectionCount: number): void;
|
|
987
|
+
setState(updates: Partial<TState> | ((prev: TState) => Partial<TState>)): void;
|
|
988
|
+
setValue<K extends keyof TState>(payload: {
|
|
989
|
+
key: K;
|
|
990
|
+
value: TState[K];
|
|
991
|
+
}): Promise<{
|
|
992
|
+
success: true;
|
|
993
|
+
key: K;
|
|
994
|
+
value: TState[K];
|
|
995
|
+
}>;
|
|
996
|
+
static publicActions?: readonly string[];
|
|
997
|
+
private static readonly BLOCKED_ACTIONS;
|
|
998
|
+
executeAction(action: string, payload: any): Promise<any>;
|
|
999
|
+
protected emit(type: string, payload: any): void;
|
|
1000
|
+
protected broadcast(type: string, payload: any, excludeCurrentUser?: boolean): void;
|
|
1001
|
+
protected emitRoomEvent(event: string, data: any, notifySelf?: boolean): number;
|
|
1002
|
+
protected onRoomEvent<T = any>(event: string, handler: (data: T) => void): void;
|
|
1003
|
+
protected emitRoomEventWithState(event: string, data: any, stateUpdates: Partial<TState>): number;
|
|
1004
|
+
protected subscribeToRoom(roomId: string): Promise<void>;
|
|
1005
|
+
protected unsubscribeFromRoom(): Promise<void>;
|
|
1006
|
+
private generateId;
|
|
1007
|
+
destroy(): void;
|
|
1008
|
+
getSerializableState(): TState;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
interface ComponentMetadata {
|
|
1012
|
+
id: string;
|
|
1013
|
+
name: string;
|
|
1014
|
+
version: string;
|
|
1015
|
+
mountedAt: Date;
|
|
1016
|
+
lastActivity: Date;
|
|
1017
|
+
state: 'mounting' | 'active' | 'inactive' | 'error' | 'destroying';
|
|
1018
|
+
healthStatus: 'healthy' | 'degraded' | 'unhealthy';
|
|
1019
|
+
dependencies: string[];
|
|
1020
|
+
services: Map<string, any>;
|
|
1021
|
+
metrics: ComponentMetrics;
|
|
1022
|
+
migrationHistory: StateMigration[];
|
|
1023
|
+
}
|
|
1024
|
+
interface ComponentMetrics {
|
|
1025
|
+
renderCount: number;
|
|
1026
|
+
actionCount: number;
|
|
1027
|
+
errorCount: number;
|
|
1028
|
+
averageRenderTime: number;
|
|
1029
|
+
memoryUsage: number;
|
|
1030
|
+
lastRenderTime?: number;
|
|
1031
|
+
}
|
|
1032
|
+
interface StateMigration {
|
|
1033
|
+
fromVersion: string;
|
|
1034
|
+
toVersion: string;
|
|
1035
|
+
migratedAt: Date;
|
|
1036
|
+
success: boolean;
|
|
1037
|
+
error?: string;
|
|
1038
|
+
}
|
|
1039
|
+
interface ComponentRegistryDeps {
|
|
1040
|
+
authManager: LiveAuthManager;
|
|
1041
|
+
debugger: LiveDebugger;
|
|
1042
|
+
stateSignature: StateSignatureManager;
|
|
1043
|
+
performanceMonitor: PerformanceMonitor;
|
|
1044
|
+
}
|
|
1045
|
+
declare class ComponentRegistry {
|
|
1046
|
+
private components;
|
|
1047
|
+
private definitions;
|
|
1048
|
+
private metadata;
|
|
1049
|
+
private rooms;
|
|
1050
|
+
private wsConnections;
|
|
1051
|
+
private autoDiscoveredComponents;
|
|
1052
|
+
private healthCheckInterval?;
|
|
1053
|
+
private singletons;
|
|
1054
|
+
private authManager;
|
|
1055
|
+
private debugger;
|
|
1056
|
+
private stateSignature;
|
|
1057
|
+
private performanceMonitor;
|
|
1058
|
+
constructor(deps: ComponentRegistryDeps);
|
|
1059
|
+
private setupHealthMonitoring;
|
|
1060
|
+
registerComponent<TState>(definition: ComponentDefinition<TState>): void;
|
|
1061
|
+
registerComponentClass(name: string, componentClass: new (initialState: any, ws: GenericWebSocket, options?: {
|
|
1062
|
+
room?: string;
|
|
1063
|
+
userId?: string;
|
|
1064
|
+
}) => LiveComponent<any>): void;
|
|
1065
|
+
autoDiscoverComponents(componentsPath: string): Promise<void>;
|
|
1066
|
+
private isLiveComponentClass;
|
|
1067
|
+
mountComponent(ws: GenericWebSocket, componentName: string, props?: Record<string, unknown>, options?: {
|
|
1068
|
+
room?: string;
|
|
1069
|
+
userId?: string;
|
|
1070
|
+
version?: string;
|
|
1071
|
+
debugLabel?: string;
|
|
1072
|
+
}): Promise<{
|
|
1073
|
+
componentId: string;
|
|
1074
|
+
initialState: unknown;
|
|
1075
|
+
signedState: unknown;
|
|
1076
|
+
}>;
|
|
1077
|
+
rehydrateComponent(componentId: string, componentName: string, signedState: SignedState, ws: GenericWebSocket, options?: {
|
|
1078
|
+
room?: string;
|
|
1079
|
+
userId?: string;
|
|
1080
|
+
}): Promise<{
|
|
1081
|
+
success: boolean;
|
|
1082
|
+
newComponentId?: string;
|
|
1083
|
+
error?: string;
|
|
1084
|
+
}>;
|
|
1085
|
+
private ensureWsData;
|
|
1086
|
+
private isSingletonComponent;
|
|
1087
|
+
private removeSingletonConnection;
|
|
1088
|
+
unmountComponent(componentId: string, ws?: GenericWebSocket): Promise<void>;
|
|
1089
|
+
private getSingletonName;
|
|
1090
|
+
executeAction(componentId: string, action: string, payload: any): Promise<any>;
|
|
1091
|
+
updateProperty(componentId: string, property: string, value: any): void;
|
|
1092
|
+
subscribeToRoom(componentId: string, roomId: string): void;
|
|
1093
|
+
unsubscribeFromRoom(componentId: string, roomId: string): void;
|
|
1094
|
+
private unsubscribeFromAllRooms;
|
|
1095
|
+
broadcastToRoom(message: BroadcastMessage, senderComponentId?: string): void;
|
|
1096
|
+
handleMessage(ws: GenericWebSocket, message: LiveMessage): Promise<{
|
|
1097
|
+
success: boolean;
|
|
1098
|
+
result?: unknown;
|
|
1099
|
+
error?: string;
|
|
1100
|
+
} | null>;
|
|
1101
|
+
cleanupConnection(ws: GenericWebSocket): void;
|
|
1102
|
+
getStats(): {
|
|
1103
|
+
components: number;
|
|
1104
|
+
definitions: number;
|
|
1105
|
+
rooms: number;
|
|
1106
|
+
connections: number;
|
|
1107
|
+
singletons: {
|
|
1108
|
+
[k: string]: {
|
|
1109
|
+
componentId: string;
|
|
1110
|
+
connections: number;
|
|
1111
|
+
};
|
|
1112
|
+
};
|
|
1113
|
+
roomDetails: {
|
|
1114
|
+
[k: string]: number;
|
|
1115
|
+
};
|
|
1116
|
+
};
|
|
1117
|
+
getRegisteredComponentNames(): string[];
|
|
1118
|
+
getComponent(componentId: string): LiveComponent | undefined;
|
|
1119
|
+
getRoomComponents(roomId: string): LiveComponent[];
|
|
1120
|
+
private createComponentMetadata;
|
|
1121
|
+
updateComponentActivity(componentId: string): boolean;
|
|
1122
|
+
recordComponentMetrics(componentId: string, renderTime?: number, action?: string): void;
|
|
1123
|
+
recordComponentError(componentId: string, error: Error): void;
|
|
1124
|
+
private performHealthChecks;
|
|
1125
|
+
private cleanupComponent;
|
|
1126
|
+
cleanup(): void;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
declare class ConnectionRateLimiter {
|
|
1130
|
+
private tokens;
|
|
1131
|
+
private lastRefill;
|
|
1132
|
+
private readonly maxTokens;
|
|
1133
|
+
private readonly refillRate;
|
|
1134
|
+
constructor(maxTokens?: number, refillRate?: number);
|
|
1135
|
+
tryConsume(count?: number): boolean;
|
|
1136
|
+
private refill;
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Manages rate limiters for all connections.
|
|
1140
|
+
*/
|
|
1141
|
+
declare class RateLimiterRegistry {
|
|
1142
|
+
private limiters;
|
|
1143
|
+
private maxTokens;
|
|
1144
|
+
private refillRate;
|
|
1145
|
+
constructor(maxTokens?: number, refillRate?: number);
|
|
1146
|
+
/**
|
|
1147
|
+
* Get or create a rate limiter for a connection.
|
|
1148
|
+
*/
|
|
1149
|
+
get(connectionId: string): ConnectionRateLimiter;
|
|
1150
|
+
/**
|
|
1151
|
+
* Remove a rate limiter for a disconnected connection.
|
|
1152
|
+
*/
|
|
1153
|
+
remove(connectionId: string): void;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
interface LiveServerOptions {
|
|
1157
|
+
/** Transport adapter (Elysia, Express, etc.) */
|
|
1158
|
+
transport: LiveTransport;
|
|
1159
|
+
/** WebSocket endpoint path. Defaults to '/api/live/ws' */
|
|
1160
|
+
wsPath?: string;
|
|
1161
|
+
/** Enable debug mode. Defaults to false. */
|
|
1162
|
+
debug?: boolean;
|
|
1163
|
+
/** State signature configuration */
|
|
1164
|
+
stateSignature?: StateSignatureConfig;
|
|
1165
|
+
/** Performance monitor configuration */
|
|
1166
|
+
performance?: PerformanceConfig;
|
|
1167
|
+
/** File upload configuration */
|
|
1168
|
+
fileUpload?: FileUploadConfig;
|
|
1169
|
+
/** Connection manager configuration */
|
|
1170
|
+
connection?: Partial<ConnectionConfig>;
|
|
1171
|
+
/** Rate limiter: max tokens per connection */
|
|
1172
|
+
rateLimitMaxTokens?: number;
|
|
1173
|
+
/** Rate limiter: tokens refilled per second */
|
|
1174
|
+
rateLimitRefillRate?: number;
|
|
1175
|
+
/** Components path for auto-discovery */
|
|
1176
|
+
componentsPath?: string;
|
|
1177
|
+
/** HTTP monitoring routes prefix. Set to false to disable. Defaults to '/api/live' */
|
|
1178
|
+
httpPrefix?: string | false;
|
|
1179
|
+
}
|
|
1180
|
+
declare class LiveServer {
|
|
1181
|
+
readonly roomEvents: RoomEventBus;
|
|
1182
|
+
readonly roomManager: LiveRoomManager;
|
|
1183
|
+
readonly debugger: LiveDebugger;
|
|
1184
|
+
readonly authManager: LiveAuthManager;
|
|
1185
|
+
readonly stateSignature: StateSignatureManager;
|
|
1186
|
+
readonly performanceMonitor: PerformanceMonitor;
|
|
1187
|
+
readonly fileUploadManager: FileUploadManager;
|
|
1188
|
+
readonly connectionManager: WebSocketConnectionManager;
|
|
1189
|
+
readonly registry: ComponentRegistry;
|
|
1190
|
+
readonly rateLimiter: RateLimiterRegistry;
|
|
1191
|
+
private transport;
|
|
1192
|
+
private options;
|
|
1193
|
+
constructor(options: LiveServerOptions);
|
|
1194
|
+
/**
|
|
1195
|
+
* Register an auth provider.
|
|
1196
|
+
*/
|
|
1197
|
+
useAuth(provider: LiveAuthProvider): this;
|
|
1198
|
+
/**
|
|
1199
|
+
* Start the LiveServer: register WS + HTTP handlers on the transport.
|
|
1200
|
+
*/
|
|
1201
|
+
start(): Promise<void>;
|
|
1202
|
+
/**
|
|
1203
|
+
* Graceful shutdown.
|
|
1204
|
+
*/
|
|
1205
|
+
shutdown(): Promise<void>;
|
|
1206
|
+
private handleOpen;
|
|
1207
|
+
private handleMessage;
|
|
1208
|
+
private handleClose;
|
|
1209
|
+
private handleError;
|
|
1210
|
+
private handleRoomMessage;
|
|
1211
|
+
private buildHttpRoutes;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
/**
|
|
1215
|
+
* Encode a binary chunk message for transmission.
|
|
1216
|
+
* @param header - JSON metadata about the chunk
|
|
1217
|
+
* @param data - Raw binary data
|
|
1218
|
+
* @returns Combined buffer ready to send over WebSocket
|
|
1219
|
+
*/
|
|
1220
|
+
declare function encodeBinaryChunk(header: BinaryChunkHeader, data: Buffer | Uint8Array): Buffer;
|
|
1221
|
+
/**
|
|
1222
|
+
* Decode a binary chunk message received over WebSocket.
|
|
1223
|
+
* @param raw - Raw ArrayBuffer or Uint8Array from WebSocket
|
|
1224
|
+
* @returns Parsed header and binary chunk data
|
|
1225
|
+
*/
|
|
1226
|
+
declare function decodeBinaryChunk(raw: ArrayBuffer | Uint8Array): {
|
|
1227
|
+
header: BinaryChunkHeader;
|
|
1228
|
+
data: Buffer;
|
|
1229
|
+
};
|
|
1230
|
+
|
|
1231
|
+
/** Current protocol version */
|
|
1232
|
+
declare const PROTOCOL_VERSION = 1;
|
|
1233
|
+
/** Default WebSocket path */
|
|
1234
|
+
declare const DEFAULT_WS_PATH = "/api/live/ws";
|
|
1235
|
+
/** Default chunk size for file uploads (64KB) */
|
|
1236
|
+
declare const DEFAULT_CHUNK_SIZE: number;
|
|
1237
|
+
|
|
1238
|
+
/**
|
|
1239
|
+
* Auth context for authenticated users.
|
|
1240
|
+
* Provides type-safe helpers for role and permission checks.
|
|
1241
|
+
*
|
|
1242
|
+
* Used internally by the framework - devs access via this.$auth in LiveComponent.
|
|
1243
|
+
*/
|
|
1244
|
+
declare class AuthenticatedContext implements LiveAuthContext {
|
|
1245
|
+
readonly authenticated = true;
|
|
1246
|
+
readonly user: LiveAuthUser;
|
|
1247
|
+
readonly token?: string;
|
|
1248
|
+
readonly authenticatedAt: number;
|
|
1249
|
+
constructor(user: LiveAuthUser, token?: string);
|
|
1250
|
+
hasRole(role: string): boolean;
|
|
1251
|
+
hasAnyRole(roles: string[]): boolean;
|
|
1252
|
+
hasAllRoles(roles: string[]): boolean;
|
|
1253
|
+
hasPermission(permission: string): boolean;
|
|
1254
|
+
hasAllPermissions(permissions: string[]): boolean;
|
|
1255
|
+
hasAnyPermission(permissions: string[]): boolean;
|
|
1256
|
+
}
|
|
1257
|
+
/**
|
|
1258
|
+
* Context for unauthenticated users (guest).
|
|
1259
|
+
* Returned when no credentials are provided or when auth fails.
|
|
1260
|
+
*/
|
|
1261
|
+
declare class AnonymousContext implements LiveAuthContext {
|
|
1262
|
+
readonly authenticated = false;
|
|
1263
|
+
readonly user: undefined;
|
|
1264
|
+
readonly token: undefined;
|
|
1265
|
+
readonly authenticatedAt: undefined;
|
|
1266
|
+
hasRole(): boolean;
|
|
1267
|
+
hasAnyRole(): boolean;
|
|
1268
|
+
hasAllRoles(): boolean;
|
|
1269
|
+
hasPermission(): boolean;
|
|
1270
|
+
hasAllPermissions(): boolean;
|
|
1271
|
+
hasAnyPermission(): boolean;
|
|
1272
|
+
}
|
|
1273
|
+
/** Singleton for anonymous contexts */
|
|
1274
|
+
declare const ANONYMOUS_CONTEXT: AnonymousContext;
|
|
1275
|
+
|
|
1276
|
+
type RoomStateData = Record<string, any>;
|
|
1277
|
+
interface RoomInfo {
|
|
1278
|
+
state: RoomStateData;
|
|
1279
|
+
componentCount: number;
|
|
1280
|
+
createdAt: number;
|
|
1281
|
+
lastUpdate: number;
|
|
1282
|
+
}
|
|
1283
|
+
declare function createTypedRoomState<TRoomTypes extends Record<string, RoomStateData>>(): {
|
|
1284
|
+
get<K extends keyof TRoomTypes>(type: K, roomId: string, defaultState: TRoomTypes[K]): TRoomTypes[K];
|
|
1285
|
+
update<K extends keyof TRoomTypes>(type: K, roomId: string, updates: Partial<TRoomTypes[K]>): TRoomTypes[K];
|
|
1286
|
+
set<K extends keyof TRoomTypes>(type: K, roomId: string, state: TRoomTypes[K]): void;
|
|
1287
|
+
join<K extends keyof TRoomTypes>(type: K, roomId: string): void;
|
|
1288
|
+
leave<K extends keyof TRoomTypes>(type: K, roomId: string): void;
|
|
1289
|
+
has<K extends keyof TRoomTypes>(type: K, roomId: string): boolean;
|
|
1290
|
+
delete<K extends keyof TRoomTypes>(type: K, roomId: string): boolean;
|
|
1291
|
+
getStats(): {
|
|
1292
|
+
totalRooms: number;
|
|
1293
|
+
rooms: Record<string, {
|
|
1294
|
+
componentCount: number;
|
|
1295
|
+
stateKeys: string[];
|
|
1296
|
+
}>;
|
|
1297
|
+
};
|
|
1298
|
+
};
|
|
1299
|
+
declare class RoomStateManager {
|
|
1300
|
+
private rooms;
|
|
1301
|
+
get<T extends RoomStateData>(roomId: string, defaultState?: T): T;
|
|
1302
|
+
update<T extends RoomStateData>(roomId: string, updates: Partial<T>): T;
|
|
1303
|
+
set<T extends RoomStateData>(roomId: string, state: T): void;
|
|
1304
|
+
join(roomId: string): void;
|
|
1305
|
+
leave(roomId: string): void;
|
|
1306
|
+
has(roomId: string): boolean;
|
|
1307
|
+
delete(roomId: string): boolean;
|
|
1308
|
+
getStats(): {
|
|
1309
|
+
totalRooms: number;
|
|
1310
|
+
rooms: Record<string, {
|
|
1311
|
+
componentCount: number;
|
|
1312
|
+
stateKeys: string[];
|
|
1313
|
+
}>;
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
type LiveLogCategory = 'lifecycle' | 'messages' | 'state' | 'performance' | 'rooms' | 'websocket';
|
|
1318
|
+
type LiveLogConfig = boolean | readonly LiveLogCategory[];
|
|
1319
|
+
/**
|
|
1320
|
+
* Register a component's logging config (called on mount)
|
|
1321
|
+
*/
|
|
1322
|
+
declare function registerComponentLogging(componentId: string, config: LiveLogConfig | undefined): void;
|
|
1323
|
+
/**
|
|
1324
|
+
* Unregister component logging (called on unmount/cleanup)
|
|
1325
|
+
*/
|
|
1326
|
+
declare function unregisterComponentLogging(componentId: string): void;
|
|
1327
|
+
/**
|
|
1328
|
+
* Log a message gated by the component's logging config.
|
|
1329
|
+
* Always forwarded to the Live Debugger when active.
|
|
1330
|
+
*/
|
|
1331
|
+
declare function liveLog(category: LiveLogCategory, componentId: string | null, message: string, ...args: unknown[]): void;
|
|
1332
|
+
/**
|
|
1333
|
+
* Warn-level log gated by config.
|
|
1334
|
+
* Always forwarded to the Live Debugger when active.
|
|
1335
|
+
*/
|
|
1336
|
+
declare function liveWarn(category: LiveLogCategory, componentId: string | null, message: string, ...args: unknown[]): void;
|
|
1337
|
+
|
|
1338
|
+
/**
|
|
1339
|
+
* Extract all public action methods from a LiveComponent class
|
|
1340
|
+
* Excludes constructor, destroy, lifecycle methods, and inherited methods
|
|
1341
|
+
*/
|
|
1342
|
+
type ExtractActions<T extends LiveComponent<any>> = {
|
|
1343
|
+
[K in keyof T as K extends string ? T[K] extends (payload?: any) => Promise<any> ? K extends 'executeAction' | 'destroy' | 'getSerializableState' | 'setState' ? never : K : never : never]: T[K];
|
|
1344
|
+
};
|
|
1345
|
+
/**
|
|
1346
|
+
* Get all action names from a component
|
|
1347
|
+
*/
|
|
1348
|
+
type ActionNames<T extends LiveComponent<any>> = keyof ExtractActions<T>;
|
|
1349
|
+
/**
|
|
1350
|
+
* Get the payload type for a specific action
|
|
1351
|
+
*/
|
|
1352
|
+
type ActionPayload<T extends LiveComponent<any>, K extends ActionNames<T>> = ExtractActions<T>[K] extends (payload: infer P) => any ? P : ExtractActions<T>[K] extends () => any ? undefined : never;
|
|
1353
|
+
/**
|
|
1354
|
+
* Get the return type for a specific action (unwrapped from Promise)
|
|
1355
|
+
*/
|
|
1356
|
+
type ActionReturn<T extends LiveComponent<any>, K extends ActionNames<T>> = ExtractActions<T>[K] extends (...args: any[]) => Promise<infer R> ? R : ExtractActions<T>[K] extends (...args: any[]) => infer R ? R : never;
|
|
1357
|
+
/**
|
|
1358
|
+
* Get the state type from a LiveComponent class
|
|
1359
|
+
*/
|
|
1360
|
+
type InferComponentState<T extends LiveComponent<any>> = T extends LiveComponent<infer S> ? S : never;
|
|
1361
|
+
/**
|
|
1362
|
+
* Get the private state type from a LiveComponent class
|
|
1363
|
+
*/
|
|
1364
|
+
type InferPrivateState<T extends LiveComponent<any, any>> = T extends LiveComponent<any, infer P> ? P : never;
|
|
1365
|
+
/**
|
|
1366
|
+
* Type-safe call signature for a component
|
|
1367
|
+
*/
|
|
1368
|
+
type TypedCall<T extends LiveComponent<any>> = <K extends ActionNames<T>>(action: K, ...args: ActionPayload<T, K> extends undefined ? [] : [payload: ActionPayload<T, K>]) => Promise<void>;
|
|
1369
|
+
/**
|
|
1370
|
+
* Type-safe callAndWait signature for a component
|
|
1371
|
+
*/
|
|
1372
|
+
type TypedCallAndWait<T extends LiveComponent<any>> = <K extends ActionNames<T>>(action: K, ...args: ActionPayload<T, K> extends undefined ? [payload?: undefined, timeout?: number] : [payload: ActionPayload<T, K>, timeout?: number]) => Promise<ActionReturn<T, K>>;
|
|
1373
|
+
/**
|
|
1374
|
+
* Type-safe setValue signature for a component
|
|
1375
|
+
*/
|
|
1376
|
+
type TypedSetValue<T extends LiveComponent<any>> = <K extends keyof InferComponentState<T>>(key: K, value: InferComponentState<T>[K]) => Promise<void>;
|
|
1377
|
+
/**
|
|
1378
|
+
* Return type for useTypedLiveComponent hook
|
|
1379
|
+
*/
|
|
1380
|
+
interface UseTypedLiveComponentReturn<T extends LiveComponent<any>> {
|
|
1381
|
+
state: InferComponentState<T>;
|
|
1382
|
+
loading: boolean;
|
|
1383
|
+
error: string | null;
|
|
1384
|
+
connected: boolean;
|
|
1385
|
+
componentId: string | null;
|
|
1386
|
+
status: 'synced' | 'disconnected' | 'connecting' | 'reconnecting' | 'loading' | 'mounting' | 'error';
|
|
1387
|
+
call: TypedCall<T>;
|
|
1388
|
+
callAndWait: TypedCallAndWait<T>;
|
|
1389
|
+
setValue: TypedSetValue<T>;
|
|
1390
|
+
mount: () => Promise<void>;
|
|
1391
|
+
unmount: () => Promise<void>;
|
|
1392
|
+
useControlledField: <K extends keyof InferComponentState<T>>(field: K, action?: string) => {
|
|
1393
|
+
value: InferComponentState<T>[K];
|
|
1394
|
+
setValue: (value: InferComponentState<T>[K]) => void;
|
|
1395
|
+
commit: (value?: InferComponentState<T>[K]) => Promise<void>;
|
|
1396
|
+
isDirty: boolean;
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
export { ANONYMOUS_CONTEXT, type ActionNames, type ActionPayload, type ActionReturn, type ActiveUpload, AnonymousContext, AuthenticatedContext, type BinaryChunkHeader, type BroadcastMessage, type ComponentDefinition, type ComponentMetadata, type ComponentMetrics, type ComponentPerformanceMetrics, ComponentRegistry, type ComponentSnapshot, type ComponentState, type ConnectionConfig, type ConnectionHealth, type ConnectionMetrics, ConnectionRateLimiter, DEFAULT_CHUNK_SIZE, DEFAULT_WS_PATH, type DebugEvent, type DebugEventType, type DebugSnapshot, type DebugWsMessage, type EventHandler, type ExtractActions, type FileChunkData, type FileUploadChunkMessage, type FileUploadCompleteMessage, type FileUploadCompleteResponse, type FileUploadConfig, FileUploadManager, type FileUploadProgressResponse, type FileUploadStartMessage, type FluxStackWSData, type FluxStackWebSocket, type GenericWebSocket, type HttpRequest, type HttpResponse, type HttpRouteDefinition, type HybridComponentOptions, type HybridState, type InferComponentState, type InferPrivateState, type LiveActionAuth, type LiveActionAuthMap, type LiveAuthContext, type LiveAuthCredentials, LiveAuthManager, type LiveAuthProvider, type LiveAuthResult, type LiveAuthUser, LiveComponent, type LiveComponentAuth, type LiveComponentContext, type LiveComponentInstance, LiveDebugger, type LiveLogCategory, type LiveLogConfig, type LiveMessage, LiveRoomManager, LiveServer, type LiveServerOptions, type LiveTransport, type LiveWSData, PROTOCOL_VERSION, type PerformanceAlert, type PerformanceConfig, PerformanceMonitor, RateLimiterRegistry, RoomEventBus, type RoomInfo, type RoomMessage, type RoomStateData, RoomStateManager, type RoomSubscription, type ServerRoomHandle, type ServerRoomProxy, type SignedState, type StateMigration, type StateSignatureConfig, StateSignatureManager, type TypedCall, type TypedCallAndWait, type TypedSetValue, type UseTypedLiveComponentReturn, type WebSocketConfig, WebSocketConnectionManager, type WebSocketMessage, type WebSocketResponse, createTypedRoomEventBus, createTypedRoomState, decodeBinaryChunk, encodeBinaryChunk, getLiveComponentContext, liveLog, liveWarn, registerComponentLogging, setLiveComponentContext, unregisterComponentLogging };
|