@droponair/sdk-js 0.3.1 → 0.5.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.
@@ -42,9 +42,28 @@ 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
+ // Per-device wrapped file key for E2EE attachments (PROTOCOL_VERSION 4+)
46
+ const DeviceWrappedKeyType = new protobuf.Type('DeviceWrappedKey')
47
+ .add(new protobuf.Field('deviceId', 1, 'string'))
48
+ .add(new protobuf.Field('wrappedKey', 2, 'bytes'))
49
+ .add(new protobuf.Field('senderPublicKey', 3, 'bytes'))
50
+ .add(new protobuf.Field('nonce', 4, 'bytes'));
51
+ // Reference to an out-of-band attachment in customer-managed storage.
52
+ const AttachmentRefEncTypeEnum = new protobuf.Enum('EncryptionType', { E2EE: 0, CLEARTEXT: 1 });
53
+ const AttachmentRefType = new protobuf.Type('AttachmentRef')
54
+ .add(AttachmentRefEncTypeEnum)
55
+ .add(DeviceWrappedKeyType)
56
+ .add(new protobuf.Field('attachmentId', 1, 'string'))
57
+ .add(new protobuf.Field('storageHint', 2, 'string'))
58
+ .add(new protobuf.Field('mimeType', 3, 'string'))
59
+ .add(new protobuf.Field('sizeBytes', 4, 'int64'))
60
+ .add(new protobuf.Field('sha256', 5, 'string'))
61
+ .add(new protobuf.Field('encryptionType', 6, 'EncryptionType'))
62
+ .add(new protobuf.Field('wrappedKeys', 7, 'DeviceWrappedKey', 'repeated'));
45
63
  const EnvelopeType = new protobuf.Type('Envelope')
46
64
  .add(EncryptionTypeEnum)
47
65
  .add(DeviceEncryptedPayloadType) // nested type must be added first
66
+ .add(AttachmentRefType)
48
67
  .add(new protobuf.Field('messageId', 1, 'string'))
49
68
  .add(new protobuf.Field('appId', 2, 'string'))
50
69
  .add(new protobuf.Field('fromUserId', 3, 'string'))
@@ -55,7 +74,8 @@ const EnvelopeType = new protobuf.Type('Envelope')
55
74
  .add(new protobuf.Field('devicePayloads', 8, 'DeviceEncryptedPayload', 'repeated'))
56
75
  .add(new protobuf.Field('senderDeviceId', 9, 'string'))
57
76
  .add(new protobuf.Field('encryptionType', 10, 'EncryptionType'))
58
- .add(new protobuf.Field('plaintextPayload', 11, 'string'));
77
+ .add(new protobuf.Field('plaintextPayload', 11, 'string'))
78
+ .add(new protobuf.Field('attachments', 12, 'AttachmentRef', 'repeated'));
59
79
  const AckType = new protobuf.Type('Ack')
60
80
  .add(new protobuf.Field('messageId', 1, 'string'))
61
81
  .add(new protobuf.Field('type', 2, 'string'));
@@ -104,6 +124,9 @@ const GroupMemberPayloadType = new protobuf.Type('GroupMemberPayload')
104
124
  .add(new protobuf.Field('userId', 1, 'string'))
105
125
  .add(new protobuf.Field('devicePayloads', 2, 'DeviceEncryptedPayload', 'repeated'));
106
126
  const GroupEnvelopeEncryptionTypeEnum = new protobuf.Enum('EncryptionType', { E2EE: 0, CLEARTEXT: 1 });
127
+ // NOTE: GroupEnvelope JS field numbers diverge from the proto contract (tracked
128
+ // under phase 2.3 "JS encrypted group send parity"). Attachments support for
129
+ // groups in JS will land with phase 2.3 / 2.4 once field numbers are aligned.
107
130
  const GroupEnvelopeType = new protobuf.Type('GroupEnvelope')
108
131
  .add(GroupEnvelopeEncryptionTypeEnum)
109
132
  .add(GroupMemberPayloadType)
@@ -141,6 +164,47 @@ const GroupCallFrameType = new protobuf.Type('GroupCallFrame')
141
164
  .add(new protobuf.Field('groupId', 3, 'string'))
142
165
  .add(new protobuf.Field('targetUserId', 4, 'string'))
143
166
  .add(new protobuf.Field('payload', 5, 'string'));
