@smarthivelabs-devs/hive-socket-node 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # @smarthivelabs-devs/hive-socket-node
2
2
 
3
- Node.js SDK for Hive-Socket — push real-time notifications and events to connected clients from any backend service.
3
+ Node.js SDK for Hive-Socket — push real-time notifications and broadcast typed messages to connected clients from any backend service.
4
+
5
+ > **v1.1.0** adds `hive.broadcast.toRoom()` for sending structured typed messages received by `useMessages()` on the client. Use this for full-duplex features (live competitions, collaborative editing, presence) where clients need to act on the payload, not just display a notification.
4
6
 
5
7
  ## Install
6
8
 
@@ -31,10 +33,13 @@ Add to your backend `.env`:
31
33
 
32
34
  ```env
33
35
  HIVE_SOCKET_URL=https://socket.smarthivelabs.dev
34
- HIVE_SOCKET_API_KEY=shai_hivesocket_<your-key>
36
+ HIVE_SOCKET_API_KEY=shai_<your-key> # same value as INTERNAL_API_KEY on the Hive-Socket server
37
+ SMARTHIVE_PROJECT_ID=<your-auth-project-id> # project ID from SmartHive Auth dashboard
35
38
  ```
36
39
 
37
- Get the API key from the Hive-Socket server's `.env` (`INTERNAL_API_KEY`).
40
+ `HIVE_SOCKET_API_KEY` — copy the value of `INTERNAL_API_KEY` set on the Hive-Socket server. This is a shared secret between your backends and Hive-Socket.
41
+
42
+ `SMARTHIVE_PROJECT_ID` — the project ID issued by SmartHive Auth when you registered your product. Pass this as `projectId` in every notify call so Hive-Socket scopes messages and notifications to the correct project.
38
43
 
39
44
  ---
40
45
 
