@pushler/js 1.0.3 → 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.
@@ -24,6 +24,18 @@ const ConnectionStates = {
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 @@ class Channel {
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 @@ class Channel {
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 @@ class Channel {
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 @@ class Channel {
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
  */
@@ -207,6 +230,8 @@ class PushlerClient {
207
230
  this.wsUrl = options.wsUrl || `wss://ws.pushler.ru/app/${this.appKey}`;
208
231
  this.apiUrl = options.apiUrl;
209
232
  this.authEndpoint = options.authEndpoint || '/pushler/auth'; // Путь для авторизации каналов
233
+ this.authHeaders = options.authHeaders || null; // Кастомные заголовки для авторизации
234
+ this.getAuthHeaders = options.getAuthHeaders || null; // Функция для динамических заголовков
210
235
  this.autoConnect = options.autoConnect !== false;
211
236
  this.reconnectDelay = options.reconnectDelay || 1000;
212
237
  this.maxReconnectAttempts = options.maxReconnectAttempts || 5;
@@ -217,10 +242,16 @@ class PushlerClient {
217
242
  this.channels = new Map(); // Хранит каналы по полному имени (с appKey)
218
243
  this.channelAliases = new Map(); // Маппинг оригинальное имя -> полное имя
219
244
  this.eventListeners = new Map();
245
+ this.pendingAuthRequests = new Map(); // Отслеживание pending auth запросов для отмены при unsubscribe
220
246
  this.reconnectAttempts = 0;
221
247
  this.reconnectTimer = null;
222
248
  this.authTimeout = null;
223
249
 
250
+ // Promise для ожидания подключения
251
+ this.connectionPromise = null;
252
+ this.connectionResolve = null;
253
+ this.connectionReject = null;
254
+
224
255
  // Автоматическое подключение
225
256
  if (this.autoConnect) {
226
257
  this.connect();
@@ -256,13 +287,25 @@ class PushlerClient {
256
287
 
257
288
  /**
258
289
  * Подключение к WebSocket серверу
290
+ * @returns {Promise} Promise, который резолвится при успешном подключении
259
291
  */
260
292
  connect() {
261
- if (this.connectionState === ConnectionStates.CONNECTED ||
262
- this.connectionState === ConnectionStates.CONNECTING) {
263
- return;
293
+ // Если уже подключены - возвращаем резолвленный Promise
294
+ if (this.connectionState === ConnectionStates.CONNECTED) {
295
+ return Promise.resolve({ socketId: this.socketId });
296
+ }
297
+
298
+ // Если уже идёт подключение - возвращаем существующий Promise
299
+ if (this.connectionState === ConnectionStates.CONNECTING && this.connectionPromise) {
300
+ return this.connectionPromise;
264
301
  }
265
302
 
303
+ // Создаём новый Promise для ожидания подключения
304
+ this.connectionPromise = new Promise((resolve, reject) => {
305
+ this.connectionResolve = resolve;
306
+ this.connectionReject = reject;
307
+ });
308
+
266
309
  this.connectionState = ConnectionStates.CONNECTING;
267
310
  this.emit(Events.CONNECTION_STATE_CHANGED, this.connectionState);
268
311
 
@@ -277,7 +320,66 @@ class PushlerClient {
277
320
  } catch (error) {
278
321
  console.error('PushlerClient: Error creating WebSocket connection:', error);
279
322
  this.handleConnectionError(error);
323
+ if (this.connectionReject) {
324
+ this.connectionReject(error);
325
+ this.connectionPromise = null;
326
+ this.connectionResolve = null;
327
+ this.connectionReject = null;
328
+ }
280
329
  }
330
+
331
+ return this.connectionPromise;
332
+ }
333
+
334
+ /**
335
+ * Ожидание установления соединения
336
+ * @param {number} timeout - Таймаут в мс (по умолчанию 10000)
337
+ * @returns {Promise} Promise с socketId
338
+ */
339
+ waitForConnection(timeout = 10000) {
340
+ // Если уже подключены
341
+ if (this.connectionState === ConnectionStates.CONNECTED && this.socketId) {
342
+ return Promise.resolve({ socketId: this.socketId });
343
+ }
344
+
345
+ // Если есть активный Promise подключения
346
+ if (this.connectionPromise) {
347
+ return Promise.race([
348
+ this.connectionPromise,
349
+ new Promise((_, reject) =>
350
+ setTimeout(() => reject(new Error('Connection timeout')), timeout)
351
+ )
352
+ ]);
353
+ }
354
+
355
+ // Если не подключаемся - начинаем подключение
356
+ if (this.connectionState === ConnectionStates.DISCONNECTED) {
357
+ return this.connect();
358
+ }
359
+
360
+ // Для других состояний создаём Promise, который ждёт connected события
361
+ return new Promise((resolve, reject) => {
362
+ const timeoutId = setTimeout(() => {
363
+ this.off(Events.CONNECTED, onConnected);
364
+ this.off(Events.ERROR, onError);
365
+ reject(new Error('Connection timeout'));
366
+ }, timeout);
367
+
368
+ const onConnected = (data) => {
369
+ clearTimeout(timeoutId);
370
+ this.off(Events.ERROR, onError);
371
+ resolve(data);
372
+ };
373
+
374
+ const onError = (err) => {
375
+ clearTimeout(timeoutId);
376
+ this.off(Events.CONNECTED, onConnected);
377
+ reject(err);
378
+ };
379
+
380
+ this.on(Events.CONNECTED, onConnected);
381
+ this.on(Events.ERROR, onError);
382
+ });
281
383
  }
282
384
 
283
385
  /**
@@ -329,31 +431,98 @@ class PushlerClient {
329
431
  };
330
432
  }
331
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
+
332
485
  /**
333
486
  * Обработка входящих сообщений
334
487
  */
335
488
  handleMessage(event) {
336
489
  try {
337
- const message = JSON.parse(event.data);
490
+ // Пытаемся распарсить как один JSON
491
+ const messages = this.splitConcatenatedJSON(event.data);
338
492
 
339
- switch (message.event) {
340
- case 'pushler:connection_established':
341
- this.handleConnectionEstablished(message.data);
342
- break;
343
- case 'pushler:subscription_succeeded':
344
- this.handleSubscriptionSucceeded(message);
345
- break;
346
- case 'pushler:auth_success':
347
- this.handleAuthSuccess(message);
348
- break;
349
- case 'pushler:auth_error':
350
- this.handleAuthError(message);
351
- break;
352
- default:
353
- 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
+ }
354
501
  }
355
502
  } catch (error) {
356
- console.error('Error parsing WebSocket message:', 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);
357
526
  }
358
527
  }
359
528
 
@@ -367,6 +536,14 @@ class PushlerClient {
367
536
 
368
537
  console.log('Connection established with socket ID:', this.socketId);
369
538
 
539
+ // Резолвим Promise подключения
540
+ if (this.connectionResolve) {
541
+ this.connectionResolve({ socketId: this.socketId });
542
+ this.connectionPromise = null;
543
+ this.connectionResolve = null;
544
+ this.connectionReject = null;
545
+ }
546
+
370
547
  // Переподписываемся на все каналы после переподключения
371
548
  this.resubscribeAllChannels();
372
549
 
@@ -406,7 +583,13 @@ class PushlerClient {
406
583
  const { channel } = message.data;
407
584
  const channelInstance = this.channels.get(channel);
408
585
 
586
+ // Очищаем pending auth запрос
587
+ this.cancelPendingAuth(channel);
588
+
409
589
  if (channelInstance) {
590
+ // Устанавливаем состояние subscribed
591
+ channelInstance.subscriptionState = SubscriptionStates.SUBSCRIBED;
592
+
410
593
  // На сервере уже происходит подписка после успешной авторизации,
411
594
  // поэтому просто помечаем канал как подписанный
412
595
  channelInstance.handleAuthSuccess(message.data);
@@ -419,11 +602,31 @@ class PushlerClient {
419
602
  * Обработка ошибки аутентификации
420
603
  */
421
604
  handleAuthError(message) {
422
- const { error } = message.data;
423
- this.emit(Events.CHANNEL_AUTH_ERROR, error);
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
+ });
424
625
  this.emit(Events.ERROR, {
425
626
  code: ErrorCodes.AUTHENTICATION_FAILED,
426
- message: error
627
+ message: error,
628
+ channel: originalChannelName,
629
+ socketId: this.socketId
427
630
  });
428
631
  }
429
632
 
@@ -481,8 +684,12 @@ class PushlerClient {
481
684
  // Поддерживаем оба формата имени
482
685
  const fullChannelName = this.channelAliases.get(channelName) || this.formatChannelName(channelName);
483
686
 
687
+ // Отменяем pending auth запросы для этого канала (исправление race condition)
688
+ this.cancelPendingAuth(fullChannelName);
689
+
484
690
  const channel = this.channels.get(fullChannelName);
485
691
  if (channel) {
692
+ channel.subscriptionState = SubscriptionStates.UNSUBSCRIBED;
486
693
  channel.unsubscribe();
487
694
  this.channels.delete(fullChannelName);
488
695
  this.channelAliases.delete(channel.originalName || channelName);
@@ -517,32 +724,141 @@ class PushlerClient {
517
724
 
518
725
  /**
519
726
  * Аутентификация канала
727
+ * С поддержкой отмены при unsubscribe
520
728
  */
521
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
+
522
743
  try {
744
+ // Проверка отмены перед началом
745
+ if (abortController.signal.aborted) {
746
+ return;
747
+ }
748
+
523
749
  const authData = await this.getChannelAuthData(channel);
524
750
 
751
+ // Проверка отмены после получения данных
752
+ if (abortController.signal.aborted) {
753
+ return;
754
+ }
755
+
525
756
  this.sendMessage({
526
757
  event: 'pushler:auth',
527
758
  data: authData
528
759
  });
529
760
 
530
761
  // Устанавливаем таймаут аутентификации
531
- this.authTimeout = setTimeout(() => {
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
+
532
774
  this.emit(Events.ERROR, {
533
775
  code: ErrorCodes.AUTHENTICATION_TIMEOUT,
534
- message: 'Authentication timeout'
776
+ message: 'Authentication timeout',
777
+ channel: channel.originalName || this.extractChannelName(channelName),
778
+ socketId: this.socketId
535
779
  });
536
780
  }, 10000);
537
781
 
782
+ // Сохраняем timeoutId для возможности очистки
783
+ const pending = this.pendingAuthRequests.get(channelName);
784
+ if (pending) {
785
+ pending.timeoutId = timeoutId;
786
+ }
787
+
538
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
+
539
800
  this.emit(Events.ERROR, {
540
801
  code: ErrorCodes.AUTHENTICATION_FAILED,
541
- message: error.message
802
+ message: error.message,
803
+ channel: channel.originalName || this.extractChannelName(channelName),
804
+ socketId: this.socketId
542
805
  });
543
806
  }
544
807
  }
545
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
+
828
+ /**
829
+ * Получение заголовков для авторизации
830
+ * Поддерживает как синхронные, так и асинхронные функции getAuthHeaders
831
+ * @returns {Promise<Object>} Заголовки для авторизации
832
+ */
833
+ async getAuthRequestHeaders() {
834
+ const headers = { 'Content-Type': 'application/json' };
835
+
836
+ // Динамические заголовки имеют приоритет
837
+ if (typeof this.getAuthHeaders === 'function') {
838
+ // Поддержка асинхронных функций (AsyncStorage, SecureStore, Keychain)
839
+ const dynamicHeaders = await Promise.resolve(this.getAuthHeaders());
840
+ Object.assign(headers, dynamicHeaders);
841
+ } else if (this.authHeaders) {
842
+ // Статические заголовки
843
+ Object.assign(headers, this.authHeaders);
844
+ }
845
+
846
+ return headers;
847
+ }
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
+
546
862
  /**
547
863
  * Получение подписи с бэкенда
548
864
  */
@@ -552,11 +868,16 @@ class PushlerClient {
552
868
  ? this.authEndpoint
553
869
  : `${this.apiUrl}${this.authEndpoint}`;
554
870
 
871
+ // Асинхронное получение заголовков (поддержка AsyncStorage/SecureStore)
872
+ const headers = await this.getAuthRequestHeaders();
873
+ const hasAuthHeaders = this.authHeaders || this.getAuthHeaders;
874
+
555
875
  const response = await fetch(authUrl, {
556
876
  method: 'POST',
557
- headers: {
558
- 'Content-Type': 'application/json',
559
- },
877
+ headers,
878
+ // Если есть authHeaders, не используем credentials (JWT режим)
879
+ // Если нет — используем cookies
880
+ ...(hasAuthHeaders ? {} : { credentials: 'include' }),
560
881
  body: JSON.stringify({
561
882
  channel_name: channelName,
562
883
  socket_id: socketId,
@@ -566,7 +887,7 @@ class PushlerClient {
566
887
  });
567
888
 
568
889
  if (!response.ok) {
569
- const error = await response.json();
890
+ const error = await response.json().catch(() => ({ error: 'Auth request failed' }));
570
891
  throw new Error(error.error || 'Failed to get channel signature');
571
892
  }
572
893
 
@@ -699,6 +1020,14 @@ class PushlerClient {
699
1020
 
700
1021
  console.error('PushlerClient connection error:', fullErrorMessage);
701
1022
 
1023
+ // Реджектим Promise подключения
1024
+ if (this.connectionReject) {
1025
+ this.connectionReject(new Error(fullErrorMessage));
1026
+ this.connectionPromise = null;
1027
+ this.connectionResolve = null;
1028
+ this.connectionReject = null;
1029
+ }
1030
+
702
1031
  this.emit(Events.ERROR, {
703
1032
  code: ErrorCodes.CONNECTION_FAILED,
704
1033
  message: fullErrorMessage,
@@ -797,6 +1126,69 @@ class PushlerClient {
797
1126
  const fullChannelName = this.channelAliases.get(channelName) || this.formatChannelName(channelName);
798
1127
  return this.channels.get(fullChannelName);
799
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
+ }
800
1192
  }
801
1193
 
802
1194
  var PushlerClient$1 = PushlerClient;
@@ -806,11 +1198,12 @@ var PushlerClient$1 = PushlerClient;
806
1198
  * Используется на сервере Node.js для отправки событий в каналы
807
1199
  */
808
1200
  class PushlerServer {
1201
+ static API_URL = 'https://api.pushler.ru';
1202
+
809
1203
  /**
810
1204
  * @param {Object} options - Параметры клиента
811
1205
  * @param {string} options.appKey - Ключ приложения (key_xxx)
812
1206
  * @param {string} options.appSecret - Секрет приложения (secret_xxx)
813
- * @param {string} [options.apiUrl='http://localhost:8000/api'] - URL API сервера
814
1207
  * @param {number} [options.timeout=10000] - Таймаут запроса в мс
815
1208
  */
816
1209
  constructor(options = {}) {
@@ -823,7 +1216,6 @@ class PushlerServer {
823
1216
 
824
1217
  this.appKey = options.appKey;
825
1218
  this.appSecret = options.appSecret;
826
- this.apiUrl = (options.apiUrl || 'http://localhost:8000/api').replace(/\/$/, '');
827
1219
  this.timeout = options.timeout || 10000;
828
1220
  }
829
1221
 
@@ -1107,7 +1499,7 @@ class PushlerServer {
1107
1499
  * @returns {Promise<Object>} Результат запроса
1108
1500
  */
1109
1501
  async makeSignedRequest(method, endpoint, data = null) {
1110
- const url = this.apiUrl + endpoint;
1502
+ const url = PushlerServer.API_URL + endpoint;
1111
1503
  const timestamp = Math.floor(Date.now() / 1000);
1112
1504
  const body = data ? JSON.stringify(data) : '';
1113
1505
  const signature = this.generateApiSignature(body, timestamp);
@@ -1155,7 +1547,7 @@ class PushlerServer {
1155
1547
  * @returns {Promise<Object>} Результат запроса
1156
1548
  */
1157
1549
  async makeHttpRequest(method, endpoint, data = null) {
1158
- const url = this.apiUrl + endpoint;
1550
+ const url = PushlerServer.API_URL + endpoint;
1159
1551
 
1160
1552
  const headers = {
1161
1553
  'Content-Type': 'application/json',
@@ -1196,7 +1588,7 @@ class PushlerServer {
1196
1588
  * @returns {string}
1197
1589
  */
1198
1590
  getApiUrl() {
1199
- return this.apiUrl;
1591
+ return PushlerServer.API_URL;
1200
1592
  }
1201
1593
  }
1202
1594
 
@@ -1231,15 +1623,21 @@ const Pushler = {
1231
1623
  Channel: Channel$1,
1232
1624
  ChannelTypes,
1233
1625
  ConnectionStates,
1626
+ SubscriptionStates,
1627
+ ErrorCodes,
1628
+ Events,
1234
1629
  generateSocketId
1235
1630
  };
1236
1631
 
1237
1632
  exports.Channel = Channel$1;
1238
1633
  exports.ChannelTypes = ChannelTypes;
1239
1634
  exports.ConnectionStates = ConnectionStates;
1635
+ exports.ErrorCodes = ErrorCodes;
1636
+ exports.Events = Events;
1240
1637
  exports.Pushler = Pushler;
1241
1638
  exports.PushlerClient = PushlerClient$1;
1242
1639
  exports.PushlerServer = PushlerServer$1;
1640
+ exports.SubscriptionStates = SubscriptionStates;
1243
1641
  exports.default = Pushler;
1244
1642
  exports.generateSocketId = generateSocketId;
1245
1643
  //# sourceMappingURL=pushler-ru.cjs.map