Skip to content

Commit 109cfc4

Browse files
committed
use generics
1 parent 23f8496 commit 109cfc4

File tree

1 file changed

+56
-22
lines changed

1 file changed

+56
-22
lines changed

templates/web/src/services/realtime.ts.twig

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,29 @@
11
import { {{ spec.title | caseUcfirst}}Exception, Client } from '../client';
22

3-
type RealtimeCallback = {
3+
export type RealtimeSubscription = {
4+
close: () => Promise<void>;
5+
}
6+
7+
export type RealtimeCallback<T = any> = {
48
channels: Set<string>;
5-
callback: (event: RealtimeResponseEvent) => void;
9+
callback: (event: RealtimeResponseEvent<T>) => void;
610
}
711

8-
type RealtimeResponseEvent = {
12+
export type RealtimeResponse = {
13+
type: string;
14+
data?: any;
15+
}
16+
17+
export type RealtimeResponseEvent<T = any> = {
918
events: string[];
1019
channels: string[];
1120
timestamp: string;
12-
payload: Record<string, any>;
21+
payload: T;
1322
}
1423

15-
export type RealtimeSubscription = {
16-
close: () => Promise<void>;
24+
export enum RealtimeCode {
25+
POLICY_VIOLATION = 1008,
26+
UNKNOWN_ERROR = -1
1727
}
1828

1929
export class Realtime {
@@ -26,7 +36,7 @@ export class Realtime {
2636
private client: Client;
2737
private socket?: WebSocket;
2838
private activeChannels = new Set<string>();
29-
private activeSubscriptions = new Map<number, RealtimeCallback>();
39+
private activeSubscriptions = new Map<number, RealtimeCallback<any>>();
3040
private heartbeatTimer?: number;
3141

3242
private subCallDepth = 0;
@@ -132,18 +142,18 @@ export class Realtime {
132142

133143
this.socket.addEventListener('message', (event: MessageEvent) => {
134144
try {
135-
const message = JSON.parse(event.data);
145+
const message = JSON.parse(event.data) as RealtimeResponse;
136146
this.handleMessage(message);
137147
} catch (error) {
138148
console.error('Failed to parse message:', error);
139149
}
140150
});
141151

142-
this.socket.addEventListener('close', async () => {
152+
this.socket.addEventListener('close', async (event: CloseEvent) => {
143153
this.stopHeartbeat();
144154
this.onCloseCallbacks.forEach(callback => callback());
145155

146-
if (!this.reconnect) {
156+
if (!this.reconnect || event.code === RealtimeCode.POLICY_VIOLATION) {
147157
this.reconnect = true;
148158
return;
149159
}
@@ -189,7 +199,7 @@ export class Realtime {
189199
this.socket.addEventListener('close', () => {
190200
resolve();
191201
}, { once: true });
192-
this.socket.close();
202+
this.socket.close(RealtimeCode.POLICY_VIOLATION);
193203
} else {
194204
resolve();
195205
}
@@ -222,7 +232,7 @@ export class Realtime {
222232
*/
223233
public async subscribe(
224234
channel: string,
225-
callback: (event: RealtimeResponseEvent) => void
235+
callback: (event: RealtimeResponseEvent<any>) => void
226236
): Promise<RealtimeSubscription>;
227237

228238
/**
@@ -234,12 +244,36 @@ export class Realtime {
234244
*/
235245
public async subscribe(
236246
channels: string[],
237-
callback: (event: RealtimeResponseEvent) => void
247+
callback: (event: RealtimeResponseEvent<any>) => void
238248
): Promise<RealtimeSubscription>;
239249

240-
public async subscribe(
250+
/**
251+
* Subscribe to a single channel with typed payload
252+
*
253+
* @param {string} channel - Channel name to subscribe to
254+
* @param {Function} callback - Callback function to handle events with typed payload
255+
* @returns {Promise<RealtimeSubscription>} Subscription object with close method
256+
*/
257+
public async subscribe<T>(
258+
channel: string,
259+
callback: (event: RealtimeResponseEvent<T>) => void
260+
): Promise<RealtimeSubscription>;
261+
262+
/**
263+
* Subscribe to multiple channels with typed payload
264+
*
265+
* @param {string[]} channels - Array of channel names to subscribe to
266+
* @param {Function} callback - Callback function to handle events with typed payload
267+
* @returns {Promise<RealtimeSubscription>} Subscription object with close method
268+
*/
269+
public async subscribe<T>(
270+
channels: string[],
271+
callback: (event: RealtimeResponseEvent<T>) => void
272+
): Promise<RealtimeSubscription>;
273+
274+
public async subscribe<T = any>(
241275
channelsOrChannel: string | string[],
242-
callback: (event: RealtimeResponseEvent) => void
276+
callback: (event: RealtimeResponseEvent<T>) => void
243277
): Promise<RealtimeSubscription> {
244278
const channels = Array.isArray(channelsOrChannel)
245279
? new Set(channelsOrChannel)
@@ -291,7 +325,7 @@ export class Realtime {
291325
);
292326
}
293327

294-
private handleMessage(message: any): void {
328+
private handleMessage(message: RealtimeResponse): void {
295329
if (!message.type) {
296330
return;
297331
}
@@ -309,23 +343,23 @@ export class Realtime {
309343
}
310344
}
311345

312-
private handleResponseError(message: any): void {
346+
private handleResponseError(message: RealtimeResponse): void {
313347
const error = new {{spec.title | caseUcfirst}}Exception(
314-
message.message || message.data?.message || 'Unknown error'
348+
message.data?.message || 'Unknown error'
315349
);
316-
const statusCode = message.code || message.data?.code;
350+
const statusCode = message.data?.code;
317351
this.onErrorCallbacks.forEach(callback => callback(error, statusCode));
318352
}
319353

320-
private handleResponseEvent(message: any): void {
354+
private handleResponseEvent(message: RealtimeResponse): void {
321355
const data = message.data;
322356
if (!data) {
323357
return;
324358
}
325359

326360
const channels = data.channels as string[];
327361
const events = data.events as string[];
328-
const payload = data.payload as Record<string, any>;
362+
const payload = data.payload;
329363
const timestamp = data.timestamp as string;
330364

331365
if (!channels || !events || !payload) {
@@ -346,7 +380,7 @@ export class Realtime {
346380
);
347381

348382
if (hasSubscribedChannel) {
349-
const response: RealtimeResponseEvent = {
383+
const response: RealtimeResponseEvent<any> = {
350384
events,
351385
channels,
352386
timestamp,

0 commit comments

Comments
 (0)