@fluxstack/live-client 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/index.d.ts +359 -0
- package/dist/index.js +1148 -0
- package/dist/index.js.map +1 -0
- package/dist/live-client.browser.global.js +1175 -0
- package/dist/live-client.browser.global.js.map +1 -0
- package/package.json +39 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { WebSocketMessage, WebSocketResponse, FileUploadCompleteResponse, BinaryChunkHeader } from '@fluxstack/live';
|
|
2
|
+
|
|
3
|
+
/** Auth credentials to send during WebSocket connection */
|
|
4
|
+
interface LiveAuthOptions {
|
|
5
|
+
/** JWT or opaque token */
|
|
6
|
+
token?: string;
|
|
7
|
+
/** Provider name (if multiple auth providers configured) */
|
|
8
|
+
provider?: string;
|
|
9
|
+
/** Additional credentials (publicKey, signature, etc.) */
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
interface LiveConnectionOptions {
|
|
13
|
+
/** WebSocket URL. Auto-detected from window.location if omitted. */
|
|
14
|
+
url?: string;
|
|
15
|
+
/** Auth credentials to send on connection */
|
|
16
|
+
auth?: LiveAuthOptions;
|
|
17
|
+
/** Auto-connect on creation. Default: true */
|
|
18
|
+
autoConnect?: boolean;
|
|
19
|
+
/** Reconnect interval in ms. Default: 1000 */
|
|
20
|
+
reconnectInterval?: number;
|
|
21
|
+
/** Max reconnect attempts. Default: 5 */
|
|
22
|
+
maxReconnectAttempts?: number;
|
|
23
|
+
/** Heartbeat interval in ms. Default: 30000 */
|
|
24
|
+
heartbeatInterval?: number;
|
|
25
|
+
/** Enable debug logging. Default: false */
|
|
26
|
+
debug?: boolean;
|
|
27
|
+
}
|
|
28
|
+
interface LiveConnectionState {
|
|
29
|
+
connected: boolean;
|
|
30
|
+
connecting: boolean;
|
|
31
|
+
error: string | null;
|
|
32
|
+
connectionId: string | null;
|
|
33
|
+
authenticated: boolean;
|
|
34
|
+
}
|
|
35
|
+
type StateChangeCallback$1 = (state: LiveConnectionState) => void;
|
|
36
|
+
type ComponentCallback = (message: WebSocketResponse) => void;
|
|
37
|
+
/**
|
|
38
|
+
* Framework-agnostic WebSocket connection manager.
|
|
39
|
+
* Handles reconnection, heartbeat, request-response pattern, and message routing.
|
|
40
|
+
*/
|
|
41
|
+
declare class LiveConnection {
|
|
42
|
+
private ws;
|
|
43
|
+
private options;
|
|
44
|
+
private reconnectAttempts;
|
|
45
|
+
private reconnectTimeout;
|
|
46
|
+
private heartbeatInterval;
|
|
47
|
+
private componentCallbacks;
|
|
48
|
+
private pendingRequests;
|
|
49
|
+
private stateListeners;
|
|
50
|
+
private _state;
|
|
51
|
+
constructor(options?: LiveConnectionOptions);
|
|
52
|
+
get state(): LiveConnectionState;
|
|
53
|
+
/** Subscribe to connection state changes */
|
|
54
|
+
onStateChange(callback: StateChangeCallback$1): () => void;
|
|
55
|
+
private setState;
|
|
56
|
+
private getWebSocketUrl;
|
|
57
|
+
private log;
|
|
58
|
+
/** Generate unique request ID */
|
|
59
|
+
generateRequestId(): string;
|
|
60
|
+
/** Connect to WebSocket server */
|
|
61
|
+
connect(): void;
|
|
62
|
+
/** Disconnect from WebSocket server */
|
|
63
|
+
disconnect(): void;
|
|
64
|
+
/** Manual reconnect */
|
|
65
|
+
reconnect(): void;
|
|
66
|
+
private attemptReconnect;
|
|
67
|
+
private startHeartbeat;
|
|
68
|
+
private stopHeartbeat;
|
|
69
|
+
private handleMessage;
|
|
70
|
+
/** Send message without waiting for response */
|
|
71
|
+
sendMessage(message: WebSocketMessage): Promise<void>;
|
|
72
|
+
/** Send message and wait for response */
|
|
73
|
+
sendMessageAndWait(message: WebSocketMessage, timeout?: number): Promise<WebSocketResponse>;
|
|
74
|
+
/** Send binary data and wait for response (for file uploads) */
|
|
75
|
+
sendBinaryAndWait(data: ArrayBuffer, requestId: string, timeout?: number): Promise<WebSocketResponse>;
|
|
76
|
+
/** Register a component message callback */
|
|
77
|
+
registerComponent(componentId: string, callback: ComponentCallback): () => void;
|
|
78
|
+
/** Unregister a component */
|
|
79
|
+
unregisterComponent(componentId: string): void;
|
|
80
|
+
/** Authenticate (or re-authenticate) the WebSocket connection */
|
|
81
|
+
authenticate(credentials: LiveAuthOptions): Promise<boolean>;
|
|
82
|
+
/** Get the raw WebSocket instance */
|
|
83
|
+
getWebSocket(): WebSocket | null;
|
|
84
|
+
/** Destroy the connection and clean up all resources */
|
|
85
|
+
destroy(): void;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
interface LiveComponentOptions<TState = Record<string, any>> {
|
|
89
|
+
/** Initial state to merge with server defaults */
|
|
90
|
+
initialState?: Partial<TState>;
|
|
91
|
+
/** Room to join on mount */
|
|
92
|
+
room?: string;
|
|
93
|
+
/** User ID for component isolation */
|
|
94
|
+
userId?: string;
|
|
95
|
+
/** Auto-mount when connection is ready. Default: true */
|
|
96
|
+
autoMount?: boolean;
|
|
97
|
+
/** Enable debug logging. Default: false */
|
|
98
|
+
debug?: boolean;
|
|
99
|
+
}
|
|
100
|
+
type StateChangeCallback<TState> = (state: TState, delta: Partial<TState> | null) => void;
|
|
101
|
+
type ErrorCallback = (error: string) => void;
|
|
102
|
+
/**
|
|
103
|
+
* High-level handle for a live component instance.
|
|
104
|
+
* Manages mount lifecycle, state sync, and action calling.
|
|
105
|
+
* Framework-agnostic — works with vanilla JS, Vue, Svelte, etc.
|
|
106
|
+
*/
|
|
107
|
+
declare class LiveComponentHandle<TState extends Record<string, any> = Record<string, any>> {
|
|
108
|
+
private connection;
|
|
109
|
+
private componentName;
|
|
110
|
+
private options;
|
|
111
|
+
private _componentId;
|
|
112
|
+
private _state;
|
|
113
|
+
private _mounted;
|
|
114
|
+
private _mounting;
|
|
115
|
+
private _error;
|
|
116
|
+
private stateListeners;
|
|
117
|
+
private errorListeners;
|
|
118
|
+
private unregisterComponent;
|
|
119
|
+
private unsubConnection;
|
|
120
|
+
constructor(connection: LiveConnection, componentName: string, options?: LiveComponentOptions<TState>);
|
|
121
|
+
/** Current component state */
|
|
122
|
+
get state(): Readonly<TState>;
|
|
123
|
+
/** Server-assigned component ID (null before mount) */
|
|
124
|
+
get componentId(): string | null;
|
|
125
|
+
/** Whether the component has been mounted */
|
|
126
|
+
get mounted(): boolean;
|
|
127
|
+
/** Whether the component is currently mounting */
|
|
128
|
+
get mounting(): boolean;
|
|
129
|
+
/** Last error message */
|
|
130
|
+
get error(): string | null;
|
|
131
|
+
/** Mount the component on the server */
|
|
132
|
+
mount(): Promise<void>;
|
|
133
|
+
/** Unmount the component from the server */
|
|
134
|
+
unmount(): Promise<void>;
|
|
135
|
+
/** Destroy the handle and clean up all resources */
|
|
136
|
+
destroy(): void;
|
|
137
|
+
/**
|
|
138
|
+
* Call an action on the server component.
|
|
139
|
+
* Returns the action's return value.
|
|
140
|
+
*/
|
|
141
|
+
call<R = any>(action: string, payload?: Record<string, any>): Promise<R>;
|
|
142
|
+
/**
|
|
143
|
+
* Subscribe to state changes.
|
|
144
|
+
* Callback receives the full new state and the delta (or null for full updates).
|
|
145
|
+
* Returns an unsubscribe function.
|
|
146
|
+
*/
|
|
147
|
+
onStateChange(callback: StateChangeCallback<TState>): () => void;
|
|
148
|
+
/**
|
|
149
|
+
* Subscribe to errors.
|
|
150
|
+
* Returns an unsubscribe function.
|
|
151
|
+
*/
|
|
152
|
+
onError(callback: ErrorCallback): () => void;
|
|
153
|
+
private handleServerMessage;
|
|
154
|
+
private notifyStateChange;
|
|
155
|
+
private notifyError;
|
|
156
|
+
private cleanup;
|
|
157
|
+
private log;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
type EventHandler<T = any> = (data: T) => void;
|
|
161
|
+
type Unsubscribe = () => void;
|
|
162
|
+
/** Message from client to server */
|
|
163
|
+
interface RoomClientMessage {
|
|
164
|
+
type: 'ROOM_JOIN' | 'ROOM_LEAVE' | 'ROOM_EMIT' | 'ROOM_STATE_GET' | 'ROOM_STATE_SET';
|
|
165
|
+
componentId: string;
|
|
166
|
+
roomId: string;
|
|
167
|
+
event?: string;
|
|
168
|
+
data?: any;
|
|
169
|
+
timestamp: number;
|
|
170
|
+
}
|
|
171
|
+
/** Message from server to client */
|
|
172
|
+
interface RoomServerMessage {
|
|
173
|
+
type: 'ROOM_EVENT' | 'ROOM_STATE' | 'ROOM_SYSTEM' | 'ROOM_JOINED' | 'ROOM_LEFT';
|
|
174
|
+
componentId: string;
|
|
175
|
+
roomId: string;
|
|
176
|
+
event: string;
|
|
177
|
+
data: any;
|
|
178
|
+
timestamp: number;
|
|
179
|
+
}
|
|
180
|
+
/** Interface of an individual room handle */
|
|
181
|
+
interface RoomHandle<TState = any, TEvents extends Record<string, any> = Record<string, any>> {
|
|
182
|
+
readonly id: string;
|
|
183
|
+
readonly joined: boolean;
|
|
184
|
+
readonly state: TState;
|
|
185
|
+
join: (initialState?: TState) => Promise<void>;
|
|
186
|
+
leave: () => Promise<void>;
|
|
187
|
+
emit: <K extends keyof TEvents>(event: K, data: TEvents[K]) => void;
|
|
188
|
+
on: <K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>) => Unsubscribe;
|
|
189
|
+
onSystem: (event: string, handler: EventHandler) => Unsubscribe;
|
|
190
|
+
setState: (updates: Partial<TState>) => void;
|
|
191
|
+
}
|
|
192
|
+
/** Proxy interface for $room - callable as function or object */
|
|
193
|
+
interface RoomProxy<TState = any, TEvents extends Record<string, any> = Record<string, any>> {
|
|
194
|
+
(roomId: string): RoomHandle<TState, TEvents>;
|
|
195
|
+
readonly id: string | null;
|
|
196
|
+
readonly joined: boolean;
|
|
197
|
+
readonly state: TState;
|
|
198
|
+
join: (initialState?: TState) => Promise<void>;
|
|
199
|
+
leave: () => Promise<void>;
|
|
200
|
+
emit: <K extends keyof TEvents>(event: K, data: TEvents[K]) => void;
|
|
201
|
+
on: <K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>) => Unsubscribe;
|
|
202
|
+
onSystem: (event: string, handler: EventHandler) => Unsubscribe;
|
|
203
|
+
setState: (updates: Partial<TState>) => void;
|
|
204
|
+
}
|
|
205
|
+
interface RoomManagerOptions {
|
|
206
|
+
componentId: string | null;
|
|
207
|
+
defaultRoom?: string;
|
|
208
|
+
sendMessage: (msg: any) => void;
|
|
209
|
+
sendMessageAndWait: (msg: any, timeout?: number) => Promise<any>;
|
|
210
|
+
onMessage: (handler: (msg: RoomServerMessage) => void) => Unsubscribe;
|
|
211
|
+
}
|
|
212
|
+
/** Client-side room manager. Framework-agnostic. */
|
|
213
|
+
declare class RoomManager<TState = any, TEvents extends Record<string, any> = Record<string, any>> {
|
|
214
|
+
private componentId;
|
|
215
|
+
private defaultRoom;
|
|
216
|
+
private rooms;
|
|
217
|
+
private handles;
|
|
218
|
+
private sendMessage;
|
|
219
|
+
private sendMessageAndWait;
|
|
220
|
+
private globalUnsubscribe;
|
|
221
|
+
constructor(options: RoomManagerOptions);
|
|
222
|
+
private handleServerMessage;
|
|
223
|
+
private getOrCreateRoom;
|
|
224
|
+
/** Create handle for a specific room (cached) */
|
|
225
|
+
createHandle(roomId: string): RoomHandle<TState, TEvents>;
|
|
226
|
+
/** Create the $room proxy */
|
|
227
|
+
createProxy(): RoomProxy<TState, TEvents>;
|
|
228
|
+
/** List of rooms currently joined */
|
|
229
|
+
getJoinedRooms(): string[];
|
|
230
|
+
/** Update componentId (when component mounts) */
|
|
231
|
+
setComponentId(id: string | null): void;
|
|
232
|
+
/** Cleanup */
|
|
233
|
+
destroy(): void;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
interface AdaptiveChunkConfig {
|
|
237
|
+
minChunkSize: number;
|
|
238
|
+
maxChunkSize: number;
|
|
239
|
+
initialChunkSize: number;
|
|
240
|
+
targetLatency: number;
|
|
241
|
+
adjustmentFactor: number;
|
|
242
|
+
measurementWindow: number;
|
|
243
|
+
}
|
|
244
|
+
interface ChunkMetrics {
|
|
245
|
+
chunkIndex: number;
|
|
246
|
+
chunkSize: number;
|
|
247
|
+
startTime: number;
|
|
248
|
+
endTime: number;
|
|
249
|
+
latency: number;
|
|
250
|
+
throughput: number;
|
|
251
|
+
success: boolean;
|
|
252
|
+
}
|
|
253
|
+
declare class AdaptiveChunkSizer {
|
|
254
|
+
private config;
|
|
255
|
+
private currentChunkSize;
|
|
256
|
+
private metrics;
|
|
257
|
+
private consecutiveErrors;
|
|
258
|
+
private consecutiveSuccesses;
|
|
259
|
+
constructor(config?: Partial<AdaptiveChunkConfig>);
|
|
260
|
+
getChunkSize(): number;
|
|
261
|
+
recordChunkStart(_chunkIndex: number): number;
|
|
262
|
+
recordChunkComplete(chunkIndex: number, chunkSize: number, startTime: number, success: boolean): void;
|
|
263
|
+
private adjustUp;
|
|
264
|
+
private adjustDown;
|
|
265
|
+
getAverageThroughput(): number;
|
|
266
|
+
getStats(): {
|
|
267
|
+
currentChunkSize: number;
|
|
268
|
+
averageThroughput: number;
|
|
269
|
+
consecutiveSuccesses: number;
|
|
270
|
+
consecutiveErrors: number;
|
|
271
|
+
totalMeasurements: number;
|
|
272
|
+
};
|
|
273
|
+
reset(): void;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Creates a binary message with header + data
|
|
277
|
+
* Format: [4 bytes header length LE][JSON header][binary data]
|
|
278
|
+
*/
|
|
279
|
+
declare function createBinaryChunkMessage(header: BinaryChunkHeader, chunkData: Uint8Array): ArrayBuffer;
|
|
280
|
+
interface ChunkedUploadOptions {
|
|
281
|
+
chunkSize?: number;
|
|
282
|
+
maxFileSize?: number;
|
|
283
|
+
allowedTypes?: string[];
|
|
284
|
+
sendMessageAndWait: (message: any, timeout?: number) => Promise<any>;
|
|
285
|
+
sendBinaryAndWait?: (data: ArrayBuffer, requestId: string, timeout?: number) => Promise<any>;
|
|
286
|
+
onProgress?: (progress: number, bytesUploaded: number, totalBytes: number) => void;
|
|
287
|
+
onComplete?: (response: FileUploadCompleteResponse) => void;
|
|
288
|
+
onError?: (error: string) => void;
|
|
289
|
+
adaptiveChunking?: boolean;
|
|
290
|
+
adaptiveConfig?: Partial<AdaptiveChunkConfig>;
|
|
291
|
+
useBinaryProtocol?: boolean;
|
|
292
|
+
}
|
|
293
|
+
interface ChunkedUploadState {
|
|
294
|
+
uploading: boolean;
|
|
295
|
+
progress: number;
|
|
296
|
+
error: string | null;
|
|
297
|
+
uploadId: string | null;
|
|
298
|
+
bytesUploaded: number;
|
|
299
|
+
totalBytes: number;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Framework-agnostic chunked file uploader.
|
|
303
|
+
* Manages the upload lifecycle without any UI framework dependency.
|
|
304
|
+
*/
|
|
305
|
+
declare class ChunkedUploader {
|
|
306
|
+
private componentId;
|
|
307
|
+
private options;
|
|
308
|
+
private abortController;
|
|
309
|
+
private adaptiveSizer;
|
|
310
|
+
private _state;
|
|
311
|
+
private stateListeners;
|
|
312
|
+
constructor(componentId: string, options: ChunkedUploadOptions);
|
|
313
|
+
get state(): ChunkedUploadState;
|
|
314
|
+
onStateChange(callback: (state: ChunkedUploadState) => void): () => void;
|
|
315
|
+
private setState;
|
|
316
|
+
uploadFile(file: File): Promise<void>;
|
|
317
|
+
cancelUpload(): void;
|
|
318
|
+
reset(): void;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
interface PersistedState {
|
|
322
|
+
componentName: string;
|
|
323
|
+
signedState: any;
|
|
324
|
+
room?: string;
|
|
325
|
+
userId?: string;
|
|
326
|
+
lastUpdate: number;
|
|
327
|
+
}
|
|
328
|
+
declare function persistState(enabled: boolean, name: string, signedState: any, room?: string, userId?: string): void;
|
|
329
|
+
declare function getPersistedState(enabled: boolean, name: string): PersistedState | null;
|
|
330
|
+
declare function clearPersistedState(enabled: boolean, name: string): void;
|
|
331
|
+
|
|
332
|
+
interface StateValidation {
|
|
333
|
+
checksum: string;
|
|
334
|
+
version: number;
|
|
335
|
+
timestamp: number;
|
|
336
|
+
source: 'client' | 'server' | 'mount';
|
|
337
|
+
}
|
|
338
|
+
interface StateConflict {
|
|
339
|
+
property: string;
|
|
340
|
+
clientValue: any;
|
|
341
|
+
serverValue: any;
|
|
342
|
+
timestamp: number;
|
|
343
|
+
resolved: boolean;
|
|
344
|
+
}
|
|
345
|
+
interface HybridState<T> {
|
|
346
|
+
data: T;
|
|
347
|
+
validation: StateValidation;
|
|
348
|
+
status: 'synced' | 'pending' | 'conflict';
|
|
349
|
+
}
|
|
350
|
+
declare class StateValidator {
|
|
351
|
+
static generateChecksum(state: any): string;
|
|
352
|
+
static createValidation(state: any, source?: 'client' | 'server' | 'mount'): StateValidation;
|
|
353
|
+
static detectConflicts<T>(clientState: T, serverState: T, excludeFields?: string[]): StateConflict[];
|
|
354
|
+
static mergeStates<T>(clientState: T, serverState: T, conflicts: StateConflict[], strategy?: 'client' | 'server' | 'smart'): T;
|
|
355
|
+
static validateState<T>(hybridState: HybridState<T>): boolean;
|
|
356
|
+
static updateValidation<T>(hybridState: HybridState<T>, source?: 'client' | 'server' | 'mount'): HybridState<T>;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export { type AdaptiveChunkConfig, AdaptiveChunkSizer, type ChunkMetrics, type ChunkedUploadOptions, type ChunkedUploadState, ChunkedUploader, type EventHandler, type HybridState, type LiveAuthOptions, LiveComponentHandle, type LiveComponentOptions, LiveConnection, type LiveConnectionOptions, type LiveConnectionState, type PersistedState, type RoomClientMessage, type RoomHandle, RoomManager, type RoomManagerOptions, type RoomProxy, type RoomServerMessage, type StateConflict, type StateValidation, StateValidator, type Unsubscribe, clearPersistedState, createBinaryChunkMessage, getPersistedState, persistState };
|