diff --git a/packages/core-realtime/src/index.ts b/packages/core-realtime/src/index.ts index 942e9a2..86f0f8b 100644 --- a/packages/core-realtime/src/index.ts +++ b/packages/core-realtime/src/index.ts @@ -13,3 +13,10 @@ export { SocketIORealtimeBroadcaster } from "./socket-io-realtime-broadcaster"; export { SocketIORealtimeServer } from "./socket-io-realtime-server"; export { authorize } from "./authorize"; export { matchChannelTemplate } from "./channel-template"; +export { + realtimePingChannel, + realtimePongChannel, + realtimePingInboundDescriptor, + type PingPayload, + type PongPayload, +} from "./realtime-ping"; diff --git a/packages/core-realtime/src/realtime-ping.ts b/packages/core-realtime/src/realtime-ping.ts new file mode 100644 index 0000000..a6a4378 --- /dev/null +++ b/packages/core-realtime/src/realtime-ping.ts @@ -0,0 +1,36 @@ +import { z } from "zod"; +import { defineRealtimeChannel } from "./realtime-channel"; +import type { IRealtimeBroadcaster } from "./realtime-broadcaster.interface"; +import type { IInboundDescriptor, RealtimeContext } from "./realtime-handler.interface"; + +const pingSchema = z.object({ at: z.string().datetime() }).strict(); +const pongSchema = z.object({ at: z.string().datetime(), echo: z.string() }).strict(); + +export type PingPayload = z.infer; +export type PongPayload = z.infer; + +export const realtimePingChannel = defineRealtimeChannel( + "realtime.ping", + pingSchema, + { scope: "authenticated" }, +); + +export const realtimePongChannel = defineRealtimeChannel( + "realtime.pong", + pongSchema, + { scope: "authenticated" }, +); + +export function realtimePingInboundDescriptor( + broadcaster: IRealtimeBroadcaster, +): IInboundDescriptor { + return { + descriptor: realtimePingChannel, + handler: async (input: PingPayload, ctx: RealtimeContext): Promise => { + await broadcaster.broadcast(realtimePongChannel, { + at: input.at, + echo: ctx.userId ?? "anonymous", + }); + }, + }; +}