Created
July 14, 2022 15:53
-
-
Save ecwyne/4cd30136a837dab936c35e55f963bc21 to your computer and use it in GitHub Desktop.
Typesafe Pusher (validated with Zod)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import PusherServer from 'pusher'; | |
import { z, ZodSchema } from 'zod'; | |
import PusherClient from 'pusher-js'; | |
const client = new PusherClient('...', { cluster: '...' }); | |
const server = new PusherServer({ | |
appId: '...', | |
key: '...', | |
secret: '...', | |
cluster: '...', | |
}); | |
type ObjKeyof<T> = T extends object ? keyof T : never; | |
type TypesafePusherSchema = Record<string, Record<string, ZodSchema>>; | |
interface TypesafePusherOptions<TSchema extends TypesafePusherSchema> { | |
schema: TSchema; | |
} | |
export class TypesafePusher<TSchema extends TypesafePusherSchema> { | |
constructor(private options: TypesafePusherOptions<TSchema>) {} | |
server = (pusher: PusherServer) => ({ | |
trigger: < | |
TChannel extends ObjKeyof<TSchema>, | |
TEvent extends ObjKeyof<TSchema[TChannel]>, | |
>( | |
channel: TChannel, | |
event: TEvent, | |
data: z.infer<TSchema[TChannel][TEvent]>, | |
) => { | |
pusher.trigger( | |
channel, | |
event, | |
this.options.schema[channel]![event]!.parse(data), | |
); | |
}, | |
}); | |
client = (pusher: PusherClient) => ({ | |
subscribe: <TChannel extends ObjKeyof<TSchema>>( | |
channelName: TChannel, | |
) => { | |
const pusherChannel = pusher.subscribe(channelName); | |
return { | |
bind: <TEvent extends ObjKeyof<TSchema[TChannel]>>( | |
eventName: TEvent, | |
callback: ( | |
data: z.infer<TSchema[TChannel][TEvent]>, | |
) => void, | |
) => { | |
pusherChannel.bind(eventName, (data: any) => | |
callback( | |
this.options.schema[channelName]![eventName]!.parse( | |
data, | |
), | |
), | |
); | |
}, | |
}; | |
}, | |
}); | |
} | |
const api = new TypesafePusher({ | |
schema: { | |
myChannel: { | |
myEvent: z.object({ id: z.string() }), | |
myOtherEvent: z.object({ id: z.number() }), | |
}, | |
}, | |
}); | |
api.server(server).trigger('myChannel', 'myEvent', { id: '2' }); | |
api.client(client) | |
.subscribe('myChannel') | |
.bind('myEvent', data => console.log(data.id)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment