@laravel/stream-vue
Version:
Laravel streaming hooks for Vue
235 lines (234 loc) • 6.3 kB
JavaScript
import { ref as m, onMounted as q, onUnmounted as x, watch as J, readonly as S } from "vue";
const M = "data: ", U = (t, {
eventName: e = "update",
endSignal: n = "</stream>",
glue: a = " ",
replace: i = !1,
onMessage: F = () => null,
onComplete: R = () => null,
onError: b = () => null
} = {}) => {
const d = m(""), h = m([]), v = Array.isArray(e) ? e : [e];
let s = null;
const l = () => {
d.value = "", h.value = [];
}, u = (r = !1) => {
v.forEach((o) => {
s == null || s.removeEventListener(o, y);
}), s == null || s.close(), s = null, r && l();
}, y = (r) => {
if ([n, `${M}${n}`].includes(r.data)) {
u(), R();
return;
}
i && l(), h.value.push(
r.data.startsWith(M) ? r.data.substring(M.length) : r.data
), d.value = h.value.join(a), F(r);
}, j = (r) => {
b(r), u();
}, C = () => {
l(), s = new EventSource(t), v.forEach((r) => {
s.addEventListener(r, y);
}), s.addEventListener("error", j);
};
return q(() => {
C();
}), x(() => {
u();
}), J(
() => t,
(r, o) => {
r !== o && (u(), C());
}
), {
message: S(d),
messageParts: S(h),
close: u,
clearMessage: l
};
}, N = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
let X = (t = 21) => {
let e = "", n = crypto.getRandomValues(new Uint8Array(t |= 0));
for (; t--; )
e += N[n[t] & 63];
return e;
};
const w = /* @__PURE__ */ new Map(), K = (t, e) => {
w.has(t) || w.set(t, {
onData: [],
onError: [],
onFinish: [],
onResponse: [],
onCancel: [],
onBeforeSend: []
});
const n = w.get(t);
return e.onData && n.onData.push(e.onData), e.onError && n.onError.push(e.onError), e.onFinish && n.onFinish.push(e.onFinish), e.onResponse && n.onResponse.push(e.onResponse), e.onCancel && n.onCancel.push(e.onCancel), e.onBeforeSend && n.onBeforeSend.push(e.onBeforeSend), () => {
H(t, e);
};
}, E = (t, e, ...n) => {
const a = w.get(t);
return a ? a[e].map((i) => i(...n)) : [];
}, T = (t) => {
E(t, "onFinish");
}, p = (t, e) => {
E(t, "onError", e);
}, V = (t, e) => {
E(t, "onResponse", e);
}, W = (t) => {
E(t, "onCancel");
}, $ = (t, e) => {
E(t, "onData", e);
}, G = (t, e) => {
const n = E(t, "onBeforeSend", e);
for (const a of n) {
if (a === !1)
return !1;
if (a !== null && typeof a == "object")
return a;
}
return null;
}, H = (t, e) => {
const n = w.get(t);
n && (e.onData && (n.onData = n.onData.filter(
(a) => a !== e.onData
)), e.onError && (n.onError = n.onError.filter(
(a) => a !== e.onError
)), e.onFinish && (n.onFinish = n.onFinish.filter(
(a) => a !== e.onFinish
)), e.onResponse && (n.onResponse = n.onResponse.filter(
(a) => a !== e.onResponse
)), e.onCancel && (n.onCancel = n.onCancel.filter(
(a) => a !== e.onCancel
)), e.onBeforeSend && (n.onBeforeSend = n.onBeforeSend.filter(
(a) => a !== e.onBeforeSend
)));
}, L = /* @__PURE__ */ new Map(), f = /* @__PURE__ */ new Map(), A = (t) => {
const e = L.get(t);
if (e)
return e;
const n = {
controller: new AbortController(),
data: "",
isFetching: !1,
isStreaming: !1,
jsonData: null
};
return L.set(t, n), n;
}, P = (t) => (f.has(t) || f.set(t, []), f.get(t)), O = (t) => {
var e;
return f.has(t) && ((e = f.get(t)) == null ? void 0 : e.length);
}, Q = (t, e) => (P(t).push(e), () => {
f.set(
t,
P(t).filter((n) => n !== e)
), O(t) || (L.delete(t), f.delete(t));
}), Y = (t, e) => {
var a;
L.set(t, {
...A(t),
...e
});
const n = A(t);
(a = f.get(t)) == null || a.forEach((i) => i(n));
}, Z = (t, e = {}) => {
const n = e.id ?? X(), a = m(A(n)), i = (() => {
var g;
const r = {
"Content-Type": "application/json",
"X-STREAM-ID": n
}, o = e.csrfToken ?? ((g = document.querySelector('meta[name="csrf-token"]')) == null ? void 0 : g.getAttribute("content"));
return o && (r["X-CSRF-TOKEN"] = o), r;
})(), F = m(a.value.data), R = m(a.value.jsonData), b = m(a.value.isFetching), d = m(a.value.isStreaming);
let h, v;
const s = (r) => {
Y(n, r);
}, l = () => {
a.value.controller.abort(), (b || d) && W(n), s({
isFetching: !1,
isStreaming: !1
});
}, u = (r) => {
const o = new AbortController(), g = {
method: "POST",
signal: o.signal,
headers: {
...i,
...e.headers ?? {}
},
body: JSON.stringify(r ?? {}),
credentials: e.credentials ?? "same-origin"
}, k = G(n, g);
k !== !1 && (s({
isFetching: !0,
controller: o
}), fetch(t, k ?? g).then(async (c) => {
if (!c.ok) {
const D = await c.text();
throw new Error(D);
}
if (!c.body)
throw new Error(
"ReadableStream not yet supported in this browser."
);
return V(n, c), s({
isFetching: !1,
isStreaming: !0
}), C(c.body.getReader());
}).catch((c) => {
s({
isFetching: !1,
isStreaming: !1
}), p(n, c), T(n);
}));
}, y = (r) => {
l(), u(r), j();
}, j = () => {
s({
data: "",
jsonData: null
});
}, C = (r, o = "") => r.read().then(({ done: g, value: k }) => {
const c = new TextDecoder("utf-8").decode(k), D = o + c;
$(n, c);
const B = {
data: D
};
if (!g)
return s(B), C(r, D);
if (B.isStreaming = !1, e.json)
try {
B.jsonData = JSON.parse(D);
} catch (I) {
p(n, I);
}
return s(B), T(n), "";
});
return q(() => {
h = Q(n, (r) => {
a.value = A(n), b.value = r.isFetching, d.value = r.isStreaming, F.value = r.data, R.value = r.jsonData;
}), v = K(n, e), window.addEventListener("beforeunload", l), e.initialInput && u(e.initialInput);
}), x(() => {
h(), v(), window.removeEventListener("beforeunload", l), O(n) || l();
}), {
data: S(F),
jsonData: S(R),
isFetching: S(b),
isStreaming: S(d),
id: n,
send: y,
cancel: l,
clearData: j
};
}, z = (t, e = {}) => {
const { jsonData: n, data: a, ...i } = Z(t, {
...e,
json: !0
});
return { data: n, strData: a, ...i };
};
export {
U as useEventStream,
z as useJsonStream,
Z as useStream
};