@twilio/conversations 2.0.0-rc.1 → 2.0.0-rc.3

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.
Files changed (35) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +3 -3
  3. package/dist/browser.js +2338 -2672
  4. package/dist/browser.js.map +1 -1
  5. package/dist/docs/assets/js/search.js +1 -1
  6. package/dist/docs/classes/AggregatedDeliveryReceipt.html +22 -6
  7. package/dist/docs/classes/Client.html +78 -33
  8. package/dist/docs/classes/Conversation.html +33 -17
  9. package/dist/docs/classes/DetailedDeliveryReceipt.html +22 -6
  10. package/dist/docs/classes/Media.html +23 -7
  11. package/dist/docs/classes/Message.html +23 -7
  12. package/dist/docs/classes/MessageBuilder.html +3280 -0
  13. package/dist/docs/classes/Participant.html +23 -7
  14. package/dist/docs/classes/PushNotification.html +24 -8
  15. package/dist/docs/classes/RestPaginator.html +28 -9
  16. package/dist/docs/classes/UnsentMessage.html +3144 -0
  17. package/dist/docs/classes/User.html +25 -9
  18. package/dist/docs/index.html +67 -26
  19. package/dist/docs/interfaces/ClientOptions.html +22 -6
  20. package/dist/docs/interfaces/ConversationState.html +22 -6
  21. package/dist/docs/interfaces/CreateConversationOptions.html +22 -6
  22. package/dist/docs/interfaces/LastMessage.html +22 -6
  23. package/dist/docs/interfaces/Paginator.html +3243 -0
  24. package/dist/docs/interfaces/PushNotificationData.html +3168 -0
  25. package/dist/docs/interfaces/SendEmailOptions.html +22 -6
  26. package/dist/docs/interfaces/SendMediaOptions.html +22 -6
  27. package/dist/docs/modules.html +66 -25
  28. package/dist/lib.d.ts +232 -57
  29. package/dist/lib.js +2263 -2126
  30. package/dist/lib.js.map +1 -1
  31. package/dist/react-native.js +487 -868
  32. package/dist/react-native.js.map +1 -1
  33. package/dist/twilio-conversations.js +23616 -23207
  34. package/dist/twilio-conversations.min.js +14 -2
  35. package/package.json +5 -4
@@ -132,13 +132,14 @@ Object.defineProperty(exports, '__esModule', { value: true });
132
132
 
133
133
  var loglevelLog = require('loglevel');
134
134
  var iso8601Duration = require('iso8601-duration');
135
+ var JsonDiff = require('rfc6902');
136
+ var declarativeTypeValidator = require('@twilio/declarative-type-validator');
137
+ var replayEventEmitter = require('@twilio/replay-event-emitter');
135
138
  var operationRetrier = require('@twilio/operation-retrier');
136
139
  var twilsock = require('twilsock');
137
140
  var notifications = require('@twilio/notifications');
138
141
  var twilioSync = require('twilio-sync');
139
142
  var mcsClient = require('@twilio/mcs-client');
140
- var JsonDiff = require('rfc6902');
141
- var declarativeTypeValidator = require('@twilio/declarative-type-validator');
142
143
  var uuid = require('uuid');
143
144
 
144
145
  function _interopNamespace(e) {
@@ -190,478 +191,6 @@ function __metadata(metadataKey, metadataValue) {
190
191
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
191
192
  }
192
193
 
193
- var domain;
194
-
195
- // This constructor is used to store event handlers. Instantiating this is
196
- // faster than explicitly calling `Object.create(null)` to get a "clean" empty
197
- // object (tested with v8 v4.9).
198
- function EventHandlers() {}
199
- EventHandlers.prototype = Object.create(null);
200
-
201
- function EventEmitter() {
202
- EventEmitter.init.call(this);
203
- }
204
-
205
- // nodejs oddity
206
- // require('events') === require('events').EventEmitter
207
- EventEmitter.EventEmitter = EventEmitter;
208
-
209
- EventEmitter.usingDomains = false;
210
-
211
- EventEmitter.prototype.domain = undefined;
212
- EventEmitter.prototype._events = undefined;
213
- EventEmitter.prototype._maxListeners = undefined;
214
-
215
- // By default EventEmitters will print a warning if more than 10 listeners are
216
- // added to it. This is a useful default which helps finding memory leaks.
217
- EventEmitter.defaultMaxListeners = 10;
218
-
219
- EventEmitter.init = function() {
220
- this.domain = null;
221
- if (EventEmitter.usingDomains) {
222
- // if there is an active domain, then attach to it.
223
- if (domain.active ) ;
224
- }
225
-
226
- if (!this._events || this._events === Object.getPrototypeOf(this)._events) {
227
- this._events = new EventHandlers();
228
- this._eventsCount = 0;
229
- }
230
-
231
- this._maxListeners = this._maxListeners || undefined;
232
- };
233
-
234
- // Obviously not all Emitters should be limited to 10. This function allows
235
- // that to be increased. Set to zero for unlimited.
236
- EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
237
- if (typeof n !== 'number' || n < 0 || isNaN(n))
238
- throw new TypeError('"n" argument must be a positive number');
239
- this._maxListeners = n;
240
- return this;
241
- };
242
-
243
- function $getMaxListeners(that) {
244
- if (that._maxListeners === undefined)
245
- return EventEmitter.defaultMaxListeners;
246
- return that._maxListeners;
247
- }
248
-
249
- EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
250
- return $getMaxListeners(this);
251
- };
252
-
253
- // These standalone emit* functions are used to optimize calling of event
254
- // handlers for fast cases because emit() itself often has a variable number of
255
- // arguments and can be deoptimized because of that. These functions always have
256
- // the same number of arguments and thus do not get deoptimized, so the code
257
- // inside them can execute faster.
258
- function emitNone(handler, isFn, self) {
259
- if (isFn)
260
- handler.call(self);
261
- else {
262
- var len = handler.length;
263
- var listeners = arrayClone(handler, len);
264
- for (var i = 0; i < len; ++i)
265
- listeners[i].call(self);
266
- }
267
- }
268
- function emitOne(handler, isFn, self, arg1) {
269
- if (isFn)
270
- handler.call(self, arg1);
271
- else {
272
- var len = handler.length;
273
- var listeners = arrayClone(handler, len);
274
- for (var i = 0; i < len; ++i)
275
- listeners[i].call(self, arg1);
276
- }
277
- }
278
- function emitTwo(handler, isFn, self, arg1, arg2) {
279
- if (isFn)
280
- handler.call(self, arg1, arg2);
281
- else {
282
- var len = handler.length;
283
- var listeners = arrayClone(handler, len);
284
- for (var i = 0; i < len; ++i)
285
- listeners[i].call(self, arg1, arg2);
286
- }
287
- }
288
- function emitThree(handler, isFn, self, arg1, arg2, arg3) {
289
- if (isFn)
290
- handler.call(self, arg1, arg2, arg3);
291
- else {
292
- var len = handler.length;
293
- var listeners = arrayClone(handler, len);
294
- for (var i = 0; i < len; ++i)
295
- listeners[i].call(self, arg1, arg2, arg3);
296
- }
297
- }
298
-
299
- function emitMany(handler, isFn, self, args) {
300
- if (isFn)
301
- handler.apply(self, args);
302
- else {
303
- var len = handler.length;
304
- var listeners = arrayClone(handler, len);
305
- for (var i = 0; i < len; ++i)
306
- listeners[i].apply(self, args);
307
- }
308
- }
309
-
310
- EventEmitter.prototype.emit = function emit(type) {
311
- var er, handler, len, args, i, events, domain;
312
- var doError = (type === 'error');
313
-
314
- events = this._events;
315
- if (events)
316
- doError = (doError && events.error == null);
317
- else if (!doError)
318
- return false;
319
-
320
- domain = this.domain;
321
-
322
- // If there is no 'error' event listener then throw.
323
- if (doError) {
324
- er = arguments[1];
325
- if (domain) {
326
- if (!er)
327
- er = new Error('Uncaught, unspecified "error" event');
328
- er.domainEmitter = this;
329
- er.domain = domain;
330
- er.domainThrown = false;
331
- domain.emit('error', er);
332
- } else if (er instanceof Error) {
333
- throw er; // Unhandled 'error' event
334
- } else {
335
- // At least give some kind of context to the user
336
- var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
337
- err.context = er;
338
- throw err;
339
- }
340
- return false;
341
- }
342
-
343
- handler = events[type];
344
-
345
- if (!handler)
346
- return false;
347
-
348
- var isFn = typeof handler === 'function';
349
- len = arguments.length;
350
- switch (len) {
351
- // fast cases
352
- case 1:
353
- emitNone(handler, isFn, this);
354
- break;
355
- case 2:
356
- emitOne(handler, isFn, this, arguments[1]);
357
- break;
358
- case 3:
359
- emitTwo(handler, isFn, this, arguments[1], arguments[2]);
360
- break;
361
- case 4:
362
- emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]);
363
- break;
364
- // slower
365
- default:
366
- args = new Array(len - 1);
367
- for (i = 1; i < len; i++)
368
- args[i - 1] = arguments[i];
369
- emitMany(handler, isFn, this, args);
370
- }
371
-
372
- return true;
373
- };
374
-
375
- function _addListener(target, type, listener, prepend) {
376
- var m;
377
- var events;
378
- var existing;
379
-
380
- if (typeof listener !== 'function')
381
- throw new TypeError('"listener" argument must be a function');
382
-
383
- events = target._events;
384
- if (!events) {
385
- events = target._events = new EventHandlers();
386
- target._eventsCount = 0;
387
- } else {
388
- // To avoid recursion in the case that type === "newListener"! Before
389
- // adding it to the listeners, first emit "newListener".
390
- if (events.newListener) {
391
- target.emit('newListener', type,
392
- listener.listener ? listener.listener : listener);
393
-
394
- // Re-assign `events` because a newListener handler could have caused the
395
- // this._events to be assigned to a new object
396
- events = target._events;
397
- }
398
- existing = events[type];
399
- }
400
-
401
- if (!existing) {
402
- // Optimize the case of one listener. Don't need the extra array object.
403
- existing = events[type] = listener;
404
- ++target._eventsCount;
405
- } else {
406
- if (typeof existing === 'function') {
407
- // Adding the second element, need to change to array.
408
- existing = events[type] = prepend ? [listener, existing] :
409
- [existing, listener];
410
- } else {
411
- // If we've already got an array, just append.
412
- if (prepend) {
413
- existing.unshift(listener);
414
- } else {
415
- existing.push(listener);
416
- }
417
- }
418
-
419
- // Check for listener leak
420
- if (!existing.warned) {
421
- m = $getMaxListeners(target);
422
- if (m && m > 0 && existing.length > m) {
423
- existing.warned = true;
424
- var w = new Error('Possible EventEmitter memory leak detected. ' +
425
- existing.length + ' ' + type + ' listeners added. ' +
426
- 'Use emitter.setMaxListeners() to increase limit');
427
- w.name = 'MaxListenersExceededWarning';
428
- w.emitter = target;
429
- w.type = type;
430
- w.count = existing.length;
431
- emitWarning(w);
432
- }
433
- }
434
- }
435
-
436
- return target;
437
- }
438
- function emitWarning(e) {
439
- typeof console.warn === 'function' ? console.warn(e) : console.log(e);
440
- }
441
- EventEmitter.prototype.addListener = function addListener(type, listener) {
442
- return _addListener(this, type, listener, false);
443
- };
444
-
445
- EventEmitter.prototype.on = EventEmitter.prototype.addListener;
446
-
447
- EventEmitter.prototype.prependListener =
448
- function prependListener(type, listener) {
449
- return _addListener(this, type, listener, true);
450
- };
451
-
452
- function _onceWrap(target, type, listener) {
453
- var fired = false;
454
- function g() {
455
- target.removeListener(type, g);
456
- if (!fired) {
457
- fired = true;
458
- listener.apply(target, arguments);
459
- }
460
- }
461
- g.listener = listener;
462
- return g;
463
- }
464
-
465
- EventEmitter.prototype.once = function once(type, listener) {
466
- if (typeof listener !== 'function')
467
- throw new TypeError('"listener" argument must be a function');
468
- this.on(type, _onceWrap(this, type, listener));
469
- return this;
470
- };
471
-
472
- EventEmitter.prototype.prependOnceListener =
473
- function prependOnceListener(type, listener) {
474
- if (typeof listener !== 'function')
475
- throw new TypeError('"listener" argument must be a function');
476
- this.prependListener(type, _onceWrap(this, type, listener));
477
- return this;
478
- };
479
-
480
- // emits a 'removeListener' event iff the listener was removed
481
- EventEmitter.prototype.removeListener =
482
- function removeListener(type, listener) {
483
- var list, events, position, i, originalListener;
484
-
485
- if (typeof listener !== 'function')
486
- throw new TypeError('"listener" argument must be a function');
487
-
488
- events = this._events;
489
- if (!events)
490
- return this;
491
-
492
- list = events[type];
493
- if (!list)
494
- return this;
495
-
496
- if (list === listener || (list.listener && list.listener === listener)) {
497
- if (--this._eventsCount === 0)
498
- this._events = new EventHandlers();
499
- else {
500
- delete events[type];
501
- if (events.removeListener)
502
- this.emit('removeListener', type, list.listener || listener);
503
- }
504
- } else if (typeof list !== 'function') {
505
- position = -1;
506
-
507
- for (i = list.length; i-- > 0;) {
508
- if (list[i] === listener ||
509
- (list[i].listener && list[i].listener === listener)) {
510
- originalListener = list[i].listener;
511
- position = i;
512
- break;
513
- }
514
- }
515
-
516
- if (position < 0)
517
- return this;
518
-
519
- if (list.length === 1) {
520
- list[0] = undefined;
521
- if (--this._eventsCount === 0) {
522
- this._events = new EventHandlers();
523
- return this;
524
- } else {
525
- delete events[type];
526
- }
527
- } else {
528
- spliceOne(list, position);
529
- }
530
-
531
- if (events.removeListener)
532
- this.emit('removeListener', type, originalListener || listener);
533
- }
534
-
535
- return this;
536
- };
537
-
538
- // Alias for removeListener added in NodeJS 10.0
539
- // https://nodejs.org/api/events.html#events_emitter_off_eventname_listener
540
- EventEmitter.prototype.off = function(type, listener){
541
- return this.removeListener(type, listener);
542
- };
543
-
544
- EventEmitter.prototype.removeAllListeners =
545
- function removeAllListeners(type) {
546
- var listeners, events;
547
-
548
- events = this._events;
549
- if (!events)
550
- return this;
551
-
552
- // not listening for removeListener, no need to emit
553
- if (!events.removeListener) {
554
- if (arguments.length === 0) {
555
- this._events = new EventHandlers();
556
- this._eventsCount = 0;
557
- } else if (events[type]) {
558
- if (--this._eventsCount === 0)
559
- this._events = new EventHandlers();
560
- else
561
- delete events[type];
562
- }
563
- return this;
564
- }
565
-
566
- // emit removeListener for all listeners on all events
567
- if (arguments.length === 0) {
568
- var keys = Object.keys(events);
569
- for (var i = 0, key; i < keys.length; ++i) {
570
- key = keys[i];
571
- if (key === 'removeListener') continue;
572
- this.removeAllListeners(key);
573
- }
574
- this.removeAllListeners('removeListener');
575
- this._events = new EventHandlers();
576
- this._eventsCount = 0;
577
- return this;
578
- }
579
-
580
- listeners = events[type];
581
-
582
- if (typeof listeners === 'function') {
583
- this.removeListener(type, listeners);
584
- } else if (listeners) {
585
- // LIFO order
586
- do {
587
- this.removeListener(type, listeners[listeners.length - 1]);
588
- } while (listeners[0]);
589
- }
590
-
591
- return this;
592
- };
593
-
594
- EventEmitter.prototype.listeners = function listeners(type) {
595
- var evlistener;
596
- var ret;
597
- var events = this._events;
598
-
599
- if (!events)
600
- ret = [];
601
- else {
602
- evlistener = events[type];
603
- if (!evlistener)
604
- ret = [];
605
- else if (typeof evlistener === 'function')
606
- ret = [evlistener.listener || evlistener];
607
- else
608
- ret = unwrapListeners(evlistener);
609
- }
610
-
611
- return ret;
612
- };
613
-
614
- EventEmitter.listenerCount = function(emitter, type) {
615
- if (typeof emitter.listenerCount === 'function') {
616
- return emitter.listenerCount(type);
617
- } else {
618
- return listenerCount.call(emitter, type);
619
- }
620
- };
621
-
622
- EventEmitter.prototype.listenerCount = listenerCount;
623
- function listenerCount(type) {
624
- var events = this._events;
625
-
626
- if (events) {
627
- var evlistener = events[type];
628
-
629
- if (typeof evlistener === 'function') {
630
- return 1;
631
- } else if (evlistener) {
632
- return evlistener.length;
633
- }
634
- }
635
-
636
- return 0;
637
- }
638
-
639
- EventEmitter.prototype.eventNames = function eventNames() {
640
- return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : [];
641
- };
642
-
643
- // About 1.5x faster than the two-arg version of Array#splice().
644
- function spliceOne(list, index) {
645
- for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1)
646
- list[i] = list[k];
647
- list.pop();
648
- }
649
-
650
- function arrayClone(arr, i) {
651
- var copy = new Array(i);
652
- while (i--)
653
- copy[i] = arr[i];
654
- return copy;
655
- }
656
-
657
- function unwrapListeners(arr) {
658
- var ret = new Array(arr.length);
659
- for (var i = 0; i < ret.length; ++i) {
660
- ret[i] = arr[i].listener || arr[i];
661
- }
662
- return ret;
663
- }
664
-
665
194
  function prepareLine(prefix, args) {
666
195
  return [`${new Date().toISOString()} Conversations ${prefix}:`].concat(Array.from(args));
667
196
  }
