quickblox 2.24.0-beta.1 → 2.24.1-alpha.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "quickblox",
3
3
  "description": "QuickBlox JavaScript SDK",
4
- "version": "2.24.0-beta.1",
4
+ "version": "2.24.1-alpha.0",
5
5
  "homepage": "https://quickblox.com/developers/Javascript",
6
6
  "main": "src/qbMain.js",
7
7
  "types": "quickblox.d.ts",
package/quickblox.d.ts CHANGED
@@ -418,6 +418,26 @@ export declare interface QBChatMessage {
418
418
  * Available since SDK 2.24.0.
419
419
  */
420
420
  reactions?: QBReaction[]
421
+ /**
422
+ * Message editing flag (SR-2955). The server sets `1` when the message was
423
+ * edited, `0`/`null` otherwise. Delivered on the message object in REST
424
+ * responses (`message.list` / `message.getById`). Note: the server also carries it
425
+ * in `NoticeUpdatedMessage` (server release 2.94.0), but the SDK's real-time
426
+ * `onMessageUpdatedListener` object does not currently include it — read it from
427
+ * a REST fetch of the message.
428
+ * Available since SDK 2.24.0.
429
+ */
430
+ is_edited?: 0 | 1 | boolean | null
431
+ /**
432
+ * ID of the user who last edited the message (SR-2955). Omitted/`null` when the
433
+ * message was never edited. Available since SDK 2.24.0.
434
+ */
435
+ editor_id?: number | null
436
+ /**
437
+ * ISO timestamp of the last edit (SR-2955). Omitted/`null` when the message was
438
+ * never edited. Available since SDK 2.24.0.
439
+ */
440
+ edited_at?: string | null
421
441
  /**
422
442
  * Name of the custom field.
423
443
  * Chat message can be extended with additional fields and contain any other user key-value custom parameters.
@@ -683,7 +703,8 @@ interface QBChatModule {
683
703
  /**
684
704
  * Subscribe the current XMPP session to Notice Feature stanzas (urn:xmpp:notice:0).
685
705
  * After a successful IQ result the server starts delivering NoticeUpdatedMessage /
686
- * NoticeDeletedMessage / NoticeUpdatedDialog / NoticeDeletedDialog headline stanzas.
706
+ * NoticeDeletedMessage / NoticeCreatedDialog / NoticeUpdatedDialog / NoticeDeletedDialog
707
+ * headline stanzas.
687
708
  * The local enabled flag is reset on chat disconnect — call enableNotices() again after reconnect.
688
709
  * @since 2.24.0
689
710
  */
@@ -749,6 +770,18 @@ interface QBChatModule {
749
770
  * @since 2.24.0
750
771
  */
751
772
  onDialogUpdatedListener?: (dialog: QBChatDialog) => void
773
+ /**
774
+ * Notice Feature: a Group or Private dialog was created (SR-2952). Delivered via the
775
+ * XMPP `NoticeCreatedDialog` headline stanza after `enableNotices()`, only to users
776
+ * with the Notice feature enabled. NOT sent for Public dialogs.
777
+ *
778
+ * The dialog object is a PARTIAL snapshot captured at creation time: it carries
779
+ * `created_at` / `updated_at` / `user_id` (creator) and the base fields, but has no
780
+ * `last_message*` fields (the dialog is brand new) and, for a private dialog, no
781
+ * `xmpp_room_jid`. Preserves the existing QBChatDialog snake_case shape.
782
+ * @since 2.24.1
783
+ */
784
+ onDialogCreatedListener?: (dialog: QBChatDialog) => void
752
785
  /** Blocked entities receive an error when try to chat with a user in a 1-1 chat and receivie nothing in a group chat. */
753
786
  onMessageErrorListener?: (messageId: QBChatMessage['_id'], error: any) => void
754
787
  /**
package/quickblox.js CHANGED
@@ -30540,6 +30540,7 @@ NOTICE_KNOWN_MODULE_IDS[noticeConsts.NOTICE_MODULE_IDENTIFIER.UPDATED_MESSAGE] =
30540
30540
  NOTICE_KNOWN_MODULE_IDS[noticeConsts.NOTICE_MODULE_IDENTIFIER.DELETED_MESSAGE] = true;
30541
30541
  NOTICE_KNOWN_MODULE_IDS[noticeConsts.NOTICE_MODULE_IDENTIFIER.UPDATED_DIALOG] = true;
30542
30542
  NOTICE_KNOWN_MODULE_IDS[noticeConsts.NOTICE_MODULE_IDENTIFIER.DELETED_DIALOG] = true;
30543
+ NOTICE_KNOWN_MODULE_IDS[noticeConsts.NOTICE_MODULE_IDENTIFIER.CREATED_DIALOG] = true;
30543
30544
 
30544
30545
  /**
30545
30546
  * Cross-env: get all direct child elements of `parent` whose tag name equals
@@ -30823,6 +30824,91 @@ function _parseCustomData(extraParams) {
30823
30824
  return out;
30824
30825
  }
30825
30826
 
30827
+ /**
30828
+ * Build a partial QBChatDialog object from a dialog-notice <extraParams>.
30829
+ * Shared by NoticeUpdatedDialog and NoticeCreatedDialog (SR-2952) — the server
30830
+ * sends a full dialog snapshot in both cases. Fields with null/empty values are
30831
+ * omitted by the server, so every read is guarded.
30832
+ *
30833
+ * CreatedDialog additionally carries created_at/updated_at/user_id (creator) and
30834
+ * has no last_message* fields (dialog just created); UpdatedDialog carries
30835
+ * last_message* and (per server contract) may also carry created_at/updated_at/
30836
+ * user_id. Reading them here for both is safe — absent fields are simply skipped.
30837
+ *
30838
+ * @param {Element} extraParams
30839
+ * @param {String} dialogId - already-resolved dialog_id text.
30840
+ * @return {Object} partial dialog snapshot.
30841
+ */
30842
+ function _parseDialogSnapshot(extraParams, dialogId) {
30843
+ var dlg = {};
30844
+ dlg._id = dialogId;
30845
+
30846
+ var name = chatUtils.getElementText(extraParams, 'name');
30847
+ if (name !== undefined && name !== null && name !== '') {
30848
+ dlg.name = name;
30849
+ }
30850
+ var photo = chatUtils.getElementText(extraParams, 'photo');
30851
+ if (photo !== undefined && photo !== null && photo !== '') {
30852
+ dlg.photo = photo;
30853
+ }
30854
+ var typeText = chatUtils.getElementText(extraParams, 'type');
30855
+ var typeNum = parseInt(typeText, 10);
30856
+ if (!isNaN(typeNum)) {
30857
+ dlg.type = typeNum;
30858
+ }
30859
+ var occupants = _parseNumericIdsCsv(chatUtils.getElementText(extraParams, 'occupants_ids'));
30860
+ if (occupants.length > 0) {
30861
+ dlg.occupants_ids = occupants;
30862
+ }
30863
+ var admins = _parseNumericIdsCsv(chatUtils.getElementText(extraParams, 'admin_ids'));
30864
+ if (admins.length > 0) {
30865
+ dlg.admin_ids = admins;
30866
+ }
30867
+ var isJoinReqText = chatUtils.getElementText(extraParams, 'is_join_required');
30868
+ if (isJoinReqText !== undefined && isJoinReqText !== null && isJoinReqText !== '') {
30869
+ var isJoinReq = parseInt(isJoinReqText, 10);
30870
+ dlg.is_join_required = isNaN(isJoinReq) ? isJoinReqText : isJoinReq;
30871
+ }
30872
+ var roomJid = chatUtils.getElementText(extraParams, 'xmpp_room_jid');
30873
+ if (roomJid !== undefined && roomJid !== null && roomJid !== '') {
30874
+ // Server may pretty-print xmpp_room_jid with surrounding whitespace.
30875
+ dlg.xmpp_room_jid = roomJid.trim();
30876
+ }
30877
+ var customData = _parseCustomData(extraParams);
30878
+ if (customData !== undefined) {
30879
+ dlg.custom_data = customData;
30880
+ }
30881
+ // Creation / modification metadata (present on CreatedDialog; also valid on
30882
+ // UpdatedDialog per server contract). Kept as ISO-8601 strings verbatim.
30883
+ var createdAt = chatUtils.getElementText(extraParams, 'created_at');
30884
+ if (createdAt !== undefined && createdAt !== null && createdAt !== '') {
30885
+ dlg.created_at = createdAt;
30886
+ }
30887
+ var updatedAt = chatUtils.getElementText(extraParams, 'updated_at');
30888
+ if (updatedAt !== undefined && updatedAt !== null && updatedAt !== '') {
30889
+ dlg.updated_at = updatedAt;
30890
+ }
30891
+ var userIdText = chatUtils.getElementText(extraParams, 'user_id');
30892
+ var userId = parseInt(userIdText, 10);
30893
+ if (!isNaN(userId)) {
30894
+ dlg.user_id = userId;
30895
+ }
30896
+ // Last message fields (present on UpdatedDialog; absent on a freshly
30897
+ // CreatedDialog — the guards below simply skip them then).
30898
+ var lmText = chatUtils.getElementText(extraParams, 'last_message');
30899
+ if (lmText) { dlg.last_message = lmText; }
30900
+ var lmId = chatUtils.getElementText(extraParams, 'last_message_id');
30901
+ if (lmId) { dlg.last_message_id = lmId; }
30902
+ var lmDsText = chatUtils.getElementText(extraParams, 'last_message_date_sent');
30903
+ var lmDs = parseInt(lmDsText, 10);
30904
+ if (!isNaN(lmDs)) { dlg.last_message_date_sent = lmDs; }
30905
+ var lmUidText = chatUtils.getElementText(extraParams, 'last_message_user_id');
30906
+ var lmUid = parseInt(lmUidText, 10);
30907
+ if (!isNaN(lmUid)) { dlg.last_message_user_id = lmUid; }
30908
+
30909
+ return dlg;
30910
+ }
30911
+
30826
30912
  /**
30827
30913
  * Parse a Notice headline stanza into {type, payload} ready for routing.
30828
30914
  * Returns null if stanza is not a Notice (no urn:xmpp:notice:0 namespace, or
@@ -30918,59 +31004,12 @@ function _parseNoticeStanza(stanza) {
30918
31004
  message: msg
30919
31005
  };
30920
31006
  }
30921
- } else if (type === IDS.UPDATED_DIALOG) {
30922
- // Build a partial QBChatDialog object from extraParams (full server snapshot).
30923
- var dlg = {};
30924
- dlg._id = dialogId;
30925
- var name = chatUtils.getElementText(extraParams, 'name');
30926
- if (name !== undefined && name !== null && name !== '') {
30927
- dlg.name = name;
30928
- }
30929
- var photo = chatUtils.getElementText(extraParams, 'photo');
30930
- if (photo !== undefined && photo !== null && photo !== '') {
30931
- dlg.photo = photo;
30932
- }
30933
- var typeText = chatUtils.getElementText(extraParams, 'type');
30934
- var typeNum = parseInt(typeText, 10);
30935
- if (!isNaN(typeNum)) {
30936
- dlg.type = typeNum;
30937
- }
30938
- var occupants = _parseNumericIdsCsv(chatUtils.getElementText(extraParams, 'occupants_ids'));
30939
- if (occupants.length > 0) {
30940
- dlg.occupants_ids = occupants;
30941
- }
30942
- var admins = _parseNumericIdsCsv(chatUtils.getElementText(extraParams, 'admin_ids'));
30943
- if (admins.length > 0) {
30944
- dlg.admin_ids = admins;
30945
- }
30946
- var isJoinReqText = chatUtils.getElementText(extraParams, 'is_join_required');
30947
- if (isJoinReqText !== undefined && isJoinReqText !== null && isJoinReqText !== '') {
30948
- var isJoinReq = parseInt(isJoinReqText, 10);
30949
- dlg.is_join_required = isNaN(isJoinReq) ? isJoinReqText : isJoinReq;
30950
- }
30951
- var roomJid = chatUtils.getElementText(extraParams, 'xmpp_room_jid');
30952
- if (roomJid !== undefined && roomJid !== null && roomJid !== '') {
30953
- dlg.xmpp_room_jid = roomJid;
30954
- }
30955
- var customData = _parseCustomData(extraParams);
30956
- if (customData !== undefined) {
30957
- dlg.custom_data = customData;
30958
- }
30959
- // Last message fields.
30960
- var lmText = chatUtils.getElementText(extraParams, 'last_message');
30961
- if (lmText) { dlg.last_message = lmText; }
30962
- var lmId = chatUtils.getElementText(extraParams, 'last_message_id');
30963
- if (lmId) { dlg.last_message_id = lmId; }
30964
- var lmDsText = chatUtils.getElementText(extraParams, 'last_message_date_sent');
30965
- var lmDs = parseInt(lmDsText, 10);
30966
- if (!isNaN(lmDs)) { dlg.last_message_date_sent = lmDs; }
30967
- var lmUidText = chatUtils.getElementText(extraParams, 'last_message_user_id');
30968
- var lmUid = parseInt(lmUidText, 10);
30969
- if (!isNaN(lmUid)) { dlg.last_message_user_id = lmUid; }
30970
-
31007
+ } else if (type === IDS.UPDATED_DIALOG || type === IDS.CREATED_DIALOG) {
31008
+ // Full server dialog snapshot. UpdatedDialog and CreatedDialog (SR-2952)
31009
+ // share the same shape; they differ only by moduleIdentifier (routing).
30971
31010
  payload = {
30972
31011
  kind: 'dialog',
30973
- dialog: dlg
31012
+ dialog: _parseDialogSnapshot(extraParams, dialogId)
30974
31013
  };
30975
31014
  } else {
30976
31015
  return null;
@@ -31017,6 +31056,10 @@ function _routeNoticeEvent(chatProxy, parsed) {
31017
31056
  if (typeof chatProxy.onDialogUpdatedListener === 'function') {
31018
31057
  Utils.safeCallbackCall(chatProxy.onDialogUpdatedListener, p.dialog);
31019
31058
  }
31059
+ } else if (type === IDS.CREATED_DIALOG) {
31060
+ if (typeof chatProxy.onDialogCreatedListener === 'function') {
31061
+ Utils.safeCallbackCall(chatProxy.onDialogCreatedListener, p.dialog);
31062
+ }
31020
31063
  }
31021
31064
  }
31022
31065
 
@@ -31117,6 +31160,15 @@ function ChatProxy(service) {
31117
31160
  this.onMessageReactionChangedListener = null;
31118
31161
  this.onDialogDeletedListener = null;
31119
31162
  this.onDialogUpdatedListener = null;
31163
+ /**
31164
+ * NoticeCreatedDialog listener (SR-2952). Fired when a Group or Private
31165
+ * dialog is created and the current user has the Notice feature enabled.
31166
+ * Signature: onDialogCreatedListener(dialog). The dialog is a partial
31167
+ * snapshot from the creation notice — it carries created_at/updated_at/
31168
+ * user_id (creator) but no last_message* fields (the dialog is brand new),
31169
+ * and for a private dialog no xmpp_room_jid. Not fired for Public dialogs.
31170
+ */
31171
+ this.onDialogCreatedListener = null;
31120
31172
 
31121
31173
  // [QC-1550] XMPP connection is considered "verified" only after the first
31122
31174
  // successful pong response. Strophe emits Status.CONNECTED at the transport
@@ -32961,7 +33013,8 @@ ChatProxy.prototype = {
32961
33013
  * Subscribe the current XMPP session to Notice Feature stanzas
32962
33014
  * (urn:xmpp:notice:0). After a successful IQ result the server starts
32963
33015
  * delivering NoticeUpdatedMessage / NoticeDeletedMessage /
32964
- * NoticeUpdatedDialog / NoticeDeletedDialog headline stanzas.
33016
+ * NoticeCreatedDialog / NoticeUpdatedDialog / NoticeDeletedDialog
33017
+ * headline stanzas.
32965
33018
  *
32966
33019
  * The local enabled flag is reset to false on chat disconnect — consumers
32967
33020
  * must call enableNotices() again after a reconnect (intentional divergence
@@ -32969,10 +33022,8 @@ ChatProxy.prototype = {
32969
33022
  *
32970
33023
  * TODO(2.25.0): make auto-enable SDK-internal — fire on initial connect and
32971
33024
  * on relogin after session-expired, skip on Stream-Management reconnect.
32972
- * Spec & three-state matrix:
32973
- * __ai-work-log/sdk-js/tasks/ad-hoc-2026-05-05-notice-reaction-admin-role/
32974
- * backlog-2.25.0-auto-enable-notices.md
32975
- * jira-stories/_jira-src-06-auto-enable-notices.md
33025
+ * Spec & three-state matrix are tracked in the internal 2.25.0 backlog
33026
+ * (auto-enable notices).
32976
33027
  *
32977
33028
  * Do not call enableNotices again before the previous callback fires —
32978
33029
  * concurrent enable calls are not specified.
@@ -35158,7 +35209,8 @@ var NOTICE_MODULE_IDENTIFIER = {
35158
35209
  UPDATED_MESSAGE: 'NoticeUpdatedMessage',
35159
35210
  DELETED_MESSAGE: 'NoticeDeletedMessage',
35160
35211
  UPDATED_DIALOG: 'NoticeUpdatedDialog',
35161
- DELETED_DIALOG: 'NoticeDeletedDialog'
35212
+ DELETED_DIALOG: 'NoticeDeletedDialog',
35213
+ CREATED_DIALOG: 'NoticeCreatedDialog'
35162
35214
  };
35163
35215
 
35164
35216
  module.exports = {
@@ -37778,92 +37830,110 @@ function _getStats(peer, lastResults, successCallback, errorCallback) {
37778
37830
 
37779
37831
  peer.getStats(null).then(function (results) {
37780
37832
  results.forEach(function (result) {
37781
- var item;
37782
-
37783
- if (result.bytesReceived && result.type === 'inbound-rtp') {
37784
- item = statistic.remote[result.mediaType];
37785
- item.bitrate = _getBitratePerSecond(result, lastResults, false);
37786
- item.bytesReceived = result.bytesReceived;
37787
- item.packetsReceived = result.packetsReceived;
37788
- item.timestamp = result.timestamp;
37789
- if (result.mediaType === 'video' && result.framerateMean) {
37790
- item.framesPerSecond = Math.round(result.framerateMean * 10) / 10;
37791
- }
37792
- } else if (result.bytesSent && result.type === 'outbound-rtp') {
37793
- item = statistic.local[result.mediaType];
37794
- item.bitrate = _getBitratePerSecond(result, lastResults, true);
37795
- item.bytesSent = result.bytesSent;
37796
- item.packetsSent = result.packetsSent;
37797
- item.timestamp = result.timestamp;
37798
- if (result.mediaType === 'video' && result.framerateMean) {
37799
- item.framesPerSecond = Math.round(result.framerateMean * 10) / 10;
37800
- }
37801
- } else if (result.type === 'local-candidate') {
37802
- item = statistic.local.candidate;
37803
- if (result.candidateType === 'host' && result.mozLocalTransport === 'udp' && result.transport === 'udp') {
37804
- item.protocol = result.transport;
37805
- item.ip = result.ipAddress;
37806
- item.port = result.portNumber;
37807
- } else if (!Helpers.getVersionFirefox()) {
37808
- item.protocol = result.protocol;
37809
- item.ip = result.ip;
37810
- item.port = result.port;
37811
- }
37812
- } else if (result.type === 'remote-candidate') {
37813
- item = statistic.remote.candidate;
37814
- item.protocol = result.protocol || result.transport;
37815
- item.ip = result.ip || result.ipAddress;
37816
- item.port = result.port || result.portNumber;
37817
- } else if (result.type === 'track' && result.kind === 'video' && !Helpers.getVersionFirefox()) {
37818
- if (result.remoteSource) {
37819
- item = statistic.remote.video;
37820
- item.frameHeight = result.frameHeight;
37821
- item.frameWidth = result.frameWidth;
37822
- item.framesPerSecond = _getFramesPerSecond(result, lastResults, false);
37823
- } else {
37824
- item = statistic.local.video;
37825
- item.frameHeight = result.frameHeight;
37826
- item.frameWidth = result.frameWidth;
37827
- item.framesPerSecond = _getFramesPerSecond(result, lastResults, true);
37828
- }
37829
- }
37833
+ _applyStatReport(statistic, result, lastResults);
37830
37834
  });
37831
37835
  successCallback(statistic, results);
37832
37836
  }, errorCallback);
37837
+ }
37838
+
37839
+ function _applyStatReport(statistic, result, lastResults) {
37840
+ var item;
37841
+ // mediaType is deprecated in the W3C webrtc-stats spec and replaced by kind.
37842
+ // Safari/WebKit omits mediaType on some reports and provides only kind, so we
37843
+ // normalize once and use it for both the statistic lookup and the 'video' check.
37844
+ var mediaType = result.mediaType || result.kind;
37845
+
37846
+ if (result.bytesReceived && result.type === 'inbound-rtp') {
37847
+ item = statistic.remote[mediaType];
37833
37848
 
37834
- function _getBitratePerSecond(result, lastResults, isLocal) {
37835
- var lastResult = lastResults && lastResults.get(result.id),
37836
- seconds = lastResult ? ((result.timestamp - lastResult.timestamp) / 1000) : 5,
37837
- kilo = 1024,
37838
- bit = 8,
37839
- bitrate;
37840
-
37841
- if (!lastResult) {
37842
- bitrate = 0;
37843
- } else if (isLocal) {
37844
- bitrate = bit * (result.bytesSent - lastResult.bytesSent) / (kilo * seconds);
37849
+ if (item) {
37850
+ item.bitrate = _getBitratePerSecond(result, lastResults, false);
37851
+ item.bytesReceived = result.bytesReceived;
37852
+ item.packetsReceived = result.packetsReceived;
37853
+ item.timestamp = result.timestamp;
37854
+ if (mediaType === 'video' && result.framerateMean) {
37855
+ item.framesPerSecond = Math.round(result.framerateMean * 10) / 10;
37856
+ }
37845
37857
  } else {
37846
- bitrate = bit * (result.bytesReceived - lastResult.bytesReceived) / (kilo * seconds);
37858
+ Helpers.traceWarning('_getStats: skipping inbound-rtp report with unknown mediaType/kind: ' + mediaType);
37859
+ }
37860
+ } else if (result.bytesSent && result.type === 'outbound-rtp') {
37861
+ item = statistic.local[mediaType];
37862
+
37863
+ if (item) {
37864
+ item.bitrate = _getBitratePerSecond(result, lastResults, true);
37865
+ item.bytesSent = result.bytesSent;
37866
+ item.packetsSent = result.packetsSent;
37867
+ item.timestamp = result.timestamp;
37868
+ if (mediaType === 'video' && result.framerateMean) {
37869
+ item.framesPerSecond = Math.round(result.framerateMean * 10) / 10;
37870
+ }
37871
+ } else {
37872
+ Helpers.traceWarning('_getStats: skipping outbound-rtp report with unknown mediaType/kind: ' + mediaType);
37873
+ }
37874
+ } else if (result.type === 'local-candidate') {
37875
+ item = statistic.local.candidate;
37876
+ if (result.candidateType === 'host' && result.mozLocalTransport === 'udp' && result.transport === 'udp') {
37877
+ item.protocol = result.transport;
37878
+ item.ip = result.ipAddress;
37879
+ item.port = result.portNumber;
37880
+ } else if (!Helpers.getVersionFirefox()) {
37881
+ item.protocol = result.protocol;
37882
+ item.ip = result.ip;
37883
+ item.port = result.port;
37884
+ }
37885
+ } else if (result.type === 'remote-candidate') {
37886
+ item = statistic.remote.candidate;
37887
+ item.protocol = result.protocol || result.transport;
37888
+ item.ip = result.ip || result.ipAddress;
37889
+ item.port = result.port || result.portNumber;
37890
+ } else if (result.type === 'track' && result.kind === 'video' && !Helpers.getVersionFirefox()) {
37891
+ if (result.remoteSource) {
37892
+ item = statistic.remote.video;
37893
+ item.frameHeight = result.frameHeight;
37894
+ item.frameWidth = result.frameWidth;
37895
+ item.framesPerSecond = _getFramesPerSecond(result, lastResults, false);
37896
+ } else {
37897
+ item = statistic.local.video;
37898
+ item.frameHeight = result.frameHeight;
37899
+ item.frameWidth = result.frameWidth;
37900
+ item.framesPerSecond = _getFramesPerSecond(result, lastResults, true);
37847
37901
  }
37902
+ }
37903
+ }
37848
37904
 
37849
- return Math.round(bitrate);
37905
+ function _getBitratePerSecond(result, lastResults, isLocal) {
37906
+ var lastResult = lastResults && lastResults.get(result.id),
37907
+ seconds = lastResult ? ((result.timestamp - lastResult.timestamp) / 1000) : 5,
37908
+ kilo = 1024,
37909
+ bit = 8,
37910
+ bitrate;
37911
+
37912
+ if (!lastResult) {
37913
+ bitrate = 0;
37914
+ } else if (isLocal) {
37915
+ bitrate = bit * (result.bytesSent - lastResult.bytesSent) / (kilo * seconds);
37916
+ } else {
37917
+ bitrate = bit * (result.bytesReceived - lastResult.bytesReceived) / (kilo * seconds);
37850
37918
  }
37851
37919
 
37852
- function _getFramesPerSecond(result, lastResults, isLocal) {
37853
- var lastResult = lastResults && lastResults.get(result.id),
37854
- seconds = lastResult ? ((result.timestamp - lastResult.timestamp) / 1000) : 5,
37855
- framesPerSecond;
37920
+ return Math.round(bitrate);
37921
+ }
37856
37922
 
37857
- if (!lastResult) {
37858
- framesPerSecond = 0;
37859
- } else if (isLocal) {
37860
- framesPerSecond = (result.framesSent - lastResult.framesSent) / seconds;
37861
- } else {
37862
- framesPerSecond = (result.framesReceived - lastResult.framesReceived) / seconds;
37863
- }
37923
+ function _getFramesPerSecond(result, lastResults, isLocal) {
37924
+ var lastResult = lastResults && lastResults.get(result.id),
37925
+ seconds = lastResult ? ((result.timestamp - lastResult.timestamp) / 1000) : 5,
37926
+ framesPerSecond;
37864
37927
 
37865
- return Math.round(framesPerSecond * 10) / 10;
37928
+ if (!lastResult) {
37929
+ framesPerSecond = 0;
37930
+ } else if (isLocal) {
37931
+ framesPerSecond = (result.framesSent - lastResult.framesSent) / seconds;
37932
+ } else {
37933
+ framesPerSecond = (result.framesReceived - lastResult.framesReceived) / seconds;
37866
37934
  }
37935
+
37936
+ return Math.round(framesPerSecond * 10) / 10;
37867
37937
  }
37868
37938
 
37869
37939
  // Find the line in sdpLines[startLine...endLine - 1] that starts with |prefix|
@@ -38050,6 +38120,9 @@ function setMediaBitrate(sdp, media, bitrate) {
38050
38120
  return newLines.join('\n');
38051
38121
  }
38052
38122
 
38123
+ // PRIVATE - exposed for unit tests only, not part of the public SDK contract.
38124
+ qbRTCPeerConnection._applyStatReport = _applyStatReport;
38125
+
38053
38126
  module.exports = qbRTCPeerConnection;
38054
38127
 
38055
38128
  },{"../../qbConfig":152,"./qbWebRTCHelpers":146}],145:[function(require,module,exports){
@@ -40550,8 +40623,8 @@ module.exports = StreamManagement;
40550
40623
  */
40551
40624
 
40552
40625
  var config = {
40553
- version: '2.24.0-beta.1',
40554
- buildNumber: '1179',
40626
+ version: '2.24.1-alpha.0',
40627
+ buildNumber: '1181',
40555
40628
  creds: {
40556
40629
  'appId': 0,
40557
40630
  'authKey': '',