pusher-zod
Version:
Pusher with type safety using Zod to parse the data
1 lines • 6.28 kB
Source Map (JSON)
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\r\nimport PusherClient, { type Channel as PusherChannel } from \"pusher-js\";\r\nimport type { z, ZodType } from \"zod\";\r\n\r\nexport type TypeSafePusherClientType<\r\n S extends Record<string, Record<string, z.ZodType<any, any>>>,\r\n P extends Partial<Record<string, z.ZodType<any, any>>> = Record<string, never>\r\n> = {\r\n joinChannel<K extends keyof S>(\r\n channelName: K,\r\n id: string | number\r\n ): PusherChannel & {\r\n listen<E extends keyof S[K]>(\r\n eventName: E,\r\n callback: (data: z.infer<S[K][E]>) => void,\r\n context?: any\r\n ): void;\r\n };\r\n leaveChannel<K extends keyof S>(channelName: K, id: string | number): void;\r\n member: {\r\n bind: <E extends Extract<keyof P, string>>(\r\n eventName: E,\r\n callback: (data: z.infer<NonNullable<P[E]>>) => void,\r\n context?: any\r\n ) => any;\r\n };\r\n subscribe(channelName: string): PusherChannel;\r\n unsubscribe(channelName: string): void;\r\n user: PusherClient[\"user\"];\r\n on(eventName: string, callback: Function): void;\r\n};\r\n\r\nexport function createPusherClient<\r\n S extends Record<string, Record<string, z.ZodType<any, any>>>,\r\n P extends Partial<Record<string, z.ZodType<any, any>>> = Record<string, never>\r\n>({\r\n setup,\r\n channel_id_separator = \".\",\r\n userEvents = {} as P,\r\n}: {\r\n setup: S;\r\n channel_id_separator?: \"_\" | \"-\" | \"=\" | \"@\" | \",\" | \".\" | \";\";\r\n userEvents?: P;\r\n}) {\r\n type ChannelsNames = keyof S;\r\n type EventKeys<K extends ChannelsNames> = {\r\n [P in keyof (typeof setup)[K]]: (typeof setup)[K][P] extends ZodType<\r\n any,\r\n any\r\n >\r\n ? P\r\n : never;\r\n }[keyof (typeof setup)[K]];\r\n\r\n class TypeSafePusherClient implements TypeSafePusherClientType<S, P> {\r\n public pusher: PusherClient;\r\n public member;\r\n\r\n constructor(...args: ConstructorParameters<typeof PusherClient>) {\r\n this.pusher = new PusherClient(...args);\r\n this.member = {\r\n ...this.pusher.user,\r\n bind: <E extends keyof P>(\r\n eventName: E,\r\n callback: (data: z.infer<NonNullable<P[E]>>) => void,\r\n context?: any\r\n ) => {\r\n const parsedCallback = (data: P[typeof eventName]) => {\r\n const parsedData = parseData(userEvents[eventName]!, data);\r\n if (parsedData !== undefined) callback(parsedData);\r\n };\r\n return this.pusher.user.bind(\r\n String(eventName),\r\n parsedCallback,\r\n context\r\n );\r\n },\r\n };\r\n }\r\n\r\n joinChannel<K extends ChannelsNames>(\r\n channelName: K,\r\n id: string | number\r\n ): PusherChannel & {\r\n listen<E extends keyof S[K]>(\r\n eventName: E,\r\n callback: (data: z.infer<S[K][E]>) => void,\r\n context?: any\r\n ): void;\r\n } {\r\n const channel = this.pusher.subscribe(\r\n `${String(channelName)}${channel_id_separator}${id}`\r\n ) as PusherChannel & {\r\n listen<E extends keyof S[K]>(\r\n eventName: E,\r\n callback: (data: z.infer<S[K][E]>) => void,\r\n context?: any\r\n ): void;\r\n };\r\n channel.listen = (eventName, callback, context) => {\r\n const parsedCallback = (data: z.infer<S[K][typeof eventName]>) => {\r\n const parsedData = parseData(setup[channelName][eventName], data);\r\n if (parsedData !== undefined) callback(parsedData);\r\n };\r\n channel.bind(String(eventName), parsedCallback, context);\r\n };\r\n return channel;\r\n }\r\n\r\n leaveChannel<K extends ChannelsNames>(\r\n channelName: K,\r\n id: string | number\r\n ): void {\r\n this.pusher.unsubscribe(\r\n `${String(channelName)}${channel_id_separator}${id}`\r\n );\r\n }\r\n\r\n subscribe(channelName: string): PusherChannel {\r\n return this.pusher.subscribe(channelName);\r\n }\r\n\r\n unsubscribe(channelName: string): void {\r\n this.pusher.unsubscribe(channelName);\r\n }\r\n\r\n get user(): PusherClient[\"user\"] {\r\n return this.pusher.user;\r\n }\r\n\r\n on(eventName: string, callback: Function): void {\r\n this.pusher.connection.bind(eventName, callback);\r\n }\r\n }\r\n\r\n function parseData<K>(\r\n zodSchema: z.ZodType<any, any>,\r\n data: K\r\n ): K | undefined {\r\n try {\r\n return zodSchema.parse(data);\r\n } catch (error) {\r\n console.error(error);\r\n return undefined;\r\n }\r\n }\r\n\r\n return {\r\n TypeSafePusherClient,\r\n };\r\n}\r\n\r\nexport default createPusherClient;\r\n"],"mappings":";;;;;;AACA,OAAO,kBAAqD;AA+BrD,SAAS,mBAGd;AAAA,EACA;AAAA,EACA,uBAAuB;AAAA,EACvB,aAAa,CAAC;AAChB,GAIG;AAAA,EAWD,MAAM,qBAA+D;AAAA,IAInE,eAAe,MAAkD;AAC/D,WAAK,SAAS,IAAI,aAAa,GAAG,IAAI;AACtC,WAAK,SAAS,iCACT,KAAK,OAAO,OADH;AAAA,QAEZ,MAAM,CACJ,WACA,UACA,YACG;AACH,gBAAM,iBAAiB,CAAC,SAA8B;AACpD,kBAAM,aAAa,UAAU,WAAW,SAAS,GAAI,IAAI;AACzD,gBAAI,eAAe,OAAW,UAAS,UAAU;AAAA,UACnD;AACA,iBAAO,KAAK,OAAO,KAAK;AAAA,YACtB,OAAO,SAAS;AAAA,YAChB;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IAEA,YACE,aACA,IAOA;AACA,YAAM,UAAU,KAAK,OAAO;AAAA,QAC1B,GAAG,OAAO,WAAW,CAAC,GAAG,oBAAoB,GAAG,EAAE;AAAA,MACpD;AAOA,cAAQ,SAAS,CAAC,WAAW,UAAU,YAAY;AACjD,cAAM,iBAAiB,CAAC,SAA0C;AAChE,gBAAM,aAAa,UAAU,MAAM,WAAW,EAAE,SAAS,GAAG,IAAI;AAChE,cAAI,eAAe,OAAW,UAAS,UAAU;AAAA,QACnD;AACA,gBAAQ,KAAK,OAAO,SAAS,GAAG,gBAAgB,OAAO;AAAA,MACzD;AACA,aAAO;AAAA,IACT;AAAA,IAEA,aACE,aACA,IACM;AACN,WAAK,OAAO;AAAA,QACV,GAAG,OAAO,WAAW,CAAC,GAAG,oBAAoB,GAAG,EAAE;AAAA,MACpD;AAAA,IACF;AAAA,IAEA,UAAU,aAAoC;AAC5C,aAAO,KAAK,OAAO,UAAU,WAAW;AAAA,IAC1C;AAAA,IAEA,YAAY,aAA2B;AACrC,WAAK,OAAO,YAAY,WAAW;AAAA,IACrC;AAAA,IAEA,IAAI,OAA6B;AAC/B,aAAO,KAAK,OAAO;AAAA,IACrB;AAAA,IAEA,GAAG,WAAmB,UAA0B;AAC9C,WAAK,OAAO,WAAW,KAAK,WAAW,QAAQ;AAAA,IACjD;AAAA,EACF;AAEA,WAAS,UACP,WACA,MACe;AACf,QAAI;AACF,aAAO,UAAU,MAAM,IAAI;AAAA,IAC7B,SAAS,OAAO;AACd,cAAQ,MAAM,KAAK;AACnB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,EACF;AACF;AAEA,IAAO,iBAAQ;","names":[]}