@wildix/xbees-conversations-utils 4.0.1 → 4.0.2

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 (124) hide show
  1. package/dist-cjs/anonymousEmail/index.js +3 -3
  2. package/dist-cjs/rateLimits/RedisCooldown.js +66 -0
  3. package/dist-cjs/rateLimits/constants.js +43 -0
  4. package/dist-cjs/rateLimits/createRateLimitedStreamProxy.js +92 -0
  5. package/dist-cjs/rateLimits/exception/createRateLimitExceededException.js +21 -0
  6. package/dist-cjs/rateLimits/exception/processStreamRateLimitException.js +47 -0
  7. package/dist-cjs/rateLimits/executeStreamRequestWithRateLimitHandling.js +94 -0
  8. package/dist-cjs/rateLimits/generatedMethodNames.js +320 -0
  9. package/dist-cjs/rateLimits/helpers/RedisCooldown.js +174 -0
  10. package/dist-cjs/rateLimits/helpers/createRateLimitExceededException.js +32 -0
  11. package/dist-cjs/rateLimits/helpers/executeWithRateLimitHandling.js +144 -0
  12. package/dist-cjs/rateLimits/helpers/headerUtils.js +40 -0
  13. package/dist-cjs/rateLimits/helpers/isStreamChannelLike.js +23 -0
  14. package/dist-cjs/rateLimits/helpers/processStreamBudgetHeaders.js +93 -0
  15. package/dist-cjs/rateLimits/helpers/processStreamRateLimitException.js +63 -0
  16. package/dist-cjs/rateLimits/helpers/processStreamRateLimitHeaders.js +50 -0
  17. package/dist-cjs/rateLimits/helpers/rateLimitSnapshot.js +31 -0
  18. package/dist-cjs/rateLimits/helpers/registerStreamBudgetInterceptors.js +48 -0
  19. package/dist-cjs/rateLimits/helpers/registerStreamInterceptors.js +58 -0
  20. package/dist-cjs/rateLimits/helpers/splitCallArgsAndOptions.js +21 -0
  21. package/dist-cjs/rateLimits/index.js +7 -0
  22. package/dist-cjs/rateLimits/types.js +3 -0
  23. package/dist-cjs/rateLimits/withStreamRateLimitOptions.js +10 -0
  24. package/dist-cjs/streamRateLimitHandlingProxy/constants.js +42 -0
  25. package/dist-cjs/streamRateLimitHandlingProxy/createRateLimitedStreamProxy.js +92 -0
  26. package/dist-cjs/streamRateLimitHandlingProxy/generatedMethodNames.js +320 -0
  27. package/dist-cjs/streamRateLimitHandlingProxy/helpers/RedisCooldown.js +174 -0
  28. package/dist-cjs/streamRateLimitHandlingProxy/helpers/createRateLimitExceededException.js +29 -0
  29. package/dist-cjs/streamRateLimitHandlingProxy/helpers/executeWithRateLimitHandling.js +153 -0
  30. package/dist-cjs/streamRateLimitHandlingProxy/helpers/headerUtils.js +48 -0
  31. package/dist-cjs/streamRateLimitHandlingProxy/helpers/isStreamChannelLike.js +23 -0
  32. package/dist-cjs/streamRateLimitHandlingProxy/helpers/processStreamBudgetHeaders.js +93 -0
  33. package/dist-cjs/streamRateLimitHandlingProxy/helpers/processStreamRateLimitException.js +63 -0
  34. package/dist-cjs/streamRateLimitHandlingProxy/helpers/processStreamRateLimitHeaders.js +46 -0
  35. package/dist-cjs/streamRateLimitHandlingProxy/helpers/rateLimitSnapshot.js +31 -0
  36. package/dist-cjs/streamRateLimitHandlingProxy/helpers/registerStreamInterceptors.js +64 -0
  37. package/dist-cjs/streamRateLimitHandlingProxy/helpers/sleep.js +6 -0
  38. package/dist-cjs/streamRateLimitHandlingProxy/helpers/splitCallArgsAndOptions.js +21 -0
  39. package/dist-cjs/streamRateLimitHandlingProxy/index.js +5 -0
  40. package/dist-cjs/streamRateLimitHandlingProxy/types.js +3 -0
  41. package/dist-cjs/streamRateLimitHandlingProxy/withStreamRateLimitOptions.js +10 -0
  42. package/dist-es/anonymousEmail/index.js +1 -1
  43. package/dist-es/rateLimits/RedisCooldown.js +62 -0
  44. package/dist-es/rateLimits/constants.js +33 -0
  45. package/dist-es/rateLimits/createRateLimitedStreamProxy.js +88 -0
  46. package/dist-es/rateLimits/exception/createRateLimitExceededException.js +17 -0
  47. package/dist-es/rateLimits/exception/processStreamRateLimitException.js +37 -0
  48. package/dist-es/rateLimits/executeStreamRequestWithRateLimitHandling.js +93 -0
  49. package/dist-es/rateLimits/generatedMethodNames.js +317 -0
  50. package/dist-es/rateLimits/helpers/RedisCooldown.js +170 -0
  51. package/dist-es/rateLimits/helpers/createRateLimitExceededException.js +28 -0
  52. package/dist-es/rateLimits/helpers/executeWithRateLimitHandling.js +140 -0
  53. package/dist-es/rateLimits/helpers/headerUtils.js +34 -0
  54. package/dist-es/rateLimits/helpers/isStreamChannelLike.js +19 -0
  55. package/dist-es/rateLimits/helpers/processStreamBudgetHeaders.js +89 -0
  56. package/dist-es/rateLimits/helpers/processStreamRateLimitException.js +59 -0
  57. package/dist-es/rateLimits/helpers/processStreamRateLimitHeaders.js +46 -0
  58. package/dist-es/rateLimits/helpers/rateLimitSnapshot.js +26 -0
  59. package/dist-es/rateLimits/helpers/registerStreamBudgetInterceptors.js +44 -0
  60. package/dist-es/rateLimits/helpers/registerStreamInterceptors.js +54 -0
  61. package/dist-es/rateLimits/helpers/splitCallArgsAndOptions.js +17 -0
  62. package/dist-es/rateLimits/index.js +4 -0
  63. package/dist-es/rateLimits/types.js +1 -0
  64. package/dist-es/rateLimits/withStreamRateLimitOptions.js +6 -0
  65. package/dist-es/streamRateLimitHandlingProxy/constants.js +32 -0
  66. package/dist-es/streamRateLimitHandlingProxy/createRateLimitedStreamProxy.js +88 -0
  67. package/dist-es/streamRateLimitHandlingProxy/generatedMethodNames.js +317 -0
  68. package/dist-es/streamRateLimitHandlingProxy/helpers/RedisCooldown.js +170 -0
  69. package/dist-es/streamRateLimitHandlingProxy/helpers/createRateLimitExceededException.js +25 -0
  70. package/dist-es/streamRateLimitHandlingProxy/helpers/executeWithRateLimitHandling.js +148 -0
  71. package/dist-es/streamRateLimitHandlingProxy/helpers/headerUtils.js +42 -0
  72. package/dist-es/streamRateLimitHandlingProxy/helpers/isStreamChannelLike.js +19 -0
  73. package/dist-es/streamRateLimitHandlingProxy/helpers/processStreamBudgetHeaders.js +89 -0
  74. package/dist-es/streamRateLimitHandlingProxy/helpers/processStreamRateLimitException.js +59 -0
  75. package/dist-es/streamRateLimitHandlingProxy/helpers/processStreamRateLimitHeaders.js +42 -0
  76. package/dist-es/streamRateLimitHandlingProxy/helpers/rateLimitSnapshot.js +26 -0
  77. package/dist-es/streamRateLimitHandlingProxy/helpers/registerStreamInterceptors.js +60 -0
  78. package/dist-es/streamRateLimitHandlingProxy/helpers/sleep.js +4 -0
  79. package/dist-es/streamRateLimitHandlingProxy/helpers/splitCallArgsAndOptions.js +17 -0
  80. package/dist-es/streamRateLimitHandlingProxy/index.js +2 -0
  81. package/dist-es/streamRateLimitHandlingProxy/types.js +1 -0
  82. package/dist-es/streamRateLimitHandlingProxy/withStreamRateLimitOptions.js +6 -0
  83. package/dist-types/anonymousEmail/index.d.ts +1 -0
  84. package/dist-types/rateLimits/RedisCooldown.d.ts +7 -0
  85. package/dist-types/rateLimits/constants.d.ts +29 -0
  86. package/dist-types/rateLimits/createRateLimitedStreamProxy.d.ts +11 -0
  87. package/dist-types/rateLimits/exception/createRateLimitExceededException.d.ts +12 -0
  88. package/dist-types/rateLimits/exception/processStreamRateLimitException.d.ts +8 -0
  89. package/dist-types/rateLimits/executeStreamRequestWithRateLimitHandling.d.ts +8 -0
  90. package/dist-types/rateLimits/generatedMethodNames.d.ts +11 -0
  91. package/dist-types/rateLimits/helpers/RedisCooldown.d.ts +11 -0
  92. package/dist-types/rateLimits/helpers/createRateLimitExceededException.d.ts +10 -0
  93. package/dist-types/rateLimits/helpers/executeWithRateLimitHandling.d.ts +2 -0
  94. package/dist-types/rateLimits/helpers/headerUtils.d.ts +4 -0
  95. package/dist-types/rateLimits/helpers/isStreamChannelLike.d.ts +6 -0
  96. package/dist-types/rateLimits/helpers/processStreamBudgetHeaders.d.ts +9 -0
  97. package/dist-types/rateLimits/helpers/processStreamRateLimitException.d.ts +3 -0
  98. package/dist-types/rateLimits/helpers/processStreamRateLimitHeaders.d.ts +6 -0
  99. package/dist-types/rateLimits/helpers/rateLimitSnapshot.d.ts +3 -0
  100. package/dist-types/rateLimits/helpers/registerStreamBudgetInterceptors.d.ts +2 -0
  101. package/dist-types/rateLimits/helpers/registerStreamInterceptors.d.ts +2 -0
  102. package/dist-types/rateLimits/helpers/splitCallArgsAndOptions.d.ts +6 -0
  103. package/dist-types/rateLimits/index.d.ts +4 -0
  104. package/dist-types/rateLimits/types.d.ts +68 -0
  105. package/dist-types/rateLimits/withStreamRateLimitOptions.d.ts +6 -0
  106. package/dist-types/streamRateLimitHandlingProxy/constants.d.ts +28 -0
  107. package/dist-types/streamRateLimitHandlingProxy/createRateLimitedStreamProxy.d.ts +11 -0
  108. package/dist-types/streamRateLimitHandlingProxy/generatedMethodNames.d.ts +11 -0
  109. package/dist-types/streamRateLimitHandlingProxy/helpers/RedisCooldown.d.ts +11 -0
  110. package/dist-types/streamRateLimitHandlingProxy/helpers/createRateLimitExceededException.d.ts +10 -0
  111. package/dist-types/streamRateLimitHandlingProxy/helpers/executeWithRateLimitHandling.d.ts +2 -0
  112. package/dist-types/streamRateLimitHandlingProxy/helpers/headerUtils.d.ts +4 -0
  113. package/dist-types/streamRateLimitHandlingProxy/helpers/isStreamChannelLike.d.ts +6 -0
  114. package/dist-types/streamRateLimitHandlingProxy/helpers/processStreamBudgetHeaders.d.ts +9 -0
  115. package/dist-types/streamRateLimitHandlingProxy/helpers/processStreamRateLimitException.d.ts +3 -0
  116. package/dist-types/streamRateLimitHandlingProxy/helpers/processStreamRateLimitHeaders.d.ts +6 -0
  117. package/dist-types/streamRateLimitHandlingProxy/helpers/rateLimitSnapshot.d.ts +3 -0
  118. package/dist-types/streamRateLimitHandlingProxy/helpers/registerStreamInterceptors.d.ts +2 -0
  119. package/dist-types/streamRateLimitHandlingProxy/helpers/sleep.d.ts +2 -0
  120. package/dist-types/streamRateLimitHandlingProxy/helpers/splitCallArgsAndOptions.d.ts +6 -0
  121. package/dist-types/streamRateLimitHandlingProxy/index.d.ts +2 -0
  122. package/dist-types/streamRateLimitHandlingProxy/types.d.ts +68 -0
  123. package/dist-types/streamRateLimitHandlingProxy/withStreamRateLimitOptions.d.ts +6 -0
  124. package/package.json +1 -1
