@gravito/radiance 1.0.0-alpha.2
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 +247 -0
- package/dist/index.js +286 -0
- package/dist/index.js.map +16 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# @gravito/radiance
|
|
2
|
+
|
|
3
|
+
輕量、高效的廣播系統,支援多種驅動(Pusher、Ably、Redis、WebSocket)。借鑑 Laravel 架構但保持 Gravito 的核心價值(高效能、低耗、輕量、AI 友善)。
|
|
4
|
+
|
|
5
|
+
> **狀態**:v0.1.0 - 核心功能已完成,支援多種廣播驅動
|
|
6
|
+
|
|
7
|
+
## 特性
|
|
8
|
+
|
|
9
|
+
- **零運行時開銷**:純類型包裝,直接委派給驅動
|
|
10
|
+
- **多驅動支援**:Pusher、Ably、Redis、WebSocket
|
|
11
|
+
- **完全模組化**:按需安裝驅動,核心包極小
|
|
12
|
+
- **與 Events 整合**:事件可實作 `ShouldBroadcast` 自動廣播
|
|
13
|
+
- **頻道授權**:支援私有頻道和存在頻道授權
|
|
14
|
+
- **AI 友善**:完整的型別推導、清晰的 JSDoc、直觀的 API
|
|
15
|
+
|
|
16
|
+
## 安裝
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
bun add @gravito/radiance
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 快速開始
|
|
23
|
+
|
|
24
|
+
### 1. 配置 OrbitRadiance
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { PlanetCore } from 'gravito-core'
|
|
28
|
+
import { OrbitRadiance } from '@gravito/radiance'
|
|
29
|
+
|
|
30
|
+
const core = await PlanetCore.boot({
|
|
31
|
+
orbits: [
|
|
32
|
+
OrbitRadiance.configure({
|
|
33
|
+
driver: 'pusher',
|
|
34
|
+
config: {
|
|
35
|
+
appId: 'your-app-id',
|
|
36
|
+
key: 'your-key',
|
|
37
|
+
secret: 'your-secret',
|
|
38
|
+
cluster: 'mt1',
|
|
39
|
+
},
|
|
40
|
+
authorizeChannel: async (channel, socketId, userId) => {
|
|
41
|
+
// 實作頻道授權邏輯
|
|
42
|
+
return true
|
|
43
|
+
},
|
|
44
|
+
}),
|
|
45
|
+
],
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 2. 創建可廣播事件
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { Event, ShouldBroadcast } from 'gravito-core'
|
|
53
|
+
import { PrivateChannel } from '@gravito/radiance'
|
|
54
|
+
|
|
55
|
+
class OrderShipped extends Event implements ShouldBroadcast {
|
|
56
|
+
constructor(public order: Order) {
|
|
57
|
+
super()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
broadcastOn(): PrivateChannel {
|
|
61
|
+
return new PrivateChannel(`user.${this.order.userId}`)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
broadcastWith(): Record<string, unknown> {
|
|
65
|
+
return {
|
|
66
|
+
order_id: this.order.id,
|
|
67
|
+
status: 'shipped',
|
|
68
|
+
tracking_number: this.order.trackingNumber,
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
broadcastAs(): string {
|
|
73
|
+
return 'OrderShipped'
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 3. 分發事件(自動廣播)
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
// 分發事件時會自動廣播
|
|
82
|
+
await core.events.dispatch(new OrderShipped(order))
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 4. 手動廣播
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
const broadcast = c.get('broadcast') as BroadcastManager
|
|
89
|
+
|
|
90
|
+
await broadcast.broadcast(
|
|
91
|
+
event,
|
|
92
|
+
{ name: 'user.123', type: 'private' },
|
|
93
|
+
{ message: 'Hello' },
|
|
94
|
+
'CustomEvent'
|
|
95
|
+
)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## 驅動
|
|
99
|
+
|
|
100
|
+
### Pusher
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
OrbitRadiance.configure({
|
|
104
|
+
driver: 'pusher',
|
|
105
|
+
config: {
|
|
106
|
+
appId: 'your-app-id',
|
|
107
|
+
key: 'your-key',
|
|
108
|
+
secret: 'your-secret',
|
|
109
|
+
cluster: 'mt1', // 可選
|
|
110
|
+
useTLS: true, // 可選
|
|
111
|
+
},
|
|
112
|
+
})
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Ably
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
OrbitRadiance.configure({
|
|
119
|
+
driver: 'ably',
|
|
120
|
+
config: {
|
|
121
|
+
apiKey: 'your-api-key',
|
|
122
|
+
},
|
|
123
|
+
})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Redis
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import { RedisDriver } from '@gravito/radiance'
|
|
130
|
+
|
|
131
|
+
// 需要先設置 Redis 客戶端
|
|
132
|
+
const redisDriver = new RedisDriver({
|
|
133
|
+
url: 'redis://localhost:6379',
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
// 設置 Redis 客戶端
|
|
137
|
+
redisDriver.setRedisClient(redisClient)
|
|
138
|
+
|
|
139
|
+
OrbitRadiance.configure({
|
|
140
|
+
driver: 'redis',
|
|
141
|
+
config: {
|
|
142
|
+
url: 'redis://localhost:6379',
|
|
143
|
+
keyPrefix: 'gravito:broadcast:', // 可選
|
|
144
|
+
},
|
|
145
|
+
})
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### WebSocket
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
OrbitRadiance.configure({
|
|
152
|
+
driver: 'websocket',
|
|
153
|
+
config: {
|
|
154
|
+
getConnections: () => {
|
|
155
|
+
// 返回所有 WebSocket 連接
|
|
156
|
+
return Array.from(websocketConnections.values())
|
|
157
|
+
},
|
|
158
|
+
filterConnectionsByChannel: (channel) => {
|
|
159
|
+
// 根據頻道過濾連接(可選)
|
|
160
|
+
return Array.from(websocketConnections.values()).filter(
|
|
161
|
+
(conn) => conn.subscribedChannels.includes(channel)
|
|
162
|
+
)
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
})
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## 頻道類型
|
|
169
|
+
|
|
170
|
+
### 公開頻道
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
import { PublicChannel } from '@gravito/radiance'
|
|
174
|
+
|
|
175
|
+
class PublicEvent extends Event implements ShouldBroadcast {
|
|
176
|
+
broadcastOn(): PublicChannel {
|
|
177
|
+
return new PublicChannel('public-channel')
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### 私有頻道
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
import { PrivateChannel } from '@gravito/radiance'
|
|
186
|
+
|
|
187
|
+
class PrivateEvent extends Event implements ShouldBroadcast {
|
|
188
|
+
broadcastOn(): PrivateChannel {
|
|
189
|
+
return new PrivateChannel(`user.${this.userId}`)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### 存在頻道
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
import { PresenceChannel } from '@gravito/radiance'
|
|
198
|
+
|
|
199
|
+
class PresenceEvent extends Event implements ShouldBroadcast {
|
|
200
|
+
broadcastOn(): PresenceChannel {
|
|
201
|
+
return new PresenceChannel('presence-room')
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## 頻道授權
|
|
207
|
+
|
|
208
|
+
私有頻道和存在頻道需要授權。在配置中提供 `authorizeChannel` 回調:
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
OrbitRadiance.configure({
|
|
212
|
+
driver: 'pusher',
|
|
213
|
+
config: { /* ... */ },
|
|
214
|
+
authorizeChannel: async (channel, socketId, userId) => {
|
|
215
|
+
// 檢查使用者是否有權限存取此頻道
|
|
216
|
+
if (channel.startsWith('private-user.')) {
|
|
217
|
+
const channelUserId = channel.replace('private-user.', '')
|
|
218
|
+
return userId?.toString() === channelUserId
|
|
219
|
+
}
|
|
220
|
+
return false
|
|
221
|
+
},
|
|
222
|
+
})
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## API 參考
|
|
226
|
+
|
|
227
|
+
### BroadcastManager
|
|
228
|
+
|
|
229
|
+
#### 方法
|
|
230
|
+
|
|
231
|
+
- `broadcast(event, channel, data, eventName): Promise<void>` - 廣播事件
|
|
232
|
+
- `authorizeChannel(channel, socketId, userId): Promise<{ auth, channel_data? } | null>` - 授權頻道存取
|
|
233
|
+
- `setDriver(driver: BroadcastDriver): void` - 設置廣播驅動
|
|
234
|
+
- `setAuthCallback(callback: ChannelAuthorizationCallback): void` - 設置授權回調
|
|
235
|
+
|
|
236
|
+
### ShouldBroadcast
|
|
237
|
+
|
|
238
|
+
事件實作此介面可自動廣播:
|
|
239
|
+
|
|
240
|
+
- `broadcastOn(): string | Channel` - 指定廣播頻道(必須實作)
|
|
241
|
+
- `broadcastWith?(): Record<string, unknown>` - 指定廣播資料(可選)
|
|
242
|
+
- `broadcastAs?(): string` - 指定廣播事件名稱(可選)
|
|
243
|
+
|
|
244
|
+
## 授權
|
|
245
|
+
|
|
246
|
+
MIT © Carl Lee
|
|
247
|
+
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
// src/BroadcastManager.ts
|
|
2
|
+
class BroadcastManager {
|
|
3
|
+
core;
|
|
4
|
+
driver = null;
|
|
5
|
+
authCallback;
|
|
6
|
+
constructor(core) {
|
|
7
|
+
this.core = core;
|
|
8
|
+
}
|
|
9
|
+
setDriver(driver) {
|
|
10
|
+
this.driver = driver;
|
|
11
|
+
}
|
|
12
|
+
setAuthCallback(callback) {
|
|
13
|
+
this.authCallback = callback;
|
|
14
|
+
}
|
|
15
|
+
async broadcast(_event, channel, data, eventName) {
|
|
16
|
+
if (!this.driver) {
|
|
17
|
+
this.core.logger.warn("[BroadcastManager] No broadcast driver set, skipping broadcast");
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
await this.driver.broadcast(channel, eventName, data);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
this.core.logger.error(`[BroadcastManager] Failed to broadcast event ${eventName}:`, error);
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async authorizeChannel(channel, socketId, userId) {
|
|
28
|
+
if (this.authCallback) {
|
|
29
|
+
const authorized = await this.authCallback(channel, socketId, userId);
|
|
30
|
+
if (!authorized) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (this.driver?.authorizeChannel) {
|
|
35
|
+
return await this.driver.authorizeChannel(channel, socketId, userId);
|
|
36
|
+
}
|
|
37
|
+
if (channel.startsWith("private-") || channel.startsWith("presence-")) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return { auth: "" };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// src/channels/Channel.ts
|
|
44
|
+
class PublicChannel {
|
|
45
|
+
name;
|
|
46
|
+
type = "public";
|
|
47
|
+
constructor(name) {
|
|
48
|
+
this.name = name;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
class PrivateChannel {
|
|
53
|
+
name;
|
|
54
|
+
type = "private";
|
|
55
|
+
constructor(name) {
|
|
56
|
+
this.name = name;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
class PresenceChannel {
|
|
61
|
+
name;
|
|
62
|
+
type = "presence";
|
|
63
|
+
constructor(name) {
|
|
64
|
+
this.name = name;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// src/drivers/AblyDriver.ts
|
|
68
|
+
class AblyDriver {
|
|
69
|
+
config;
|
|
70
|
+
baseUrl = "https://rest.ably.io";
|
|
71
|
+
constructor(config) {
|
|
72
|
+
this.config = config;
|
|
73
|
+
}
|
|
74
|
+
async broadcast(channel, event, data) {
|
|
75
|
+
const path = `/channels/${channel.name}/messages`;
|
|
76
|
+
const auth = btoa(this.config.apiKey);
|
|
77
|
+
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
78
|
+
method: "POST",
|
|
79
|
+
headers: {
|
|
80
|
+
Authorization: `Basic ${auth}`,
|
|
81
|
+
"Content-Type": "application/json"
|
|
82
|
+
},
|
|
83
|
+
body: JSON.stringify({
|
|
84
|
+
name: event,
|
|
85
|
+
data
|
|
86
|
+
})
|
|
87
|
+
});
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
const error = await response.text();
|
|
90
|
+
throw new Error(`Failed to broadcast via Ably: ${error}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async authorizeChannel(channel, _socketId, userId) {
|
|
94
|
+
return {
|
|
95
|
+
auth: this.config.apiKey,
|
|
96
|
+
...channel.startsWith("presence-") && userId ? {
|
|
97
|
+
channel_data: JSON.stringify({
|
|
98
|
+
clientId: userId.toString()
|
|
99
|
+
})
|
|
100
|
+
} : {}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// src/drivers/PusherDriver.ts
|
|
105
|
+
class PusherDriver {
|
|
106
|
+
config;
|
|
107
|
+
baseUrl;
|
|
108
|
+
constructor(config) {
|
|
109
|
+
this.config = config;
|
|
110
|
+
const cluster = this.config.cluster || "mt1";
|
|
111
|
+
this.baseUrl = `https://api-${cluster}.pusher.com`;
|
|
112
|
+
}
|
|
113
|
+
async broadcast(channel, event, data) {
|
|
114
|
+
const path = `/apps/${this.config.appId}/events`;
|
|
115
|
+
const body = {
|
|
116
|
+
name: event,
|
|
117
|
+
channel: channel.name,
|
|
118
|
+
data: JSON.stringify(data)
|
|
119
|
+
};
|
|
120
|
+
const timestamp = Math.floor(Date.now() / 1000);
|
|
121
|
+
const queryString = new URLSearchParams({
|
|
122
|
+
auth_key: this.config.key,
|
|
123
|
+
auth_timestamp: timestamp.toString(),
|
|
124
|
+
auth_version: "1.0",
|
|
125
|
+
body_md5: this.md5(JSON.stringify(body))
|
|
126
|
+
});
|
|
127
|
+
const authString = `POST
|
|
128
|
+
${path}
|
|
129
|
+
${queryString.toString()}`;
|
|
130
|
+
const authSignature = await this.hmacSHA256(authString, this.config.secret);
|
|
131
|
+
queryString.append("auth_signature", authSignature);
|
|
132
|
+
const response = await fetch(`${this.baseUrl}${path}?${queryString.toString()}`, {
|
|
133
|
+
method: "POST",
|
|
134
|
+
headers: {
|
|
135
|
+
"Content-Type": "application/json"
|
|
136
|
+
},
|
|
137
|
+
body: JSON.stringify(body)
|
|
138
|
+
});
|
|
139
|
+
if (!response.ok) {
|
|
140
|
+
const error = await response.text();
|
|
141
|
+
throw new Error(`Failed to broadcast via Pusher: ${error}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async authorizeChannel(channel, socketId, userId) {
|
|
145
|
+
const stringToSign = `${socketId}:${channel}`;
|
|
146
|
+
const signature = await this.hmacSHA256(stringToSign, this.config.secret);
|
|
147
|
+
if (channel.startsWith("presence-")) {
|
|
148
|
+
const channelData = JSON.stringify({
|
|
149
|
+
user_id: userId?.toString(),
|
|
150
|
+
user_info: {}
|
|
151
|
+
});
|
|
152
|
+
return {
|
|
153
|
+
auth: `${this.config.key}:${signature}`,
|
|
154
|
+
channel_data: channelData
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
auth: `${this.config.key}:${signature}`
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
async hmacSHA256(message, secret) {
|
|
162
|
+
const encoder = new TextEncoder;
|
|
163
|
+
const keyData = encoder.encode(secret);
|
|
164
|
+
const messageData = encoder.encode(message);
|
|
165
|
+
const key = await crypto.subtle.importKey("raw", keyData, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
166
|
+
const signature = await crypto.subtle.sign("HMAC", key, messageData);
|
|
167
|
+
const hashArray = Array.from(new Uint8Array(signature));
|
|
168
|
+
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
169
|
+
}
|
|
170
|
+
md5(text) {
|
|
171
|
+
return btoa(text).replace(/[+/=]/g, "").substring(0, 32);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// src/drivers/RedisDriver.ts
|
|
175
|
+
class RedisDriver {
|
|
176
|
+
config;
|
|
177
|
+
redis = null;
|
|
178
|
+
constructor(config) {
|
|
179
|
+
this.config = config;
|
|
180
|
+
}
|
|
181
|
+
setRedisClient(client) {
|
|
182
|
+
this.redis = client;
|
|
183
|
+
}
|
|
184
|
+
async broadcast(channel, event, data) {
|
|
185
|
+
if (!this.redis) {
|
|
186
|
+
throw new Error("Redis client not set. Please install a Redis client and call setRedisClient()");
|
|
187
|
+
}
|
|
188
|
+
const prefix = this.config.keyPrefix || "gravito:broadcast:";
|
|
189
|
+
const channelName = `${prefix}${channel.name}`;
|
|
190
|
+
const message = JSON.stringify({
|
|
191
|
+
event,
|
|
192
|
+
data,
|
|
193
|
+
channel: channel.name,
|
|
194
|
+
type: channel.type
|
|
195
|
+
});
|
|
196
|
+
await this.redis.publish(channelName, message);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// src/drivers/WebSocketDriver.ts
|
|
200
|
+
class WebSocketDriver {
|
|
201
|
+
config;
|
|
202
|
+
constructor(config) {
|
|
203
|
+
this.config = config;
|
|
204
|
+
}
|
|
205
|
+
async broadcast(channel, event, data) {
|
|
206
|
+
const message = JSON.stringify({
|
|
207
|
+
channel: channel.name,
|
|
208
|
+
event,
|
|
209
|
+
data
|
|
210
|
+
});
|
|
211
|
+
let connections = this.config.getConnections();
|
|
212
|
+
if (this.config.filterConnectionsByChannel) {
|
|
213
|
+
connections = this.config.filterConnectionsByChannel(channel.name);
|
|
214
|
+
}
|
|
215
|
+
for (const connection of connections) {
|
|
216
|
+
if (connection.readyState === 1) {
|
|
217
|
+
try {
|
|
218
|
+
connection.send(message);
|
|
219
|
+
} catch (error) {
|
|
220
|
+
console.error("Failed to send WebSocket message:", error);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// src/OrbitRadiance.ts
|
|
227
|
+
class OrbitRadiance {
|
|
228
|
+
options;
|
|
229
|
+
constructor(options) {
|
|
230
|
+
this.options = options;
|
|
231
|
+
}
|
|
232
|
+
static configure(options) {
|
|
233
|
+
return new OrbitRadiance(options);
|
|
234
|
+
}
|
|
235
|
+
async install(core) {
|
|
236
|
+
const manager = new BroadcastManager(core);
|
|
237
|
+
let driver;
|
|
238
|
+
switch (this.options.driver) {
|
|
239
|
+
case "pusher":
|
|
240
|
+
driver = new PusherDriver(this.options.config);
|
|
241
|
+
break;
|
|
242
|
+
case "ably":
|
|
243
|
+
driver = new AblyDriver(this.options.config);
|
|
244
|
+
break;
|
|
245
|
+
case "redis": {
|
|
246
|
+
driver = new RedisDriver(this.options.config);
|
|
247
|
+
const redisClient = core.services.get("redis");
|
|
248
|
+
if (redisClient) {
|
|
249
|
+
driver.setRedisClient(redisClient);
|
|
250
|
+
}
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
case "websocket":
|
|
254
|
+
driver = new WebSocketDriver(this.options.config);
|
|
255
|
+
break;
|
|
256
|
+
default:
|
|
257
|
+
throw new Error(`Unsupported broadcast driver: ${this.options.driver}`);
|
|
258
|
+
}
|
|
259
|
+
manager.setDriver(driver);
|
|
260
|
+
if (this.options.authorizeChannel) {
|
|
261
|
+
manager.setAuthCallback(this.options.authorizeChannel);
|
|
262
|
+
}
|
|
263
|
+
core.services.set("broadcast", manager);
|
|
264
|
+
if (core.events) {
|
|
265
|
+
core.events.setBroadcastManager({
|
|
266
|
+
broadcast: async (event, channel, data, eventName) => {
|
|
267
|
+
await manager.broadcast(event, channel, data, eventName);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
core.logger.info(`[OrbitRadiance] Installed with ${this.options.driver} driver`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
export {
|
|
275
|
+
WebSocketDriver,
|
|
276
|
+
RedisDriver,
|
|
277
|
+
PusherDriver,
|
|
278
|
+
PublicChannel,
|
|
279
|
+
PrivateChannel,
|
|
280
|
+
PresenceChannel,
|
|
281
|
+
OrbitRadiance,
|
|
282
|
+
BroadcastManager,
|
|
283
|
+
AblyDriver
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
//# debugId=01B589AE84540A2064756E2164756E21
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/BroadcastManager.ts", "../src/channels/Channel.ts", "../src/drivers/AblyDriver.ts", "../src/drivers/PusherDriver.ts", "../src/drivers/RedisDriver.ts", "../src/drivers/WebSocketDriver.ts", "../src/OrbitRadiance.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { PlanetCore } from 'gravito-core'\nimport type { BroadcastDriver } from './drivers/BroadcastDriver'\n\n/**\n * Channel authorization callback.\n */\nexport type ChannelAuthorizationCallback = (\n channel: string,\n socketId: string,\n userId?: string | number\n) => Promise<boolean>\n\n/**\n * Broadcast manager.\n *\n * Responsible for managing the broadcast driver and handling broadcast requests.\n */\nexport class BroadcastManager {\n private driver: BroadcastDriver | null = null\n private authCallback?: ChannelAuthorizationCallback\n\n constructor(private core: PlanetCore) {}\n\n /**\n * Set the broadcast driver.\n */\n setDriver(driver: BroadcastDriver): void {\n this.driver = driver\n }\n\n /**\n * Set the channel authorization callback.\n */\n setAuthCallback(callback: ChannelAuthorizationCallback): void {\n this.authCallback = callback\n }\n\n /**\n * Broadcast an event.\n *\n * @param event - Event instance\n * @param channel - Channel object\n * @param data - Event payload\n * @param eventName - Event name\n */\n async broadcast(\n _event: unknown,\n channel: { name: string; type: string },\n data: Record<string, unknown>,\n eventName: string\n ): Promise<void> {\n if (!this.driver) {\n this.core.logger.warn('[BroadcastManager] No broadcast driver set, skipping broadcast')\n return\n }\n\n try {\n await this.driver.broadcast(channel, eventName, data)\n } catch (error) {\n this.core.logger.error(`[BroadcastManager] Failed to broadcast event ${eventName}:`, error)\n throw error\n }\n }\n\n /**\n * Authorize channel access.\n *\n * @param channel - Channel name\n * @param socketId - Socket ID\n * @param userId - User ID (optional)\n * @returns Authorization payload\n */\n async authorizeChannel(\n channel: string,\n socketId: string,\n userId?: string | number\n ): Promise<{ auth: string; channel_data?: string } | null> {\n // Check authorization callback first.\n if (this.authCallback) {\n const authorized = await this.authCallback(channel, socketId, userId)\n if (!authorized) {\n return null\n }\n }\n\n // If the driver supports authorization, use it.\n if (this.driver?.authorizeChannel) {\n return await this.driver.authorizeChannel(channel, socketId, userId)\n }\n\n // Default deny for private/presence channels.\n if (channel.startsWith('private-') || channel.startsWith('presence-')) {\n return null\n }\n\n // Public channels do not require authorization.\n return { auth: '' }\n }\n}\n",
|
|
6
|
+
"/**\n * Base channel interface.\n */\nexport interface Channel {\n /**\n * Channel name.\n */\n name: string\n\n /**\n * Channel type.\n */\n type: 'public' | 'private' | 'presence'\n}\n\n/**\n * Public channel.\n */\nexport class PublicChannel implements Channel {\n type = 'public' as const\n\n constructor(public name: string) {}\n}\n\n/**\n * Private channel.\n */\nexport class PrivateChannel implements Channel {\n type = 'private' as const\n\n constructor(public name: string) {}\n}\n\n/**\n * Presence channel (can track online users).\n */\nexport class PresenceChannel implements Channel {\n type = 'presence' as const\n\n constructor(public name: string) {}\n}\n",
|
|
7
|
+
"import type { BroadcastDriver } from './BroadcastDriver'\n\n/**\n * Ably driver configuration.\n */\nexport interface AblyDriverConfig {\n apiKey: string\n}\n\n/**\n * Ably driver.\n *\n * Broadcasts through the Ably service.\n */\nexport class AblyDriver implements BroadcastDriver {\n private baseUrl = 'https://rest.ably.io'\n\n constructor(private config: AblyDriverConfig) {}\n\n async broadcast(\n channel: { name: string; type: string },\n event: string,\n data: Record<string, unknown>\n ): Promise<void> {\n const path = `/channels/${channel.name}/messages`\n const auth = btoa(this.config.apiKey)\n\n const response = await fetch(`${this.baseUrl}${path}`, {\n method: 'POST',\n headers: {\n Authorization: `Basic ${auth}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n name: event,\n data,\n }),\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Failed to broadcast via Ably: ${error}`)\n }\n }\n\n async authorizeChannel(\n channel: string,\n _socketId: string,\n userId?: string | number\n ): Promise<{ auth: string; channel_data?: string }> {\n // Ably uses a different authorization mechanism.\n // This is only a basic implementation.\n return {\n auth: this.config.apiKey,\n ...(channel.startsWith('presence-') && userId\n ? {\n channel_data: JSON.stringify({\n clientId: userId.toString(),\n }),\n }\n : {}),\n }\n }\n}\n",
|
|
8
|
+
"import type { BroadcastDriver } from './BroadcastDriver'\n\n/**\n * Pusher driver configuration.\n */\nexport interface PusherDriverConfig {\n appId: string\n key: string\n secret: string\n cluster?: string\n useTLS?: boolean\n}\n\n/**\n * Pusher driver.\n *\n * Broadcasts through the Pusher service.\n */\nexport class PusherDriver implements BroadcastDriver {\n private baseUrl: string\n\n constructor(private config: PusherDriverConfig) {\n const cluster = this.config.cluster || 'mt1'\n this.baseUrl = `https://api-${cluster}.pusher.com`\n }\n\n async broadcast(\n channel: { name: string; type: string },\n event: string,\n data: Record<string, unknown>\n ): Promise<void> {\n const path = `/apps/${this.config.appId}/events`\n const body = {\n name: event,\n channel: channel.name,\n data: JSON.stringify(data),\n }\n\n const timestamp = Math.floor(Date.now() / 1000)\n const queryString = new URLSearchParams({\n auth_key: this.config.key,\n auth_timestamp: timestamp.toString(),\n auth_version: '1.0',\n body_md5: this.md5(JSON.stringify(body)),\n })\n\n const authString = `POST\\n${path}\\n${queryString.toString()}`\n const authSignature = await this.hmacSHA256(authString, this.config.secret)\n\n queryString.append('auth_signature', authSignature)\n\n const response = await fetch(`${this.baseUrl}${path}?${queryString.toString()}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Failed to broadcast via Pusher: ${error}`)\n }\n }\n\n async authorizeChannel(\n channel: string,\n socketId: string,\n userId?: string | number\n ): Promise<{ auth: string; channel_data?: string }> {\n const stringToSign = `${socketId}:${channel}`\n const signature = await this.hmacSHA256(stringToSign, this.config.secret)\n\n if (channel.startsWith('presence-')) {\n const channelData = JSON.stringify({\n user_id: userId?.toString(),\n user_info: {},\n })\n return {\n auth: `${this.config.key}:${signature}`,\n channel_data: channelData,\n }\n }\n\n return {\n auth: `${this.config.key}:${signature}`,\n }\n }\n\n private async hmacSHA256(message: string, secret: string): Promise<string> {\n // Uses the Web Crypto API.\n const encoder = new TextEncoder()\n const keyData = encoder.encode(secret)\n const messageData = encoder.encode(message)\n\n const key = await crypto.subtle.importKey(\n 'raw',\n keyData,\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign']\n )\n\n const signature = await crypto.subtle.sign('HMAC', key, messageData)\n const hashArray = Array.from(new Uint8Array(signature))\n return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')\n }\n\n private md5(text: string): string {\n // Simplified MD5 implementation (production should use a proper MD5 implementation).\n return btoa(text).replace(/[+/=]/g, '').substring(0, 32)\n }\n}\n",
|
|
9
|
+
"import type { BroadcastDriver } from './BroadcastDriver'\n\n/**\n * Redis driver configuration.\n */\nexport interface RedisDriverConfig {\n url?: string\n host?: string\n port?: number\n password?: string\n db?: number\n keyPrefix?: string\n}\n\n/**\n * Redis driver.\n *\n * Broadcasts via Redis Pub/Sub.\n * Requires an external Redis client.\n */\nexport class RedisDriver implements BroadcastDriver {\n private redis: {\n publish(channel: string, message: string): Promise<number>\n } | null = null\n\n constructor(private config: RedisDriverConfig) {\n // The Redis client should be injected from the outside.\n // This class only provides the adapter interface.\n }\n\n /**\n * Set Redis client.\n */\n setRedisClient(client: { publish(channel: string, message: string): Promise<number> }): void {\n this.redis = client\n }\n\n async broadcast(\n channel: { name: string; type: string },\n event: string,\n data: Record<string, unknown>\n ): Promise<void> {\n if (!this.redis) {\n throw new Error(\n 'Redis client not set. Please install a Redis client and call setRedisClient()'\n )\n }\n\n const prefix = this.config.keyPrefix || 'gravito:broadcast:'\n const channelName = `${prefix}${channel.name}`\n const message = JSON.stringify({\n event,\n data,\n channel: channel.name,\n type: channel.type,\n })\n\n await this.redis.publish(channelName, message)\n }\n}\n",
|
|
10
|
+
"import type { BroadcastDriver } from './BroadcastDriver'\n\n/**\n * WebSocket connection interface.\n */\nexport interface WebSocketConnection {\n send(data: string): void\n close(): void\n readyState: number\n}\n\n/**\n * WebSocket driver configuration.\n */\nexport interface WebSocketDriverConfig {\n /**\n * Get all active connections.\n */\n getConnections(): WebSocketConnection[]\n\n /**\n * Filter connections by channel (optional).\n */\n filterConnectionsByChannel?(channel: string): WebSocketConnection[]\n}\n\n/**\n * WebSocket driver.\n *\n * Broadcasts via WebSocket.\n * Suitable for single-node deployments or when direct WebSocket connections are required.\n */\nexport class WebSocketDriver implements BroadcastDriver {\n constructor(private config: WebSocketDriverConfig) {}\n\n async broadcast(\n channel: { name: string; type: string },\n event: string,\n data: Record<string, unknown>\n ): Promise<void> {\n const message = JSON.stringify({\n channel: channel.name,\n event,\n data,\n })\n\n let connections = this.config.getConnections()\n\n // If channel filtering is supported, use it.\n if (this.config.filterConnectionsByChannel) {\n connections = this.config.filterConnectionsByChannel(channel.name)\n }\n\n // Send to all connections.\n for (const connection of connections) {\n if (connection.readyState === 1) {\n // WebSocket.OPEN\n try {\n connection.send(message)\n } catch (error) {\n // Ignore failed sends.\n console.error('Failed to send WebSocket message:', error)\n }\n }\n }\n }\n}\n",
|
|
11
|
+
"import type { GravitoOrbit, PlanetCore } from 'gravito-core'\nimport { BroadcastManager } from './BroadcastManager'\nimport type { AblyDriverConfig } from './drivers/AblyDriver'\nimport { AblyDriver } from './drivers/AblyDriver'\nimport type { BroadcastDriver } from './drivers/BroadcastDriver'\nimport type { PusherDriverConfig } from './drivers/PusherDriver'\nimport { PusherDriver } from './drivers/PusherDriver'\nimport type { RedisDriverConfig } from './drivers/RedisDriver'\nimport { RedisDriver } from './drivers/RedisDriver'\nimport type { WebSocketDriverConfig } from './drivers/WebSocketDriver'\nimport { WebSocketDriver } from './drivers/WebSocketDriver'\n\n/**\n * OrbitRadiance options.\n */\nexport interface OrbitRadianceOptions {\n /**\n * Driver type.\n */\n driver: 'pusher' | 'ably' | 'redis' | 'websocket'\n\n /**\n * Driver configuration.\n */\n config: PusherDriverConfig | AblyDriverConfig | RedisDriverConfig | WebSocketDriverConfig\n\n /**\n * Channel authorization callback (optional).\n */\n authorizeChannel?: (\n channel: string,\n socketId: string,\n userId?: string | number\n ) => Promise<boolean>\n}\n\n/**\n * Broadcasting Orbit\n *\n * Provides broadcasting capabilities with multiple drivers (Pusher, Ably, Redis, WebSocket).\n */\nexport class OrbitRadiance implements GravitoOrbit {\n private options: OrbitRadianceOptions\n\n constructor(options: OrbitRadianceOptions) {\n this.options = options\n }\n\n /**\n * Configure OrbitRadiance.\n */\n static configure(options: OrbitRadianceOptions): OrbitRadiance {\n return new OrbitRadiance(options)\n }\n\n async install(core: PlanetCore): Promise<void> {\n const manager = new BroadcastManager(core)\n\n // Create and set driver.\n let driver: BroadcastDriver\n\n switch (this.options.driver) {\n case 'pusher':\n driver = new PusherDriver(this.options.config as PusherDriverConfig)\n break\n case 'ably':\n driver = new AblyDriver(this.options.config as AblyDriverConfig)\n break\n case 'redis': {\n driver = new RedisDriver(this.options.config as RedisDriverConfig)\n // If a Redis client is provided via core services, set it.\n const redisClient = core.services.get('redis') as\n | { publish(channel: string, message: string): Promise<number> }\n | undefined\n if (redisClient) {\n ;(driver as RedisDriver).setRedisClient(redisClient)\n }\n break\n }\n case 'websocket':\n driver = new WebSocketDriver(this.options.config as WebSocketDriverConfig)\n break\n default:\n throw new Error(`Unsupported broadcast driver: ${this.options.driver}`)\n }\n\n manager.setDriver(driver)\n\n // Set auth callback.\n if (this.options.authorizeChannel) {\n manager.setAuthCallback(this.options.authorizeChannel)\n }\n\n // Register into core services.\n core.services.set('broadcast', manager)\n\n // Integrate with EventManager.\n if (core.events) {\n core.events.setBroadcastManager({\n broadcast: async (event: any, channel: any, data: any, eventName: any) => {\n await manager.broadcast(event, channel, data, eventName)\n },\n })\n }\n\n core.logger.info(`[OrbitRadiance] Installed with ${this.options.driver} driver`)\n }\n}\n"
|
|
12
|
+
],
|
|
13
|
+
"mappings": "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",
|
|
14
|
+
"debugId": "01B589AE84540A2064756E2164756E21",
|
|
15
|
+
"names": []
|
|
16
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gravito/radiance",
|
|
3
|
+
"version": "1.0.0-alpha.2",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"description": "Lightweight, high-performance broadcasting system for Gravito framework. Supports multiple drivers (Pusher, Ably, Redis, WebSocket) with zero runtime overhead.",
|
|
8
|
+
"module": "./dist/index.mjs",
|
|
9
|
+
"main": "./dist/index.cjs",
|
|
10
|
+
"type": "module",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.mjs",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "bun run build.ts",
|
|
26
|
+
"test": "bun test",
|
|
27
|
+
"typecheck": "tsc --noEmit"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"gravito",
|
|
31
|
+
"orbit",
|
|
32
|
+
"broadcasting",
|
|
33
|
+
"websocket",
|
|
34
|
+
"pusher",
|
|
35
|
+
"ably",
|
|
36
|
+
"redis",
|
|
37
|
+
"realtime"
|
|
38
|
+
],
|
|
39
|
+
"author": "Carl Lee <carllee0520@gmail.com>",
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"gravito-core": "1.0.0-beta.2"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"bun-types": "latest",
|
|
46
|
+
"typescript": "^5.9.3"
|
|
47
|
+
},
|
|
48
|
+
"homepage": "https://github.com/gravito-framework/gravito#readme",
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "git+https://github.com/gravito-framework/gravito.git",
|
|
52
|
+
"directory": "packages/radiance"
|
|
53
|
+
}
|
|
54
|
+
}
|