@bitrix24/b24jssdk 0.4.10 → 1.0.1

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 (258) hide show
  1. package/README-AI.md +2 -1
  2. package/dist/esm/_virtual/_commonjsHelpers.mjs +16 -0
  3. package/dist/esm/_virtual/_commonjsHelpers.mjs.map +1 -0
  4. package/dist/esm/_virtual/protobuf.mjs +16 -0
  5. package/dist/esm/_virtual/protobuf.mjs.map +1 -0
  6. package/dist/esm/_virtual/protobuf2.mjs +12 -0
  7. package/dist/esm/_virtual/protobuf2.mjs.map +1 -0
  8. package/dist/esm/core/abstract-b24.mjs +355 -0
  9. package/dist/esm/core/abstract-b24.mjs.map +1 -0
  10. package/dist/esm/core/actions/abstract-action.mjs +24 -0
  11. package/dist/esm/core/actions/abstract-action.mjs.map +1 -0
  12. package/dist/esm/core/actions/abstract-batch.mjs +95 -0
  13. package/dist/esm/core/actions/abstract-batch.mjs.map +1 -0
  14. package/dist/esm/core/actions/manager.mjs +53 -0
  15. package/dist/esm/core/actions/manager.mjs.map +1 -0
  16. package/dist/esm/core/actions/v2/batch-by-chunk.mjs +92 -0
  17. package/dist/esm/core/actions/v2/batch-by-chunk.mjs.map +1 -0
  18. package/dist/esm/core/actions/v2/batch.mjs +126 -0
  19. package/dist/esm/core/actions/v2/batch.mjs.map +1 -0
  20. package/dist/esm/core/actions/v2/call-list.mjs +131 -0
  21. package/dist/esm/core/actions/v2/call-list.mjs.map +1 -0
  22. package/dist/esm/core/actions/v2/call.mjs +68 -0
  23. package/dist/esm/core/actions/v2/call.mjs.map +1 -0
  24. package/dist/esm/core/actions/v2/fetch-list.mjs +132 -0
  25. package/dist/esm/core/actions/v2/fetch-list.mjs.map +1 -0
  26. package/dist/esm/core/actions/v2/manager-v2.mjs +74 -0
  27. package/dist/esm/core/actions/v2/manager-v2.mjs.map +1 -0
  28. package/dist/esm/core/actions/v3/batch-by-chunk.mjs +91 -0
  29. package/dist/esm/core/actions/v3/batch-by-chunk.mjs.map +1 -0
  30. package/dist/esm/core/actions/v3/batch.mjs +129 -0
  31. package/dist/esm/core/actions/v3/batch.mjs.map +1 -0
  32. package/dist/esm/core/actions/v3/call-list.mjs +127 -0
  33. package/dist/esm/core/actions/v3/call-list.mjs.map +1 -0
  34. package/dist/esm/core/actions/v3/call.mjs +58 -0
  35. package/dist/esm/core/actions/v3/call.mjs.map +1 -0
  36. package/dist/esm/core/actions/v3/fetch-list.mjs +125 -0
  37. package/dist/esm/core/actions/v3/fetch-list.mjs.map +1 -0
  38. package/dist/esm/core/actions/v3/manager-v3.mjs +74 -0
  39. package/dist/esm/core/actions/v3/manager-v3.mjs.map +1 -0
  40. package/dist/esm/core/http/abstract-http.mjs +563 -0
  41. package/dist/esm/core/http/abstract-http.mjs.map +1 -0
  42. package/dist/esm/core/http/ajax-error.mjs +107 -0
  43. package/dist/esm/core/http/ajax-error.mjs.map +1 -0
  44. package/dist/esm/core/http/ajax-result.mjs +176 -0
  45. package/dist/esm/core/http/ajax-result.mjs.map +1 -0
  46. package/dist/esm/core/http/limiters/adaptive-delayer.mjs +135 -0
  47. package/dist/esm/core/http/limiters/adaptive-delayer.mjs.map +1 -0
  48. package/dist/esm/core/http/limiters/manager.mjs +308 -0
  49. package/dist/esm/core/http/limiters/manager.mjs.map +1 -0
  50. package/dist/esm/core/http/limiters/operating-limiter.mjs +171 -0
  51. package/dist/esm/core/http/limiters/operating-limiter.mjs.map +1 -0
  52. package/dist/esm/core/http/limiters/params-factory.mjs +121 -0
  53. package/dist/esm/core/http/limiters/params-factory.mjs.map +1 -0
  54. package/dist/esm/core/http/limiters/rate-limiter.mjs +402 -0
  55. package/dist/esm/core/http/limiters/rate-limiter.mjs.map +1 -0
  56. package/dist/esm/core/http/v2.mjs +100 -0
  57. package/dist/esm/core/http/v2.mjs.map +1 -0
  58. package/dist/esm/core/http/v3.mjs +94 -0
  59. package/dist/esm/core/http/v3.mjs.map +1 -0
  60. package/dist/esm/core/interaction/batch/abstract-interaction-batch.mjs +69 -0
  61. package/dist/esm/core/interaction/batch/abstract-interaction-batch.mjs.map +1 -0
  62. package/dist/esm/core/interaction/batch/parse-row.mjs +67 -0
  63. package/dist/esm/core/interaction/batch/parse-row.mjs.map +1 -0
  64. package/dist/esm/core/interaction/batch/processing/interface-strategy.mjs +42 -0
  65. package/dist/esm/core/interaction/batch/processing/interface-strategy.mjs.map +1 -0
  66. package/dist/esm/core/interaction/batch/processing/v2/abstract-processing.mjs +121 -0
  67. package/dist/esm/core/interaction/batch/processing/v2/abstract-processing.mjs.map +1 -0
  68. package/dist/esm/core/interaction/batch/processing/v2/as-array.mjs +32 -0
  69. package/dist/esm/core/interaction/batch/processing/v2/as-array.mjs.map +1 -0
  70. package/dist/esm/core/interaction/batch/processing/v2/as-object.mjs +32 -0
  71. package/dist/esm/core/interaction/batch/processing/v2/as-object.mjs.map +1 -0
  72. package/dist/esm/core/interaction/batch/processing/v3/abstract-processing.mjs +118 -0
  73. package/dist/esm/core/interaction/batch/processing/v3/abstract-processing.mjs.map +1 -0
  74. package/dist/esm/core/interaction/batch/processing/v3/as-array.mjs +32 -0
  75. package/dist/esm/core/interaction/batch/processing/v3/as-array.mjs.map +1 -0
  76. package/dist/esm/core/interaction/batch/processing/v3/as-object.mjs +32 -0
  77. package/dist/esm/core/interaction/batch/processing/v3/as-object.mjs.map +1 -0
  78. package/dist/esm/core/interaction/batch/v2.mjs +44 -0
  79. package/dist/esm/core/interaction/batch/v2.mjs.map +1 -0
  80. package/dist/esm/core/interaction/batch/v3.mjs +42 -0
  81. package/dist/esm/core/interaction/batch/v3.mjs.map +1 -0
  82. package/dist/esm/core/language/list.mjs +56 -0
  83. package/dist/esm/core/language/list.mjs.map +1 -0
  84. package/dist/esm/core/request-id-generator.mjs +42 -0
  85. package/dist/esm/core/request-id-generator.mjs.map +1 -0
  86. package/dist/esm/core/result.mjs +101 -0
  87. package/dist/esm/core/result.mjs.map +1 -0
  88. package/dist/esm/core/sdk-error.mjs +83 -0
  89. package/dist/esm/core/sdk-error.mjs.map +1 -0
  90. package/dist/esm/core/tools/abstract-tool.mjs +24 -0
  91. package/dist/esm/core/tools/abstract-tool.mjs.map +1 -0
  92. package/dist/esm/core/tools/healthcheck.mjs +48 -0
  93. package/dist/esm/core/tools/healthcheck.mjs.map +1 -0
  94. package/dist/esm/core/tools/manager.mjs +50 -0
  95. package/dist/esm/core/tools/manager.mjs.map +1 -0
  96. package/dist/esm/core/tools/ping.mjs +56 -0
  97. package/dist/esm/core/tools/ping.mjs.map +1 -0
  98. package/dist/esm/core/version-manager.mjs +116 -0
  99. package/dist/esm/core/version-manager.mjs.map +1 -0
  100. package/dist/esm/frame/auth.mjs +98 -0
  101. package/dist/esm/frame/auth.mjs.map +1 -0
  102. package/dist/esm/frame/b24.mjs +170 -0
  103. package/dist/esm/frame/b24.mjs.map +1 -0
  104. package/dist/esm/frame/dialog.mjs +78 -0
  105. package/dist/esm/frame/dialog.mjs.map +1 -0
  106. package/dist/esm/frame/frame.mjs +101 -0
  107. package/dist/esm/frame/frame.mjs.map +1 -0
  108. package/dist/esm/frame/message/commands.mjs +37 -0
  109. package/dist/esm/frame/message/commands.mjs.map +1 -0
  110. package/dist/esm/frame/message/controller.mjs +177 -0
  111. package/dist/esm/frame/message/controller.mjs.map +1 -0
  112. package/dist/esm/frame/options.mjs +106 -0
  113. package/dist/esm/frame/options.mjs.map +1 -0
  114. package/dist/esm/frame/parent.mjs +250 -0
  115. package/dist/esm/frame/parent.mjs.map +1 -0
  116. package/dist/esm/frame/placement.mjs +131 -0
  117. package/dist/esm/frame/placement.mjs.map +1 -0
  118. package/dist/esm/frame/slider.mjs +156 -0
  119. package/dist/esm/frame/slider.mjs.map +1 -0
  120. package/dist/esm/helper/abstract-helper.mjs +52 -0
  121. package/dist/esm/helper/abstract-helper.mjs.map +1 -0
  122. package/dist/esm/helper/app-manager.mjs +37 -0
  123. package/dist/esm/helper/app-manager.mjs.map +1 -0
  124. package/dist/esm/helper/currency-manager.mjs +207 -0
  125. package/dist/esm/helper/currency-manager.mjs.map +1 -0
  126. package/dist/esm/helper/helper-manager.mjs +388 -0
  127. package/dist/esm/helper/helper-manager.mjs.map +1 -0
  128. package/dist/esm/helper/license-manager.mjs +50 -0
  129. package/dist/esm/helper/license-manager.mjs.map +1 -0
  130. package/dist/esm/helper/options-manager.mjs +196 -0
  131. package/dist/esm/helper/options-manager.mjs.map +1 -0
  132. package/dist/esm/helper/payment-manager.mjs +33 -0
  133. package/dist/esm/helper/payment-manager.mjs.map +1 -0
  134. package/dist/esm/helper/profile-manager.mjs +33 -0
  135. package/dist/esm/helper/profile-manager.mjs.map +1 -0
  136. package/dist/esm/helper/use-b24-helper.mjs +84 -0
  137. package/dist/esm/helper/use-b24-helper.mjs.map +1 -0
  138. package/dist/esm/hook/auth.mjs +77 -0
  139. package/dist/esm/hook/auth.mjs.map +1 -0
  140. package/dist/esm/hook/b24.mjs +115 -0
  141. package/dist/esm/hook/b24.mjs.map +1 -0
  142. package/dist/esm/index.d.mts +2757 -525
  143. package/dist/esm/index.d.ts +2757 -525
  144. package/dist/esm/index.mjs +70 -14111
  145. package/dist/esm/index.mjs.map +1 -1
  146. package/dist/esm/loader-b24frame.mjs +101 -0
  147. package/dist/esm/loader-b24frame.mjs.map +1 -0
  148. package/dist/esm/logger/abstract-logger.mjs +69 -0
  149. package/dist/esm/logger/abstract-logger.mjs.map +1 -0
  150. package/dist/esm/logger/browser.mjs +162 -0
  151. package/dist/esm/logger/browser.mjs.map +1 -0
  152. package/dist/esm/logger/formatter/abstract-formatter.mjs +34 -0
  153. package/dist/esm/logger/formatter/abstract-formatter.mjs.map +1 -0
  154. package/dist/esm/logger/formatter/json-formatter.mjs +34 -0
  155. package/dist/esm/logger/formatter/json-formatter.mjs.map +1 -0
  156. package/dist/esm/logger/formatter/line-formatter.mjs +41 -0
  157. package/dist/esm/logger/formatter/line-formatter.mjs.map +1 -0
  158. package/dist/esm/logger/formatter/telegram-formatter.mjs +103 -0
  159. package/dist/esm/logger/formatter/telegram-formatter.mjs.map +1 -0
  160. package/dist/esm/logger/handler/abstract-handler.mjs +39 -0
  161. package/dist/esm/logger/handler/abstract-handler.mjs.map +1 -0
  162. package/dist/esm/logger/handler/consola-adapter.mjs +62 -0
  163. package/dist/esm/logger/handler/consola-adapter.mjs.map +1 -0
  164. package/dist/esm/logger/handler/console-handler.mjs +98 -0
  165. package/dist/esm/logger/handler/console-handler.mjs.map +1 -0
  166. package/dist/esm/logger/handler/console-v2-handler.mjs +51 -0
  167. package/dist/esm/logger/handler/console-v2-handler.mjs.map +1 -0
  168. package/dist/esm/logger/handler/memory-handler.mjs +48 -0
  169. package/dist/esm/logger/handler/memory-handler.mjs.map +1 -0
  170. package/dist/esm/logger/handler/stream-handler.mjs +73 -0
  171. package/dist/esm/logger/handler/stream-handler.mjs.map +1 -0
  172. package/dist/esm/logger/handler/telegram-handler.mjs +157 -0
  173. package/dist/esm/logger/handler/telegram-handler.mjs.map +1 -0
  174. package/dist/esm/logger/handler/winston-adapter.mjs +57 -0
  175. package/dist/esm/logger/handler/winston-adapter.mjs.map +1 -0
  176. package/dist/esm/logger/logger-factory.mjs +67 -0
  177. package/dist/esm/logger/logger-factory.mjs.map +1 -0
  178. package/dist/esm/logger/logger.mjs +76 -0
  179. package/dist/esm/logger/logger.mjs.map +1 -0
  180. package/dist/esm/logger/null-logger.mjs +32 -0
  181. package/dist/esm/logger/null-logger.mjs.map +1 -0
  182. package/dist/esm/logger/processor/memory-usage-processor.mjs +20 -0
  183. package/dist/esm/logger/processor/memory-usage-processor.mjs.map +1 -0
  184. package/dist/esm/logger/processor/pid-processor.mjs +20 -0
  185. package/dist/esm/logger/processor/pid-processor.mjs.map +1 -0
  186. package/dist/esm/oauth/auth.mjs +211 -0
  187. package/dist/esm/oauth/auth.mjs.map +1 -0
  188. package/dist/esm/oauth/b24.mjs +117 -0
  189. package/dist/esm/oauth/b24.mjs.map +1 -0
  190. package/dist/esm/oauth/refresh-token-error.mjs +20 -0
  191. package/dist/esm/oauth/refresh-token-error.mjs.map +1 -0
  192. package/dist/esm/pullClient/abstract-connector.mjs +78 -0
  193. package/dist/esm/pullClient/abstract-connector.mjs.map +1 -0
  194. package/dist/esm/pullClient/channel-manager.mjs +89 -0
  195. package/dist/esm/pullClient/channel-manager.mjs.map +1 -0
  196. package/dist/esm/pullClient/client.mjs +2064 -0
  197. package/dist/esm/pullClient/client.mjs.map +1 -0
  198. package/dist/esm/pullClient/errors.mjs +31 -0
  199. package/dist/esm/pullClient/errors.mjs.map +1 -0
  200. package/dist/esm/pullClient/json-rpc.mjs +210 -0
  201. package/dist/esm/pullClient/json-rpc.mjs.map +1 -0
  202. package/dist/esm/pullClient/long-polling-connector.mjs +157 -0
  203. package/dist/esm/pullClient/long-polling-connector.mjs.map +1 -0
  204. package/dist/esm/pullClient/protobuf/index.mjs +17 -0
  205. package/dist/esm/pullClient/protobuf/index.mjs.map +1 -0
  206. package/dist/esm/pullClient/protobuf/model.mjs +1058 -0
  207. package/dist/esm/pullClient/protobuf/model.mjs.map +1 -0
  208. package/dist/esm/pullClient/protobuf/protobuf.mjs +4653 -0
  209. package/dist/esm/pullClient/protobuf/protobuf.mjs.map +1 -0
  210. package/dist/esm/pullClient/shared-config.mjs +133 -0
  211. package/dist/esm/pullClient/shared-config.mjs.map +1 -0
  212. package/dist/esm/pullClient/storage-manager.mjs +72 -0
  213. package/dist/esm/pullClient/storage-manager.mjs.map +1 -0
  214. package/dist/esm/pullClient/web-socket-connector.mjs +129 -0
  215. package/dist/esm/pullClient/web-socket-connector.mjs.map +1 -0
  216. package/dist/esm/tools/browser.mjs +154 -0
  217. package/dist/esm/tools/browser.mjs.map +1 -0
  218. package/dist/esm/tools/environment.mjs +29 -0
  219. package/dist/esm/tools/environment.mjs.map +1 -0
  220. package/dist/esm/tools/formatters/iban.mjs +304 -0
  221. package/dist/esm/tools/formatters/iban.mjs.map +1 -0
  222. package/dist/esm/tools/formatters/numbers.mjs +64 -0
  223. package/dist/esm/tools/formatters/numbers.mjs.map +1 -0
  224. package/dist/esm/tools/index.mjs +37 -0
  225. package/dist/esm/tools/index.mjs.map +1 -0
  226. package/dist/esm/tools/scroll-size.mjs +25 -0
  227. package/dist/esm/tools/scroll-size.mjs.map +1 -0
  228. package/dist/esm/tools/text.mjs +208 -0
  229. package/dist/esm/tools/text.mjs.map +1 -0
  230. package/dist/esm/tools/type.mjs +337 -0
  231. package/dist/esm/tools/type.mjs.map +1 -0
  232. package/dist/esm/tools/use-formatters.mjs +460 -0
  233. package/dist/esm/tools/use-formatters.mjs.map +1 -0
  234. package/dist/esm/tools/uuidv7.mjs +54 -0
  235. package/dist/esm/tools/uuidv7.mjs.map +1 -0
  236. package/dist/esm/types/b24-helper.mjs +56 -0
  237. package/dist/esm/types/b24-helper.mjs.map +1 -0
  238. package/dist/esm/types/b24.mjs +16 -0
  239. package/dist/esm/types/b24.mjs.map +1 -0
  240. package/dist/esm/types/bizproc/index.mjs +187 -0
  241. package/dist/esm/types/bizproc/index.mjs.map +1 -0
  242. package/dist/esm/types/catalog/index.mjs +35 -0
  243. package/dist/esm/types/catalog/index.mjs.map +1 -0
  244. package/dist/esm/types/common.mjs +31 -0
  245. package/dist/esm/types/common.mjs.map +1 -0
  246. package/dist/esm/types/crm/entity-type.mjs +57 -0
  247. package/dist/esm/types/crm/entity-type.mjs.map +1 -0
  248. package/dist/esm/types/crm/productrow.mjs +17 -0
  249. package/dist/esm/types/crm/productrow.mjs.map +1 -0
  250. package/dist/esm/types/logger.mjs +22 -0
  251. package/dist/esm/types/logger.mjs.map +1 -0
  252. package/dist/esm/types/pull.mjs +83 -0
  253. package/dist/esm/types/pull.mjs.map +1 -0
  254. package/dist/umd/index.js +31216 -26905
  255. package/dist/umd/index.js.map +1 -1
  256. package/dist/umd/index.min.js +63 -40
  257. package/dist/umd/index.min.js.map +1 -1
  258. package/package.json +36 -31
