@droponair/sdk-js 0.23.1 → 0.24.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/CHANGELOG.md CHANGED
@@ -6,6 +6,22 @@ This project follows [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  ---
8
8
 
9
+ ## [0.24.0], 2026-07-30
10
+
11
+ ### Added
12
+
13
+ - **`rejectGroupCall(callId, groupId)`** - decline an incoming group-call invite (sends `GROUP_CALL_REJECTED`). Brings the JS SDK to parity with the Android, iOS, and Unity SDKs, which already exposed it.
14
+
15
+ ---
16
+
17
+ ## [0.23.2], 2026-06-04
18
+
19
+ ### Fixed
20
+
21
+ - **Status acks (SERVER_RECEIVED / DELIVERED / PROCESSED) were mis-decoded and never reached the app.** Inbound `Ack {messageId, type}` frames were being decoded as `GroupMessageNotification` (the ack's `type` lands in the notification's `groupId`, field 2) because the group-notification / group-ack / broadcast decode guards only checked fields 1-2, which an ack satisfies. They now also require field 3 (`fromUserId` / `type` / `publisherId`), which every real group/broadcast frame carries but an ack lacks, so acks fall through to the ack decoder. This regressed in 0.22.1: making `GroupMessageNotification` decodable (the AttachmentRef fix) removed the decode-time throw that previously let acks fall through. Net effect of the bug: a sender's message never advanced past "sending" even though it was delivered. Also hardened the inbound frame handler to never drop the channel on an undecodable frame.
22
+
23
+ ---
24
+
9
25
  ## [0.23.1], 2026-06-01
10
26
 
11
27
  ### Fixed
package/README.md CHANGED
@@ -294,6 +294,7 @@ client.onMessage(async (msg) => {
294
294
  |--------|---------|-------------|
295
295
  | `startGroupCall(groupId)` | `Promise<string>` | Start a group call, returns callId |
296
296
  | `joinGroupCall(callId, groupId)` | `Promise<void>` | Join an active group call |
297
+ | `rejectGroupCall(callId, groupId)` | `Promise<void>` | Decline an incoming group call invite |
297
298
  | `leaveGroupCall(callId)` | `Promise<void>` | Leave a group call |
298
299
  | `endGroupCall(callId)` | `Promise<void>` | End a group call for all |
299
300
  | `sendGroupCallSignal(type, callId, groupId, targetUserId, payload)` | `void` | Send SDP/ICE to a peer |
@@ -292,6 +292,8 @@ export declare class MessagingClient implements DropOnAirClient {
292
292
  onGroupMessage(callback: GroupMessageCallback): () => void;
293
293
  startGroupCall(groupId: string): Promise<string>;
294
294
  joinGroupCall(callId: string, groupId: string): Promise<void>;
295
+ /** Decline an incoming group-call invite. */
296
+ rejectGroupCall(callId: string, groupId: string): Promise<void>;
295
297
  leaveGroupCall(callId: string): Promise<void>;
296
298
  endGroupCall(callId: string): Promise<void>;
297
299
  sendGroupCallSignal(type: 'GROUP_CALL_SDP_OFFER' | 'GROUP_CALL_SDP_ANSWER' | 'GROUP_CALL_ICE_CANDIDATE', callId: string, groupId: string, targetUserId: string, payload: string): void;
@@ -1280,6 +1280,10 @@ class MessagingClient {
1280
1280
  async joinGroupCall(callId, groupId) {
1281
1281
  this.sendGroupCallFrame({ type: 'GROUP_CALL_JOIN', callId, groupId });
1282
1282
  }
1283
+ /** Decline an incoming group-call invite. */
1284
+ async rejectGroupCall(callId, groupId) {
1285
+ this.sendGroupCallFrame({ type: 'GROUP_CALL_REJECTED', callId, groupId });
1286
+ }
1283
1287
  async leaveGroupCall(callId) {
1284
1288
  this.sendGroupCallFrame({ type: 'GROUP_CALL_LEAVE', callId, groupId: '' });
1285
1289
  }
@@ -1779,7 +1783,16 @@ class MessagingClient {
1779
1783
  }
1780
1784
  });
1781
1785
  transport.onFrame(async (bytes) => {
1782
- const frame = this.codec.decodeFrame(bytes);
1786
+ // Defensive: a single undecodable/unknown frame must not reject the
1787
+ // handler (which would silently drop it and could wedge inbound state).
1788
+ let frame;
1789
+ try {
1790
+ frame = this.codec.decodeFrame(bytes);
1791
+ }
1792
+ catch (err) {
1793
+ this.logError('frame_decode_failed', { bytes: bytes.length, error: String(err?.message ?? err) });
1794
+ return;
1795
+ }
1783
1796
  if (frame.kind === 'event') {
1784
1797
  if (frame.data.type === 'LIMIT_REACHED') {
1785
1798
  this.rateLimited = true;
@@ -419,7 +419,10 @@ class ProtobufCodec {
419
419
  tryDecodeBroadcastNotification(payload) {
420
420
  try {
421
421
  const decoded = BroadcastNotificationType.decode(payload);
422
- if (!decoded.broadcastId || !decoded.channelId) {
422
+ // Require publisherId (field 3): an Ack {messageId(1), type(2)} otherwise
423
+ // decodes as a broadcast (type lands in channelId, field 3 empty). Real
424
+ // broadcasts always carry a publisherId.
425
+ if (!decoded.broadcastId || !decoded.channelId || !decoded.publisherId) {
423
426
  return null;
424
427
  }
425
428
  return {
@@ -435,7 +438,11 @@ class ProtobufCodec {
435
438
  tryDecodeGroupMessageNotification(payload) {
436
439
  try {
437
440
  const decoded = GroupMessageNotificationType.decode(payload);
438
- if (!decoded.messageId || !decoded.groupId) {
441
+ // Require fromUserId (field 3): an Ack {messageId(1), type(2)} otherwise
442
+ // decodes as a group notification (its type lands in groupId) and steals
443
+ // status acks before the Ack probe. Real group notifications always carry
444
+ // fromUserId; acks have no field 3.
445
+ if (!decoded.messageId || !decoded.groupId || !decoded.fromUserId) {
439
446
  return null;
440
447
  }
441
448
  return {
@@ -455,7 +462,10 @@ class ProtobufCodec {
455
462
  tryDecodeGroupAck(payload) {
456
463
  try {
457
464
  const decoded = GroupAckType.decode(payload);
458
- if (!decoded.messageId || !decoded.groupId) {
465
+ // Require type (field 3): an Ack {messageId(1), type(2)} otherwise decodes
466
+ // as a GroupAck (its type lands in groupId, field 3 stays empty). Real
467
+ // GroupAcks always carry a type.
468
+ if (!decoded.messageId || !decoded.groupId || !decoded.type) {
459
469
  return null;
460
470
  }
461
471
  return decoded;
package/dist/version.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * MINOR, additive feature (e.g. multi-device payloads, new call event type)
8
8
  * PATCH, bug-fix / perf improvement with no wire or API change
9
9
  */
10
- export declare const SDK_VERSION = "0.23.1";
10
+ export declare const SDK_VERSION = "0.24.0";
11
11
  /**
12
12
  * Binary encrypted-payload format version.
13
13
  * Included as the first byte of every encrypted payload so receivers can
package/dist/version.js CHANGED
@@ -10,7 +10,7 @@ exports.PROTOCOL_VERSION = exports.PAYLOAD_FORMAT_VERSION = exports.SDK_VERSION
10
10
  * MINOR, additive feature (e.g. multi-device payloads, new call event type)
11
11
  * PATCH, bug-fix / perf improvement with no wire or API change
12
12
  */
13
- exports.SDK_VERSION = '0.23.1';
13
+ exports.SDK_VERSION = '0.24.0';
14
14
  /**
15
15
  * Binary encrypted-payload format version.
16
16
  * Included as the first byte of every encrypted payload so receivers can
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@droponair/sdk-js",
3
- "version": "0.23.1",
3
+ "version": "0.24.0",
4
4
  "description": "DropOnAir SDK for end-to-end encrypted messaging",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",