@@ -0,0 +1,320 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CHANNEL_SYNC_PROXY_RESULT_METHOD_NAMES = exports.CHANNEL_SYNC_METHOD_NAMES = exports.CHANNEL_METHOD_NAMES = exports.STREAM_SYNC_CHANNEL_RESULT_METHOD_NAMES = exports.STREAM_CHANNEL_RESULT_METHOD_NAMES = exports.STREAM_SYNC_METHOD_NAMES = exports.STREAM_METHOD_NAMES = void 0;
4
+ exports.STREAM_METHOD_NAMES = [
5
+ '_addChannelConfig',
6
+ '_buildWSPayload',
7
+ '_callClientListeners',
8
+ '_createImport',
9
+ '_createImportURL',
10
+ '_deleteUserMessageReference',
11
+ '_enrichAxiosOptions',
12
+ '_getConnectionID',
13
+ '_getImport',
14
+ '_getToken',
15
+ '_handleClientEvent',
16
+ '_handleUserEvent',
17
+ '_hasConnectionID',
18
+ '_isUsingServerAuth',
19
+ '_listImports',
20
+ '_logApiError',
21
+ '_logApiRequest',
22
+ '_logApiResponse',
23
+ '_muteStatus',
24
+ '_normalizeDate',
25
+ '_normalizeExpiration',
26
+ '_queryFlagReports',
27
+ '_queryFlags',
28
+ '_reviewFlagReport',
29
+ '_sayHi',
30
+ '_setToken',
31
+ '_setupConnection',
32
+ '_setUser',
33
+ '_startCleaning',
34
+ '_unblockMessage',
35
+ '_updateMemberWatcherReferences',
36
+ '_updateUserMessageReferences',
37
+ '_updateUserReferences',
38
+ '_validateAndGetMessageId',
39
+ 'addDevice',
40
+ 'banUser',
41
+ 'channel',
42
+ 'closeConnection',
43
+ 'commitMessage',
44
+ 'connect',
45
+ 'connectAnonymousUser',
46
+ 'connectUser',
47
+ 'createAbortControllerForNextRequest',
48
+ 'createBlockList',
49
+ 'createCampaign',
50
+ 'createChannelType',
51
+ 'createCommand',
52
+ 'createPermission',
53
+ 'createRole',
54
+ 'createSegment',
55
+ 'createToken',
56
+ 'deactivateUser',
57
+ 'deactivateUsers',
58
+ 'delete',
59
+ 'deleteBlockList',
60
+ 'deleteCampaign',
61
+ 'deleteChannels',
62
+ 'deleteChannelType',
63
+ 'deleteCommand',
64
+ 'deleteMessage',
65
+ 'deletePermission',
66
+ 'deletePushProvider',
67
+ 'deleteRole',
68
+ 'deleteSegment',
69
+ 'deleteUser',
70
+ 'deleteUsers',
71
+ 'devToken',
72
+ 'disconnect',
73
+ 'disconnectUser',
74
+ 'dispatchEvent',
75
+ 'doAxiosRequest',
76
+ 'enrichURL',
77
+ 'errorFromResponse',
78
+ 'exportChannel',
79
+ 'exportChannels',
80
+ 'exportUser',
81
+ 'exportUsers',
82
+ 'flagMessage',
83
+ 'flagUser',
84
+ 'get',
85
+ 'getAppSettings',
86
+ 'getAuthType',
87
+ 'getBlockList',
88
+ 'getCallToken',
89
+ 'getChannelById',
90
+ 'getChannelByMembers',
91
+ 'getChannelType',
92
+ 'getCommand',
93
+ 'getDevices',
94
+ 'getExportChannelStatus',
95
+ 'getMessage',
96
+ 'getPermission',
97
+ 'getRateLimits',
98
+ 'getTask',
99
+ 'getUnreadCount',
100
+ 'getUserAgent',
101
+ 'handleEvent',
102
+ 'handleResponse',
103
+ 'hydrateActiveChannels',
104
+ 'listBlockLists',
105
+ 'listChannelTypes',
106
+ 'listCommands',
107
+ 'listPermissions',
108
+ 'listPushProviders',
109
+ 'listRoles',
110
+ 'markAllRead',
111
+ 'markChannelsRead',
112
+ 'muteUser',
113
+ 'off',
114
+ 'on',
115
+ 'openConnection',
116
+ 'partialUpdateMessage',
117
+ 'partialUpdateUser',
118
+ 'partialUpdateUsers',
119
+ 'patch',
120
+ 'pinMessage',
121
+ 'post',
122
+ 'put',
123
+ 'queryBannedUsers',
124
+ 'queryCampaigns',
125
+ 'queryChannels',
126
+ 'queryMessageFlags',
127
+ 'queryRecipients',
128
+ 'querySegments',
129
+ 'queryUsers',
130
+ 'reactivateUser',
131
+ 'reactivateUsers',
132
+ 'recoverState',
133
+ 'removeDevice',
134
+ 'removeShadowBan',
135
+ 'restoreUsers',
136
+ 'resumeCampaign',
137
+ 'revokeTokens',
138
+ 'revokeUsersToken',
139
+ 'revokeUserToken',
140
+ 'scheduleCampaign',
141
+ 'search',
142
+ 'sendFile',
143
+ 'sendUserCustomEvent',
144
+ 'setAnonymousUser',
145
+ 'setBaseURL',
146
+ 'setGuestUser',
147
+ 'setLocalDevice',
148
+ 'setUser',
149
+ 'setUserAgent',
150
+ 'shadowBan',
151
+ 'stopCampaign',
152
+ 'sync',
153
+ 'testCampaign',
154
+ 'testPushSettings',
155
+ 'testSQSSettings',
156
+ 'translateMessage',
157
+ 'unbanUser',
158
+ 'unblockMessage',
159
+ 'unflagMessage',
160
+ 'unflagUser',
161
+ 'unmuteUser',
162
+ 'unpinMessage',
163
+ 'updateAppSettings',
164
+ 'updateBlockList',
165
+ 'updateCampaign',
166
+ 'updateChannelType',
167
+ 'updateCommand',
168
+ 'updateMessage',
169
+ 'updatePermission',
170
+ 'updateSegment',
171
+ 'updateUser',
172
+ 'updateUsers',
173
+ 'upsertPushProvider',
174
+ 'upsertUser',
175
+ 'upsertUsers',
176
+ 'userMuteStatus',
177
+ 'verifyWebhook',
178
+ ];
179
+ exports.STREAM_SYNC_METHOD_NAMES = [
180
+ '_addChannelConfig',
181
+ '_buildWSPayload',
182
+ '_callClientListeners',
183
+ '_deleteUserMessageReference',
184
+ '_enrichAxiosOptions',
185
+ '_getConnectionID',
186
+ '_getToken',
187
+ '_handleClientEvent',
188
+ '_handleUserEvent',
189
+ '_hasConnectionID',
190
+ '_isUsingServerAuth',
191
+ '_logApiError',
192
+ '_logApiRequest',
193
+ '_logApiResponse',
194
+ '_muteStatus',
195
+ '_normalizeDate',
196
+ '_normalizeExpiration',
197
+ '_sayHi',
198
+ '_setUser',
199
+ '_startCleaning',
200
+ '_updateMemberWatcherReferences',
201
+ '_updateUserMessageReferences',
202
+ '_updateUserReferences',
203
+ '_validateAndGetMessageId',
204
+ 'createAbortControllerForNextRequest',
205
+ 'createToken',
206
+ 'devToken',
207
+ 'dispatchEvent',
208
+ 'errorFromResponse',
209
+ 'getAuthType',
210
+ 'getUserAgent',
211
+ 'handleEvent',
212
+ 'handleResponse',
213
+ 'off',
214
+ 'on',
215
+ 'setBaseURL',
216
+ 'setLocalDevice',
217
+ 'setUserAgent',
218
+ 'userMuteStatus',
219
+ 'verifyWebhook',
220
+ ];
221
+ exports.STREAM_CHANNEL_RESULT_METHOD_NAMES = [
222
+ 'channel',
223
+ 'getChannelById',
224
+ 'getChannelByMembers',
225
+ 'hydrateActiveChannels',
226
+ 'queryChannels',
227
+ ];
228
+ exports.STREAM_SYNC_CHANNEL_RESULT_METHOD_NAMES = [
229
+ 'channel',
230
+ 'getChannelById',
231
+ 'getChannelByMembers',
232
+ 'hydrateActiveChannels',
233
+ ];
234
+ exports.CHANNEL_METHOD_NAMES = [
235
+ '_callChannelListeners',
236
+ '_channelURL',
237
+ '_checkInitialized',
238
+ '_countMessageAsUnread',
239
+ '_disconnect',
240
+ '_extendEventWithOwnReactions',
241
+ '_handleChannelEvent',
242
+ '_initializeState',
243
+ '_update',
244
+ 'acceptInvite',
245
+ 'addMembers',
246
+ 'addModerators',
247
+ 'assignRoles',
248
+ 'banUser',
249
+ 'clean',
250
+ 'countUnread',
251
+ 'countUnreadMentions',
252
+ 'create',
253
+ 'createCall',
254
+ 'delete',
255
+ 'deleteFile',
256
+ 'deleteImage',
257
+ 'deleteReaction',
258
+ 'demoteModerators',
259
+ 'disableSlowMode',
260
+ 'enableSlowMode',
261
+ 'getClient',
262
+ 'getConfig',
263
+ 'getMessagesById',
264
+ 'getPinnedMessages',
265
+ 'getReactions',
266
+ 'getReplies',
267
+ 'hide',
268
+ 'inviteMembers',
269
+ 'keystroke',
270
+ 'lastMessage',
271
+ 'lastRead',
272
+ 'markRead',
273
+ 'markUnread',
274
+ 'mute',
275
+ 'muteStatus',
276
+ 'off',
277
+ 'on',
278
+ 'query',
279
+ 'queryMembers',
280
+ 'rejectInvite',
281
+ 'removeMembers',
282
+ 'removeShadowBan',
283
+ 'search',
284
+ 'sendAction',
285
+ 'sendEvent',
286
+ 'sendFile',
287
+ 'sendImage',
288
+ 'sendMessage',
289
+ 'sendReaction',
290
+ 'shadowBan',
291
+ 'show',
292
+ 'stopTyping',
293
+ 'stopWatching',
294
+ 'truncate',
295
+ 'unbanUser',
296
+ 'unmute',
297
+ 'update',
298
+ 'updatePartial',
299
+ 'watch',
300
+ ];
301
+ exports.CHANNEL_SYNC_METHOD_NAMES = [
302
+ '_callChannelListeners',
303
+ '_channelURL',
304
+ '_checkInitialized',
305
+ '_countMessageAsUnread',
306
+ '_disconnect',
307
+ '_extendEventWithOwnReactions',
308
+ '_handleChannelEvent',
309
+ '_initializeState',
310
+ 'clean',
311
+ 'countUnread',
312
+ 'countUnreadMentions',
313
+ 'getConfig',
314
+ 'lastMessage',
315
+ 'lastRead',
316
+ 'muteStatus',
317
+ 'off',
318
+ 'on',
319
+ ];
320
+ exports.CHANNEL_SYNC_PROXY_RESULT_METHOD_NAMES = ['getClient'];
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RedisCooldown = void 0;
4
+ const constants_1 = require("../constants");
5
+ class RedisCooldown {
6
+ static resolveRedisKey(cooldownKey, options) {
7
+ const keyPrefix = options?.keyPrefix ?? constants_1.STREAM_RATE_LIMIT_COOLDOWN_KEY_PREFIX;
8
+ return `${keyPrefix}${cooldownKey}`;
9
+ }
10
+ static async getRemainingMs(cooldownKey, context, options) {
11
+ const { redis, logger } = context;
12
+ try {
13
+ const ttlMs = await redis.pttl(RedisCooldown.resolveRedisKey(cooldownKey, options));
14
+ if (ttlMs > 0) {
15
+ return ttlMs;
16
+ }
17
+ }
18
+ catch (error) {
19
+ logger.warn('Failed to read stream rate-limit cooldown from Redis', {
20
+ cooldownKey,
21
+ error,
22
+ });
23
+ }
24
+ return 0;
25
+ }
26
+ static async setRemainingMs(cooldownKey, cooldownMs, context, options) {
27
+ if (cooldownMs <= 0) {
28
+ return;
29
+ }
30
+ const { redis, logger } = context;
31
+ const redisKey = RedisCooldown.resolveRedisKey(cooldownKey, options);
32
+ try {
33
+ await redis.eval(`
34
+ local key = KEYS[1]
35
+ local new_ttl = tonumber(ARGV[1])
36
+ local current_ttl = redis.call('PTTL', key)
37
+
38
+ if current_ttl == -2 then
39
+ redis.call('SET', key, '1', 'PX', new_ttl)
40
+ return 1
41
+ end
42
+
43
+ if current_ttl == -1 or current_ttl < new_ttl then
44
+ redis.call('PEXPIRE', key, new_ttl)
45
+ return 2
46
+ end
47
+
48
+ return 0
49
+ `, 1, redisKey, cooldownMs.toString());
50
+ }
51
+ catch (error) {
52
+ logger.warn('Failed to write stream rate-limit cooldown to Redis', {
53
+ cooldownKey,
54
+ cooldownMs,
55
+ error,
56
+ });
57
+ }
58
+ }
59
+ static async setRemainingMsExact(cooldownKey, cooldownMs, context, options) {
60
+ if (cooldownMs <= 0) {
61
+ return;
62
+ }
63
+ const { redis, logger } = context;
64
+ try {
65
+ await redis.set(RedisCooldown.resolveRedisKey(cooldownKey, options), '1', 'PX', cooldownMs);
66
+ }
67
+ catch (error) {
68
+ logger.warn('Failed to write exact stream rate-limit cooldown to Redis', {
69
+ cooldownKey,
70
+ cooldownMs,
71
+ error,
72
+ });
73
+ }
74
+ }
75
+ static async getValue(cooldownKey, context, options) {
76
+ const { redis, logger } = context;
77
+ try {
78
+ const value = await redis.get(RedisCooldown.resolveRedisKey(cooldownKey, options));
79
+ if (typeof value === 'string' && value.length > 0) {
80
+ return value;
81
+ }
82
+ }
83
+ catch (error) {
84
+ logger.warn('Failed to read stream rate-limit metadata from Redis', {
85
+ cooldownKey,
86
+ error,
87
+ });
88
+ }
89
+ }
90
+ static async setValueWithRemainingMs(cooldownKey, value, cooldownMs, context, options) {
91
+ if (value.length === 0 || cooldownMs <= 0) {
92
+ return;
93
+ }
94
+ const { redis, logger } = context;
95
+ try {
96
+ await redis.eval(`
97
+ local key = KEYS[1]
98
+ local value = ARGV[1]
99
+ local new_ttl = tonumber(ARGV[2])
100
+ local current_ttl = redis.call('PTTL', key)
101
+
102
+ if current_ttl == -2 then
103
+ redis.call('SET', key, value, 'PX', new_ttl)
104
+ return 1
105
+ end
106
+
107
+ if current_ttl == -1 or current_ttl < new_ttl then
108
+ redis.call('SET', key, value, 'PX', new_ttl)
109
+ return 2
110
+ end
111
+
112
+ redis.call('SET', key, value, 'KEEPTTL')
113
+ return 0
114
+ `, 1, RedisCooldown.resolveRedisKey(cooldownKey, options), value, cooldownMs.toString());
115
+ }
116
+ catch (error) {
117
+ logger.warn('Failed to write stream rate-limit metadata to Redis', {
118
+ cooldownKey,
119
+ cooldownMs,
120
+ error,
121
+ });
122
+ }
123
+ }
124
+ static async setRemainingMsWithValue(cooldownKey, valueKey, value, cooldownMs, context, options) {
125
+ if (value.length === 0 || cooldownMs <= 0) {
126
+ return;
127
+ }
128
+ const { redis, logger } = context;
129
+ const cooldownRedisKey = RedisCooldown.resolveRedisKey(cooldownKey, options);
130
+ const valueRedisKey = RedisCooldown.resolveRedisKey(valueKey, options);
131
+ try {
132
+ await redis.eval(`
133
+ local cooldown_key = KEYS[1]
134
+ local value_key = KEYS[2]
135
+ local value = ARGV[1]
136
+ local new_ttl = tonumber(ARGV[2])
137
+ local current_ttl = redis.call('PTTL', cooldown_key)
138
+ local final_ttl = new_ttl
139
+
140
+ if current_ttl == -2 then
141
+ redis.call('SET', cooldown_key, '1', 'PX', new_ttl)
142
+ elseif current_ttl == -1 or current_ttl < new_ttl then
143
+ redis.call('PEXPIRE', cooldown_key, new_ttl)
144
+ else
145
+ final_ttl = current_ttl
146
+ end
147
+
148
+ redis.call('SET', value_key, value, 'PX', final_ttl)
149
+ return final_ttl
150
+ `, 2, cooldownRedisKey, valueRedisKey, value, cooldownMs.toString());
151
+ }
152
+ catch (error) {
153
+ logger.warn('Failed to write stream rate-limit cooldown and metadata to Redis', {
154
+ cooldownKey,
155
+ valueKey,
156
+ cooldownMs,
157
+ error,
158
+ });
159
+ }
160
+ }
161
+ static async clear(cooldownKey, context, options) {
162
+ const { redis, logger } = context;
163
+ try {
164
+ await redis.del(RedisCooldown.resolveRedisKey(cooldownKey, options));
165
+ }
166
+ catch (error) {
167
+ logger.warn('Failed to clear stream rate-limit cooldown from Redis', {
168
+ cooldownKey,
169
+ error,
170
+ });
171
+ }
172
+ }
173
+ }
174
+ exports.RedisCooldown = RedisCooldown;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createRateLimitExceededException = void 0;
4
+ const xbees_conversations_client_1 = require("@wildix/xbees-conversations-client");
5
+ const constants_1 = require("../constants");
6
+ const RETRY_AFTER_HEADER_PADDING_MS = 3000;
7
+ function toPositiveMilliseconds(milliseconds) {
8
+ return Math.max(0, Math.ceil(milliseconds));
9
+ }
10
+ function toEpochSecondsFromNow(delayMs) {
11
+ return Math.ceil((Date.now() + toPositiveMilliseconds(delayMs)) / 1000);
12
+ }
13
+ function createRateLimitExceededException(options = {}) {
14
+ const { rateLimit, message, operation, retryAfterMs = constants_1.DEFAULT_RETRY_AFTER_MS } = options;
15
+ const defaultMessage = operation ? `Rate limit exceeded for ${operation}` : 'Rate limit exceeded';
16
+ const retryAfterFromResetMs = rateLimit?.reset ? rateLimit.reset - Date.now() : undefined;
17
+ const effectiveRetryAfterMs = retryAfterFromResetMs ?? retryAfterMs;
18
+ const retryAfterMilliseconds = toPositiveMilliseconds(effectiveRetryAfterMs);
19
+ const rateLimitResetMilliseconds = rateLimit?.reset
20
+ ? toPositiveMilliseconds(retryAfterFromResetMs ?? 0)
21
+ : retryAfterMilliseconds;
22
+ const retryAfterEpochSeconds = toEpochSecondsFromNow(effectiveRetryAfterMs + RETRY_AFTER_HEADER_PADDING_MS);
23
+ return new xbees_conversations_client_1.RateLimitExceededException({
24
+ $metadata: {},
25
+ message: message || defaultMessage,
26
+ rateLimitRemaining: rateLimit?.remaining || 0,
27
+ retryAfter: retryAfterEpochSeconds,
28
+ rateLimit: rateLimit?.limit?.toString() || 'unknown',
29
+ rateLimitReset: rateLimitResetMilliseconds,
30
+ });
31
+ }
32
+ exports.createRateLimitExceededException = createRateLimitExceededException;
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.executeWithRateLimitHandling = void 0;
4
+ const promises_1 = require("node:timers/promises");
5
+ const constants_1 = require("../constants");
6
+ const createRateLimitExceededException_1 = require("./createRateLimitExceededException");
7
+ const processStreamRateLimitException_1 = require("./processStreamRateLimitException");
8
+ const rateLimitSnapshot_1 = require("./rateLimitSnapshot");
9
+ const RedisCooldown_1 = require("./RedisCooldown");
10
+ function calculateRateLimitDelayMs(error, maxDelayMs) {
11
+ const retryAfterMs = typeof error.rateLimitReset === 'number' && error.rateLimitReset > 0 ? error.rateLimitReset : 0;
12
+ const hasServerRetryAfter = retryAfterMs > 0;
13
+ const baseDelayMs = hasServerRetryAfter ? retryAfterMs + constants_1.RETRY_AFTER_BUFFER_MS : constants_1.DEFAULT_RETRY_AFTER_MS;
14
+ if (baseDelayMs > 0) {
15
+ const jitterMs = Math.floor(Math.random() * constants_1.DEFAULT_JITTER_MS);
16
+ const computedDelayMs = baseDelayMs + jitterMs;
17
+ if (hasServerRetryAfter) {
18
+ return computedDelayMs;
19
+ }
20
+ return Math.min(maxDelayMs, computedDelayMs);
21
+ }
22
+ return Math.min(maxDelayMs, constants_1.DEFAULT_MAX_DELAY_MS);
23
+ }
24
+ function getRateLimitMetadataKey(operation) {
25
+ return `${operation}${constants_1.STREAM_RATE_LIMIT_METADATA_KEY_SUFFIX}`;
26
+ }
27
+ function toCooldownRateLimit(snapshot, cooldownMs) {
28
+ return {
29
+ limit: snapshot.limit,
30
+ remaining: snapshot.remaining,
31
+ reset: Math.max(snapshot.reset, Date.now() + cooldownMs),
32
+ };
33
+ }
34
+ function toRateLimitSnapshot(error) {
35
+ const limit = Number(error.rateLimit);
36
+ const remaining = error.rateLimitRemaining;
37
+ const resetMs = error.rateLimitReset;
38
+ if (!Number.isFinite(limit) ||
39
+ typeof remaining !== 'number' ||
40
+ !Number.isFinite(remaining) ||
41
+ typeof resetMs !== 'number' ||
42
+ !Number.isFinite(resetMs) ||
43
+ resetMs <= 0) {
44
+ return;
45
+ }
46
+ return {
47
+ limit,
48
+ remaining,
49
+ reset: Date.now() + resetMs,
50
+ };
51
+ }
52
+ async function executeWithRateLimitHandling(execute, operation, context, options) {
53
+ const { logger, redis } = context;
54
+ const { enableCooldown, maxAttempts, maxDelayMs, maxRetryableDelayMs } = options;
55
+ let attempt = 1;
56
+ if (enableCooldown) {
57
+ const cooldownMs = await RedisCooldown_1.RedisCooldown.getRemainingMs(operation, { redis, logger });
58
+ if (cooldownMs > 0) {
59
+ const rateLimitSnapshot = (0, rateLimitSnapshot_1.parseRateLimitSnapshot)(await RedisCooldown_1.RedisCooldown.getValue(getRateLimitMetadataKey(operation), { redis, logger }));
60
+ logger.warn('Skipping stream request due to active rate-limit cooldown', {
61
+ operation,
62
+ cooldown: cooldownMs,
63
+ rateLimitSnapshot,
64
+ });
65
+ throw (0, createRateLimitExceededException_1.createRateLimitExceededException)({
66
+ operation,
67
+ retryAfterMs: cooldownMs,
68
+ rateLimit: rateLimitSnapshot ? toCooldownRateLimit(rateLimitSnapshot, cooldownMs) : undefined,
69
+ });
70
+ }
71
+ const softCooldownMs = await RedisCooldown_1.RedisCooldown.getRemainingMs(constants_1.STREAM_RATE_LIMIT_SOFT_COOLDOWN_KEY, { redis, logger }, { keyPrefix: constants_1.STREAM_RATE_LIMIT_COOLDOWN_KEY_PREFIX });
72
+ if (softCooldownMs > 0) {
73
+ logger.debug('Delaying stream request due to active soft rate-limit cooldown', {
74
+ operation,
75
+ cooldown: softCooldownMs,
76
+ });
77
+ await (0, promises_1.setTimeout)(softCooldownMs);
78
+ }
79
+ const budgetCooldownMs = await RedisCooldown_1.RedisCooldown.getRemainingMs(constants_1.STREAM_BUDGET_COOLDOWN_KEY, { redis, logger }, { keyPrefix: constants_1.STREAM_BUDGET_COOLDOWN_KEY_PREFIX });
80
+ if (budgetCooldownMs > 0) {
81
+ logger.warn('Delaying stream request due to active budget cooldown', {
82
+ operation,
83
+ cooldown: budgetCooldownMs,
84
+ });
85
+ let totalBudgetWaitMs = 0;
86
+ for (;;) {
87
+ const waitMs = await RedisCooldown_1.RedisCooldown.getRemainingMs(constants_1.STREAM_BUDGET_COOLDOWN_KEY, { redis, logger }, { keyPrefix: constants_1.STREAM_BUDGET_COOLDOWN_KEY_PREFIX });
88
+ if (waitMs <= 0 || totalBudgetWaitMs >= constants_1.BUDGET_DELAY_MAX_MS) {
89
+ break;
90
+ }
91
+ const chunkMs = Math.min(waitMs, constants_1.BUDGET_COOLDOWN_SLEEP_CHUNK_MS);
92
+ await (0, promises_1.setTimeout)(chunkMs);
93
+ totalBudgetWaitMs += chunkMs;
94
+ }
95
+ }
96
+ }
97
+ for (;;) {
98
+ try {
99
+ return await execute();
100
+ }
101
+ catch (error) {
102
+ const rateLimitError = (0, processStreamRateLimitException_1.processStreamRateLimitException)(error, logger);
103
+ if (!rateLimitError) {
104
+ throw error;
105
+ }
106
+ const retryAfterMs = calculateRateLimitDelayMs(rateLimitError, maxDelayMs);
107
+ if (enableCooldown) {
108
+ const rateLimitSnapshot = toRateLimitSnapshot(rateLimitError);
109
+ if (rateLimitSnapshot) {
110
+ await RedisCooldown_1.RedisCooldown.setRemainingMsWithValue(operation, getRateLimitMetadataKey(operation), (0, rateLimitSnapshot_1.serializeRateLimitSnapshot)(rateLimitSnapshot), retryAfterMs, { redis, logger });
111
+ }
112
+ else {
113
+ await RedisCooldown_1.RedisCooldown.setRemainingMs(operation, retryAfterMs, { redis, logger });
114
+ }
115
+ }
116
+ if (attempt < maxAttempts && retryAfterMs <= maxRetryableDelayMs) {
117
+ logger.warn('Retrying stream request after rate limit', {
118
+ operation,
119
+ attempt,
120
+ maxAttempts,
121
+ retryAfterMs,
122
+ rateLimit: rateLimitError.rateLimit,
123
+ rateLimitRemaining: rateLimitError.rateLimitRemaining,
124
+ rateLimitReset: rateLimitError.rateLimitReset,
125
+ });
126
+ await (0, promises_1.setTimeout)(retryAfterMs);
127
+ attempt++;
128
+ continue;
129
+ }
130
+ logger.warn('Propagating stream rate limit w/o retry', {
131
+ operation,
132
+ attempt,
133
+ maxAttempts,
134
+ retryAfterMs,
135
+ maxRetryableDelayMs,
136
+ rateLimit: rateLimitError.rateLimit,
137
+ rateLimitRemaining: rateLimitError.rateLimitRemaining,
138
+ rateLimitReset: rateLimitError.rateLimitReset,
139
+ });
140
+ throw rateLimitError;
141
+ }
142
+ }
143
+ }
144
+ exports.executeWithRateLimitHandling = executeWithRateLimitHandling;