UNPKG

@oiij/use

Version:

Som Composable Functions for Vue 3

150 lines (148 loc) 4.3 kB
import { onUnmounted, ref, shallowRef, toValue, watchEffect } from "vue"; import { createEventHook } from "@vueuse/core"; //#region src/composables/use-web-socket.ts const ReadyState = { 0: "CONNECTING", 1: "OPEN", 2: "CLOSING", 3: "CLOSED" }; function useWebSocket(url, options) { const { manual = false, autoRetry, parseMessage = false, handlerKey = "type", ..._options } = options ?? {}; const { retries = 3, delay = 1e3, onFailed } = typeof autoRetry === "boolean" ? {} : autoRetry ?? {}; let retryCount = 0; const urlRef = ref(toValue(url)); watchEffect(() => { if (urlRef.value !== toValue(url)) { urlRef.value = toValue(url); if (!manual) connect(); } }); const handlerMap = /* @__PURE__ */ new Map(); const socket = shallowRef(null); const status = ref("PENDING"); const error = ref(null); const data = ref(null); const dataRecord = ref([]); const messageEvent = ref(null); const messageEventRecord = ref([]); const controller = shallowRef(new AbortController()); const onOpenEvent = createEventHook(); const onMessageEvent = createEventHook(); const onCloseEvent = createEventHook(); const onErrorEvent = createEventHook(); function setStatus() { if (socket.value) status.value = ReadyState[socket.value.readyState]; } function connect(url$1, protocols) { if (socket.value) destroy(); if (url$1) urlRef.value = url$1; if (protocols) _options.protocols = protocols; if (!urlRef.value) throw new Error("WebSocket url is not defined"); socket.value = new WebSocket(urlRef.value, _options.protocols); controller.value = new AbortController(); socket.value.addEventListener("open", onOpen, { signal: controller.value.signal }); socket.value.addEventListener("message", onMessage, { signal: controller.value.signal }); socket.value.addEventListener("close", onClose, { signal: controller.value.signal }); socket.value.addEventListener("error", onError, { signal: controller.value.signal }); } function close() { if (socket.value?.readyState === 1) socket.value?.close(); } if (!manual) connect(); function sendRaw(data$1) { socket.value?.send(data$1); } function send(data$1) { sendRaw(JSON.stringify(data$1)); } function onOpen(ev) { setStatus(); error.value = null; data.value = null; messageEvent.value = null; retryCount = 0; onOpenEvent.trigger(ev); } async function onMessage(ev) { setStatus(); data.value = ev.data; dataRecord.value.push(ev.data); messageEvent.value = ev; messageEventRecord.value.push(ev); onMessageEvent.trigger(ev); if (parseMessage && typeof ev.data === "string") try { const dataJson = typeof parseMessage === "function" ? await Promise.try(parseMessage, ev.data) : JSON.parse(ev.data); if (dataJson?.[handlerKey]) handlerMap.get(dataJson[handlerKey])?.forEach((f) => { f(dataJson); }); } catch (err) { console.warn("Failed to parse message:", err); } } function onClose(ev) { setStatus(); error.value = null; data.value = null; messageEvent.value = null; onCloseEvent.trigger(ev); } function onError(ev) { setStatus(); error.value = ev; onErrorEvent.trigger(ev); if (autoRetry) if (retryCount < retries) { retryCount++; setTimeout(() => { connect(); }, delay); } else { onFailed?.(); retryCount = 0; } } function registerHandler(type, handler) { if (!handlerMap.has(type)) handlerMap.set(type, []); handlerMap.get(type)?.push(handler); return () => cancelHandler(type, handler); } function cancelHandler(type, handler) { if (handlerMap.has(type)) handlerMap.set(type, handlerMap.get(type)?.filter((f) => f !== handler) || []); } function registerEvent(type, handler) { if (socket.value) socket.value.addEventListener(type, handler, { signal: controller.value.signal }); } function destroy() { close(); controller.value.abort(); socket.value = null; } onUnmounted(() => { destroy(); }); return { socket, url: urlRef, status, data, dataRecord, messageEvent, messageEventRecord, error, controller, connect, close, sendRaw, send, destroy, registerHandler, cancelHandler, registerEvent, onOpen: onOpenEvent.on, onMessage: onMessageEvent.on, onClose: onCloseEvent.on, onError: onErrorEvent.on }; } //#endregion export { useWebSocket };