import * as plugins from './typedsocket.plugins.js'; const publicRoleName = 'publicRoleName'; const publicRolePass = 'publicRolePass'; export type TTypedSocketSide = 'server' | 'client'; export class TypedSocket { // STATIC /** * creates a typedsocket server * note: this will fail in browser environments as server libs are not bundled. */ public static async createServer( typedrouterArg: plugins.typedrequest.TypedRouter, smartexpressServerArg?: any ): Promise { const smartsocketServer = new plugins.smartsocket.Smartsocket({ alias: 'typedsocketServer', port: 3000, }); if (smartexpressServerArg) { smartsocketServer.setExternalServer('smartexpress', smartexpressServerArg); } smartsocketServer.socketFunctions.add( new plugins.smartsocket.SocketFunction({ funcName: 'processMessage', funcDef: async (dataArg, socketConnectionArg) => { return typedrouterArg.routeAndAddResponse(dataArg); }, }) ); const typedsocket = new TypedSocket( 'server', typedrouterArg, async ( dataArg: T, targetConnectionArg?: plugins.smartsocket.SocketConnection ): Promise => { if (!targetConnectionArg) { if ((smartsocketServer.socketConnections.getArray().length = 1)) { console.log( 'Since no targetConnection was supplied and there is only one active one present, choosing that one automatically' ); targetConnectionArg = smartsocketServer.socketConnections.getArray()[0]; } else { throw new Error( 'you need to specify the wanted targetConnection. Currently no target is selectable automatically.' ); } } const response: T = (await smartsocketServer.clientCall( 'processMessage', dataArg, targetConnectionArg )) as any; return response; }, smartsocketServer ); await smartsocketServer.start(); return typedsocket; } public static async createClient( typedrouterArg: plugins.typedrequest.TypedRouter, serverUrlArg: string, aliasArg = 'clientArg' ): Promise { const domain = new plugins.smartstring.Domain(serverUrlArg); const socketOptions: plugins.smartsocket.ISmartsocketClientOptions = { alias: aliasArg, port: domain.port || 3000, url: `${domain.nodeParsedUrl.protocol}//${domain.nodeParsedUrl.hostname}`, autoReconnect: true, }; console.log(`starting typedsocket with the following settings:`); console.log(socketOptions); const smartsocketClient = new plugins.smartsocket.SmartsocketClient(socketOptions); smartsocketClient.addSocketFunction( new plugins.smartsocket.SocketFunction({ funcName: 'processMessage', funcDef: async (dataArg, socketConnectionArg) => { return typedrouterArg.routeAndAddResponse(dataArg); }, }) ); const typedsocket = new TypedSocket( 'client', typedrouterArg, async (dataArg: T): Promise => { const response: T = smartsocketClient.serverCall('processMessage', dataArg) as any as T; return response; }, smartsocketClient ); console.log(`typedsocket triggering smartsocket to connect...`); const before = Date.now(); await smartsocketClient.connect(); console.log(`typedsocket triggered smartsocket connected in ${Date.now() - before}ms!!!`) return typedsocket; } public static useWindowLocationOriginUrl = () => { const windowLocationResult = plugins.smarturl.Smarturl.createFromUrl(globalThis.location.origin).toString(); return windowLocationResult; } // INSTANCE public side: TTypedSocketSide; public typedrouter: plugins.typedrequest.TypedRouter; public get eventSubject() { return this.socketServerOrClient.eventSubject; } private postMethod: plugins.typedrequest.IPostMethod & (( typedRequestPostObject: plugins.typedrequestInterfaces.ITypedRequest, socketConnectionArg?: plugins.smartsocket.SocketConnection ) => Promise); private socketServerOrClient: | plugins.smartsocket.Smartsocket | plugins.smartsocket.SmartsocketClient; constructor( sideArg: TTypedSocketSide, typedrouterArg: plugins.typedrequest.TypedRouter, postMethodArg: plugins.typedrequest.IPostMethod, socketServerOrClientArg: plugins.smartsocket.Smartsocket | plugins.smartsocket.SmartsocketClient ) { this.side = sideArg; this.typedrouter = typedrouterArg; this.postMethod = postMethodArg; this.socketServerOrClient = socketServerOrClientArg; } public addTag( nameArg: T['name'], payloadArg: T['payload'] ) { if ( this.side === 'client' && this.socketServerOrClient instanceof plugins.smartsocket.SmartsocketClient ) { this.socketServerOrClient.socketConnection.addTag({ id: nameArg, payload: payloadArg, }); } else { throw new Error('tagging is only supported on clients'); } } public createTypedRequest( methodName: T['method'], targetConnection?: plugins.smartsocket.SocketConnection ): plugins.typedrequest.TypedRequest { const typedrequest = new plugins.typedrequest.TypedRequest( new plugins.typedrequest.TypedTarget({ postMethod: async (requestDataArg) => { const result = await this.postMethod(requestDataArg, targetConnection); return result; }, }), methodName ); return typedrequest; } /** * returns all matching target connection * @param asyncFindFuncArg * @returns */ public async findAllTargetConnections( asyncFindFuncArg: (connectionArg: plugins.smartsocket.SocketConnection) => Promise ) { if (this.socketServerOrClient instanceof plugins.smartsocket.Smartsocket) { const matchingSockets: plugins.smartsocket.SocketConnection[] = []; for (const socketConnection of this.socketServerOrClient.socketConnections.getArray()) { if (await asyncFindFuncArg(socketConnection)) { matchingSockets.push(socketConnection); } } return matchingSockets; } else { throw new Error('this method >>findTargetConnection<< is only available from the server'); } } /** * returns a single target connection by returning the first one of all matching ones * @param asyncFindFuncArg * @returns */ public async findTargetConnection( asyncFindFuncArg: (connectionArg: plugins.smartsocket.SocketConnection) => Promise ) { const allMatching = await this.findAllTargetConnections(asyncFindFuncArg); return allMatching[0]; } public async findAllTargetConnectionsByTag< TTag extends plugins.typedrequestInterfaces.ITag = any >(keyArg: TTag['name'], payloadArg?: TTag['payload']) { return this.findAllTargetConnections(async (socketConnectionArg) => { let result: boolean; if (!payloadArg) { result = !!(await socketConnectionArg.getTagById(keyArg)); } else { result = !!( plugins.smartjson.stringify((await socketConnectionArg.getTagById(keyArg))?.payload) === plugins.smartjson.stringify(payloadArg) ); } return result; }); } public async findTargetConnectionByTag( keyArg: TTag['name'], payloadArg?: TTag['payload'] ) { const allResults = await this.findAllTargetConnectionsByTag(keyArg, payloadArg); return allResults[0]; } public async stop() { await this.socketServerOrClient.stop(); } }