{"version":3,"sources":["../../../src/server/sockets.ts"],"sourcesContent":["import { match as Match } from 'path-to-regexp'\nimport type { Server } from 'socket.io'\n\nimport type { Entity } from '../dbs/base/core'\nimport { Instance } from '../instance'\nimport type { AuthUser } from '../types'\nimport type { ServerConfig } from './pipes'\nimport { BaseRequestAuthMethod } from './requests-auth-methods'\nimport { StatusCodes, type StatusCodesEnum } from './types'\n\nenum EmitTypes {\n\tcreated = 'created',\n\tupdated = 'updated',\n\tdeleted = 'deleted',\n}\n\nconst EmitterEvent = '__listener_emitter'\ntype EmitData = { channel: string; type: EmitTypes; after: any; before: any }\ntype LeaveRoomParams = { channel: string }\ntype JoinRoomParams = { channel: string; token?: string; query: Record<string, any> }\ntype Callback = (params: { code: StatusCodesEnum; message: string; channel: string }) => void\nexport type OnJoinFn = (\n\tdata: { channel: string; user: AuthUser | null },\n\tparams: Record<string, any>,\n\tquery: Record<string, any>,\n) => Promise<string | null>\nexport type SocketCallbacks = {\n\tonConnect: (userId: string, socketId: string) => Promise<void>\n\tonDisconnect: (userId: string, socketId: string) => Promise<void>\n}\n\nconst defaultTo = '*'\n\nexport class SocketEmitter {\n\treadonly socketInstance: Server\n\t#connectionCallbacks: SocketCallbacks = { onConnect: async () => {}, onDisconnect: async () => {} }\n\t#routes = {} as Record<string, OnJoinFn>\n\t#publish: (data: EmitData) => Promise<void> = async () => {}\n\n\tconstructor(\n\t\tsocket: Server,\n\t\tprivate config: ServerConfig,\n\t) {\n\t\tthis.socketInstance = socket\n\t\tthis.#setupSocketConnection()\n\t\tInstance.on(\n\t\t\t'setup',\n\t\t\t() => {\n\t\t\t\tconst stream = config.eventBus?.createStream(EmitterEvent as never, { fanout: true })\n\t\t\t\tthis.#publish = stream\n\t\t\t\t\t? (stream.publish as unknown as (data: EmitData) => Promise<void>)\n\t\t\t\t\t: async (data: EmitData) => {\n\t\t\t\t\t\t\tsocket.to(data.channel).emit(data.channel, data)\n\t\t\t\t\t\t}\n\t\t\t\tstream?.subscribe(async (data: EmitData) => {\n\t\t\t\t\tsocket.to(data.channel).emit(data.channel, data)\n\t\t\t\t})\n\t\t\t},\n\t\t\t1,\n\t\t)\n\t}\n\n\tasync created<T extends Entity>(channels: string[], data: T, to: string | string[] | null) {\n\t\tawait this.#emit(channels, EmitTypes.created, { after: data.toJSON(), before: null }, to)\n\t}\n\n\tasync updated<T extends Entity>(channels: string[], { after, before }: { after: T; before: T }, to: string | string[] | null) {\n\t\tawait this.#emit(channels, EmitTypes.updated, { after: after.toJSON(), before: before.toJSON() }, to)\n\t}\n\n\tasync deleted<T extends Entity>(channels: string[], data: T, to: string | string[] | null) {\n\t\tawait this.#emit(channels, EmitTypes.deleted, { before: data.toJSON(), after: null }, to)\n\t}\n\n\tasync #emit(channels: string[], type: EmitTypes, { before, after }: { after: any; before: any }, to: string | string[] | null) {\n\t\tconst toArray = Array.isArray(to) ? to : [to ?? defaultTo]\n\t\tconst channelMap = channels.flatMap((c) => toArray.map((to) => `${to}:${c}`))\n\t\tawait Promise.all(channelMap.map(async (channel) => this.#publish({ channel, type, before, after })))\n\t}\n\n\tset connectionCallbacks(callbacks: SocketCallbacks) {\n\t\tthis.#connectionCallbacks = callbacks\n\t\tthis.#setupSocketConnection()\n\t}\n\n\tregister(channel: string, onJoin: OnJoinFn) {\n\t\tthis.#routes[channel] = onJoin\n\t\tthis.#routes[channel + '/:id'] = onJoin\n\t\treturn this\n\t}\n\n\t#getConfig(channel: string) {\n\t\tconst matcher = (key: string) => Match(key)(channel)\n\t\tconst matchedChannel = Object.keys(this.#routes).find(matcher) ?? null\n\t\tif (!matchedChannel) return null\n\t\tconst match = matcher(matchedChannel)\n\t\tif (!match) return null\n\t\treturn {\n\t\t\tconfig: this.#routes[matchedChannel],\n\t\t\tparams: match.params,\n\t\t}\n\t}\n\n\t#setupSocketConnection = () => {\n\t\tthis.socketInstance.removeAllListeners('connection')\n\t\tthis.socketInstance.on('connection', async (socket) => {\n\t\t\tconst socketId = socket.id\n\t\t\tconst user = await BaseRequestAuthMethod.process(this.config.socketsAuthMethods, socket.handshake.headers).catch(() => null)\n\t\t\tsocket.on('leave', async (data: LeaveRoomParams, callback: Callback) => {\n\t\t\t\tif (!data.channel)\n\t\t\t\t\treturn (\n\t\t\t\t\t\ttypeof callback === 'function' &&\n\t\t\t\t\t\tcallback({\n\t\t\t\t\t\t\tcode: StatusCodes.ValidationError,\n\t\t\t\t\t\t\tmessage: 'channel is required',\n\t\t\t\t\t\t\tchannel: '',\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\tsocket.leave(data.channel)\n\t\t\t\treturn (\n\t\t\t\t\ttypeof callback === 'function' &&\n\t\t\t\t\tcallback({\n\t\t\t\t\t\tcode: StatusCodes.Ok,\n\t\t\t\t\t\tmessage: '',\n\t\t\t\t\t\tchannel: data.channel,\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t})\n\t\t\tsocket.on('join', async (data: JoinRoomParams, callback: Callback) => {\n\t\t\t\tif (!data.channel)\n\t\t\t\t\treturn (\n\t\t\t\t\t\ttypeof callback === 'function' &&\n\t\t\t\t\t\tcallback({\n\t\t\t\t\t\t\tcode: StatusCodes.ValidationError,\n\t\t\t\t\t\t\tmessage: 'channel is required',\n\t\t\t\t\t\t\tchannel: '',\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\tconst channel = data.channel\n\t\t\t\tconst route = this.#getConfig(channel) ?? null\n\t\t\t\tif (!route)\n\t\t\t\t\treturn (\n\t\t\t\t\t\ttypeof callback === 'function' &&\n\t\t\t\t\t\tcallback({\n\t\t\t\t\t\t\tcode: StatusCodes.BadRequest,\n\t\t\t\t\t\t\tmessage: 'unknown channel',\n\t\t\t\t\t\t\tchannel,\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\tconst to = await route.config({ channel, user }, route.params, data.query ?? {})\n\t\t\t\tconst newChannel = `${to ?? defaultTo}:${channel}`\n\t\t\t\tsocket.join(newChannel)\n\t\t\t\treturn (\n\t\t\t\t\ttypeof callback === 'function' &&\n\t\t\t\t\tcallback({\n\t\t\t\t\t\tcode: StatusCodes.Ok,\n\t\t\t\t\t\tmessage: '',\n\t\t\t\t\t\tchannel: newChannel,\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t})\n\t\t\tif (user) await this.#connectionCallbacks.onConnect(user.id, socketId)\n\t\t\tsocket.on('disconnect', async () => {\n\t\t\t\tif (user) await this.#connectionCallbacks.onDisconnect(user.id, socketId)\n\t\t\t})\n\t\t})\n\t}\n}\n"],"mappings":"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","names":["Match","Instance","BaseRequestAuthMethod","StatusCodes","EmitTypes","EmitterEvent","defaultTo","SocketEmitter","socket","config","#setupSocketConnection","stream","#publish","data","#connectionCallbacks","#routes","channels","to","#emit","after","before","type","toArray","channelMap","channel","callbacks","onJoin","#getConfig","matcher","key","matchedChannel","match","socketId","user","callback","route","newChannel"]}