feat(websocket): Add TypedRouter WebSocket integration, connection registry, peer tagging and broadcast APIs
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import * as plugins from '../plugins.js';
|
||||
import type { ISmartServeInstance, IConnectionInfo } from '../core/smartserve.interfaces.js';
|
||||
import type { ISmartServeInstance, IConnectionInfo, IWebSocketPeer, IWebSocketConnectionCallbacks } from '../core/smartserve.interfaces.js';
|
||||
import { BaseAdapter, type IAdapterCharacteristics, type TRequestHandler } from './adapter.base.js';
|
||||
|
||||
/**
|
||||
@@ -256,6 +256,10 @@ export class NodeAdapter extends BaseAdapter {
|
||||
|
||||
const wss = new WebSocketServer({ noServer: true });
|
||||
|
||||
// Get internal callbacks if typedRouter mode
|
||||
const callbacks = (hooks as any)._connectionCallbacks as IWebSocketConnectionCallbacks | undefined;
|
||||
const typedRouter = hooks.typedRouter;
|
||||
|
||||
this.server.on('upgrade', (request, socket, head) => {
|
||||
wss.handleUpgrade(request, socket, head, (ws) => {
|
||||
wss.emit('connection', ws, request);
|
||||
@@ -265,19 +269,44 @@ export class NodeAdapter extends BaseAdapter {
|
||||
wss.on('connection', (ws: any, request: any) => {
|
||||
const peer = this.wrapNodeWebSocket(ws, request);
|
||||
|
||||
// Register connection if typedRouter mode
|
||||
if (callbacks) {
|
||||
callbacks.onRegister(peer);
|
||||
}
|
||||
|
||||
// Call user's onOpen hook
|
||||
hooks.onOpen?.(peer);
|
||||
|
||||
ws.on('message', (data: Buffer | string) => {
|
||||
const message = {
|
||||
type: typeof data === 'string' ? 'text' as const : 'binary' as const,
|
||||
text: typeof data === 'string' ? data : undefined,
|
||||
data: Buffer.isBuffer(data) ? new Uint8Array(data) : undefined,
|
||||
size: typeof data === 'string' ? data.length : data.length,
|
||||
};
|
||||
hooks.onMessage?.(peer, message);
|
||||
ws.on('message', async (data: Buffer | string) => {
|
||||
// If typedRouter is configured, route through it
|
||||
if (typedRouter) {
|
||||
try {
|
||||
const messageText = typeof data === 'string' ? data : data.toString('utf8');
|
||||
const requestObj = JSON.parse(messageText);
|
||||
const response = await typedRouter.routeAndAddResponse(requestObj);
|
||||
if (response) {
|
||||
peer.send(JSON.stringify(response));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('TypedRouter message handling error:', error);
|
||||
}
|
||||
} else {
|
||||
// Legacy mode: use onMessage hook
|
||||
const message = {
|
||||
type: typeof data === 'string' ? 'text' as const : 'binary' as const,
|
||||
text: typeof data === 'string' ? data : undefined,
|
||||
data: Buffer.isBuffer(data) ? new Uint8Array(data) : undefined,
|
||||
size: typeof data === 'string' ? data.length : data.length,
|
||||
};
|
||||
hooks.onMessage?.(peer, message);
|
||||
}
|
||||
});
|
||||
|
||||
ws.on('close', (code: number, reason: Buffer) => {
|
||||
// Unregister connection if typedRouter mode
|
||||
if (callbacks) {
|
||||
callbacks.onUnregister(peer.id);
|
||||
}
|
||||
hooks.onClose?.(peer, code, reason.toString());
|
||||
});
|
||||
|
||||
@@ -298,7 +327,7 @@ export class NodeAdapter extends BaseAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
private wrapNodeWebSocket(ws: any, request: any): any {
|
||||
private wrapNodeWebSocket(ws: any, request: any): IWebSocketPeer {
|
||||
return {
|
||||
id: crypto.randomUUID(),
|
||||
url: request.url ?? '',
|
||||
@@ -312,6 +341,7 @@ export class NodeAdapter extends BaseAdapter {
|
||||
terminate: () => ws.terminate(),
|
||||
context: {} as any,
|
||||
data: new Map(),
|
||||
tags: new Set<string>(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user