@@ -754,84 +283,6 @@ class Configuration {
754
283
  }
755
284
  }
756
285
 
757
- class Network {
758
- constructor(configuration, services) {
759
- this.configuration = configuration;
760
- this.services = services;
761
- this.cache = new Map();
762
- this.cacheLifetime = this.configuration.httpCacheInterval * 100;
763
- this.cleanupCache();
764
- }
765
- isExpired(timestamp) {
766
- return !this.cacheLifetime || (Date.now() - timestamp) > this.cacheLifetime;
767
- }
768
- cleanupCache() {
769
- for (let [k, v] of this.cache) {
770
- if (this.isExpired(v.timestamp)) {
771
- this.cache.delete(k);
772
- }
773
- }
774
- if (this.cache.size === 0) {
775
- clearInterval(this.timer);
776
- }
777
- }
778
- pokeTimer() {
779
- this.timer = this.timer || setInterval(() => this.cleanupCache(), this.cacheLifetime * 2);
780
- }
781
- executeWithRetry(request, retryWhenThrottled = false) {
782
- return new Promise((resolve, reject) => {
783
- let codesToRetryOn = [502, 503, 504];
784
- if (retryWhenThrottled) {
785
- codesToRetryOn.push(429);
786
- }
787
- let retrier = new operationRetrier.Retrier(this.configuration.backoffConfiguration);
788
- retrier.on('attempt', () => {
789
- request()
790
- .then(result => retrier.succeeded(result))
791
- .catch(err => {
792
- if (codesToRetryOn.indexOf(err.status) > -1) {
793
- retrier.failed(err);
794
- }
795
- else if (err.message === 'Twilsock disconnected') {
796
- // Ugly hack. We must make a proper exceptions for twilsock
797
- retrier.failed(err);
798
- }
799
- else {
800
- // Fatal error
801
- retrier.removeAllListeners();
802
- retrier.cancel();
803
- reject(err);
804
- }
805
- });
806
- });
807
- retrier.on('succeeded', result => { resolve(result); });
808
- retrier.on('cancelled', err => reject(err));
809
- retrier.on('failed', err => reject(err));
810
- retrier.start();
811
- });
812
- }
813
- async get(url) {
814
- let cacheEntry = this.cache.get(url);
815
- if (cacheEntry && !this.isExpired(cacheEntry.timestamp)) {
816
- return cacheEntry.response;
817
- }
818
- const headers = {};
819
- let response = await this.executeWithRetry(() => this.services.transport.get(url, headers, this.configuration.productId), this.configuration.retryWhenThrottled);
820
- this.cache.set(url, { response, timestamp: Date.now() });
821
- this.pokeTimer();
822
- return response;
823
- }
824
- }
825
-
826
- class NotificationTypes {
827
- }
828
- NotificationTypes.TYPING_INDICATOR = 'twilio.ipmsg.typing_indicator';
829
- NotificationTypes.NEW_MESSAGE = 'twilio.conversations.new_message';
830
- NotificationTypes.ADDED_TO_CONVERSATION = 'twilio.conversations.added_to_conversation';
831
- // static readonly INVITED_TO_CHANNEL = 'twilio.channel.invited_to_channel';
832
- NotificationTypes.REMOVED_FROM_CONVERSATION = 'twilio.conversations.removed_from_conversation';
833
- NotificationTypes.CONSUMPTION_UPDATE = 'twilio.channel.consumption_update';
834
-
835
286
  /**
836
287
  * Checks if objects are equal
837
288
  */
@@ -906,11 +357,336 @@ class UriBuilder {
906
357
  }
907
358
  }
908
359
 
