@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.esm.js
CHANGED
|
@@ -20,6 +20,18 @@ const ConnectionStates = {
|
|
|
20
20
|
FAILED: 'failed'
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Состояния подписки на канал
|
|
25
|
+
*/
|
|
26
|
+
const SubscriptionStates = {
|
|
27
|
+
PENDING: 'pending',
|
|
28
|
+
PENDING_AUTH: 'pending_auth',
|
|
29
|
+
SUBSCRIBING: 'subscribing',
|
|
30
|
+
SUBSCRIBED: 'subscribed',
|
|
31
|
+
FAILED: 'failed',
|
|
32
|
+
UNSUBSCRIBED: 'unsubscribed'
|
|
33
|
+
};
|
|
34
|
+
|
|
23
35
|
/**
|
|
24
36
|
* События WebSocket
|
|
25
37
|
*/
|
|
@@ -61,6 +73,7 @@ class Channel {
|
|
|
61
73
|
this.name = name;
|
|
62
74
|
this.options = options;
|
|
63
75
|
this.subscribed = false;
|
|
76
|
+
this.subscriptionState = SubscriptionStates.PENDING;
|
|
64
77
|
this.eventListeners = new Map();
|
|
65
78
|
this.presenceData = null;
|
|
66
79
|
}
|
|
@@ -108,6 +121,7 @@ class Channel {
|
|
|
108
121
|
*/
|
|
109
122
|
handleAuthSuccess(data) {
|
|
110
123
|
this.subscribed = true;
|
|
124
|
+
this.subscriptionState = SubscriptionStates.SUBSCRIBED;
|
|
111
125
|
this.emit(Events.CHANNEL_SUBSCRIBED, data);
|
|
112
126
|
|
|
113
127
|
// Для presence каналов сохраняем данные пользователя
|
|
@@ -135,6 +149,7 @@ class Channel {
|
|
|
135
149
|
}
|
|
136
150
|
|
|
137
151
|
this.subscribed = false;
|
|
152
|
+
this.subscriptionState = SubscriptionStates.UNSUBSCRIBED;
|
|
138
153
|
this.emit(Events.CHANNEL_UNSUBSCRIBED);
|
|
139
154
|
}
|
|
140
155
|
|
|
@@ -145,6 +160,14 @@ class Channel {
|
|
|
145
160
|
return this.subscribed;
|
|
146
161
|
}
|
|
147
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Получение состояния подписки
|
|
165
|
+
* @returns {string} Состояние подписки
|
|
166
|
+
*/
|
|
167
|
+
getSubscriptionState() {
|
|
168
|
+
return this.subscriptionState;
|
|
169
|
+
}
|
|
170
|
+
|
|
148
171
|
/**
|
|
149
172
|
* Получение данных присутствия (для presence каналов)
|
|
150
173
|
*/
|
|
@@ -215,6 +238,7 @@ class PushlerClient {
|
|
|
215
238
|
this.channels = new Map(); // Хранит каналы по полному имени (с appKey)
|
|
216
239
|
this.channelAliases = new Map(); // Маппинг оригинальное имя -> полное имя
|
|
217
240
|
this.eventListeners = new Map();
|
|
241
|
+
this.pendingAuthRequests = new Map(); // Отслеживание pending auth запросов для отмены при unsubscribe
|
|
218
242
|
this.reconnectAttempts = 0;
|
|
219
243
|
this.reconnectTimer = null;
|
|
220
244
|
this.authTimeout = null;
|
|
@@ -403,31 +427,98 @@ class PushlerClient {
|
|
|
403
427
|
};
|
|
404
428
|
}
|
|
405
429
|
|
|
430
|
+
/**
|
|
431
|
+
* Разбор склеенных JSON из одного WebSocket фрейма
|
|
432
|
+
* Сервер иногда отправляет несколько JSON объектов подряд в одном фрейме:
|
|
433
|
+
* {"event":"a",...}{"event":"b",...}
|
|
434
|
+
* @param {string} str - Строка с одним или несколькими JSON объектами
|
|
435
|
+
* @returns {string[]} Массив отдельных JSON строк
|
|
436
|
+
*/
|
|
437
|
+
splitConcatenatedJSON(str) {
|
|
438
|
+
const results = [];
|
|
439
|
+
let depth = 0;
|
|
440
|
+
let start = 0;
|
|
441
|
+
let inString = false;
|
|
442
|
+
let escape = false;
|
|
443
|
+
|
|
444
|
+
for (let i = 0; i < str.length; i++) {
|
|
445
|
+
const char = str[i];
|
|
446
|
+
|
|
447
|
+
// Обработка escape-символов внутри строк
|
|
448
|
+
if (escape) {
|
|
449
|
+
escape = false;
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (char === '\\' && inString) {
|
|
454
|
+
escape = true;
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Отслеживание строковых литералов
|
|
459
|
+
if (char === '"') {
|
|
460
|
+
inString = !inString;
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Считаем скобки только вне строк
|
|
465
|
+
if (!inString) {
|
|
466
|
+
if (char === '{') {
|
|
467
|
+
depth++;
|
|
468
|
+
} else if (char === '}') {
|
|
469
|
+
depth--;
|
|
470
|
+
if (depth === 0) {
|
|
471
|
+
results.push(str.slice(start, i + 1));
|
|
472
|
+
start = i + 1;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return results;
|
|
479
|
+
}
|
|
480
|
+
|
|
406
481
|
/**
|
|
407
482
|
* Обработка входящих сообщений
|
|
408
483
|
*/
|
|
409
484
|
handleMessage(event) {
|
|
410
485
|
try {
|
|
411
|
-
|
|
486
|
+
// Пытаемся распарсить как один JSON
|
|
487
|
+
const messages = this.splitConcatenatedJSON(event.data);
|
|
412
488
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
this.handleAuthSuccess(message);
|
|
422
|
-
break;
|
|
423
|
-
case 'pushler:auth_error':
|
|
424
|
-
this.handleAuthError(message);
|
|
425
|
-
break;
|
|
426
|
-
default:
|
|
427
|
-
this.handleChannelMessage(message);
|
|
489
|
+
// Если несколько JSON объектов, обрабатываем каждый
|
|
490
|
+
for (const jsonStr of messages) {
|
|
491
|
+
try {
|
|
492
|
+
const message = JSON.parse(jsonStr);
|
|
493
|
+
this.processMessage(message);
|
|
494
|
+
} catch (parseError) {
|
|
495
|
+
console.error('Error parsing JSON message:', parseError, 'Raw:', jsonStr);
|
|
496
|
+
}
|
|
428
497
|
}
|
|
429
498
|
} catch (error) {
|
|
430
|
-
console.error('Error
|
|
499
|
+
console.error('Error handling WebSocket message:', error);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Обработка распарсенного сообщения
|
|
505
|
+
*/
|
|
506
|
+
processMessage(message) {
|
|
507
|
+
switch (message.event) {
|
|
508
|
+
case 'pushler:connection_established':
|
|
509
|
+
this.handleConnectionEstablished(message.data);
|
|
510
|
+
break;
|
|
511
|
+
case 'pushler:subscription_succeeded':
|
|
512
|
+
this.handleSubscriptionSucceeded(message);
|
|
513
|
+
break;
|
|
514
|
+
case 'pushler:auth_success':
|
|
515
|
+
this.handleAuthSuccess(message);
|
|
516
|
+
break;
|
|
517
|
+
case 'pushler:auth_error':
|
|
518
|
+
this.handleAuthError(message);
|
|
519
|
+
break;
|
|
520
|
+
default:
|
|
521
|
+
this.handleChannelMessage(message);
|
|
431
522
|
}
|
|
432
523
|
}
|
|
433
524
|
|
|
@@ -488,7 +579,13 @@ class PushlerClient {
|
|
|
488
579
|
const { channel } = message.data;
|
|
489
580
|
const channelInstance = this.channels.get(channel);
|
|
490
581
|
|
|
582
|
+
// Очищаем pending auth запрос
|
|
583
|
+
this.cancelPendingAuth(channel);
|
|
584
|
+
|
|
491
585
|
if (channelInstance) {
|
|
586
|
+
// Устанавливаем состояние subscribed
|
|
587
|
+
channelInstance.subscriptionState = SubscriptionStates.SUBSCRIBED;
|
|
588
|
+
|
|
492
589
|
// На сервере уже происходит подписка после успешной авторизации,
|
|
493
590
|
// поэтому просто помечаем канал как подписанный
|
|
494
591
|
channelInstance.handleAuthSuccess(message.data);
|
|
@@ -501,11 +598,31 @@ class PushlerClient {
|
|
|
501
598
|
* Обработка ошибки аутентификации
|
|
502
599
|
*/
|
|
503
600
|
handleAuthError(message) {
|
|
504
|
-
const { error } = message.data;
|
|
505
|
-
|
|
601
|
+
const { error, channel } = message.data;
|
|
602
|
+
const channelName = channel || null;
|
|
603
|
+
|
|
604
|
+
// Очищаем pending auth запрос
|
|
605
|
+
if (channelName) {
|
|
606
|
+
this.cancelPendingAuth(channelName);
|
|
607
|
+
|
|
608
|
+
const channelInstance = this.channels.get(channelName);
|
|
609
|
+
if (channelInstance) {
|
|
610
|
+
channelInstance.subscriptionState = SubscriptionStates.FAILED;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const originalChannelName = channelName ? this.extractChannelName(channelName) : null;
|
|
615
|
+
|
|
616
|
+
this.emit(Events.CHANNEL_AUTH_ERROR, {
|
|
617
|
+
error,
|
|
618
|
+
channel: originalChannelName,
|
|
619
|
+
socketId: this.socketId
|
|
620
|
+
});
|
|
506
621
|
this.emit(Events.ERROR, {
|
|
507
622
|
code: ErrorCodes.AUTHENTICATION_FAILED,
|
|
508
|
-
message: error
|
|
623
|
+
message: error,
|
|
624
|
+
channel: originalChannelName,
|
|
625
|
+
socketId: this.socketId
|
|
509
626
|
});
|
|
510
627
|
}
|
|
511
628
|
|
|
@@ -563,8 +680,12 @@ class PushlerClient {
|
|
|
563
680
|
// Поддерживаем оба формата имени
|
|
564
681
|
const fullChannelName = this.channelAliases.get(channelName) || this.formatChannelName(channelName);
|
|
565
682
|
|
|
683
|
+
// Отменяем pending auth запросы для этого канала (исправление race condition)
|
|
684
|
+
this.cancelPendingAuth(fullChannelName);
|
|
685
|
+
|
|
566
686
|
const channel = this.channels.get(fullChannelName);
|
|
567
687
|
if (channel) {
|
|
688
|
+
channel.subscriptionState = SubscriptionStates.UNSUBSCRIBED;
|
|
568
689
|
channel.unsubscribe();
|
|
569
690
|
this.channels.delete(fullChannelName);
|
|
570
691
|
this.channelAliases.delete(channel.originalName || channelName);
|
|
@@ -599,41 +720,119 @@ class PushlerClient {
|
|
|
599
720
|
|
|
600
721
|
/**
|
|
601
722
|
* Аутентификация канала
|
|
723
|
+
* С поддержкой отмены при unsubscribe
|
|
602
724
|
*/
|
|
603
725
|
async authenticateChannel(channel) {
|
|
726
|
+
// Создаём AbortController для возможности отмены
|
|
727
|
+
const abortController = new AbortController();
|
|
728
|
+
const channelName = channel.name;
|
|
729
|
+
|
|
730
|
+
// Сохраняем для возможности отмены при unsubscribe
|
|
731
|
+
this.pendingAuthRequests.set(channelName, {
|
|
732
|
+
controller: abortController,
|
|
733
|
+
timeoutId: null
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
// Устанавливаем состояние канала
|
|
737
|
+
channel.subscriptionState = SubscriptionStates.PENDING_AUTH;
|
|
738
|
+
|
|
604
739
|
try {
|
|
740
|
+
// Проверка отмены перед началом
|
|
741
|
+
if (abortController.signal.aborted) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
|
|
605
745
|
const authData = await this.getChannelAuthData(channel);
|
|
606
746
|
|
|
747
|
+
// Проверка отмены после получения данных
|
|
748
|
+
if (abortController.signal.aborted) {
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
|
|
607
752
|
this.sendMessage({
|
|
608
753
|
event: 'pushler:auth',
|
|
609
754
|
data: authData
|
|
610
755
|
});
|
|
611
756
|
|
|
612
757
|
// Устанавливаем таймаут аутентификации
|
|
613
|
-
|
|
758
|
+
const timeoutId = setTimeout(() => {
|
|
759
|
+
// Не отправляем ошибку, если запрос уже отменён
|
|
760
|
+
if (abortController.signal.aborted) {
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// Очищаем pending запрос
|
|
765
|
+
this.pendingAuthRequests.delete(channelName);
|
|
766
|
+
|
|
767
|
+
// Устанавливаем состояние failed
|
|
768
|
+
channel.subscriptionState = SubscriptionStates.FAILED;
|
|
769
|
+
|
|
614
770
|
this.emit(Events.ERROR, {
|
|
615
771
|
code: ErrorCodes.AUTHENTICATION_TIMEOUT,
|
|
616
|
-
message: 'Authentication timeout'
|
|
772
|
+
message: 'Authentication timeout',
|
|
773
|
+
channel: channel.originalName || this.extractChannelName(channelName),
|
|
774
|
+
socketId: this.socketId
|
|
617
775
|
});
|
|
618
776
|
}, 10000);
|
|
619
777
|
|
|
778
|
+
// Сохраняем timeoutId для возможности очистки
|
|
779
|
+
const pending = this.pendingAuthRequests.get(channelName);
|
|
780
|
+
if (pending) {
|
|
781
|
+
pending.timeoutId = timeoutId;
|
|
782
|
+
}
|
|
783
|
+
|
|
620
784
|
} catch (error) {
|
|
785
|
+
// Не отправляем ошибку, если запрос уже отменён
|
|
786
|
+
if (abortController.signal.aborted) {
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// Очищаем pending запрос
|
|
791
|
+
this.pendingAuthRequests.delete(channelName);
|
|
792
|
+
|
|
793
|
+
// Устанавливаем состояние failed
|
|
794
|
+
channel.subscriptionState = SubscriptionStates.FAILED;
|
|
795
|
+
|
|
621
796
|
this.emit(Events.ERROR, {
|
|
622
797
|
code: ErrorCodes.AUTHENTICATION_FAILED,
|
|
623
|
-
message: error.message
|
|
798
|
+
message: error.message,
|
|
799
|
+
channel: channel.originalName || this.extractChannelName(channelName),
|
|
800
|
+
socketId: this.socketId
|
|
624
801
|
});
|
|
625
802
|
}
|
|
626
803
|
}
|
|
627
804
|
|
|
805
|
+
/**
|
|
806
|
+
* Отмена pending auth запроса для канала
|
|
807
|
+
*/
|
|
808
|
+
cancelPendingAuth(channelName) {
|
|
809
|
+
const pending = this.pendingAuthRequests.get(channelName);
|
|
810
|
+
if (pending) {
|
|
811
|
+
// Отменяем запрос
|
|
812
|
+
pending.controller.abort();
|
|
813
|
+
|
|
814
|
+
// Очищаем таймаут
|
|
815
|
+
if (pending.timeoutId) {
|
|
816
|
+
clearTimeout(pending.timeoutId);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// Удаляем из карты
|
|
820
|
+
this.pendingAuthRequests.delete(channelName);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
628
824
|
/**
|
|
629
825
|
* Получение заголовков для авторизации
|
|
826
|
+
* Поддерживает как синхронные, так и асинхронные функции getAuthHeaders
|
|
827
|
+
* @returns {Promise<Object>} Заголовки для авторизации
|
|
630
828
|
*/
|
|
631
|
-
getAuthRequestHeaders() {
|
|
829
|
+
async getAuthRequestHeaders() {
|
|
632
830
|
const headers = { 'Content-Type': 'application/json' };
|
|
633
831
|
|
|
634
832
|
// Динамические заголовки имеют приоритет
|
|
635
833
|
if (typeof this.getAuthHeaders === 'function') {
|
|
636
|
-
|
|
834
|
+
// Поддержка асинхронных функций (AsyncStorage, SecureStore, Keychain)
|
|
835
|
+
const dynamicHeaders = await Promise.resolve(this.getAuthHeaders());
|
|
637
836
|
Object.assign(headers, dynamicHeaders);
|
|
638
837
|
} else if (this.authHeaders) {
|
|
639
838
|
// Статические заголовки
|
|
@@ -643,6 +842,19 @@ class PushlerClient {
|
|
|
643
842
|
return headers;
|
|
644
843
|
}
|
|
645
844
|
|
|
845
|
+
/**
|
|
846
|
+
* Установка токена авторизации
|
|
847
|
+
* Удобный метод для установки Bearer токена без создания функции
|
|
848
|
+
* @param {string} token - JWT токен
|
|
849
|
+
*/
|
|
850
|
+
setAuthToken(token) {
|
|
851
|
+
if (token) {
|
|
852
|
+
this.authHeaders = { 'Authorization': `Bearer ${token}` };
|
|
853
|
+
} else {
|
|
854
|
+
this.authHeaders = null;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
|
|
646
858
|
/**
|
|
647
859
|
* Получение подписи с бэкенда
|
|
648
860
|
*/
|
|
@@ -652,7 +864,8 @@ class PushlerClient {
|
|
|
652
864
|
? this.authEndpoint
|
|
653
865
|
: `${this.apiUrl}${this.authEndpoint}`;
|
|
654
866
|
|
|
655
|
-
|
|
867
|
+
// Асинхронное получение заголовков (поддержка AsyncStorage/SecureStore)
|
|
868
|
+
const headers = await this.getAuthRequestHeaders();
|
|
656
869
|
const hasAuthHeaders = this.authHeaders || this.getAuthHeaders;
|
|
657
870
|
|
|
658
871
|
const response = await fetch(authUrl, {
|
|
@@ -909,6 +1122,69 @@ class PushlerClient {
|
|
|
909
1122
|
const fullChannelName = this.channelAliases.get(channelName) || this.formatChannelName(channelName);
|
|
910
1123
|
return this.channels.get(fullChannelName);
|
|
911
1124
|
}
|
|
1125
|
+
|
|
1126
|
+
/**
|
|
1127
|
+
* Проверка подписки на канал
|
|
1128
|
+
* @param {string} channelName - Имя канала
|
|
1129
|
+
* @returns {boolean} true если подписан
|
|
1130
|
+
*/
|
|
1131
|
+
isSubscribed(channelName) {
|
|
1132
|
+
const fullChannelName = this.channelAliases.get(channelName) || this.formatChannelName(channelName);
|
|
1133
|
+
const channel = this.channels.get(fullChannelName);
|
|
1134
|
+
return channel ? channel.subscribed : false;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
/**
|
|
1138
|
+
* Получение состояния подписки на канал
|
|
1139
|
+
* @param {string} channelName - Имя канала
|
|
1140
|
+
* @returns {string|null} Состояние подписки или null если канал не найден
|
|
1141
|
+
*/
|
|
1142
|
+
getSubscriptionState(channelName) {
|
|
1143
|
+
const fullChannelName = this.channelAliases.get(channelName) || this.formatChannelName(channelName);
|
|
1144
|
+
const channel = this.channels.get(fullChannelName);
|
|
1145
|
+
|
|
1146
|
+
if (!channel) {
|
|
1147
|
+
return null;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
return channel.subscriptionState || (channel.subscribed ? SubscriptionStates.SUBSCRIBED : SubscriptionStates.PENDING);
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
/**
|
|
1154
|
+
* Получение списка подписанных каналов
|
|
1155
|
+
* @returns {string[]} Массив имён подписанных каналов
|
|
1156
|
+
*/
|
|
1157
|
+
getSubscribedChannels() {
|
|
1158
|
+
const subscribedChannels = [];
|
|
1159
|
+
|
|
1160
|
+
for (const [fullName, channel] of this.channels.entries()) {
|
|
1161
|
+
if (channel.subscribed) {
|
|
1162
|
+
subscribedChannels.push(channel.originalName || this.extractChannelName(fullName));
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
return subscribedChannels;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
/**
|
|
1170
|
+
* Получение всех каналов с их состояниями
|
|
1171
|
+
* @returns {Object[]} Массив объектов с информацией о каналах
|
|
1172
|
+
*/
|
|
1173
|
+
getAllChannelsInfo() {
|
|
1174
|
+
const channelsInfo = [];
|
|
1175
|
+
|
|
1176
|
+
for (const [fullName, channel] of this.channels.entries()) {
|
|
1177
|
+
channelsInfo.push({
|
|
1178
|
+
name: channel.originalName || this.extractChannelName(fullName),
|
|
1179
|
+
fullName: fullName,
|
|
1180
|
+
subscribed: channel.subscribed,
|
|
1181
|
+
state: channel.subscriptionState || (channel.subscribed ? SubscriptionStates.SUBSCRIBED : SubscriptionStates.PENDING),
|
|
1182
|
+
type: channel.getType()
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
return channelsInfo;
|
|
1187
|
+
}
|
|
912
1188
|
}
|
|
913
1189
|
|
|
914
1190
|
var PushlerClient$1 = PushlerClient;
|
|
@@ -1343,8 +1619,11 @@ const Pushler = {
|
|
|
1343
1619
|
Channel: Channel$1,
|
|
1344
1620
|
ChannelTypes,
|
|
1345
1621
|
ConnectionStates,
|
|
1622
|
+
SubscriptionStates,
|
|
1623
|
+
ErrorCodes,
|
|
1624
|
+
Events,
|
|
1346
1625
|
generateSocketId
|
|
1347
1626
|
};
|
|
1348
1627
|
|
|
1349
|
-
export { Channel$1 as Channel, ChannelTypes, ConnectionStates, Pushler, PushlerClient$1 as PushlerClient, PushlerServer$1 as PushlerServer, Pushler as default, generateSocketId };
|
|
1628
|
+
export { Channel$1 as Channel, ChannelTypes, ConnectionStates, ErrorCodes, Events, Pushler, PushlerClient$1 as PushlerClient, PushlerServer$1 as PushlerServer, SubscriptionStates, Pushler as default, generateSocketId };
|
|
1350
1629
|
//# sourceMappingURL=pushler-ru.esm.js.map
|