@pushler/js 1.1.1 → 1.2.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/dist/pushler-ru.cjs +308 -26
- package/dist/pushler-ru.cjs.map +1 -1
- package/dist/pushler-ru.esm.js +306 -27
- package/dist/pushler-ru.esm.js.map +1 -1
- package/dist/pushler-ru.js +308 -26
- package/dist/pushler-ru.js.map +1 -1
- package/dist/pushler-ru.min.js +1 -1
- package/dist/pushler-ru.min.js.map +1 -1
- package/package.json +1 -1
- package/src/Channel.js +12 -1
- package/src/PushlerClient.js +280 -27
- package/src/constants.js +12 -0
- package/src/index.js +16 -2
package/dist/pushler-ru.js
CHANGED
|
@@ -24,6 +24,18 @@
|
|
|
24
24
|
FAILED: 'failed'
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Состояния подписки на канал
|
|
29
|
+
*/
|
|
30
|
+
const SubscriptionStates = {
|
|
31
|
+
PENDING: 'pending',
|
|
32
|
+
PENDING_AUTH: 'pending_auth',
|
|
33
|
+
SUBSCRIBING: 'subscribing',
|
|
34
|
+
SUBSCRIBED: 'subscribed',
|
|
35
|
+
FAILED: 'failed',
|
|
36
|
+
UNSUBSCRIBED: 'unsubscribed'
|
|
37
|
+
};
|
|
38
|
+
|
|
27
39
|
/**
|
|
28
40
|
* События WebSocket
|
|
29
41
|
*/
|
|
@@ -65,6 +77,7 @@
|
|
|
65
77
|
this.name = name;
|
|
66
78
|
this.options = options;
|
|
67
79
|
this.subscribed = false;
|
|
80
|
+
this.subscriptionState = SubscriptionStates.PENDING;
|
|
68
81
|
this.eventListeners = new Map();
|
|
69
82
|
this.presenceData = null;
|
|
70
83
|
}
|
|
@@ -112,6 +125,7 @@
|
|
|
112
125
|
*/
|
|
113
126
|
handleAuthSuccess(data) {
|
|
114
127
|
this.subscribed = true;
|
|
128
|
+
this.subscriptionState = SubscriptionStates.SUBSCRIBED;
|
|
115
129
|
this.emit(Events.CHANNEL_SUBSCRIBED, data);
|
|
116
130
|
|
|
117
131
|
// Для presence каналов сохраняем данные пользователя
|
|
@@ -139,6 +153,7 @@
|
|
|
139
153
|
}
|
|
140
154
|
|
|
141
155
|
this.subscribed = false;
|
|
156
|
+
this.subscriptionState = SubscriptionStates.UNSUBSCRIBED;
|
|
142
157
|
this.emit(Events.CHANNEL_UNSUBSCRIBED);
|
|
143
158
|
}
|
|
144
159
|
|
|
@@ -149,6 +164,14 @@
|
|
|
149
164
|
return this.subscribed;
|
|
150
165
|
}
|
|
151
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Получение состояния подписки
|
|
169
|
+
* @returns {string} Состояние подписки
|
|
170
|
+
*/
|
|
171
|
+
getSubscriptionState() {
|
|
172
|
+
return this.subscriptionState;
|
|
173
|
+
}
|
|
174
|
+
|
|
152
175
|
/**
|
|
153
176
|
* Получение данных присутствия (для presence каналов)
|
|
154
177
|
*/
|
|
@@ -219,6 +242,7 @@
|
|
|
219
242
|
this.channels = new Map(); // Хранит каналы по полному имени (с appKey)
|
|
220
243
|
this.channelAliases = new Map(); // Маппинг оригинальное имя -> полное имя
|
|
221
244
|
this.eventListeners = new Map();
|
|
245
|
+
this.pendingAuthRequests = new Map(); // Отслеживание pending auth запросов для отмены при unsubscribe
|
|
222
246
|
this.reconnectAttempts = 0;
|
|
223
247
|
this.reconnectTimer = null;
|
|
224
248
|
this.authTimeout = null;
|
|
@@ -407,31 +431,98 @@
|
|
|
407
431
|
};
|
|
408
432
|
}
|
|
409
433
|
|
|
434
|
+
/**
|
|
435
|
+
* Разбор склеенных JSON из одного WebSocket фрейма
|
|
436
|
+
* Сервер иногда отправляет несколько JSON объектов подряд в одном фрейме:
|
|
437
|
+
* {"event":"a",...}{"event":"b",...}
|
|
438
|
+
* @param {string} str - Строка с одним или несколькими JSON объектами
|
|
439
|
+
* @returns {string[]} Массив отдельных JSON строк
|
|
440
|
+
*/
|
|
441
|
+
splitConcatenatedJSON(str) {
|
|
442
|
+
const results = [];
|
|
443
|
+
let depth = 0;
|
|
444
|
+
let start = 0;
|
|
445
|
+
let inString = false;
|
|
446
|
+
let escape = false;
|
|
447
|
+
|
|
448
|
+
for (let i = 0; i < str.length; i++) {
|
|
449
|
+
const char = str[i];
|
|
450
|
+
|
|
451
|
+
// Обработка escape-символов внутри строк
|
|
452
|
+
if (escape) {
|
|
453
|
+
escape = false;
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (char === '\\' && inString) {
|
|
458
|
+
escape = true;
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Отслеживание строковых литералов
|
|
463
|
+
if (char === '"') {
|
|
464
|
+
inString = !inString;
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Считаем скобки только вне строк
|
|
469
|
+
if (!inString) {
|
|
470
|
+
if (char === '{') {
|
|
471
|
+
depth++;
|
|
472
|
+
} else if (char === '}') {
|
|
473
|
+
depth--;
|
|
474
|
+
if (depth === 0) {
|
|
475
|
+
results.push(str.slice(start, i + 1));
|
|
476
|
+
start = i + 1;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return results;
|
|
483
|
+
}
|
|
484
|
+
|
|
410
485
|
/**
|
|
411
486
|
* Обработка входящих сообщений
|
|
412
487
|
*/
|
|
413
488
|
handleMessage(event) {
|
|
414
489
|
try {
|
|
415
|
-
|
|
490
|
+
// Пытаемся распарсить как один JSON
|
|
491
|
+
const messages = this.splitConcatenatedJSON(event.data);
|
|
416
492
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
this.handleAuthSuccess(message);
|
|
426
|
-
break;
|
|
427
|
-
case 'pushler:auth_error':
|
|
428
|
-
this.handleAuthError(message);
|
|
429
|
-
break;
|
|
430
|
-
default:
|
|
431
|
-
this.handleChannelMessage(message);
|
|
493
|
+
// Если несколько JSON объектов, обрабатываем каждый
|
|
494
|
+
for (const jsonStr of messages) {
|
|
495
|
+
try {
|
|
496
|
+
const message = JSON.parse(jsonStr);
|
|
497
|
+
this.processMessage(message);
|
|
498
|
+
} catch (parseError) {
|
|
499
|
+
console.error('Error parsing JSON message:', parseError, 'Raw:', jsonStr);
|
|
500
|
+
}
|
|
432
501
|
}
|
|
433
502
|
} catch (error) {
|
|
434
|
-
console.error('Error
|
|
503
|
+
console.error('Error handling WebSocket message:', error);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Обработка распарсенного сообщения
|
|
509
|
+
*/
|
|
510
|
+
processMessage(message) {
|
|
511
|
+
switch (message.event) {
|
|
512
|
+
case 'pushler:connection_established':
|
|
513
|
+
this.handleConnectionEstablished(message.data);
|
|
514
|
+
break;
|
|
515
|
+
case 'pushler:subscription_succeeded':
|
|
516
|
+
this.handleSubscriptionSucceeded(message);
|
|
517
|
+
break;
|
|
518
|
+
case 'pushler:auth_success':
|
|
519
|
+
this.handleAuthSuccess(message);
|
|
520
|
+
break;
|
|
521
|
+
case 'pushler:auth_error':
|
|
522
|
+
this.handleAuthError(message);
|
|
523
|
+
break;
|
|
524
|
+
default:
|
|
525
|
+
this.handleChannelMessage(message);
|
|
435
526
|
}
|
|
436
527
|
}
|
|
437
528
|
|
|
@@ -492,7 +583,13 @@
|
|
|
492
583
|
const { channel } = message.data;
|
|
493
584
|
const channelInstance = this.channels.get(channel);
|
|
494
585
|
|
|
586
|
+
// Очищаем pending auth запрос
|
|
587
|
+
this.cancelPendingAuth(channel);
|
|
588
|
+
|
|
495
589
|
if (channelInstance) {
|
|
590
|
+
// Устанавливаем состояние subscribed
|
|
591
|
+
channelInstance.subscriptionState = SubscriptionStates.SUBSCRIBED;
|
|
592
|
+
|
|
496
593
|
// На сервере уже происходит подписка после успешной авторизации,
|
|
497
594
|
// поэтому просто помечаем канал как подписанный
|
|
498
595
|
channelInstance.handleAuthSuccess(message.data);
|
|
@@ -505,11 +602,31 @@
|
|
|
505
602
|
* Обработка ошибки аутентификации
|
|
506
603
|
*/
|
|
507
604
|
handleAuthError(message) {
|
|
508
|
-
const { error } = message.data;
|
|
509
|
-
|
|
605
|
+
const { error, channel } = message.data;
|
|
606
|
+
const channelName = channel || null;
|
|
607
|
+
|
|
608
|
+
// Очищаем pending auth запрос
|
|
609
|
+
if (channelName) {
|
|
610
|
+
this.cancelPendingAuth(channelName);
|
|
611
|
+
|
|
612
|
+
const channelInstance = this.channels.get(channelName);
|
|
613
|
+
if (channelInstance) {
|
|
614
|
+
channelInstance.subscriptionState = SubscriptionStates.FAILED;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
const originalChannelName = channelName ? this.extractChannelName(channelName) : null;
|
|
619
|
+
|
|
620
|
+
this.emit(Events.CHANNEL_AUTH_ERROR, {
|
|
621
|
+
error,
|
|
622
|
+
channel: originalChannelName,
|
|
623
|
+
socketId: this.socketId
|
|
624
|
+
});
|
|
510
625
|
this.emit(Events.ERROR, {
|
|
511
626
|
code: ErrorCodes.AUTHENTICATION_FAILED,
|
|
512
|
-
message: error
|
|
627
|
+
message: error,
|
|
628
|
+
channel: originalChannelName,
|
|
629
|
+
socketId: this.socketId
|
|
513
630
|
});
|
|
514
631
|
}
|
|
515
632
|
|
|
@@ -567,8 +684,12 @@
|
|
|
567
684
|
// Поддерживаем оба формата имени
|
|
568
685
|
const fullChannelName = this.channelAliases.get(channelName) || this.formatChannelName(channelName);
|
|
569
686
|
|
|
687
|
+
// Отменяем pending auth запросы для этого канала (исправление race condition)
|
|
688
|
+
this.cancelPendingAuth(fullChannelName);
|
|
689
|
+
|
|
570
690
|
const channel = this.channels.get(fullChannelName);
|
|
571
691
|
if (channel) {
|
|
692
|
+
channel.subscriptionState = SubscriptionStates.UNSUBSCRIBED;
|
|
572
693
|
channel.unsubscribe();
|
|
573
694
|
this.channels.delete(fullChannelName);
|
|
574
695
|
this.channelAliases.delete(channel.originalName || channelName);
|
|
@@ -603,41 +724,119 @@
|
|
|
603
724
|
|
|
604
725
|
/**
|
|
605
726
|
* Аутентификация канала
|
|
727
|
+
* С поддержкой отмены при unsubscribe
|
|
606
728
|
*/
|
|
607
729
|
async authenticateChannel(channel) {
|
|
730
|
+
// Создаём AbortController для возможности отмены
|
|
731
|
+
const abortController = new AbortController();
|
|
732
|
+
const channelName = channel.name;
|
|
733
|
+
|
|
734
|
+
// Сохраняем для возможности отмены при unsubscribe
|
|
735
|
+
this.pendingAuthRequests.set(channelName, {
|
|
736
|
+
controller: abortController,
|
|
737
|
+
timeoutId: null
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
// Устанавливаем состояние канала
|
|
741
|
+
channel.subscriptionState = SubscriptionStates.PENDING_AUTH;
|
|
742
|
+
|
|
608
743
|
try {
|
|
744
|
+
// Проверка отмены перед началом
|
|
745
|
+
if (abortController.signal.aborted) {
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
|
|
609
749
|
const authData = await this.getChannelAuthData(channel);
|
|
610
750
|
|
|
751
|
+
// Проверка отмены после получения данных
|
|
752
|
+
if (abortController.signal.aborted) {
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
|
|
611
756
|
this.sendMessage({
|
|
612
757
|
event: 'pushler:auth',
|
|
613
758
|
data: authData
|
|
614
759
|
});
|
|
615
760
|
|
|
616
761
|
// Устанавливаем таймаут аутентификации
|
|
617
|
-
|
|
762
|
+
const timeoutId = setTimeout(() => {
|
|
763
|
+
// Не отправляем ошибку, если запрос уже отменён
|
|
764
|
+
if (abortController.signal.aborted) {
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// Очищаем pending запрос
|
|
769
|
+
this.pendingAuthRequests.delete(channelName);
|
|
770
|
+
|
|
771
|
+
// Устанавливаем состояние failed
|
|
772
|
+
channel.subscriptionState = SubscriptionStates.FAILED;
|
|
773
|
+
|
|
618
774
|
this.emit(Events.ERROR, {
|
|
619
775
|
code: ErrorCodes.AUTHENTICATION_TIMEOUT,
|
|
620
|
-
message: 'Authentication timeout'
|
|
776
|
+
message: 'Authentication timeout',
|
|
777
|
+
channel: channel.originalName || this.extractChannelName(channelName),
|
|
778
|
+
socketId: this.socketId
|
|
621
779
|
});
|
|
622
780
|
}, 10000);
|
|
623
781
|
|
|
782
|
+
// Сохраняем timeoutId для возможности очистки
|
|
783
|
+
const pending = this.pendingAuthRequests.get(channelName);
|
|
784
|
+
if (pending) {
|
|
785
|
+
pending.timeoutId = timeoutId;
|
|
786
|
+
}
|
|
787
|
+
|
|
624
788
|
} catch (error) {
|
|
789
|
+
// Не отправляем ошибку, если запрос уже отменён
|
|
790
|
+
if (abortController.signal.aborted) {
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// Очищаем pending запрос
|
|
795
|
+
this.pendingAuthRequests.delete(channelName);
|
|
796
|
+
|
|
797
|
+
// Устанавливаем состояние failed
|
|
798
|
+
channel.subscriptionState = SubscriptionStates.FAILED;
|
|
799
|
+
|
|
625
800
|
this.emit(Events.ERROR, {
|
|
626
801
|
code: ErrorCodes.AUTHENTICATION_FAILED,
|
|
627
|
-
message: error.message
|
|
802
|
+
message: error.message,
|
|
803
|
+
channel: channel.originalName || this.extractChannelName(channelName),
|
|
804
|
+
socketId: this.socketId
|
|
628
805
|
});
|
|
629
806
|
}
|
|
630
807
|
}
|
|
631
808
|
|
|
809
|
+
/**
|
|
810
|
+
* Отмена pending auth запроса для канала
|
|
811
|
+
*/
|
|
812
|
+
cancelPendingAuth(channelName) {
|
|
813
|
+
const pending = this.pendingAuthRequests.get(channelName);
|
|
814
|
+
if (pending) {
|
|
815
|
+
// Отменяем запрос
|
|
816
|
+
pending.controller.abort();
|
|
817
|
+
|
|
818
|
+
// Очищаем таймаут
|
|
819
|
+
if (pending.timeoutId) {
|
|
820
|
+
clearTimeout(pending.timeoutId);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// Удаляем из карты
|
|
824
|
+
this.pendingAuthRequests.delete(channelName);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
632
828
|
/**
|
|
633
829
|
* Получение заголовков для авторизации
|
|
830
|
+
* Поддерживает как синхронные, так и асинхронные функции getAuthHeaders
|
|
831
|
+
* @returns {Promise<Object>} Заголовки для авторизации
|
|
634
832
|
*/
|
|
635
|
-
getAuthRequestHeaders() {
|
|
833
|
+
async getAuthRequestHeaders() {
|
|
636
834
|
const headers = { 'Content-Type': 'application/json' };
|
|
637
835
|
|
|
638
836
|
// Динамические заголовки имеют приоритет
|
|
639
837
|
if (typeof this.getAuthHeaders === 'function') {
|
|
640
|
-
|
|
838
|
+
// Поддержка асинхронных функций (AsyncStorage, SecureStore, Keychain)
|
|
839
|
+
const dynamicHeaders = await Promise.resolve(this.getAuthHeaders());
|
|
641
840
|
Object.assign(headers, dynamicHeaders);
|
|
642
841
|
} else if (this.authHeaders) {
|
|
643
842
|
// Статические заголовки
|
|
@@ -647,6 +846,19 @@
|
|
|
647
846
|
return headers;
|
|
648
847
|
}
|
|
649
848
|
|
|
849
|
+
/**
|
|
850
|
+
* Установка токена авторизации
|
|
851
|
+
* Удобный метод для установки Bearer токена без создания функции
|
|
852
|
+
* @param {string} token - JWT токен
|
|
853
|
+
*/
|
|
854
|
+
setAuthToken(token) {
|
|
855
|
+
if (token) {
|
|
856
|
+
this.authHeaders = { 'Authorization': `Bearer ${token}` };
|
|
857
|
+
} else {
|
|
858
|
+
this.authHeaders = null;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
650
862
|
/**
|
|
651
863
|
* Получение подписи с бэкенда
|
|
652
864
|
*/
|
|
@@ -656,7 +868,8 @@
|
|
|
656
868
|
? this.authEndpoint
|
|
657
869
|
: `${this.apiUrl}${this.authEndpoint}`;
|
|
658
870
|
|
|
659
|
-
|
|
871
|
+
// Асинхронное получение заголовков (поддержка AsyncStorage/SecureStore)
|
|
872
|
+
const headers = await this.getAuthRequestHeaders();
|
|
660
873
|
const hasAuthHeaders = this.authHeaders || this.getAuthHeaders;
|
|
661
874
|
|
|
662
875
|
const response = await fetch(authUrl, {
|
|
@@ -913,6 +1126,69 @@
|
|
|
913
1126
|
const fullChannelName = this.channelAliases.get(channelName) || this.formatChannelName(channelName);
|
|
914
1127
|
return this.channels.get(fullChannelName);
|
|
915
1128
|
}
|
|
1129
|
+
|
|
1130
|
+
/**
|
|
1131
|
+
* Проверка подписки на канал
|
|
1132
|
+
* @param {string} channelName - Имя канала
|
|
1133
|
+
* @returns {boolean} true если подписан
|
|
1134
|
+
*/
|
|
1135
|
+
isSubscribed(channelName) {
|
|
1136
|
+
const fullChannelName = this.channelAliases.get(channelName) || this.formatChannelName(channelName);
|
|
1137
|
+
const channel = this.channels.get(fullChannelName);
|
|
1138
|
+
return channel ? channel.subscribed : false;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
/**
|
|
1142
|
+
* Получение состояния подписки на канал
|
|
1143
|
+
* @param {string} channelName - Имя канала
|
|
1144
|
+
* @returns {string|null} Состояние подписки или null если канал не найден
|
|
1145
|
+
*/
|
|
1146
|
+
getSubscriptionState(channelName) {
|
|
1147
|
+
const fullChannelName = this.channelAliases.get(channelName) || this.formatChannelName(channelName);
|
|
1148
|
+
const channel = this.channels.get(fullChannelName);
|
|
1149
|
+
|
|
1150
|
+
if (!channel) {
|
|
1151
|
+
return null;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
return channel.subscriptionState || (channel.subscribed ? SubscriptionStates.SUBSCRIBED : SubscriptionStates.PENDING);
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
/**
|
|
1158
|
+
* Получение списка подписанных каналов
|
|
1159
|
+
* @returns {string[]} Массив имён подписанных каналов
|
|
1160
|
+
*/
|
|
1161
|
+
getSubscribedChannels() {
|
|
1162
|
+
const subscribedChannels = [];
|
|
1163
|
+
|
|
1164
|
+
for (const [fullName, channel] of this.channels.entries()) {
|
|
1165
|
+
if (channel.subscribed) {
|
|
1166
|
+
subscribedChannels.push(channel.originalName || this.extractChannelName(fullName));
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
return subscribedChannels;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
/**
|
|
1174
|
+
* Получение всех каналов с их состояниями
|
|
1175
|
+
* @returns {Object[]} Массив объектов с информацией о каналах
|
|
1176
|
+
*/
|
|
1177
|
+
getAllChannelsInfo() {
|
|
1178
|
+
const channelsInfo = [];
|
|
1179
|
+
|
|
1180
|
+
for (const [fullName, channel] of this.channels.entries()) {
|
|
1181
|
+
channelsInfo.push({
|
|
1182
|
+
name: channel.originalName || this.extractChannelName(fullName),
|
|
1183
|
+
fullName: fullName,
|
|
1184
|
+
subscribed: channel.subscribed,
|
|
1185
|
+
state: channel.subscriptionState || (channel.subscribed ? SubscriptionStates.SUBSCRIBED : SubscriptionStates.PENDING),
|
|
1186
|
+
type: channel.getType()
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
return channelsInfo;
|
|
1191
|
+
}
|
|
916
1192
|
}
|
|
917
1193
|
|
|
918
1194
|
var PushlerClient$1 = PushlerClient;
|
|
@@ -1347,15 +1623,21 @@
|
|
|
1347
1623
|
Channel: Channel$1,
|
|
1348
1624
|
ChannelTypes,
|
|
1349
1625
|
ConnectionStates,
|
|
1626
|
+
SubscriptionStates,
|
|
1627
|
+
ErrorCodes,
|
|
1628
|
+
Events,
|
|
1350
1629
|
generateSocketId
|
|
1351
1630
|
};
|
|
1352
1631
|
|
|
1353
1632
|
exports.Channel = Channel$1;
|
|
1354
1633
|
exports.ChannelTypes = ChannelTypes;
|
|
1355
1634
|
exports.ConnectionStates = ConnectionStates;
|
|
1635
|
+
exports.ErrorCodes = ErrorCodes;
|
|
1636
|
+
exports.Events = Events;
|
|
1356
1637
|
exports.Pushler = Pushler;
|
|
1357
1638
|
exports.PushlerClient = PushlerClient$1;
|
|
1358
1639
|
exports.PushlerServer = PushlerServer$1;
|
|
1640
|
+
exports.SubscriptionStates = SubscriptionStates;
|
|
1359
1641
|
exports.default = Pushler;
|
|
1360
1642
|
exports.generateSocketId = generateSocketId;
|
|
1361
1643
|
|