@laravel/stream-vue 0.2.0 → 0.3.0

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 CHANGED
@@ -7,7 +7,7 @@
7
7
  <a href="https://www.npmjs.com/package/@laravel/stream-vue"><img src="https://img.shields.io/npm/l/@laravel/stream-vue" alt="License"></a>
8
8
  </p>
9
9
 
10
- Easily consume [Server-Sent Events (SSE)](https://laravel.com/docs/responses#event-streams) in your Vue application.
10
+ Easily consume streams in your Vue application.
11
11
 
12
12
  ## Installation
13
13
 
@@ -15,7 +15,149 @@ Easily consume [Server-Sent Events (SSE)](https://laravel.com/docs/responses#eve
15
15
  npm install @laravel/stream-vue
16
16
  ```
17
17
 
18
- ## Usage
18
+ ## Streaming Responses
19
+
20
+ > [!IMPORTANT]
21
+ > The `useStream` hook is currently in Beta, the API is subject to change prior to the v1.0.0 release. All notable changes will be documented in the [changelog](./../../CHANGELOG.md).
22
+
23
+ The `useStream` hook allows you to seamlessly consume [streamed responses](https://laravel.com/docs/responses#streamed-responses) in your Vue application.
24
+
25
+ Provide your stream URL and the hook will automatically update `data` with the concatenated response as data is returned from your server:
26
+
27
+ ```vue
28
+ <script setup lang="ts">
29
+ import { useStream } from "@laravel/stream-vue";
30
+
31
+ const { data, isFetching, isStreaming, send } = useStream("chat");
32
+
33
+ const sendMessage = () => {
34
+ send({
35
+ message: `Current timestamp: ${Date.now()}`,
36
+ });
37
+ };
38
+ </script>
39
+
40
+ <template>
41
+ <div>
42
+ <div>{{ data }}</div>
43
+ <div v-if="isFetching">Connecting...</div>
44
+ <div v-if="isStreaming">Generating...</div>
45
+ <button @click="sendMessage">Send Message</button>
46
+ </div>
47
+ </template>
48
+ ```
49
+
50
+ When sending data back to the stream, the active connection to the stream is canceled before sending the new data. All requests are sent as JSON `POST` requests.
51
+
52
+ The second argument given to `useStream` is an options object that you may use to customize the stream consumption behavior. The default values for this object are shown below:
53
+
54
+ ```vue
55
+ <script setup lang="ts">
56
+ import { useStream } from "@laravel/stream-vue";
57
+
58
+ const { data } = useStream("chat", {
59
+ id: undefined,
60
+ initialInput: undefined,
61
+ headers: undefined,
62
+ csrfToken: undefined,
63
+ onResponse: (response: Response) => void,
64
+ onData: (data: string) => void,
65
+ onCancel: () => void,
66
+ onFinish: () => void,
67
+ onError: (error: Error) => void,
68
+ });
69
+ </script>
70
+
71
+ <template>
72
+ <div>{{ data }}</div>
73
+ </template>
74
+ ```
75
+
76
+ `onResponse` is triggered after a successful initial response from the stream and the raw [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) is passed to the callback.
77
+
78
+ `onData` is called as each chunk is received, the current chunk is passed to the callback.
79
+
80
+ `onFinish` is called when a stream has finished and when an error is thrown during the fetch/read cycle.
81
+
82
+ By default, a request is not made the to stream on initialization. You may pass an initial payload to the stream by using the `initialInput` option:
83
+
84
+ ```vue
85
+ <script setup lang="ts">
86
+ import { useStream } from "@laravel/stream-vue";
87
+
88
+ const { data } = useStream("chat", {
89
+ initialInput: {
90
+ message: "Introduce yourself.",
91
+ },
92
+ });
93
+ </script>
94
+
95
+ <template>
96
+ <div>{{ data }}</div>
97
+ </template>
98
+ ```
99
+
100
+ To cancel a stream manually, you may use the `cancel` method returned from the hook:
101
+
102
+ ```vue
103
+ <script setup lang="ts">
104
+ import { useStream } from "@laravel/stream-vue";
105
+
106
+ const { data, cancel } = useStream("chat");
107
+ </script>
108
+
109
+ <template>
110
+ <div>
111
+ <div>{{ data }}</div>
112
+ <button @click="cancel">Cancel</button>
113
+ </div>
114
+ </template>
115
+ ```
116
+
117
+ Each time the `useStream` hook is used, a random `id` is generated to identify the stream. This is sent back to the server with each request in the `X-STREAM-ID` header.
118
+
119
+ When consuming the same stream from multiple components, you can read and write to the stream by providing your own `id`:
120
+
121
+ ```vue
122
+ <!-- App.vue -->
123
+ <script setup lang="ts">
124
+ import { useStream } from "@laravel/stream-vue";
125
+ import StreamStatus from "./StreamStatus.vue";
126
+
127
+ const { data, id } = useStream("chat");
128
+ </script>
129
+
130
+ <template>
131
+ <div>
132
+ <div>{{ data }}</div>
133
+ <StreamStatus :id="id" />
134
+ </div>
135
+ </template>
136
+ ```
137
+
138
+ ```vue
139
+ <!-- StreamStatus.vue -->
140
+ <script setup lang="ts">
141
+ import { useStream } from "@laravel/stream-vue";
142
+
143
+ const props = defineProps<{
144
+ id: string;
145
+ }>();
146
+
147
+ const { isFetching, isStreaming } = useStream("chat", { id: props.id });
148
+ </script>
149
+
150
+ <template>
151
+ <div>
152
+ <div v-if="isFetching">Connecting...</div>
153
+ <div v-if="isStreaming">Generating...</div>
154
+ </div>
155
+ </template>
156
+ ```
157
+
158
+ ## Event Streams (SSE)
159
+
160
+ The `useEventStream` hook allows you to seamlessly consume [Server-Sent Events (SSE)](https://laravel.com/docs/responses#event-streams) in your Vue application.
19
161
 
20
162
  Provide your stream URL and the hook will automatically update the `message` with the concatenated response as messages are returned from your server:
21
163
 
@@ -27,7 +169,7 @@ const { message } = useEventStream("/stream");
27
169
  </script>
28
170
 
29
171
  <template>
30
- <div>{{ message }}</div>
172
+ <div>{{ message }}</div>
31
173
  </template>
32
174
  ```
33
175
 
@@ -41,14 +183,33 @@ const { messageParts } = useEventStream("/stream");
41
183
  </script>
42
184
 
43
185
  <template>
44
- <ul>
45
- <li v-for="message in messageParts">
46
- {{ message }}
47
- </li>
48
- </ul>
186
+ <ul>
187
+ <li v-for="message in messageParts">
188
+ {{ message }}
189
+ </li>
190
+ </ul>
49
191
  </template>
50
192
  ```
51
193
 
194
+ If you'd like to listen to multiple events:
195
+
196
+ ```vue
197
+ <script setup lang="ts">
198
+ import { useEventStream } from "@laravel/stream-vue";
199
+
200
+ useEventStream("/stream", {
201
+ eventName: ["update", "create"],
202
+ onMessage: (event) => {
203
+ if (event.type === "update") {
204
+ // Handle update
205
+ } else {
206
+ // Handle create
207
+ }
208
+ },
209
+ });
210
+ </script>
211
+ ```
212
+
52
213
  The second parameter is an options object where all properties are optional (defaults are shown below):
53
214
 
54
215
  ```vue
@@ -56,18 +217,19 @@ The second parameter is an options object where all properties are optional (def
56
217
  import { useEventStream } from "@laravel/stream-vue";
57
218
 
58
219
  const { message } = useEventStream("/stream", {
59
- event: "update",
60
- onMessage: (message) => {
61
- //
62
- },
63
- onError: (error) => {
64
- //
65
- },
66
- onComplete: () => {
67
- //
68
- },
69
- endSignal: "</stream>",
70
- glue: " ",
220
+ event: "update",
221
+ onMessage: (message) => {
222
+ //
223
+ },
224
+ onError: (error) => {
225
+ //
226
+ },
227
+ onComplete: () => {
228
+ //
229
+ },
230
+ endSignal: "</stream>",
231
+ glue: " ",
232
+ replace: false,
71
233
  });
72
234
  </script>
73
235
  ```
@@ -82,14 +244,14 @@ import { onMounted } from "vue";
82
244
  const { message, close } = useEventStream("/stream");
83
245
 
84
246
  onMounted(() => {
85
- setTimeout(() => {
86
- close();
87
- }, 3000);
247
+ setTimeout(() => {
248
+ close();
249
+ }, 3000);
88
250
  });
89
251
  </script>
90
252
 
91
253
  <template>
92
- <div>{{ message }}</div>
254
+ <div>{{ message }}</div>
93
255
  </template>
94
256
  ```
95
257
 
@@ -103,14 +265,14 @@ import { onMounted } from "vue";
103
265
  const { message, clearMessage } = useEventStream("/stream");
104
266
 
105
267
  onMounted(() => {
106
- setTimeout(() => {
107
- clearMessage();
108
- }, 3000);
268
+ setTimeout(() => {
269
+ clearMessage();
270
+ }, 3000);
109
271
  });
110
272
  </script>
111
273
 
112
274
  <template>
113
- <div>{{ message }}</div>
275
+ <div>{{ message }}</div>
114
276
  </template>
115
277
  ```
116
278
 
package/dist/index.d.ts CHANGED
@@ -1,21 +1,34 @@
1
1
  import { Ref } from 'vue';
2
2
 
3
- declare type Options = {
4
- eventName?: string;
3
+ declare type EventStreamOptions = {
4
+ eventName?: string | string[];
5
5
  endSignal?: string;
6
6
  glue?: string;
7
+ replace?: boolean;
7
8
  onMessage?: (event: MessageEvent) => void;
8
9
  onComplete?: () => void;
9
10
  onError?: (error: Event) => void;
10
11
  };
11
12
 
12
- declare type StreamResult = {
13
+ declare type EventStreamResult = {
13
14
  message: Readonly<Ref<string>>;
14
15
  messageParts: Readonly<Ref<readonly string[]>>;
15
16
  close: (resetMessage?: boolean) => void;
16
17
  clearMessage: () => void;
17
18
  };
18
19
 
20
+ declare type StreamOptions = {
21
+ id?: string;
22
+ initialInput?: Record<string, any>;
23
+ headers?: Record<string, string>;
24
+ csrfToken?: string;
25
+ onResponse?: (response: Response) => void;
26
+ onData?: (data: string) => void;
27
+ onCancel?: () => void;
28
+ onFinish?: () => void;
29
+ onError?: (error: Error) => void;
30
+ };
31
+
19
32
  /**
20
33
  * Composable for handling server-sent events (SSE) streams
21
34
  *
@@ -26,6 +39,15 @@ declare type StreamResult = {
26
39
  *
27
40
  * @returns StreamResult object containing the accumulated response, close, and reset functions
28
41
  */
29
- export declare const useEventStream: (url: string, { eventName, endSignal, glue, onMessage, onComplete, onError, }?: Options) => StreamResult;
42
+ export declare const useEventStream: (url: string, { eventName, endSignal, glue, replace, onMessage, onComplete, onError, }?: EventStreamOptions) => EventStreamResult;
43
+
44
+ export declare const useStream: (url: string, options?: StreamOptions) => {
45
+ data: Readonly<Ref<string, string>>;
46
+ isFetching: Readonly<Ref<boolean, boolean>>;
47
+ isStreaming: Readonly<Ref<boolean, boolean>>;
48
+ id: string;
49
+ send: (body: Record<string, any>) => void;
50
+ cancel: () => void;
51
+ };
30
52
 
31
53
  export { }
package/dist/index.es.js CHANGED
@@ -1,47 +1,159 @@
1
- import { ref as g, onMounted as M, onUnmounted as P, watch as w, readonly as v } from "vue";
2
- const o = "data: ", C = (l, {
3
- eventName: d = "update",
4
- endSignal: u = "</stream>",
5
- glue: f = " ",
6
- onMessage: E = () => null,
7
- onComplete: h = () => null,
8
- onError: p = () => null
1
+ import { ref as m, onMounted as k, onUnmounted as x, watch as D, readonly as v } from "vue";
2
+ const C = "data: ", q = (r, {
3
+ eventName: e = "update",
4
+ endSignal: s = "</stream>",
5
+ glue: o = " ",
6
+ replace: A = !1,
7
+ onMessage: b = () => null,
8
+ onComplete: S = () => null,
9
+ onError: w = () => null
9
10
  } = {}) => {
10
- const a = g(""), s = g([]);
11
- let e = null;
12
- const r = () => {
13
- a.value = "", s.value = [];
14
- }, n = (t = !1) => {
15
- e == null || e.removeEventListener(d, i), e == null || e.removeEventListener("error", c), e == null || e.close(), e = null, t && r();
16
- }, i = (t) => {
17
- if ([u, `${o}${u}`].includes(t.data)) {
18
- n(), h();
11
+ const f = m(""), l = m([]), g = Array.isArray(e) ? e : [e];
12
+ let c = null;
13
+ const h = () => {
14
+ f.value = "", l.value = [];
15
+ }, d = (t = !1) => {
16
+ g.forEach((u) => {
17
+ c == null || c.removeEventListener(u, a);
18
+ }), c == null || c.close(), c = null, t && h();
19
+ }, a = (t) => {
20
+ if ([s, `${C}${s}`].includes(t.data)) {
21
+ d(), S();
19
22
  return;
20
23
  }
21
- s.value.push(
22
- t.data.startsWith(o) ? t.data.substring(o.length) : t.data
23
- ), a.value = s.value.join(f), E(t);
24
- }, c = (t) => {
25
- p(t), n();
26
- }, m = () => {
27
- r(), e = new EventSource(l), e.addEventListener(d, i), e.addEventListener("error", c);
24
+ A && h(), l.value.push(
25
+ t.data.startsWith(C) ? t.data.substring(C.length) : t.data
26
+ ), f.value = l.value.join(o), b(t);
27
+ }, i = (t) => {
28
+ w(t), d();
29
+ }, n = () => {
30
+ h(), c = new EventSource(r), g.forEach((t) => {
31
+ c.addEventListener(t, a);
32
+ }), c.addEventListener("error", i);
28
33
  };
29
- return M(() => {
30
- m();
31
- }), P(() => {
34
+ return k(() => {
32
35
  n();
33
- }), w(
34
- () => l,
35
- (t, L) => {
36
- t !== L && (n(), m());
36
+ }), x(() => {
37
+ d();
38
+ }), D(
39
+ () => r,
40
+ (t, u) => {
41
+ t !== u && (d(), n());
37
42
  }
38
43
  ), {
39
- message: v(a),
40
- messageParts: v(s),
41
- close: n,
42
- clearMessage: r
44
+ message: v(f),
45
+ messageParts: v(l),
46
+ close: d,
47
+ clearMessage: h
48
+ };
49
+ }, P = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
50
+ let I = (r = 21) => {
51
+ let e = "", s = crypto.getRandomValues(new Uint8Array(r |= 0));
52
+ for (; r--; )
53
+ e += P[s[r] & 63];
54
+ return e;
55
+ };
56
+ const F = /* @__PURE__ */ new Map(), E = /* @__PURE__ */ new Map(), y = (r) => {
57
+ const e = F.get(r);
58
+ return e || (F.set(r, {
59
+ controller: new AbortController(),
60
+ data: "",
61
+ isFetching: !1,
62
+ isStreaming: !1
63
+ }), F.get(r));
64
+ }, T = (r) => (E.has(r) || E.set(r, []), E.get(r)), O = (r, e) => (T(r).push(e), () => {
65
+ E.set(
66
+ r,
67
+ T(r).filter((s) => s !== e)
68
+ );
69
+ }), X = (r, e = {}) => {
70
+ const s = e.id ?? I(), o = m(y(s)), A = (() => {
71
+ var n;
72
+ const a = {
73
+ "Content-Type": "application/json",
74
+ "X-STREAM-ID": s
75
+ }, i = e.csrfToken ?? ((n = document.querySelector('meta[name="csrf-token"]')) == null ? void 0 : n.getAttribute("content"));
76
+ return i && (a["X-CSRF-TOKEN"] = i), a;
77
+ })(), b = m(o.value.data), S = m(o.value.isFetching), w = m(o.value.isStreaming);
78
+ let f;
79
+ const l = (a) => {
80
+ var n;
81
+ F.set(s, {
82
+ ...y(s),
83
+ ...a
84
+ });
85
+ const i = y(s);
86
+ (n = E.get(s)) == null || n.forEach((t) => t(i));
87
+ }, g = () => {
88
+ var a;
89
+ o.value.controller.abort(), (S || w) && ((a = e.onCancel) == null || a.call(e)), l({
90
+ isFetching: !1,
91
+ isStreaming: !1
92
+ });
93
+ }, c = (a = {}) => {
94
+ const i = new AbortController();
95
+ l({
96
+ isFetching: !0,
97
+ controller: i
98
+ }), fetch(r, {
99
+ method: "POST",
100
+ signal: i.signal,
101
+ headers: {
102
+ ...A,
103
+ ...e.headers ?? {}
104
+ },
105
+ body: JSON.stringify(a)
106
+ }).then(async (n) => {
107
+ var t;
108
+ if (!n.ok) {
109
+ const u = await n.text();
110
+ throw new Error(u);
111
+ }
112
+ if (!n.body)
113
+ throw new Error(
114
+ "ReadableStream not yet supported in this browser."
115
+ );
116
+ return (t = e.onResponse) == null || t.call(e, n), l({
117
+ isFetching: !1,
118
+ isStreaming: !0
119
+ }), d(n.body.getReader());
120
+ }).catch((n) => {
121
+ var t, u;
122
+ l({
123
+ isFetching: !1,
124
+ isStreaming: !1
125
+ }), (t = e.onError) == null || t.call(e, n), (u = e.onFinish) == null || u.call(e);
126
+ });
127
+ }, h = (a) => {
128
+ g(), c(a), l({
129
+ data: ""
130
+ });
131
+ }, d = (a, i = "") => a.read().then(({ done: n, value: t }) => {
132
+ var M, R;
133
+ const u = new TextDecoder("utf-8").decode(t), L = i + u;
134
+ return (M = e.onData) == null || M.call(e, u), n ? (l({
135
+ data: L,
136
+ isStreaming: !1
137
+ }), (R = e.onFinish) == null || R.call(e), "") : (l({
138
+ data: L
139
+ }), d(a, L));
140
+ });
141
+ return k(() => {
142
+ f = O(s, (a) => {
143
+ o.value = y(s), S.value = a.isFetching, w.value = a.isStreaming, b.value = a.data;
144
+ }), window.addEventListener("beforeunload", g), e.initialInput && c(e.initialInput);
145
+ }), x(() => {
146
+ f(), window.removeEventListener("beforeunload", g);
147
+ }), {
148
+ data: v(b),
149
+ isFetching: v(S),
150
+ isStreaming: v(w),
151
+ id: s,
152
+ send: h,
153
+ cancel: g
43
154
  };
44
155
  };
45
156
  export {
46
- C as useEventStream
157
+ q as useEventStream,
158
+ X as useStream
47
159
  };
package/dist/index.umd.js CHANGED
@@ -1 +1 @@
1
- (function(s,t){typeof exports=="object"&&typeof module<"u"?t(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],t):(s=typeof globalThis<"u"?globalThis:s||self,t(s.LaravelStreamVue={},s.Vue))})(this,function(s,t){"use strict";const r="data: ",g=(i,{eventName:u="update",endSignal:f="</stream>",glue:h=" ",onMessage:E=()=>null,onComplete:v=()=>null,onError:y=()=>null}={})=>{const l=t.ref(""),o=t.ref([]);let e=null;const d=()=>{l.value="",o.value=[]},a=(n=!1)=>{e==null||e.removeEventListener(u,c),e==null||e.removeEventListener("error",m),e==null||e.close(),e=null,n&&d()},c=n=>{if([f,`${r}${f}`].includes(n.data)){a(),v();return}o.value.push(n.data.startsWith(r)?n.data.substring(r.length):n.data),l.value=o.value.join(h),E(n)},m=n=>{y(n),a()},p=()=>{d(),e=new EventSource(i),e.addEventListener(u,c),e.addEventListener("error",m)};return t.onMounted(()=>{p()}),t.onUnmounted(()=>{a()}),t.watch(()=>i,(n,S)=>{n!==S&&(a(),p())}),{message:t.readonly(l),messageParts:t.readonly(o),close:a,clearMessage:d}};s.useEventStream=g,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})});
1
+ (function(u,r){typeof exports=="object"&&typeof module<"u"?r(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],r):(u=typeof globalThis<"u"?globalThis:u||self,r(u.LaravelStreamVue={},u.Vue))})(this,function(u,r){"use strict";const v="data: ",k=(a,{eventName:e="update",endSignal:l="</stream>",glue:g=" ",replace:L=!1,onMessage:T=()=>null,onComplete:w=()=>null,onError:E=()=>null}={})=>{const m=r.ref(""),i=r.ref([]),h=Array.isArray(e)?e:[e];let c=null;const S=()=>{m.value="",i.value=[]},f=(t=!1)=>{h.forEach(o=>{c==null||c.removeEventListener(o,n)}),c==null||c.close(),c=null,t&&S()},n=t=>{if([l,`${v}${l}`].includes(t.data)){f(),w();return}L&&S(),i.value.push(t.data.startsWith(v)?t.data.substring(v.length):t.data),m.value=i.value.join(g),T(t)},d=t=>{E(t),f()},s=()=>{S(),c=new EventSource(a),h.forEach(t=>{c.addEventListener(t,n)}),c.addEventListener("error",d)};return r.onMounted(()=>{s()}),r.onUnmounted(()=>{f()}),r.watch(()=>a,(t,o)=>{t!==o&&(f(),s())}),{message:r.readonly(m),messageParts:r.readonly(i),close:f,clearMessage:S}},P="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";let j=(a=21)=>{let e="",l=crypto.getRandomValues(new Uint8Array(a|=0));for(;a--;)e+=P[l[a]&63];return e};const b=new Map,y=new Map,F=a=>{const e=b.get(a);return e||(b.set(a,{controller:new AbortController,data:"",isFetching:!1,isStreaming:!1}),b.get(a))},A=a=>(y.has(a)||y.set(a,[]),y.get(a)),x=(a,e)=>(A(a).push(e),()=>{y.set(a,A(a).filter(l=>l!==e))}),D=(a,e={})=>{const l=e.id??j(),g=r.ref(F(l)),L=(()=>{var s;const n={"Content-Type":"application/json","X-STREAM-ID":l},d=e.csrfToken??((s=document.querySelector('meta[name="csrf-token"]'))==null?void 0:s.getAttribute("content"));return d&&(n["X-CSRF-TOKEN"]=d),n})(),T=r.ref(g.value.data),w=r.ref(g.value.isFetching),E=r.ref(g.value.isStreaming);let m;const i=n=>{var s;b.set(l,{...F(l),...n});const d=F(l);(s=y.get(l))==null||s.forEach(t=>t(d))},h=()=>{var n;g.value.controller.abort(),(w||E)&&((n=e.onCancel)==null||n.call(e)),i({isFetching:!1,isStreaming:!1})},c=(n={})=>{const d=new AbortController;i({isFetching:!0,controller:d}),fetch(a,{method:"POST",signal:d.signal,headers:{...L,...e.headers??{}},body:JSON.stringify(n)}).then(async s=>{var t;if(!s.ok){const o=await s.text();throw new Error(o)}if(!s.body)throw new Error("ReadableStream not yet supported in this browser.");return(t=e.onResponse)==null||t.call(e,s),i({isFetching:!1,isStreaming:!0}),f(s.body.getReader())}).catch(s=>{var t,o;i({isFetching:!1,isStreaming:!1}),(t=e.onError)==null||t.call(e,s),(o=e.onFinish)==null||o.call(e)})},S=n=>{h(),c(n),i({data:""})},f=(n,d="")=>n.read().then(({done:s,value:t})=>{var C,R;const o=new TextDecoder("utf-8").decode(t),M=d+o;return(C=e.onData)==null||C.call(e,o),s?(i({data:M,isStreaming:!1}),(R=e.onFinish)==null||R.call(e),""):(i({data:M}),f(n,M))});return r.onMounted(()=>{m=x(l,n=>{g.value=F(l),w.value=n.isFetching,E.value=n.isStreaming,T.value=n.data}),window.addEventListener("beforeunload",h),e.initialInput&&c(e.initialInput)}),r.onUnmounted(()=>{m(),window.removeEventListener("beforeunload",h)}),{data:r.readonly(T),isFetching:r.readonly(w),isStreaming:r.readonly(E),id:l,send:S,cancel:h}};u.useEventStream=k,u.useStream=D,Object.defineProperty(u,Symbol.toStringTag,{value:"Module"})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@laravel/stream-vue",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Laravel streaming hooks for Vue",
5
5
  "keywords": [
6
6
  "laravel",
@@ -44,6 +44,7 @@
44
44
  "@vitejs/plugin-vue": "^5.0.0",
45
45
  "eslint": "^9.0.0",
46
46
  "jsdom": "^26.0.0",
47
+ "msw": "^2.8.2",
47
48
  "prettier": "^3.5.3",
48
49
  "typescript": "^5.3.0",
49
50
  "vite": "^5.4.19",
@@ -53,6 +54,9 @@
53
54
  "peerDependencies": {
54
55
  "vue": "^3.0.0"
55
56
  },
57
+ "dependencies": {
58
+ "nanoid": "^5.1.5"
59
+ },
56
60
  "scripts": {
57
61
  "build": "vite build",
58
62
  "lint": "eslint --config eslint.config.mjs \"src/**/*.ts\"",