@@ -50,7 +55,7 @@ const hive = createHiveSocketClient({
50
55
 
51
56
  // Push a notification to a user
52
57
  await hive.notify.user('user-id-123', {
53
- projectId: 'your-project-id',
58
+ projectId: process.env.SMARTHIVE_PROJECT_ID!,
54
59
  title: 'New message',
55
60
  body: 'Alice sent you a message',
56
61
  type: 'message',
@@ -67,9 +72,9 @@ Creates a client instance. Create once and reuse across your application.
67
72
 
68
73
  ```typescript
69
74
  const hive = createHiveSocketClient({
70
- baseUrl: 'https://socket.smarthivelabs.dev', // Hive-Socket server URL
71
- apiKey: 'shai_hivesocket_...', // Internal API key
72
- timeout: 10_000, // Optional. Request timeout in ms. Default: 10000
75
+ baseUrl: process.env.HIVE_SOCKET_URL!, // e.g. https://socket.smarthivelabs.dev
76
+ apiKey: process.env.HIVE_SOCKET_API_KEY!, // shai_* internal API key
77
+ timeout: 10_000, // Optional. Request timeout in ms. Default: 10000
73
78
  });
74
79
  ```
75
80
 
@@ -81,7 +86,7 @@ Push a notification to a specific user. Delivered instantly to all their connect
81
86
 
82
87
  ```typescript
83
88
  await hive.notify.user(userId, {
84
- projectId: string, // Your SmartHive project ID
89
+ projectId: string, // SmartHive Auth project ID (process.env.SMARTHIVE_PROJECT_ID)
85
90
  title: string, // Notification title
86
91
  body: string, // Notification body
87
92
  type: NotificationType, // 'info' | 'success' | 'warning' | 'error' | 'message'
@@ -93,7 +98,7 @@ await hive.notify.user(userId, {
93
98
  **Example — payment confirmed:**
94
99
  ```typescript
95
100
  await hive.notify.user(userId, {
96
- projectId: 'workspace-project-id',
101
+ projectId: process.env.SMARTHIVE_PROJECT_ID!,
97
102
  title: 'Payment confirmed',
98
103
  body: `₦${amount.toLocaleString()} received`,
99
104
  type: 'success',
@@ -122,7 +127,7 @@ Broadcast a notification to all users currently in a room. Room notifications ar
122
127
 
123
128
  ```typescript
124
129
  await hive.notify.room(roomId, {
125
- projectId: string,
130
+ projectId: string, // SmartHive Auth project ID (process.env.SMARTHIVE_PROJECT_ID)
126
131
  title: string,
127
132
  body: string,
128
133
  type: NotificationType,
@@ -134,7 +139,7 @@ await hive.notify.room(roomId, {
134
139
  **Example — quiz started:**
135
140
  ```typescript
136
141
  await hive.notify.room(`course:${courseId}`, {
137
- projectId: 'hivedemia-project-id',
142
+ projectId: process.env.SMARTHIVE_PROJECT_ID!,
138
143
  title: 'Quiz started',
139
144
  body: 'Chapter 3 quiz is now live',
140
145
  type: 'info',
@@ -145,7 +150,7 @@ await hive.notify.room(`course:${courseId}`, {
145
150
  **Example — live vote update:**
146
151
  ```typescript
147
152
  await hive.notify.room(`event:${eventId}`, {
148
- projectId: 'votyhive-project-id',
153
+ projectId: process.env.SMARTHIVE_PROJECT_ID!,
149
154
  title: 'Results updated',
150
155
  body: 'New votes have been counted',
151
156
  type: 'info',
@@ -156,6 +161,40 @@ await hive.notify.room(`event:${eventId}`, {
156
161
 
157
162
  ---
158
163
 
164
+ ### `hive.broadcast.toRoom(roomId, payload)` *(v1.1.0)*
165
+
166
+ Broadcast a typed structured message to all sockets in a room. Messages are received by clients via `useMessages(roomId)` — they arrive as `message:new` events, distinct from `notification:new` produced by `notify.*`. Use this for real-time game state, collaborative state changes, or any payload where the client needs to react to the message type programmatically.
167
+
168
+ ```typescript
169
+ await hive.broadcast.toRoom(roomId, {
170
+ projectId: string, // SmartHive Auth project ID
171
+ type: string, // your event type, e.g. "competition.question-change"
172
+ payload: Record<string, unknown>, // arbitrary JSON
173
+ senderId?: string, // optional — defaults to "system"
174
+ });
175
+ ```
176
+
177
+ **Example — competition state advance:**
178
+ ```typescript
179
+ await hive.broadcast.toRoom(`competition:${competitionId}`, {
180
+ projectId: process.env.SMARTHIVE_PROJECT_ID!,
181
+ type: 'competition.question-change',
182
+ payload: { questionIndex: 2, questionEndsAt: '2026-05-24T12:00:30.000Z' },
183
+ senderId: 'hivedemia-api',
184
+ });
185
+ ```
186
+
187
+ **Difference between `notify.room` and `broadcast.toRoom`:**
188
+
189
+ | | `notify.room` | `broadcast.toRoom` |
190
+ |---|---|---|
191
+ | Client hook | `useNotifications()` | `useMessages(roomId)` |
192
+ | Wire event | `notification:new` | `message:new` |
193
+ | Persisted | Yes (offline delivery) | No (ephemeral) |
194
+ | Best for | In-app alerts, toasts | Game state, live sync |
195
+
196
+ ---
197
+
159
198
  ### `hive.health()`
160
199
 
161
200
  Check server health and current connection count.
@@ -330,12 +369,46 @@ export async function POST(req: Request) {
330
369
 
331
370
  ---
332
371
 
372
+ ## Security
373
+
374
+ ### How the API key is sent and validated
375
+
376
+ Every request from this SDK includes an `x-internal-api-key` header:
377
+
378
+ ```
379
+ POST /notify/user
380
+ x-internal-api-key: shai_<your-key>
381
+ ```
382
+
383
+ The Hive-Socket server compares this header against its `INTERNAL_API_KEY` env var using a constant-time comparison (no timing attacks). If the key doesn't match the request is rejected with HTTP 401.
384
+
385
+ **This key is a shared secret between your backends and Hive-Socket.** It must never be used in browser or mobile code — it gives unrestricted ability to send notifications to any user.
386
+
387
+ ### What the server enforces
388
+
389
+ | Guarantee | How |
390
+ |-----------|-----|
391
+ | Only trusted backends can push notifications | `x-internal-api-key` validated on every request |
392
+ | Notifications are scoped to the correct project | `projectId` from your payload scopes DB storage and room routing |
393
+ | `userId` comes from your backend, not the client | Clients never call notify endpoints — only your server-side code does |
394
+
395
+ ### What you must do
396
+
397
+ - **Store the key in environment variables only.** Never commit it to source control or embed it in client-side code.
398
+ - **Use the same key value** as `INTERNAL_API_KEY` on the Hive-Socket server. They must match exactly.
399
+ - **Use a different key per environment** — generate a separate key for dev and prod, set them independently.
400
+ - **Rotate by updating both env vars** — update `INTERNAL_API_KEY` on Hive-Socket and `HIVE_SOCKET_API_KEY` on your backends, then redeploy. There is no revocation endpoint; rotation is instant once both sides are updated.
401
+ - **Always pass `projectId` from your own config** (`process.env.SMARTHIVE_PROJECT_ID`) — never accept it from user input. It is the SmartHive Auth project ID for your product.
402
+
403
+ ---
404
+
333
405
  ## Types
334
406
 
335
407
  ```typescript
336
408
  import type {
337
409
  HiveSocketConfig,
338
410
  HiveNotifyPayload,
411
+ HiveBroadcastPayload,
339
412
  HiveHealthResponse,
340
413
  NotificationType,
341
414
  } from '@smarthivelabs-devs/hive-socket-node';
@@ -357,6 +430,13 @@ interface HiveNotifyPayload {
357
430
  sourceService?: string;
358
431
  }
359
432
 
433
+ interface HiveBroadcastPayload {
434
+ projectId: string;
435
+ type: string;
436
+ payload: Record<string, unknown>;
437
+ senderId?: string;
438
+ }
439
+
360
440
  interface HiveHealthResponse {
361
441
  status: 'ok';
362
442
  connections: number;
package/dist/index.cjs CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ BroadcastResource: () => BroadcastResource,
23
24
  HiveSocketAuthError: () => HiveSocketAuthError,
24
25
  HiveSocketClient: () => HiveSocketClient,
25
26
  HiveSocketError: () => HiveSocketError,
@@ -99,9 +100,54 @@ var NotifyResource = class {
99
100
  }
100
101
  };
101
102
 
103
+ // src/resources/broadcast.ts
104
+ var BroadcastResource = class {
105
+ constructor(baseUrl, apiKey, timeout) {
106
+ this.baseUrl = baseUrl;
107
+ this.apiKey = apiKey;
108
+ this.timeout = timeout;
109
+ }
110
+ baseUrl;
111
+ apiKey;
112
+ timeout;
113
+ /** Broadcast a real-time message to all sockets in a room (received via useMessages). */
114
+ async toRoom(roomId, payload) {
115
+ await this.post(`/internal/broadcast/room/${encodeURIComponent(roomId)}`, payload);
116
+ }
117
+ async post(path, body) {
118
+ const controller = new AbortController();
119
+ const timer = setTimeout(() => controller.abort(), this.timeout);
120
+ try {
121
+ const res = await fetch(`${this.baseUrl}${path}`, {
122
+ method: "POST",
123
+ headers: {
124
+ "content-type": "application/json",
125
+ "x-internal-api-key": this.apiKey
126
+ },
127
+ body: JSON.stringify(body),
128
+ signal: controller.signal
129
+ });
130
+ if (res.status === 401) throw new HiveSocketAuthError();
131
+ if (!res.ok) {
132
+ const text = await res.text().catch(() => res.statusText);
133
+ throw new HiveSocketError(`Hive-Socket broadcast failed: ${text}`, res.status);
134
+ }
135
+ } catch (err) {
136
+ if (err instanceof HiveSocketError) throw err;
137
+ const msg = err instanceof Error ? err.message : String(err);
138
+ throw new HiveSocketNetworkError(
139
+ controller.signal.aborted ? `Request timed out after ${this.timeout}ms` : msg
140
+ );
141
+ } finally {
142
+ clearTimeout(timer);
143
+ }
144
+ }
145
+ };
146
+
102
147
  // src/client.ts
103
148
  var HiveSocketClient = class {
104
149
  notify;
150
+ broadcast;
105
151
  baseUrl;
106
152
  apiKey;
107
153
  timeout;
@@ -110,6 +156,7 @@ var HiveSocketClient = class {
110
156
  this.apiKey = config.apiKey;
111
157
  this.timeout = config.timeout ?? 1e4;
112
158
  this.notify = new NotifyResource(this.baseUrl, this.apiKey, this.timeout);
159
+ this.broadcast = new BroadcastResource(this.baseUrl, this.apiKey, this.timeout);
113
160
  }
114
161
  /** Check the Hive-Socket server health and current connection count. */
115
162
  async health() {
@@ -137,6 +184,7 @@ function createHiveSocketClient(config) {
137
184
  }
138
185
  // Annotate the CommonJS export names for ESM import in node:
139
186
  0 && (module.exports = {
187
+ BroadcastResource,
140
188
  HiveSocketAuthError,
141
189
  HiveSocketClient,
142
190
  HiveSocketError,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/resources/notify.ts","../src/client.ts"],"sourcesContent":["export { HiveSocketClient, createHiveSocketClient } from './client.js';\nexport { NotifyResource } from './resources/notify.js';\nexport { HiveSocketError, HiveSocketAuthError, HiveSocketNetworkError } from './errors.js';\nexport type {\n HiveSocketConfig,\n HiveNotifyPayload,\n HiveHealthResponse,\n NotificationType,\n} from './types.js';\n","export class HiveSocketError extends Error {\n readonly statusCode: number;\n\n constructor(message: string, statusCode: number) {\n super(message);\n this.name = 'HiveSocketError';\n this.statusCode = statusCode;\n }\n}\n\n/** Thrown when the API key is wrong (HTTP 401) */\nexport class HiveSocketAuthError extends HiveSocketError {\n constructor(message = 'Invalid API key') {\n super(message, 401);\n this.name = 'HiveSocketAuthError';\n }\n}\n\n/** Thrown when the fetch fails entirely (network down, timeout) */\nexport class HiveSocketNetworkError extends HiveSocketError {\n constructor(message: string) {\n super(message, 0);\n this.name = 'HiveSocketNetworkError';\n }\n}\n","import { HiveSocketAuthError, HiveSocketError, HiveSocketNetworkError } from '../errors.js';\nimport type { HiveNotifyPayload } from '../types.js';\n\nexport class NotifyResource {\n constructor(\n private readonly baseUrl: string,\n private readonly apiKey: string,\n private readonly timeout: number,\n ) {}\n\n /** Push a notification to a specific user's connected sockets. */\n async user(userId: string, payload: HiveNotifyPayload): Promise<void> {\n await this.post(`/internal/notify/user/${encodeURIComponent(userId)}`, payload);\n }\n\n /** Broadcast a notification to all sockets in a room. */\n async room(roomId: string, payload: HiveNotifyPayload): Promise<void> {\n await this.post(`/internal/notify/room/${encodeURIComponent(roomId)}`, payload);\n }\n\n private async post(path: string, body: unknown): Promise<void> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const res = await fetch(`${this.baseUrl}${path}`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-internal-api-key': this.apiKey,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (res.status === 401) throw new HiveSocketAuthError();\n if (!res.ok) {\n const text = await res.text().catch(() => res.statusText);\n throw new HiveSocketError(`Hive-Socket request failed: ${text}`, res.status);\n }\n } catch (err) {\n if (err instanceof HiveSocketError) throw err;\n const msg = err instanceof Error ? err.message : String(err);\n throw new HiveSocketNetworkError(\n controller.signal.aborted ? `Request timed out after ${this.timeout}ms` : msg,\n );\n } finally {\n clearTimeout(timer);\n }\n }\n}\n","import { NotifyResource } from './resources/notify.js';\nimport { HiveSocketNetworkError } from './errors.js';\nimport type { HiveSocketConfig, HiveHealthResponse } from './types.js';\n\nexport class HiveSocketClient {\n readonly notify: NotifyResource;\n\n private readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly timeout: number;\n\n constructor(config: HiveSocketConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, '');\n this.apiKey = config.apiKey;\n this.timeout = config.timeout ?? 10_000;\n this.notify = new NotifyResource(this.baseUrl, this.apiKey, this.timeout);\n }\n\n /** Check the Hive-Socket server health and current connection count. */\n async health(): Promise<HiveHealthResponse> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const res = await fetch(`${this.baseUrl}/health`, {\n signal: controller.signal,\n });\n if (!res.ok) throw new HiveSocketNetworkError(`Health check failed: ${res.statusText}`);\n return res.json() as Promise<HiveHealthResponse>;\n } catch (err) {\n if (err instanceof HiveSocketNetworkError) throw err;\n const msg = err instanceof Error ? err.message : String(err);\n throw new HiveSocketNetworkError(\n controller.signal.aborted ? `Health check timed out after ${this.timeout}ms` : msg,\n );\n } finally {\n clearTimeout(timer);\n }\n }\n}\n\n/**\n * Create a Hive-Socket server-side client.\n *\n * @example\n * const hive = createHiveSocketClient({\n * baseUrl: process.env.HIVE_SOCKET_URL!,\n * apiKey: process.env.HIVE_SOCKET_API_KEY!,\n * });\n * await hive.notify.user(userId, { projectId, title: 'Hey', body: 'Msg', type: 'message' });\n */\nexport function createHiveSocketClient(config: HiveSocketConfig): HiveSocketClient {\n return new HiveSocketClient(config);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/resources/notify.ts","../src/resources/broadcast.ts","../src/client.ts"],"sourcesContent":["export { HiveSocketClient, createHiveSocketClient } from './client.js';\nexport { NotifyResource } from './resources/notify.js';\nexport { BroadcastResource } from './resources/broadcast.js';\nexport { HiveSocketError, HiveSocketAuthError, HiveSocketNetworkError } from './errors.js';\nexport type {\n HiveSocketConfig,\n HiveNotifyPayload,\n HiveBroadcastPayload,\n HiveHealthResponse,\n NotificationType,\n} from './types.js';\n","export class HiveSocketError extends Error {\n readonly statusCode: number;\n\n constructor(message: string, statusCode: number) {\n super(message);\n this.name = 'HiveSocketError';\n this.statusCode = statusCode;\n }\n}\n\n/** Thrown when the API key is wrong (HTTP 401) */\nexport class HiveSocketAuthError extends HiveSocketError {\n constructor(message = 'Invalid API key') {\n super(message, 401);\n this.name = 'HiveSocketAuthError';\n }\n}\n\n/** Thrown when the fetch fails entirely (network down, timeout) */\nexport class HiveSocketNetworkError extends HiveSocketError {\n constructor(message: string) {\n super(message, 0);\n this.name = 'HiveSocketNetworkError';\n }\n}\n","import { HiveSocketAuthError, HiveSocketError, HiveSocketNetworkError } from '../errors.js';\nimport type { HiveNotifyPayload } from '../types.js';\n\nexport class NotifyResource {\n constructor(\n private readonly baseUrl: string,\n private readonly apiKey: string,\n private readonly timeout: number,\n ) {}\n\n /** Push a notification to a specific user's connected sockets. */\n async user(userId: string, payload: HiveNotifyPayload): Promise<void> {\n await this.post(`/internal/notify/user/${encodeURIComponent(userId)}`, payload);\n }\n\n /** Broadcast a notification to all sockets in a room. */\n async room(roomId: string, payload: HiveNotifyPayload): Promise<void> {\n await this.post(`/internal/notify/room/${encodeURIComponent(roomId)}`, payload);\n }\n\n private async post(path: string, body: unknown): Promise<void> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const res = await fetch(`${this.baseUrl}${path}`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-internal-api-key': this.apiKey,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (res.status === 401) throw new HiveSocketAuthError();\n if (!res.ok) {\n const text = await res.text().catch(() => res.statusText);\n throw new HiveSocketError(`Hive-Socket request failed: ${text}`, res.status);\n }\n } catch (err) {\n if (err instanceof HiveSocketError) throw err;\n const msg = err instanceof Error ? err.message : String(err);\n throw new HiveSocketNetworkError(\n controller.signal.aborted ? `Request timed out after ${this.timeout}ms` : msg,\n );\n } finally {\n clearTimeout(timer);\n }\n }\n}\n","import { HiveSocketAuthError, HiveSocketError, HiveSocketNetworkError } from '../errors.js';\n\nexport class BroadcastResource {\n constructor(\n private readonly baseUrl: string,\n private readonly apiKey: string,\n private readonly timeout: number,\n ) {}\n\n /** Broadcast a real-time message to all sockets in a room (received via useMessages). */\n async toRoom(\n roomId: string,\n payload: { projectId: string; senderId?: string; type: string; payload: Record<string, unknown> },\n ): Promise<void> {\n await this.post(`/internal/broadcast/room/${encodeURIComponent(roomId)}`, payload);\n }\n\n private async post(path: string, body: unknown): Promise<void> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const res = await fetch(`${this.baseUrl}${path}`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-internal-api-key': this.apiKey,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (res.status === 401) throw new HiveSocketAuthError();\n if (!res.ok) {\n const text = await res.text().catch(() => res.statusText);\n throw new HiveSocketError(`Hive-Socket broadcast failed: ${text}`, res.status);\n }\n } catch (err) {\n if (err instanceof HiveSocketError) throw err;\n const msg = err instanceof Error ? err.message : String(err);\n throw new HiveSocketNetworkError(\n controller.signal.aborted ? `Request timed out after ${this.timeout}ms` : msg,\n );\n } finally {\n clearTimeout(timer);\n }\n }\n}\n","import { NotifyResource } from './resources/notify.js';\nimport { BroadcastResource } from './resources/broadcast.js';\nimport { HiveSocketNetworkError } from './errors.js';\nimport type { HiveSocketConfig, HiveHealthResponse } from './types.js';\n\nexport class HiveSocketClient {\n readonly notify: NotifyResource;\n readonly broadcast: BroadcastResource;\n\n private readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly timeout: number;\n\n constructor(config: HiveSocketConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, '');\n this.apiKey = config.apiKey;\n this.timeout = config.timeout ?? 10_000;\n this.notify = new NotifyResource(this.baseUrl, this.apiKey, this.timeout);\n this.broadcast = new BroadcastResource(this.baseUrl, this.apiKey, this.timeout);\n }\n\n /** Check the Hive-Socket server health and current connection count. */\n async health(): Promise<HiveHealthResponse> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const res = await fetch(`${this.baseUrl}/health`, {\n signal: controller.signal,\n });\n if (!res.ok) throw new HiveSocketNetworkError(`Health check failed: ${res.statusText}`);\n return res.json() as Promise<HiveHealthResponse>;\n } catch (err) {\n if (err instanceof HiveSocketNetworkError) throw err;\n const msg = err instanceof Error ? err.message : String(err);\n throw new HiveSocketNetworkError(\n controller.signal.aborted ? `Health check timed out after ${this.timeout}ms` : msg,\n );\n } finally {\n clearTimeout(timer);\n }\n }\n}\n\n/**\n * Create a Hive-Socket server-side client.\n *\n * @example\n * const hive = createHiveSocketClient({\n * baseUrl: process.env.HIVE_SOCKET_URL!,\n * apiKey: process.env.HIVE_SOCKET_API_KEY!,\n * });\n * await hive.notify.user(userId, { projectId, title: 'Hey', body: 'Msg', type: 'message' });\n */\nexport function createHiveSocketClient(config: HiveSocketConfig): HiveSocketClient {\n return new HiveSocketClient(config);\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -8,6 +8,13 @@ interface HiveNotifyPayload {
8
8
  metadata?: Record<string, unknown>;
9
9
  sourceService?: string;
10
10
  }
11
+ /** Payload sent to POST /internal/broadcast/room/:roomId */
12
+ interface HiveBroadcastPayload {
13
+ projectId: string;
14
+ type: string;
15
+ payload: Record<string, unknown>;
16
+ senderId?: string;
17
+ }
11
18
  /** Response from GET /health */
12
19
  interface HiveHealthResponse {
13
20
  status: 'ok';
@@ -35,8 +42,24 @@ declare class NotifyResource {
35
42
  private post;
36
43
  }
37
44
 
45
+ declare class BroadcastResource {
46
+ private readonly baseUrl;
47
+ private readonly apiKey;
48
+ private readonly timeout;
49
+ constructor(baseUrl: string, apiKey: string, timeout: number);
50
+ /** Broadcast a real-time message to all sockets in a room (received via useMessages). */
51
+ toRoom(roomId: string, payload: {
52
+ projectId: string;
53
+ senderId?: string;
54
+ type: string;
55
+ payload: Record<string, unknown>;
56
+ }): Promise<void>;
57
+ private post;
58
+ }
59
+
38
60
  declare class HiveSocketClient {
39
61
  readonly notify: NotifyResource;
62
+ readonly broadcast: BroadcastResource;
40
63
  private readonly baseUrl;
41
64
  private readonly apiKey;
42
65
  private readonly timeout;
@@ -69,4 +92,4 @@ declare class HiveSocketNetworkError extends HiveSocketError {
69
92
  constructor(message: string);
70
93
  }
71
94
 
72
- export { type HiveHealthResponse, type HiveNotifyPayload, HiveSocketAuthError, HiveSocketClient, type HiveSocketConfig, HiveSocketError, HiveSocketNetworkError, type NotificationType, NotifyResource, createHiveSocketClient };
95
+ export { BroadcastResource, type HiveBroadcastPayload, type HiveHealthResponse, type HiveNotifyPayload, HiveSocketAuthError, HiveSocketClient, type HiveSocketConfig, HiveSocketError, HiveSocketNetworkError, type NotificationType, NotifyResource, createHiveSocketClient };
package/dist/index.d.ts CHANGED
@@ -8,6 +8,13 @@ interface HiveNotifyPayload {
8
8
  metadata?: Record<string, unknown>;
9
9
  sourceService?: string;
10
10
  }
11
+ /** Payload sent to POST /internal/broadcast/room/:roomId */
12
+ interface HiveBroadcastPayload {
13
+ projectId: string;
14
+ type: string;
15
+ payload: Record<string, unknown>;
16
+ senderId?: string;
17
+ }
11
18
  /** Response from GET /health */
12
19
  interface HiveHealthResponse {
13
20
  status: 'ok';
@@ -35,8 +42,24 @@ declare class NotifyResource {
35
42
  private post;
36
43
  }
37
44
 
45
+ declare class BroadcastResource {
46
+ private readonly baseUrl;
47
+ private readonly apiKey;
48
+ private readonly timeout;
49
+ constructor(baseUrl: string, apiKey: string, timeout: number);
50
+ /** Broadcast a real-time message to all sockets in a room (received via useMessages). */
51
+ toRoom(roomId: string, payload: {
52
+ projectId: string;
53
+ senderId?: string;
54
+ type: string;
55
+ payload: Record<string, unknown>;
56
+ }): Promise<void>;
57
+ private post;
58
+ }
59
+
38
60
  declare class HiveSocketClient {
39
61
  readonly notify: NotifyResource;
62
+ readonly broadcast: BroadcastResource;
40
63
  private readonly baseUrl;
41
64
  private readonly apiKey;
42
65
  private readonly timeout;
@@ -69,4 +92,4 @@ declare class HiveSocketNetworkError extends HiveSocketError {
69
92
  constructor(message: string);
70
93
  }
71
94
 
72
- export { type HiveHealthResponse, type HiveNotifyPayload, HiveSocketAuthError, HiveSocketClient, type HiveSocketConfig, HiveSocketError, HiveSocketNetworkError, type NotificationType, NotifyResource, createHiveSocketClient };
95
+ export { BroadcastResource, type HiveBroadcastPayload, type HiveHealthResponse, type HiveNotifyPayload, HiveSocketAuthError, HiveSocketClient, type HiveSocketConfig, HiveSocketError, HiveSocketNetworkError, type NotificationType, NotifyResource, createHiveSocketClient };
package/dist/index.js CHANGED
@@ -68,9 +68,54 @@ var NotifyResource = class {
68
68
  }
69
69
  };
70
70
 
71
+ // src/resources/broadcast.ts
72
+ var BroadcastResource = class {
73
+ constructor(baseUrl, apiKey, timeout) {
74
+ this.baseUrl = baseUrl;
75
+ this.apiKey = apiKey;
76
+ this.timeout = timeout;
77
+ }
78
+ baseUrl;
79
+ apiKey;
80
+ timeout;
81
+ /** Broadcast a real-time message to all sockets in a room (received via useMessages). */
82
+ async toRoom(roomId, payload) {
83
+ await this.post(`/internal/broadcast/room/${encodeURIComponent(roomId)}`, payload);
84
+ }
85
+ async post(path, body) {
86
+ const controller = new AbortController();
87
+ const timer = setTimeout(() => controller.abort(), this.timeout);
88
+ try {
89
+ const res = await fetch(`${this.baseUrl}${path}`, {
90
+ method: "POST",
91
+ headers: {
92
+ "content-type": "application/json",
93
+ "x-internal-api-key": this.apiKey
94
+ },
95
+ body: JSON.stringify(body),
96
+ signal: controller.signal
97
+ });
98
+ if (res.status === 401) throw new HiveSocketAuthError();
99
+ if (!res.ok) {
100
+ const text = await res.text().catch(() => res.statusText);
101
+ throw new HiveSocketError(`Hive-Socket broadcast failed: ${text}`, res.status);
102
+ }
103
+ } catch (err) {
104
+ if (err instanceof HiveSocketError) throw err;
105
+ const msg = err instanceof Error ? err.message : String(err);
106
+ throw new HiveSocketNetworkError(
107
+ controller.signal.aborted ? `Request timed out after ${this.timeout}ms` : msg
108
+ );
109
+ } finally {
110
+ clearTimeout(timer);
111
+ }
112
+ }
113
+ };
114
+
71
115
  // src/client.ts
72
116
  var HiveSocketClient = class {
73
117
  notify;
118
+ broadcast;
74
119
  baseUrl;
75
120
  apiKey;
76
121
  timeout;
@@ -79,6 +124,7 @@ var HiveSocketClient = class {
79
124
  this.apiKey = config.apiKey;
80
125
  this.timeout = config.timeout ?? 1e4;
81
126
  this.notify = new NotifyResource(this.baseUrl, this.apiKey, this.timeout);
127
+ this.broadcast = new BroadcastResource(this.baseUrl, this.apiKey, this.timeout);
82
128
  }
83
129
  /** Check the Hive-Socket server health and current connection count. */
84
130
  async health() {
@@ -105,6 +151,7 @@ function createHiveSocketClient(config) {
105
151
  return new HiveSocketClient(config);
106
152
  }
107
153
  export {
154
+ BroadcastResource,
108
155
  HiveSocketAuthError,
109
156
  HiveSocketClient,
110
157
  HiveSocketError,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/resources/notify.ts","../src/client.ts"],"sourcesContent":["export class HiveSocketError extends Error {\n readonly statusCode: number;\n\n constructor(message: string, statusCode: number) {\n super(message);\n this.name = 'HiveSocketError';\n this.statusCode = statusCode;\n }\n}\n\n/** Thrown when the API key is wrong (HTTP 401) */\nexport class HiveSocketAuthError extends HiveSocketError {\n constructor(message = 'Invalid API key') {\n super(message, 401);\n this.name = 'HiveSocketAuthError';\n }\n}\n\n/** Thrown when the fetch fails entirely (network down, timeout) */\nexport class HiveSocketNetworkError extends HiveSocketError {\n constructor(message: string) {\n super(message, 0);\n this.name = 'HiveSocketNetworkError';\n }\n}\n","import { HiveSocketAuthError, HiveSocketError, HiveSocketNetworkError } from '../errors.js';\nimport type { HiveNotifyPayload } from '../types.js';\n\nexport class NotifyResource {\n constructor(\n private readonly baseUrl: string,\n private readonly apiKey: string,\n private readonly timeout: number,\n ) {}\n\n /** Push a notification to a specific user's connected sockets. */\n async user(userId: string, payload: HiveNotifyPayload): Promise<void> {\n await this.post(`/internal/notify/user/${encodeURIComponent(userId)}`, payload);\n }\n\n /** Broadcast a notification to all sockets in a room. */\n async room(roomId: string, payload: HiveNotifyPayload): Promise<void> {\n await this.post(`/internal/notify/room/${encodeURIComponent(roomId)}`, payload);\n }\n\n private async post(path: string, body: unknown): Promise<void> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const res = await fetch(`${this.baseUrl}${path}`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-internal-api-key': this.apiKey,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (res.status === 401) throw new HiveSocketAuthError();\n if (!res.ok) {\n const text = await res.text().catch(() => res.statusText);\n throw new HiveSocketError(`Hive-Socket request failed: ${text}`, res.status);\n }\n } catch (err) {\n if (err instanceof HiveSocketError) throw err;\n const msg = err instanceof Error ? err.message : String(err);\n throw new HiveSocketNetworkError(\n controller.signal.aborted ? `Request timed out after ${this.timeout}ms` : msg,\n );\n } finally {\n clearTimeout(timer);\n }\n }\n}\n","import { NotifyResource } from './resources/notify.js';\nimport { HiveSocketNetworkError } from './errors.js';\nimport type { HiveSocketConfig, HiveHealthResponse } from './types.js';\n\nexport class HiveSocketClient {\n readonly notify: NotifyResource;\n\n private readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly timeout: number;\n\n constructor(config: HiveSocketConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, '');\n this.apiKey = config.apiKey;\n this.timeout = config.timeout ?? 10_000;\n this.notify = new NotifyResource(this.baseUrl, this.apiKey, this.timeout);\n }\n\n /** Check the Hive-Socket server health and current connection count. */\n async health(): Promise<HiveHealthResponse> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const res = await fetch(`${this.baseUrl}/health`, {\n signal: controller.signal,\n });\n if (!res.ok) throw new HiveSocketNetworkError(`Health check failed: ${res.statusText}`);\n return res.json() as Promise<HiveHealthResponse>;\n } catch (err) {\n if (err instanceof HiveSocketNetworkError) throw err;\n const msg = err instanceof Error ? err.message : String(err);\n throw new HiveSocketNetworkError(\n controller.signal.aborted ? `Health check timed out after ${this.timeout}ms` : msg,\n );\n } finally {\n clearTimeout(timer);\n }\n }\n}\n\n/**\n * Create a Hive-Socket server-side client.\n *\n * @example\n * const hive = createHiveSocketClient({\n * baseUrl: process.env.HIVE_SOCKET_URL!,\n * apiKey: process.env.HIVE_SOCKET_API_KEY!,\n * });\n * await hive.notify.user(userId, { projectId, title: 'Hey', body: 'Msg', type: 'message' });\n */\nexport function createHiveSocketClient(config: HiveSocketConfig): HiveSocketClient {\n return new HiveSocketClient(config);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/resources/notify.ts","../src/resources/broadcast.ts","../src/client.ts"],"sourcesContent":["export class HiveSocketError extends Error {\n readonly statusCode: number;\n\n constructor(message: string, statusCode: number) {\n super(message);\n this.name = 'HiveSocketError';\n this.statusCode = statusCode;\n }\n}\n\n/** Thrown when the API key is wrong (HTTP 401) */\nexport class HiveSocketAuthError extends HiveSocketError {\n constructor(message = 'Invalid API key') {\n super(message, 401);\n this.name = 'HiveSocketAuthError';\n }\n}\n\n/** Thrown when the fetch fails entirely (network down, timeout) */\nexport class HiveSocketNetworkError extends HiveSocketError {\n constructor(message: string) {\n super(message, 0);\n this.name = 'HiveSocketNetworkError';\n }\n}\n","import { HiveSocketAuthError, HiveSocketError, HiveSocketNetworkError } from '../errors.js';\nimport type { HiveNotifyPayload } from '../types.js';\n\nexport class NotifyResource {\n constructor(\n private readonly baseUrl: string,\n private readonly apiKey: string,\n private readonly timeout: number,\n ) {}\n\n /** Push a notification to a specific user's connected sockets. */\n async user(userId: string, payload: HiveNotifyPayload): Promise<void> {\n await this.post(`/internal/notify/user/${encodeURIComponent(userId)}`, payload);\n }\n\n /** Broadcast a notification to all sockets in a room. */\n async room(roomId: string, payload: HiveNotifyPayload): Promise<void> {\n await this.post(`/internal/notify/room/${encodeURIComponent(roomId)}`, payload);\n }\n\n private async post(path: string, body: unknown): Promise<void> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const res = await fetch(`${this.baseUrl}${path}`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-internal-api-key': this.apiKey,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (res.status === 401) throw new HiveSocketAuthError();\n if (!res.ok) {\n const text = await res.text().catch(() => res.statusText);\n throw new HiveSocketError(`Hive-Socket request failed: ${text}`, res.status);\n }\n } catch (err) {\n if (err instanceof HiveSocketError) throw err;\n const msg = err instanceof Error ? err.message : String(err);\n throw new HiveSocketNetworkError(\n controller.signal.aborted ? `Request timed out after ${this.timeout}ms` : msg,\n );\n } finally {\n clearTimeout(timer);\n }\n }\n}\n","import { HiveSocketAuthError, HiveSocketError, HiveSocketNetworkError } from '../errors.js';\n\nexport class BroadcastResource {\n constructor(\n private readonly baseUrl: string,\n private readonly apiKey: string,\n private readonly timeout: number,\n ) {}\n\n /** Broadcast a real-time message to all sockets in a room (received via useMessages). */\n async toRoom(\n roomId: string,\n payload: { projectId: string; senderId?: string; type: string; payload: Record<string, unknown> },\n ): Promise<void> {\n await this.post(`/internal/broadcast/room/${encodeURIComponent(roomId)}`, payload);\n }\n\n private async post(path: string, body: unknown): Promise<void> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const res = await fetch(`${this.baseUrl}${path}`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-internal-api-key': this.apiKey,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (res.status === 401) throw new HiveSocketAuthError();\n if (!res.ok) {\n const text = await res.text().catch(() => res.statusText);\n throw new HiveSocketError(`Hive-Socket broadcast failed: ${text}`, res.status);\n }\n } catch (err) {\n if (err instanceof HiveSocketError) throw err;\n const msg = err instanceof Error ? err.message : String(err);\n throw new HiveSocketNetworkError(\n controller.signal.aborted ? `Request timed out after ${this.timeout}ms` : msg,\n );\n } finally {\n clearTimeout(timer);\n }\n }\n}\n","import { NotifyResource } from './resources/notify.js';\nimport { BroadcastResource } from './resources/broadcast.js';\nimport { HiveSocketNetworkError } from './errors.js';\nimport type { HiveSocketConfig, HiveHealthResponse } from './types.js';\n\nexport class HiveSocketClient {\n readonly notify: NotifyResource;\n readonly broadcast: BroadcastResource;\n\n private readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly timeout: number;\n\n constructor(config: HiveSocketConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, '');\n this.apiKey = config.apiKey;\n this.timeout = config.timeout ?? 10_000;\n this.notify = new NotifyResource(this.baseUrl, this.apiKey, this.timeout);\n this.broadcast = new BroadcastResource(this.baseUrl, this.apiKey, this.timeout);\n }\n\n /** Check the Hive-Socket server health and current connection count. */\n async health(): Promise<HiveHealthResponse> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const res = await fetch(`${this.baseUrl}/health`, {\n signal: controller.signal,\n });\n if (!res.ok) throw new HiveSocketNetworkError(`Health check failed: ${res.statusText}`);\n return res.json() as Promise<HiveHealthResponse>;\n } catch (err) {\n if (err instanceof HiveSocketNetworkError) throw err;\n const msg = err instanceof Error ? err.message : String(err);\n throw new HiveSocketNetworkError(\n controller.signal.aborted ? `Health check timed out after ${this.timeout}ms` : msg,\n );\n } finally {\n clearTimeout(timer);\n }\n }\n}\n\n/**\n * Create a Hive-Socket server-side client.\n *\n * @example\n * const hive = createHiveSocketClient({\n * baseUrl: process.env.HIVE_SOCKET_URL!,\n * apiKey: process.env.HIVE_SOCKET_API_KEY!,\n * });\n * await hive.notify.user(userId, { projectId, title: 'Hey', body: 'Msg', type: 'message' });\n */\nexport function createHiveSocketClient(config: HiveSocketConfig): HiveSocketClient {\n return new HiveSocketClient(config);\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@smarthivelabs-devs/hive-socket-node",
3
- "version": "1.0.0",
4
- "description": "Node.js SDK for Hive-Socket — push notifications and events to connected clients",
3
+ "version": "1.1.0",
4
+ "description": "Node.js SDK for Hive-Socket — push notifications, events, and real-time messages to connected clients",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
7
7
  "module": "./dist/index.js",