@laravel/stream-vue 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -0
- package/dist/index.d.ts +18 -4
- package/dist/index.es.js +138 -112
- package/dist/index.umd.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -155,6 +155,39 @@ const { isFetching, isStreaming } = useStream("chat", { id: props.id });
|
|
|
155
155
|
</template>
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
+
The `useJsonStream` hook is identical to the `useStream` hook except that it will attempt to parse the data as JSON once it has finished streaming:
|
|
159
|
+
|
|
160
|
+
```vue
|
|
161
|
+
<script setup lang="ts">
|
|
162
|
+
import { useJsonStream } from "@laravel/stream-vue";
|
|
163
|
+
|
|
164
|
+
type User = {
|
|
165
|
+
id: number;
|
|
166
|
+
name: string;
|
|
167
|
+
email: string;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const { data, send } = useJsonStream<{ users: User[] }>("users");
|
|
171
|
+
|
|
172
|
+
const loadUsers = () => {
|
|
173
|
+
send({
|
|
174
|
+
query: "taylor",
|
|
175
|
+
});
|
|
176
|
+
};
|
|
177
|
+
</script>
|
|
178
|
+
|
|
179
|
+
<template>
|
|
180
|
+
<div>
|
|
181
|
+
<ul>
|
|
182
|
+
<li v-for="user in data?.users" :key="user.id">
|
|
183
|
+
{{ user.id }}: {{ user.name }}
|
|
184
|
+
</li>
|
|
185
|
+
</ul>
|
|
186
|
+
<button @click="loadUsers">Load Users</button>
|
|
187
|
+
</div>
|
|
188
|
+
</template>
|
|
189
|
+
```
|
|
190
|
+
|
|
158
191
|
## Event Streams (SSE)
|
|
159
192
|
|
|
160
193
|
The `useEventStream` hook allows you to seamlessly consume [Server-Sent Events (SSE)](https://laravel.com/docs/responses#event-streams) in your Vue application.
|
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ declare type StreamOptions = {
|
|
|
22
22
|
initialInput?: Record<string, any>;
|
|
23
23
|
headers?: Record<string, string>;
|
|
24
24
|
csrfToken?: string;
|
|
25
|
+
json?: boolean;
|
|
25
26
|
onResponse?: (response: Response) => void;
|
|
26
27
|
onData?: (data: string) => void;
|
|
27
28
|
onCancel?: () => void;
|
|
@@ -41,13 +42,26 @@ declare type StreamOptions = {
|
|
|
41
42
|
*/
|
|
42
43
|
export declare const useEventStream: (url: string, { eventName, endSignal, glue, replace, onMessage, onComplete, onError, }?: EventStreamOptions) => EventStreamResult;
|
|
43
44
|
|
|
44
|
-
export declare const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
isStreaming: Readonly<Ref<boolean, boolean>>;
|
|
45
|
+
export declare const useJsonStream: <TJsonData = null>(url: string, options?: Omit<StreamOptions, "json">) => {
|
|
46
|
+
isFetching: Readonly<Ref<boolean>>;
|
|
47
|
+
isStreaming: Readonly<Ref<boolean>>;
|
|
48
48
|
id: string;
|
|
49
49
|
send: (body: Record<string, any>) => void;
|
|
50
50
|
cancel: () => void;
|
|
51
|
+
clearData: () => void;
|
|
52
|
+
data: Readonly<TJsonData | null>;
|
|
53
|
+
strData: Readonly<Ref<string, string>>;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export declare const useStream: <TJsonData = null>(url: string, options?: StreamOptions) => {
|
|
57
|
+
data: Readonly<Ref<string>>;
|
|
58
|
+
jsonData: Readonly<TJsonData | null>;
|
|
59
|
+
isFetching: Readonly<Ref<boolean>>;
|
|
60
|
+
isStreaming: Readonly<Ref<boolean>>;
|
|
61
|
+
id: string;
|
|
62
|
+
send: (body: Record<string, any>) => void;
|
|
63
|
+
cancel: () => void;
|
|
64
|
+
clearData: () => void;
|
|
51
65
|
};
|
|
52
66
|
|
|
53
67
|
export { }
|
package/dist/index.es.js
CHANGED
|
@@ -1,159 +1,185 @@
|
|
|
1
|
-
import { ref as
|
|
2
|
-
const
|
|
1
|
+
import { ref as S, onMounted as x, onUnmounted as O, watch as q, readonly as v } from "vue";
|
|
2
|
+
const M = "data: ", $ = (t, {
|
|
3
3
|
eventName: e = "update",
|
|
4
|
-
endSignal:
|
|
5
|
-
glue:
|
|
6
|
-
replace:
|
|
4
|
+
endSignal: r = "</stream>",
|
|
5
|
+
glue: l = " ",
|
|
6
|
+
replace: w = !1,
|
|
7
7
|
onMessage: b = () => null,
|
|
8
|
-
onComplete:
|
|
9
|
-
onError:
|
|
8
|
+
onComplete: D = () => null,
|
|
9
|
+
onError: E = () => null
|
|
10
10
|
} = {}) => {
|
|
11
|
-
const
|
|
11
|
+
const d = S(""), f = S([]), u = Array.isArray(e) ? e : [e];
|
|
12
12
|
let c = null;
|
|
13
13
|
const h = () => {
|
|
14
|
-
|
|
15
|
-
},
|
|
16
|
-
|
|
17
|
-
c == null || c.removeEventListener(
|
|
18
|
-
}), c == null || c.close(), c = null,
|
|
19
|
-
},
|
|
20
|
-
if ([
|
|
21
|
-
|
|
14
|
+
d.value = "", f.value = [];
|
|
15
|
+
}, g = (a = !1) => {
|
|
16
|
+
u.forEach((s) => {
|
|
17
|
+
c == null || c.removeEventListener(s, y);
|
|
18
|
+
}), c == null || c.close(), c = null, a && h();
|
|
19
|
+
}, y = (a) => {
|
|
20
|
+
if ([r, `${M}${r}`].includes(a.data)) {
|
|
21
|
+
g(), D();
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
),
|
|
27
|
-
},
|
|
28
|
-
|
|
24
|
+
w && h(), f.value.push(
|
|
25
|
+
a.data.startsWith(M) ? a.data.substring(M.length) : a.data
|
|
26
|
+
), d.value = f.value.join(l), b(a);
|
|
27
|
+
}, j = (a) => {
|
|
28
|
+
E(a), g();
|
|
29
29
|
}, n = () => {
|
|
30
|
-
h(), c = new EventSource(
|
|
31
|
-
c.addEventListener(
|
|
32
|
-
}), c.addEventListener("error",
|
|
30
|
+
h(), c = new EventSource(t), u.forEach((a) => {
|
|
31
|
+
c.addEventListener(a, y);
|
|
32
|
+
}), c.addEventListener("error", j);
|
|
33
33
|
};
|
|
34
|
-
return
|
|
34
|
+
return x(() => {
|
|
35
35
|
n();
|
|
36
|
-
}),
|
|
37
|
-
|
|
38
|
-
}),
|
|
39
|
-
() =>
|
|
40
|
-
(
|
|
41
|
-
|
|
36
|
+
}), O(() => {
|
|
37
|
+
g();
|
|
38
|
+
}), q(
|
|
39
|
+
() => t,
|
|
40
|
+
(a, s) => {
|
|
41
|
+
a !== s && (g(), n());
|
|
42
42
|
}
|
|
43
43
|
), {
|
|
44
|
-
message: v(
|
|
45
|
-
messageParts: v(
|
|
46
|
-
close:
|
|
44
|
+
message: v(d),
|
|
45
|
+
messageParts: v(f),
|
|
46
|
+
close: g,
|
|
47
47
|
clearMessage: h
|
|
48
48
|
};
|
|
49
|
-
},
|
|
50
|
-
let
|
|
51
|
-
let e = "",
|
|
52
|
-
for (;
|
|
53
|
-
e +=
|
|
49
|
+
}, N = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
|
|
50
|
+
let X = (t = 21) => {
|
|
51
|
+
let e = "", r = crypto.getRandomValues(new Uint8Array(t |= 0));
|
|
52
|
+
for (; t--; )
|
|
53
|
+
e += N[r[t] & 63];
|
|
54
54
|
return e;
|
|
55
55
|
};
|
|
56
|
-
const
|
|
57
|
-
const e =
|
|
58
|
-
|
|
56
|
+
const A = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Map(), L = (t) => {
|
|
57
|
+
const e = A.get(t);
|
|
58
|
+
if (e)
|
|
59
|
+
return e;
|
|
60
|
+
const r = {
|
|
59
61
|
controller: new AbortController(),
|
|
60
62
|
data: "",
|
|
61
63
|
isFetching: !1,
|
|
62
|
-
isStreaming: !1
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
64
|
+
isStreaming: !1,
|
|
65
|
+
jsonData: null
|
|
66
|
+
};
|
|
67
|
+
return A.set(t, r), r;
|
|
68
|
+
}, P = (t) => (i.has(t) || i.set(t, []), i.get(t)), I = (t) => {
|
|
69
|
+
var e;
|
|
70
|
+
return i.has(t) && ((e = i.get(t)) == null ? void 0 : e.length);
|
|
71
|
+
}, K = (t, e) => (P(t).push(e), () => {
|
|
72
|
+
i.set(
|
|
73
|
+
t,
|
|
74
|
+
P(t).filter((r) => r !== e)
|
|
75
|
+
), I(t) || (A.delete(t), i.delete(t));
|
|
76
|
+
}), V = (t, e = {}) => {
|
|
77
|
+
const r = e.id ?? X(), l = S(L(r)), w = (() => {
|
|
78
|
+
var s;
|
|
79
|
+
const n = {
|
|
73
80
|
"Content-Type": "application/json",
|
|
74
|
-
"X-STREAM-ID":
|
|
75
|
-
},
|
|
76
|
-
return
|
|
77
|
-
})(), b =
|
|
81
|
+
"X-STREAM-ID": r
|
|
82
|
+
}, a = e.csrfToken ?? ((s = document.querySelector('meta[name="csrf-token"]')) == null ? void 0 : s.getAttribute("content"));
|
|
83
|
+
return a && (n["X-CSRF-TOKEN"] = a), n;
|
|
84
|
+
})(), b = S(l.value.data), D = S(l.value.jsonData), E = S(l.value.isFetching), d = S(l.value.isStreaming);
|
|
78
85
|
let f;
|
|
79
|
-
const
|
|
80
|
-
var
|
|
81
|
-
|
|
82
|
-
...
|
|
83
|
-
...
|
|
86
|
+
const u = (n) => {
|
|
87
|
+
var s;
|
|
88
|
+
A.set(r, {
|
|
89
|
+
...L(r),
|
|
90
|
+
...n
|
|
84
91
|
});
|
|
85
|
-
const
|
|
86
|
-
(
|
|
87
|
-
},
|
|
88
|
-
var
|
|
89
|
-
|
|
92
|
+
const a = L(r);
|
|
93
|
+
(s = i.get(r)) == null || s.forEach((o) => o(a));
|
|
94
|
+
}, c = () => {
|
|
95
|
+
var n;
|
|
96
|
+
l.value.controller.abort(), (E || d) && ((n = e.onCancel) == null || n.call(e)), u({
|
|
90
97
|
isFetching: !1,
|
|
91
98
|
isStreaming: !1
|
|
92
99
|
});
|
|
93
|
-
},
|
|
94
|
-
const
|
|
95
|
-
|
|
100
|
+
}, h = (n = {}) => {
|
|
101
|
+
const a = new AbortController();
|
|
102
|
+
u({
|
|
96
103
|
isFetching: !0,
|
|
97
|
-
controller:
|
|
98
|
-
}), fetch(
|
|
104
|
+
controller: a
|
|
105
|
+
}), fetch(t, {
|
|
99
106
|
method: "POST",
|
|
100
|
-
signal:
|
|
107
|
+
signal: a.signal,
|
|
101
108
|
headers: {
|
|
102
|
-
...
|
|
109
|
+
...w,
|
|
103
110
|
...e.headers ?? {}
|
|
104
111
|
},
|
|
105
|
-
body: JSON.stringify(
|
|
106
|
-
}).then(async (
|
|
107
|
-
var
|
|
108
|
-
if (!
|
|
109
|
-
const
|
|
110
|
-
throw new Error(
|
|
112
|
+
body: JSON.stringify(n)
|
|
113
|
+
}).then(async (s) => {
|
|
114
|
+
var o;
|
|
115
|
+
if (!s.ok) {
|
|
116
|
+
const m = await s.text();
|
|
117
|
+
throw new Error(m);
|
|
111
118
|
}
|
|
112
|
-
if (!
|
|
119
|
+
if (!s.body)
|
|
113
120
|
throw new Error(
|
|
114
121
|
"ReadableStream not yet supported in this browser."
|
|
115
122
|
);
|
|
116
|
-
return (
|
|
123
|
+
return (o = e.onResponse) == null || o.call(e, s), u({
|
|
117
124
|
isFetching: !1,
|
|
118
125
|
isStreaming: !0
|
|
119
|
-
}),
|
|
120
|
-
}).catch((
|
|
121
|
-
var
|
|
122
|
-
|
|
126
|
+
}), j(s.body.getReader());
|
|
127
|
+
}).catch((s) => {
|
|
128
|
+
var o, m;
|
|
129
|
+
u({
|
|
123
130
|
isFetching: !1,
|
|
124
131
|
isStreaming: !1
|
|
125
|
-
}), (
|
|
132
|
+
}), (o = e.onError) == null || o.call(e, s), (m = e.onFinish) == null || m.call(e);
|
|
126
133
|
});
|
|
127
|
-
},
|
|
128
|
-
|
|
129
|
-
|
|
134
|
+
}, g = (n) => {
|
|
135
|
+
c(), h(n), y();
|
|
136
|
+
}, y = () => {
|
|
137
|
+
u({
|
|
138
|
+
data: "",
|
|
139
|
+
jsonData: null
|
|
130
140
|
});
|
|
131
|
-
},
|
|
132
|
-
var
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
141
|
+
}, j = (n, a = "") => n.read().then(({ done: s, value: o }) => {
|
|
142
|
+
var R, T, k;
|
|
143
|
+
const m = new TextDecoder("utf-8").decode(o), C = a + m;
|
|
144
|
+
(R = e.onData) == null || R.call(e, m);
|
|
145
|
+
const F = {
|
|
146
|
+
data: C
|
|
147
|
+
};
|
|
148
|
+
if (!s)
|
|
149
|
+
return u(F), j(n, C);
|
|
150
|
+
if (F.isStreaming = !1, e.json)
|
|
151
|
+
try {
|
|
152
|
+
F.jsonData = JSON.parse(C);
|
|
153
|
+
} catch (J) {
|
|
154
|
+
(T = e.onError) == null || T.call(e, J);
|
|
155
|
+
}
|
|
156
|
+
return u(F), (k = e.onFinish) == null || k.call(e), "";
|
|
140
157
|
});
|
|
141
|
-
return
|
|
142
|
-
f =
|
|
143
|
-
|
|
144
|
-
}), window.addEventListener("beforeunload",
|
|
145
|
-
}),
|
|
146
|
-
f(), window.removeEventListener("beforeunload",
|
|
158
|
+
return x(() => {
|
|
159
|
+
f = K(r, (n) => {
|
|
160
|
+
l.value = L(r), E.value = n.isFetching, d.value = n.isStreaming, b.value = n.data, D.value = n.jsonData;
|
|
161
|
+
}), window.addEventListener("beforeunload", c), e.initialInput && h(e.initialInput);
|
|
162
|
+
}), O(() => {
|
|
163
|
+
f(), window.removeEventListener("beforeunload", c), I(r) || c();
|
|
147
164
|
}), {
|
|
148
165
|
data: v(b),
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
166
|
+
jsonData: v(D),
|
|
167
|
+
isFetching: v(E),
|
|
168
|
+
isStreaming: v(d),
|
|
169
|
+
id: r,
|
|
170
|
+
send: g,
|
|
171
|
+
cancel: c,
|
|
172
|
+
clearData: y
|
|
154
173
|
};
|
|
174
|
+
}, B = (t, e = {}) => {
|
|
175
|
+
const { jsonData: r, data: l, ...w } = V(t, {
|
|
176
|
+
...e,
|
|
177
|
+
json: !0
|
|
178
|
+
});
|
|
179
|
+
return { data: r, strData: l, ...w };
|
|
155
180
|
};
|
|
156
181
|
export {
|
|
157
|
-
|
|
158
|
-
|
|
182
|
+
$ as useEventStream,
|
|
183
|
+
B as useJsonStream,
|
|
184
|
+
V as useStream
|
|
159
185
|
};
|
package/dist/index.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(u,
|
|
1
|
+
(function(u,n){typeof exports=="object"&&typeof module<"u"?n(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],n):(u=typeof globalThis<"u"?globalThis:u||self,n(u.LaravelStreamVue={},u.Vue))})(this,function(u,n){"use strict";const M="data: ",q=(t,{eventName:e="update",endSignal:r="</stream>",glue:o=" ",replace:w=!1,onMessage:F=()=>null,onComplete:L=()=>null,onError:E=()=>null}={})=>{const h=n.ref(""),m=n.ref([]),d=Array.isArray(e)?e:[e];let c=null;const g=()=>{h.value="",m.value=[]},S=(a=!1)=>{d.forEach(l=>{c==null||c.removeEventListener(l,b)}),c==null||c.close(),c=null,a&&g()},b=a=>{if([r,`${M}${r}`].includes(a.data)){S(),L();return}w&&g(),m.value.push(a.data.startsWith(M)?a.data.substring(M.length):a.data),h.value=m.value.join(o),F(a)},T=a=>{E(a),S()},s=()=>{g(),c=new EventSource(t),d.forEach(a=>{c.addEventListener(a,b)}),c.addEventListener("error",T)};return n.onMounted(()=>{s()}),n.onUnmounted(()=>{S()}),n.watch(()=>t,(a,l)=>{a!==l&&(S(),s())}),{message:n.readonly(h),messageParts:n.readonly(m),close:S,clearMessage:g}},x="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";let I=(t=21)=>{let e="",r=crypto.getRandomValues(new Uint8Array(t|=0));for(;t--;)e+=x[r[t]&63];return e};const j=new Map,f=new Map,D=t=>{const e=j.get(t);if(e)return e;const r={controller:new AbortController,data:"",isFetching:!1,isStreaming:!1,jsonData:null};return j.set(t,r),r},C=t=>(f.has(t)||f.set(t,[]),f.get(t)),R=t=>{var e;return f.has(t)&&((e=f.get(t))==null?void 0:e.length)},V=(t,e)=>(C(t).push(e),()=>{f.set(t,C(t).filter(r=>r!==e)),R(t)||(j.delete(t),f.delete(t))}),P=(t,e={})=>{const r=e.id??I(),o=n.ref(D(r)),w=(()=>{var l;const s={"Content-Type":"application/json","X-STREAM-ID":r},a=e.csrfToken??((l=document.querySelector('meta[name="csrf-token"]'))==null?void 0:l.getAttribute("content"));return a&&(s["X-CSRF-TOKEN"]=a),s})(),F=n.ref(o.value.data),L=n.ref(o.value.jsonData),E=n.ref(o.value.isFetching),h=n.ref(o.value.isStreaming);let m;const d=s=>{var l;j.set(r,{...D(r),...s});const a=D(r);(l=f.get(r))==null||l.forEach(i=>i(a))},c=()=>{var s;o.value.controller.abort(),(E||h)&&((s=e.onCancel)==null||s.call(e)),d({isFetching:!1,isStreaming:!1})},g=(s={})=>{const a=new AbortController;d({isFetching:!0,controller:a}),fetch(t,{method:"POST",signal:a.signal,headers:{...w,...e.headers??{}},body:JSON.stringify(s)}).then(async l=>{var i;if(!l.ok){const y=await l.text();throw new Error(y)}if(!l.body)throw new Error("ReadableStream not yet supported in this browser.");return(i=e.onResponse)==null||i.call(e,l),d({isFetching:!1,isStreaming:!0}),T(l.body.getReader())}).catch(l=>{var i,y;d({isFetching:!1,isStreaming:!1}),(i=e.onError)==null||i.call(e,l),(y=e.onFinish)==null||y.call(e)})},S=s=>{c(),g(s),b()},b=()=>{d({data:"",jsonData:null})},T=(s,a="")=>s.read().then(({done:l,value:i})=>{var k,O,J;const y=new TextDecoder("utf-8").decode(i),A=a+y;(k=e.onData)==null||k.call(e,y);const v={data:A};if(!l)return d(v),T(s,A);if(v.isStreaming=!1,e.json)try{v.jsonData=JSON.parse(A)}catch(X){(O=e.onError)==null||O.call(e,X)}return d(v),(J=e.onFinish)==null||J.call(e),""});return n.onMounted(()=>{m=V(r,s=>{o.value=D(r),E.value=s.isFetching,h.value=s.isStreaming,F.value=s.data,L.value=s.jsonData}),window.addEventListener("beforeunload",c),e.initialInput&&g(e.initialInput)}),n.onUnmounted(()=>{m(),window.removeEventListener("beforeunload",c),R(r)||c()}),{data:n.readonly(F),jsonData:n.readonly(L),isFetching:n.readonly(E),isStreaming:n.readonly(h),id:r,send:S,cancel:c,clearData:b}},N=(t,e={})=>{const{jsonData:r,data:o,...w}=P(t,{...e,json:!0});return{data:r,strData:o,...w}};u.useEventStream=q,u.useJsonStream=N,u.useStream=P,Object.defineProperty(u,Symbol.toStringTag,{value:"Module"})});
|