hermes-chat-react 0.1.1 → 0.1.3
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/dist/{chunk-OMLFDWYU.js → chunk-LJYB6LS7.js} +2 -5
- package/dist/chunk-LJYB6LS7.js.map +1 -0
- package/dist/cli.cjs +87 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +64 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +1 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1 -1
- package/dist/react.cjs +1744 -520
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +624 -22
- package/dist/react.d.ts +624 -22
- package/dist/react.js +1657 -463
- package/dist/react.js.map +1 -1
- package/package.json +13 -3
- package/readme.md +367 -0
- package/dist/chunk-OMLFDWYU.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hermes-chat-react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Official React SDK for the Hermes real-time messaging engine.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
7
7
|
"module": "./dist/index.js",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
|
+
"bin": {
|
|
10
|
+
"hermes": "./dist/cli.js"
|
|
11
|
+
},
|
|
9
12
|
"exports": {
|
|
10
13
|
".": {
|
|
11
14
|
"import": {
|
|
@@ -33,7 +36,9 @@
|
|
|
33
36
|
"scripts": {
|
|
34
37
|
"build": "tsup",
|
|
35
38
|
"dev": "tsup --watch",
|
|
36
|
-
"prepublishOnly": "npm run build"
|
|
39
|
+
"prepublishOnly": "npm run build",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"test:watch": "vitest"
|
|
37
42
|
},
|
|
38
43
|
"peerDependencies": {
|
|
39
44
|
"react": ">=17.0.0",
|
|
@@ -44,10 +49,15 @@
|
|
|
44
49
|
"socket.io-client": "^4.8.3"
|
|
45
50
|
},
|
|
46
51
|
"devDependencies": {
|
|
52
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
53
|
+
"@testing-library/react": "^16.3.2",
|
|
54
|
+
"@testing-library/user-event": "^14.6.1",
|
|
47
55
|
"@types/node": "^24.5.2",
|
|
48
56
|
"@types/react": "^18.0.0",
|
|
49
57
|
"@types/react-dom": "^18.0.0",
|
|
58
|
+
"jsdom": "^29.0.1",
|
|
50
59
|
"tsup": "^8.5.0",
|
|
51
|
-
"typescript": "^5.9.2"
|
|
60
|
+
"typescript": "^5.9.2",
|
|
61
|
+
"vitest": "^4.1.0"
|
|
52
62
|
}
|
|
53
63
|
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
# hermes-chat-react
|
|
2
|
+
|
|
3
|
+
> Real-time chat SDK for React — hooks, components, and typed events out of the box.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install hermes-chat-react
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## What is this?
|
|
12
|
+
|
|
13
|
+
`hermes-chat-react` is the official React SDK for the **Hermes** real-time messaging engine. Drop in a fully working chat system — rooms, messages, typing indicators, presence, reactions, file uploads, read receipts — without building any of it yourself.
|
|
14
|
+
|
|
15
|
+
Built on **Socket.IO**, typed end-to-end with **TypeScript**, and designed to be styled however you want.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { HermesClient } from "hermes-chat-react";
|
|
23
|
+
import {
|
|
24
|
+
useMessages,
|
|
25
|
+
useRooms,
|
|
26
|
+
ChatInput,
|
|
27
|
+
MessageList,
|
|
28
|
+
} from "hermes-chat-react/react";
|
|
29
|
+
|
|
30
|
+
// 1. Create and connect a client
|
|
31
|
+
const client = new HermesClient({
|
|
32
|
+
endpoint: "https://your-hermes-server.com",
|
|
33
|
+
apiKey: "YOUR_API_KEY",
|
|
34
|
+
secret: "YOUR_SECRET",
|
|
35
|
+
userId: "user_123",
|
|
36
|
+
displayName: "Jane Doe",
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
await client.connect();
|
|
40
|
+
|
|
41
|
+
// 2. Use hooks in your components
|
|
42
|
+
function Chat() {
|
|
43
|
+
const { rooms } = useRooms(client);
|
|
44
|
+
const { messages, sendMessage } = useMessages(client, rooms[0]?._id);
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<>
|
|
48
|
+
<MessageList messages={messages} currentUser={client.currentUser!} />
|
|
49
|
+
<ChatInput
|
|
50
|
+
onSendText={(text) => sendMessage({ type: "text", text })}
|
|
51
|
+
onSendFile={() => {}}
|
|
52
|
+
/>
|
|
53
|
+
</>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Two Ways to Authenticate
|
|
61
|
+
|
|
62
|
+
**Option A — API Key + Secret** _(server-to-server auth, recommended for production)_
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
const client = new HermesClient({
|
|
66
|
+
endpoint: "https://your-hermes-server.com",
|
|
67
|
+
apiKey: "YOUR_API_KEY",
|
|
68
|
+
secret: "YOUR_SECRET",
|
|
69
|
+
userId: "user_123",
|
|
70
|
+
displayName: "Jane Doe",
|
|
71
|
+
avatar: "https://...", // optional
|
|
72
|
+
email: "jane@example.com", // optional
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Option B — Pre-issued Token** _(exchange credentials yourself, pass the token)_
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
const client = new HermesClient({
|
|
80
|
+
endpoint: "https://your-hermes-server.com",
|
|
81
|
+
token: "eyJhbGci...", // JWT from your auth flow
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Hooks
|
|
88
|
+
|
|
89
|
+
All hooks take a connected `HermesClient` instance.
|
|
90
|
+
|
|
91
|
+
### `useRooms(client)`
|
|
92
|
+
|
|
93
|
+
Manage rooms — list, create direct messages, create groups.
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
const {
|
|
97
|
+
rooms, // Room[]
|
|
98
|
+
loading, // boolean
|
|
99
|
+
createDirect, // (input: CreateDirectRoomInput) => Promise<Room>
|
|
100
|
+
createGroup, // (input: CreateGroupRoomInput) => Promise<Room>
|
|
101
|
+
} = useRooms(client);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### `useMessages(client, roomId)`
|
|
105
|
+
|
|
106
|
+
Send, edit, delete, and paginate messages in a room.
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
const {
|
|
110
|
+
messages, // Message[]
|
|
111
|
+
loading, // boolean
|
|
112
|
+
loadingMore, // boolean
|
|
113
|
+
hasMore, // boolean
|
|
114
|
+
loadMore, // () => void
|
|
115
|
+
sendMessage, // (input: SendMessageInput) => Promise<Message>
|
|
116
|
+
editMessage, // (messageId, roomId, text) => Promise<Message>
|
|
117
|
+
deleteMessage, // (messageId, roomId) => Promise<void>
|
|
118
|
+
addReaction, // (messageId, roomId, emoji) => Promise<void>
|
|
119
|
+
} = useMessages(client, roomId);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### `useTyping(client, roomId)`
|
|
123
|
+
|
|
124
|
+
Typing indicators — start, stop, and listen for others.
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
const {
|
|
128
|
+
typingText, // string — e.g. "Jane is typing..."
|
|
129
|
+
startTyping, // () => void
|
|
130
|
+
stopTyping, // () => void
|
|
131
|
+
} = useTyping(client, roomId);
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### `usePresence(client)`
|
|
135
|
+
|
|
136
|
+
Track who's online in real time.
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
const { isOnline } = usePresence(client);
|
|
140
|
+
|
|
141
|
+
isOnline("user_123"); // boolean
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### `useReadReceipts(client, roomId)`
|
|
145
|
+
|
|
146
|
+
Mark messages as seen and track who has read what.
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
const {
|
|
150
|
+
markSeen, // (lastMessageId: string) => Promise<void>
|
|
151
|
+
seenBy, // (messageId: string) => string[]
|
|
152
|
+
receipts, // Map<string, Set<string>>
|
|
153
|
+
} = useReadReceipts(client, roomId);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### `useUpload(client)`
|
|
157
|
+
|
|
158
|
+
Upload files and send them as messages.
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
const {
|
|
162
|
+
sendFile, // (roomId, file, replyTo?) => Promise<void>
|
|
163
|
+
uploading, // boolean
|
|
164
|
+
} = useUpload(client);
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Components
|
|
170
|
+
|
|
171
|
+
Pre-built components you can drop in and style with CSS.
|
|
172
|
+
|
|
173
|
+
### `<MessageList />`
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
<MessageList
|
|
177
|
+
messages={messages}
|
|
178
|
+
currentUser={currentUser}
|
|
179
|
+
loading={loading}
|
|
180
|
+
loadingMore={loadingMore}
|
|
181
|
+
hasMore={hasMore}
|
|
182
|
+
onLoadMore={loadMore}
|
|
183
|
+
onEdit={editMessage}
|
|
184
|
+
onDelete={deleteMessage}
|
|
185
|
+
onReact={addReaction}
|
|
186
|
+
onReply={(msg) => setReplyingTo(msg)}
|
|
187
|
+
autoScroll
|
|
188
|
+
/>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### `<ChatInput />`
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
<ChatInput
|
|
195
|
+
onSendText={async (text) => sendMessage({ type: "text", text })}
|
|
196
|
+
onSendFile={async (file) => sendFile(roomId, file)}
|
|
197
|
+
onTypingStart={startTyping}
|
|
198
|
+
onTypingStop={stopTyping}
|
|
199
|
+
replyingTo={replyingTo}
|
|
200
|
+
onCancelReply={() => setReplyingTo(null)}
|
|
201
|
+
placeholder="Type a message..."
|
|
202
|
+
/>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### `<TypingIndicator />`
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
<TypingIndicator typingText={typingText} />
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### `<RoomList />` · `<OnlineBadge />` · `<ReactionPicker />` · `<MediaMessage />`
|
|
212
|
+
|
|
213
|
+
Additional utility components — import from `hermes-chat-react/react`.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Styling
|
|
218
|
+
|
|
219
|
+
All components expose BEM class names so you can override any style.
|
|
220
|
+
|
|
221
|
+
```css
|
|
222
|
+
/* Message bubbles */
|
|
223
|
+
.hermes-message--own .hermes-message__bubble {
|
|
224
|
+
background: #your-color;
|
|
225
|
+
}
|
|
226
|
+
.hermes-message--other .hermes-message__bubble {
|
|
227
|
+
background: #your-color;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/* Input area */
|
|
231
|
+
.hermes-chat-input textarea {
|
|
232
|
+
border-radius: 12px;
|
|
233
|
+
}
|
|
234
|
+
.hermes-chat-input button {
|
|
235
|
+
background: #your-brand-color;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* Typing indicator */
|
|
239
|
+
.hermes-typing-indicator {
|
|
240
|
+
color: #your-color;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/* Load more button */
|
|
244
|
+
.hermes-load-more {
|
|
245
|
+
border-radius: 8px;
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Events
|
|
252
|
+
|
|
253
|
+
Listen to real-time events directly on the client.
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
client.on("message:receive", (message) => console.log("New message", message));
|
|
257
|
+
client.on("user:online", (event) =>
|
|
258
|
+
console.log(event.displayName, "came online"),
|
|
259
|
+
);
|
|
260
|
+
client.on("user:offline", (event) => console.log(event.userId, "went offline"));
|
|
261
|
+
client.on("typing:started", (event) =>
|
|
262
|
+
console.log(event.displayName, "is typing"),
|
|
263
|
+
);
|
|
264
|
+
client.on("reaction:updated", (event) => console.log("Reaction update", event));
|
|
265
|
+
client.on("disconnected", (reason) => console.log("Lost connection:", reason));
|
|
266
|
+
client.on("error", (err) => console.error(err));
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Full event reference:
|
|
270
|
+
|
|
271
|
+
| Event | Payload |
|
|
272
|
+
| -------------------- | ----------------------- |
|
|
273
|
+
| `connected` | — |
|
|
274
|
+
| `disconnected` | `reason: string` |
|
|
275
|
+
| `error` | `Error` |
|
|
276
|
+
| `message:receive` | `Message` |
|
|
277
|
+
| `message:edited` | `Message` |
|
|
278
|
+
| `message:deleted` | `{ messageId, roomId }` |
|
|
279
|
+
| `room:created` | `Room` |
|
|
280
|
+
| `room:deleted` | `{ roomId }` |
|
|
281
|
+
| `room:member:joined` | `{ roomId, userId }` |
|
|
282
|
+
| `room:member:left` | `{ roomId, userId }` |
|
|
283
|
+
| `user:online` | `PresenceEvent` |
|
|
284
|
+
| `user:offline` | `LastSeenEvent` |
|
|
285
|
+
| `typing:started` | `TypingEvent` |
|
|
286
|
+
| `typing:stopped` | `TypingEvent` |
|
|
287
|
+
| `receipt:updated` | `ReceiptEvent` |
|
|
288
|
+
| `reaction:updated` | `ReactionEvent` |
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## TypeScript
|
|
293
|
+
|
|
294
|
+
Everything is typed. Import types directly:
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
import type {
|
|
298
|
+
HermesConfig,
|
|
299
|
+
HermesUser,
|
|
300
|
+
Room,
|
|
301
|
+
Message,
|
|
302
|
+
SendMessageInput,
|
|
303
|
+
MessageType,
|
|
304
|
+
ConnectionStatus,
|
|
305
|
+
PresenceEvent,
|
|
306
|
+
TypingEvent,
|
|
307
|
+
ReceiptEvent,
|
|
308
|
+
ReactionEvent,
|
|
309
|
+
UploadResult,
|
|
310
|
+
} from "hermes-chat-react";
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## Client API
|
|
316
|
+
|
|
317
|
+
```ts
|
|
318
|
+
// Connection
|
|
319
|
+
client.connect() // Promise<HermesUser>
|
|
320
|
+
client.disconnect() // void
|
|
321
|
+
client.isConnected // boolean
|
|
322
|
+
client.currentUser // HermesUser | null
|
|
323
|
+
client.status // ConnectionStatus
|
|
324
|
+
|
|
325
|
+
// Messaging
|
|
326
|
+
client.sendMessage(input) // Promise<Message>
|
|
327
|
+
client.editMessage(messageId, roomId, text) // Promise<Message>
|
|
328
|
+
client.deleteMessage(messageId, roomId) // Promise<void>
|
|
329
|
+
client.getHistory(roomId, before?, limit?) // Promise<MessageHistoryResult>
|
|
330
|
+
|
|
331
|
+
// Rooms
|
|
332
|
+
client.getRooms() // Promise<Room[]>
|
|
333
|
+
client.createDirectRoom({ targetUserId }) // Promise<Room>
|
|
334
|
+
client.createGroupRoom({ name, memberIds }) // Promise<Room>
|
|
335
|
+
client.deleteRoom(roomId) // Promise<void>
|
|
336
|
+
client.addMember(roomId, userId) // Promise<void>
|
|
337
|
+
client.removeMember(roomId, userId) // Promise<void>
|
|
338
|
+
|
|
339
|
+
// Presence & Typing
|
|
340
|
+
client.pingPresence(roomId) // void
|
|
341
|
+
client.startTyping(roomId) // void
|
|
342
|
+
client.stopTyping(roomId) // void
|
|
343
|
+
|
|
344
|
+
// Reactions & Receipts
|
|
345
|
+
client.addReaction(messageId, roomId, emoji) // Promise<void>
|
|
346
|
+
client.markSeen(roomId, lastMessageId) // Promise<void>
|
|
347
|
+
|
|
348
|
+
// File Upload
|
|
349
|
+
client.uploadFile(file) // Promise<UploadResult>
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## Peer Dependencies
|
|
355
|
+
|
|
356
|
+
```json
|
|
357
|
+
{
|
|
358
|
+
"react": ">=17.0.0",
|
|
359
|
+
"react-dom": ">=17.0.0"
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## License
|
|
366
|
+
|
|
367
|
+
MIT © Harshavardanan Moorthy
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/HermesClient.ts","../src/core/EventEmitter.ts"],"sourcesContent":["import { io, Socket } from \"socket.io-client\";\nimport { EventEmitter } from \"./EventEmitter\";\nimport type {\n HermesConfig,\n HermesUser,\n ConnectionStatus,\n Message,\n Room,\n SendMessageInput,\n MessageHistoryResult,\n CreateDirectRoomInput,\n CreateGroupRoomInput,\n UploadResult,\n} from \"../types/index\";\n\n// Manual interface — avoids Extract<> returning never on discriminated union\ninterface ApiKeyConfig {\n endpoint: string;\n apiKey: string;\n secret: string;\n userId: string;\n displayName: string;\n avatar?: string;\n email?: string;\n}\n\nexport class HermesClient extends EventEmitter {\n private config: HermesConfig;\n private socket: Socket | null = null;\n private token: string | null = null;\n\n public user: HermesUser | null = null;\n public status: ConnectionStatus = \"idle\";\n\n constructor(config: HermesConfig) {\n super();\n this.config = config;\n if (\"token\" in config && typeof config.token === \"string\") {\n this.token = config.token;\n }\n }\n\n async connect(): Promise<HermesUser> {\n this.status = \"connecting\";\n try {\n if (this.token) {\n await this._connectSocket();\n return this.user!;\n }\n if (!(\"apiKey\" in this.config)) {\n throw new Error(\"Either token or (apiKey + secret + userId) must be provided\");\n }\n const cfg = this.config as unknown as ApiKeyConfig;\n const res = await fetch(`${cfg.endpoint}/hermes/connect`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n apiKey: cfg.apiKey,\n secret: cfg.secret,\n userId: cfg.userId,\n displayName: cfg.displayName ?? cfg.userId,\n avatar: cfg.avatar,\n email: cfg.email,\n }),\n });\n const data = await res.json();\n if (!data.success) throw new Error(data.message || \"Auth failed\");\n this.token = data.token;\n this.user = {\n userId: data.user.hermesUserId,\n displayName: data.user.displayName,\n avatar: data.user.avatar,\n email: data.user.email,\n };\n await this._connectSocket();\n return this.user!;\n } catch (err) {\n this.status = \"error\";\n this.emit(\"error\", err as Error);\n throw err;\n }\n }\n\n private async _connectSocket(): Promise<void> {\n this.socket = io(`${this.config.endpoint}/hermes`, {\n auth: { token: this.token },\n transports: [\"websocket\"],\n reconnection: true,\n reconnectionAttempts: 5,\n reconnectionDelay: 1000,\n });\n this._wireSocketEvents();\n await new Promise<void>((resolve, reject) => {\n this.socket!.once(\"connect\", resolve);\n this.socket!.once(\"connect_error\", (err) => reject(err));\n });\n this.status = \"connected\";\n this.emit(\"connected\");\n }\n\n disconnect(): void {\n this.socket?.disconnect();\n this.socket = null;\n this.token = null;\n this.status = \"disconnected\";\n this.emit(\"disconnected\", \"manual\");\n }\n\n private _wireSocketEvents(): void {\n const s = this.socket!;\n s.on(\"disconnect\", (reason) => { this.status = \"disconnected\"; this.emit(\"disconnected\", reason); });\n s.on(\"connect_error\", (err) => { this.status = \"error\"; this.emit(\"error\", err); });\n s.on(\"message:receive\", (msg: Message) => this.emit(\"message:receive\", msg));\n s.on(\"message:deleted\", (data) => this.emit(\"message:deleted\", data));\n s.on(\"message:edited\", (msg: Message) => this.emit(\"message:edited\", msg));\n s.on(\"room:created\", (room: Room) => this.emit(\"room:created\", room));\n s.on(\"room:deleted\", (data) => this.emit(\"room:deleted\", data));\n s.on(\"room:member:joined\", (data) => this.emit(\"room:member:joined\", data));\n s.on(\"room:member:left\", (data) => this.emit(\"room:member:left\", data));\n s.on(\"user:online\", (event) => this.emit(\"user:online\", event));\n s.on(\"user:offline\", (event) => this.emit(\"user:offline\", event));\n s.on(\"typing:started\", (event) => this.emit(\"typing:started\", event));\n s.on(\"typing:stopped\", (event) => this.emit(\"typing:stopped\", event));\n s.on(\"receipt:updated\", (event) => this.emit(\"receipt:updated\", event));\n s.on(\"reaction:updated\", (event) => this.emit(\"reaction:updated\", event));\n }\n\n _emit<T = any>(event: string, data?: any): Promise<T> {\n return new Promise((resolve, reject) => {\n if (!this.socket?.connected) return reject(new Error(\"Not connected to Hermes engine\"));\n const timer = setTimeout(() => reject(new Error(`Timed out waiting for \"${event}\"`)), 5000);\n const callback = (response: any) => {\n clearTimeout(timer);\n if (response?.success === false) reject(new Error(response.error || \"Unknown error\"));\n else resolve(response);\n };\n if (data && Object.keys(data).length > 0) this.socket!.emit(event, data, callback);\n else this.socket!.emit(event, callback);\n });\n }\n\n async sendMessage(input: SendMessageInput): Promise<Message> {\n const res = await this._emit<{ message: Message }>(\"message:send\", input);\n return res.message;\n }\n async getHistory(roomId: string, before?: string, limit?: number): Promise<MessageHistoryResult> {\n return this._emit(\"message:history\", { roomId, before, limit });\n }\n async deleteMessage(messageId: string, roomId: string): Promise<void> {\n await this._emit(\"message:delete\", { messageId, roomId });\n }\n async editMessage(messageId: string, roomId: string, text: string): Promise<Message> {\n const res = await this._emit<{ message: Message }>(\"message:edit\", { messageId, roomId, text });\n return res.message;\n }\n async createDirectRoom(input: CreateDirectRoomInput): Promise<Room> {\n const res = await this._emit<{ room: Room }>(\"room:create:direct\", { targetHermesUserId: input.targetUserId });\n return res.room;\n }\n async createGroupRoom(input: CreateGroupRoomInput): Promise<Room> {\n const res = await this._emit<{ room: Room }>(\"room:create:group\", input);\n return res.room;\n }\n async deleteRoom(roomId: string): Promise<void> { await this._emit(\"room:delete\", { roomId }); }\n async getRooms(): Promise<Room[]> {\n const res = await this._emit<{ rooms: Room[] }>(\"room:list\");\n return res.rooms;\n }\n async addMember(roomId: string, newMemberId: string): Promise<void> { await this._emit(\"room:member:add\", { roomId, newMemberId }); }\n async removeMember(roomId: string, targetId: string): Promise<void> { await this._emit(\"room:member:remove\", { roomId, targetId }); }\n pingPresence(roomId: string): void { this.socket?.emit(\"presence:ping\", { roomId }); }\n startTyping(roomId: string): void { this.socket?.emit(\"typing:start\", { roomId }); }\n stopTyping(roomId: string): void { this.socket?.emit(\"typing:stop\", { roomId }); }\n async markSeen(roomId: string, lastMessageId: string): Promise<void> { await this._emit(\"receipt:seen\", { roomId, lastMessageId }); }\n async addReaction(messageId: string, roomId: string, emoji: string): Promise<void> { await this._emit(\"reaction:add\", { messageId, roomId, emoji }); }\n async uploadFile(file: File): Promise<UploadResult> {\n if (!this.token) throw new Error(\"Not connected\");\n const formData = new FormData();\n formData.append(\"file\", file);\n const res = await fetch(`${this.config.endpoint}/hermes/upload`, {\n method: \"POST\",\n headers: { Authorization: `Bearer ${this.token}` },\n body: formData,\n });\n const data = await res.json();\n if (!data.success) throw new Error(data.error || \"Upload failed\");\n return data as UploadResult;\n }\n get isConnected(): boolean { return this.status === \"connected\" && !!this.socket?.connected; }\n get currentUser(): HermesUser | null { return this.user; }\n}\n","import type { HermesEvents } from \"../types/index\";\n\ntype EventKey = keyof HermesEvents;\ntype EventCallback<K extends EventKey> = HermesEvents[K];\ntype ListenerMap = { [K in EventKey]?: EventCallback<K>[] };\n\nexport class EventEmitter {\n private listeners: ListenerMap = {};\n\n on<K extends EventKey>(event: K, callback: EventCallback<K>): this {\n if (!this.listeners[event]) {\n this.listeners[event] = [];\n }\n (this.listeners[event] as EventCallback<K>[]).push(callback);\n return this;\n }\n\n off<K extends EventKey>(event: K, callback: EventCallback<K>): void {\n if (!this.listeners[event]) return;\n this.listeners[event] = (\n this.listeners[event] as EventCallback<K>[]\n ).filter((cb) => cb !== callback) as any;\n }\n\n once<K extends EventKey>(event: K, callback: EventCallback<K>): this {\n const wrapper = ((...args: any[]) => {\n (callback as any)(...args);\n this.off(event, wrapper as any);\n }) as EventCallback<K>;\n return this.on(event, wrapper);\n }\n\n emit<K extends EventKey>(\n event: K,\n ...args: Parameters<EventCallback<K>>\n ): this {\n if (!this.listeners[event]) return this;\n (this.listeners[event] as EventCallback<K>[]).forEach((cb) =>\n (cb as any)(...args),\n );\n return this;\n }\n\n removeAllListeners<K extends EventKey>(event?: K): this {\n if (event) {\n delete this.listeners[event];\n } else {\n this.listeners = {};\n }\n return this;\n }\n\n listenerCount<K extends EventKey>(event: K): number {\n return this.listeners[event]?.length ?? 0;\n }\n}\n"],"mappings":"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","names":[]}
|