167
+ // ---------------------------------------------------------------------------
168
+ // Message Edit and Delete (PROTOCOL_VERSION 3+)
169
+ // ---------------------------------------------------------------------------
170
+ //
171
+ // E2EE invariant, server never sees plaintext for E2EE edits. The same frame
172
+ // type is sent on the wire in both directions, client to server (request) and
173
+ // server to recipient devices (notification).
174
+ //
175
+ // Type discriminator at field 1 mirrors the CallFrame pattern. Probed BEFORE
176
+ // Envelope to avoid mis-routing.
177
+ const MessageEditDeviceType = new protobuf.Type('DeviceEncryptedPayload')
178
+ .add(new protobuf.Field('deviceId', 1, 'string'))
179
+ .add(new protobuf.Field('encryptedPayload', 2, 'bytes'))
180
+ .add(new protobuf.Field('senderPublicKey', 3, 'bytes'));
181
+ const MessageEditEncryptionTypeEnum = new protobuf.Enum('EncryptionType', { E2EE: 0, CLEARTEXT: 1 });
182
+ const MessageEditFrameType = new protobuf.Type('MessageEditFrame')
183
+ .add(MessageEditEncryptionTypeEnum)
184
+ .add(MessageEditDeviceType)
185
+ .add(new protobuf.Field('type', 1, 'string'))
186
+ .add(new protobuf.Field('editId', 2, 'string'))
187
+ .add(new protobuf.Field('originalMessageId', 3, 'string'))
188
+ .add(new protobuf.Field('appId', 4, 'string'))
189
+ .add(new protobuf.Field('fromUserId', 5, 'string'))
190
+ .add(new protobuf.Field('toUserId', 6, 'string'))
191
+ .add(new protobuf.Field('timestamp', 7, 'int64'))
192
+ .add(new protobuf.Field('encryptionType', 8, 'EncryptionType'))
193
+ .add(new protobuf.Field('encryptedPayload', 9, 'bytes'))
194
+ .add(new protobuf.Field('devicePayloads', 10, 'DeviceEncryptedPayload', 'repeated'))
195
+ .add(new protobuf.Field('senderDeviceId', 11, 'string'))
196
+ .add(new protobuf.Field('plaintextPayload', 12, 'string'))
197
+ .add(new protobuf.Field('clientEditId', 13, 'string'));
198
+ const MessageDeleteFrameType = new protobuf.Type('MessageDeleteFrame')
199
+ .add(new protobuf.Field('type', 1, 'string'))
200
+ .add(new protobuf.Field('deleteId', 2, 'string'))
201
+ .add(new protobuf.Field('originalMessageId', 3, 'string'))
202
+ .add(new protobuf.Field('appId', 4, 'string'))
203
+ .add(new protobuf.Field('fromUserId', 5, 'string'))
204
+ .add(new protobuf.Field('toUserId', 6, 'string'))
205
+ .add(new protobuf.Field('timestamp', 7, 'int64'))
206
+ .add(new protobuf.Field('scope', 8, 'string'))
207
+ .add(new protobuf.Field('clientDeleteId', 9, 'string'));
144
208
  class ProtobufCodec {
145
209
  encodeEnvelope(value) {
146
210
  return EnvelopeType.encode(value).finish();
@@ -163,7 +227,24 @@ class ProtobufCodec {
163
227
  encodeGroupAck(value) {
164
228
  return GroupAckType.encode(value).finish();
165
229
  }
230
+ encodeMessageEditFrame(value) {
231
+ return MessageEditFrameType.encode(value).finish();
232
+ }
233
+ encodeMessageDeleteFrame(value) {
234
+ return MessageDeleteFrameType.encode(value).finish();
235
+ }
166
236
  decodeFrame(payload) {
237
+ // Edit and delete frames must be probed BEFORE Envelope, the type
238
+ // discriminator at field 1 ("MESSAGE_EDIT" / "MESSAGE_DELETE") would
239
+ // otherwise parse as Envelope.messageId and could be mis-routed.
240
+ const asEdit = this.tryDecodeMessageEditFrame(payload);
241
+ if (asEdit) {
242
+ return { kind: 'messageEdit', data: asEdit };
243
+ }
244
+ const asDelete = this.tryDecodeMessageDeleteFrame(payload);
245
+ if (asDelete) {
246
+ return { kind: 'messageDelete', data: asDelete };
247
+ }
167
248
  const asEnvelope = this.tryDecodeEnvelope(payload);
168
249
  if (asEnvelope) {
169
250
  return { kind: 'envelope', data: asEnvelope };
@@ -231,6 +312,23 @@ class ProtobufCodec {
231
312
  senderPublicKey: new Uint8Array(dp.senderPublicKey),
232
313
  }));
233
314
  }
315
+ // Normalize attachments (PROTOCOL_VERSION 4+)
316
+ if (decoded.attachments && decoded.attachments.length > 0) {
317
+ result.attachments = decoded.attachments.map(att => ({
318
+ attachmentId: att.attachmentId,
319
+ storageHint: att.storageHint,
320
+ mimeType: att.mimeType,
321
+ sizeBytes: Number(att.sizeBytes),
322
+ sha256: att.sha256,
323
+ encryptionType: att.encryptionType ?? 0,
324
+ wrappedKeys: att.wrappedKeys?.map(wk => ({
325
+ deviceId: wk.deviceId,
326
+ wrappedKey: new Uint8Array(wk.wrappedKey),
327
+ senderPublicKey: new Uint8Array(wk.senderPublicKey),
328
+ nonce: new Uint8Array(wk.nonce),
329
+ })) ?? [],
330
+ }));
331
+ }
234
332
  return result;
235
333
  }
236
334
  catch {
@@ -333,5 +431,45 @@ class ProtobufCodec {
333
431
  return null;
334
432
  }
335
433
  }
434
+ tryDecodeMessageEditFrame(payload) {
435
+ try {
436
+ const decoded = MessageEditFrameType.decode(payload);
437
+ if (decoded.type !== 'MESSAGE_EDIT') {
438
+ return null;
439
+ }
440
+ const result = {
441
+ ...decoded,
442
+ timestamp: Number(decoded.timestamp),
443
+ encryptedPayload: decoded.encryptedPayload && decoded.encryptedPayload.length > 0
444
+ ? new Uint8Array(decoded.encryptedPayload) : undefined,
445
+ };
446
+ if (decoded.devicePayloads && decoded.devicePayloads.length > 0) {
447
+ result.devicePayloads = decoded.devicePayloads.map(dp => ({
448
+ deviceId: dp.deviceId,
449
+ encryptedPayload: new Uint8Array(dp.encryptedPayload),
450
+ senderPublicKey: new Uint8Array(dp.senderPublicKey),
451
+ }));
452
+ }
453
+ return result;
454
+ }
455
+ catch {
456
+ return null;
457
+ }
458
+ }
459
+ tryDecodeMessageDeleteFrame(payload) {
460
+ try {
461
+ const decoded = MessageDeleteFrameType.decode(payload);
462
+ if (decoded.type !== 'MESSAGE_DELETE') {
463
+ return null;
464
+ }
465
+ return {
466
+ ...decoded,
467
+ timestamp: Number(decoded.timestamp),
468
+ };
469
+ }
470
+ catch {
471
+ return null;
472
+ }
473
+ }
336
474
  }
