lightstream-sdk 1.0.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/LICENSE +21 -0
- package/QUICKSTART.md +150 -0
- package/README.md +178 -0
- package/dist/errors.d.ts +26 -0
- package/dist/index.cjs +404 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.mjs +365 -0
- package/dist/rest-client.d.ts +45 -0
- package/dist/src/errors.d.ts +26 -0
- package/dist/src/index.d.ts +13 -0
- package/dist/src/rest-client.d.ts +45 -0
- package/dist/src/stream-client.d.ts +26 -0
- package/dist/src/types.d.ts +86 -0
- package/dist/stream-client.d.ts +26 -0
- package/dist/types.d.ts +86 -0
- package/examples/basic-connection.ts +15 -0
- package/examples/bot-moderation.ts +27 -0
- package/examples/earnings-tracker.ts +19 -0
- package/examples/react-use-stream.tsx +48 -0
- package/package.json +53 -0
- package/src/errors.ts +49 -0
- package/src/index.ts +29 -0
- package/src/rest-client.ts +143 -0
- package/src/stream-client.ts +250 -0
- package/src/types.ts +102 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LightStream
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/QUICKSTART.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# LightStream SDK Quickstart Guide
|
|
2
|
+
|
|
3
|
+
This guide walks you through setting up and running your first LightStream integration in 5 minutes.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
1. Node.js 18+, Bun, or Deno installed.
|
|
10
|
+
2. A LightStream API Key.
|
|
11
|
+
3. LightStream Gateway Base URL: `https://gateway.lightstream.lat`
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. Project Setup
|
|
16
|
+
|
|
17
|
+
Create a new directory and install dependencies:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
mkdir my-lightstream-bot
|
|
21
|
+
cd my-lightstream-bot
|
|
22
|
+
npm init -y
|
|
23
|
+
npm install lightstream-sdk
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 2. Hello World: Realtime Chat Listener
|
|
29
|
+
|
|
30
|
+
Create `bot.js` (or `bot.ts`):
|
|
31
|
+
|
|
32
|
+
```javascript
|
|
33
|
+
import { createLightStreamClient } from 'lightstream-sdk';
|
|
34
|
+
|
|
35
|
+
const client = createLightStreamClient({
|
|
36
|
+
baseUrl: 'https://gateway.lightstream.lat',
|
|
37
|
+
wsUrl: 'wss://gateway.lightstream.lat/ws',
|
|
38
|
+
apiKey: process.env.LIGHTSTREAM_API_KEY || 'YOUR_API_KEY'
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
client.stream.on('status', (status) => {
|
|
42
|
+
console.log(`[Status] ${status}`);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
client.stream.on('connected', (evt) => {
|
|
46
|
+
console.log(`[Connected] Live on ${evt.platform} - ${evt.channel} (${evt.viewerCount} viewers)`);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
client.stream.on('stream:offline', (evt) => {
|
|
50
|
+
console.log(`[Offline] Channel ${evt.channel} is not streaming right now.`);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
client.stream.on('chat', (msg) => {
|
|
54
|
+
console.log(`💬 ${msg.user.displayName}: ${msg.content}`);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
client.stream.on('gift', (gift) => {
|
|
58
|
+
console.log(`🎁 ${gift.user.displayName} sent ${gift.gift.name} x${gift.gift.repeatCount}`);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Connect to TikTok, Kick, or Twitch
|
|
62
|
+
client.stream.connect('kick', 'xqc');
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Run the bot:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
node bot.js
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 3. Frontend Integration (Next.js / React)
|
|
74
|
+
|
|
75
|
+
When building client-facing web applications or OBS overlays, **never** bundle your master API key in the client!
|
|
76
|
+
|
|
77
|
+
### Step A: Backend API Route (`/api/get-stream-token`)
|
|
78
|
+
```typescript
|
|
79
|
+
import { createLightStreamClient } from 'lightstream-sdk'
|
|
80
|
+
|
|
81
|
+
const serverClient = createLightStreamClient({
|
|
82
|
+
baseUrl: 'https://gateway.lightstream.lat',
|
|
83
|
+
apiKey: process.env.LIGHTSTREAM_MASTER_KEY!
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
export async function POST(req: Request) {
|
|
87
|
+
const { channel, platform } = await req.json()
|
|
88
|
+
|
|
89
|
+
// Issue a 5-minute scoped token for this viewer/channel only
|
|
90
|
+
const { token, expiresAt } = await serverClient.rest.issueToken({
|
|
91
|
+
allowedPlatforms: [platform],
|
|
92
|
+
allowedChannels: [channel],
|
|
93
|
+
expireAfterSeconds: 300,
|
|
94
|
+
maxWebSockets: 1
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
return Response.json({ token, expiresAt })
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Step B: React Component
|
|
102
|
+
```tsx
|
|
103
|
+
'use client'
|
|
104
|
+
import { useEffect, useState } from 'react'
|
|
105
|
+
import { createLightStreamClient, type ConnectionStatus } from 'lightstream-sdk'
|
|
106
|
+
|
|
107
|
+
export function LiveChatWidget({ platform, channel }) {
|
|
108
|
+
const [messages, setMessages] = useState<string[]>([])
|
|
109
|
+
const [status, setStatus] = useState<ConnectionStatus>('idle')
|
|
110
|
+
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
let client: any
|
|
113
|
+
|
|
114
|
+
async function init() {
|
|
115
|
+
// 1. Fetch ephemeral token from our secure server route
|
|
116
|
+
const res = await fetch('/api/get-stream-token', {
|
|
117
|
+
method: 'POST',
|
|
118
|
+
headers: { 'Content-Type': 'application/json' },
|
|
119
|
+
body: JSON.stringify({ platform, channel })
|
|
120
|
+
})
|
|
121
|
+
const { token } = await res.json()
|
|
122
|
+
|
|
123
|
+
// 2. Connect client with ephemeral token
|
|
124
|
+
client = createLightStreamClient({ token })
|
|
125
|
+
|
|
126
|
+
client.stream.on('status', setStatus)
|
|
127
|
+
client.stream.on('chat', (msg) => {
|
|
128
|
+
setMessages((prev) => [...prev.slice(-49), `${msg.user.displayName}: ${msg.content}`])
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
await client.stream.connect(platform, channel)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
init()
|
|
135
|
+
|
|
136
|
+
return () => {
|
|
137
|
+
client?.stream.disconnect()
|
|
138
|
+
}
|
|
139
|
+
}, [platform, channel])
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<div className="chat-container">
|
|
143
|
+
<div className="badge">Status: {status}</div>
|
|
144
|
+
<ul>
|
|
145
|
+
{messages.map((m, i) => <li key={i}>{m}</li>)}
|
|
146
|
+
</ul>
|
|
147
|
+
</div>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
```
|
package/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# lightstream-sdk
|
|
2
|
+
|
|
3
|
+
Official TypeScript & JavaScript SDK for the **LightStream Developer Platform**.
|
|
4
|
+
|
|
5
|
+
Interact with **TikTok LIVE**, **Kick**, and **Twitch** using a single, unified client. LightStream provides real-time WebSockets, verified stream lifecycle management, rich metadata APIs, leaderboards, and unified cross-platform chat moderation.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## ✨ Features
|
|
10
|
+
|
|
11
|
+
- **🌐 Unified Real-time WebSockets**: Connect to TikTok, Kick, and Twitch with one common interface and protocol.
|
|
12
|
+
- **🛡️ Zero False Positives**: Connection state is strictly backed by upstream verification. The client emits `stream:offline` with `STREAMER_OFFLINE` when the channel is not live, guaranteeing that `connected` only fires when live data is flowing.
|
|
13
|
+
- **⚡ Fast REST APIs**:
|
|
14
|
+
- **Bulk Live Check**: Check live status and viewer counts for up to 100 streamers in a single call.
|
|
15
|
+
- **Playback & Stream Info**: Retrieve HLS (`.m3u8`) playback streams and stream titles.
|
|
16
|
+
- **Monetization & Earnings**: Track gifts, bits, kicks, and estimated USD earnings.
|
|
17
|
+
- **Rankings & Leaderboards**: Query hourly, daily, and weekly creator leaderboards.
|
|
18
|
+
- **Cross-Platform Moderation**: Mute, ban, delete chat messages, and toggle comments across platforms.
|
|
19
|
+
- **🔑 Scoped Ephemeral Tokens**: Generate secure, short-lived JWTs to embed directly in frontend applications or widgets without leaking master API keys.
|
|
20
|
+
- **🔄 Resilient State Machine**: Built-in heartbeat (`ping`/`pong`), auto-reconnection with exponential backoff, and strict lifecycle states.
|
|
21
|
+
- **📦 Isomorphic**: Works out of the box in Node.js, Bun, Deno, and modern web browsers.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🚀 Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Using npm
|
|
29
|
+
npm install lightstream-sdk
|
|
30
|
+
|
|
31
|
+
# Using pnpm
|
|
32
|
+
pnpm add lightstream-sdk
|
|
33
|
+
|
|
34
|
+
# Using bun
|
|
35
|
+
bun add lightstream-sdk
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
*(If consuming as a local package, you can also link it via `"file:../lightstream-sdk"`)*
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## ⚡ Quick Start
|
|
43
|
+
|
|
44
|
+
### 1. Connecting to a Live Stream (WebSocket)
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { createLightStreamClient } from 'lightstream-sdk'
|
|
48
|
+
|
|
49
|
+
const client = createLightStreamClient({
|
|
50
|
+
baseUrl: 'https://gateway.lightstream.lat',
|
|
51
|
+
wsUrl: 'wss://gateway.lightstream.lat/ws',
|
|
52
|
+
apiKey: 'your_api_key_here' // Or use ephemeral token: token: 'eyJ...'
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
// Track connection state machine
|
|
56
|
+
client.stream.on('status', (status) => {
|
|
57
|
+
console.log('Connection status:', status)
|
|
58
|
+
// 'idle' | 'connecting' | 'connected' | 'offline' | 'reconnecting' | 'disconnected' | 'error'
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
// Guaranteed: Only fires when the channel is 100% verified LIVE
|
|
62
|
+
client.stream.on('connected', (event) => {
|
|
63
|
+
console.log(`✅ Connected to ${event.platform} channel: ${event.channel}`)
|
|
64
|
+
console.log(`Viewers: ${event.viewerCount}, Title: ${event.title}`)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
// Streamer is not streaming (Zero false positives)
|
|
68
|
+
client.stream.on('stream:offline', (event) => {
|
|
69
|
+
console.warn(`⚠️ Channel ${event.channel} is currently offline. Code: ${event.code}`)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
// Real-time Chat
|
|
73
|
+
client.stream.on('chat', (msg) => {
|
|
74
|
+
console.log(`[${msg.platform}] ${msg.user.displayName}: ${msg.content}`)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
// Real-time Gifts / Monetization
|
|
78
|
+
client.stream.on('gift', (gift) => {
|
|
79
|
+
console.log(`🎁 ${gift.user.displayName} sent ${gift.gift.name} x${gift.gift.repeatCount} (${gift.gift.value} diamonds/coins)`)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// Handle errors
|
|
83
|
+
client.stream.on('error', (err) => {
|
|
84
|
+
console.error(`❌ Error [${err.code}]: ${err.message}`)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
// Connect to a channel
|
|
88
|
+
await client.stream.connect('twitch', 'ninja')
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 🛠️ REST API Usage
|
|
94
|
+
|
|
95
|
+
### Bulk Check Live Status
|
|
96
|
+
|
|
97
|
+
Check the status of multiple streamers simultaneously across different platforms:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
const results = await client.rest.bulkCheckStreams([
|
|
101
|
+
{ platform: 'tiktok', channel: 'charlidamelio' },
|
|
102
|
+
{ platform: 'kick', channel: 'xqc' },
|
|
103
|
+
{ platform: 'twitch', channel: 'shroud' },
|
|
104
|
+
])
|
|
105
|
+
|
|
106
|
+
for (const stream of results) {
|
|
107
|
+
console.log(`${stream.platform}/@${stream.channel}: ${stream.isLive ? '🔴 LIVE' : '⚪ OFFLINE'} (${stream.viewerCount} viewers)`)
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Issue Scoped Ephemeral Tokens (for Frontend / Overlays)
|
|
112
|
+
|
|
113
|
+
Do not expose your master API key in client-side code! Issue a short-lived token instead:
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
const { token, expiresAt } = await client.rest.issueToken({
|
|
117
|
+
allowedPlatforms: ['kick', 'twitch'],
|
|
118
|
+
allowedChannels: ['my_channel'],
|
|
119
|
+
expireAfterSeconds: 300, // 5 minutes
|
|
120
|
+
maxWebSockets: 2,
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
// The frontend can now safely connect:
|
|
124
|
+
// const client = createLightStreamClient({ token })
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Unified Chat Moderation
|
|
128
|
+
|
|
129
|
+
Perform moderation actions regardless of the platform:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// Mute user for 60 seconds
|
|
133
|
+
await client.rest.moderation.mute('tiktok', 'my_channel', 'spammer_username', 60)
|
|
134
|
+
|
|
135
|
+
// Ban user
|
|
136
|
+
await client.rest.moderation.ban('kick', 'my_channel', 'bad_actor', 'Violated rules')
|
|
137
|
+
|
|
138
|
+
// Delete a message
|
|
139
|
+
await client.rest.moderation.deleteMessage('twitch', 'my_channel', 'msg-uuid-123')
|
|
140
|
+
|
|
141
|
+
// Disable chat comments
|
|
142
|
+
await client.rest.moderation.toggleComments('tiktok', 'my_channel', false)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Rankings & Leaderboards
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// Top streamers on TikTok today
|
|
149
|
+
const topDaily = await client.rest.getLeaderboard('tiktok', 'daily', 20)
|
|
150
|
+
console.log('Top Streamer:', topDaily[0])
|
|
151
|
+
|
|
152
|
+
// Search streamer ranking
|
|
153
|
+
const rank = await client.rest.searchRankings('my_streamer', 'tiktok', 'daily')
|
|
154
|
+
console.log(`Position: #${rank.rank} with ${rank.score} points`)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 📖 Canonical Error Codes
|
|
160
|
+
|
|
161
|
+
The SDK and Gateway use canonical, strongly typed error codes:
|
|
162
|
+
|
|
163
|
+
| Code | Meaning | Action / Handling |
|
|
164
|
+
| :--- | :--- | :--- |
|
|
165
|
+
| `STREAMER_OFFLINE` | The channel exists but is not broadcasting live. | Display offline banner in UI. Do not expect stream data. |
|
|
166
|
+
| `ROOM_NOT_FOUND` | The requested username/channel was not found. | Check spelling or channel username. |
|
|
167
|
+
| `AUTHENTICATION_FAILED` | Invalid API key or expired token. | Refresh credentials. |
|
|
168
|
+
| `FORBIDDEN_CHANNEL` | The ephemeral token is not authorized for this channel. | Generate a token with correct `allowedChannels`. |
|
|
169
|
+
| `RATE_LIMITED` | Too many requests or concurrent connections. | Backoff and retry later. |
|
|
170
|
+
| `UPSTREAM_ERROR` | Error from TikTok, Kick, or Twitch API. | Client will auto-reconnect if retryable. |
|
|
171
|
+
| `TIMEOUT` | Connection attempt timed out. | Retry with exponential backoff. |
|
|
172
|
+
| `INTERNAL_ERROR` | Unhandled gateway server error. | Contact support or check gateway status. |
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## 📄 License
|
|
177
|
+
|
|
178
|
+
MIT © 2026 LightStream
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare const LIGHTSTREAM_ERROR_CODES: {
|
|
2
|
+
readonly STREAMER_OFFLINE: "STREAMER_OFFLINE";
|
|
3
|
+
readonly ROOM_NOT_FOUND: "ROOM_NOT_FOUND";
|
|
4
|
+
readonly AUTHENTICATION_FAILED: "AUTHENTICATION_FAILED";
|
|
5
|
+
readonly FORBIDDEN_CHANNEL: "FORBIDDEN_CHANNEL";
|
|
6
|
+
readonly RATE_LIMITED: "RATE_LIMITED";
|
|
7
|
+
readonly UPSTREAM_ERROR: "UPSTREAM_ERROR";
|
|
8
|
+
readonly TIMEOUT: "TIMEOUT";
|
|
9
|
+
readonly INTERNAL_ERROR: "INTERNAL_ERROR";
|
|
10
|
+
};
|
|
11
|
+
export type LightStreamErrorCode = keyof typeof LIGHTSTREAM_ERROR_CODES;
|
|
12
|
+
export interface LightStreamErrorPayload {
|
|
13
|
+
code: LightStreamErrorCode;
|
|
14
|
+
message: string;
|
|
15
|
+
details?: unknown;
|
|
16
|
+
retryable?: boolean;
|
|
17
|
+
timestamp: string;
|
|
18
|
+
}
|
|
19
|
+
export declare class LightStreamError extends Error {
|
|
20
|
+
readonly code: LightStreamErrorCode;
|
|
21
|
+
readonly details?: unknown;
|
|
22
|
+
readonly retryable: boolean;
|
|
23
|
+
readonly timestamp: string;
|
|
24
|
+
constructor(payload: LightStreamErrorPayload);
|
|
25
|
+
toJSON(): LightStreamErrorPayload;
|
|
26
|
+
}
|