909
- const log$8 = Logger.scope('Participant');
360
+ const log$8 = Logger.scope('User');
361
+ /**
362
+ * Extended user information.
363
+ * Note that `isOnline` and `isNotifiable` properties are eligible
364
+ * for use only if the reachability function is enabled.
365
+ * You may check if it is enabled by reading the value of {@link Client.reachabilityEnabled}.
366
+ */
367
+ class User extends replayEventEmitter.ReplayEventEmitter {
368
+ /**
369
+ * @internal
370
+ */
371
+ constructor(identity, entityName, configuration, services) {
372
+ super();
373
+ this.promiseToFetch = null;
374
+ /**
375
+ * Fired when the properties or the reachability status of the message has been updated.
376
+ *
377
+ * Parameters:
378
+ * 1. object `data` - info object provided with the event. It has the following properties:
379
+ * * {@link User} `user` - the user in question
380
+ * * {@link UserUpdateReason}[] `updateReasons` - array of reasons for the update
381
+ * @event
382
+ */
383
+ this.updated = 'updated';
384
+ /**
385
+ * Fired when the client has subscribed to the user.
386
+ *
387
+ * Parameters:
388
+ * 1. {@link User} `user` - the user in question
389
+ * @event
390
+ */
391
+ this.userSubscribed = 'userSubscribed';
392
+ /**
393
+ * Fired when the client has unsubscribed from the user.
394
+ *
395
+ * Parameters:
396
+ * 1. {@link User} `user` - the user in question
397
+ * @event
398
+ */
399
+ this.userUnsubscribed = 'userUnsubscribed';
400
+ this.services = services;
401
+ this.subscribed = 'initializing';
402
+ this.setMaxListeners(0);
403
+ this.state = {
404
+ identity,
405
+ entityName,
406
+ friendlyName: null,
407
+ attributes: {},
408
+ online: null,
409
+ notifiable: null
410
+ };
411
+ this._initializationPromise = new Promise((resolve) => {
412
+ this._resolveInitializationPromise = resolve;
413
+ });
414
+ if (configuration !== null) {
415
+ this._resolveInitialization(configuration, identity, entityName, false);
416
+ }
417
+ }
418
+ /**
419
+ * User identity.
420
+ */
421
+ get identity() { return this.state.identity; }
422
+ set identity(identity) { this.state.identity = identity; }
423
+ set entityName(name) { this.state.entityName = name; }
424
+ /**
425
+ * Custom attributes of the user.
426
+ */
427
+ get attributes() { return this.state.attributes; }
428
+ /**
429
+ * Friendly name of the user, null if not set.
430
+ */
431
+ get friendlyName() { return this.state.friendlyName; }
432
+ /**
433
+ * Status of the real-time conversation connection of the user.
434
+ */
435
+ get isOnline() { return this.state.online; }
436
+ /**
437
+ * User push notification registration status.
438
+ */
439
+ get isNotifiable() { return this.state.notifiable; }
440
+ /**
441
+ * True if this user is receiving real-time status updates.
442
+ */
443
+ get isSubscribed() { return this.subscribed == 'subscribed'; }
444
+ // Handles service updates
445
+ async _update(key, value) {
446
+ await this._initializationPromise;
447
+ let updateReasons = [];
448
+ log$8.debug('User for', this.state.identity, 'updated:', key, value);
449
+ switch (key) {
450
+ case 'friendlyName':
451
+ if (this.state.friendlyName !== value.value) {
452
+ updateReasons.push('friendlyName');
453
+ this.state.friendlyName = value.value;
454
+ }
455
+ break;
456
+ case 'attributes':
457
+ const updateAttributes = parseAttributes(value.value, `Retrieved malformed attributes from the server for user: ${this.state.identity}`, log$8);
458
+ if (!isDeepEqual(this.state.attributes, updateAttributes)) {
459
+ this.state.attributes = updateAttributes;
460
+ updateReasons.push('attributes');
461
+ }
462
+ break;
463
+ case 'reachability':
464
+ if (this.state.online !== value.online) {
465
+ this.state.online = value.online;
466
+ updateReasons.push('reachabilityOnline');
467
+ }
468
+ if (this.state.notifiable !== value.notifiable) {
469
+ this.state.notifiable = value.notifiable;
470
+ updateReasons.push('reachabilityNotifiable');
471
+ }
472
+ break;
473
+ default:
474
+ return;
475
+ }
476
+ if (updateReasons.length > 0) {
477
+ this.emit('updated', { user: this, updateReasons: updateReasons });
478
+ }
479
+ }
480
+ // Fetch reachability info
481
+ async _updateReachabilityInfo(map, update) {
482
+ await this._initializationPromise;
483
+ if (!this.configuration.reachabilityEnabled) {
484
+ return Promise.resolve();
485
+ }
486
+ return map.get('reachability')
487
+ .then(update)
488
+ .catch(err => { log$8.warn('Failed to get reachability info for ', this.state.identity, err); });
489
+ }
490
+ // Fetch user
491
+ async _fetch() {
492
+ await this._initializationPromise;
493
+ if (!this.state.entityName) {
494
+ return this;
495
+ }
496
+ this.promiseToFetch = this.services.syncClient.map({
497
+ id: this.state.entityName,
498
+ mode: 'open_existing',
499
+ includeItems: true
500
+ })
501
+ .then(map => {
502
+ this.entity = map;
503
+ map.on('itemUpdated', args => {
504
+ log$8.debug(this.state.entityName + ' (' + this.state.identity + ') itemUpdated: ' + args.item.key);
505
+ return this._update(args.item.key, args.item.data);
506
+ });
507
+ return Promise.all([
508
+ map.get('friendlyName')
509
+ .then(item => this._update(item.key, item.data)),
510
+ map.get('attributes')
511
+ .then(item => this._update(item.key, item.data)),
512
+ this._updateReachabilityInfo(map, item => this._update(item.key, item.data))
513
+ ]);
514
+ })
515
+ .then(() => {
516
+ log$8.debug('Fetched for', this.identity);
517
+ this.subscribed = 'subscribed';
518
+ this.emit('userSubscribed', this);
519
+ return this;
520
+ })
521
+ .catch(err => {
522
+ this.promiseToFetch = null;
523
+ throw err;
524
+ });
525
+ return this.promiseToFetch;
526
+ }
527
+ async _ensureFetched() {
528
+ await this._initializationPromise;
529
+ return this.promiseToFetch || this._fetch();
530
+ }
531
+ /**
532
+ * Edit user attributes.
533
+ * @param attributes New attributes.
534
+ */
535
+ async updateAttributes(attributes) {
536
+ await this._initializationPromise;
537
+ if (this.subscribed == 'unsubscribed') {
538
+ throw new Error('Can\'t modify unsubscribed object');
539
+ }
540
+ await this.services.commandExecutor.mutateResource('post', this.links.self, {
541
+ attributes: JSON.stringify(attributes)
542
+ });
543
+ return this;
544
+ }
545
+ /**
546
+ * Update the friendly name of the user.
547
+ * @param friendlyName New friendly name.
548
+ */
549
+ async updateFriendlyName(friendlyName) {
550
+ await this._initializationPromise;
551
+ if (this.subscribed == 'unsubscribed') {
552
+ throw new Error('Can\'t modify unsubscribed object');
553
+ }
554
+ await this.services.commandExecutor.mutateResource('post', this.links.self, {
555
+ friendly_name: friendlyName
556
+ });
557
+ return this;
558
+ }
559
+ /**
560
+ * Remove the user from the subscription list.
561
+ * @return A promise of completion.
562
+ */
563
+ async unsubscribe() {
564
+ await this._initializationPromise;
565
+ if (this.promiseToFetch) {
566
+ await this.promiseToFetch;
567
+ this.entity.close();
568
+ this.promiseToFetch = null;
569
+ this.subscribed = 'unsubscribed';
570
+ this.emit('userUnsubscribed', this);
571
+ }
572
+ }
573
+ _resolveInitialization(configuration, identity, entityName, emitUpdated) {
574
+ this.configuration = configuration;
575
+ this.identity = identity;
576
+ this.entityName = entityName;
577
+ this.links = {
578
+ self: `${this.configuration.links.users}/${this.identity}`
579
+ };
580
+ this._resolveInitializationPromise();
581
+ if (emitUpdated) {
582
+ this.emit('updated', {
583
+ user: this,
584
+ updateReasons: [
585
+ 'friendlyName',
586
+ 'attributes',
587
+ 'reachabilityOnline',
588
+ 'reachabilityNotifiable'
589
+ ]
590
+ });
591
+ }
592
+ }
593
+ }
594
+ __decorate([
595
+ declarativeTypeValidator.validateTypesAsync(['string', 'number', 'boolean', 'object', declarativeTypeValidator.literal(null)]),
596
+ __metadata("design:type", Function),
597
+ __metadata("design:paramtypes", [Object]),
598
+ __metadata("design:returntype", Promise)
599
+ ], User.prototype, "updateAttributes", null);
600
+ __decorate([
601
+ declarativeTypeValidator.validateTypesAsync(['string']),
602
+ __metadata("design:type", Function),
603
+ __metadata("design:paramtypes", [String]),
604
+ __metadata("design:returntype", Promise)
605
+ ], User.prototype, "updateFriendlyName", null);
606
+
607
+ class Network {
608
+ constructor(configuration, services) {
609
+ this.configuration = configuration;
610
+ this.services = services;
611
+ this.cache = new Map();
612
+ this.cacheLifetime = this.configuration.httpCacheInterval * 100;
613
+ this.cleanupCache();
614
+ }
615
+ isExpired(timestamp) {
616
+ return !this.cacheLifetime || (Date.now() - timestamp) > this.cacheLifetime;
617
+ }
618
+ cleanupCache() {
619
+ for (let [k, v] of this.cache) {
620
+ if (this.isExpired(v.timestamp)) {
621
+ this.cache.delete(k);
622
+ }
623
+ }
624
+ if (this.cache.size === 0) {
625
+ clearInterval(this.timer);
626
+ }
627
+ }
628
+ pokeTimer() {
629
+ this.timer = this.timer || setInterval(() => this.cleanupCache(), this.cacheLifetime * 2);
630
+ }
631
+ executeWithRetry(request, retryWhenThrottled = false) {
632
+ return new Promise((resolve, reject) => {
633
+ let codesToRetryOn = [502, 503, 504];
634
+ if (retryWhenThrottled) {
635
+ codesToRetryOn.push(429);
636
+ }
637
+ let retrier = new operationRetrier.Retrier(this.configuration.backoffConfiguration);
638
+ retrier.on('attempt', () => {
639
+ request()
640
+ .then(result => retrier.succeeded(result))
641
+ .catch(err => {
642
+ if (codesToRetryOn.indexOf(err.status) > -1) {
643
+ retrier.failed(err);
644
+ }
645
+ else if (err.message === 'Twilsock disconnected') {
646
+ // Ugly hack. We must make a proper exceptions for twilsock
647
+ retrier.failed(err);
648
+ }
649
+ else {
650
+ // Fatal error
651
+ retrier.removeAllListeners();
652
+ retrier.cancel();
653
+ reject(err);
654
+ }
655
+ });
656
+ });
657
+ retrier.on('succeeded', result => { resolve(result); });
658
+ retrier.on('cancelled', err => reject(err));
659
+ retrier.on('failed', err => reject(err));
660
+ retrier.start();
661
+ });
662
+ }
663
+ async get(url) {
664
+ let cacheEntry = this.cache.get(url);
665
+ if (cacheEntry && !this.isExpired(cacheEntry.timestamp)) {
666
+ return cacheEntry.response;
667
+ }
668
+ const headers = {};
669
+ let response = await this.executeWithRetry(() => this.services.transport.get(url, headers, this.configuration.productId), this.configuration.retryWhenThrottled);
670
+ this.cache.set(url, { response, timestamp: Date.now() });
671
+ this.pokeTimer();
672
+ return response;
673
+ }
674
+ }
675
+
676
+ class NotificationTypes {
677
+ }
678
+ NotificationTypes.TYPING_INDICATOR = 'twilio.ipmsg.typing_indicator';
679
+ NotificationTypes.NEW_MESSAGE = 'twilio.conversations.new_message';
680
+ NotificationTypes.ADDED_TO_CONVERSATION = 'twilio.conversations.added_to_conversation';
681
+ // static readonly INVITED_TO_CHANNEL = 'twilio.channel.invited_to_channel';
682
+ NotificationTypes.REMOVED_FROM_CONVERSATION = 'twilio.conversations.removed_from_conversation';
683
+ NotificationTypes.CONSUMPTION_UPDATE = 'twilio.channel.consumption_update';
684
+
685
+ const log$7 = Logger.scope('Participant');
910
686
  /**
911
687
  * A participant represents a remote client in a conversation.
912
688
  */
