vue-streaming 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 +699 -0
- package/dist/index.cjs +202 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +144 -0
- package/dist/index.d.ts +144 -0
- package/dist/index.js +199 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,699 @@
|
|
|
1
|
+
# vue-streaming
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
**Universal Vue 3 streaming component for real-time data and media**
|
|
5
|
+
|
|
6
|
+
[](https://github.com/keroloszakaria/vue-streaming)
|
|
7
|
+
[](https://vuejs.org/)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
[](https://opensource.org/licenses/MIT)
|
|
10
|
+
[](https://bundlephobia.com/keroloszakaria/vue-streaming)
|
|
11
|
+
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
## 🌟 Features
|
|
15
|
+
|
|
16
|
+
Vue Streaming provides a unified `<StreamPlayer>` component that handles **6 different streaming protocols** with a consistent, declarative API:
|
|
17
|
+
|
|
18
|
+
- 🔌 **WebSocket** - Real-time bidirectional communication
|
|
19
|
+
- 📡 **Server-Sent Events (SSE)** - Server-to-client event streams
|
|
20
|
+
- 🌊 **HTTP Streaming** - Chunked transfer encoding streams
|
|
21
|
+
- 🔄 **Long Polling** - HTTP-based persistent connections
|
|
22
|
+
- 📺 **HLS Video** - HTTP Live Streaming for adaptive video
|
|
23
|
+
- 🎥 **WebRTC** - Peer-to-peer real-time communication
|
|
24
|
+
|
|
25
|
+
### ✨ Why Choose Vue Streaming?
|
|
26
|
+
|
|
27
|
+
- **One API, Many Protocols**: Switch between streaming types with just a prop change
|
|
28
|
+
- **Vue 3 Native**: Built for Composition API with full TypeScript support
|
|
29
|
+
- **Headless Design**: No imposed styling - complete UI control
|
|
30
|
+
- **Production Ready**: Auto-reconnection, error handling, and backoff strategies
|
|
31
|
+
- **Lightweight**: Thin wrapper around `js-streaming` core library
|
|
32
|
+
- **Extensible**: Custom slots and event system for any use case
|
|
33
|
+
|
|
34
|
+
## 📦 Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# npm
|
|
38
|
+
npm install vue-streaming js-streaming
|
|
39
|
+
|
|
40
|
+
# yarn
|
|
41
|
+
yarn add vue-streaming js-streaming
|
|
42
|
+
|
|
43
|
+
# pnpm
|
|
44
|
+
pnpm add vue-streaming js-streaming
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
> **Note**: Both `vue-streaming` and `js-streaming` are required. Vue Streaming is a wrapper that leverages the core `js-streaming` library.
|
|
48
|
+
|
|
49
|
+
### Requirements
|
|
50
|
+
|
|
51
|
+
- **Vue**: 3.3.0 or higher
|
|
52
|
+
- **Node**: 16+ (for build tools)
|
|
53
|
+
- **Build System**: Vite, Nuxt 3, or Vue CLI with TypeScript support
|
|
54
|
+
|
|
55
|
+
## 🚀 Quick Start
|
|
56
|
+
|
|
57
|
+
### Basic WebSocket Example
|
|
58
|
+
|
|
59
|
+
```vue
|
|
60
|
+
<script setup lang="ts">
|
|
61
|
+
import { ref } from "vue";
|
|
62
|
+
import { StreamPlayer } from "vue-streaming";
|
|
63
|
+
|
|
64
|
+
const player = ref<InstanceType<typeof StreamPlayer>>();
|
|
65
|
+
const config = {
|
|
66
|
+
url: "wss://echo.websocket.events",
|
|
67
|
+
protocols: ["echo-protocol"],
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
function sendMessage() {
|
|
71
|
+
player.value?.send("Hello WebSocket!");
|
|
72
|
+
}
|
|
73
|
+
</script>
|
|
74
|
+
|
|
75
|
+
<template>
|
|
76
|
+
<div>
|
|
77
|
+
<h2>WebSocket Demo</h2>
|
|
78
|
+
<StreamPlayer
|
|
79
|
+
ref="player"
|
|
80
|
+
type="websocket"
|
|
81
|
+
:config="config"
|
|
82
|
+
:auto-open="true"
|
|
83
|
+
@message="console.log('Received:', $event)"
|
|
84
|
+
@status="console.log('Status:', $event)"
|
|
85
|
+
/>
|
|
86
|
+
<button @click="sendMessage">Send Message</button>
|
|
87
|
+
</div>
|
|
88
|
+
</template>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### HLS Video Streaming
|
|
92
|
+
|
|
93
|
+
```vue
|
|
94
|
+
<script setup lang="ts">
|
|
95
|
+
import { StreamPlayer } from "vue-streaming";
|
|
96
|
+
|
|
97
|
+
const hlsConfig = {
|
|
98
|
+
url: "https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8",
|
|
99
|
+
};
|
|
100
|
+
</script>
|
|
101
|
+
|
|
102
|
+
<template>
|
|
103
|
+
<StreamPlayer
|
|
104
|
+
type="hls"
|
|
105
|
+
:config="hlsConfig"
|
|
106
|
+
:auto-open="true"
|
|
107
|
+
:autoplay="true"
|
|
108
|
+
:controls="true"
|
|
109
|
+
:muted="false"
|
|
110
|
+
/>
|
|
111
|
+
</template>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Server-Sent Events
|
|
115
|
+
|
|
116
|
+
```vue
|
|
117
|
+
<script setup lang="ts">
|
|
118
|
+
import { StreamPlayer } from "vue-streaming";
|
|
119
|
+
|
|
120
|
+
const sseConfig = {
|
|
121
|
+
url: "/api/events",
|
|
122
|
+
headers: { Authorization: "Bearer token" },
|
|
123
|
+
};
|
|
124
|
+
</script>
|
|
125
|
+
|
|
126
|
+
<template>
|
|
127
|
+
<StreamPlayer
|
|
128
|
+
type="sse"
|
|
129
|
+
:config="sseConfig"
|
|
130
|
+
:auto-open="true"
|
|
131
|
+
@message="handleServerEvent"
|
|
132
|
+
/>
|
|
133
|
+
</template>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## 📚 API Reference
|
|
137
|
+
|
|
138
|
+
### Props
|
|
139
|
+
|
|
140
|
+
| Prop | Type | Default | Description |
|
|
141
|
+
| ------------- | ------------ | ---------- | -------------------------------------------------------------------------- |
|
|
142
|
+
| `type` | `StreamType` | _required_ | Protocol type: `websocket`, `sse`, `http`, `long-polling`, `hls`, `webrtc` |
|
|
143
|
+
| `config` | `object` | _required_ | Protocol-specific configuration object |
|
|
144
|
+
| `autoOpen` | `boolean` | `false` | Automatically open connection on mount |
|
|
145
|
+
| `autoplay` | `boolean` | `false` | Auto-play for video streams (HLS/WebRTC) |
|
|
146
|
+
| `controls` | `boolean` | `true` | Show video controls |
|
|
147
|
+
| `playsInline` | `boolean` | `true` | Play video inline on mobile |
|
|
148
|
+
| `muted` | `boolean` | `false` | Start video muted |
|
|
149
|
+
| `logLimit` | `number` | `500` | Maximum messages to keep in memory |
|
|
150
|
+
| `videoAttrs` | `object` | `{}` | Additional HTML video element attributes |
|
|
151
|
+
|
|
152
|
+
### Events
|
|
153
|
+
|
|
154
|
+
| Event | Payload | Description |
|
|
155
|
+
| ---------- | -------------- | ------------------------------------------------------------------------- |
|
|
156
|
+
| `@open` | `void` | Connection established |
|
|
157
|
+
| `@close` | `void` | Connection closed |
|
|
158
|
+
| `@status` | `StreamStatus` | Status change: `idle`, `connecting`, `open`, `closing`, `closed`, `error` |
|
|
159
|
+
| `@error` | `Error` | Error occurred |
|
|
160
|
+
| `@message` | `any` | New message/data received |
|
|
161
|
+
|
|
162
|
+
### Exposed Methods & Properties
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
interface StreamPlayerInstance {
|
|
166
|
+
// Methods
|
|
167
|
+
open(): Promise<void>;
|
|
168
|
+
close(): Promise<void>;
|
|
169
|
+
send(data: unknown): void; // Available for WebSocket/WebRTC
|
|
170
|
+
|
|
171
|
+
// Reactive State
|
|
172
|
+
status: Ref<StreamStatus>;
|
|
173
|
+
isOpen: Ref<boolean>;
|
|
174
|
+
error: Ref<Error | null>;
|
|
175
|
+
messages: Ref<unknown[]>;
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Configuration Objects
|
|
180
|
+
|
|
181
|
+
Each stream type accepts different configuration options:
|
|
182
|
+
|
|
183
|
+
#### WebSocket Config
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
{
|
|
187
|
+
url: string
|
|
188
|
+
protocols?: string[]
|
|
189
|
+
headers?: Record<string, string>
|
|
190
|
+
binaryType?: 'blob' | 'arraybuffer'
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
#### SSE Config
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
{
|
|
198
|
+
url: string
|
|
199
|
+
headers?: Record<string, string>
|
|
200
|
+
withCredentials?: boolean
|
|
201
|
+
eventSourceInitDict?: EventSourceInit
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
#### HTTP Streaming Config
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
{
|
|
209
|
+
url: string
|
|
210
|
+
method?: string
|
|
211
|
+
headers?: Record<string, string>
|
|
212
|
+
body?: any
|
|
213
|
+
signal?: AbortSignal
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
#### Long Polling Config
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
{
|
|
221
|
+
url: string
|
|
222
|
+
interval?: number
|
|
223
|
+
headers?: Record<string, string>
|
|
224
|
+
timeout?: number
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
#### HLS Config
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
{
|
|
232
|
+
url: string
|
|
233
|
+
hlsConfig?: any // Passed to HLS.js
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
#### WebRTC Config
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
{
|
|
241
|
+
configuration?: RTCConfiguration
|
|
242
|
+
attachVideo?: boolean // Auto-attach video stream
|
|
243
|
+
// Additional WebRTC-specific options
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## 🎨 Customization with Slots
|
|
248
|
+
|
|
249
|
+
### Custom Log Display
|
|
250
|
+
|
|
251
|
+
```vue
|
|
252
|
+
<template>
|
|
253
|
+
<StreamPlayer type="websocket" :config="config">
|
|
254
|
+
<template #log="{ messages, status, error }">
|
|
255
|
+
<div class="custom-log">
|
|
256
|
+
<div class="status-bar">
|
|
257
|
+
Status: <span :class="status">{{ status }}</span>
|
|
258
|
+
</div>
|
|
259
|
+
<div v-if="error" class="error">{{ error.message }}</div>
|
|
260
|
+
<div class="messages">
|
|
261
|
+
<div v-for="(msg, i) in messages" :key="i" class="message">
|
|
262
|
+
{{ typeof msg === "string" ? msg : JSON.stringify(msg) }}
|
|
263
|
+
</div>
|
|
264
|
+
</div>
|
|
265
|
+
</div>
|
|
266
|
+
</template>
|
|
267
|
+
</StreamPlayer>
|
|
268
|
+
</template>
|
|
269
|
+
|
|
270
|
+
<style scoped>
|
|
271
|
+
.custom-log {
|
|
272
|
+
border: 2px solid #007acc;
|
|
273
|
+
border-radius: 8px;
|
|
274
|
+
padding: 16px;
|
|
275
|
+
background: #f8f9fa;
|
|
276
|
+
}
|
|
277
|
+
.status-bar {
|
|
278
|
+
font-weight: bold;
|
|
279
|
+
}
|
|
280
|
+
.status.open {
|
|
281
|
+
color: green;
|
|
282
|
+
}
|
|
283
|
+
.status.error {
|
|
284
|
+
color: red;
|
|
285
|
+
}
|
|
286
|
+
.messages {
|
|
287
|
+
max-height: 300px;
|
|
288
|
+
overflow-y: auto;
|
|
289
|
+
}
|
|
290
|
+
.message {
|
|
291
|
+
padding: 4px 0;
|
|
292
|
+
border-bottom: 1px solid #eee;
|
|
293
|
+
}
|
|
294
|
+
</style>
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Custom Action Buttons
|
|
298
|
+
|
|
299
|
+
```vue
|
|
300
|
+
<template>
|
|
301
|
+
<StreamPlayer type="websocket" :config="config">
|
|
302
|
+
<template #actions="{ open, close, send, isOpen, status }">
|
|
303
|
+
<div class="action-bar">
|
|
304
|
+
<button @click="open" :disabled="isOpen">Connect</button>
|
|
305
|
+
<button @click="close" :disabled="!isOpen">Disconnect</button>
|
|
306
|
+
<button @click="send('ping')" :disabled="!isOpen">Ping</button>
|
|
307
|
+
<span class="status-indicator" :class="status">{{ status }}</span>
|
|
308
|
+
</div>
|
|
309
|
+
</template>
|
|
310
|
+
</StreamPlayer>
|
|
311
|
+
</template>
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## 🛠️ Advanced Usage
|
|
315
|
+
|
|
316
|
+
### Auto-Reconnection and Error Handling
|
|
317
|
+
|
|
318
|
+
```vue
|
|
319
|
+
<script setup lang="ts">
|
|
320
|
+
import { ref } from "vue";
|
|
321
|
+
import { StreamPlayer } from "vue-streaming";
|
|
322
|
+
|
|
323
|
+
const config = ref({
|
|
324
|
+
url: "wss://api.example.com/ws",
|
|
325
|
+
// Auto-reconnection settings
|
|
326
|
+
autoReconnect: true,
|
|
327
|
+
maxRetries: 5,
|
|
328
|
+
heartbeatMs: 30000,
|
|
329
|
+
backoff: {
|
|
330
|
+
baseMs: 1000,
|
|
331
|
+
maxMs: 10000,
|
|
332
|
+
factor: 1.5,
|
|
333
|
+
jitter: true,
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
function handleError(error: Error) {
|
|
338
|
+
console.error("Stream error:", error);
|
|
339
|
+
// Custom error handling logic
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function handleStatusChange(status: string) {
|
|
343
|
+
if (status === "error") {
|
|
344
|
+
// Handle connection errors
|
|
345
|
+
} else if (status === "open") {
|
|
346
|
+
console.log("Successfully connected!");
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
</script>
|
|
350
|
+
|
|
351
|
+
<template>
|
|
352
|
+
<StreamPlayer
|
|
353
|
+
type="websocket"
|
|
354
|
+
:config="config"
|
|
355
|
+
@error="handleError"
|
|
356
|
+
@status="handleStatusChange"
|
|
357
|
+
/>
|
|
358
|
+
</template>
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Dynamic Configuration
|
|
362
|
+
|
|
363
|
+
```vue
|
|
364
|
+
<script setup lang="ts">
|
|
365
|
+
import { ref, computed } from "vue";
|
|
366
|
+
import { StreamPlayer } from "vue-streaming";
|
|
367
|
+
|
|
368
|
+
const apiEndpoint = ref("wss://api.example.com");
|
|
369
|
+
const authToken = ref("");
|
|
370
|
+
|
|
371
|
+
const dynamicConfig = computed(() => ({
|
|
372
|
+
url: `${apiEndpoint.value}/ws`,
|
|
373
|
+
headers: {
|
|
374
|
+
Authorization: `Bearer ${authToken.value}`,
|
|
375
|
+
},
|
|
376
|
+
}));
|
|
377
|
+
|
|
378
|
+
// Configuration changes will automatically recreate the stream
|
|
379
|
+
</script>
|
|
380
|
+
|
|
381
|
+
<template>
|
|
382
|
+
<div>
|
|
383
|
+
<input v-model="apiEndpoint" placeholder="WebSocket URL" />
|
|
384
|
+
<input v-model="authToken" placeholder="Auth Token" />
|
|
385
|
+
|
|
386
|
+
<StreamPlayer
|
|
387
|
+
type="websocket"
|
|
388
|
+
:config="dynamicConfig"
|
|
389
|
+
:auto-open="!!authToken"
|
|
390
|
+
/>
|
|
391
|
+
</div>
|
|
392
|
+
</template>
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Programmatic Control
|
|
396
|
+
|
|
397
|
+
```vue
|
|
398
|
+
<script setup lang="ts">
|
|
399
|
+
import { ref, onMounted } from "vue";
|
|
400
|
+
import { StreamPlayer } from "vue-streaming";
|
|
401
|
+
|
|
402
|
+
const player = ref<InstanceType<typeof StreamPlayer>>();
|
|
403
|
+
|
|
404
|
+
onMounted(() => {
|
|
405
|
+
// Programmatic control
|
|
406
|
+
setTimeout(() => {
|
|
407
|
+
player.value?.open();
|
|
408
|
+
}, 1000);
|
|
409
|
+
|
|
410
|
+
// Send periodic messages
|
|
411
|
+
setInterval(() => {
|
|
412
|
+
if (player.value?.isOpen) {
|
|
413
|
+
player.value.send({
|
|
414
|
+
type: "heartbeat",
|
|
415
|
+
timestamp: Date.now(),
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
}, 10000);
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
// Access reactive state
|
|
422
|
+
const connectionStatus = computed(() => player.value?.status || "idle");
|
|
423
|
+
const messageCount = computed(() => player.value?.messages.length || 0);
|
|
424
|
+
</script>
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
## 🔧 TypeScript Support
|
|
428
|
+
|
|
429
|
+
Vue Streaming is fully typed. Import types for enhanced development experience:
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
import type {
|
|
433
|
+
StreamType,
|
|
434
|
+
StreamStatus,
|
|
435
|
+
StreamState,
|
|
436
|
+
StreamAPI,
|
|
437
|
+
} from "vue-streaming";
|
|
438
|
+
|
|
439
|
+
// Component instance type
|
|
440
|
+
import { StreamPlayer } from "vue-streaming";
|
|
441
|
+
type StreamPlayerInstance = InstanceType<typeof StreamPlayer>;
|
|
442
|
+
|
|
443
|
+
// Usage in composition function
|
|
444
|
+
function useStreamPlayer() {
|
|
445
|
+
const player = ref<StreamPlayerInstance>();
|
|
446
|
+
const status = computed(() => player.value?.status || "idle");
|
|
447
|
+
|
|
448
|
+
return { player, status };
|
|
449
|
+
}
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
## 🌐 Nuxt 3 / SSR Usage
|
|
453
|
+
|
|
454
|
+
For server-side rendering, wrap the component to prevent hydration issues:
|
|
455
|
+
|
|
456
|
+
```vue
|
|
457
|
+
<template>
|
|
458
|
+
<div>
|
|
459
|
+
<ClientOnly>
|
|
460
|
+
<StreamPlayer type="websocket" :config="config" :auto-open="true" />
|
|
461
|
+
<template #fallback>
|
|
462
|
+
<div>Loading stream player...</div>
|
|
463
|
+
</template>
|
|
464
|
+
</ClientOnly>
|
|
465
|
+
</div>
|
|
466
|
+
</template>
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
Or create an async component:
|
|
470
|
+
|
|
471
|
+
```vue
|
|
472
|
+
<script setup lang="ts">
|
|
473
|
+
import { defineAsyncComponent } from "vue";
|
|
474
|
+
|
|
475
|
+
const StreamPlayer = defineAsyncComponent(() =>
|
|
476
|
+
import("vue-streaming").then((m) => m.StreamPlayer)
|
|
477
|
+
);
|
|
478
|
+
</script>
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
## 📱 Real-World Examples
|
|
482
|
+
|
|
483
|
+
### Chat Application
|
|
484
|
+
|
|
485
|
+
```vue
|
|
486
|
+
<script setup lang="ts">
|
|
487
|
+
import { ref } from "vue";
|
|
488
|
+
import { StreamPlayer } from "vue-streaming";
|
|
489
|
+
|
|
490
|
+
const message = ref("");
|
|
491
|
+
const chatPlayer = ref<InstanceType<typeof StreamPlayer>>();
|
|
492
|
+
|
|
493
|
+
const config = {
|
|
494
|
+
url: "wss://chat.example.com/ws",
|
|
495
|
+
protocols: ["chat-v1"],
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
function sendMessage() {
|
|
499
|
+
if (message.value.trim()) {
|
|
500
|
+
chatPlayer.value?.send({
|
|
501
|
+
type: "message",
|
|
502
|
+
content: message.value,
|
|
503
|
+
timestamp: new Date().toISOString(),
|
|
504
|
+
});
|
|
505
|
+
message.value = "";
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function handleChatMessage(msg: any) {
|
|
510
|
+
if (msg.type === "message") {
|
|
511
|
+
// Handle incoming chat message
|
|
512
|
+
console.log(`${msg.user}: ${msg.content}`);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
</script>
|
|
516
|
+
|
|
517
|
+
<template>
|
|
518
|
+
<div class="chat-container">
|
|
519
|
+
<StreamPlayer
|
|
520
|
+
ref="chatPlayer"
|
|
521
|
+
type="websocket"
|
|
522
|
+
:config="config"
|
|
523
|
+
:auto-open="true"
|
|
524
|
+
@message="handleChatMessage"
|
|
525
|
+
>
|
|
526
|
+
<template #log="{ messages }">
|
|
527
|
+
<div class="chat-messages">
|
|
528
|
+
<div v-for="msg in messages" :key="msg.id" class="message">
|
|
529
|
+
<strong>{{ msg.user }}:</strong> {{ msg.content }}
|
|
530
|
+
</div>
|
|
531
|
+
</div>
|
|
532
|
+
</template>
|
|
533
|
+
|
|
534
|
+
<template #actions="{ isOpen }">
|
|
535
|
+
<div class="chat-input">
|
|
536
|
+
<input
|
|
537
|
+
v-model="message"
|
|
538
|
+
@keyup.enter="sendMessage"
|
|
539
|
+
:disabled="!isOpen"
|
|
540
|
+
placeholder="Type a message..."
|
|
541
|
+
/>
|
|
542
|
+
<button @click="sendMessage" :disabled="!isOpen">Send</button>
|
|
543
|
+
</div>
|
|
544
|
+
</template>
|
|
545
|
+
</StreamPlayer>
|
|
546
|
+
</div>
|
|
547
|
+
</template>
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### Live Data Dashboard
|
|
551
|
+
|
|
552
|
+
```vue
|
|
553
|
+
<script setup lang="ts">
|
|
554
|
+
import { ref, computed } from "vue";
|
|
555
|
+
import { StreamPlayer } from "vue-streaming";
|
|
556
|
+
|
|
557
|
+
const metrics = ref<any[]>([]);
|
|
558
|
+
|
|
559
|
+
const config = {
|
|
560
|
+
url: "/api/metrics/stream",
|
|
561
|
+
headers: { Accept: "text/event-stream" },
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
function handleMetricsUpdate(data: any) {
|
|
565
|
+
if (data.type === "metrics") {
|
|
566
|
+
metrics.value = [...metrics.value, data.payload].slice(-50); // Keep last 50
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
const latestMetrics = computed(
|
|
571
|
+
() => metrics.value[metrics.value.length - 1]?.payload || {}
|
|
572
|
+
);
|
|
573
|
+
</script>
|
|
574
|
+
|
|
575
|
+
<template>
|
|
576
|
+
<div class="dashboard">
|
|
577
|
+
<h1>Live Metrics Dashboard</h1>
|
|
578
|
+
|
|
579
|
+
<div class="metrics-grid">
|
|
580
|
+
<div class="metric-card">
|
|
581
|
+
<h3>CPU Usage</h3>
|
|
582
|
+
<div class="metric-value">{{ latestMetrics.cpu }}%</div>
|
|
583
|
+
</div>
|
|
584
|
+
<div class="metric-card">
|
|
585
|
+
<h3>Memory</h3>
|
|
586
|
+
<div class="metric-value">{{ latestMetrics.memory }}MB</div>
|
|
587
|
+
</div>
|
|
588
|
+
</div>
|
|
589
|
+
|
|
590
|
+
<StreamPlayer
|
|
591
|
+
type="sse"
|
|
592
|
+
:config="config"
|
|
593
|
+
:auto-open="true"
|
|
594
|
+
@message="handleMetricsUpdate"
|
|
595
|
+
>
|
|
596
|
+
<template #log="{ messages, status }">
|
|
597
|
+
<div class="connection-status">
|
|
598
|
+
Status: <span :class="status">{{ status }}</span> | Updates:
|
|
599
|
+
{{ messages.length }}
|
|
600
|
+
</div>
|
|
601
|
+
</template>
|
|
602
|
+
</StreamPlayer>
|
|
603
|
+
</div>
|
|
604
|
+
</template>
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
## 🐛 Troubleshooting
|
|
608
|
+
|
|
609
|
+
### Common Issues
|
|
610
|
+
|
|
611
|
+
**Connection fails immediately**
|
|
612
|
+
|
|
613
|
+
```typescript
|
|
614
|
+
// Check if the URL is correct and accessible
|
|
615
|
+
const config = {
|
|
616
|
+
url: "ws://localhost:3000/ws", // http:// for ws://, https:// for wss://
|
|
617
|
+
};
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
**CORS issues with SSE/HTTP**
|
|
621
|
+
|
|
622
|
+
```typescript
|
|
623
|
+
const config = {
|
|
624
|
+
url: "/api/stream",
|
|
625
|
+
headers: {
|
|
626
|
+
"Access-Control-Allow-Origin": "*",
|
|
627
|
+
},
|
|
628
|
+
};
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
**Video not playing (HLS/WebRTC)**
|
|
632
|
+
|
|
633
|
+
```vue
|
|
634
|
+
<StreamPlayer
|
|
635
|
+
type="hls"
|
|
636
|
+
:config="hlsConfig"
|
|
637
|
+
:autoplay="true"
|
|
638
|
+
:muted="true" <!-- Required for autoplay in most browsers -->
|
|
639
|
+
:plays-inline="true"
|
|
640
|
+
/>
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
**Messages not displaying**
|
|
644
|
+
|
|
645
|
+
```javascript
|
|
646
|
+
// Check if logLimit is sufficient
|
|
647
|
+
<StreamPlayer :log-limit="1000" />
|
|
648
|
+
|
|
649
|
+
// Or handle messages manually
|
|
650
|
+
@message="msg => console.log('Received:', msg)"
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
### Browser Compatibility
|
|
654
|
+
|
|
655
|
+
- **WebSocket**: All modern browsers
|
|
656
|
+
- **SSE**: All modern browsers (IE/Edge requires polyfill)
|
|
657
|
+
- **HLS**: Requires HLS.js for non-Safari browsers
|
|
658
|
+
- **WebRTC**: Modern browsers (check specific API support)
|
|
659
|
+
|
|
660
|
+
### Performance Tips
|
|
661
|
+
|
|
662
|
+
1. **Limit message history**: Use reasonable `logLimit` values
|
|
663
|
+
2. **Debounce rapid updates**: Use `shallowRef` for message arrays
|
|
664
|
+
3. **Clean up properly**: Component handles cleanup automatically
|
|
665
|
+
4. **Use object URLs**: For large binary data in WebRTC/HLS
|
|
666
|
+
|
|
667
|
+
## 🤝 Contributing
|
|
668
|
+
|
|
669
|
+
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
670
|
+
|
|
671
|
+
### Development Setup
|
|
672
|
+
|
|
673
|
+
```bash
|
|
674
|
+
git clone https://github.com/yourusername/vue-streaming.git
|
|
675
|
+
cd vue-streaming
|
|
676
|
+
npm install
|
|
677
|
+
npm run dev
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
### Building
|
|
681
|
+
|
|
682
|
+
```bash
|
|
683
|
+
npm run build
|
|
684
|
+
npm run test
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
## 📄 License
|
|
688
|
+
|
|
689
|
+
MIT © Vue Streaming Contributors
|
|
690
|
+
|
|
691
|
+
---
|
|
692
|
+
|
|
693
|
+
<div align="center">
|
|
694
|
+
|
|
695
|
+
**[Documentation](https://github.com/yourusername/vue-streaming#readme) • [Examples](https://github.com/yourusername/vue-streaming/tree/main/examples) • [Issues](https://github.com/yourusername/vue-streaming/issues)**
|
|
696
|
+
|
|
697
|
+
Made with ❤️ for the Vue.js community
|
|
698
|
+
|
|
699
|
+
</div>
|