337
475
  exports.ProtobufCodec = ProtobufCodec;
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.3.1";
10
+ export declare const SDK_VERSION = "0.5.0";
11
11
  /**
12
12
  * Binary encrypted-payload format version.
13
13
  * Included as the first byte of every encrypted payload so receivers can
@@ -19,4 +19,4 @@ export declare const PAYLOAD_FORMAT_VERSION = 1;
19
19
  * Increment when adding required proto fields or changing frame semantics.
20
20
  * The server advertises its supported range via GET /api/info.
21
21
  */
22
- export declare const PROTOCOL_VERSION = 2;
22
+ export declare const PROTOCOL_VERSION = 4;
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.3.1';
13
+ exports.SDK_VERSION = '0.5.0';
14
14
  /**
15
15
  * Binary encrypted-payload format version.
16
16
  * Included as the first byte of every encrypted payload so receivers can
@@ -22,4 +22,4 @@ exports.PAYLOAD_FORMAT_VERSION = 1;
22
22
  * Increment when adding required proto fields or changing frame semantics.
23
23
  * The server advertises its supported range via GET /api/info.
24
24
  */
25
- exports.PROTOCOL_VERSION = 2;
25
+ exports.PROTOCOL_VERSION = 4;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@droponair/sdk-js",
3
- "version": "0.3.1",
3
+ "version": "0.5.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",