@droponair/sdk-js 0.22.0 → 0.23.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 +24 -10
- package/README.md +2 -2
- package/dist/core/messaging-client.d.ts +1 -0
- package/dist/core/messaging-client.js +4 -1
- package/dist/core/types.d.ts +7 -0
- package/dist/transport/protobuf-codec.js +31 -21
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,12 +6,28 @@ This project follows [Semantic Versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [0.23.0], 2026-06-01
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Idempotent retries via `sendMessage(..., { clientMessageId })`.** Pass a stable id to reuse across retries of the same logical message. The relay now dedups by `(appId, fromUserId, messageId)`, so re-sending after a lost ack or a reconnect delivers the message at most once instead of creating duplicates. Omit it for new messages and a fresh id is generated (unchanged default behavior). Requires relay support shipped alongside this release; wire format and `PROTOCOL_VERSION` are unchanged (the `clientMessageId` field already existed).
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## [0.22.1], 2026-05-31
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **Message send/receive broken when attachments support is compiled in.** The protobuf reflection tree shared a single `AttachmentRef` type instance across `Envelope`, `GroupEnvelope`, and `GroupMessageNotification`. Because a protobufjs reflection object can have only one parent, adding it to a later message re-parented it and removed it from `Envelope`, so encoding any 1:1 message threw `no such Type or Enum 'AttachmentRef' in Type Envelope` before a frame was ever produced. Each message now builds its own `AttachmentRef` (and nested `DeviceWrappedKey` / `EncryptionType`) instance. Wire format is unchanged, this is purely a client-side construction fix. Also resolves the same latent failure on `GroupMessageNotification` decode.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
9
25
|
## [0.22.0], 2026-05-25
|
|
10
26
|
|
|
11
27
|
### Added
|
|
12
28
|
|
|
13
29
|
- **MessagingClient transport selection.** `initialize({ transport: 'ws' | 'sse' | 'wt' | 'auto' })` lets your app choose the signaling lane at init time. Default stays `'ws'`, so existing integrations are byte-identical. `'auto'` queries `GET /api/info.transports` and picks the best available lane from what the runtime supports. WebSocket close-code semantics (1008 JWT-expiry surfacing, 4001 JWT-refresh-on-event) are preserved on the WebSocket path; SSE/WT synthesize close events.
|
|
14
|
-
- **MQTT (IoT) broadcast lane
|
|
30
|
+
- **MQTT (IoT) broadcast lane.** Broadcasts now mirror to MQTT topic `droponair/v1/broadcast/{appId}/{channelId}` on `mqtts://mqtt.droponair.com:8883`. For IoT subscribers that consume your broadcasts without our full SDK. Subscribe-only, per-app credentials, TLS only. Visible via `GET /api/info` (`transports` includes `"mqtt"`, `features` includes `"transport_mqtt"`). No SDK API change, the lane is consumed directly by MQTT clients.
|
|
15
31
|
|
|
16
32
|
### Changed
|
|
17
33
|
|
|
@@ -23,11 +39,11 @@ This project follows [Semantic Versioning](https://semver.org/).
|
|
|
23
39
|
|
|
24
40
|
### Added
|
|
25
41
|
|
|
26
|
-
- **Transport auto-select helper
|
|
42
|
+
- **Transport auto-select helper.** New `selectTransport({ httpUrl })` reads `/api/info.transports`, intersects with the runtime's `WebTransport`/`WebSocket`/`EventSource` availability, and returns the best lane (preference: WebTransport -> WebSocket -> SSE; overridable via `preference`). Caller then constructs the corresponding primitive. Pluggable `fetchFn` for Node + tests.
|
|
27
43
|
|
|
28
44
|
### Notes
|
|
29
45
|
|
|
30
|
-
- Foundation for an `init({ transport: 'auto' })` shortcut once full `MessagingClient` integration lands
|
|
46
|
+
- Foundation for an `init({ transport: 'auto' })` shortcut once full `MessagingClient` integration lands in a follow-up release. Today it's a small standalone helper so existing customers can adopt the pattern without waiting.
|
|
31
47
|
|
|
32
48
|
---
|
|
33
49
|
|
|
@@ -35,12 +51,12 @@ This project follows [Semantic Versioning](https://semver.org/).
|
|
|
35
51
|
|
|
36
52
|
### Added
|
|
37
53
|
|
|
38
|
-
- **WebTransport (HTTP/3) transport primitive
|
|
54
|
+
- **WebTransport (HTTP/3) transport primitive.** New `WebTransportTransport` class uses the browser-native `WebTransport` API to ride the platform's `/v1/transport/wt` HTTP/3 endpoint,. Same shape as `SseTransport`: `onFrame`, `sendEnvelope`, `connect`, `close`. Caller brings own protobuf decoder. Browser-only for v1 (Chromium 97+, Firefox 125+, Safari TP-only); Node 22+ has experimental support behind flags.
|
|
39
55
|
|
|
40
56
|
### Notes
|
|
41
57
|
|
|
42
58
|
- WebTransport gives multi-stream perf wins over WebSocket on flaky cellular and similar networks. WebSocket on `/ws` remains the default; this primitive is opt-in for environments that have native WebTransport.
|
|
43
|
-
- The droponair.com endpoint only advertises `transports: ["webtransport"]` once the
|
|
59
|
+
- The droponair.com endpoint only advertises `transports: ["webtransport"]` once the HTTP/3 lane is enabled and `app.transport.webtransport-enabled` is flipped on the platform. Check `GET /api/info.transports` for the live list.
|
|
44
60
|
|
|
45
61
|
---
|
|
46
62
|
|
|
@@ -48,11 +64,11 @@ This project follows [Semantic Versioning](https://semver.org/).
|
|
|
48
64
|
|
|
49
65
|
### Added
|
|
50
66
|
|
|
51
|
-
- **SSE transport primitive
|
|
67
|
+
- **SSE transport primitive.** 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).
|
|
52
68
|
|
|
53
69
|
### Notes
|
|
54
70
|
|
|
55
|
-
- v1 is a primitive; full `init({ transport: 'sse' })` integration into `MessagingClient` lands in
|
|
71
|
+
- v1 is a primitive; full `init({ transport: 'sse' })` integration into `MessagingClient` lands in a follow-up release. Customers who need the SSE lane today can use `SseTransport` directly alongside the existing WS client.
|
|
56
72
|
- WebSocket on `/ws` remains the default for all SDK calls; this addition is purely opt-in. Older code paths and existing apps are unaffected.
|
|
57
73
|
|
|
58
74
|
---
|
|
@@ -61,7 +77,7 @@ This project follows [Semantic Versioning](https://semver.org/).
|
|
|
61
77
|
|
|
62
78
|
### Added
|
|
63
79
|
|
|
64
|
-
- **SFU recording
|
|
80
|
+
- **SFU recording.** 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`.
|
|
65
81
|
- Three new client methods:
|
|
66
82
|
- `startSfuRecording(roomId, destinationId)` -> `SfuRecording`
|
|
67
83
|
- `stopSfuRecording(roomId, recordingId)` -> `SfuRecording`
|
|
@@ -326,7 +342,6 @@ This project follows [Semantic Versioning](https://semver.org/).
|
|
|
326
342
|
- Wire-level additive: legacy 0.4.x clients ignore the new `Envelope.attachments` field (proto3 forwards-compat). Existing apps continue to work unchanged.
|
|
327
343
|
- v1 storage adapters supported by the server: S3-compatible (S3, R2, MinIO, B2, Wasabi), Google Cloud Storage, Azure Blob.
|
|
328
344
|
- Plan limits: `maxAttachmentSizeMb` per file + `maxAttachmentsPerMonth`. FREE = attachments disabled. PRO = 25 MB / 1000 per month. GROWTH = 100 MB / 10000. PAYG = 250 MB / metered. ENTERPRISE = custom.
|
|
329
|
-
- Requires sdk-be `0.5.0` server.
|
|
330
345
|
|
|
331
346
|
---
|
|
332
347
|
|
|
@@ -342,7 +357,6 @@ This project follows [Semantic Versioning](https://semver.org/).
|
|
|
342
357
|
### Notes
|
|
343
358
|
- Each edit and each `FOR_EVERYONE` delete counts as **one** MESSAGE usage record (subject to plan quotas + rate limits).
|
|
344
359
|
- Wire-level additive: legacy 0.3.x clients ignore unknown frame types (proto3 forwards-compat), so existing TwinFlame and CrowdSyncer apps continue to work unchanged.
|
|
345
|
-
- Requires sdk-be `0.4.0` server.
|
|
346
360
|
|
|
347
361
|
---
|
|
348
362
|
|
package/README.md
CHANGED
|
@@ -183,7 +183,7 @@ E2EE invariant: the push body is sender-supplied cleartext metadata only (e.g. "
|
|
|
183
183
|
|
|
184
184
|
### Message Edit & Delete
|
|
185
185
|
|
|
186
|
-
Available since SDK `0.4.0
|
|
186
|
+
Available since SDK `0.4.0`.
|
|
187
187
|
|
|
188
188
|
```typescript
|
|
189
189
|
// Edit an encrypted message
|
|
@@ -213,7 +213,7 @@ client.onMessageDelete((del) => {
|
|
|
213
213
|
|
|
214
214
|
### Attachments (Customer-Managed Storage)
|
|
215
215
|
|
|
216
|
-
Available since SDK `0.5.0
|
|
216
|
+
Available since SDK `0.5.0`. Configure your bucket once in the DropOnAir panel (S3-compatible, GCS, or Azure Blob). DropOnAir never holds file bytes - the SDK uploads/downloads directly against your bucket via short-lived presigned URLs.
|
|
217
217
|
|
|
218
218
|
```typescript
|
|
219
219
|
// Convenience: encrypt + upload + finalize in one call, then send with the message.
|
|
@@ -101,6 +101,7 @@ export declare class MessagingClient implements DropOnAirClient {
|
|
|
101
101
|
disconnect(): void;
|
|
102
102
|
sendMessage(toUserId: string, plaintextMessage: string, options?: {
|
|
103
103
|
attachments?: AttachmentRef[];
|
|
104
|
+
clientMessageId?: string;
|
|
104
105
|
}): Promise<{
|
|
105
106
|
messageId: string;
|
|
106
107
|
}>;
|
|
@@ -337,7 +337,10 @@ class MessagingClient {
|
|
|
337
337
|
throw new Error('JWT_EXPIRED: Secure messaging session is reconnecting; please retry in a moment');
|
|
338
338
|
}
|
|
339
339
|
const myDeviceId = this.deviceId ?? await this.getOrCreateDeviceId();
|
|
340
|
-
|
|
340
|
+
// Reuse a caller-supplied id for idempotent retries: the relay dedups by
|
|
341
|
+
// (appId, fromUserId, messageId), so re-sending with the same id is a no-op
|
|
342
|
+
// delivery instead of a duplicate. Defaults to a fresh id for new messages.
|
|
343
|
+
const messageId = options?.clientMessageId ?? crypto.randomUUID();
|
|
341
344
|
const timestamp = Date.now();
|
|
342
345
|
const myIdentity = await this.cryptoService.getOrCreateIdentity();
|
|
343
346
|
const myPublicKeyBytes = (0, bytes_1.fromBase64)(myIdentity.publicKey);
|
package/dist/core/types.d.ts
CHANGED
|
@@ -304,9 +304,16 @@ export interface DropOnAirClient {
|
|
|
304
304
|
/**
|
|
305
305
|
* Send a 1:1 E2EE message. Optionally attach one or more attachments
|
|
306
306
|
* prepared via {@link prepareAttachmentAndUpload} or {@link createUploadSession}.
|
|
307
|
+
*
|
|
308
|
+
* Pass `options.clientMessageId` to reuse a stable id across retries: the relay
|
|
309
|
+
* dedups by `(appId, fromUserId, messageId)`, so re-sending a message with the
|
|
310
|
+
* same id delivers it at most once instead of creating a duplicate. Omit it for
|
|
311
|
+
* new messages and a fresh id is generated. The chosen id is returned as
|
|
312
|
+
* `messageId`.
|
|
307
313
|
*/
|
|
308
314
|
sendMessage(toUserId: string, plaintextMessage: string, options?: {
|
|
309
315
|
attachments?: import('../attachment/attachment-types').AttachmentRef[];
|
|
316
|
+
clientMessageId?: string;
|
|
310
317
|
}): Promise<{
|
|
311
318
|
messageId: string;
|
|
312
319
|
}>;
|
|
@@ -42,29 +42,38 @@ const DeviceEncryptedPayloadType = new protobuf.Type('DeviceEncryptedPayload')
|
|
|
42
42
|
.add(new protobuf.Field('deviceId', 1, 'string'))
|
|
43
43
|
.add(new protobuf.Field('encryptedPayload', 2, 'bytes'))
|
|
44
44
|
.add(new protobuf.Field('senderPublicKey', 3, 'bytes'));
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
45
|
+
// Reference to an out-of-band attachment in customer-managed storage, plus its
|
|
46
|
+
// nested per-device wrapped file key (PROTOCOL_VERSION 4+).
|
|
47
|
+
//
|
|
48
|
+
// A protobufjs reflection object can have only ONE parent: calling `.add(obj)`
|
|
49
|
+
// on a second message re-parents `obj` and silently removes it from the first.
|
|
50
|
+
// Envelope, GroupEnvelope and GroupMessageNotification each nest AttachmentRef,
|
|
51
|
+
// so each needs its OWN instance, built fresh here. Sharing a single instance
|
|
52
|
+
// across them re-parented it onto the last adder and orphaned it from Envelope,
|
|
53
|
+
// breaking every 1:1 send with "no such Type or Enum 'AttachmentRef' in Type
|
|
54
|
+
// Envelope". The EncryptionType enum and DeviceWrappedKey type are nested inside
|
|
55
|
+
// each AttachmentRef and so are rebuilt per call for the same reason.
|
|
56
|
+
function buildAttachmentRefType() {
|
|
57
|
+
return new protobuf.Type('AttachmentRef')
|
|
58
|
+
.add(new protobuf.Enum('EncryptionType', { E2EE: 0, CLEARTEXT: 1 }))
|
|
59
|
+
.add(new protobuf.Type('DeviceWrappedKey')
|
|
60
|
+
.add(new protobuf.Field('deviceId', 1, 'string'))
|
|
61
|
+
.add(new protobuf.Field('wrappedKey', 2, 'bytes'))
|
|
62
|
+
.add(new protobuf.Field('senderPublicKey', 3, 'bytes'))
|
|
63
|
+
.add(new protobuf.Field('nonce', 4, 'bytes')))
|
|
64
|
+
.add(new protobuf.Field('attachmentId', 1, 'string'))
|
|
65
|
+
.add(new protobuf.Field('storageHint', 2, 'string'))
|
|
66
|
+
.add(new protobuf.Field('mimeType', 3, 'string'))
|
|
67
|
+
.add(new protobuf.Field('sizeBytes', 4, 'int64'))
|
|
68
|
+
.add(new protobuf.Field('sha256', 5, 'string'))
|
|
69
|
+
.add(new protobuf.Field('encryptionType', 6, 'EncryptionType'))
|
|
70
|
+
.add(new protobuf.Field('wrappedKeys', 7, 'DeviceWrappedKey', 'repeated'))
|
|
71
|
+
.add(new protobuf.Field('thumbnailAttachmentId', 8, 'string'));
|
|
72
|
+
}
|
|
64
73
|
const EnvelopeType = new protobuf.Type('Envelope')
|
|
65
74
|
.add(EncryptionTypeEnum)
|
|
66
75
|
.add(DeviceEncryptedPayloadType) // nested type must be added first
|
|
67
|
-
.add(
|
|
76
|
+
.add(buildAttachmentRefType())
|
|
68
77
|
.add(new protobuf.Field('messageId', 1, 'string'))
|
|
69
78
|
.add(new protobuf.Field('appId', 2, 'string'))
|
|
70
79
|
.add(new protobuf.Field('fromUserId', 3, 'string'))
|
|
@@ -128,7 +137,7 @@ const GroupEnvelopeEncryptionTypeEnum = new protobuf.Enum('EncryptionType', { E2
|
|
|
128
137
|
const GroupEnvelopeType = new protobuf.Type('GroupEnvelope')
|
|
129
138
|
.add(GroupEnvelopeEncryptionTypeEnum)
|
|
130
139
|
.add(GroupMemberPayloadType)
|
|
131
|
-
.add(
|
|
140
|
+
.add(buildAttachmentRefType())
|
|
132
141
|
.add(new protobuf.Field('messageId', 1, 'string'))
|
|
133
142
|
.add(new protobuf.Field('appId', 2, 'string'))
|
|
134
143
|
.add(new protobuf.Field('groupId', 3, 'string'))
|
|
@@ -149,6 +158,7 @@ const GroupNotifDeviceType = new protobuf.Type('DeviceEncryptedPayload')
|
|
|
149
158
|
const GroupMessageNotificationType = new protobuf.Type('GroupMessageNotification')
|
|
150
159
|
.add(GroupNotifEncryptionTypeEnum)
|
|
151
160
|
.add(GroupNotifDeviceType)
|
|
161
|
+
.add(buildAttachmentRefType())
|
|
152
162
|
.add(new protobuf.Field('messageId', 1, 'string'))
|
|
153
163
|
.add(new protobuf.Field('groupId', 2, 'string'))
|
|
154
164
|
.add(new protobuf.Field('fromUserId', 3, 'string'))
|
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.23.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.23.0';
|
|
14
14
|
/**
|
|
15
15
|
* Binary encrypted-payload format version.
|
|
16
16
|
* Included as the first byte of every encrypted payload so receivers can
|