913
- class Participant extends EventEmitter {
689
+ class Participant extends replayEventEmitter.ReplayEventEmitter {
914
690
  /**
915
691
  * @internal
916
692
  */
@@ -920,7 +696,7 @@ class Participant extends EventEmitter {
920
696
  this.links = links;
921
697
  this.services = services;
922
698
  this.state = {
923
- attributes: parseAttributes(data.attributes, 'Retrieved malformed attributes from the server for participant: ' + sid, log$8),
699
+ attributes: parseAttributes(data.attributes, 'Retrieved malformed attributes from the server for participant: ' + sid, log$7),
924
700
  dateCreated: data.dateCreated ? parseTime$1(data.dateCreated) : null,
925
701
  dateUpdated: data.dateCreated ? parseTime$1(data.dateUpdated) : null,
926
702
  sid: sid,
@@ -1009,7 +785,7 @@ class Participant extends EventEmitter {
1009
785
  */
1010
786
  _update(data) {
1011
787
  let updateReasons = [];
1012
- let updateAttributes = parseAttributes(data.attributes, 'Retrieved malformed attributes from the server for participant: ' + this.state.sid, log$8);
788
+ let updateAttributes = parseAttributes(data.attributes, 'Retrieved malformed attributes from the server for participant: ' + this.state.sid, log$7);
1013
789
  if (data.attributes && !isDeepEqual(this.state.attributes, updateAttributes)) {
1014
790
  this.state.attributes = updateAttributes;
1015
791
  updateReasons.push('attributes');
@@ -1107,14 +883,14 @@ __decorate([
1107
883
  __metadata("design:returntype", Promise)
1108
884
  ], Participant.prototype, "updateAttributes", null);
1109
885
 
1110
- const log$7 = Logger.scope('Participants');
886
+ const log$6 = Logger.scope('Participants');
1111
887
  /**
1112
888
  * @classdesc Represents the collection of participants for the conversation
1113
889
  * @fires Participants#participantJoined
1114
890
  * @fires Participants#participantLeft
1115
891
  * @fires Participants#participantUpdated
1116
892
  */
1117
- class Participants extends EventEmitter {
893
+ class Participants extends replayEventEmitter.ReplayEventEmitter {
1118
894
  constructor(conversation, participants, links, configuration, services) {
1119
895
  super();
1120
896
  this.conversation = conversation;
@@ -1135,14 +911,14 @@ class Participants extends EventEmitter {
1135
911
  || this.services.syncClient.map({ id: rosterObjectName, mode: 'open_existing' })
1136
912
  .then(rosterMap => {
1137
913
  rosterMap.on('itemAdded', args => {
1138
- log$7.debug(this.conversation.sid + ' itemAdded: ' + args.item.key);
914
+ log$6.debug(this.conversation.sid + ' itemAdded: ' + args.item.key);
1139
915
  this.upsertParticipant(args.item.key, args.item.data)
1140
916
  .then(participant => {
1141
917
  this.emit('participantJoined', participant);
1142
918
  });
1143
919
  });
1144
920
  rosterMap.on('itemRemoved', args => {
1145
- log$7.debug(this.conversation.sid + ' itemRemoved: ' + args.key);
921
+ log$6.debug(this.conversation.sid + ' itemRemoved: ' + args.key);
1146
922
  let participantSid = args.key;
1147
923
  if (!this.participants.has(participantSid)) {
1148
924
  return;
@@ -1152,7 +928,7 @@ class Participants extends EventEmitter {
1152
928
  this.emit('participantLeft', leftParticipant);
1153
929
  });
1154
930
  rosterMap.on('itemUpdated', args => {
1155
- log$7.debug(this.conversation.sid + ' itemUpdated: ' + args.item.key);
931
+ log$6.debug(this.conversation.sid + ' itemUpdated: ' + args.item.key);
1156
932
  this.upsertParticipant(args.item.key, args.item.data);
1157
933
  });
1158
934
  let participantsPromises = [];
@@ -1170,9 +946,9 @@ class Participants extends EventEmitter {
1170
946
  .catch(err => {
1171
947
  this.rosterEntityPromise = null;
1172
948
  if (this.services.syncClient.connectionState != 'disconnected') {
1173
- log$7.error('Failed to get roster object for conversation', this.conversation.sid, err);
949
+ log$6.error('Failed to get roster object for conversation', this.conversation.sid, err);
1174
950
  }
1175
- log$7.debug('ERROR: Failed to get roster object for conversation', this.conversation.sid, err);
951
+ log$6.debug('ERROR: Failed to get roster object for conversation', this.conversation.sid, err);
1176
952
  throw err;
1177
953
  });
1178
954
  }
@@ -1248,9 +1024,9 @@ class Participants extends EventEmitter {
1248
1024
  * @param attributes
1249
1025
  * @returns {Promise<any>}
1250
1026
  */
1251
- addNonChatParticipant(proxyAddress, address, attributes = {}) {
1027
+ addNonChatParticipant(proxyAddress, address, attributes) {
1252
1028
  return this.services.commandExecutor.mutateResource('post', this.links.participants, {
1253
- attributes: JSON.stringify(attributes),
1029
+ attributes: typeof attributes !== 'undefined' ? JSON.stringify(attributes) : undefined,
1254
1030
  messaging_binding: {
1255
1031
  address,
1256
1032
  proxy_address: proxyAddress
@@ -1320,7 +1096,7 @@ class Media {
1320
1096
  */
1321
1097
  get size() { return this.state.size; }
1322
1098
  /**
1323
- * Media category, can be one of the {MediaCategory} values.
1099
+ * Media category, can be one of the {@link MediaCategory} values.
1324
1100
  */
1325
1101
  get category() { return this.state.category; }
1326
1102
  /**
@@ -1513,11 +1289,11 @@ class DetailedDeliveryReceipt {
1513
1289
  }
1514
1290
  }
1515
1291
 
1516
- const log$6 = Logger.scope('Message');
1292
+ const log$5 = Logger.scope('Message');
1517
1293
  /**
1518
1294
  * A message in a conversation.
1519
1295
  */
1520
- class Message extends EventEmitter {
1296
+ class Message extends replayEventEmitter.ReplayEventEmitter {
1521
1297
  /**
1522
1298
  * @internal
1523
1299
  */
@@ -1537,7 +1313,7 @@ class Message extends EventEmitter {
1537
1313
  timestamp: data.timestamp ? new Date(data.timestamp) : null,
1538
1314
  dateUpdated: data.dateUpdated ? new Date(data.dateUpdated) : null,
1539
1315
  lastUpdatedBy: (_c = data.lastUpdatedBy) !== null && _c !== void 0 ? _c : null,
1540
- attributes: parseAttributes(data.attributes, `Got malformed attributes for the message ${data.sid}`, log$6),
1316
+ attributes: parseAttributes(data.attributes, `Got malformed attributes for the message ${data.sid}`, log$5),
1541
1317
  type: (_d = data.type) !== null && _d !== void 0 ? _d : 'text',
1542
1318
  media: (data.type && data.type === 'media' && data.media)
1543
1319
  ? new Media(data.media, this.services) : null,
@@ -1652,7 +1428,7 @@ class Message extends EventEmitter {
1652
1428
  this.state.timestamp = new Date(data.timestamp);
1653
1429
  updateReasons.push('dateCreated');
1654
1430
  }
1655
- let updatedAttributes = parseAttributes(data.attributes, `Got malformed attributes for the message ${this.sid}`, log$6);
1431
+ let updatedAttributes = parseAttributes(data.attributes, `Got malformed attributes for the message ${this.sid}`, log$5);
1656
1432
  if (!isDeepEqual(this.state.attributes, updatedAttributes)) {
1657
1433
  this.state.attributes = updatedAttributes;
1658
1434
  updateReasons.push('attributes');
@@ -1684,14 +1460,14 @@ class Message extends EventEmitter {
1684
1460
  if (this.state.participantSid) {
1685
1461
  participant = await this.conversation.getParticipantBySid(this.participantSid)
1686
1462
  .catch(() => {
1687
- log$6.debug(`Participant with sid "${this.participantSid}" not found for message ${this.sid}`);
1463
+ log$5.debug(`Participant with sid "${this.participantSid}" not found for message ${this.sid}`);
1688
1464
  return null;
1689
1465
  });
1690
1466
  }
1691
1467
  if (!participant && this.state.author) {
1692
1468
  participant = await this.conversation.getParticipantByIdentity(this.state.author)
1693
1469
  .catch(() => {
1694
- log$6.debug(`Participant with identity "${this.author}" not found for message ${this.sid}`);
1470
+ log$5.debug(`Participant with identity "${this.author}" not found for message ${this.sid}`);
1695
1471
  return null;
1696
1472
  });
1697
1473
  }
@@ -1814,11 +1590,11 @@ __decorate([
1814
1590
  __metadata("design:returntype", Promise)
1815
1591
  ], Message.prototype, "attachTemporaryUrlsFor", null);
1816
1592
 
1817
- const log$5 = Logger.scope('Messages');
1593
+ const log$4 = Logger.scope('Messages');
1818
1594
  /**
1819
1595
  * Represents the collection of messages in a conversation
1820
1596
  */
1821
- class Messages extends EventEmitter {
1597
+ class Messages extends replayEventEmitter.ReplayEventEmitter {
1822
1598
  constructor(conversation, configuration, services) {
1823
1599
  super();
1824
1600
  this.conversation = conversation;
@@ -1842,7 +1618,7 @@ class Messages extends EventEmitter {
1842
1618
  try {
1843
1619
  const list = await this.messagesListPromise;
1844
1620
  list.on('itemAdded', (args) => {
1845
- log$5.debug(`${this.conversation.sid} itemAdded: ${args.item.index}`);
1621
+ log$4.debug(`${this.conversation.sid} itemAdded: ${args.item.index}`);
1846
1622
  const links = {
1847
1623
  self: `${this.conversation.links.messages}/${args.item.data.sid}`,
1848
1624
  conversation: this.conversation.links.self,
@@ -1850,7 +1626,7 @@ class Messages extends EventEmitter {
1850
1626
  };
1851
1627
  const message = new Message(args.item.index, args.item.data, this.conversation, links, this.configuration, this.services);
1852
1628
  if (this.messagesByIndex.has(message.index)) {
1853
- log$5.debug('Message arrived, but is already known and ignored', this.conversation.sid, message.index);
1629
+ log$4.debug('Message arrived, but is already known and ignored', this.conversation.sid, message.index);
1854
1630
  return;
1855
1631
  }
1856
1632
  this.messagesByIndex.set(message.index, message);
@@ -1858,7 +1634,7 @@ class Messages extends EventEmitter {
1858
1634
  this.emit('messageAdded', message);
1859
1635
  });
1860
1636
  list.on('itemRemoved', (args) => {
1861
- log$5.debug(`#{this.conversation.sid} itemRemoved: ${args.index}`);
1637
+ log$4.debug(`#{this.conversation.sid} itemRemoved: ${args.index}`);
1862
1638
  const index = args.index;
1863
1639
  if (this.messagesByIndex.has(index)) {
1864
1640
  let message = this.messagesByIndex.get(index);
@@ -1868,7 +1644,7 @@ class Messages extends EventEmitter {
1868
1644
  }
1869
1645
  });
1870
1646
  list.on('itemUpdated', (args) => {
1871
- log$5.debug(`${this.conversation.sid} itemUpdated: ${args.item.index}`);
1647
+ log$4.debug(`${this.conversation.sid} itemUpdated: ${args.item.index}`);
1872
1648
  const message = this.messagesByIndex.get(args.item.index);
1873
1649
  if (message) {
1874
1650
  message._update(args.item.data);
@@ -1879,9 +1655,9 @@ class Messages extends EventEmitter {
1879
1655
  catch (err) {
1880
1656
  this.messagesListPromise = null;
1881
1657
  if (this.services.syncClient.connectionState !== 'disconnected') {
1882
- log$5.error('Failed to get messages object for conversation', this.conversation.sid, err);
1658
+ log$4.error('Failed to get messages object for conversation', this.conversation.sid, err);
1883
1659
  }
1884
- log$5.debug('ERROR: Failed to get messages object for conversation', this.conversation.sid, err);
1660
+ log$4.debug('ERROR: Failed to get messages object for conversation', this.conversation.sid, err);
1885
1661
  throw err;
1886
1662
  }
1887
1663
  }
@@ -1900,10 +1676,10 @@ class Messages extends EventEmitter {
1900
1676
  */
1901
1677
  async sendV2(message) {
1902
1678
  var _a;
1903
- log$5.debug('Sending message V2', message.mediaContent, message.attributes, message.emailOptions);
1679
+ log$4.debug('Sending message V2', message.mediaContent, message.attributes, message.emailOptions);
1904
1680
  const media = [];
1905
1681
  for (const [category, mediaContent] of message.mediaContent) {
1906
- log$5.debug(`Adding media to a message as ${mediaContent instanceof FormData ? 'FormData' : 'SendMediaOptions'}`, mediaContent);
1682
+ log$4.debug(`Adding media to a message as ${mediaContent instanceof FormData ? 'FormData' : 'SendMediaOptions'}`, mediaContent);
1907
1683
  media.push(mediaContent instanceof FormData
1908
1684
  ? await this.services.mcsClient.postFormData(mediaContent, category)
1909
1685
  : await this.services.mcsClient.post(mediaContent.contentType, mediaContent.media, category, mediaContent.filename));
@@ -1925,7 +1701,7 @@ class Messages extends EventEmitter {
1925
1701
  * @returns Returns promise which can fail
1926
1702
  */
1927
1703
  async send(message, attributes = {}, emailOptions) {
1928
- log$5.debug('Sending text message', message, attributes, emailOptions);
1704
+ log$4.debug('Sending text message', message, attributes, emailOptions);
1929
1705
  return await this.services.commandExecutor.mutateResource('post', this.conversation.links.messages, {
1930
1706
  body: message !== null && message !== void 0 ? message : '',
1931
1707
  attributes: typeof attributes !== 'undefined'
@@ -1942,8 +1718,8 @@ class Messages extends EventEmitter {
1942
1718
  * @returns Returns promise which can fail
1943
1719
  */
1944
1720
  async sendMedia(mediaContent, attributes = {}, emailOptions) {
1945
- log$5.debug('Sending media message', mediaContent, attributes, emailOptions);
1946
- log$5.debug(`Sending media message as ${mediaContent instanceof FormData ? 'FormData' : 'SendMediaOptions'}`, mediaContent, attributes);
1721
+ log$4.debug('Sending media message', mediaContent, attributes, emailOptions);
1722
+ log$4.debug(`Sending media message as ${mediaContent instanceof FormData ? 'FormData' : 'SendMediaOptions'}`, mediaContent, attributes);
1947
1723
  const media = mediaContent instanceof FormData
1948
1724
  ? await this.services.mcsClient.postFormData(mediaContent)
1949
1725
  : await this.services.mcsClient.post(mediaContent.contentType, mediaContent.media, 'media', mediaContent.filename);
@@ -2017,7 +1793,13 @@ class Messages extends EventEmitter {
2017
1793
  }
2018
1794
  }
2019
1795
 
1796
+ /**
1797
+ * An unsent message. Returned from {@link MessageBuilder.build}.
1798
+ */
2020
1799
  class UnsentMessage {
1800
+ /**
1801
+ * @internal
1802
+ */
2021
1803
  constructor(messagesEntity) {
2022
1804
  this.messagesEntity = messagesEntity;
2023
1805
  this.attributes = {};
@@ -2026,35 +1808,72 @@ class UnsentMessage {
2026
1808
  }
2027
1809
  /**
2028
1810
  * Send the prepared message to the conversation.
2029
- * @returns {Promise<number>} new Message's index in the Conversation's messages list
1811
+ * @returns Index of the new message in the conversation.
2030
1812
  */
2031
1813
  async send() {
2032
1814
  const response = await this.messagesEntity.sendV2(this);
2033
- return parseToNumber(response.messageId);
1815
+ return parseToNumber(response.index);
2034
1816
  }
2035
1817
  }
2036
1818
 
1819
+ /**
1820
+ * Message builder. Allows the message to be built and sent via method chaining.
1821
+ *
1822
+ * Example:
1823
+ *
1824
+ * ```ts
1825
+ * await testConversation.prepareMessage()
1826
+ * .setBody('Hello!')
1827
+ * .setAttributes({foo: 'bar'})
1828
+ * .addMedia(media1)
1829
+ * .addMedia(media2)
1830
+ * .build()
1831
+ * .send();
1832
+ * ```
1833
+ */
2037
1834
  class MessageBuilder {
1835
+ /**
1836
+ * @internal
1837
+ */
2038
1838
  constructor(limits, messagesEntity) {
2039
1839
  this.limits = limits;
2040
1840
  this.message = new UnsentMessage(messagesEntity);
2041
1841
  }
1842
+ /**
1843
+ * Sets the message body.
1844
+ * @param text Contents of the body.
1845
+ */
2042
1846
  setBody(text) {
2043
1847
  this.message.text = text;
2044
1848
  return this;
2045
1849
  }
1850
+ /**
1851
+ * Sets the message subject.
1852
+ * @param subject Contents of the subject.
1853
+ */
2046
1854
  setSubject(subject) {
2047
1855
  this.message.emailOptions.subject = subject;
2048
1856
  return this;
2049
1857
  }
1858
+ /**
1859
+ * Sets the message attributes.
1860
+ * @param attributes Message attributes.
1861
+ */
2050
1862
  setAttributes(attributes) {
2051
1863
  this.message.attributes = attributes;
2052
1864
  return this;
2053
1865
  }
1866
+ /**
1867
+ * Adds media to the message.
1868
+ * @param payload Media to add.
1869
+ */
2054
1870
  addMedia(payload) {
2055
1871
  this.message.mediaContent.push(['media', payload]);
2056
1872
  return this;
2057
1873
  }
1874
+ /**
1875
+ * Builds the message, making it ready to be sent.
1876
+ */
2058
1877
  build() {
2059
1878
  if (this.message.mediaContent.length > this.limits.mediaAttachmentsCountLimit) {
2060
1879
  throw new Error(`Too many media attachments in the message (${this.message.mediaContent.length} > ${this.limits.mediaAttachmentsCountLimit})`);
@@ -2070,7 +1889,7 @@ class MessageBuilder {
2070
1889
  }
2071
1890
  }
2072
1891
 
2073
- const log$4 = Logger.scope('Conversation');
1892
+ const log$3 = Logger.scope('Conversation');
2074
1893
  const fieldMappings = {
2075
1894
  lastMessage: 'lastMessage',
2076
1895
  attributes: 'attributes',
@@ -2096,7 +1915,7 @@ function parseTime(timeString) {
2096
1915
  /**
2097
1916
  * A conversation represents communication between multiple Conversations clients
2098
1917
  */
2099
- class Conversation extends EventEmitter {
1918
+ class Conversation extends replayEventEmitter.ReplayEventEmitter {
2100
1919
  /**
2101
1920
  * @internal
2102
1921
  */
@@ -2210,9 +2029,9 @@ class Conversation extends EventEmitter {
2210
2029
  this.entity = null;
2211
2030
  this.entityPromise = null;
2212
2031
  if (this.services.syncClient.connectionState != 'disconnected') {
2213
- log$4.error('Failed to get conversation object', err);
2032
+ log$3.error('Failed to get conversation object', err);
2214
2033
  }
2215
- log$4.debug('ERROR: Failed to get conversation object', err);
2034
+ log$3.debug('ERROR: Failed to get conversation object', err);
2216
2035
  throw err;
2217
2036
  });
2218
2037
  }
@@ -2225,7 +2044,7 @@ class Conversation extends EventEmitter {
2225
2044
  async _subscribeStreams() {
2226
2045
  try {
2227
2046
  await this._subscribe();
2228
- log$4.trace('_subscribeStreams, this.entity.data=', this.entity.data);
2047
+ log$3.trace('_subscribeStreams, this.entity.data=', this.entity.data);
2229
2048
  const messagesObjectName = this.entity.data.messages;
2230
2049
  const rosterObjectName = this.entity.data.roster;
2231
2050
  await Promise.all([
@@ -2235,9 +2054,9 @@ class Conversation extends EventEmitter {
2235
2054
  }
2236
2055
  catch (err) {
2237
2056
  if (this.services.syncClient.connectionState !== 'disconnected') {
2238
- log$4.error('Failed to subscribe on conversation objects', this.sid, err);
2057
+ log$3.error('Failed to subscribe on conversation objects', this.sid, err);
2239
2058
  }
2240
- log$4.debug('ERROR: Failed to subscribe on conversation objects', this.sid, err);
2059
+ log$3.debug('ERROR: Failed to subscribe on conversation objects', this.sid, err);
2241
2060
  throw err;
2242
2061
  }
2243
2062
  }
@@ -2269,7 +2088,7 @@ class Conversation extends EventEmitter {
2269
2088
  if (status === 'joined') {
2270
2089
  this._subscribeStreams()
2271
2090
  .catch(err => {
2272
- log$4.debug('ERROR while setting conversation status ' + status, err);
2091
+ log$3.debug('ERROR while setting conversation status ' + status, err);
2273
2092
  if (this.services.syncClient.connectionState !== 'disconnected') {
2274
2093
  throw err;
2275
2094
  }
@@ -2277,7 +2096,7 @@ class Conversation extends EventEmitter {
2277
2096
  }
2278
2097
  else if (this.entityPromise) {
2279
2098
  this._unsubscribe().catch(err => {
2280
- log$4.debug('ERROR while setting conversation status ' + status, err);
2099
+ log$3.debug('ERROR while setting conversation status ' + status, err);
2281
2100
  if (this.services.syncClient.connectionState !== 'disconnected') {
2282
2101
  throw err;
2283
2102
  }
@@ -2301,7 +2120,7 @@ class Conversation extends EventEmitter {
2301
2120
  }
2302
2121
  }
2303
2122
  catch (e) {
2304
- log$4.warn('Retrieved malformed attributes from the server for conversation: ' + conversationSid);
2123
+ log$3.warn('Retrieved malformed attributes from the server for conversation: ' + conversationSid);
2305
2124
  update.attributes = {};
2306
2125
  }
2307
2126
  try {
@@ -2310,7 +2129,7 @@ class Conversation extends EventEmitter {
2310
2129
  }
2311
2130
  }
2312
2131
  catch (e) {
2313
- log$4.warn('Retrieved malformed dateCreated from the server for conversation: ' + conversationSid);
2132
+ log$3.warn('Retrieved malformed dateCreated from the server for conversation: ' + conversationSid);
2314
2133
  delete update.dateCreated;
2315
2134
  }
2316
2135
  try {
@@ -2319,7 +2138,7 @@ class Conversation extends EventEmitter {
2319
2138
  }
2320
2139
  }
2321
2140
  catch (e) {
2322
- log$4.warn('Retrieved malformed dateUpdated from the server for conversation: ' + conversationSid);
2141
+ log$3.warn('Retrieved malformed dateUpdated from the server for conversation: ' + conversationSid);
2323
2142
  delete update.dateUpdated;
2324
2143
  }
2325
2144
  try {
@@ -2328,7 +2147,7 @@ class Conversation extends EventEmitter {
2328
2147
  }
2329
2148
  }
2330
2149
  catch (e) {
2331
- log$4.warn('Retrieved malformed lastMessage.timestamp from the server for conversation: ' + conversationSid);
2150
+ log$3.warn('Retrieved malformed lastMessage.timestamp from the server for conversation: ' + conversationSid);
2332
2151
  delete update.lastMessage.timestamp;
2333
2152
  }
2334
2153
  }
@@ -2338,7 +2157,7 @@ class Conversation extends EventEmitter {
2338
2157
  */
2339
2158
  _update(update) {
2340
2159
  var _a, _b, _c, _d, _e;
2341
- log$4.trace('_update', update);
2160
+ log$3.trace('_update', update);
2342
2161
  Conversation.preprocessUpdate(update, this.sid);
2343
2162
  const updateReasons = new Set();
2344
2163
  for (const key of Object.keys(update)) {
@@ -2449,7 +2268,7 @@ class Conversation extends EventEmitter {
2449
2268
  * @param address User address of the participant.
2450
2269
  * @param attributes Attributes to be attached to the participant.
2451
2270
  */
2452
- async addNonChatParticipant(proxyAddress, address, attributes = {}) {
2271
+ async addNonChatParticipant(proxyAddress, address, attributes) {
2453
2272
  return this.participantsEntity.addNonChatParticipant(proxyAddress, address, attributes !== null && attributes !== void 0 ? attributes : {});
2454
2273
  }
2455
2274
  /**
@@ -2595,8 +2414,9 @@ class Conversation extends EventEmitter {
2595
2414
  return this;
2596
2415
  }
2597
2416
  /**
2598
- * Remove a participant from the conversation. When a string is passed as the argument, it will assume that the string is an identity.
2599
- * @param participant Identity or the participant object to remove.
2417
+ * Remove a participant from the conversation. When a string is passed as the
2418
+ * argument, it will assume that the string is an identity or SID.
2419
+ * @param participant Identity, SID or the participant object to remove.
2600
2420
  */
2601
2421
  async removeParticipant(participant) {
2602
2422
  await this.participantsEntity.remove(typeof participant === 'string'
@@ -2606,7 +2426,7 @@ class Conversation extends EventEmitter {
2606
2426
  /**
2607
2427
  * Send a message to the conversation.
2608
2428
  * @param message Message body for the text message,
2609
- * `FormData` or {@link Conversation.MediaOptions) for media content. Sending FormData is supported only with the browser engine.
2429
+ * `FormData` or {@link SendMediaOptions} for media content. Sending FormData is supported only with the browser engine.
2610
2430
  * @param messageAttributes Attributes for the message.
2611
2431
  * @param emailOptions Email options for the message.
2612
2432
  * @return Index of the new message.
@@ -2622,7 +2442,6 @@ class Conversation extends EventEmitter {
2622
2442
  /**
2623
2443
  * New interface to prepare for sending a message.
2624
2444
  * Use instead of `sendMessage`.
2625
- * @return {MessageBuilder} [description]
2626
2445
  */
2627
2446
  prepareMessage() {
2628
2447
  return new MessageBuilder(this.limits, this.messagesEntity);
@@ -2802,7 +2621,7 @@ __decorate([
2802
2621
  __metadata("design:returntype", Promise)
2803
2622
  ], Conversation.prototype, "add", null);
2804
2623
  __decorate([
2805
- declarativeTypeValidator.validateTypesAsync(declarativeTypeValidator.nonEmptyString, declarativeTypeValidator.nonEmptyString, [declarativeTypeValidator.pureObject, 'undefined']),
2624
+ declarativeTypeValidator.validateTypesAsync(declarativeTypeValidator.nonEmptyString, declarativeTypeValidator.nonEmptyString, ['undefined', 'string', 'number', 'boolean', 'object', declarativeTypeValidator.literal(null)]),
2806
2625
  __metadata("design:type", Function),
2807
2626
  __metadata("design:paramtypes", [String, String, Object]),
2808
2627
  __metadata("design:returntype", Promise)
@@ -2876,7 +2695,7 @@ __decorate([
2876
2695
  __metadata("design:returntype", Promise)
2877
2696
  ], Conversation.prototype, "updateAttributes", null);
2878
2697
  __decorate([
2879
- declarativeTypeValidator.validateTypesAsync(['string', declarativeTypeValidator.literal(null)]),
2698
+ declarativeTypeValidator.validateTypesAsync(['string']),
2880
2699
  __metadata("design:type", Function),
2881
2700
  __metadata("design:paramtypes", [String]),
2882
2701
  __metadata("design:returntype", Promise)
@@ -2914,12 +2733,12 @@ class Deferred {
2914
2733
  }
2915
2734
  }
2916
2735
 
2917
- const log$3 = Logger.scope('Conversations');
2736
+ const log$2 = Logger.scope('Conversations');
2918
2737
  /**
2919
2738
  * Represents conversations collection
2920
2739
  * {@see Conversation}
2921
2740
  */
2922
- class Conversations extends EventEmitter {
2741
+ class Conversations extends replayEventEmitter.ReplayEventEmitter {
2923
2742
  constructor(configuration, services) {
2924
2743
  super();
2925
2744
  this.conversations = new Map();
@@ -2971,11 +2790,11 @@ class Conversations extends EventEmitter {
2971
2790
  try {
2972
2791
  const map = await this._getMap();
2973
2792
  map.on('itemAdded', args => {
2974
- log$3.debug(`itemAdded: ${args.item.key}`);
2793
+ log$2.debug(`itemAdded: ${args.item.key}`);
2975
2794
  this._upsertConversation('sync', args.item.key, args.item.data);
2976
2795
  });
2977
2796
  map.on('itemRemoved', args => {
2978
- log$3.debug(`itemRemoved: ${args.key}`);
2797
+ log$2.debug(`itemRemoved: ${args.key}`);
2979
2798
  const sid = args.key;
2980
2799
  if (!this.myConversationsFetched) {
2981
2800
  this.tombstones.add(sid);
@@ -2993,7 +2812,7 @@ class Conversations extends EventEmitter {
2993
2812
  conversation.emit('removed', conversation);
2994
2813
  });
2995
2814
  map.on('itemUpdated', args => {
2996
- log$3.debug(`itemUpdated: ${args.item.key}`);
2815
+ log$2.debug(`itemUpdated: ${args.item.key}`);
2997
2816
  this._upsertConversation('sync', args.item.key, args.item.data);
2998
2817
  });
2999
2818
  const myConversations = await this._fetchMyConversations();
@@ -3005,15 +2824,15 @@ class Conversations extends EventEmitter {
3005
2824
  await Promise.all(upserts);
3006
2825
  this.myConversationsFetched = true;
3007
2826
  this.tombstones.clear();
3008
- log$3.debug('The conversations list has been successfully fetched');
2827
+ log$2.debug('The conversations list has been successfully fetched');
3009
2828
  return this;
3010
2829
  }
3011
2830
  catch (error) {
3012
2831
  const errorMessage = 'Failed to fetch the conversations list';
3013
2832
  if (this.services.syncClient.connectionState !== 'disconnected') {
3014
- log$3.error(errorMessage, error);
2833
+ log$2.error(errorMessage, error);
3015
2834
  }
3016
- log$3.debug(`ERROR: ${errorMessage}`, error);
2835
+ log$2.debug(`ERROR: ${errorMessage}`, error);
3017
2836
  throw error;
3018
2837
  }
3019
2838
  }
@@ -3057,9 +2876,6 @@ class Conversations extends EventEmitter {
3057
2876
  .build();
3058
2877
  const response = await this.services.network.get(url);
3059
2878
  const body = response.body;
3060
- if (body.type !== 'private') {
3061
- return null;
3062
- }
3063
2879
  const data = {
3064
2880
  entityName: null,
3065
2881
  // lastConsumedMessageIndex: body.last_read_message_index,
@@ -3096,7 +2912,7 @@ class Conversations extends EventEmitter {
3096
2912
  const areSourcesDifferent = conversation._statusSource() !== undefined && source !== conversation._statusSource();
3097
2913
  const isChannelSourceSync = source !== 'rest' || conversation._statusSource() === 'sync';
3098
2914
  if (areSourcesDifferent && isChannelSourceSync && source !== 'sync') {
3099
- log$3.trace('upsertConversation: conversation is known from sync and came from chat, ignoring', {
2915
+ log$2.trace('upsertConversation: conversation is known from sync and came from chat, ignoring', {
3100
2916
  sid: conversation.sid,
3101
2917
  data: data.status,
3102
2918
  conversation: conversation.status
@@ -3134,11 +2950,11 @@ class Conversations extends EventEmitter {
3134
2950
  conversation._update(data);
3135
2951
  }
3136
2952
  async _upsertConversation(source, sid, data) {
3137
- log$3.trace(`upsertConversation called for ${sid}`, data);
2953
+ log$2.trace(`upsertConversation called for ${sid}`, data);
3138
2954
  const conversation = this.conversations.get(sid);
3139
2955
  // If the channel is known, update it
3140
2956
  if (conversation) {
3141
- log$3.trace(`upsertConversation: the conversation ${conversation.sid} is known;` +
2957
+ log$2.trace(`upsertConversation: the conversation ${conversation.sid} is known;` +
3142
2958
  `its status is known from the source ${conversation._statusSource()} ` +
3143
2959
  `and the update came from the source ${source}`, conversation);
3144
2960
  await this._updateConversation(source, conversation, data);
@@ -3147,11 +2963,11 @@ class Conversations extends EventEmitter {
3147
2963
  }
3148
2964
  // If the conversations is deleted, ignore it
3149
2965
  if (['chat', 'rest'].includes(source) && this.tombstones.has(sid)) {
3150
- log$3.trace('upsertChannel: the channel is deleted but reappeared again from chat, ignoring', sid);
2966
+ log$2.trace('upsertChannel: the channel is deleted but reappeared again from chat, ignoring', sid);
3151
2967
  return;
3152
2968
  }
3153
2969
  // If the conversation is unknown, fetch it
3154
- log$3.trace('upsertConversation: creating a local conversation object with sid ' + sid, data);
2970
+ log$2.trace('upsertConversation: creating a local conversation object with sid ' + sid, data);
3155
2971
  const baseLink = `${this.configuration.links.conversations}/${sid}`;
3156
2972
  const links = {
3157
2973
  self: baseLink,
@@ -3214,231 +3030,16 @@ class Conversations extends EventEmitter {
3214
3030
  }
3215
3031
  }
3216
3032
 
3217
- const log$2 = Logger.scope('User');
3218
- /**
3219
- * Extended user information.
3220
- * Note that `isOnline` and `isNotifiable` properties are eligible
3221
- * for use only if the reachability function is enabled.
3222
- * You may check if it is enabled by reading the value of {@link Client.reachabilityEnabled}.
3223
- */
3224
- class User extends EventEmitter {
3225
- /**
3226
- * @internal
3227
- */
3228
- constructor(identity, entityName, links, configuration, services) {
3229
- super();
3230
- /**
3231
- * Fired when the properties or the reachability status of the message has been updated.
3232
- *
3233
- * Parameters:
3234
- * 1. object `data` - info object provided with the event. It has the following properties:
3235
- * * {@link User} user - the user in question
3236
- * * {@link UserUpdateReason}[] updateReasons - array of reasons for the update
3237
- * @event
3238
- */
3239
- this.updated = 'updated';
3240
- /**
3241
- * Fired when the client has subscribed to the user.
3242
- *
3243
- * Parameters:
3244
- * 1. {@link User} `user` - the user in question
3245
- * @event
3246
- */
3247
- this.userSubscribed = 'userSubscribed';
3248
- /**
3249
- * Fired when the client has unsubscribed from the user.
3250
- *
3251
- * Parameters:
3252
- * 1. {@link User} `user` - the user in question
3253
- * @event
3254
- */
3255
- this.userUnsubscribed = 'userUnsubscribed';
3256
- this.links = links;
3257
- this.configuration = configuration;
3258
- this.services = services;
3259
- this.subscribed = 'initializing';
3260
- this.setMaxListeners(0);
3261
- this.state = {
3262
- identity,
3263
- entityName,
3264
- friendlyName: null,
3265
- attributes: {},
3266
- online: null,
3267
- notifiable: null
3268
- };
3269
- }
3270
- /**
3271
- * User identity.
3272
- */
3273
- get identity() { return this.state.identity; }
3274
- set identity(identity) { this.state.identity = identity; }
3275
- set entityName(name) { this.state.entityName = name; }
3276
- /**
3277
- * Custom attributes of the user.
3278
- */
3279
- get attributes() { return this.state.attributes; }
3280
- /**
3281
- * Friendly name of the user, null if not set.
3282
- */
3283
- get friendlyName() { return this.state.friendlyName; }
3284
- /**
3285
- * Status of the real-time conversation connection of the user.
3286
- */
3287
- get isOnline() { return this.state.online; }
3288
- /**
3289
- * User push notification registration status.
3290
- */
3291
- get isNotifiable() { return this.state.notifiable; }
3292
- /**
3293
- * True if this user is receiving real-time status updates.
3294
- */
3295
- get isSubscribed() { return this.subscribed == 'subscribed'; }
3296
- // Handles service updates
3297
- _update(key, value) {
3298
- let updateReasons = [];
3299
- log$2.debug('User for', this.state.identity, 'updated:', key, value);
3300
- switch (key) {
3301
- case 'friendlyName':
3302
- if (this.state.friendlyName !== value.value) {
3303
- updateReasons.push('friendlyName');
3304
- this.state.friendlyName = value.value;
3305
- }
3306
- break;
3307
- case 'attributes':
3308
- const updateAttributes = parseAttributes(value.value, `Retrieved malformed attributes from the server for user: ${this.state.identity}`, log$2);
3309
- if (!isDeepEqual(this.state.attributes, updateAttributes)) {
3310
- this.state.attributes = updateAttributes;
3311
- updateReasons.push('attributes');
3312
- }
3313
- break;
3314
- case 'reachability':
3315
- if (this.state.online !== value.online) {
3316
- this.state.online = value.online;
3317
- updateReasons.push('reachabilityOnline');
3318
- }
3319
- if (this.state.notifiable !== value.notifiable) {
3320
- this.state.notifiable = value.notifiable;
3321
- updateReasons.push('reachabilityNotifiable');
3322
- }
3323
- break;
3324
- default:
3325
- return;
3326
- }
3327
- if (updateReasons.length > 0) {
3328
- this.emit('updated', { user: this, updateReasons: updateReasons });
3329
- }
3330
- }
3331
- // Fetch reachability info
3332
- async _updateReachabilityInfo(map, update) {
3333
- if (!this.configuration.reachabilityEnabled) {
3334
- return Promise.resolve();
3335
- }
3336
- return map.get('reachability')
3337
- .then(update)
3338
- .catch(err => { log$2.warn('Failed to get reachability info for ', this.state.identity, err); });
3339
- }
3340
- // Fetch user
3341
- async _fetch() {
3342
- if (!this.state.entityName) {
3343
- return this;
3344
- }
3345
- this.promiseToFetch = this.services.syncClient.map({ id: this.state.entityName, mode: 'open_existing', includeItems: true })
3346
- .then(map => {
3347
- this.entity = map;
3348
- map.on('itemUpdated', args => {
3349
- log$2.debug(this.state.entityName + ' (' + this.state.identity + ') itemUpdated: ' + args.item.key);
3350
- return this._update(args.item.key, args.item.data);
3351
- });
3352
- return Promise.all([
3353
- map.get('friendlyName')
3354
- .then(item => this._update(item.key, item.data)),
3355
- map.get('attributes')
3356
- .then(item => this._update(item.key, item.data)),
3357
- this._updateReachabilityInfo(map, item => this._update(item.key, item.data))
3358
- ]);
3359
- })
3360
- .then(() => {
3361
- log$2.debug('Fetched for', this.identity);
3362
- this.subscribed = 'subscribed';
3363
- this.emit('userSubscribed', this);
3364
- return this;
3365
- })
3366
- .catch(err => {
3367
- this.promiseToFetch = null;
3368
- throw err;
3369
- });
3370
- return this.promiseToFetch;
3371
- }
3372
- _ensureFetched() {
3373
- return this.promiseToFetch || this._fetch();
3374
- }
3375
- /**
3376
- * Edit user attributes.
3377
- * @param attributes New attributes.
3378
- */
3379
- async updateAttributes(attributes) {
3380
- if (this.subscribed == 'unsubscribed') {
3381
- throw new Error('Can\'t modify unsubscribed object');
3382
- }
3383
- await this.services.commandExecutor.mutateResource('post', this.links.self, {
3384
- attributes: JSON.stringify(attributes)
3385
- });
3386
- return this;
3387
- }
3388
- /**
3389
- * Update the friendly name of the user.
3390
- * @param friendlyName New friendly name.
3391
- */
3392
- async updateFriendlyName(friendlyName) {
3393
- if (this.subscribed == 'unsubscribed') {
3394
- throw new Error('Can\'t modify unsubscribed object');
3395
- }
3396
- await this.services.commandExecutor.mutateResource('post', this.links.self, {
3397
- friendly_name: friendlyName
3398
- });
3399
- return this;
3400
- }
3401
- /**
3402
- * Remove the user from the subscription list.
3403
- * @return A promise of completion.
3404
- */
3405
- async unsubscribe() {
3406
- if (this.promiseToFetch) {
3407
- await this.promiseToFetch;
3408
- this.entity.close();
3409
- this.promiseToFetch = null;
3410
- this.subscribed = 'unsubscribed';
3411
- this.emit('userUnsubscribed', this);
3412
- }
3413
- }
3414
- }
3415
- __decorate([
3416
- declarativeTypeValidator.validateTypesAsync(['string', 'number', 'boolean', 'object', declarativeTypeValidator.literal(null)]),
3417
- __metadata("design:type", Function),
3418
- __metadata("design:paramtypes", [Object]),
3419
- __metadata("design:returntype", Promise)
3420
- ], User.prototype, "updateAttributes", null);
3421
- __decorate([
3422
- declarativeTypeValidator.validateTypesAsync(['string']),
3423
- __metadata("design:type", Function),
3424
- __metadata("design:paramtypes", [String]),
3425
- __metadata("design:returntype", Promise)
3426
- ], User.prototype, "updateFriendlyName", null);
3427
-
3428
3033
  /**
3429
- * @classdesc Container for known users
3430
- * @fires Users#userUpdated
3034
+ * Container for known users
3431
3035
  */
3432
- class Users extends EventEmitter {
3433
- constructor(configuration, services) {
3036
+ class Users extends replayEventEmitter.ReplayEventEmitter {
3037
+ constructor(myself, configuration, services) {
3434
3038
  super();
3435
- const links = {
3436
- self: `${configuration.links.users}/${configuration.userIdentity}`
3437
- };
3438
3039
  this.configuration = configuration;
3439
3040
  this.services = services;
3440
3041
  this.fifoStack = [];
3441
- this.myself = new User(this.configuration.userIdentity, this.configuration.userInfo, links, this.configuration, this.services);
3042
+ this.myself = myself;
3442
3043
  this.myself.on('updated', (args) => this.emit('userUpdated', args));
3443
3044
  this.myself.on('userSubscribed', () => this.emit('userSubscribed', this.myself));
3444
3045
  this.myself.on('userUnsubscribed', () => {
@@ -3490,10 +3091,7 @@ class Users extends EventEmitter {
3490
3091
  if (!entityName) {
3491
3092
  entityName = await this.getSyncUniqueName(identity);
3492
3093
  }
3493
- const links = {
3494
- self: `${this.configuration.links.users}/${identity}`
3495
- };
3496
- user = new User(identity, entityName, links, this.configuration, this.services);
3094
+ user = new User(identity, entityName, this.configuration, this.services);
3497
3095
  user.on('updated', (args) => this.emit('userUpdated', args));
3498
3096
  user.on('userSubscribed', () => this.handleSubscribeUser(user));
3499
3097
  user.on('userUnsubscribed', () => this.handleUnsubscribeUser(user));
@@ -3638,7 +3236,7 @@ class PushNotification {
3638
3236
  }
3639
3237
  }
3640
3238
 
3641
- var version = "2.0.0-rc.1";
3239
+ var version = "2.0.0-rc.3";
3642
3240
 
3643
3241
  const trimSlashes = (url) => url.replace(/(^\/+|\/+$)/g, '');
3644
3242
  const isMutationConflictResponse = (response) => response.status.code === 202;
@@ -3714,7 +3312,7 @@ class ClientServices {
3714
3312
  /**
3715
3313
  * A client is the starting point to the Twilio Conversations functionality.
3716
3314
  */
3717
- exports.Client = Client_1 = class Client extends EventEmitter {
3315
+ exports.Client = Client_1 = class Client extends replayEventEmitter.ReplayEventEmitter {
3718
3316
  /**
3719
3317
  * Returned Conversations instance is not yet fully initialized. Calling any operations will block until it is.
3720
3318
  * Use connection events to monitor when client becomes fully available (connectionStateChanged with state
@@ -3773,6 +3371,7 @@ exports.Client = Client_1 = class Client extends EventEmitter {
3773
3371
  throw new Error('A valid Twilio token should be provided');
3774
3372
  }
3775
3373
  this.services = new ClientServices();
3374
+ this._myself = new User('', '', null, this.services);
3776
3375
  const startTwilsock = !this.options.twilsockClient;
3777
3376
  // Create default init registrations if none were provided.
3778
3377
  // Otherwise, the outside party have to list all the init registrations they
@@ -3868,13 +3467,23 @@ exports.Client = Client_1 = class Client extends EventEmitter {
3868
3467
  return client;
3869
3468
  }
3870
3469
  /**
3871
- * Information of the logged-in user.
3470
+ * Information of the logged-in user. Before client initialization, returns an
3471
+ * uninitialized user. Will trigger a {@link Client.userUpdated} event after
3472
+ * initialization.
3872
3473
  */
3873
- get user() { return this.services.users.myself; }
3474
+ get user() { return this._myself; }
3874
3475
  /**
3875
- * Client reachability state.
3476
+ * Client reachability state. Throws if accessed before the client
3477
+ * initialization was completed.
3876
3478
  */
3877
- get reachabilityEnabled() { return this.configuration.reachabilityEnabled; }
3479
+ get reachabilityEnabled() {
3480
+ if (!this.configuration) {
3481
+ throw new Error('Reachability information could not yet be accessed as the client ' +
3482
+ "has not yet been initialized. Subscribe to the 'stateChanged' event " +
3483
+ 'to properly react to the client initialization.');
3484
+ }
3485
+ return this.configuration.reachabilityEnabled;
3486
+ }
3878
3487
  get token() { return this.fpaToken; }
3879
3488
  _subscribeToPushNotifications(channelType) {
3880
3489
  [NotificationTypes.NEW_MESSAGE,
@@ -3899,9 +3508,10 @@ exports.Client = Client_1 = class Client extends EventEmitter {
3899
3508
  async _initialize() {
3900
3509
  const configurationResponse = await this.services.commandExecutor.fetchResource('Client/v2/Configuration');
3901
3510
  this.configuration = new Configuration(this.options, configurationResponse, log);
3511
+ this._myself._resolveInitialization(this.configuration, this.configuration.userIdentity, this.configuration.userInfo, true);
3902
3512
  this.services.typingIndicator = new TypingIndicator(this.getConversationBySid.bind(this), this.configuration, this.services);
3903
3513
  this.services.network = new Network(this.configuration, this.services);
3904
- this.services.users = new Users(this.configuration, this.services);
3514
+ this.services.users = new Users(this._myself, this.configuration, this.services);
3905
3515
  this.services.users.on('userSubscribed', this.emit.bind(this, 'userSubscribed'));
3906
3516
  this.services.users.on('userUpdated', (args) => this.emit('userUpdated', args));
3907
3517
  this.services.users.on('userUnsubscribed', this.emit.bind(this, 'userUnsubscribed'));
@@ -4007,6 +3617,7 @@ exports.Client = Client_1 = class Client extends EventEmitter {
4007
3617
  */
4008
3618
  async setPushRegistrationId(channelType, registrationId) {
4009
3619
  await this._ensureReady;
3620
+ this._subscribeToPushNotifications(channelType);
4010
3621
  this.services.notificationClient.setPushRegistrationId(channelType, registrationId);
4011
3622
  await this.services.notificationClient.commitChanges(); // Committing before this point is useless because we have no push id
4012
3623
  }
@@ -4173,7 +3784,7 @@ exports.Client.conversationLeft = 'conversationLeft';
4173
3784
  exports.Client.conversationRemoved = 'conversationRemoved';
4174
3785
  /**
4175
3786
  * Fired when the attributes or the metadata of a conversation have been updated.
4176
- * During conversation's {@link Client.create| creation and initialization}, this event might be fired multiple times
3787
+ * During conversation's creation and initialization, this event might be fired multiple times
4177
3788
  * for same joined or created conversation as new data is arriving from different sources.
4178
3789
  *
4179
3790
  * Parameters:
@@ -4183,14 +3794,6 @@ exports.Client.conversationRemoved = 'conversationRemoved';
4183
3794
  * @event
4184
3795
  */
4185
3796
  exports.Client.conversationUpdated = 'conversationUpdated';
4186
- /**
4187
- * Fired when the connection state of the client has been changed.
4188
- *
4189
- * Parameters:
4190
- * 1. {@link Conversation} `conversation` - the conversation in question
4191
- * @event
4192
- */
4193
- exports.Client.conversationStateChanged = 'connectionStateChanged';
4194
3797
  /**
4195
3798
  * Fired when a participant has joined a conversation.
4196
3799
  *
@@ -4245,6 +3848,8 @@ exports.Client.messageRemoved = 'messageRemoved';
4245
3848
  exports.Client.messageUpdated = 'messageUpdated';
4246
3849
  /**
4247
3850
  * Fired when the token is about to expire and needs to be updated.
3851
+ * * Parameters:
3852
+ * 1. number `message` - token's time to live
4248
3853
  * @event
4249
3854
  */
4250
3855
  exports.Client.tokenAboutToExpire = 'tokenAboutToExpire';
@@ -4303,6 +3908,22 @@ exports.Client.userUnsubscribed = 'userUnsubscribed';
4303
3908
  * @event
4304
3909
  */
4305
3910
  exports.Client.userUpdated = 'userUpdated';
3911
+ /**
3912
+ * Fired when the state of the client has been changed.
3913
+ *
3914
+ * Parameters:
3915
+ * 1. {@link State} `state` - the new client state
3916
+ * @event
3917
+ */
3918
+ exports.Client.stateChanged = 'stateChanged';
3919
+ /**
3920
+ * Fired when the connection state of the client has been changed.
3921
+ *
3922
+ * Paremeters:
3923
+ * 1. {@link ConnectionState} `state` - the new connection state
3924
+ * @event
3925
+ */
3926
+ exports.Client.connectionStateChanged = 'connectionStateChanged';
4306
3927
  /**
4307
3928
  * Fired when the connection is interrupted for an unexpected reason.
4308
3929
  *
@@ -4391,22 +4012,20 @@ __decorate([
4391
4012
  exports.Client = Client_1 = __decorate([
4392
4013
  declarativeTypeValidator.validateConstructorTypes(declarativeTypeValidator.nonEmptyString, [
4393
4014
  declarativeTypeValidator.pureObject,
4394
- 'undefined',
4395
- declarativeTypeValidator.literal(null),
4015
+ 'undefined'
4396
4016
  ]),
4397
4017
  __metadata("design:paramtypes", [String, Object])
4398
4018
  ], exports.Client);
4399
4019
 
4400
- var Client = exports.Client;
4401
-
4402
4020
  exports.AggregatedDeliveryReceipt = AggregatedDeliveryReceipt;
4403
4021
  exports.Conversation = Conversation;
4404
4022
  exports.DetailedDeliveryReceipt = DetailedDeliveryReceipt;
4405
4023
  exports.Media = Media;
4406
4024
  exports.Message = Message;
4025
+ exports.MessageBuilder = MessageBuilder;
4407
4026
  exports.Participant = Participant;
4408
4027
  exports.PushNotification = PushNotification;
4409
4028
  exports.RestPaginator = RestPaginator;
4029
+ exports.UnsentMessage = UnsentMessage;
4410
4030
  exports.User = User;
4411
- exports.default = Client;
4412
4031
  //# sourceMappingURL=react-native.js.map