use-sse-vue 0.0.1 → 0.1.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
@@ -27,8 +27,18 @@ const { data, status, error, connect, disconnect } = useSse('http://localhost:30
27
27
  <div>
28
28
  <p>Status: {{ status }}</p>
29
29
  <div v-if="data">Received: {{ data }}</div>
30
- <button @click="connect" :disabled="status !== 'closed'">Connect</button>
31
- <button @click="disconnect" :disabled="status === 'closed'">Disconnect</button>
30
+ <button
31
+ @click="connect"
32
+ :disabled="status !== 'closed'"
33
+ >
34
+ Connect
35
+ </button>
36
+ <button
37
+ @click="disconnect"
38
+ :disabled="status === 'closed'"
39
+ >
40
+ Disconnect
41
+ </button>
32
42
  </div>
33
43
  </template>
34
44
  ```
@@ -44,6 +54,9 @@ const { data, status, error, connect, disconnect } = useSse('http://localhost:30
44
54
  - `autoConnect` (`boolean`, default: `true`): Automatically initiate connection on component mount.
45
55
  - `reconnectDelay` (`number`, default: `3000`): Time to wait (in ms) before attempting a reconnect.
46
56
  - `maxReconnectAttempts` (`number`, default: `5`): Maximum consecutive reconnect attempts.
57
+ - `events` (`string[]`, optional): Custom event names to listen to (e.g. `['update', 'delete']`).
58
+ - `eventSourceClass` (`any`, optional): Custom EventSource constructor (e.g. `EventSourcePolyfill` for custom headers).
59
+ - `eventSourceInitDict` (`any`, optional): Init dictionary passed to EventSource (e.g. `{ headers: { Authorization: 'Bearer token' } }`).
47
60
  - `onMessage` (`(data: any, event: MessageEvent) => void`): Callback when a message is received.
48
61
  - `onOpen` (`(event: Event) => void`): Callback when connection opens.
49
62
  - `onError` (`(event: Event) => void`): Callback when connection errors occur.
package/dist/index.d.ts CHANGED
@@ -1 +1 @@
1
- export { useSse, type UseSseOptions } from './useSse';
1
+ export { useSse, type UseSseOptions, type UseSseReturn } from './useSse';
package/dist/index.js CHANGED
@@ -1,7 +1,16 @@
1
- import { onUnmounted, ref, watch } from "vue-demi";
1
+ import { getCurrentScope, onScopeDispose, ref, watch } from "vue-demi";
2
2
  //#region src/useSse.ts
3
+ /**
4
+ * Vue and Nuxt composable for managing Server-Sent Events (SSE) connections.
5
+ * Fully compatible with Vue 2 and Vue 3, featuring auto-reconnect, JSON parsing,
6
+ * dynamic url reactivity, custom events.
7
+ *
8
+ * @param url - The SSE endpoint URL. Can be a static string, a getter function, or a Vue Ref.
9
+ * @param options - Configuration options for the connection.
10
+ * @returns An object containing reactive states and connection controls.
11
+ */
3
12
  function useSse(url, options = {}) {
4
- const { autoConnect = true, reconnectDelay = 3e3, maxReconnectAttempts = 5, onMessage, onError, onOpen } = options;
13
+ const { autoConnect = true, reconnectDelay = 3e3, maxReconnectAttempts = 5, events = [], eventSourceClass, eventSourceInitDict, onMessage, onError, onOpen } = options;
5
14
  const data = ref(null);
6
15
  const status = ref("closed");
7
16
  const error = ref(null);
@@ -9,33 +18,38 @@ function useSse(url, options = {}) {
9
18
  let reconnectCount = 0;
10
19
  let reconnectTimer = null;
11
20
  const isSSR = typeof window === "undefined";
21
+ const handleMessage = (event) => {
22
+ try {
23
+ const parsed = JSON.parse(event.data);
24
+ data.value = parsed;
25
+ onMessage?.(parsed, event);
26
+ } catch {
27
+ data.value = event.data;
28
+ onMessage?.(event.data, event);
29
+ }
30
+ };
12
31
  const connect = () => {
13
32
  if (isSSR || eventSource) return;
14
33
  const targetUrl = typeof url === "function" ? url() : typeof url === "object" && "value" in url ? url.value : url;
15
34
  status.value = "connecting";
16
35
  error.value = null;
17
36
  try {
18
- eventSource = new EventSource(targetUrl);
19
- eventSource.onopen = (event) => {
37
+ const es = new (eventSourceClass || EventSource)(targetUrl, eventSourceInitDict);
38
+ es.onopen = (event) => {
20
39
  status.value = "open";
21
40
  reconnectCount = 0;
22
41
  onOpen?.(event);
23
42
  };
24
- eventSource.onmessage = (event) => {
25
- try {
26
- const parsed = JSON.parse(event.data);
27
- data.value = parsed;
28
- onMessage?.(parsed, event);
29
- } catch {
30
- data.value = event.data;
31
- onMessage?.(event.data, event);
32
- }
33
- };
34
- eventSource.onerror = (event) => {
43
+ es.onmessage = handleMessage;
44
+ events.forEach((eventName) => {
45
+ es.addEventListener(eventName, handleMessage);
46
+ });
47
+ es.onerror = (event) => {
35
48
  error.value = event;
36
49
  onError?.(event);
37
50
  tryReconnect();
38
51
  };
52
+ eventSource = es;
39
53
  } catch (err) {
40
54
  status.value = "closed";
41
55
  error.value = err;
@@ -48,6 +62,9 @@ function useSse(url, options = {}) {
48
62
  reconnectTimer = null;
49
63
  }
50
64
  if (eventSource) {
65
+ events.forEach((eventName) => {
66
+ if (eventSource) eventSource.removeEventListener(eventName, handleMessage);
67
+ });
51
68
  eventSource.close();
52
69
  eventSource = null;
53
70
  }
@@ -68,7 +85,7 @@ function useSse(url, options = {}) {
68
85
  }
69
86
  });
70
87
  if (autoConnect) connect();
71
- onUnmounted(() => {
88
+ if (getCurrentScope()) onScopeDispose(() => {
72
89
  disconnect();
73
90
  });
74
91
  return {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/useSse.ts"],"sourcesContent":["import { ref, onUnmounted, watch, type Ref } from 'vue-demi'\n\nexport interface UseSseOptions<T = any> {\n autoConnect?: boolean\n reconnectDelay?: number\n maxReconnectAttempts?: number\n onMessage?: (data: T, event: MessageEvent) => void\n onError?: (event: Event) => void\n onOpen?: (event: Event) => void\n}\n\nexport function useSse<T = any>(\n url: string | (() => string) | Ref<string>,\n options: UseSseOptions<T> = {}\n) {\n const {\n autoConnect = true,\n reconnectDelay = 3000,\n maxReconnectAttempts = 5,\n onMessage,\n onError,\n onOpen\n } = options\n\n const data = ref<T | null>(null) as Ref<T | null>\n const status = ref<'connecting' | 'open' | 'closed'>('closed')\n const error = ref<Event | null>(null)\n\n let eventSource: EventSource | null = null\n let reconnectCount = 0\n let reconnectTimer: any = null\n\n // Environment check\n const isSSR = typeof window === 'undefined'\n\n const connect = () => {\n if (isSSR || eventSource) return\n\n // Extract target URL from string, ref, or getter function\n const targetUrl =\n typeof url === 'function'\n ? url()\n : typeof url === 'object' && 'value' in url\n ? url.value\n : url\n\n status.value = 'connecting'\n error.value = null\n\n try {\n eventSource = new EventSource(targetUrl)\n\n eventSource.onopen = (event) => {\n status.value = 'open'\n reconnectCount = 0\n onOpen?.(event)\n }\n\n eventSource.onmessage = (event: MessageEvent) => {\n // Attempt to parse JSON, fallback to raw string if it fails\n try {\n const parsed = JSON.parse(event.data)\n data.value = parsed\n onMessage?.(parsed, event)\n } catch {\n data.value = event.data as any\n onMessage?.(event.data, event)\n }\n }\n\n eventSource.onerror = (event) => {\n error.value = event\n onError?.(event)\n\n // Trigger reconnection\n tryReconnect()\n }\n } catch (err) {\n status.value = 'closed'\n error.value = err as any\n tryReconnect()\n }\n }\n\n const disconnect = () => {\n if (reconnectTimer) {\n clearTimeout(reconnectTimer)\n reconnectTimer = null\n }\n if (eventSource) {\n eventSource.close()\n eventSource = null\n }\n status.value = 'closed'\n }\n\n const tryReconnect = () => {\n disconnect()\n\n if (reconnectCount < maxReconnectAttempts) {\n status.value = 'connecting'\n reconnectCount++\n reconnectTimer = setTimeout(connect, reconnectDelay)\n } else {\n status.value = 'closed'\n }\n }\n\n // Monitor URL change if it is reactive\n if (typeof url === 'function' || (typeof url === 'object' && 'value' in url)) {\n watch(url, () => {\n if (status.value !== 'closed') {\n disconnect()\n connect()\n }\n })\n }\n\n if (autoConnect) {\n connect()\n }\n\n onUnmounted(() => {\n disconnect()\n })\n\n return {\n data,\n status,\n error,\n connect,\n disconnect\n }\n}\n"],"mappings":";;AAWA,SAAgB,OACd,KACA,UAA4B,CAAC,GAC7B;CACA,MAAM,EACJ,cAAc,MACd,iBAAiB,KACjB,uBAAuB,GACvB,WACA,SACA,WACE;CAEJ,MAAM,OAAO,IAAc,IAAI;CAC/B,MAAM,SAAS,IAAsC,QAAQ;CAC7D,MAAM,QAAQ,IAAkB,IAAI;CAEpC,IAAI,cAAkC;CACtC,IAAI,iBAAiB;CACrB,IAAI,iBAAsB;CAG1B,MAAM,QAAQ,OAAO,WAAW;CAEhC,MAAM,gBAAgB;EACpB,IAAI,SAAS,aAAa;EAG1B,MAAM,YACJ,OAAO,QAAQ,aACX,IAAI,IACJ,OAAO,QAAQ,YAAY,WAAW,MACpC,IAAI,QACJ;EAER,OAAO,QAAQ;EACf,MAAM,QAAQ;EAEd,IAAI;GACF,cAAc,IAAI,YAAY,SAAS;GAEvC,YAAY,UAAU,UAAU;IAC9B,OAAO,QAAQ;IACf,iBAAiB;IACjB,SAAS,KAAK;GAChB;GAEA,YAAY,aAAa,UAAwB;IAE/C,IAAI;KACF,MAAM,SAAS,KAAK,MAAM,MAAM,IAAI;KACpC,KAAK,QAAQ;KACb,YAAY,QAAQ,KAAK;IAC3B,QAAQ;KACN,KAAK,QAAQ,MAAM;KACnB,YAAY,MAAM,MAAM,KAAK;IAC/B;GACF;GAEA,YAAY,WAAW,UAAU;IAC/B,MAAM,QAAQ;IACd,UAAU,KAAK;IAGf,aAAa;GACf;EACF,SAAS,KAAK;GACZ,OAAO,QAAQ;GACf,MAAM,QAAQ;GACd,aAAa;EACf;CACF;CAEA,MAAM,mBAAmB;EACvB,IAAI,gBAAgB;GAClB,aAAa,cAAc;GAC3B,iBAAiB;EACnB;EACA,IAAI,aAAa;GACf,YAAY,MAAM;GAClB,cAAc;EAChB;EACA,OAAO,QAAQ;CACjB;CAEA,MAAM,qBAAqB;EACzB,WAAW;EAEX,IAAI,iBAAiB,sBAAsB;GACzC,OAAO,QAAQ;GACf;GACA,iBAAiB,WAAW,SAAS,cAAc;EACrD,OACE,OAAO,QAAQ;CAEnB;CAGA,IAAI,OAAO,QAAQ,cAAe,OAAO,QAAQ,YAAY,WAAW,KACtE,MAAM,WAAW;EACf,IAAI,OAAO,UAAU,UAAU;GAC7B,WAAW;GACX,QAAQ;EACV;CACF,CAAC;CAGH,IAAI,aACF,QAAQ;CAGV,kBAAkB;EAChB,WAAW;CACb,CAAC;CAED,OAAO;EACL;EACA;EACA;EACA;EACA;CACF;AACF"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/useSse.ts"],"sourcesContent":["import { ref, getCurrentScope, onScopeDispose, watch, type Ref } from 'vue-demi'\n\n/**\n * Minimal representation of an EventSource interface.\n * Ensures maximum compatibility with custom EventSource polyfills (e.g. event-source-polyfill).\n */\nexport interface MinimalEventSource {\n onopen: ((this: MinimalEventSource, ev: Event) => any) | null\n onmessage: ((this: MinimalEventSource, ev: MessageEvent) => any) | null\n onerror: ((this: MinimalEventSource, ev: Event) => any) | null\n close(): void\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions\n ): void\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions\n ): void\n}\n\n/**\n * Options configuration for the `useSse` composable.\n */\nexport interface UseSseOptions<T = any> {\n /**\n * Whether to automatically connect to the SSE endpoint when the composable is initialized.\n * @default true\n */\n autoConnect?: boolean\n\n /**\n * Delay (in milliseconds) before attempting to reconnect after a connection loss.\n * @default 3000\n */\n reconnectDelay?: number\n\n /**\n * Maximum number of consecutive reconnection attempts before giving up.\n * @default 5\n */\n maxReconnectAttempts?: number\n\n /**\n * Custom event names to listen to (e.g. `['update', 'delete']`).\n * By default, the composable only listens to the default 'message' event.\n */\n events?: string[]\n\n /**\n * Custom `EventSource` class constructor.\n * Useful for using libraries like `event-source-polyfill` to support custom authorization headers.\n */\n eventSourceClass?: new (url: string, eventSourceInitDict?: any) => MinimalEventSource\n\n /**\n * Optional configuration object passed directly to the `EventSource` constructor.\n * e.g., `{ headers: { Authorization: 'Bearer token' } }` when using a polyfill.\n */\n eventSourceInitDict?: any\n\n /**\n * Callback function triggered when a new message is received.\n * @param data - The parsed message data (JSON-parsed if valid, otherwise raw string).\n * @param event - The raw MessageEvent object.\n */\n onMessage?: (data: T, event: MessageEvent) => void\n\n /**\n * Callback function triggered when a connection error occurs.\n * @param event - The raw error Event object.\n */\n onError?: (event: Event) => void\n\n /**\n * Callback function triggered when the connection is successfully opened.\n * @param event - The raw open Event object.\n */\n onOpen?: (event: Event) => void\n}\n\n/**\n * Return signature of the `useSse` composable.\n */\nexport interface UseSseReturn<T = any> {\n /**\n * Reactive data holding the last received event message.\n * Automatically parses JSON payloads if valid; falls back to raw string otherwise.\n */\n data: Ref<T | null>\n\n /**\n * Reactive connection status:\n * - `'connecting'`: currently establishing the connection.\n * - `'open'`: connected and listening for events.\n * - `'closed'`: disconnected or failed to connect.\n */\n status: Ref<'connecting' | 'open' | 'closed'>\n\n /**\n * Reactive reference containing the latest connection error Event, if any occurred.\n */\n error: Ref<Event | null>\n\n /**\n * Manually opens the SSE connection.\n */\n connect: () => void\n\n /**\n * Manually closes the SSE connection and clears any pending reconnection timers.\n */\n disconnect: () => void\n}\n\n/**\n * Vue and Nuxt composable for managing Server-Sent Events (SSE) connections.\n * Fully compatible with Vue 2 and Vue 3, featuring auto-reconnect, JSON parsing,\n * dynamic url reactivity, custom events.\n *\n * @param url - The SSE endpoint URL. Can be a static string, a getter function, or a Vue Ref.\n * @param options - Configuration options for the connection.\n * @returns An object containing reactive states and connection controls.\n */\nexport function useSse<T = any>(\n url: string | (() => string) | Ref<string>,\n options: UseSseOptions<T> = {}\n): UseSseReturn<T> {\n const {\n autoConnect = true,\n reconnectDelay = 3000,\n maxReconnectAttempts = 5,\n events = [],\n eventSourceClass,\n eventSourceInitDict,\n onMessage,\n onError,\n onOpen\n } = options\n\n const data = ref<T | null>(null) as Ref<T | null>\n const status = ref<'connecting' | 'open' | 'closed'>('closed')\n const error = ref<Event | null>(null)\n\n let eventSource: MinimalEventSource | EventSource | null = null\n let reconnectCount = 0\n let reconnectTimer: any = null\n\n // Environment check for Server-Side Rendering (Nuxt safety)\n const isSSR = typeof window === 'undefined'\n\n // Helper to attach message parser and triggers\n const handleMessage = (event: MessageEvent) => {\n try {\n // Attempt to parse JSON; fallback to raw string if it fails\n const parsed = JSON.parse(event.data)\n data.value = parsed\n onMessage?.(parsed, event)\n } catch {\n data.value = event.data as any\n onMessage?.(event.data, event)\n }\n }\n\n const connect = () => {\n if (isSSR || eventSource) return\n\n // Extract target URL from string, ref, or getter function\n const targetUrl =\n typeof url === 'function'\n ? url()\n : typeof url === 'object' && 'value' in url\n ? url.value\n : url\n\n status.value = 'connecting'\n error.value = null\n\n try {\n // Use custom EventSource Ctor if provided (useful for auth headers via polyfill)\n const EventSourceCtor = eventSourceClass || EventSource\n const es = new EventSourceCtor(targetUrl, eventSourceInitDict)\n\n es.onopen = (event: Event) => {\n status.value = 'open'\n reconnectCount = 0 // Reset reconnect attempt counter on successful connection\n onOpen?.(event)\n }\n\n // Always listen to default 'message' event\n es.onmessage = handleMessage\n\n // Listen to custom events if specified\n events.forEach((eventName) => {\n es.addEventListener(eventName, handleMessage as EventListener)\n })\n\n es.onerror = (event: Event) => {\n error.value = event\n onError?.(event)\n\n // Trigger automatic reconnection\n tryReconnect()\n }\n\n eventSource = es\n } catch (err) {\n status.value = 'closed'\n error.value = err as any\n tryReconnect()\n }\n }\n\n const disconnect = () => {\n if (reconnectTimer) {\n clearTimeout(reconnectTimer)\n reconnectTimer = null\n }\n if (eventSource) {\n // Clean up custom event listeners\n events.forEach((eventName) => {\n if (eventSource) {\n eventSource.removeEventListener(eventName, handleMessage as EventListener)\n }\n })\n eventSource.close()\n eventSource = null\n }\n status.value = 'closed'\n }\n\n const tryReconnect = () => {\n disconnect()\n\n if (reconnectCount < maxReconnectAttempts) {\n status.value = 'connecting'\n reconnectCount++\n reconnectTimer = setTimeout(connect, reconnectDelay)\n } else {\n status.value = 'closed'\n }\n }\n\n // Monitor URL change if it is reactive (function or ref)\n if (typeof url === 'function' || (typeof url === 'object' && 'value' in url)) {\n watch(url, () => {\n if (status.value !== 'closed') {\n disconnect()\n connect()\n }\n })\n }\n\n if (autoConnect) {\n connect()\n }\n\n // Advanced composable lifecycle: clean up scope on dispose (Nuxt / Pinia safe)\n if (getCurrentScope()) {\n onScopeDispose(() => {\n disconnect()\n })\n }\n\n return {\n data,\n status,\n error,\n connect,\n disconnect\n }\n}\n"],"mappings":";;;;;;;;;;;AA8HA,SAAgB,OACd,KACA,UAA4B,CAAC,GACZ;CACjB,MAAM,EACJ,cAAc,MACd,iBAAiB,KACjB,uBAAuB,GACvB,SAAS,CAAC,GACV,kBACA,qBACA,WACA,SACA,WACE;CAEJ,MAAM,OAAO,IAAc,IAAI;CAC/B,MAAM,SAAS,IAAsC,QAAQ;CAC7D,MAAM,QAAQ,IAAkB,IAAI;CAEpC,IAAI,cAAuD;CAC3D,IAAI,iBAAiB;CACrB,IAAI,iBAAsB;CAG1B,MAAM,QAAQ,OAAO,WAAW;CAGhC,MAAM,iBAAiB,UAAwB;EAC7C,IAAI;GAEF,MAAM,SAAS,KAAK,MAAM,MAAM,IAAI;GACpC,KAAK,QAAQ;GACb,YAAY,QAAQ,KAAK;EAC3B,QAAQ;GACN,KAAK,QAAQ,MAAM;GACnB,YAAY,MAAM,MAAM,KAAK;EAC/B;CACF;CAEA,MAAM,gBAAgB;EACpB,IAAI,SAAS,aAAa;EAG1B,MAAM,YACJ,OAAO,QAAQ,aACX,IAAI,IACJ,OAAO,QAAQ,YAAY,WAAW,MACpC,IAAI,QACJ;EAER,OAAO,QAAQ;EACf,MAAM,QAAQ;EAEd,IAAI;GAGF,MAAM,KAAK,KADa,oBAAoB,aACb,WAAW,mBAAmB;GAE7D,GAAG,UAAU,UAAiB;IAC5B,OAAO,QAAQ;IACf,iBAAiB;IACjB,SAAS,KAAK;GAChB;GAGA,GAAG,YAAY;GAGf,OAAO,SAAS,cAAc;IAC5B,GAAG,iBAAiB,WAAW,aAA8B;GAC/D,CAAC;GAED,GAAG,WAAW,UAAiB;IAC7B,MAAM,QAAQ;IACd,UAAU,KAAK;IAGf,aAAa;GACf;GAEA,cAAc;EAChB,SAAS,KAAK;GACZ,OAAO,QAAQ;GACf,MAAM,QAAQ;GACd,aAAa;EACf;CACF;CAEA,MAAM,mBAAmB;EACvB,IAAI,gBAAgB;GAClB,aAAa,cAAc;GAC3B,iBAAiB;EACnB;EACA,IAAI,aAAa;GAEf,OAAO,SAAS,cAAc;IAC5B,IAAI,aACF,YAAY,oBAAoB,WAAW,aAA8B;GAE7E,CAAC;GACD,YAAY,MAAM;GAClB,cAAc;EAChB;EACA,OAAO,QAAQ;CACjB;CAEA,MAAM,qBAAqB;EACzB,WAAW;EAEX,IAAI,iBAAiB,sBAAsB;GACzC,OAAO,QAAQ;GACf;GACA,iBAAiB,WAAW,SAAS,cAAc;EACrD,OACE,OAAO,QAAQ;CAEnB;CAGA,IAAI,OAAO,QAAQ,cAAe,OAAO,QAAQ,YAAY,WAAW,KACtE,MAAM,WAAW;EACf,IAAI,OAAO,UAAU,UAAU;GAC7B,WAAW;GACX,QAAQ;EACV;CACF,CAAC;CAGH,IAAI,aACF,QAAQ;CAIV,IAAI,gBAAgB,GAClB,qBAAqB;EACnB,WAAW;CACb,CAAC;CAGH,OAAO;EACL;EACA;EACA;EACA;EACA;CACF;AACF"}
@@ -1,10 +1,19 @@
1
1
  (function(global, factory) {
2
- typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("vue-demi")) : typeof define === "function" && define.amd ? define(["exports", "vue-demi"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["use-sse-vue"] = {}, global.VueDemi));
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("vue-demi")) : typeof define === "function" && define.amd ? define(["exports", "vue-demi"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.VueSse = {}, global.VueDemi));
3
3
  })(this, function(exports, vue_demi) {
4
4
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
5
5
  //#region src/useSse.ts
6
+ /**
7
+ * Vue and Nuxt composable for managing Server-Sent Events (SSE) connections.
8
+ * Fully compatible with Vue 2 and Vue 3, featuring auto-reconnect, JSON parsing,
9
+ * dynamic url reactivity, custom events.
10
+ *
11
+ * @param url - The SSE endpoint URL. Can be a static string, a getter function, or a Vue Ref.
12
+ * @param options - Configuration options for the connection.
13
+ * @returns An object containing reactive states and connection controls.
14
+ */
6
15
  function useSse(url, options = {}) {
7
- const { autoConnect = true, reconnectDelay = 3e3, maxReconnectAttempts = 5, onMessage, onError, onOpen } = options;
16
+ const { autoConnect = true, reconnectDelay = 3e3, maxReconnectAttempts = 5, events = [], eventSourceClass, eventSourceInitDict, onMessage, onError, onOpen } = options;
8
17
  const data = (0, vue_demi.ref)(null);
9
18
  const status = (0, vue_demi.ref)("closed");
10
19
  const error = (0, vue_demi.ref)(null);
@@ -12,33 +21,38 @@
12
21
  let reconnectCount = 0;
13
22
  let reconnectTimer = null;
14
23
  const isSSR = typeof window === "undefined";
24
+ const handleMessage = (event) => {
25
+ try {
26
+ const parsed = JSON.parse(event.data);
27
+ data.value = parsed;
28
+ onMessage?.(parsed, event);
29
+ } catch {
30
+ data.value = event.data;
31
+ onMessage?.(event.data, event);
32
+ }
33
+ };
15
34
  const connect = () => {
16
35
  if (isSSR || eventSource) return;
17
36
  const targetUrl = typeof url === "function" ? url() : typeof url === "object" && "value" in url ? url.value : url;
18
37
  status.value = "connecting";
19
38
  error.value = null;
20
39
  try {
21
- eventSource = new EventSource(targetUrl);
22
- eventSource.onopen = (event) => {
40
+ const es = new (eventSourceClass || EventSource)(targetUrl, eventSourceInitDict);
41
+ es.onopen = (event) => {
23
42
  status.value = "open";
24
43
  reconnectCount = 0;
25
44
  onOpen?.(event);
26
45
  };
27
- eventSource.onmessage = (event) => {
28
- try {
29
- const parsed = JSON.parse(event.data);
30
- data.value = parsed;
31
- onMessage?.(parsed, event);
32
- } catch {
33
- data.value = event.data;
34
- onMessage?.(event.data, event);
35
- }
36
- };
37
- eventSource.onerror = (event) => {
46
+ es.onmessage = handleMessage;
47
+ events.forEach((eventName) => {
48
+ es.addEventListener(eventName, handleMessage);
49
+ });
50
+ es.onerror = (event) => {
38
51
  error.value = event;
39
52
  onError?.(event);
40
53
  tryReconnect();
41
54
  };
55
+ eventSource = es;
42
56
  } catch (err) {
43
57
  status.value = "closed";
44
58
  error.value = err;
@@ -51,6 +65,9 @@
51
65
  reconnectTimer = null;
52
66
  }
53
67
  if (eventSource) {
68
+ events.forEach((eventName) => {
69
+ if (eventSource) eventSource.removeEventListener(eventName, handleMessage);
70
+ });
54
71
  eventSource.close();
55
72
  eventSource = null;
56
73
  }
@@ -71,7 +88,7 @@
71
88
  }
72
89
  });
73
90
  if (autoConnect) connect();
74
- (0, vue_demi.onUnmounted)(() => {
91
+ if ((0, vue_demi.getCurrentScope)()) (0, vue_demi.onScopeDispose)(() => {
75
92
  disconnect();
76
93
  });
77
94
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"index.umd.cjs","names":[],"sources":["../src/useSse.ts"],"sourcesContent":["import { ref, onUnmounted, watch, type Ref } from 'vue-demi'\n\nexport interface UseSseOptions<T = any> {\n autoConnect?: boolean\n reconnectDelay?: number\n maxReconnectAttempts?: number\n onMessage?: (data: T, event: MessageEvent) => void\n onError?: (event: Event) => void\n onOpen?: (event: Event) => void\n}\n\nexport function useSse<T = any>(\n url: string | (() => string) | Ref<string>,\n options: UseSseOptions<T> = {}\n) {\n const {\n autoConnect = true,\n reconnectDelay = 3000,\n maxReconnectAttempts = 5,\n onMessage,\n onError,\n onOpen\n } = options\n\n const data = ref<T | null>(null) as Ref<T | null>\n const status = ref<'connecting' | 'open' | 'closed'>('closed')\n const error = ref<Event | null>(null)\n\n let eventSource: EventSource | null = null\n let reconnectCount = 0\n let reconnectTimer: any = null\n\n // Environment check\n const isSSR = typeof window === 'undefined'\n\n const connect = () => {\n if (isSSR || eventSource) return\n\n // Extract target URL from string, ref, or getter function\n const targetUrl =\n typeof url === 'function'\n ? url()\n : typeof url === 'object' && 'value' in url\n ? url.value\n : url\n\n status.value = 'connecting'\n error.value = null\n\n try {\n eventSource = new EventSource(targetUrl)\n\n eventSource.onopen = (event) => {\n status.value = 'open'\n reconnectCount = 0\n onOpen?.(event)\n }\n\n eventSource.onmessage = (event: MessageEvent) => {\n // Attempt to parse JSON, fallback to raw string if it fails\n try {\n const parsed = JSON.parse(event.data)\n data.value = parsed\n onMessage?.(parsed, event)\n } catch {\n data.value = event.data as any\n onMessage?.(event.data, event)\n }\n }\n\n eventSource.onerror = (event) => {\n error.value = event\n onError?.(event)\n\n // Trigger reconnection\n tryReconnect()\n }\n } catch (err) {\n status.value = 'closed'\n error.value = err as any\n tryReconnect()\n }\n }\n\n const disconnect = () => {\n if (reconnectTimer) {\n clearTimeout(reconnectTimer)\n reconnectTimer = null\n }\n if (eventSource) {\n eventSource.close()\n eventSource = null\n }\n status.value = 'closed'\n }\n\n const tryReconnect = () => {\n disconnect()\n\n if (reconnectCount < maxReconnectAttempts) {\n status.value = 'connecting'\n reconnectCount++\n reconnectTimer = setTimeout(connect, reconnectDelay)\n } else {\n status.value = 'closed'\n }\n }\n\n // Monitor URL change if it is reactive\n if (typeof url === 'function' || (typeof url === 'object' && 'value' in url)) {\n watch(url, () => {\n if (status.value !== 'closed') {\n disconnect()\n connect()\n }\n })\n }\n\n if (autoConnect) {\n connect()\n }\n\n onUnmounted(() => {\n disconnect()\n })\n\n return {\n data,\n status,\n error,\n connect,\n disconnect\n }\n}\n"],"mappings":";;;;;CAWA,SAAgB,OACd,KACA,UAA4B,CAAC,GAC7B;EACA,MAAM,EACJ,cAAc,MACd,iBAAiB,KACjB,uBAAuB,GACvB,WACA,SACA,WACE;EAEJ,MAAM,QAAA,GAAA,SAAA,IAAA,CAAqB,IAAI;EAC/B,MAAM,UAAA,GAAA,SAAA,IAAA,CAA+C,QAAQ;EAC7D,MAAM,SAAA,GAAA,SAAA,IAAA,CAA0B,IAAI;EAEpC,IAAI,cAAkC;EACtC,IAAI,iBAAiB;EACrB,IAAI,iBAAsB;EAG1B,MAAM,QAAQ,OAAO,WAAW;EAEhC,MAAM,gBAAgB;GACpB,IAAI,SAAS,aAAa;GAG1B,MAAM,YACJ,OAAO,QAAQ,aACX,IAAI,IACJ,OAAO,QAAQ,YAAY,WAAW,MACpC,IAAI,QACJ;GAER,OAAO,QAAQ;GACf,MAAM,QAAQ;GAEd,IAAI;IACF,cAAc,IAAI,YAAY,SAAS;IAEvC,YAAY,UAAU,UAAU;KAC9B,OAAO,QAAQ;KACf,iBAAiB;KACjB,SAAS,KAAK;IAChB;IAEA,YAAY,aAAa,UAAwB;KAE/C,IAAI;MACF,MAAM,SAAS,KAAK,MAAM,MAAM,IAAI;MACpC,KAAK,QAAQ;MACb,YAAY,QAAQ,KAAK;KAC3B,QAAQ;MACN,KAAK,QAAQ,MAAM;MACnB,YAAY,MAAM,MAAM,KAAK;KAC/B;IACF;IAEA,YAAY,WAAW,UAAU;KAC/B,MAAM,QAAQ;KACd,UAAU,KAAK;KAGf,aAAa;IACf;GACF,SAAS,KAAK;IACZ,OAAO,QAAQ;IACf,MAAM,QAAQ;IACd,aAAa;GACf;EACF;EAEA,MAAM,mBAAmB;GACvB,IAAI,gBAAgB;IAClB,aAAa,cAAc;IAC3B,iBAAiB;GACnB;GACA,IAAI,aAAa;IACf,YAAY,MAAM;IAClB,cAAc;GAChB;GACA,OAAO,QAAQ;EACjB;EAEA,MAAM,qBAAqB;GACzB,WAAW;GAEX,IAAI,iBAAiB,sBAAsB;IACzC,OAAO,QAAQ;IACf;IACA,iBAAiB,WAAW,SAAS,cAAc;GACrD,OACE,OAAO,QAAQ;EAEnB;EAGA,IAAI,OAAO,QAAQ,cAAe,OAAO,QAAQ,YAAY,WAAW,KACtE,CAAA,GAAA,SAAA,MAAA,CAAM,WAAW;GACf,IAAI,OAAO,UAAU,UAAU;IAC7B,WAAW;IACX,QAAQ;GACV;EACF,CAAC;EAGH,IAAI,aACF,QAAQ;EAGV,CAAA,GAAA,SAAA,YAAA,OAAkB;GAChB,WAAW;EACb,CAAC;EAED,OAAO;GACL;GACA;GACA;GACA;GACA;EACF;CACF"}
1
+ {"version":3,"file":"index.umd.cjs","names":[],"sources":["../src/useSse.ts"],"sourcesContent":["import { ref, getCurrentScope, onScopeDispose, watch, type Ref } from 'vue-demi'\n\n/**\n * Minimal representation of an EventSource interface.\n * Ensures maximum compatibility with custom EventSource polyfills (e.g. event-source-polyfill).\n */\nexport interface MinimalEventSource {\n onopen: ((this: MinimalEventSource, ev: Event) => any) | null\n onmessage: ((this: MinimalEventSource, ev: MessageEvent) => any) | null\n onerror: ((this: MinimalEventSource, ev: Event) => any) | null\n close(): void\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions\n ): void\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions\n ): void\n}\n\n/**\n * Options configuration for the `useSse` composable.\n */\nexport interface UseSseOptions<T = any> {\n /**\n * Whether to automatically connect to the SSE endpoint when the composable is initialized.\n * @default true\n */\n autoConnect?: boolean\n\n /**\n * Delay (in milliseconds) before attempting to reconnect after a connection loss.\n * @default 3000\n */\n reconnectDelay?: number\n\n /**\n * Maximum number of consecutive reconnection attempts before giving up.\n * @default 5\n */\n maxReconnectAttempts?: number\n\n /**\n * Custom event names to listen to (e.g. `['update', 'delete']`).\n * By default, the composable only listens to the default 'message' event.\n */\n events?: string[]\n\n /**\n * Custom `EventSource` class constructor.\n * Useful for using libraries like `event-source-polyfill` to support custom authorization headers.\n */\n eventSourceClass?: new (url: string, eventSourceInitDict?: any) => MinimalEventSource\n\n /**\n * Optional configuration object passed directly to the `EventSource` constructor.\n * e.g., `{ headers: { Authorization: 'Bearer token' } }` when using a polyfill.\n */\n eventSourceInitDict?: any\n\n /**\n * Callback function triggered when a new message is received.\n * @param data - The parsed message data (JSON-parsed if valid, otherwise raw string).\n * @param event - The raw MessageEvent object.\n */\n onMessage?: (data: T, event: MessageEvent) => void\n\n /**\n * Callback function triggered when a connection error occurs.\n * @param event - The raw error Event object.\n */\n onError?: (event: Event) => void\n\n /**\n * Callback function triggered when the connection is successfully opened.\n * @param event - The raw open Event object.\n */\n onOpen?: (event: Event) => void\n}\n\n/**\n * Return signature of the `useSse` composable.\n */\nexport interface UseSseReturn<T = any> {\n /**\n * Reactive data holding the last received event message.\n * Automatically parses JSON payloads if valid; falls back to raw string otherwise.\n */\n data: Ref<T | null>\n\n /**\n * Reactive connection status:\n * - `'connecting'`: currently establishing the connection.\n * - `'open'`: connected and listening for events.\n * - `'closed'`: disconnected or failed to connect.\n */\n status: Ref<'connecting' | 'open' | 'closed'>\n\n /**\n * Reactive reference containing the latest connection error Event, if any occurred.\n */\n error: Ref<Event | null>\n\n /**\n * Manually opens the SSE connection.\n */\n connect: () => void\n\n /**\n * Manually closes the SSE connection and clears any pending reconnection timers.\n */\n disconnect: () => void\n}\n\n/**\n * Vue and Nuxt composable for managing Server-Sent Events (SSE) connections.\n * Fully compatible with Vue 2 and Vue 3, featuring auto-reconnect, JSON parsing,\n * dynamic url reactivity, custom events.\n *\n * @param url - The SSE endpoint URL. Can be a static string, a getter function, or a Vue Ref.\n * @param options - Configuration options for the connection.\n * @returns An object containing reactive states and connection controls.\n */\nexport function useSse<T = any>(\n url: string | (() => string) | Ref<string>,\n options: UseSseOptions<T> = {}\n): UseSseReturn<T> {\n const {\n autoConnect = true,\n reconnectDelay = 3000,\n maxReconnectAttempts = 5,\n events = [],\n eventSourceClass,\n eventSourceInitDict,\n onMessage,\n onError,\n onOpen\n } = options\n\n const data = ref<T | null>(null) as Ref<T | null>\n const status = ref<'connecting' | 'open' | 'closed'>('closed')\n const error = ref<Event | null>(null)\n\n let eventSource: MinimalEventSource | EventSource | null = null\n let reconnectCount = 0\n let reconnectTimer: any = null\n\n // Environment check for Server-Side Rendering (Nuxt safety)\n const isSSR = typeof window === 'undefined'\n\n // Helper to attach message parser and triggers\n const handleMessage = (event: MessageEvent) => {\n try {\n // Attempt to parse JSON; fallback to raw string if it fails\n const parsed = JSON.parse(event.data)\n data.value = parsed\n onMessage?.(parsed, event)\n } catch {\n data.value = event.data as any\n onMessage?.(event.data, event)\n }\n }\n\n const connect = () => {\n if (isSSR || eventSource) return\n\n // Extract target URL from string, ref, or getter function\n const targetUrl =\n typeof url === 'function'\n ? url()\n : typeof url === 'object' && 'value' in url\n ? url.value\n : url\n\n status.value = 'connecting'\n error.value = null\n\n try {\n // Use custom EventSource Ctor if provided (useful for auth headers via polyfill)\n const EventSourceCtor = eventSourceClass || EventSource\n const es = new EventSourceCtor(targetUrl, eventSourceInitDict)\n\n es.onopen = (event: Event) => {\n status.value = 'open'\n reconnectCount = 0 // Reset reconnect attempt counter on successful connection\n onOpen?.(event)\n }\n\n // Always listen to default 'message' event\n es.onmessage = handleMessage\n\n // Listen to custom events if specified\n events.forEach((eventName) => {\n es.addEventListener(eventName, handleMessage as EventListener)\n })\n\n es.onerror = (event: Event) => {\n error.value = event\n onError?.(event)\n\n // Trigger automatic reconnection\n tryReconnect()\n }\n\n eventSource = es\n } catch (err) {\n status.value = 'closed'\n error.value = err as any\n tryReconnect()\n }\n }\n\n const disconnect = () => {\n if (reconnectTimer) {\n clearTimeout(reconnectTimer)\n reconnectTimer = null\n }\n if (eventSource) {\n // Clean up custom event listeners\n events.forEach((eventName) => {\n if (eventSource) {\n eventSource.removeEventListener(eventName, handleMessage as EventListener)\n }\n })\n eventSource.close()\n eventSource = null\n }\n status.value = 'closed'\n }\n\n const tryReconnect = () => {\n disconnect()\n\n if (reconnectCount < maxReconnectAttempts) {\n status.value = 'connecting'\n reconnectCount++\n reconnectTimer = setTimeout(connect, reconnectDelay)\n } else {\n status.value = 'closed'\n }\n }\n\n // Monitor URL change if it is reactive (function or ref)\n if (typeof url === 'function' || (typeof url === 'object' && 'value' in url)) {\n watch(url, () => {\n if (status.value !== 'closed') {\n disconnect()\n connect()\n }\n })\n }\n\n if (autoConnect) {\n connect()\n }\n\n // Advanced composable lifecycle: clean up scope on dispose (Nuxt / Pinia safe)\n if (getCurrentScope()) {\n onScopeDispose(() => {\n disconnect()\n })\n }\n\n return {\n data,\n status,\n error,\n connect,\n disconnect\n }\n}\n"],"mappings":";;;;;;;;;;;;;;CA8HA,SAAgB,OACd,KACA,UAA4B,CAAC,GACZ;EACjB,MAAM,EACJ,cAAc,MACd,iBAAiB,KACjB,uBAAuB,GACvB,SAAS,CAAC,GACV,kBACA,qBACA,WACA,SACA,WACE;EAEJ,MAAM,QAAA,GAAA,SAAA,IAAA,CAAqB,IAAI;EAC/B,MAAM,UAAA,GAAA,SAAA,IAAA,CAA+C,QAAQ;EAC7D,MAAM,SAAA,GAAA,SAAA,IAAA,CAA0B,IAAI;EAEpC,IAAI,cAAuD;EAC3D,IAAI,iBAAiB;EACrB,IAAI,iBAAsB;EAG1B,MAAM,QAAQ,OAAO,WAAW;EAGhC,MAAM,iBAAiB,UAAwB;GAC7C,IAAI;IAEF,MAAM,SAAS,KAAK,MAAM,MAAM,IAAI;IACpC,KAAK,QAAQ;IACb,YAAY,QAAQ,KAAK;GAC3B,QAAQ;IACN,KAAK,QAAQ,MAAM;IACnB,YAAY,MAAM,MAAM,KAAK;GAC/B;EACF;EAEA,MAAM,gBAAgB;GACpB,IAAI,SAAS,aAAa;GAG1B,MAAM,YACJ,OAAO,QAAQ,aACX,IAAI,IACJ,OAAO,QAAQ,YAAY,WAAW,MACpC,IAAI,QACJ;GAER,OAAO,QAAQ;GACf,MAAM,QAAQ;GAEd,IAAI;IAGF,MAAM,KAAK,KADa,oBAAoB,aACb,WAAW,mBAAmB;IAE7D,GAAG,UAAU,UAAiB;KAC5B,OAAO,QAAQ;KACf,iBAAiB;KACjB,SAAS,KAAK;IAChB;IAGA,GAAG,YAAY;IAGf,OAAO,SAAS,cAAc;KAC5B,GAAG,iBAAiB,WAAW,aAA8B;IAC/D,CAAC;IAED,GAAG,WAAW,UAAiB;KAC7B,MAAM,QAAQ;KACd,UAAU,KAAK;KAGf,aAAa;IACf;IAEA,cAAc;GAChB,SAAS,KAAK;IACZ,OAAO,QAAQ;IACf,MAAM,QAAQ;IACd,aAAa;GACf;EACF;EAEA,MAAM,mBAAmB;GACvB,IAAI,gBAAgB;IAClB,aAAa,cAAc;IAC3B,iBAAiB;GACnB;GACA,IAAI,aAAa;IAEf,OAAO,SAAS,cAAc;KAC5B,IAAI,aACF,YAAY,oBAAoB,WAAW,aAA8B;IAE7E,CAAC;IACD,YAAY,MAAM;IAClB,cAAc;GAChB;GACA,OAAO,QAAQ;EACjB;EAEA,MAAM,qBAAqB;GACzB,WAAW;GAEX,IAAI,iBAAiB,sBAAsB;IACzC,OAAO,QAAQ;IACf;IACA,iBAAiB,WAAW,SAAS,cAAc;GACrD,OACE,OAAO,QAAQ;EAEnB;EAGA,IAAI,OAAO,QAAQ,cAAe,OAAO,QAAQ,YAAY,WAAW,KACtE,CAAA,GAAA,SAAA,MAAA,CAAM,WAAW;GACf,IAAI,OAAO,UAAU,UAAU;IAC7B,WAAW;IACX,QAAQ;GACV;EACF,CAAC;EAGH,IAAI,aACF,QAAQ;EAIV,KAAA,GAAA,SAAA,gBAAA,CAAoB,GAClB,CAAA,GAAA,SAAA,eAAA,OAAqB;GACnB,WAAW;EACb,CAAC;EAGH,OAAO;GACL;GACA;GACA;GACA;GACA;EACF;CACF"}
package/dist/useSse.d.ts CHANGED
@@ -1,51 +1,103 @@
1
1
  import { type Ref } from 'vue-demi';
2
+ /**
3
+ * Minimal representation of an EventSource interface.
4
+ * Ensures maximum compatibility with custom EventSource polyfills (e.g. event-source-polyfill).
5
+ */
6
+ export interface MinimalEventSource {
7
+ onopen: ((this: MinimalEventSource, ev: Event) => any) | null;
8
+ onmessage: ((this: MinimalEventSource, ev: MessageEvent) => any) | null;
9
+ onerror: ((this: MinimalEventSource, ev: Event) => any) | null;
10
+ close(): void;
11
+ addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
12
+ removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
13
+ }
14
+ /**
15
+ * Options configuration for the `useSse` composable.
16
+ */
2
17
  export interface UseSseOptions<T = any> {
18
+ /**
19
+ * Whether to automatically connect to the SSE endpoint when the composable is initialized.
20
+ * @default true
21
+ */
3
22
  autoConnect?: boolean;
23
+ /**
24
+ * Delay (in milliseconds) before attempting to reconnect after a connection loss.
25
+ * @default 3000
26
+ */
4
27
  reconnectDelay?: number;
28
+ /**
29
+ * Maximum number of consecutive reconnection attempts before giving up.
30
+ * @default 5
31
+ */
5
32
  maxReconnectAttempts?: number;
33
+ /**
34
+ * Custom event names to listen to (e.g. `['update', 'delete']`).
35
+ * By default, the composable only listens to the default 'message' event.
36
+ */
37
+ events?: string[];
38
+ /**
39
+ * Custom `EventSource` class constructor.
40
+ * Useful for using libraries like `event-source-polyfill` to support custom authorization headers.
41
+ */
42
+ eventSourceClass?: new (url: string, eventSourceInitDict?: any) => MinimalEventSource;
43
+ /**
44
+ * Optional configuration object passed directly to the `EventSource` constructor.
45
+ * e.g., `{ headers: { Authorization: 'Bearer token' } }` when using a polyfill.
46
+ */
47
+ eventSourceInitDict?: any;
48
+ /**
49
+ * Callback function triggered when a new message is received.
50
+ * @param data - The parsed message data (JSON-parsed if valid, otherwise raw string).
51
+ * @param event - The raw MessageEvent object.
52
+ */
6
53
  onMessage?: (data: T, event: MessageEvent) => void;
54
+ /**
55
+ * Callback function triggered when a connection error occurs.
56
+ * @param event - The raw error Event object.
57
+ */
7
58
  onError?: (event: Event) => void;
59
+ /**
60
+ * Callback function triggered when the connection is successfully opened.
61
+ * @param event - The raw open Event object.
62
+ */
8
63
  onOpen?: (event: Event) => void;
9
64
  }
10
- export declare function useSse<T = any>(url: string | (() => string) | Ref<string>, options?: UseSseOptions<T>): {
65
+ /**
66
+ * Return signature of the `useSse` composable.
67
+ */
68
+ export interface UseSseReturn<T = any> {
69
+ /**
70
+ * Reactive data holding the last received event message.
71
+ * Automatically parses JSON payloads if valid; falls back to raw string otherwise.
72
+ */
11
73
  data: Ref<T | null>;
12
- status: Ref<"connecting" | "open" | "closed">;
13
- error: Ref<{
14
- readonly bubbles: boolean;
15
- cancelBubble: boolean;
16
- readonly cancelable: boolean;
17
- readonly composed: boolean;
18
- readonly currentTarget: {
19
- addEventListener: (type: string, callback: EventListenerOrEventListenerObject | null, options?: AddEventListenerOptions | boolean) => void;
20
- dispatchEvent: (event: Event) => boolean;
21
- removeEventListener: (type: string, callback: EventListenerOrEventListenerObject | null, options?: EventListenerOptions | boolean) => void;
22
- } | null;
23
- readonly defaultPrevented: boolean;
24
- readonly eventPhase: number;
25
- readonly isTrusted: boolean;
26
- returnValue: boolean;
27
- readonly srcElement: {
28
- addEventListener: (type: string, callback: EventListenerOrEventListenerObject | null, options?: AddEventListenerOptions | boolean) => void;
29
- dispatchEvent: (event: Event) => boolean;
30
- removeEventListener: (type: string, callback: EventListenerOrEventListenerObject | null, options?: EventListenerOptions | boolean) => void;
31
- } | null;
32
- readonly target: {
33
- addEventListener: (type: string, callback: EventListenerOrEventListenerObject | null, options?: AddEventListenerOptions | boolean) => void;
34
- dispatchEvent: (event: Event) => boolean;
35
- removeEventListener: (type: string, callback: EventListenerOrEventListenerObject | null, options?: EventListenerOptions | boolean) => void;
36
- } | null;
37
- readonly timeStamp: DOMHighResTimeStamp;
38
- readonly type: string;
39
- composedPath: () => EventTarget[];
40
- initEvent: (type: string, bubbles?: boolean, cancelable?: boolean) => void;
41
- preventDefault: () => void;
42
- stopImmediatePropagation: () => void;
43
- stopPropagation: () => void;
44
- readonly NONE: 0;
45
- readonly CAPTURING_PHASE: 1;
46
- readonly AT_TARGET: 2;
47
- readonly BUBBLING_PHASE: 3;
48
- } | null>;
74
+ /**
75
+ * Reactive connection status:
76
+ * - `'connecting'`: currently establishing the connection.
77
+ * - `'open'`: connected and listening for events.
78
+ * - `'closed'`: disconnected or failed to connect.
79
+ */
80
+ status: Ref<'connecting' | 'open' | 'closed'>;
81
+ /**
82
+ * Reactive reference containing the latest connection error Event, if any occurred.
83
+ */
84
+ error: Ref<Event | null>;
85
+ /**
86
+ * Manually opens the SSE connection.
87
+ */
49
88
  connect: () => void;
89
+ /**
90
+ * Manually closes the SSE connection and clears any pending reconnection timers.
91
+ */
50
92
  disconnect: () => void;
51
- };
93
+ }
94
+ /**
95
+ * Vue and Nuxt composable for managing Server-Sent Events (SSE) connections.
96
+ * Fully compatible with Vue 2 and Vue 3, featuring auto-reconnect, JSON parsing,
97
+ * dynamic url reactivity, custom events.
98
+ *
99
+ * @param url - The SSE endpoint URL. Can be a static string, a getter function, or a Vue Ref.
100
+ * @param options - Configuration options for the connection.
101
+ * @returns An object containing reactive states and connection controls.
102
+ */
103
+ export declare function useSse<T = any>(url: string | (() => string) | Ref<string>, options?: UseSseOptions<T>): UseSseReturn<T>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "use-sse-vue",
3
3
  "private": false,
4
- "version": "0.0.1",
4
+ "version": "0.1.0",
5
5
  "description": "A lightweight, robust Vue and Nuxt composable for managing Server-Sent Events (SSE) connections with auto-reconnect, JSON parsing, and SSR safety",
6
6
  "type": "module",
7
7
  "license": "MIT",
@@ -41,9 +41,9 @@
41
41
  "dist"
42
42
  ],
43
43
  "scripts": {
44
- "dev": "node playground/server.js & vite playground --config vite.config.ts",
44
+ "dev": "node --env-file=playground/.env playground/server.js & vite playground --config vite.config.ts",
45
45
  "playground": "vite playground --config vite.config.ts",
46
- "server": "node playground/server.js",
46
+ "server": "node --env-file=playground/.env playground/server.js",
47
47
  "build": "vite build && vue-tsc",
48
48
  "preview": "vite preview",
49
49
  "type-check": "vue-tsc --noEmit && vue-tsc -p playground/tsconfig.json",
@@ -59,6 +59,7 @@
59
59
  "eslint-plugin-vue": "^10.9.2",
60
60
  "globals": "^17.7.0",
61
61
  "prettier": "^3.8.5",
62
+ "rollup-plugin-visualizer": "^7.0.1",
62
63
  "tailwindcss": "^4.3.1",
63
64
  "typescript": "~6.0.2",
64
65
  "typescript-eslint": "^8.62.0",