diff --git a/lib/realtime-events.ts b/lib/realtime-events.ts index fa820e6..d7e88cf 100644 --- a/lib/realtime-events.ts +++ b/lib/realtime-events.ts @@ -2,9 +2,19 @@ import type { PrismaClient } from "@prisma/client" import { enqueueRealtimeEvent } from "@/lib/realtime-queue" -type PrismaMiddleware = Parameters[0] -type PrismaMiddlewareParams = Parameters[0] -type PrismaMiddlewareNext = Parameters[1] +type PrismaMiddlewareParams = { + action: string + model?: string | null + args?: unknown + [key: string]: unknown +} + +type PrismaMiddlewareNext = (params: PrismaMiddlewareParams) => Promise + +type PrismaMiddleware = ( + params: PrismaMiddlewareParams, + next: PrismaMiddlewareNext, +) => Promise const MUTATION_ACTIONS = new Set([ "create", diff --git a/lib/realtime-queue.ts b/lib/realtime-queue.ts index c925308..bc7709a 100644 --- a/lib/realtime-queue.ts +++ b/lib/realtime-queue.ts @@ -1,5 +1,5 @@ import { Queue, Worker } from "bullmq" -import type { JobsOptions } from "bullmq" +import type { Job, JobsOptions } from "bullmq" import { createRedisConnection, getRedisUrl } from "@/lib/redis" import { getRealtimeIO } from "@/lib/realtime" @@ -47,7 +47,7 @@ function ensureWorker() { if (!globalForQueue.realtimeWorker) { globalForQueue.realtimeWorker = new Worker( queueName, - async job => { + async (job: Job) => { const io = getRealtimeIO() if (io) { @@ -64,7 +64,7 @@ function ensureWorker() { { connection }, ) - globalForQueue.realtimeWorker.on("error", error => { + globalForQueue.realtimeWorker.on("error", (error: Error) => { console.error("Realtime worker error", error) }) } diff --git a/pages/api/socket/io.ts b/pages/api/socket/io.ts index a9c4ebb..052c849 100644 --- a/pages/api/socket/io.ts +++ b/pages/api/socket/io.ts @@ -1,5 +1,6 @@ import type { NextApiRequest } from "next" import { Server as IOServer } from "socket.io" +import type { DisconnectReason, Socket } from "socket.io" import type { NextApiResponseServerIO } from "@/types/next" import { getRealtimeIO, setRealtimeIO } from "@/lib/realtime" @@ -24,14 +25,14 @@ export default function handler(req: NextApiRequest, res: NextApiResponseServerI setRealtimeIO(io) existingServer.io = io - io.on("connection", socket => { + io.on("connection", (socket: Socket) => { socket.emit("realtime:event", { event: "socket:connected", socketId: socket.id, timestamp: Date.now(), }) - socket.on("disconnect", reason => { + socket.on("disconnect", (reason: DisconnectReason) => { socket.broadcast.emit("realtime:event", { event: "socket:disconnected", socketId: socket.id, diff --git a/types/external.d.ts b/types/external.d.ts new file mode 100644 index 0000000..13d6813 --- /dev/null +++ b/types/external.d.ts @@ -0,0 +1,103 @@ +declare module "socket.io-client" { + export interface Socket { + id: string + emit(event: string, ...args: unknown[]): void + on(event: string, listener: (...args: unknown[]) => void): this + off(event: string, listener: (...args: unknown[]) => void): this + connect(): void + disconnect(): void + broadcast?: { + emit(event: string, ...args: unknown[]): void + } + } + + export interface SocketOptions { + path?: string + transports?: string[] + autoConnect?: boolean + reconnection?: boolean + reconnectionDelay?: number + reconnectionDelayMax?: number + } + + export function io(opts?: SocketOptions): Socket + export function io(uri: string, opts?: SocketOptions): Socket + export { io } + export default io +} + +declare module "socket.io" { + import type { Server as HTTPServer } from "http" + + export interface ServerOptions { + path?: string + cors?: { + origin?: string | string[] + } + } + + export interface BroadcastOperator { + emit(event: string, ...args: unknown[]): void + } + + export type DisconnectReason = string + + export interface Socket { + id: string + emit(event: string, ...args: unknown[]): void + on(event: "disconnect", listener: (reason: DisconnectReason) => void): this + on(event: string, listener: (...args: unknown[]) => void): this + broadcast: BroadcastOperator + } + + export class Server { + constructor(httpServer?: HTTPServer, opts?: ServerOptions) + emit(event: string, ...args: unknown[]): boolean + on(event: "connection", listener: (socket: Socket) => void): this + on(event: string, listener: (...args: unknown[]) => void): this + } + + export { Server as IOServer } +} + +declare module "bullmq" { + export interface JobsOptions { + removeOnComplete?: boolean | { age?: number; count?: number } + removeOnFail?: boolean | { age?: number; count?: number } + [key: string]: unknown + } + + export interface QueueOptions { + connection?: unknown + } + + export interface WorkerOptions { + connection?: unknown + } + + export interface Job { + id?: string | number + name: string + data: Data + } + + export class Queue { + constructor(name: Name, opts?: QueueOptions) + add(name: Name, data: Data, opts?: JobsOptions): Promise> + } + + export class Worker { + constructor(name: Name, processor: (job: Job) => Promise, opts?: WorkerOptions) + on(event: "error", handler: (error: Error) => void): this + } +} + +declare module "ioredis" { + export interface RedisOptions { + [key: string]: unknown + } + + export default class IORedis { + constructor(url?: string, options?: RedisOptions) + } +}