@droponair/sdk-js 0.17.0 → 0.19.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,37 @@ This project follows [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  ---
8
8
 
9
+ ## [0.19.0], 2026-05-24
10
+
11
+ ### Added
12
+
13
+ - **SSE transport primitive (Phase 4.2.1).** New `SseTransport` class exposes the platform's HTTP-fallback lane (`/v1/transport/stream` + `/v1/transport/send`) as a small standalone client, for environments where WebSocket upgrades are blocked by corporate firewalls but HTTPS GET/POST work. EventSource-based receive (auto-reconnects), `fetch` POST for send. Bring your own protobuf decoder (use the codec already exported from the SDK).
14
+
15
+ ### Notes
16
+
17
+ - v1 is a primitive; full `init({ transport: 'sse' })` integration into `MessagingClient` lands in 4.2.1b. Customers who need the SSE lane today can use `SseTransport` directly alongside the existing WS client.
18
+ - WebSocket on `/ws` remains the default for all SDK calls; this addition is purely opt-in. Older code paths and existing apps are unaffected.
19
+
20
+ ---
21
+
22
+ ## [0.18.0], 2026-05-23
23
+
24
+ ### Added
25
+
26
+ - **SFU recording (Phase 3.7.5).** Server-side recording for SFU-mode rooms with `mediaEncryption: 'SFU'`. The platform's media server records the composite of every track in the room and uploads the finalized file directly to the customer's storage bucket - the platform never holds the recorded bytes. Storage destinations (S3 / GCS / Azure) are registered once in the panel; the SDK references each by an opaque `destinationId`.
27
+ - Three new client methods:
28
+ - `startSfuRecording(roomId, destinationId)` -> `SfuRecording`
29
+ - `stopSfuRecording(roomId, recordingId)` -> `SfuRecording`
30
+ - `listSfuRecordings(roomId)` -> `SfuRecording[]`
31
+ - New `SfuRecording` type exported from the package root.
32
+ - Completion is also observable via the new runtime webhooks: `sfu-recording.started`, `sfu-recording.completed`, `sfu-recording.failed`.
33
+
34
+ ### Notes
35
+
36
+ - Only rooms with both `policy.mediaMode: 'SFU'` and `policy.mediaEncryption: 'SFU'` are server-recordable; E2EE-encrypted SFU rooms cannot be recorded by design (the media server forwards traffic it cannot decrypt). For E2EE rooms, continue using the client-side `startRecording` (0.16.0).
37
+
38
+ ---
39
+
9
40
  ## [0.17.0], 2026-05-22
10
41
 
11
42
  ### Added
package/README.md CHANGED
@@ -313,6 +313,9 @@ Available since SDK `0.14.0`. A **room** is an addressable container a live mult
313
313
  | `joinRoom(roomId)` | `Promise<string>` | Join the room's live call, returns callId |
314
314
  | `leaveRoom(callId)` | `Promise<void>` | Leave the room's live call |
315
315
  | `getSfuToken(roomId)` | `Promise<SfuToken>` | Token to join the room's SFU media (SFU-mode rooms only) |
316
+ | `startSfuRecording(roomId, destinationId)` | `Promise<SfuRecording>` | Start server-side recording (SFU + `mediaEncryption: 'SFU'` only) |
317
+ | `stopSfuRecording(roomId, recordingId)` | `Promise<SfuRecording>` | Stop a running SFU recording |
318
+ | `listSfuRecordings(roomId)` | `Promise<SfuRecording[]>` | List SFU recordings for a room, newest first |
316
319
 
317
320
  Room policy (`RoomPolicy`, all optional): `waitingRoom` (non-hosts wait for host admit), `requireHost` (non-hosts cannot open the call), `maxParticipants` (per-room cap), `autoCloseWhenEmpty` (room flips to `CLOSED` when the last participant leaves), `mediaMode` (`'MESH'` default | `'SFU'`), `mediaEncryption` (`'E2EE'` default | `'SFU'`).
318
321
 
@@ -380,6 +383,20 @@ const sfu = await client.getSfuToken(room.roomId);
380
383
 
381
384
  Mesh remains the default; only switch to SFU once the participant count outgrows mesh or you specifically need server-side recording. The `getSfuToken` call returns `409` for mesh-mode rooms and `503` if the media server is not available.
382
385
 
386
+ ### SFU recording
387
+
388
+ Available since SDK `0.18.0`. Server-side recording for SFU-mode rooms with `mediaEncryption: 'SFU'`. The platform's media server captures the room composite and uploads the finalized file directly to your storage bucket (S3, GCS, or Azure); the platform never holds the recorded bytes. Storage destinations are registered in the panel; the SDK references each by an opaque `destinationId`.
389
+
390
+ ```typescript
391
+ const rec = await client.startSfuRecording(room.roomId, 'dst_aBc123');
392
+ // { recordingId, status: 'STARTING', startedAt, ... }
393
+
394
+ // Later, when the host ends the recording:
395
+ await client.stopSfuRecording(room.roomId, rec.recordingId);
396
+ ```
397
+
398
+ Completion lands as the `sfu-recording.completed` webhook (or by polling `listSfuRecordings`) and carries `locationUri` pointing at the finalized file in your bucket. E2EE-encrypted SFU rooms cannot be server-recorded by design (the media server forwards traffic it cannot decrypt) - use the client-side `startRecording` for those.
399
+
383
400
  ### Call Recording
384
401
 
385
402
  Available since SDK `0.16.0`. The SDK **signals** recording state on a group or room call; your app does the actual media capture (`MediaRecorder`) and uploads the file to your own storage — the platform never holds the media. The recording signal is broadcast to every participant (including anyone who joins later); that transparency is enforced server-side.
@@ -1,6 +1,6 @@
1
1
  import { CryptoService } from '../crypto/crypto-service';
2
2
  import { SessionManager } from './session-manager';
3
- import { BroadcastCallback, CallEventCallback, DropOnAirClient, EventCallback, CreateRoomOptions, GroupCallEventCallback, GroupInfo, GroupMessageCallback, Room, UpdateRoomOptions, SfuToken, InitializeOptions, MessageCallback, MessageDeleteCallback, MessageEditCallback, TurnCredentials, DeviceInfo, ReadReceiptCallback, NotificationClearCallback, DraftSyncCallback } from './types';
3
+ import { BroadcastCallback, CallEventCallback, DropOnAirClient, EventCallback, CreateRoomOptions, GroupCallEventCallback, GroupInfo, GroupMessageCallback, Room, UpdateRoomOptions, SfuToken, SfuRecording, InitializeOptions, MessageCallback, MessageDeleteCallback, MessageEditCallback, TurnCredentials, DeviceInfo, ReadReceiptCallback, NotificationClearCallback, DraftSyncCallback } from './types';
4
4
  import { AttachmentRef, CreateUploadSessionOptions, DownloadedAttachment, PrepareAttachmentOptions, UploadSession } from '../attachment/attachment-types';
5
5
  export declare class MessagingClient implements DropOnAirClient {
6
6
  private readonly options;
@@ -234,6 +234,9 @@ export declare class MessagingClient implements DropOnAirClient {
234
234
  updateRoom(roomId: string, update: UpdateRoomOptions): Promise<Room>;
235
235
  deleteRoom(roomId: string): Promise<void>;
236
236
  getSfuToken(roomId: string): Promise<SfuToken>;
237
+ startSfuRecording(roomId: string, destinationId: string): Promise<SfuRecording>;
238
+ stopSfuRecording(roomId: string, recordingId: string): Promise<SfuRecording>;
239
+ listSfuRecordings(roomId: string): Promise<SfuRecording[]>;
237
240
  /**
238
241
  * Join the live call in a room. Resolves with the callId once joined; that
239
242
  * callId works with the existing group-call signaling methods (pass an empty
@@ -1025,6 +1025,31 @@ class MessagingClient {
1025
1025
  throw new Error(`getSfuToken failed (HTTP ${res.status})`);
1026
1026
  return res.json();
1027
1027
  }
1028
+ async startSfuRecording(roomId, destinationId) {
1029
+ const jwt = await this.getValidDropOnAirJwt(false);
1030
+ const res = await this.fetchFn(`${this.httpUrl}/api/rooms/${encodeURIComponent(roomId)}/sfu-recordings`, {
1031
+ method: 'POST',
1032
+ headers: { Authorization: `Bearer ${jwt}`, 'Content-Type': 'application/json' },
1033
+ body: JSON.stringify({ destinationId }),
1034
+ });
1035
+ if (!res.ok)
1036
+ throw new Error(`startSfuRecording failed (HTTP ${res.status})`);
1037
+ return res.json();
1038
+ }
1039
+ async stopSfuRecording(roomId, recordingId) {
1040
+ const jwt = await this.getValidDropOnAirJwt(false);
1041
+ const res = await this.fetchFn(`${this.httpUrl}/api/rooms/${encodeURIComponent(roomId)}/sfu-recordings/${encodeURIComponent(recordingId)}`, { method: 'DELETE', headers: { Authorization: `Bearer ${jwt}` } });
1042
+ if (!res.ok)
1043
+ throw new Error(`stopSfuRecording failed (HTTP ${res.status})`);
1044
+ return res.json();
1045
+ }
1046
+ async listSfuRecordings(roomId) {
1047
+ const jwt = await this.getValidDropOnAirJwt(false);
1048
+ const res = await this.fetchFn(`${this.httpUrl}/api/rooms/${encodeURIComponent(roomId)}/sfu-recordings`, { method: 'GET', headers: { Authorization: `Bearer ${jwt}` } });
1049
+ if (!res.ok)
1050
+ throw new Error(`listSfuRecordings failed (HTTP ${res.status})`);
1051
+ return res.json();
1052
+ }
1028
1053
  /**
1029
1054
  * Join the live call in a room. Resolves with the callId once joined; that
1030
1055
  * callId works with the existing group-call signaling methods (pass an empty
@@ -190,6 +190,30 @@ export interface SfuToken {
190
190
  /** Token expiry, Unix epoch millis. */
191
191
  expiresAt: number;
192
192
  }
193
+ /**
194
+ * A server-side recording of an SFU-mode room (Phase 3.7.5). The platform
195
+ * never holds the recorded bytes - LiveKit Egress uploads the finalized file
196
+ * directly to the destination configured in the panel under `destinationId`.
197
+ */
198
+ export interface SfuRecording {
199
+ /** Opaque ID; pass to stopSfuRecording. */
200
+ recordingId: string;
201
+ roomId: string;
202
+ /** ID of the panel-registered RecordingDestination this recording targets. */
203
+ destinationId: string;
204
+ /** Lifecycle state. */
205
+ status: 'STARTING' | 'ACTIVE' | 'STOPPING' | 'COMPLETED' | 'FAILED';
206
+ /** Unix epoch millis when the recording was kicked off. */
207
+ startedAt: number | null;
208
+ /** Populated once Egress finalizes the file (COMPLETED or FAILED). */
209
+ finishedAt: number | null;
210
+ durationSeconds: number | null;
211
+ fileSizeBytes: number | null;
212
+ /** Customer-side URI of the finalized file (e.g. s3://bucket/path.mp4). */
213
+ locationUri: string | null;
214
+ /** Populated when status is FAILED. */
215
+ errorMessage: string | null;
216
+ }
193
217
  export interface Room {
194
218
  roomId: string;
195
219
  name: string;
@@ -522,6 +546,19 @@ export interface DropOnAirClient {
522
546
  * and need no token. Hand the returned url + token to your SFU client.
523
547
  */
524
548
  getSfuToken(roomId: string): Promise<SfuToken>;
549
+ /**
550
+ * Start a server-side recording of an SFU-mode room. The destination is a
551
+ * RecordingDestination pre-registered in the panel; the platform never
552
+ * holds the recorded bytes, LiveKit Egress uploads directly. Only valid
553
+ * when the room's policy sets mediaEncryption 'SFU' (E2EE rooms cannot
554
+ * be server-recorded). Completion is observable via the
555
+ * sfu-recording.completed webhook or by polling listSfuRecordings.
556
+ */
557
+ startSfuRecording(roomId: string, destinationId: string): Promise<SfuRecording>;
558
+ /** Stop a running SFU recording. Idempotent on terminal status. */
559
+ stopSfuRecording(roomId: string, recordingId: string): Promise<SfuRecording>;
560
+ /** List SFU recordings for a room, most-recent first. */
561
+ listSfuRecordings(roomId: string): Promise<SfuRecording[]>;
525
562
  /** Raise your hand to request promotion to speaker (any participant). */
526
563
  raiseHand(callId: string): void;
527
564
  /** Lower your previously raised hand. */
package/dist/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import { InitializeOptions, DropOnAirClient } from './core/types';
2
2
  export { SDK_VERSION, PROTOCOL_VERSION, PAYLOAD_FORMAT_VERSION } from './version';
3
3
  export declare function initialize(options: InitializeOptions): Promise<DropOnAirClient>;
4
- export type { InitializeOptions, DropOnAirClient, DropOnAirEvent, MessageCallback, EventCallback, DecryptedMessage, KeyStorageAdapter, CallEvent, CallEventType, CallEventCallback, TurnCredentials, BroadcastMessage, BroadcastCallback, GroupInfo, GroupMemberInfo, DecryptedGroupMessage, GroupMessageCallback, GroupCallEvent, GroupCallEventType, GroupCallEventCallback, Room, RoomPolicy, CreateRoomOptions, UpdateRoomOptions, SfuToken, MessageEditEvent, MessageEditCallback, MessageDeleteEvent, MessageDeleteCallback, DeviceInfo, ReadReceiptEvent, ReadReceiptCallback, NotificationClearEvent, NotificationClearCallback, DraftSyncEvent, DraftSyncCallback, } from './core/types';
4
+ export type { InitializeOptions, DropOnAirClient, DropOnAirEvent, MessageCallback, EventCallback, DecryptedMessage, KeyStorageAdapter, CallEvent, CallEventType, CallEventCallback, TurnCredentials, BroadcastMessage, BroadcastCallback, GroupInfo, GroupMemberInfo, DecryptedGroupMessage, GroupMessageCallback, GroupCallEvent, GroupCallEventType, GroupCallEventCallback, Room, RoomPolicy, CreateRoomOptions, UpdateRoomOptions, SfuToken, SfuRecording, MessageEditEvent, MessageEditCallback, MessageDeleteEvent, MessageDeleteCallback, DeviceInfo, ReadReceiptEvent, ReadReceiptCallback, NotificationClearEvent, NotificationClearCallback, DraftSyncEvent, DraftSyncCallback, } from './core/types';
5
5
  export type { AttachmentEncryptionType, AttachmentConversationType, AttachmentRef, DeviceWrappedKey, CreateUploadSessionOptions, UploadSession, PrepareAttachmentOptions, DownloadedAttachment, } from './attachment/attachment-types';
6
+ export { SseTransport, type SseTransportOptions, type SseFrameHandler, type SseStateHandler, } from './transport/sse-transport';
6
7
  declare const _default: {
7
8
  initialize: typeof initialize;
8
9
  };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PAYLOAD_FORMAT_VERSION = exports.PROTOCOL_VERSION = exports.SDK_VERSION = void 0;
3
+ exports.SseTransport = exports.PAYLOAD_FORMAT_VERSION = exports.PROTOCOL_VERSION = exports.SDK_VERSION = void 0;
4
4
  exports.initialize = initialize;
5
5
  const messaging_client_1 = require("./core/messaging-client");
6
6
  const session_manager_1 = require("./core/session-manager");
@@ -26,4 +26,7 @@ async function initialize(options) {
26
26
  }
27
27
  return client;
28
28
  }
29
+ // Phase 4.2.1: HTTP fallback lane primitive for restrictive networks.
30
+ var sse_transport_1 = require("./transport/sse-transport");
31
+ Object.defineProperty(exports, "SseTransport", { enumerable: true, get: function () { return sse_transport_1.SseTransport; } });
29
32
  exports.default = { initialize };
@@ -0,0 +1,62 @@
1
+ /**
2
+ * SSE-lane primitive (Phase 4.2.1 v1).
3
+ *
4
+ * Standalone client for the platform's HTTP fallback transport
5
+ * (`GET /v1/transport/stream` for receive, `POST /v1/transport/send` for
6
+ * send). Suitable for environments where WebSocket upgrades are blocked
7
+ * by a corporate firewall but plain HTTPS GET/POST work.
8
+ *
9
+ * v1 surface intentionally narrow: this exposes the bytes lane only.
10
+ * Caller is responsible for parsing the protobuf Envelope (or using the
11
+ * codec exported from the SDK). Full `init({ transport: 'sse' })`
12
+ * integration into `MessagingClient` lands in 4.2.1b once the wire
13
+ * shape stabilises with real-world feedback.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * import { SseTransport } from '@droponair/sdk-js';
18
+ * const sse = new SseTransport({ httpUrl: 'https://sdk.droponair.com', getJwt: async () => myJwt });
19
+ * sse.onFrame((bytes) => { ... your protobuf decode ... });
20
+ * await sse.connect();
21
+ * await sse.sendEnvelope(myEnvelopeBytes);
22
+ * ```
23
+ */
24
+ export interface SseTransportOptions {
25
+ /** Base URL of the platform's REST API (no trailing slash). */
26
+ httpUrl: string;
27
+ /** Returns a fresh JWT. Called once per connect. */
28
+ getJwt: () => Promise<string>;
29
+ /**
30
+ * Custom fetch implementation, useful for Node-side polyfills or test
31
+ * doubles. Defaults to globalThis.fetch.
32
+ */
33
+ fetchFn?: typeof fetch;
34
+ /**
35
+ * Custom EventSource constructor, useful for environments (e.g. Node)
36
+ * that need the `eventsource` npm polyfill. Defaults to the global.
37
+ */
38
+ eventSourceCtor?: typeof EventSource;
39
+ }
40
+ export type SseFrameHandler = (frame: Uint8Array) => void;
41
+ export type SseStateHandler = (state: 'connecting' | 'open' | 'closed' | 'error') => void;
42
+ export declare class SseTransport {
43
+ private readonly httpUrl;
44
+ private readonly getJwt;
45
+ private readonly fetchFn;
46
+ private readonly EventSourceCtor;
47
+ private eventSource;
48
+ private jwt;
49
+ private frameHandler;
50
+ private stateHandler;
51
+ constructor(options: SseTransportOptions);
52
+ /** Subscribe to incoming protobuf frames. Replaces any previous handler. */
53
+ onFrame(handler: SseFrameHandler): void;
54
+ /** Subscribe to lifecycle state changes. Replaces any previous handler. */
55
+ onState(handler: SseStateHandler): void;
56
+ /** Open the long-lived SSE stream. Resolves on first 'ready' event from the server. */
57
+ connect(): Promise<void>;
58
+ /** POST a single protobuf Envelope to the server. */
59
+ sendEnvelope(envelopeBytes: Uint8Array): Promise<void>;
60
+ /** Close the stream. Idempotent. */
61
+ close(): void;
62
+ }
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ /**
3
+ * SSE-lane primitive (Phase 4.2.1 v1).
4
+ *
5
+ * Standalone client for the platform's HTTP fallback transport
6
+ * (`GET /v1/transport/stream` for receive, `POST /v1/transport/send` for
7
+ * send). Suitable for environments where WebSocket upgrades are blocked
8
+ * by a corporate firewall but plain HTTPS GET/POST work.
9
+ *
10
+ * v1 surface intentionally narrow: this exposes the bytes lane only.
11
+ * Caller is responsible for parsing the protobuf Envelope (or using the
12
+ * codec exported from the SDK). Full `init({ transport: 'sse' })`
13
+ * integration into `MessagingClient` lands in 4.2.1b once the wire
14
+ * shape stabilises with real-world feedback.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { SseTransport } from '@droponair/sdk-js';
19
+ * const sse = new SseTransport({ httpUrl: 'https://sdk.droponair.com', getJwt: async () => myJwt });
20
+ * sse.onFrame((bytes) => { ... your protobuf decode ... });
21
+ * await sse.connect();
22
+ * await sse.sendEnvelope(myEnvelopeBytes);
23
+ * ```
24
+ */
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.SseTransport = void 0;
27
+ class SseTransport {
28
+ constructor(options) {
29
+ this.eventSource = null;
30
+ this.jwt = null;
31
+ this.frameHandler = null;
32
+ this.stateHandler = null;
33
+ this.httpUrl = options.httpUrl.replace(/\/+$/, '');
34
+ this.getJwt = options.getJwt;
35
+ this.fetchFn = options.fetchFn ?? globalThis.fetch.bind(globalThis);
36
+ this.EventSourceCtor = options.eventSourceCtor ?? globalThis.EventSource;
37
+ if (typeof this.EventSourceCtor !== 'function') {
38
+ throw new Error('SseTransport: EventSource is not available; pass options.eventSourceCtor (e.g. the `eventsource` npm package)');
39
+ }
40
+ }
41
+ /** Subscribe to incoming protobuf frames. Replaces any previous handler. */
42
+ onFrame(handler) {
43
+ this.frameHandler = handler;
44
+ }
45
+ /** Subscribe to lifecycle state changes. Replaces any previous handler. */
46
+ onState(handler) {
47
+ this.stateHandler = handler;
48
+ }
49
+ /** Open the long-lived SSE stream. Resolves on first 'ready' event from the server. */
50
+ async connect() {
51
+ this.close();
52
+ this.jwt = await this.getJwt();
53
+ this.stateHandler?.('connecting');
54
+ const url = `${this.httpUrl}/v1/transport/stream?token=${encodeURIComponent(this.jwt)}`;
55
+ return new Promise((resolve, reject) => {
56
+ const es = new this.EventSourceCtor(url);
57
+ const onReady = () => {
58
+ this.stateHandler?.('open');
59
+ resolve();
60
+ };
61
+ es.addEventListener('ready', onReady);
62
+ es.addEventListener('envelope', (e) => {
63
+ const bytes = decodeBase64(e.data);
64
+ this.frameHandler?.(bytes);
65
+ });
66
+ es.onerror = (e) => {
67
+ this.stateHandler?.(es.readyState === EventSource.CLOSED ? 'closed' : 'error');
68
+ if (es.readyState === EventSource.CONNECTING) {
69
+ // EventSource auto-reconnects; nothing for us to do.
70
+ return;
71
+ }
72
+ if (es.readyState === EventSource.CLOSED) {
73
+ // Hard close, e.g. 401. Reject the initial promise if still pending.
74
+ reject(new Error('SseTransport: stream closed'));
75
+ }
76
+ };
77
+ this.eventSource = es;
78
+ });
79
+ }
80
+ /** POST a single protobuf Envelope to the server. */
81
+ async sendEnvelope(envelopeBytes) {
82
+ if (!this.jwt) {
83
+ throw new Error('SseTransport: not connected (call connect() first)');
84
+ }
85
+ const res = await this.fetchFn(`${this.httpUrl}/v1/transport/send`, {
86
+ method: 'POST',
87
+ headers: {
88
+ 'Authorization': `Bearer ${this.jwt}`,
89
+ 'Content-Type': 'application/octet-stream',
90
+ },
91
+ // BodyInit accepts ArrayBuffer; this avoids a TS type mismatch on
92
+ // Uint8Array<ArrayBufferLike> while sending the same bytes.
93
+ body: envelopeBytes.buffer.slice(envelopeBytes.byteOffset, envelopeBytes.byteOffset + envelopeBytes.byteLength),
94
+ });
95
+ if (!res.ok) {
96
+ throw new Error(`SseTransport: send failed (HTTP ${res.status})`);
97
+ }
98
+ }
99
+ /** Close the stream. Idempotent. */
100
+ close() {
101
+ if (this.eventSource) {
102
+ this.eventSource.close();
103
+ this.eventSource = null;
104
+ this.stateHandler?.('closed');
105
+ }
106
+ }
107
+ }
108
+ exports.SseTransport = SseTransport;
109
+ function decodeBase64(b64) {
110
+ if (typeof atob === 'function') {
111
+ const bin = atob(b64);
112
+ const out = new Uint8Array(bin.length);
113
+ for (let i = 0; i < bin.length; i++)
114
+ out[i] = bin.charCodeAt(i);
115
+ return out;
116
+ }
117
+ // Node fallback. Buffer is global there.
118
+ const Buf = globalThis.Buffer;
119
+ if (Buf)
120
+ return Buf.from(b64, 'base64');
121
+ throw new Error('SseTransport: no base64 decoder available (need atob or Buffer)');
122
+ }
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.17.0";
10
+ export declare const SDK_VERSION = "0.19.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.17.0';
13
+ exports.SDK_VERSION = '0.19.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.17.0",
3
+ "version": "0.19.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",