{"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":["EmitTypes","to"]}