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