@@ -0,0 +1,2064 @@
1
+ /**
2
+ * @package @bitrix24/b24jssdk
3
+ * @version 1.0.1
4
+ * @copyright (c) 2026 Bitrix24
5
+ * @license MIT
6
+ * @see https://github.com/bitrix24/b24jssdk
7
+ * @see https://bitrix24.github.io/b24jssdk/
8
+ */
9
+ import { Type } from '../tools/type.mjs';
10
+ import { Text } from '../tools/text.mjs';
11
+ import { Browser } from '../tools/browser.mjs';
12
+ import { StorageManager } from './storage-manager.mjs';
13
+ import { JsonRpc } from './json-rpc.mjs';
14
+ import { SharedConfig } from './shared-config.mjs';
15
+ import { ChannelManager } from './channel-manager.mjs';
16
+ import { Receiver, IncomingMessage, RequestBatch, ResponseBatch } from './protobuf/index.mjs';
17
+ import { ConnectionType, PullStatus, CloseReasons, SubscriptionType, LsKeys, RpcMethod, ServerMode, SenderType, SystemCommands } from '../types/pull.mjs';
18
+ import { WebSocketConnector } from './web-socket-connector.mjs';
19
+ import { LongPollingConnector } from './long-polling-connector.mjs';
20
+ import { LoggerFactory } from '../logger/logger-factory.mjs';
21
+
22
+ var __defProp = Object.defineProperty;
23
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
24
+ const REVISION = 19;
25
+ const RESTORE_WEBSOCKET_TIMEOUT = 30 * 60;
26
+ const OFFLINE_STATUS_DELAY = 5e3;
27
+ const CONFIG_CHECK_INTERVAL = 60 * 1e3;
28
+ const MAX_IDS_TO_STORE = 10;
29
+ const PING_TIMEOUT = 10;
30
+ const JSON_RPC_PING = "ping";
31
+ const JSON_RPC_PONG = "pong";
32
+ const LS_SESSION = "bx-pull-session";
33
+ const LS_SESSION_CACHE_TIME = 20;
34
+ const EmptyConfig = {
35
+ api: {},
36
+ channels: {},
37
+ publicChannels: {},
38
+ server: { timeShift: 0 },
39
+ clientId: null,
40
+ jwt: null,
41
+ exp: 0
42
+ };
43
+ class PullClient {
44
+ static {
45
+ __name(this, "PullClient");
46
+ }
47
+ // region Params ////
48
+ _logger;
49
+ _restClient;
50
+ _status;
51
+ _context;
52
+ _guestMode;
53
+ _guestUserId;
54
+ _userId;
55
+ _configGetMethod;
56
+ _getPublicListMethod;
57
+ _siteId;
58
+ _enabled;
59
+ _unloading = false;
60
+ _starting = false;
61
+ _debug = false;
62
+ _connectionAttempt = 0;
63
+ _connectionType = ConnectionType.WebSocket;
64
+ _skipStorageInit;
65
+ _skipCheckRevision;
66
+ _subscribers = {};
67
+ _watchTagsQueue = /* @__PURE__ */ new Map();
68
+ _watchUpdateInterval = 174e4;
69
+ _watchForceUpdateInterval = 5e3;
70
+ _configTimestamp = 0;
71
+ _session = {
72
+ mid: null,
73
+ tag: null,
74
+ time: null,
75
+ history: {},
76
+ lastMessageIds: [],
77
+ messageCount: 0
78
+ };
79
+ _connectors = {
80
+ [ConnectionType.Undefined]: null,
81
+ [ConnectionType.WebSocket]: null,
82
+ [ConnectionType.LongPolling]: null
83
+ };
84
+ _isSecure;
85
+ _config = null;
86
+ _storage = null;
87
+ _sharedConfig;
88
+ _channelManager;
89
+ _jsonRpcAdapter = null;
90
+ /**
91
+ * @depricate
92
+ */
93
+ // private _notificationPopup: null = null
94
+ // timers ////
95
+ _reconnectTimeout = null;
96
+ _restartTimeout = null;
97
+ _restoreWebSocketTimeout = null;
98
+ _checkInterval = null;
99
+ _offlineTimeout = null;
100
+ _watchUpdateTimeout = null;
101
+ _pingWaitTimeout = null;
102
+ // manual stop workaround ////
103
+ _isManualDisconnect = false;
104
+ _loggingEnabled = false;
105
+ // bound event handlers ////
106
+ _onPingTimeoutHandler;
107
+ // [userId] => array of callbacks
108
+ _userStatusCallbacks = {};
109
+ _connectPromise = null;
110
+ _startingPromise = null;
111
+ // endregion ////
112
+ // region Init ////
113
+ /**
114
+ * @param params
115
+ */
116
+ constructor(params) {
117
+ this._logger = LoggerFactory.createNullLogger();
118
+ this._restClient = params.b24;
119
+ this._status = PullStatus.Offline;
120
+ this._context = "master";
121
+ if (params.restApplication) {
122
+ if (typeof params.configGetMethod === "undefined") {
123
+ params.configGetMethod = "pull.application.config.get";
124
+ }
125
+ if (typeof params.skipCheckRevision === "undefined") {
126
+ params.skipCheckRevision = true;
127
+ }
128
+ if (Type.isStringFilled(params.restApplication)) {
129
+ params.siteId = params.restApplication;
130
+ }
131
+ params.serverEnabled = true;
132
+ }
133
+ this._guestMode = params.guestMode ? Text.toBoolean(params.guestMode) : false;
134
+ this._guestUserId = params.guestUserId ? Text.toInteger(params.guestUserId) : 0;
135
+ if (this._guestMode && this._guestUserId > 0) {
136
+ this._userId = this._guestUserId;
137
+ } else {
138
+ this._guestMode = false;
139
+ this._userId = params.userId ? Text.toInteger(params.userId) : 0;
140
+ }
141
+ this._siteId = params.siteId ?? "none";
142
+ this._enabled = !Type.isUndefined(params.serverEnabled) ? params.serverEnabled === true : true;
143
+ this._configGetMethod = !Type.isStringFilled(params.configGetMethod) ? "pull.config.get" : params.configGetMethod || "";
144
+ this._getPublicListMethod = !Type.isStringFilled(params.getPublicListMethod) ? "pull.channel.public.list" : params.getPublicListMethod || "";
145
+ this._skipStorageInit = params.skipStorageInit === true;
146
+ this._skipCheckRevision = params.skipCheckRevision === true;
147
+ if (!Type.isUndefined(params.configTimestamp)) {
148
+ this._configTimestamp = Text.toInteger(params.configTimestamp);
149
+ }
150
+ this._isSecure = document?.location.href.indexOf("https") === 0;
151
+ if (this._userId && !this._skipStorageInit) {
152
+ this._storage = new StorageManager({
153
+ userId: this._userId,
154
+ siteId: this._siteId
155
+ });
156
+ }
157
+ this._sharedConfig = new SharedConfig({
158
+ onWebSocketBlockChanged: this.onWebSocketBlockChanged.bind(this),
159
+ storage: this._storage
160
+ });
161
+ this._channelManager = new ChannelManager({
162
+ b24: this._restClient,
163
+ getPublicListMethod: this._getPublicListMethod
164
+ });
165
+ this._loggingEnabled = this._sharedConfig.isLoggingEnabled();
166
+ this._onPingTimeoutHandler = this.onPingTimeout.bind(this);
167
+ }
168
+ setLogger(logger) {
169
+ this._logger = logger;
170
+ this._jsonRpcAdapter?.setLogger(this.getLogger());
171
+ this._storage?.setLogger(this.getLogger());
172
+ this._sharedConfig.setLogger(this.getLogger());
173
+ this._channelManager.setLogger(this.getLogger());
174
+ this._connectors.webSocket?.setLogger(this.getLogger());
175
+ this._connectors.longPolling?.setLogger(this.getLogger());
176
+ }
177
+ getLogger() {
178
+ return this._logger;
179
+ }
180
+ destroy() {
181
+ this.stop(CloseReasons.NORMAL_CLOSURE, "manual stop");
182
+ this.onBeforeUnload();
183
+ }
184
+ init() {
185
+ this._connectors.webSocket = new WebSocketConnector({
186
+ parent: this,
187
+ onOpen: this.onWebSocketOpen.bind(this),
188
+ onMessage: this.onIncomingMessage.bind(this),
189
+ onDisconnect: this.onWebSocketDisconnect.bind(this),
190
+ onError: this.onWebSocketError.bind(this)
191
+ });
192
+ this._connectors.longPolling = new LongPollingConnector({
193
+ parent: this,
194
+ onOpen: this.onLongPollingOpen.bind(this),
195
+ onMessage: this.onIncomingMessage.bind(this),
196
+ onDisconnect: this.onLongPollingDisconnect.bind(this),
197
+ onError: this.onLongPollingError.bind(this)
198
+ });
199
+ this._connectionType = this.isWebSocketAllowed() ? ConnectionType.WebSocket : ConnectionType.LongPolling;
200
+ window.addEventListener("beforeunload", this.onBeforeUnload.bind(this));
201
+ window.addEventListener("offline", this.onOffline.bind(this));
202
+ window.addEventListener("online", this.onOnline.bind(this));
203
+ this._jsonRpcAdapter = new JsonRpc({
204
+ connector: this._connectors.webSocket,
205
+ handlers: {
206
+ "incoming.message": this.handleRpcIncomingMessage.bind(this)
207
+ }
208
+ });
209
+ }
210
+ // endregion ////
211
+ // region Get-Set ////
212
+ get connector() {
213
+ return this._connectors[this._connectionType];
214
+ }
215
+ get status() {
216
+ return this._status;
217
+ }
218
+ /**
219
+ * @param status
220
+ */
221
+ set status(status) {
222
+ if (this._status === status) {
223
+ return;
224
+ }
225
+ this._status = status;
226
+ if (this._offlineTimeout) {
227
+ clearTimeout(this._offlineTimeout);
228
+ this._offlineTimeout = null;
229
+ }
230
+ if (status === PullStatus.Offline) {
231
+ this.sendPullStatusDelayed(status, OFFLINE_STATUS_DELAY);
232
+ } else {
233
+ this.sendPullStatus(status);
234
+ }
235
+ }
236
+ get session() {
237
+ return this._session;
238
+ }
239
+ // endregion ////
240
+ // region Public /////
241
+ /**
242
+ * Creates a subscription to incoming messages.
243
+ *
244
+ * @param {TypeSubscriptionOptions | TypeSubscriptionCommandHandler} params
245
+ * @returns { () => void } - Unsubscribe callback function
246
+ */
247
+ subscribe(params) {
248
+ if (!Type.isPlainObject(params)) {
249
+ return this.attachCommandHandler(params);
250
+ }
251
+ params = params;
252
+ params.type = params.type || SubscriptionType.Server;
253
+ params.command = params.command || null;
254
+ if (params.type == SubscriptionType.Server || params.type == SubscriptionType.Client) {
255
+ if (typeof params.moduleId === "undefined") {
256
+ throw new TypeError(
257
+ `${Text.getDateForLog()}: Pull.subscribe: parameter moduleId is not specified`
258
+ );
259
+ }
260
+ if (typeof this._subscribers[params.type] === "undefined") {
261
+ this._subscribers[params.type] = {};
262
+ }
263
+ if (typeof this._subscribers[params.type][params.moduleId] === "undefined") {
264
+ this._subscribers[params.type][params.moduleId] = {
265
+ callbacks: [],
266
+ commands: {}
267
+ };
268
+ }
269
+ if (params.command) {
270
+ if (typeof this._subscribers[params.type][params.moduleId]["commands"][params.command] === "undefined") {
271
+ this._subscribers[params.type][params.moduleId]["commands"][params.command] = [];
272
+ }
273
+ this._subscribers[params.type][params.moduleId]["commands"][params.command].push(params.callback);
274
+ return () => {
275
+ if (typeof params.type === "undefined" || typeof params.moduleId === "undefined" || typeof params.command === "undefined" || null === params.command) {
276
+ return;
277
+ }
278
+ this._subscribers[params.type][params.moduleId]["commands"][params.command] = this._subscribers[params.type][params.moduleId]["commands"][params.command].filter((element) => {
279
+ return element !== params.callback;
280
+ });
281
+ };
282
+ } else {
283
+ this._subscribers[params.type][params.moduleId]["callbacks"].push(
284
+ params.callback
285
+ );
286
+ return () => {
287
+ if (typeof params.type === "undefined" || typeof params.moduleId === "undefined") {
288
+ return;
289
+ }
290
+ this._subscribers[params.type][params.moduleId]["callbacks"] = this._subscribers[params.type][params.moduleId]["callbacks"].filter(
291
+ (element) => {
292
+ return element !== params.callback;
293
+ }
294
+ );
295
+ };
296
+ }
297
+ } else {
298
+ if (typeof this._subscribers[params.type] === "undefined") {
299
+ this._subscribers[params.type] = [];
300
+ }
301
+ this._subscribers[params.type].push(params.callback);
302
+ return () => {
303
+ if (typeof params.type === "undefined") {
304
+ return;
305
+ }
306
+ this._subscribers[params.type] = this._subscribers[params.type].filter(
307
+ (element) => {
308
+ return element !== params.callback;
309
+ }
310
+ );
311
+ };
312
+ }
313
+ }
314
+ /**
315
+ * @param {TypeSubscriptionCommandHandler} handler
316
+ * @returns {() => void} - Unsubscribe callback function
317
+ */
318
+ attachCommandHandler(handler) {
319
+ if (typeof handler.getModuleId !== "function" || typeof handler.getModuleId() !== "string") {
320
+ this.getLogger().error(`${Text.getDateForLog()}: Pull.attachCommandHandler: result of handler.getModuleId() is not a string.`);
321
+ return () => {
322
+ };
323
+ }
324
+ let type = SubscriptionType.Server;
325
+ if (typeof handler.getSubscriptionType === "function") {
326
+ type = handler.getSubscriptionType();
327
+ }
328
+ return this.subscribe({
329
+ type,
330
+ moduleId: handler.getModuleId(),
331
+ callback: /* @__PURE__ */ __name((data) => {
332
+ let method = null;
333
+ if (typeof handler.getMap === "function") {
334
+ const mapping = handler.getMap();
335
+ if (mapping && typeof mapping === "object") {
336
+ const rowMapping = mapping[data.command];
337
+ if (typeof rowMapping === "function") {
338
+ method = rowMapping.bind(handler);
339
+ } else if (typeof rowMapping === "string" && typeof handler[rowMapping] === "function") {
340
+ method = handler[rowMapping].bind(handler);
341
+ }
342
+ }
343
+ }
344
+ if (!method) {
345
+ const methodName = `handle${Text.capitalize(data.command)}`;
346
+ if (typeof handler[methodName] === "function") {
347
+ method = handler[methodName].bind(handler);
348
+ }
349
+ }
350
+ if (method) {
351
+ if (this._debug && this._context !== "master") {
352
+ this.getLogger().warning(
353
+ `${Text.getDateForLog()}: Pull.attachCommandHandler: result of handler.getModuleId() is not a string`,
354
+ { data }
355
+ );
356
+ }
357
+ method(data.params, data.extra, data.command);
358
+ }
359
+ }, "callback")
360
+ });
361
+ }
362
+ /**
363
+ * @param config
364
+ */
365
+ async start(config = null) {
366
+ let allowConfigCaching = true;
367
+ if (this.isConnected()) {
368
+ return Promise.resolve(true);
369
+ }
370
+ if (this._starting && this._startingPromise) {
371
+ return this._startingPromise;
372
+ }
373
+ if (!this._userId) {
374
+ throw new Error("Not set userId");
375
+ }
376
+ if (this._siteId === "none") {
377
+ throw new Error("Not set siteId");
378
+ }
379
+ let skipReconnectToLastSession = false;
380
+ if (!!config && Type.isPlainObject(config)) {
381
+ if (typeof config?.skipReconnectToLastSession !== "undefined") {
382
+ skipReconnectToLastSession = config.skipReconnectToLastSession;
383
+ delete config.skipReconnectToLastSession;
384
+ }
385
+ this._config = config;
386
+ allowConfigCaching = false;
387
+ }
388
+ if (!this._enabled) {
389
+ return Promise.reject({
390
+ ex: {
391
+ error: "PULL_DISABLED",
392
+ error_description: "Push & Pull server is disabled"
393
+ }
394
+ });
395
+ }
396
+ const now = Date.now();
397
+ let oldSession;
398
+ if (!skipReconnectToLastSession && this._storage) {
399
+ oldSession = this._storage.get(LS_SESSION, null);
400
+ }
401
+ if (Type.isPlainObject(oldSession) && Object.prototype.hasOwnProperty.call(oldSession, "ttl") && oldSession["ttl"] >= now) {
402
+ this._session.mid = oldSession["mid"];
403
+ }
404
+ this._starting = true;
405
+ return this._startingPromise = new Promise((resolve, reject) => {
406
+ this.loadConfig("client_start").then((config2) => {
407
+ this.setConfig(config2, allowConfigCaching);
408
+ this.init();
409
+ this.updateWatch(true);
410
+ this.startCheckConfig();
411
+ this.connect().then(
412
+ () => resolve(true),
413
+ (error) => reject(error)
414
+ );
415
+ }).catch((error) => {
416
+ this._starting = false;
417
+ this.status = PullStatus.Offline;
418
+ this.stopCheckConfig();
419
+ this.getLogger().error(
420
+ `${Text.getDateForLog()}: Pull: could not read push-server config`,
421
+ { error }
422
+ );
423
+ reject(error);
424
+ });
425
+ });
426
+ }
427
+ /**
428
+ * @param disconnectCode
429
+ * @param disconnectReason
430
+ */
431
+ restart(disconnectCode = CloseReasons.NORMAL_CLOSURE, disconnectReason = "manual restart") {
432
+ if (this._restartTimeout) {
433
+ clearTimeout(this._restartTimeout);
434
+ this._restartTimeout = null;
435
+ }
436
+ this.getLogger().debug(
437
+ `${Text.getDateForLog()}: Pull: restarting with code ${disconnectCode}`
438
+ );
439
+ this.disconnect(disconnectCode, disconnectReason);
440
+ if (this._storage) {
441
+ this._storage.remove(LsKeys.PullConfig);
442
+ }
443
+ this._config = null;
444
+ const loadConfigReason = `${disconnectCode}_${disconnectReason.replaceAll(" ", "_")}`;
445
+ this.loadConfig(loadConfigReason).then(
446
+ (config) => {
447
+ this.setConfig(config, true);
448
+ this.updateWatch();
449
+ this.startCheckConfig();
450
+ this.connect().catch((error) => {
451
+ this.getLogger().error("restart error", { error });
452
+ });
453
+ },
454
+ (error) => {
455
+ this.getLogger().error(
456
+ `${Text.getDateForLog()}: Pull: could not read push-server config `,
457
+ { error }
458
+ );
459
+ this.status = PullStatus.Offline;
460
+ if (this._reconnectTimeout) {
461
+ clearTimeout(this._reconnectTimeout);
462
+ this._reconnectTimeout = null;
463
+ }
464
+ if (error?.status == 401 || error?.status == 403) {
465
+ this.stopCheckConfig();
466
+ this.onCustomEvent("onPullError", ["AUTHORIZE_ERROR"]);
467
+ }
468
+ }
469
+ );
470
+ }
471
+ stop(disconnectCode = CloseReasons.NORMAL_CLOSURE, disconnectReason = "manual stop") {
472
+ this.disconnect(disconnectCode, disconnectReason);
473
+ this.stopCheckConfig();
474
+ }
475
+ reconnect(disconnectCode, disconnectReason, delay = 1) {
476
+ this.disconnect(disconnectCode, disconnectReason);
477
+ this.scheduleReconnect(delay);
478
+ }
479
+ /**
480
+ * @param lastMessageId
481
+ */
482
+ setLastMessageId(lastMessageId) {
483
+ this._session.mid = lastMessageId;
484
+ }
485
+ /**
486
+ * Send a single message to the specified users.
487
+ *
488
+ * @param users User ids of the message receivers.
489
+ * @param moduleId Name of the module to receive a message,
490
+ * @param command Command name.
491
+ * @param {object} params Command parameters.
492
+ * @param [expiry] Message expiry time in seconds.
493
+ * @return {Promise}
494
+ */
495
+ async sendMessage(users, moduleId, command, params, expiry) {
496
+ const message = {
497
+ userList: users,
498
+ body: {
499
+ module_id: moduleId,
500
+ command,
501
+ params
502
+ },
503
+ expiry
504
+ };
505
+ if (this.isJsonRpc()) {
506
+ return this._jsonRpcAdapter?.executeOutgoingRpcCommand(
507
+ RpcMethod.Publish,
508
+ message
509
+ );
510
+ } else {
511
+ return this.sendMessageBatch([message]);
512
+ }
513
+ }
514
+ /**
515
+ * Send a single message to the specified public channels.
516
+ *
517
+ * @param publicChannels Public ids of the channels to receive a message.
518
+ * @param moduleId Name of the module to receive a message,
519
+ * @param command Command name.
520
+ * @param {object} params Command parameters.
521
+ * @param [expiry] Message expiry time in seconds.
522
+ * @return {Promise}
523
+ */
524
+ async sendMessageToChannels(publicChannels, moduleId, command, params, expiry) {
525
+ const message = {
526
+ channelList: publicChannels,
527
+ body: {
528
+ module_id: moduleId,
529
+ command,
530
+ params
531
+ },
532
+ expiry
533
+ };
534
+ if (this.isJsonRpc()) {
535
+ return this._jsonRpcAdapter?.executeOutgoingRpcCommand(
536
+ RpcMethod.Publish,
537
+ message
538
+ );
539
+ } else {
540
+ return this.sendMessageBatch([message]);
541
+ }
542
+ }
543
+ /**
544
+ * @param debugFlag
545
+ */
546
+ capturePullEvent(debugFlag = true) {
547
+ this._debug = debugFlag;
548
+ }
549
+ /**
550
+ * @param loggingFlag
551
+ */
552
+ enableLogging(loggingFlag = true) {
553
+ this._sharedConfig.setLoggingEnabled(loggingFlag);
554
+ this._loggingEnabled = loggingFlag;
555
+ }
556
+ /**
557
+ * Returns list channels that the connection is subscribed to.
558
+ *
559
+ * @returns {Promise}
560
+ */
561
+ async listChannels() {
562
+ return this._jsonRpcAdapter?.executeOutgoingRpcCommand(
563
+ RpcMethod.ListChannels,
564
+ {}
565
+ ) || Promise.reject(new Error("jsonRpcAdapter not init"));
566
+ }
567
+ /**
568
+ * Returns "last seen" time in seconds for the users.
569
+ * Result format: Object{userId: int}
570
+ * If the user is currently connected - will return 0.
571
+ * If the user is offline - will return the diff between the current timestamp and the last seen timestamp in seconds.
572
+ * If the user was never online - the record for the user will be missing from the result object.
573
+ *
574
+ * @param {integer[]} userList List of user ids.
575
+ * @returns {Promise}
576
+ */
577
+ async getUsersLastSeen(userList) {
578
+ if (!Type.isArray(userList) || !userList.every((item) => typeof item === "number")) {
579
+ throw new Error("userList must be an array of numbers");
580
+ }
581
+ const result = {};
582
+ return new Promise((resolve, reject) => {
583
+ this._jsonRpcAdapter?.executeOutgoingRpcCommand(RpcMethod.GetUsersLastSeen, {
584
+ userList
585
+ }).then((response) => {
586
+ const unresolved = [];
587
+ for (let i = 0; i < userList.length; i++) {
588
+ if (!Object.prototype.hasOwnProperty.call(response, userList[i])) {
589
+ unresolved.push(userList[i]);
590
+ }
591
+ }
592
+ if (unresolved.length === 0) {
593
+ return resolve(result);
594
+ }
595
+ const params = {
596
+ userIds: unresolved,
597
+ sendToQueueSever: true
598
+ };
599
+ this._restClient.actions.v2.call.make({
600
+ method: "pull.api.user.getLastSeen",
601
+ params
602
+ }).then((response2) => {
603
+ const data = response2.getData().result;
604
+ for (const userId in data) {
605
+ result[Number(userId)] = Number(data[userId]);
606
+ }
607
+ return resolve(result);
608
+ }).catch((error) => {
609
+ this.getLogger().error("getUsersLastSeen", { error });
610
+ reject(error);
611
+ });
612
+ }).catch((error) => {
613
+ this.getLogger().error("getUsersLastSeen", { error });
614
+ reject(error);
615
+ });
616
+ });
617
+ }
618
+ /**
619
+ * Pings server.
620
+ * In case of success promise will be resolved, otherwise - rejected.
621
+ *
622
+ * @param {number} timeout Request timeout in seconds
623
+ * @returns {Promise}
624
+ */
625
+ async ping(timeout = 5) {
626
+ return this._jsonRpcAdapter?.executeOutgoingRpcCommand(
627
+ RpcMethod.Ping,
628
+ {},
629
+ timeout
630
+ );
631
+ }
632
+ /**
633
+ * @param userId {number}
634
+ * @param callback {UserStatusCallback}
635
+ * @returns {Promise}
636
+ */
637
+ async subscribeUserStatusChange(userId, callback) {
638
+ return new Promise((resolve, reject) => {
639
+ this._jsonRpcAdapter?.executeOutgoingRpcCommand(RpcMethod.SubscribeStatusChange, {
640
+ userId
641
+ }).then(() => {
642
+ if (!this._userStatusCallbacks[userId]) {
643
+ this._userStatusCallbacks[userId] = [];
644
+ }
645
+ if (Type.isFunction(callback)) {
646
+ this._userStatusCallbacks[userId].push(callback);
647
+ }
648
+ return resolve();
649
+ }).catch((error) => reject(error));
650
+ });
651
+ }
652
+ /**
653
+ * @param {number} userId
654
+ * @param {UserStatusCallback} callback
655
+ * @returns {Promise}
656
+ */
657
+ async unsubscribeUserStatusChange(userId, callback) {
658
+ if (this._userStatusCallbacks[userId]) {
659
+ this._userStatusCallbacks[userId] = this._userStatusCallbacks[userId].filter((cb) => cb !== callback);
660
+ if (this._userStatusCallbacks[userId].length === 0) {
661
+ return this._jsonRpcAdapter?.executeOutgoingRpcCommand(
662
+ RpcMethod.UnsubscribeStatusChange,
663
+ {
664
+ userId
665
+ }
666
+ );
667
+ }
668
+ }
669
+ return Promise.resolve();
670
+ }
671
+ // endregion ////
672
+ // region Get ////
673
+ getRevision() {
674
+ return this._config && this._config.api ? this._config.api.revision_web : null;
675
+ }
676
+ getServerVersion() {
677
+ return this._config && this._config.server ? this._config.server.version : 0;
678
+ }
679
+ getServerMode() {
680
+ return this._config && this._config.server ? this._config.server.mode : null;
681
+ }
682
+ getConfig() {
683
+ return this._config;
684
+ }
685
+ getDebugInfo() {
686
+ if (!JSON || !JSON.stringify) {
687
+ return {};
688
+ }
689
+ let configDump;
690
+ if (this._config && this._config.channels) {
691
+ configDump = {
692
+ ChannelID: this._config.channels.private?.id || "n/a",
693
+ ChannelDie: this._config.channels.private?.end || "n/a",
694
+ ChannelDieShared: this._config.channels.shared?.end || "n/a"
695
+ };
696
+ } else {
697
+ configDump = {
698
+ ConfigError: "config is not loaded"
699
+ };
700
+ }
701
+ let websocketMode = "-";
702
+ if (this._connectors.webSocket && this._connectors.webSocket?.socket) {
703
+ if (this.isJsonRpc()) {
704
+ websocketMode = "json-rpc";
705
+ } else {
706
+ websocketMode = this._connectors.webSocket?.socket?.url.search("binaryMode=true") != -1 ? "protobuf" : "text";
707
+ }
708
+ }
709
+ return {
710
+ "UserId": this._userId + (this._userId > 0 ? "" : "(guest)"),
711
+ "Guest userId": this._guestMode && this._guestUserId !== 0 ? this._guestUserId : "-",
712
+ "Browser online": navigator.onLine ? "Y" : "N",
713
+ "Connect": this.isConnected() ? "Y" : "N",
714
+ "Server type": this.isSharedMode() ? "cloud" : "local",
715
+ "WebSocket supported": this.isWebSocketSupported() ? "Y" : "N",
716
+ "WebSocket connected": this._connectors.webSocket && this._connectors.webSocket.connected ? "Y" : "N",
717
+ "WebSocket mode": websocketMode,
718
+ "Try connect": this._reconnectTimeout ? "Y" : "N",
719
+ "Try number": this._connectionAttempt,
720
+ "Path": this.connector?.connectionPath || "-",
721
+ ...configDump,
722
+ "Last message": this._session.mid || "-",
723
+ "Session history": this._session.history,
724
+ "Watch tags": this._watchTagsQueue.entries()
725
+ };
726
+ }
727
+ /**
728
+ * @process
729
+ * @param connectionType
730
+ */
731
+ getConnectionPath(connectionType) {
732
+ let path;
733
+ const params = {};
734
+ switch (connectionType) {
735
+ case ConnectionType.WebSocket:
736
+ path = this._isSecure ? this._config?.server.websocket_secure : this._config?.server.websocket;
737
+ break;
738
+ case ConnectionType.LongPolling:
739
+ path = this._isSecure ? this._config?.server.long_pooling_secure : this._config?.server.long_polling;
740
+ break;
741
+ default:
742
+ throw new Error(`Unknown connection type ${connectionType}`);
743
+ }
744
+ if (!Type.isStringFilled(path)) {
745
+ throw new Error(`Empty path`);
746
+ }
747
+ if (typeof this._config?.jwt === "string" && this._config?.jwt !== "") {
748
+ params["token"] = this._config?.jwt;
749
+ } else {
750
+ const channels = [];
751
+ if (this._config?.channels?.private) {
752
+ channels.push(this._config.channels.private?.id || "");
753
+ }
754
+ if (this._config?.channels.private?.id) {
755
+ channels.push(this._config.channels.private.id);
756
+ }
757
+ if (this._config?.channels.shared?.id) {
758
+ channels.push(this._config.channels.shared.id);
759
+ }
760
+ if (channels.length === 0) {
761
+ throw new Error(`Empty channels`);
762
+ }
763
+ params["CHANNEL_ID"] = channels.join("/");
764
+ }
765
+ if (this.isJsonRpc()) {
766
+ params.jsonRpc = "true";
767
+ } else if (this.isProtobufSupported()) {
768
+ params.binaryMode = "true";
769
+ }
770
+ if (this.isSharedMode()) {
771
+ if (!this._config?.clientId) {
772
+ throw new Error(
773
+ "Push-server is in shared mode, but clientId is not set"
774
+ );
775
+ }
776
+ params.clientId = this._config.clientId;
777
+ }
778
+ if (this._session.mid) {
779
+ params.mid = this._session.mid;
780
+ }
781
+ if (this._session.tag) {
782
+ params.tag = this._session.tag;
783
+ }
784
+ if (this._session.time) {
785
+ params.time = this._session.time;
786
+ }
787
+ params.revision = REVISION;
788
+ return `${path}?${Text.buildQueryString(params)}`;
789
+ }
790
+ /**
791
+ * @process
792
+ */
793
+ getPublicationPath() {
794
+ const path = this._isSecure ? this._config?.server.publish_secure : this._config?.server.publish;
795
+ if (!path) {
796
+ return "";
797
+ }
798
+ const channels = [];
799
+ if (this._config?.channels.private?.id) {
800
+ channels.push(this._config.channels.private.id);
801
+ }
802
+ if (this._config?.channels.shared?.id) {
803
+ channels.push(this._config.channels.shared.id);
804
+ }
805
+ const params = {
806
+ CHANNEL_ID: channels.join("/")
807
+ };
808
+ return path + "?" + Text.buildQueryString(params);
809
+ }
810
+ // endregion ////
811
+ // region Is* ////
812
+ isConnected() {
813
+ return this.connector ? this.connector.connected : false;
814
+ }
815
+ isWebSocketSupported() {
816
+ return typeof window.WebSocket !== "undefined";
817
+ }
818
+ isWebSocketAllowed() {
819
+ if (this._sharedConfig.isWebSocketBlocked()) {
820
+ return false;
821
+ }
822
+ return this.isWebSocketEnabled();
823
+ }
824
+ isWebSocketEnabled() {
825
+ if (!this.isWebSocketSupported()) {
826
+ return false;
827
+ }
828
+ if (!this._config) {
829
+ return false;
830
+ }
831
+ if (!this._config.server) {
832
+ return false;
833
+ }
834
+ return this._config.server.websocket_enabled;
835
+ }
836
+ isPublishingSupported() {
837
+ return this.getServerVersion() > 3;
838
+ }
839
+ isPublishingEnabled() {
840
+ if (!this.isPublishingSupported()) {
841
+ return false;
842
+ }
843
+ return this._config?.server.publish_enabled === true;
844
+ }
845
+ isProtobufSupported() {
846
+ return this.getServerVersion() == 4 && !Browser.isIE();
847
+ }
848
+ isJsonRpc() {
849
+ return this.getServerVersion() >= 5;
850
+ }
851
+ isSharedMode() {
852
+ return this.getServerMode() === ServerMode.Shared;
853
+ }
854
+ // endregion ////
855
+ // region Events ////
856
+ /**
857
+ * @param {TypePullClientEmitConfig} params
858
+ * @returns {boolean}
859
+ */
860
+ emit(params) {
861
+ if (params.type == SubscriptionType.Server || params.type == SubscriptionType.Client) {
862
+ if (typeof this._subscribers[params.type] === "undefined") {
863
+ this._subscribers[params.type] = {};
864
+ }
865
+ if (typeof params.moduleId === "undefined") {
866
+ throw new TypeError(
867
+ `${Text.getDateForLog()}: Pull.emit: parameter moduleId is not specified`
868
+ );
869
+ }
870
+ if (typeof this._subscribers[params.type][params.moduleId] === "undefined") {
871
+ this._subscribers[params.type][params.moduleId] = {
872
+ callbacks: [],
873
+ commands: {}
874
+ };
875
+ }
876
+ if (this._subscribers[params.type][params.moduleId]["callbacks"].length > 0) {
877
+ this._subscribers[params.type][params.moduleId]["callbacks"].forEach(
878
+ (callback) => {
879
+ callback(params.data, {
880
+ type: params.type,
881
+ moduleId: params.moduleId ?? "?"
882
+ });
883
+ }
884
+ );
885
+ }
886
+ if (!(typeof params.data === "undefined") && !(typeof params.data["command"] === "undefined") && this._subscribers[params.type][params.moduleId]["commands"][params.data["command"]] && this._subscribers[params.type][params.moduleId]["commands"][params.data["command"]].length > 0) {
887
+ this._subscribers[params.type][params.moduleId]["commands"][params.data["command"]].forEach((callback) => {
888
+ if (typeof params.data === "undefined") {
889
+ return;
890
+ }
891
+ callback(
892
+ params.data["params"],
893
+ params.data["extra"],
894
+ params.data["command"],
895
+ {
896
+ type: params.type,
897
+ moduleId: params.moduleId
898
+ }
899
+ );
900
+ });
901
+ }
902
+ return true;
903
+ } else {
904
+ if (typeof this._subscribers[params.type] === "undefined") {
905
+ this._subscribers[params.type] = [];
906
+ }
907
+ if (this._subscribers[params.type].length <= 0) {
908
+ return true;
909
+ }
910
+ this._subscribers[params.type].forEach(
911
+ (callback) => {
912
+ callback(params.data, {
913
+ type: params.type
914
+ });
915
+ }
916
+ );
917
+ return true;
918
+ }
919
+ }
920
+ /**
921
+ * @process
922
+ *
923
+ * @param message
924
+ */
925
+ broadcastMessage(message) {
926
+ const moduleId = message.module_id = message.module_id.toLowerCase();
927
+ const command = message.command;
928
+ if (!message.extra) {
929
+ message.extra = {};
930
+ }
931
+ if (message.extra.server_time_unix) {
932
+ message.extra.server_time_ago = (Date.now() - message.extra.server_time_unix * 1e3) / 1e3 - (this._config?.server.timeShift || 0);
933
+ message.extra.server_time_ago = Math.max(message.extra.server_time_ago, 0);
934
+ }
935
+ this.logMessage(message);
936
+ try {
937
+ if (message.extra.sender && message.extra.sender.type === SenderType.Client) {
938
+ this.onCustomEvent(
939
+ "onPullClientEvent-" + moduleId,
940
+ [command, message.params, message.extra],
941
+ true
942
+ );
943
+ this.onCustomEvent(
944
+ "onPullClientEvent",
945
+ [moduleId, command, message.params, message.extra],
946
+ true
947
+ );
948
+ this.emit({
949
+ type: SubscriptionType.Client,
950
+ moduleId,
951
+ data: {
952
+ command,
953
+ params: Type.clone(message.params),
954
+ extra: Type.clone(message.extra)
955
+ }
956
+ });
957
+ } else if (moduleId === "pull") {
958
+ this.handleInternalPullEvent(command, message);
959
+ } else if (moduleId == "online") {
960
+ if ((message?.extra?.server_time_ago || 0) < 240) {
961
+ this.onCustomEvent(
962
+ "onPullOnlineEvent",
963
+ [command, message.params, message.extra],
964
+ true
965
+ );
966
+ this.emit({
967
+ type: SubscriptionType.Online,
968
+ data: {
969
+ command,
970
+ params: Type.clone(message.params),
971
+ extra: Type.clone(message.extra)
972
+ }
973
+ });
974
+ }
975
+ if (command === "userStatusChange") {
976
+ this.emitUserStatusChange(
977
+ message.params.user_id,
978
+ message.params.online
979
+ );
980
+ }
981
+ } else {
982
+ this.onCustomEvent(
983
+ "onPullEvent-" + moduleId,
984
+ [command, message.params, message.extra],
985
+ true
986
+ );
987
+ this.onCustomEvent(
988
+ "onPullEvent",
989
+ [moduleId, command, message.params, message.extra],
990
+ true
991
+ );
992
+ this.emit({
993
+ type: SubscriptionType.Server,
994
+ moduleId,
995
+ data: {
996
+ command,
997
+ params: Type.clone(message.params),
998
+ extra: Type.clone(message.extra)
999
+ }
1000
+ });
1001
+ }
1002
+ } catch (error) {
1003
+ this.getLogger().warning("PULL ERROR", {
1004
+ errorType: "broadcastMessages execute error",
1005
+ errorEvent: error,
1006
+ message
1007
+ });
1008
+ }
1009
+ if (message.extra && message.extra.revision_web) {
1010
+ this.checkRevision(Text.toInteger(message.extra.revision_web));
1011
+ }
1012
+ }
1013
+ /**
1014
+ * @process
1015
+ *
1016
+ * @param messages
1017
+ */
1018
+ broadcastMessages(messages) {
1019
+ for (const message of messages) {
1020
+ this.broadcastMessage(message);
1021
+ }
1022
+ }
1023
+ // endregion ////
1024
+ // region sendMessage ////
1025
+ /**
1026
+ * Sends batch of messages to the multiple public channels.
1027
+ *
1028
+ * @param messageBatchList Array of messages to send.
1029
+ * @return void
1030
+ */
1031
+ async sendMessageBatch(messageBatchList) {
1032
+ if (!this.isPublishingEnabled()) {
1033
+ this.getLogger().error(`Client publishing is not supported or is disabled`);
1034
+ return Promise.reject(
1035
+ new Error(`Client publishing is not supported or is disabled`)
1036
+ );
1037
+ }
1038
+ if (this.isJsonRpc()) {
1039
+ const rpcRequest = this._jsonRpcAdapter?.createPublishRequest(messageBatchList);
1040
+ this.connector?.send(JSON.stringify(rpcRequest));
1041
+ return Promise.resolve(true);
1042
+ } else {
1043
+ const userIds = {};
1044
+ for (const messageBatch of messageBatchList) {
1045
+ if (typeof messageBatch.userList !== "undefined") {
1046
+ for (const user of messageBatch.userList) {
1047
+ const userId = Number(user);
1048
+ userIds[userId] = userId;
1049
+ }
1050
+ }
1051
+ }
1052
+ this._channelManager?.getPublicIds(Object.values(userIds)).then((publicIds) => {
1053
+ const response = this.connector?.send(
1054
+ this.encodeMessageBatch(messageBatchList, publicIds)
1055
+ );
1056
+ return Promise.resolve(response);
1057
+ });
1058
+ }
1059
+ }
1060
+ /**
1061
+ * @param messageBatchList
1062
+ * @param publicIds
1063
+ */
1064
+ encodeMessageBatch(messageBatchList, publicIds) {
1065
+ const messages = [];
1066
+ messageBatchList.forEach((messageFields) => {
1067
+ const messageBody = messageFields.body;
1068
+ let receivers = [];
1069
+ if (messageFields.userList) {
1070
+ receivers = this.createMessageReceivers(
1071
+ messageFields.userList,
1072
+ publicIds
1073
+ );
1074
+ }
1075
+ if (messageFields.channelList) {
1076
+ if (!Type.isArray(messageFields.channelList)) {
1077
+ throw new TypeError("messageFields.publicChannels must be an array");
1078
+ }
1079
+ messageFields.channelList.forEach((publicChannel) => {
1080
+ let publicId;
1081
+ let signature;
1082
+ if (typeof publicChannel === "string" && publicChannel.includes(".")) {
1083
+ const fields = publicChannel.toString().split(".");
1084
+ publicId = fields[0];
1085
+ signature = fields[1];
1086
+ } else if (typeof publicChannel === "object" && "publicId" in publicChannel && "signature" in publicChannel) {
1087
+ publicId = publicChannel?.publicId;
1088
+ signature = publicChannel?.signature;
1089
+ } else {
1090
+ throw new Error(
1091
+ `Public channel MUST be either a string, formatted like "{publicId}.{signature}" or an object with fields 'publicId' and 'signature'`
1092
+ );
1093
+ }
1094
+ receivers.push(
1095
+ Receiver.create({
1096
+ id: this.encodeId(publicId),
1097
+ signature: this.encodeId(signature)
1098
+ })
1099
+ );
1100
+ });
1101
+ }
1102
+ const message = IncomingMessage.create({
1103
+ receivers,
1104
+ body: JSON.stringify(messageBody),
1105
+ expiry: messageFields.expiry || 0
1106
+ });
1107
+ messages.push(message);
1108
+ });
1109
+ const requestBatch = RequestBatch.create({
1110
+ requests: [
1111
+ {
1112
+ incomingMessages: {
1113
+ messages
1114
+ }
1115
+ }
1116
+ ]
1117
+ });
1118
+ return RequestBatch.encode(requestBatch).finish();
1119
+ }
1120
+ /**
1121
+ * @memo fix return type
1122
+ * @param users
1123
+ * @param publicIds
1124
+ */
1125
+ createMessageReceivers(users, publicIds) {
1126
+ const result = [];
1127
+ for (const userId of users) {
1128
+ if (!publicIds[userId] || !publicIds[userId].publicId) {
1129
+ throw new Error(`Could not determine public id for user ${userId}`);
1130
+ }
1131
+ result.push(
1132
+ Receiver.create({
1133
+ id: this.encodeId(publicIds[userId].publicId),
1134
+ signature: this.encodeId(publicIds[userId].signature)
1135
+ })
1136
+ );
1137
+ }
1138
+ return result;
1139
+ }
1140
+ // endregion ////
1141
+ // region _userStatusCallbacks ////
1142
+ /**
1143
+ * @param userId
1144
+ * @param isOnline
1145
+ */
1146
+ emitUserStatusChange(userId, isOnline) {
1147
+ if (this._userStatusCallbacks[userId]) {
1148
+ for (const callback of this._userStatusCallbacks[userId]) {
1149
+ callback({
1150
+ userId,
1151
+ isOnline
1152
+ });
1153
+ }
1154
+ }
1155
+ }
1156
+ restoreUserStatusSubscription() {
1157
+ for (const userId in this._userStatusCallbacks) {
1158
+ if (Object.prototype.hasOwnProperty.call(this._userStatusCallbacks, userId) && this._userStatusCallbacks[userId].length > 0) {
1159
+ this._jsonRpcAdapter?.executeOutgoingRpcCommand(
1160
+ RpcMethod.SubscribeStatusChange,
1161
+ {
1162
+ userId
1163
+ }
1164
+ );
1165
+ }
1166
+ }
1167
+ }
1168
+ // endregion ////
1169
+ // region Config ////
1170
+ async loadConfig(_logTag) {
1171
+ if (!this._config) {
1172
+ this._config = Object.assign({}, EmptyConfig);
1173
+ let config;
1174
+ if (this._storage) {
1175
+ config = this._storage.get(LsKeys.PullConfig, null);
1176
+ }
1177
+ if (this.isConfigActual(config) && this.checkRevision(config.api.revision_web)) {
1178
+ return Promise.resolve(config);
1179
+ } else if (this._storage) {
1180
+ this._storage.remove(LsKeys.PullConfig);
1181
+ }
1182
+ } else if (this.isConfigActual(this._config) && this.checkRevision(this._config.api.revision_web)) {
1183
+ return Promise.resolve(this._config);
1184
+ } else {
1185
+ this._config = Object.assign({}, EmptyConfig);
1186
+ }
1187
+ return new Promise((resolve, reject) => {
1188
+ this._restClient.actions.v2.call.make({
1189
+ method: this._configGetMethod,
1190
+ params: { CACHE: "N" }
1191
+ }).then((response) => {
1192
+ const data = response.getData().result;
1193
+ const timeShift = Math.floor(
1194
+ (Date.now() - new Date(data.serverTime).getTime()) / 1e3
1195
+ );
1196
+ delete data.serverTime;
1197
+ const config = Object.assign({}, data);
1198
+ config.server.timeShift = timeShift;
1199
+ resolve(config);
1200
+ }).catch((error) => {
1201
+ reject(error);
1202
+ });
1203
+ });
1204
+ }
1205
+ /**
1206
+ * @param config
1207
+ */
1208
+ isConfigActual(config) {
1209
+ if (!Type.isPlainObject(config)) {
1210
+ return false;
1211
+ }
1212
+ if (Number(config["server"].config_timestamp) !== this._configTimestamp) {
1213
+ return false;
1214
+ }
1215
+ const now = /* @__PURE__ */ new Date();
1216
+ if (Type.isNumber(config["exp"]) && config["exp"] > 0 && config["exp"] < now.getTime() / 1e3) {
1217
+ return false;
1218
+ }
1219
+ const channelCount = Object.keys(config["channels"]).length;
1220
+ if (channelCount === 0) {
1221
+ return false;
1222
+ }
1223
+ for (const channelType in config["channels"]) {
1224
+ if (!Object.prototype.hasOwnProperty.call(config["channels"], channelType)) {
1225
+ continue;
1226
+ }
1227
+ const channel = config["channels"][channelType];
1228
+ const channelEnd = new Date(channel.end);
1229
+ if (channelEnd < now) {
1230
+ return false;
1231
+ }
1232
+ }
1233
+ return true;
1234
+ }
1235
+ startCheckConfig() {
1236
+ if (this._checkInterval) {
1237
+ clearInterval(this._checkInterval);
1238
+ this._checkInterval = null;
1239
+ }
1240
+ this._checkInterval = setInterval(
1241
+ this.checkConfig.bind(this),
1242
+ CONFIG_CHECK_INTERVAL
1243
+ );
1244
+ }
1245
+ stopCheckConfig() {
1246
+ if (this._checkInterval) {
1247
+ clearInterval(this._checkInterval);
1248
+ }
1249
+ this._checkInterval = null;
1250
+ }
1251
+ checkConfig() {
1252
+ if (this.isConfigActual(this._config)) {
1253
+ if (!this.checkRevision(Text.toInteger(this._config?.api.revision_web))) {
1254
+ return false;
1255
+ }
1256
+ } else {
1257
+ this.logToConsole("Stale config detected. Restarting");
1258
+ this.restart(CloseReasons.CONFIG_EXPIRED, "config expired");
1259
+ }
1260
+ return true;
1261
+ }
1262
+ /**
1263
+ * @param config
1264
+ * @param allowCaching
1265
+ */
1266
+ setConfig(config, allowCaching) {
1267
+ for (const key in config) {
1268
+ if (Object.prototype.hasOwnProperty.call(config, key) && Object.prototype.hasOwnProperty.call(this._config, key)) {
1269
+ this._config[key] = config[key];
1270
+ }
1271
+ }
1272
+ if (config.publicChannels) {
1273
+ this.setPublicIds(Object.values(config.publicChannels));
1274
+ }
1275
+ this._configTimestamp = Number(config.server.config_timestamp);
1276
+ if (this._storage && allowCaching) {
1277
+ try {
1278
+ this._storage.set(LsKeys.PullConfig, config);
1279
+ } catch (error) {
1280
+ if (localStorage && localStorage.removeItem) {
1281
+ localStorage.removeItem("history");
1282
+ }
1283
+ this.getLogger().error(
1284
+ `${Text.getDateForLog()}: Pull: Could not cache config in local storage.`,
1285
+ { error }
1286
+ );
1287
+ }
1288
+ }
1289
+ }
1290
+ setPublicIds(publicIds) {
1291
+ this._channelManager.setPublicIds(publicIds);
1292
+ }
1293
+ /**
1294
+ * @param serverRevision
1295
+ */
1296
+ checkRevision(serverRevision) {
1297
+ if (this._skipCheckRevision) {
1298
+ return true;
1299
+ }
1300
+ if (serverRevision > 0 && serverRevision !== REVISION) {
1301
+ this._enabled = false;
1302
+ this.showNotification("PULL_OLD_REVISION");
1303
+ this.disconnect(CloseReasons.NORMAL_CLOSURE, "check_revision");
1304
+ this.onCustomEvent("onPullRevisionUp", [serverRevision, REVISION]);
1305
+ this.emit({
1306
+ type: SubscriptionType.Revision,
1307
+ data: {
1308
+ server: serverRevision,
1309
+ client: REVISION
1310
+ }
1311
+ });
1312
+ this.logToConsole(
1313
+ `Pull revision changed from ${REVISION} to ${serverRevision}. Reload required`
1314
+ );
1315
+ return false;
1316
+ }
1317
+ return true;
1318
+ }
1319
+ // endregion ////
1320
+ // region Connect|ReConnect|DisConnect ////
1321
+ disconnect(disconnectCode, disconnectReason) {
1322
+ if (this.connector) {
1323
+ this._isManualDisconnect = true;
1324
+ this.connector.disconnect(disconnectCode, disconnectReason);
1325
+ }
1326
+ }
1327
+ restoreWebSocketConnection() {
1328
+ if (this._connectionType === ConnectionType.WebSocket) {
1329
+ return;
1330
+ }
1331
+ this._connectors.webSocket?.connect();
1332
+ }
1333
+ /**
1334
+ * @param connectionDelay
1335
+ */
1336
+ scheduleReconnect(connectionDelay = 0) {
1337
+ if (!this._enabled) {
1338
+ return;
1339
+ }
1340
+ if (!connectionDelay) {
1341
+ {
1342
+ connectionDelay = this.getConnectionAttemptDelay(
1343
+ this._connectionAttempt
1344
+ );
1345
+ }
1346
+ }
1347
+ if (this._reconnectTimeout) {
1348
+ clearTimeout(this._reconnectTimeout);
1349
+ this._reconnectTimeout = null;
1350
+ }
1351
+ this.logToConsole(
1352
+ `Pull: scheduling reconnection in ${connectionDelay} seconds; attempt # ${this._connectionAttempt}`
1353
+ );
1354
+ this._reconnectTimeout = setTimeout(() => {
1355
+ this.connect().catch((error) => {
1356
+ this.getLogger().error("scheduleReconnect", { error });
1357
+ });
1358
+ }, connectionDelay * 1e3);
1359
+ }
1360
+ scheduleRestoreWebSocketConnection() {
1361
+ this.logToConsole(
1362
+ `Pull: scheduling restoration of websocket connection in ${RESTORE_WEBSOCKET_TIMEOUT} seconds`
1363
+ );
1364
+ if (this._restoreWebSocketTimeout) {
1365
+ return;
1366
+ }
1367
+ this._restoreWebSocketTimeout = setTimeout(() => {
1368
+ this._restoreWebSocketTimeout = null;
1369
+ this.restoreWebSocketConnection();
1370
+ }, RESTORE_WEBSOCKET_TIMEOUT * 1e3);
1371
+ }
1372
+ /**
1373
+ * @returns {Promise}
1374
+ */
1375
+ async connect() {
1376
+ if (!this._enabled) {
1377
+ return Promise.reject();
1378
+ }
1379
+ if (this.connector?.connected) {
1380
+ return Promise.resolve();
1381
+ }
1382
+ if (this._reconnectTimeout) {
1383
+ clearTimeout(this._reconnectTimeout);
1384
+ this._reconnectTimeout = null;
1385
+ }
1386
+ this.status = PullStatus.Connecting;
1387
+ this._connectionAttempt++;
1388
+ return new Promise((resolve, reject) => {
1389
+ this._connectPromise = {
1390
+ resolve,
1391
+ reject
1392
+ };
1393
+ this.connector?.connect();
1394
+ });
1395
+ }
1396
+ /**
1397
+ * @param disconnectCode
1398
+ * @param disconnectReason
1399
+ * @param restartDelay
1400
+ */
1401
+ scheduleRestart(disconnectCode, disconnectReason, restartDelay = 0) {
1402
+ if (this._restartTimeout) {
1403
+ clearTimeout(this._restartTimeout);
1404
+ this._restartTimeout = null;
1405
+ }
1406
+ if (restartDelay < 1) {
1407
+ restartDelay = Math.ceil(Math.random() * 30) + 5;
1408
+ }
1409
+ this._restartTimeout = setTimeout(
1410
+ () => this.restart(disconnectCode, disconnectReason),
1411
+ restartDelay * 1e3
1412
+ );
1413
+ }
1414
+ // endregion ////
1415
+ // region Handlers ////
1416
+ /**
1417
+ * @param messageFields
1418
+ */
1419
+ handleRpcIncomingMessage(messageFields) {
1420
+ this._session.mid = messageFields.mid;
1421
+ const body = messageFields.body;
1422
+ if (!messageFields.body.extra) {
1423
+ body.extra = {};
1424
+ }
1425
+ body.extra.sender = messageFields.sender;
1426
+ if ("user_params" in messageFields && Type.isPlainObject(messageFields.user_params)) {
1427
+ Object.assign(body.params, messageFields.user_params);
1428
+ }
1429
+ if ("dictionary" in messageFields && Type.isPlainObject(messageFields.dictionary)) {
1430
+ Object.assign(body.params, messageFields.dictionary);
1431
+ }
1432
+ if (this.checkDuplicate(messageFields.mid)) {
1433
+ this.addMessageToStat(body);
1434
+ this.trimDuplicates();
1435
+ this.broadcastMessage(body);
1436
+ }
1437
+ this.connector?.send(`mack:${messageFields.mid}`);
1438
+ return {};
1439
+ }
1440
+ /**
1441
+ * @param events
1442
+ */
1443
+ handleIncomingEvents(events) {
1444
+ const messages = [];
1445
+ if (events.length === 0) {
1446
+ this._session.mid = null;
1447
+ return;
1448
+ }
1449
+ for (const event of events) {
1450
+ this.updateSessionFromEvent(event);
1451
+ if (event.mid && !this.checkDuplicate(event.mid)) {
1452
+ continue;
1453
+ }
1454
+ this.addMessageToStat(
1455
+ event.text
1456
+ );
1457
+ messages.push(event.text);
1458
+ }
1459
+ this.trimDuplicates();
1460
+ this.broadcastMessages(messages);
1461
+ }
1462
+ /**
1463
+ * @param event
1464
+ */
1465
+ updateSessionFromEvent(event) {
1466
+ this._session.mid = event.mid || null;
1467
+ this._session.tag = event.tag || null;
1468
+ this._session.time = event.time || null;
1469
+ }
1470
+ /**
1471
+ * @process
1472
+ *
1473
+ * @param command
1474
+ * @param message
1475
+ */
1476
+ handleInternalPullEvent(command, message) {
1477
+ switch (command.toUpperCase()) {
1478
+ case SystemCommands.CHANNEL_EXPIRE: {
1479
+ if (message.params.action === "reconnect") {
1480
+ const typeChanel = message.params?.channel.type;
1481
+ if (typeChanel === "private" && this._config?.channels?.private) {
1482
+ this._config.channels.private = message.params.new_channel;
1483
+ this.logToConsole(
1484
+ `Pull: new config for ${message.params.channel.type} channel set: ${this._config.channels.private}`
1485
+ );
1486
+ }
1487
+ if (typeChanel === "shared" && this._config?.channels?.shared) {
1488
+ this._config.channels.shared = message.params.new_channel;
1489
+ this.logToConsole(
1490
+ `Pull: new config for ${message.params.channel.type} channel set: ${this._config.channels.shared}`
1491
+ );
1492
+ }
1493
+ this.reconnect(CloseReasons.CONFIG_REPLACED, "config was replaced");
1494
+ } else {
1495
+ this.restart(CloseReasons.CHANNEL_EXPIRED, "channel expired received");
1496
+ }
1497
+ break;
1498
+ }
1499
+ case SystemCommands.CONFIG_EXPIRE: {
1500
+ this.restart(CloseReasons.CONFIG_EXPIRED, "config expired received");
1501
+ break;
1502
+ }
1503
+ case SystemCommands.SERVER_RESTART: {
1504
+ this.reconnect(
1505
+ CloseReasons.SERVER_RESTARTED,
1506
+ "server was restarted",
1507
+ 15
1508
+ );
1509
+ break;
1510
+ }
1511
+ }
1512
+ }
1513
+ // region Handlers For Message ////
1514
+ /**
1515
+ * @param response
1516
+ */
1517
+ onIncomingMessage(response) {
1518
+ if (this.isJsonRpc()) {
1519
+ if (response === JSON_RPC_PING) {
1520
+ this.onJsonRpcPing();
1521
+ } else {
1522
+ this._jsonRpcAdapter?.parseJsonRpcMessage(response);
1523
+ }
1524
+ } else {
1525
+ const events = this.extractMessages(response);
1526
+ this.handleIncomingEvents(events);
1527
+ }
1528
+ }
1529
+ // region onLongPolling ////
1530
+ onLongPollingOpen() {
1531
+ this._unloading = false;
1532
+ this._starting = false;
1533
+ this._connectionAttempt = 0;
1534
+ this._isManualDisconnect = false;
1535
+ this.status = PullStatus.Online;
1536
+ this.logToConsole("Pull: Long polling connection with push-server opened");
1537
+ if (this.isWebSocketEnabled()) {
1538
+ this.scheduleRestoreWebSocketConnection();
1539
+ }
1540
+ if (this._connectPromise) {
1541
+ this._connectPromise.resolve({});
1542
+ }
1543
+ }
1544
+ /**
1545
+ * @param response
1546
+ */
1547
+ onLongPollingDisconnect(response) {
1548
+ if (this._connectionType === ConnectionType.LongPolling) {
1549
+ this.status = PullStatus.Offline;
1550
+ }
1551
+ this.logToConsole(
1552
+ `Pull: Long polling connection with push-server closed. Code: ${response.code}, reason: ${response.reason}`
1553
+ );
1554
+ if (!this._isManualDisconnect) {
1555
+ this.scheduleReconnect();
1556
+ }
1557
+ this._isManualDisconnect = false;
1558
+ this.clearPingWaitTimeout();
1559
+ }
1560
+ /**
1561
+ * @param error
1562
+ */
1563
+ onLongPollingError(error) {
1564
+ this._starting = false;
1565
+ if (this._connectionType === ConnectionType.LongPolling) {
1566
+ this.status = PullStatus.Offline;
1567
+ }
1568
+ this.getLogger().error(
1569
+ `${Text.getDateForLog()}: Pull: Long polling connection error`,
1570
+ { error }
1571
+ );
1572
+ this.scheduleReconnect();
1573
+ if (this._connectPromise) {
1574
+ this._connectPromise.reject(error);
1575
+ }
1576
+ this.clearPingWaitTimeout();
1577
+ }
1578
+ // endregion ////
1579
+ // region onWebSocket ////
1580
+ /**
1581
+ * @param response
1582
+ */
1583
+ onWebSocketBlockChanged(response) {
1584
+ const isWebSocketBlocked = response.isWebSocketBlocked;
1585
+ if (isWebSocketBlocked && this._connectionType === ConnectionType.WebSocket && !this.isConnected()) {
1586
+ if (this._reconnectTimeout) {
1587
+ clearTimeout(this._reconnectTimeout);
1588
+ this._reconnectTimeout = null;
1589
+ }
1590
+ this._connectionAttempt = 0;
1591
+ this._connectionType = ConnectionType.LongPolling;
1592
+ this.scheduleReconnect(1);
1593
+ } else if (!isWebSocketBlocked && this._connectionType === ConnectionType.LongPolling) {
1594
+ if (this._reconnectTimeout) {
1595
+ clearTimeout(this._reconnectTimeout);
1596
+ this._reconnectTimeout = null;
1597
+ }
1598
+ if (this._restoreWebSocketTimeout) {
1599
+ clearTimeout(this._restoreWebSocketTimeout);
1600
+ this._restoreWebSocketTimeout = null;
1601
+ }
1602
+ this._connectionAttempt = 0;
1603
+ this._connectionType = ConnectionType.WebSocket;
1604
+ this.scheduleReconnect(1);
1605
+ }
1606
+ }
1607
+ onWebSocketOpen() {
1608
+ this._unloading = false;
1609
+ this._starting = false;
1610
+ this._connectionAttempt = 0;
1611
+ this._isManualDisconnect = false;
1612
+ this.status = PullStatus.Online;
1613
+ this._sharedConfig.setWebSocketBlocked(false);
1614
+ this._sharedConfig.setLongPollingBlocked(true);
1615
+ if (this._connectionType == ConnectionType.LongPolling) {
1616
+ this._connectionType = ConnectionType.WebSocket;
1617
+ this._connectors.longPolling?.disconnect(
1618
+ CloseReasons.CONFIG_REPLACED,
1619
+ "Fire at onWebSocketOpen"
1620
+ );
1621
+ }
1622
+ if (this._restoreWebSocketTimeout) {
1623
+ clearTimeout(this._restoreWebSocketTimeout);
1624
+ this._restoreWebSocketTimeout = null;
1625
+ }
1626
+ this.logToConsole("Pull: Websocket connection with push-server opened");
1627
+ if (this._connectPromise) {
1628
+ this._connectPromise.resolve({});
1629
+ }
1630
+ this.restoreUserStatusSubscription();
1631
+ }
1632
+ /**
1633
+ * @param response
1634
+ */
1635
+ onWebSocketDisconnect(response) {
1636
+ if (this._connectionType === ConnectionType.WebSocket) {
1637
+ this.status = PullStatus.Offline;
1638
+ }
1639
+ this.logToConsole(
1640
+ `Pull: Websocket connection with push-server closed. Code: ${response.code}, reason: ${response.reason}`,
1641
+ true
1642
+ );
1643
+ if (!this._isManualDisconnect) {
1644
+ if (response.code == CloseReasons.WRONG_CHANNEL_ID) {
1645
+ this.scheduleRestart(
1646
+ CloseReasons.WRONG_CHANNEL_ID,
1647
+ "wrong channel signature"
1648
+ );
1649
+ } else {
1650
+ this.scheduleReconnect();
1651
+ }
1652
+ }
1653
+ this._sharedConfig.setLongPollingBlocked(true);
1654
+ this._isManualDisconnect = false;
1655
+ this.clearPingWaitTimeout();
1656
+ }
1657
+ /**
1658
+ * @param error
1659
+ */
1660
+ onWebSocketError(error) {
1661
+ this._starting = false;
1662
+ if (this._connectionType === ConnectionType.WebSocket) {
1663
+ this.status = PullStatus.Offline;
1664
+ }
1665
+ this.getLogger().error(
1666
+ `${Text.getDateForLog()}: Pull: WebSocket connection error`,
1667
+ { error }
1668
+ );
1669
+ this.scheduleReconnect();
1670
+ if (this._connectPromise) {
1671
+ this._connectPromise.reject(error);
1672
+ }
1673
+ this.clearPingWaitTimeout();
1674
+ }
1675
+ // endregion ////
1676
+ // endregion ////
1677
+ // endregion ////
1678
+ // region extractMessages ////
1679
+ /**
1680
+ * @param pullEvent
1681
+ */
1682
+ extractMessages(pullEvent) {
1683
+ if (pullEvent instanceof ArrayBuffer) {
1684
+ return this.extractProtobufMessages(pullEvent);
1685
+ } else if (Type.isStringFilled(pullEvent)) {
1686
+ return this.extractPlainTextMessages(pullEvent);
1687
+ }
1688
+ throw new Error("Error pullEvent type");
1689
+ }
1690
+ /**
1691
+ * @param pullEvent
1692
+ */
1693
+ extractProtobufMessages(pullEvent) {
1694
+ const result = [];
1695
+ try {
1696
+ const responseBatch = ResponseBatch.decode(new Uint8Array(pullEvent));
1697
+ for (let i = 0; i < responseBatch.responses.length; i++) {
1698
+ const response = responseBatch.responses[i];
1699
+ if (response.command !== "outgoingMessages") {
1700
+ continue;
1701
+ }
1702
+ const messages = response.outgoingMessages.messages;
1703
+ for (const message of messages) {
1704
+ let messageFields;
1705
+ try {
1706
+ messageFields = JSON.parse(message.body);
1707
+ } catch (error) {
1708
+ this.getLogger().error(
1709
+ `${Text.getDateForLog()}: Pull: Could not parse message body`,
1710
+ { error }
1711
+ );
1712
+ continue;
1713
+ }
1714
+ if (!messageFields.extra) {
1715
+ messageFields.extra = {};
1716
+ }
1717
+ messageFields.extra.sender = {
1718
+ type: message.sender.type
1719
+ };
1720
+ if (message.sender.id instanceof Uint8Array) {
1721
+ messageFields.extra.sender.id = this.decodeId(message.sender.id);
1722
+ }
1723
+ const compatibleMessage = {
1724
+ mid: this.decodeId(message.id),
1725
+ text: messageFields
1726
+ };
1727
+ result.push(compatibleMessage);
1728
+ }
1729
+ }
1730
+ } catch (error) {
1731
+ this.getLogger().error(
1732
+ `${Text.getDateForLog()}: Pull: Could not parse message`,
1733
+ { error }
1734
+ );
1735
+ }
1736
+ return result;
1737
+ }
1738
+ /**
1739
+ * @param pullEvent
1740
+ */
1741
+ extractPlainTextMessages(pullEvent) {
1742
+ const result = [];
1743
+ const dataArray = pullEvent.match(/#!NGINXNMS!#(.*?)#!NGINXNME!#/g);
1744
+ if (dataArray === null) {
1745
+ this.getLogger().warning("PULL ERROR", {
1746
+ errorType: "parseResponse error parsing message",
1747
+ dataString: pullEvent
1748
+ });
1749
+ return [];
1750
+ }
1751
+ for (let i = 0; i < dataArray.length; i++) {
1752
+ dataArray[i] = dataArray[i].substring(12, dataArray[i].length - 12);
1753
+ if (dataArray[i].length <= 0) {
1754
+ continue;
1755
+ }
1756
+ let data;
1757
+ try {
1758
+ data = JSON.parse(dataArray[i]);
1759
+ } catch {
1760
+ continue;
1761
+ }
1762
+ result.push(data);
1763
+ }
1764
+ return result;
1765
+ }
1766
+ /**
1767
+ * Converts message id from byte[] to string
1768
+ * @param {Uint8Array} encodedId
1769
+ * @return {string}
1770
+ */
1771
+ decodeId(encodedId) {
1772
+ let result = "";
1773
+ for (const element_ of encodedId) {
1774
+ const hexByte = element_.toString(16);
1775
+ if (hexByte.length === 1) {
1776
+ result += "0";
1777
+ }
1778
+ result += hexByte;
1779
+ }
1780
+ return result;
1781
+ }
1782
+ /**
1783
+ * Converts message id from hex-encoded string to byte[]
1784
+ * @param {string} id Hex-encoded string.
1785
+ * @return {Uint8Array}
1786
+ */
1787
+ encodeId(id) {
1788
+ if (!id) {
1789
+ return new Uint8Array();
1790
+ }
1791
+ const result = [];
1792
+ for (let i = 0; i < id.length; i += 2) {
1793
+ result.push(Number.parseInt(id.slice(i, i + 2), 16));
1794
+ }
1795
+ return new Uint8Array(result);
1796
+ }
1797
+ // endregion ////
1798
+ // region Events.Status /////
1799
+ onOffline() {
1800
+ this.disconnect(CloseReasons.NORMAL_CLOSURE, "offline");
1801
+ }
1802
+ onOnline() {
1803
+ this.connect().catch((error) => {
1804
+ this.getLogger().error("onOnline", { error });
1805
+ });
1806
+ }
1807
+ onBeforeUnload() {
1808
+ this._unloading = true;
1809
+ const session = Type.clone(this.session);
1810
+ session.ttl = Date.now() + LS_SESSION_CACHE_TIME * 1e3;
1811
+ if (this._storage) {
1812
+ try {
1813
+ this._storage.set(
1814
+ LS_SESSION,
1815
+ JSON.stringify(session)
1816
+ // LS_SESSION_CACHE_TIME
1817
+ );
1818
+ } catch (error) {
1819
+ this.getLogger().error(
1820
+ `${Text.getDateForLog()}: Pull: Could not save session info in local storage. Error: `,
1821
+ { error }
1822
+ );
1823
+ }
1824
+ }
1825
+ this.scheduleReconnect(15);
1826
+ }
1827
+ // endregion ////
1828
+ // region PullStatus ////
1829
+ /**
1830
+ * @param status
1831
+ * @param delay
1832
+ */
1833
+ sendPullStatusDelayed(status, delay) {
1834
+ if (this._offlineTimeout) {
1835
+ clearTimeout(this._offlineTimeout);
1836
+ this._offlineTimeout = null;
1837
+ }
1838
+ this._offlineTimeout = setTimeout(() => {
1839
+ this._offlineTimeout = null;
1840
+ this.sendPullStatus(status);
1841
+ }, delay);
1842
+ }
1843
+ /**
1844
+ * @param status
1845
+ */
1846
+ sendPullStatus(status) {
1847
+ if (this._unloading) {
1848
+ return;
1849
+ }
1850
+ this.onCustomEvent("onPullStatus", [status]);
1851
+ this.emit({
1852
+ type: SubscriptionType.Status,
1853
+ data: {
1854
+ status
1855
+ }
1856
+ });
1857
+ }
1858
+ // endregion ////
1859
+ // region _watchTagsQueue ////
1860
+ /**
1861
+ * @memo if private?
1862
+ * @param tagId
1863
+ * @param force
1864
+ */
1865
+ // @ts-expect-error When we rewrite it to something more modern, then we'll remove this
1866
+ extendWatch(tagId, force = false) {
1867
+ if (this._watchTagsQueue.get(tagId)) {
1868
+ return;
1869
+ }
1870
+ this._watchTagsQueue.set(tagId, true);
1871
+ if (force) {
1872
+ this.updateWatch(force);
1873
+ }
1874
+ }
1875
+ /**
1876
+ * @param force
1877
+ */
1878
+ updateWatch(force = false) {
1879
+ if (this._watchUpdateTimeout) {
1880
+ clearTimeout(this._watchUpdateTimeout);
1881
+ this._watchUpdateTimeout = null;
1882
+ }
1883
+ this._watchUpdateTimeout = setTimeout(
1884
+ () => {
1885
+ const watchTags = [...this._watchTagsQueue.keys()];
1886
+ if (watchTags.length > 0) {
1887
+ this._restClient.actions.v2.call.make({
1888
+ method: "pull.watch.extend",
1889
+ params: { tags: watchTags }
1890
+ }).then((response) => {
1891
+ const updatedTags = response.getData().result;
1892
+ for (const tagId of updatedTags) {
1893
+ this.clearWatch(tagId);
1894
+ }
1895
+ this.updateWatch();
1896
+ }).catch(() => {
1897
+ this.updateWatch();
1898
+ });
1899
+ } else {
1900
+ this.updateWatch();
1901
+ }
1902
+ },
1903
+ force ? this._watchForceUpdateInterval : this._watchUpdateInterval
1904
+ );
1905
+ }
1906
+ /**
1907
+ * @param tagId
1908
+ */
1909
+ clearWatch(tagId) {
1910
+ this._watchTagsQueue.delete(tagId);
1911
+ }
1912
+ // endregion ////
1913
+ // region Ping ////
1914
+ onJsonRpcPing() {
1915
+ this.updatePingWaitTimeout();
1916
+ this.connector?.send(JSON_RPC_PONG);
1917
+ }
1918
+ updatePingWaitTimeout() {
1919
+ if (this._pingWaitTimeout) {
1920
+ clearTimeout(this._pingWaitTimeout);
1921
+ this._pingWaitTimeout = null;
1922
+ }
1923
+ this._pingWaitTimeout = setTimeout(
1924
+ this._onPingTimeoutHandler,
1925
+ PING_TIMEOUT * 2 * 1e3
1926
+ );
1927
+ }
1928
+ clearPingWaitTimeout() {
1929
+ if (this._pingWaitTimeout) {
1930
+ clearTimeout(this._pingWaitTimeout);
1931
+ }
1932
+ this._pingWaitTimeout = null;
1933
+ }
1934
+ onPingTimeout() {
1935
+ this._pingWaitTimeout = null;
1936
+ if (!this._enabled || !this.isConnected()) {
1937
+ return;
1938
+ }
1939
+ this.getLogger().warning(`No pings are received in ${PING_TIMEOUT * 2} seconds. Reconnecting`);
1940
+ this.disconnect(CloseReasons.STUCK, "connection stuck");
1941
+ this.scheduleReconnect();
1942
+ }
1943
+ // endregion ////
1944
+ // region Time ////
1945
+ /**
1946
+ * Returns reconnect delay in seconds
1947
+ *
1948
+ * @param attemptNumber
1949
+ * @return {number}
1950
+ */
1951
+ getConnectionAttemptDelay(attemptNumber) {
1952
+ let result;
1953
+ if (attemptNumber < 1) {
1954
+ result = 0.5;
1955
+ } else if (attemptNumber < 3) {
1956
+ result = 15;
1957
+ } else if (attemptNumber < 5) {
1958
+ result = 45;
1959
+ } else if (attemptNumber < 10) {
1960
+ result = 600;
1961
+ } else {
1962
+ result = 3600;
1963
+ }
1964
+ return result + result * Math.random() * 0.2;
1965
+ }
1966
+ // endregion ////
1967
+ // region Tools ////
1968
+ /**
1969
+ * @param mid
1970
+ */
1971
+ checkDuplicate(mid) {
1972
+ if (this._session.lastMessageIds.includes(mid)) {
1973
+ this.getLogger().warning(`Duplicate message ${mid} skipped`);
1974
+ return false;
1975
+ } else {
1976
+ this._session.lastMessageIds.push(mid);
1977
+ return true;
1978
+ }
1979
+ }
1980
+ trimDuplicates() {
1981
+ if (this._session.lastMessageIds.length > MAX_IDS_TO_STORE) {
1982
+ this._session.lastMessageIds = this._session.lastMessageIds.slice(-MAX_IDS_TO_STORE);
1983
+ }
1984
+ }
1985
+ // endregion ////
1986
+ // region Logging ////
1987
+ /**
1988
+ * @param message
1989
+ */
1990
+ logMessage(message) {
1991
+ if (!this._debug) {
1992
+ return;
1993
+ }
1994
+ if (message.extra?.sender && message.extra.sender.type === SenderType.Client) {
1995
+ this.getLogger().info(
1996
+ `onPullClientEvent-${message.module_id}`,
1997
+ {
1998
+ command: message.command,
1999
+ params: message.params,
2000
+ extra: message.extra
2001
+ }
2002
+ );
2003
+ } else if (message.module_id == "online") {
2004
+ this.getLogger().info(
2005
+ `onPullOnlineEvent`,
2006
+ {
2007
+ command: message.command,
2008
+ params: message.params,
2009
+ extra: message.extra
2010
+ }
2011
+ );
2012
+ } else {
2013
+ this.getLogger().info(
2014
+ `onPullEvent`,
2015
+ {
2016
+ moduleId: message.module_id,
2017
+ command: message.command,
2018
+ params: message.params,
2019
+ extra: message.extra
2020
+ }
2021
+ );
2022
+ }
2023
+ }
2024
+ /**
2025
+ * @param message
2026
+ * @param force
2027
+ */
2028
+ logToConsole(message, force = false) {
2029
+ if (this._loggingEnabled || force) {
2030
+ this.getLogger().debug(`${Text.getDateForLog()}: ${message}`);
2031
+ }
2032
+ }
2033
+ /**
2034
+ * @param message
2035
+ */
2036
+ addMessageToStat(message) {
2037
+ if (!this._session.history[message.module_id]) {
2038
+ this._session.history[message.module_id] = {};
2039
+ }
2040
+ if (!this._session.history[message.module_id][message.command]) {
2041
+ this.session.history[message.module_id][message.command] = 0;
2042
+ }
2043
+ this._session.history[message.module_id][message.command]++;
2044
+ this._session.messageCount++;
2045
+ }
2046
+ /**
2047
+ * @param text
2048
+ */
2049
+ showNotification(text) {
2050
+ this.getLogger().notice(text);
2051
+ }
2052
+ // endregion ////
2053
+ // region onCustomEvent ////
2054
+ /**
2055
+ * @memo may be need to use onCustomEvent
2056
+ * @memo ? force
2057
+ */
2058
+ onCustomEvent(eventName, data, force = false) {
2059
+ }
2060
+ // endregion ////
2061
+ }
2062
+
2063
+ export { PullClient };
2064
+ //# sourceMappingURL=client.mjs.map