import {useCallback, useEffect, useMemo, useState} from "react"; import {getHost} from "../utils"; export type Headers = [string, string][] export interface RequestPayload { id: string body: string headers: Headers method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' path: string } interface Request { type: 'request', payload: RequestPayload, } export interface ResponsePayload { id: string timing: number body: string headers: Headers status: number } interface Response { type: 'response', payload: ResponsePayload, } interface Historic { type: 'historic' payload: (Request | Response)[] } export interface RequestResponse { request: RequestPayload response?: ResponsePayload } export enum ReadyState { CONNECTING = 0, OPEN = 1, CLOSING = 2, CLOSED = 3, } export interface useRequestsProps { onConnect: () => Promise } export interface UseRequests { calls: RequestResponse[] readyState: ReadyState clear: () => void } export default function useRequests({ onConnect }: useRequestsProps): UseRequests { const wsHost = useMemo(getHost, []); const [initialConnection, setInitialConnection] = useState(true); const [requests, setRequests] = useState([]); const [responses, setResponses] = useState([]); const connect = useCallback(() => ( new WebSocket(`ws://${wsHost}/inspect/`) ), [wsHost]); const [ws, setWs] = useState(() => connect()); const [readyState, setReadyState] = useState(ws.readyState); useEffect(() => { setReadyState(ws.readyState); const onClose = () => { setReadyState(ws.readyState); setWs(connect()); } const onOpen = () => { onConnect(); setInitialConnection(false); setReadyState(ws.readyState); } const onMessage = ({ data }) => { const { type, payload } = JSON.parse(data) as Historic | Request | Response switch (type) { case 'historic': if (initialConnection) { const requests = (payload as (Request | Response)[]).filter(({ type }) => type === 'request'); const responses = (payload as (Request | Response)[]).filter(({ type }) => type === 'response'); setRequests((rqs) => [...rqs, ...requests.map(({ payload }) => payload as RequestPayload)]); setResponses((rps) => [...rps, ...responses.map(({ payload }) => payload as ResponsePayload)]); } break case 'request': setRequests((rqs) => [...rqs, payload as RequestPayload]) break case 'response': setResponses((rps) => [...rps, payload as ResponsePayload]) break } } ws.addEventListener('message', onMessage) ws.addEventListener('close', onClose) ws.addEventListener('open', onOpen) return () => { ws.removeEventListener('message', onMessage); ws.removeEventListener('close', onClose); ws.removeEventListener('open', onOpen) } }, [ws]) return { calls: useMemo(() => requests.map((request) => ({ request: request, response: responses.find(({id}) => id === request.id) })), [requests, responses]), readyState, clear: () => { setRequests([]) setResponses([]) } } }