@droponair/sdk-js 0.5.0 → 0.6.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 +35 -0
- package/README.md +45 -0
- package/dist/core/messaging-client.d.ts +4 -0
- package/dist/core/messaging-client.js +24 -0
- package/dist/core/types.d.ts +22 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,41 @@ This project follows [Semantic Versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [0.6.0], 2026-05-18
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **Screen sharing signaling.** New client APIs and event types so apps can
|
|
13
|
+
coordinate the start/stop of a screen-share track on top of the existing
|
|
14
|
+
WebRTC call. The SDK only signals - capture (`getDisplayMedia()` on web) and
|
|
15
|
+
track management remain in the app layer.
|
|
16
|
+
- **1:1 calls:**
|
|
17
|
+
- `client.startScreenShare(callId, payload?)` and `client.stopScreenShare(callId, payload?)`.
|
|
18
|
+
- New `CallEventType` values: `'CALL_SCREEN_SHARE_STARTED'`, `'CALL_SCREEN_SHARE_STOPPED'`.
|
|
19
|
+
- **Group calls:**
|
|
20
|
+
- `client.startGroupScreenShare(callId, groupId, payload?)` and `client.stopGroupScreenShare(callId, groupId, payload?)`.
|
|
21
|
+
- New `GroupCallEventType` values: `'GROUP_CALL_SCREEN_SHARE_STARTED'`, `'GROUP_CALL_SCREEN_SHARE_STOPPED'`.
|
|
22
|
+
- Server enforces a single concurrent sharer per group call. If another
|
|
23
|
+
participant already holds the slot when you call `startGroupScreenShare`,
|
|
24
|
+
you'll receive a `GROUP_CALL_SCREEN_SHARE_STOPPED` event with the current
|
|
25
|
+
holder's userId in `payload`, so the app can roll back its optimistic UI.
|
|
26
|
+
- When a sharer leaves the call, the server broadcasts a STOPPED event to
|
|
27
|
+
remaining participants before they see `PARTICIPANT_LEFT`.
|
|
28
|
+
- `GET /api/info` now advertises `"screen_sharing"` in the `features` array.
|
|
29
|
+
|
|
30
|
+
### Notes
|
|
31
|
+
- Wire-level additive: legacy 0.5.x clients ignore the new signal type strings
|
|
32
|
+
(string discriminators on existing `CallFrame.type` / `GroupCallFrame.type`).
|
|
33
|
+
No proto schema change, no `PROTOCOL_VERSION` bump.
|
|
34
|
+
- Screen content is opaque WebRTC media; the server never sees it. Use
|
|
35
|
+
`getDisplayMedia()` (web) / `MediaProjection` (Android) / `ReplayKit` (iOS)
|
|
36
|
+
in the app, add the track to the existing peer connection, then call
|
|
37
|
+
`startScreenShare(...)` to notify the peer.
|
|
38
|
+
- Plan gate: `screen_sharing` is enabled on PRO/GROWTH/PAYG/ENTERPRISE and
|
|
39
|
+
disabled on FREE. Check `subscription.featuresEnabled.screen_sharing` (server
|
|
40
|
+
side, on the customer's backend).
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
9
44
|
## [0.5.0], 2026-05-18
|
|
10
45
|
|
|
11
46
|
### Added
|
package/README.md
CHANGED
|
@@ -209,6 +209,51 @@ client.onMessage(async (msg) => {
|
|
|
209
209
|
| `sendGroupCallSignal(type, callId, groupId, targetUserId, payload)` | `void` | Send SDP/ICE to a peer |
|
|
210
210
|
| `onGroupCallEvent(callback)` | `() => void` | Listen for group call events |
|
|
211
211
|
|
|
212
|
+
### Screen Sharing
|
|
213
|
+
|
|
214
|
+
Available since SDK `0.6.0`. The SDK only signals start/stop - capture (via the browser's `getDisplayMedia()`) and adding the resulting `MediaStreamTrack` to the existing peer connection are the app's responsibility.
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
// 1:1 call
|
|
218
|
+
async function shareMyScreen(callId: string, peerConnection: RTCPeerConnection) {
|
|
219
|
+
const stream = await navigator.mediaDevices.getDisplayMedia({ video: true });
|
|
220
|
+
const track = stream.getVideoTracks()[0];
|
|
221
|
+
peerConnection.addTrack(track, stream);
|
|
222
|
+
client.startScreenShare(callId);
|
|
223
|
+
track.onended = () => client.stopScreenShare(callId);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Group call
|
|
227
|
+
async function shareMyScreenInGroup(callId: string, groupId: string, peerConnections: RTCPeerConnection[]) {
|
|
228
|
+
const stream = await navigator.mediaDevices.getDisplayMedia({ video: true });
|
|
229
|
+
const track = stream.getVideoTracks()[0];
|
|
230
|
+
peerConnections.forEach(pc => pc.addTrack(track, stream));
|
|
231
|
+
client.startGroupScreenShare(callId, groupId);
|
|
232
|
+
track.onended = () => client.stopGroupScreenShare(callId, groupId);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
client.onCallEvent((evt) => {
|
|
236
|
+
if (evt.type === 'CALL_SCREEN_SHARE_STARTED') showShareIndicator(evt.callId);
|
|
237
|
+
if (evt.type === 'CALL_SCREEN_SHARE_STOPPED') hideShareIndicator(evt.callId);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
client.onGroupCallEvent((evt) => {
|
|
241
|
+
if (evt.type === 'GROUP_CALL_SCREEN_SHARE_STARTED') showShareIndicator(evt.callId, evt.payload); // payload = sharer userId
|
|
242
|
+
if (evt.type === 'GROUP_CALL_SCREEN_SHARE_STOPPED') hideShareIndicator(evt.callId);
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
| Method | Returns | Description |
|
|
247
|
+
|--------|---------|-------------|
|
|
248
|
+
| `startScreenShare(callId, payload?)` | `void` | Signal start in a 1:1 call |
|
|
249
|
+
| `stopScreenShare(callId, payload?)` | `void` | Signal stop in a 1:1 call |
|
|
250
|
+
| `startGroupScreenShare(callId, groupId, payload?)` | `void` | Signal start in a group call |
|
|
251
|
+
| `stopGroupScreenShare(callId, groupId, payload?)` | `void` | Signal stop in a group call |
|
|
252
|
+
|
|
253
|
+
- The server enforces **one concurrent sharer per group call**. If another participant already holds the slot when you call `startGroupScreenShare`, the SDK delivers a `GROUP_CALL_SCREEN_SHARE_STOPPED` event with the current holder's userId in `payload`, so you can roll back the optimistic UI.
|
|
254
|
+
- When a sharer leaves the call, the server broadcasts a STOPPED event to remaining participants before they see `PARTICIPANT_LEFT`.
|
|
255
|
+
- Plan gate: `screen_sharing` is on `PRO`/`GROWTH`/`PAYG`/`ENTERPRISE`, off on `FREE`. Read `subscription.featuresEnabled.screen_sharing` on your backend to decide whether to expose the share button.
|
|
256
|
+
|
|
212
257
|
## Security
|
|
213
258
|
|
|
214
259
|
- **X25519 ECDH** key agreement for shared secrets
|
|
@@ -136,6 +136,8 @@ export declare class MessagingClient implements DropOnAirClient {
|
|
|
136
136
|
leaveGroupCall(callId: string): Promise<void>;
|
|
137
137
|
endGroupCall(callId: string): Promise<void>;
|
|
138
138
|
sendGroupCallSignal(type: 'GROUP_CALL_SDP_OFFER' | 'GROUP_CALL_SDP_ANSWER' | 'GROUP_CALL_ICE_CANDIDATE', callId: string, groupId: string, targetUserId: string, payload: string): void;
|
|
139
|
+
startGroupScreenShare(callId: string, groupId: string, payload?: string): void;
|
|
140
|
+
stopGroupScreenShare(callId: string, groupId: string, payload?: string): void;
|
|
139
141
|
onGroupCallEvent(callback: GroupCallEventCallback): () => void;
|
|
140
142
|
private sendGroupCallFrame;
|
|
141
143
|
/**
|
|
@@ -149,6 +151,8 @@ export declare class MessagingClient implements DropOnAirClient {
|
|
|
149
151
|
endCall(callId: string): Promise<void>;
|
|
150
152
|
toggleVideo(callId: string, enabled: boolean): void;
|
|
151
153
|
sendCallSignal(type: 'CALL_SDP_OFFER' | 'CALL_SDP_ANSWER' | 'CALL_ICE_CANDIDATE', callId: string, payload: string): void;
|
|
154
|
+
startScreenShare(callId: string, payload?: string): void;
|
|
155
|
+
stopScreenShare(callId: string, payload?: string): void;
|
|
152
156
|
fetchTurnCredentials(): Promise<TurnCredentials>;
|
|
153
157
|
private sendCallFrame;
|
|
154
158
|
private emitCallEvent;
|
|
@@ -740,6 +740,24 @@ class MessagingClient {
|
|
|
740
740
|
sendGroupCallSignal(type, callId, groupId, targetUserId, payload) {
|
|
741
741
|
this.sendGroupCallFrame({ type, callId, groupId, targetUserId, payload });
|
|
742
742
|
}
|
|
743
|
+
startGroupScreenShare(callId, groupId, payload) {
|
|
744
|
+
this.sendGroupCallFrame({
|
|
745
|
+
type: 'GROUP_CALL_SCREEN_SHARE_STARTED',
|
|
746
|
+
callId,
|
|
747
|
+
groupId,
|
|
748
|
+
targetUserId: '',
|
|
749
|
+
payload: payload ?? '',
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
stopGroupScreenShare(callId, groupId, payload) {
|
|
753
|
+
this.sendGroupCallFrame({
|
|
754
|
+
type: 'GROUP_CALL_SCREEN_SHARE_STOPPED',
|
|
755
|
+
callId,
|
|
756
|
+
groupId,
|
|
757
|
+
targetUserId: '',
|
|
758
|
+
payload: payload ?? '',
|
|
759
|
+
});
|
|
760
|
+
}
|
|
743
761
|
onGroupCallEvent(callback) {
|
|
744
762
|
this.groupCallListeners.add(callback);
|
|
745
763
|
return () => this.groupCallListeners.delete(callback);
|
|
@@ -781,6 +799,12 @@ class MessagingClient {
|
|
|
781
799
|
sendCallSignal(type, callId, payload) {
|
|
782
800
|
this.sendCallFrame({ type, callId, payload });
|
|
783
801
|
}
|
|
802
|
+
startScreenShare(callId, payload) {
|
|
803
|
+
this.sendCallFrame({ type: 'CALL_SCREEN_SHARE_STARTED', callId, payload: payload ?? '' });
|
|
804
|
+
}
|
|
805
|
+
stopScreenShare(callId, payload) {
|
|
806
|
+
this.sendCallFrame({ type: 'CALL_SCREEN_SHARE_STOPPED', callId, payload: payload ?? '' });
|
|
807
|
+
}
|
|
784
808
|
async fetchTurnCredentials() {
|
|
785
809
|
const jwt = await this.getValidDropOnAirJwt(false);
|
|
786
810
|
const url = `${this.httpUrl}/api/v1/turn/credentials`;
|
package/dist/core/types.d.ts
CHANGED
|
@@ -57,7 +57,7 @@ export interface BroadcastMessage {
|
|
|
57
57
|
sequenceNumber: number;
|
|
58
58
|
}
|
|
59
59
|
export type BroadcastCallback = (message: BroadcastMessage) => void;
|
|
60
|
-
export type CallEventType = 'CALL_INVITE' | 'CALL_RINGING' | 'CALL_ACCEPTED' | 'CALL_REJECTED' | 'CALL_ENDED' | 'CALL_CANCELLED' | 'CALL_SDP_OFFER' | 'CALL_SDP_ANSWER' | 'CALL_ICE_CANDIDATE' | 'CALL_VIDEO_TOGGLE' | 'CALL_DENIED_LIMIT_REACHED';
|
|
60
|
+
export type CallEventType = 'CALL_INVITE' | 'CALL_RINGING' | 'CALL_ACCEPTED' | 'CALL_REJECTED' | 'CALL_ENDED' | 'CALL_CANCELLED' | 'CALL_SDP_OFFER' | 'CALL_SDP_ANSWER' | 'CALL_ICE_CANDIDATE' | 'CALL_VIDEO_TOGGLE' | 'CALL_SCREEN_SHARE_STARTED' | 'CALL_SCREEN_SHARE_STOPPED' | 'CALL_DENIED_LIMIT_REACHED';
|
|
61
61
|
export interface CallEvent {
|
|
62
62
|
type: CallEventType | string;
|
|
63
63
|
callId?: string;
|
|
@@ -92,7 +92,7 @@ export interface DecryptedGroupMessage {
|
|
|
92
92
|
plaintext: string;
|
|
93
93
|
}
|
|
94
94
|
export type GroupMessageCallback = (message: DecryptedGroupMessage) => void;
|
|
95
|
-
export type GroupCallEventType = 'GROUP_CALL_INVITE' | 'GROUP_CALL_RINGING' | 'GROUP_CALL_JOIN' | 'GROUP_CALL_LEAVE' | 'GROUP_CALL_END' | 'GROUP_CALL_ENDED' | 'GROUP_CALL_PARTICIPANT_JOINED' | 'GROUP_CALL_PARTICIPANT_LEFT' | 'GROUP_CALL_ALREADY_ACTIVE' | 'GROUP_CALL_SDP_OFFER' | 'GROUP_CALL_SDP_ANSWER' | 'GROUP_CALL_ICE_CANDIDATE';
|
|
95
|
+
export type GroupCallEventType = 'GROUP_CALL_INVITE' | 'GROUP_CALL_RINGING' | 'GROUP_CALL_JOIN' | 'GROUP_CALL_LEAVE' | 'GROUP_CALL_END' | 'GROUP_CALL_ENDED' | 'GROUP_CALL_PARTICIPANT_JOINED' | 'GROUP_CALL_PARTICIPANT_LEFT' | 'GROUP_CALL_ALREADY_ACTIVE' | 'GROUP_CALL_SDP_OFFER' | 'GROUP_CALL_SDP_ANSWER' | 'GROUP_CALL_ICE_CANDIDATE' | 'GROUP_CALL_SCREEN_SHARE_STARTED' | 'GROUP_CALL_SCREEN_SHARE_STOPPED';
|
|
96
96
|
export interface GroupCallEvent {
|
|
97
97
|
type: GroupCallEventType | string;
|
|
98
98
|
callId: string;
|
|
@@ -212,6 +212,16 @@ export interface DropOnAirClient {
|
|
|
212
212
|
* The payload is forwarded opaquely, the server does NOT inspect or store it.
|
|
213
213
|
*/
|
|
214
214
|
sendCallSignal(type: 'CALL_SDP_OFFER' | 'CALL_SDP_ANSWER' | 'CALL_ICE_CANDIDATE', callId: string, payload: string): void;
|
|
215
|
+
/**
|
|
216
|
+
* Signal that this user started sharing their screen in a 1:1 call
|
|
217
|
+
* (phase1/screen-sharing, PROTOCOL_VERSION 4+). The actual screen MediaStreamTrack
|
|
218
|
+
* is added to the existing peer connection by the application; the SDK only
|
|
219
|
+
* broadcasts the start/stop notification so the remote SDK can update its UI.
|
|
220
|
+
* Optional `payload` (JSON string) can carry track-id or app-specific metadata.
|
|
221
|
+
*/
|
|
222
|
+
startScreenShare(callId: string, payload?: string): void;
|
|
223
|
+
/** Signal that this user stopped sharing their screen in a 1:1 call. */
|
|
224
|
+
stopScreenShare(callId: string, payload?: string): void;
|
|
215
225
|
/** Register a listener for all incoming call events. Returns an unsubscribe function. */
|
|
216
226
|
onCallEvent(callback: CallEventCallback): () => void;
|
|
217
227
|
/** Fetch short-lived TURN credentials for ICE negotiation. */
|
|
@@ -244,6 +254,16 @@ export interface DropOnAirClient {
|
|
|
244
254
|
endGroupCall(callId: string): Promise<void>;
|
|
245
255
|
/** Send a signaling frame to a specific peer in a group call. */
|
|
246
256
|
sendGroupCallSignal(type: 'GROUP_CALL_SDP_OFFER' | 'GROUP_CALL_SDP_ANSWER' | 'GROUP_CALL_ICE_CANDIDATE', callId: string, groupId: string, targetUserId: string, payload: string): void;
|
|
257
|
+
/**
|
|
258
|
+
* Signal that this user started sharing their screen in a group call.
|
|
259
|
+
* The server enforces one concurrent sharer per group call; if another
|
|
260
|
+
* participant already holds the slot, the SDK will receive a
|
|
261
|
+
* `GROUP_CALL_SCREEN_SHARE_STOPPED` event echoing the current holder's
|
|
262
|
+
* userId in `payload` so the app can roll back its optimistic UI.
|
|
263
|
+
*/
|
|
264
|
+
startGroupScreenShare(callId: string, groupId: string, payload?: string): void;
|
|
265
|
+
/** Signal that this user stopped sharing their screen in a group call. */
|
|
266
|
+
stopGroupScreenShare(callId: string, groupId: string, payload?: string): void;
|
|
247
267
|
/** Register a listener for group call events. Returns an unsubscribe function. */
|
|
248
268
|
onGroupCallEvent(callback: GroupCallEventCallback): () => void;
|
|
249
269
|
}
|
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.
|
|
10
|
+
export declare const SDK_VERSION = "0.6.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.
|
|
13
|
+
exports.SDK_VERSION = '0.6.0';
|
|
14
14
|
/**
|
|
15
15
|
* Binary encrypted-payload format version.
|
|
16
16
|
* Included as the first byte of every encrypted payload so receivers can
|