{ "version": 3, "sources": ["../../../../../../../../source/febe/ui/src/scripts/classes/WrappedEventSource.ts", "../../../../../../../../source/febe/ui/src/scripts/components/SampleStatus.ts"], "sourcesContent": ["import Utils from \"./Utils\";\n\nexport default class WrappedEventSource extends EventSource {\n constructor(url: string, options: EventSourceInit) {\n super(url, options);\n window.addEventListener(\"beforeunload\", () => {\n this.close();\n });\n const timeout = setTimeout(() => {\n let event = new CustomEvent(\"error\", null);\n this.showSSEError();\n this.close();\n this.dispatchEvent(event);\n }, 10000);\n this.onopen = () => {\n clearTimeout(timeout);\n };\n return this;\n }\n\n showSSEError = () => {\n if (!document.querySelector(\"#toasts #sse-error\")) {\n Utils.spawnToast({\n title: \"Failed to open connection to server-sent events source.\",\n message:\n \"Are server-sent events allowed by your proxy server/firewall? Not allowing or buffering server-sent events or websockets will result in certain parts of the product becoming non-functional.\",\n type: \"warning\",\n id: \"sse-error\",\n action: {\n label: \"Troubleshoot\",\n type: \"primary\",\n href: \"/troubleshooting\",\n },\n });\n }\n };\n}\n", "import WrappedEventSource from \"../classes/WrappedEventSource\";\nimport { Sample } from \"../global/interfaces\";\nimport { debounce, wait } from \"../classes/Utils\";\n\nconst connectWS = async (sampleId: string, onMessage: (data: any) => void, onDirtyClose: () => void) => {\n const wsURL = `${globalThis.location.origin\n .replace(\"https\", \"wss\")\n .replace(\"http\", \"ws\")}/api/samples/${sampleId}/events`;\n let ws = new WebSocket(wsURL);\n ws.addEventListener(\"message\", (ev) => {\n onMessage(ev.data);\n });\n // TODO(gt) handle WS 401 a bit neater\n try {\n await new Promise((resolve, reject) => {\n ws.addEventListener(\"message\", () => resolve(null), { once: true });\n ws.addEventListener(\"error\", (ev) => reject(ev), { once: true });\n ws.addEventListener(\"close\", (ev) => reject(ev), { once: true });\n });\n } catch (e) {\n ws.close();\n throw e;\n }\n const onClose = (ev: CloseEvent) => {\n if (!ev.wasClean) {\n onDirtyClose();\n }\n ws.removeEventListener(\"close\", onClose);\n };\n ws.addEventListener(\"close\", onClose);\n return {\n close: () => ws.close(),\n isClosed: () => ws.readyState === ws.CLOSED,\n };\n};\n\nconst connectSSE = async (sampleId: string, onMessage: (data: string) => void, onDirtyClose: () => void) => {\n let finished = false;\n const evtSource = new WrappedEventSource(`${globalThis.location.origin}/api/samples/${sampleId}/events`, {\n withCredentials: true,\n });\n\n const initialMsg = await new Promise((res, rej) => {\n evtSource.addEventListener(\"message\", (ev) => res(ev), { once: true });\n evtSource.addEventListener(\n \"error\",\n () => {\n evtSource.close();\n rej();\n },\n { once: true }\n );\n });\n onMessage(initialMsg.data);\n evtSource.addEventListener(\"message\", (ev) => {\n if (ev.data === \"end\") {\n finished = true;\n evtSource.close();\n return;\n }\n onMessage(ev.data as string);\n });\n evtSource.addEventListener(\"error\", (err) => {\n if (!finished) {\n onDirtyClose();\n console.warn(err);\n evtSource.showSSEError();\n evtSource.close();\n }\n });\n return {\n close: () => evtSource.close(),\n isClosed: () => evtSource.readyState === evtSource.CLOSED,\n };\n};\n\ntype SSStream = { close(): any; isClosed(): boolean };\nconst retry = async (attempt: () => Promise) => {\n try {\n return await attempt();\n } catch (e) {}\n const retries = 3;\n for (let i = 0; i < retries; i++) {\n await wait(1000 * 3);\n console.info(`retrying... ${i + 2} times`);\n try {\n return await attempt();\n } catch (e) {}\n }\n return await attempt();\n};\n\nexport const connectToSampleStatus = async (\n sampleId: string,\n onMessage: (data: string) => void,\n onDirtyClose: () => void\n) => {\n try {\n return await retry(() => connectWS(sampleId, onMessage, onDirtyClose));\n } catch (e) {\n console.error(\"unable to connect to Sample Status Websocket. Trying SSE\", e);\n }\n try {\n return await retry(() => connectSSE(sampleId, onMessage, onDirtyClose));\n } catch (e) {\n console.error(\"unable to connect to Sample Status SSE.\", e);\n throw e;\n }\n};\n\ntype SSCallback = (ss: Sample) => any;\n\nasync function connect(sampleId: string, onMessage: (data: string) => void, triesAmount: number) {\n const onDirtyClose = async () => {\n console.warn(\"connection closed unexpectedly\");\n if (triesAmount > 5) {\n console.warn(\"will not try connecting again\");\n return;\n }\n await wait(5000);\n try {\n console.warn(\"reconnecting\");\n\n return await connect(sampleId, onMessage, triesAmount + 1);\n } catch (e) {\n console.error(\"unable to connect\", e);\n }\n };\n return await connectToSampleStatus(sampleId, onMessage, onDirtyClose);\n}\n\nconst getSampleStatus = async (sampleId: string) => {\n let sampleStatus: Sample;\n let cbs: SSCallback[] = [];\n\n const deferReconnect = debounce(35 * 1000, async () => {\n if (conn.isClosed()) {\n return;\n }\n conn.close();\n console.warn(\"reconnecting after timeout exceeded...\");\n conn = await connect(sampleId, onMessage, 0);\n });\n const onMessage = (data: string) => {\n deferReconnect();\n const dat = JSON.parse(data) as Sample | { ping: string };\n if (\"ping\" in dat) {\n return;\n }\n sampleStatus = dat;\n for (let cb of cbs) {\n cb(sampleStatus);\n }\n };\n let conn = await connect(sampleId, onMessage, 0);\n\n return {\n get() {\n return sampleStatus;\n },\n watch(cb: SSCallback) {\n cbs.push(cb);\n if (!sampleStatus) {\n return;\n }\n cb(sampleStatus);\n },\n };\n};\n\nconst LUT = new Map>();\n/** getSampleStatusConnection returns existing connection if it exists. creates one if it doesn't*/\nexport const getSampleStatusConnection = async (sampleId: string) => {\n const conn = LUT.get(sampleId);\n if (conn) {\n return await conn;\n }\n const prom = getSampleStatus(sampleId);\n LUT.set(sampleId, prom);\n return await prom;\n};\n"], "mappings": "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", "names": ["WrappedEventSource", "url", "options", "Utils", "timeout", "event", "connectWS", "sampleId", "onMessage", "onDirtyClose", "wsURL", "ws", "ev", "resolve", "reject", "e", "onClose", "connectSSE", "finished", "evtSource", "WrappedEventSource", "initialMsg", "res", "rej", "err", "retry", "attempt", "retries", "i", "wait", "connectToSampleStatus", "connect", "triesAmount", "getSampleStatus", "sampleStatus", "cbs", "deferReconnect", "debounce", "conn", "data", "dat", "cb", "LUT", "getSampleStatusConnection", "prom"] }