@wildix/xbees-conversations-utils 1.1.84 → 1.1.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +118 -1
- package/dist-cjs/index.js +2 -1
- package/dist-cjs/rateLimits/RedisCooldown.js +66 -0
- package/dist-cjs/rateLimits/constants.js +43 -0
- package/dist-cjs/rateLimits/createRateLimitedStreamProxy.js +92 -0
- package/dist-cjs/rateLimits/exception/createRateLimitExceededException.js +21 -0
- package/dist-cjs/rateLimits/exception/processStreamRateLimitException.js +47 -0
- package/dist-cjs/rateLimits/executeStreamRequestWithRateLimitHandling.js +94 -0
- package/dist-cjs/rateLimits/generatedMethodNames.js +320 -0
- package/dist-cjs/rateLimits/helpers/RedisCooldown.js +174 -0
- package/dist-cjs/rateLimits/helpers/createRateLimitExceededException.js +32 -0
- package/dist-cjs/rateLimits/helpers/executeWithRateLimitHandling.js +144 -0
- package/dist-cjs/rateLimits/helpers/headerUtils.js +40 -0
- package/dist-cjs/rateLimits/helpers/isStreamChannelLike.js +23 -0
- package/dist-cjs/rateLimits/helpers/processStreamBudgetHeaders.js +93 -0
- package/dist-cjs/rateLimits/helpers/processStreamRateLimitException.js +63 -0
- package/dist-cjs/rateLimits/helpers/processStreamRateLimitHeaders.js +50 -0
- package/dist-cjs/rateLimits/helpers/rateLimitSnapshot.js +31 -0
- package/dist-cjs/rateLimits/helpers/registerStreamBudgetInterceptors.js +48 -0
- package/dist-cjs/rateLimits/helpers/registerStreamInterceptors.js +58 -0
- package/dist-cjs/rateLimits/helpers/splitCallArgsAndOptions.js +21 -0
- package/dist-cjs/rateLimits/index.js +7 -0
- package/dist-cjs/rateLimits/types.js +3 -0
- package/dist-cjs/rateLimits/withStreamRateLimitOptions.js +10 -0
- package/dist-cjs/streamRateLimitHandlingProxy/constants.js +43 -0
- package/dist-cjs/streamRateLimitHandlingProxy/createRateLimitedStreamProxy.js +92 -0
- package/dist-cjs/streamRateLimitHandlingProxy/generatedMethodNames.js +320 -0
- package/dist-cjs/streamRateLimitHandlingProxy/helpers/RedisCooldown.js +174 -0
- package/dist-cjs/streamRateLimitHandlingProxy/helpers/createRateLimitExceededException.js +32 -0
- package/dist-cjs/streamRateLimitHandlingProxy/helpers/executeWithRateLimitHandling.js +144 -0
- package/dist-cjs/streamRateLimitHandlingProxy/helpers/headerUtils.js +40 -0
- package/dist-cjs/streamRateLimitHandlingProxy/helpers/isStreamChannelLike.js +23 -0
- package/dist-cjs/streamRateLimitHandlingProxy/helpers/processStreamBudgetHeaders.js +93 -0
- package/dist-cjs/streamRateLimitHandlingProxy/helpers/processStreamRateLimitException.js +63 -0
- package/dist-cjs/streamRateLimitHandlingProxy/helpers/processStreamRateLimitHeaders.js +72 -0
- package/dist-cjs/streamRateLimitHandlingProxy/helpers/rateLimitSnapshot.js +31 -0
- package/dist-cjs/streamRateLimitHandlingProxy/helpers/registerStreamInterceptors.js +58 -0
- package/dist-cjs/streamRateLimitHandlingProxy/helpers/splitCallArgsAndOptions.js +21 -0
- package/dist-cjs/streamRateLimitHandlingProxy/index.js +5 -0
- package/dist-cjs/streamRateLimitHandlingProxy/types.js +3 -0
- package/dist-cjs/streamRateLimitHandlingProxy/withStreamRateLimitOptions.js +10 -0
- package/dist-es/index.js +2 -1
- package/dist-es/rateLimits/RedisCooldown.js +62 -0
- package/dist-es/rateLimits/constants.js +33 -0
- package/dist-es/rateLimits/createRateLimitedStreamProxy.js +88 -0
- package/dist-es/rateLimits/exception/createRateLimitExceededException.js +17 -0
- package/dist-es/rateLimits/exception/processStreamRateLimitException.js +37 -0
- package/dist-es/rateLimits/executeStreamRequestWithRateLimitHandling.js +93 -0
- package/dist-es/rateLimits/generatedMethodNames.js +317 -0
- package/dist-es/rateLimits/helpers/RedisCooldown.js +170 -0
- package/dist-es/rateLimits/helpers/createRateLimitExceededException.js +28 -0
- package/dist-es/rateLimits/helpers/executeWithRateLimitHandling.js +140 -0
- package/dist-es/rateLimits/helpers/headerUtils.js +34 -0
- package/dist-es/rateLimits/helpers/isStreamChannelLike.js +19 -0
- package/dist-es/rateLimits/helpers/processStreamBudgetHeaders.js +89 -0
- package/dist-es/rateLimits/helpers/processStreamRateLimitException.js +59 -0
- package/dist-es/rateLimits/helpers/processStreamRateLimitHeaders.js +46 -0
- package/dist-es/rateLimits/helpers/rateLimitSnapshot.js +26 -0
- package/dist-es/rateLimits/helpers/registerStreamBudgetInterceptors.js +44 -0
- package/dist-es/rateLimits/helpers/registerStreamInterceptors.js +54 -0
- package/dist-es/rateLimits/helpers/splitCallArgsAndOptions.js +17 -0
- package/dist-es/rateLimits/index.js +4 -0
- package/dist-es/rateLimits/types.js +1 -0
- package/dist-es/rateLimits/withStreamRateLimitOptions.js +6 -0
- package/dist-es/streamRateLimitHandlingProxy/constants.js +33 -0
- package/dist-es/streamRateLimitHandlingProxy/createRateLimitedStreamProxy.js +88 -0
- package/dist-es/streamRateLimitHandlingProxy/generatedMethodNames.js +317 -0
- package/dist-es/streamRateLimitHandlingProxy/helpers/RedisCooldown.js +170 -0
- package/dist-es/streamRateLimitHandlingProxy/helpers/createRateLimitExceededException.js +28 -0
- package/dist-es/streamRateLimitHandlingProxy/helpers/executeWithRateLimitHandling.js +140 -0
- package/dist-es/streamRateLimitHandlingProxy/helpers/headerUtils.js +34 -0
- package/dist-es/streamRateLimitHandlingProxy/helpers/isStreamChannelLike.js +19 -0
- package/dist-es/streamRateLimitHandlingProxy/helpers/processStreamBudgetHeaders.js +89 -0
- package/dist-es/streamRateLimitHandlingProxy/helpers/processStreamRateLimitException.js +59 -0
- package/dist-es/streamRateLimitHandlingProxy/helpers/processStreamRateLimitHeaders.js +68 -0
- package/dist-es/streamRateLimitHandlingProxy/helpers/rateLimitSnapshot.js +26 -0
- package/dist-es/streamRateLimitHandlingProxy/helpers/registerStreamInterceptors.js +54 -0
- package/dist-es/streamRateLimitHandlingProxy/helpers/splitCallArgsAndOptions.js +17 -0
- package/dist-es/streamRateLimitHandlingProxy/index.js +2 -0
- package/dist-es/streamRateLimitHandlingProxy/types.js +1 -0
- package/dist-es/streamRateLimitHandlingProxy/withStreamRateLimitOptions.js +6 -0
- package/dist-types/index.d.ts +2 -1
- package/dist-types/rateLimits/RedisCooldown.d.ts +7 -0
- package/dist-types/rateLimits/constants.d.ts +29 -0
- package/dist-types/rateLimits/createRateLimitedStreamProxy.d.ts +11 -0
- package/dist-types/rateLimits/exception/createRateLimitExceededException.d.ts +12 -0
- package/dist-types/rateLimits/exception/processStreamRateLimitException.d.ts +8 -0
- package/dist-types/rateLimits/executeStreamRequestWithRateLimitHandling.d.ts +8 -0
- package/dist-types/rateLimits/generatedMethodNames.d.ts +11 -0
- package/dist-types/rateLimits/helpers/RedisCooldown.d.ts +11 -0
- package/dist-types/rateLimits/helpers/createRateLimitExceededException.d.ts +10 -0
- package/dist-types/rateLimits/helpers/executeWithRateLimitHandling.d.ts +2 -0
- package/dist-types/rateLimits/helpers/headerUtils.d.ts +4 -0
- package/dist-types/rateLimits/helpers/isStreamChannelLike.d.ts +6 -0
- package/dist-types/rateLimits/helpers/processStreamBudgetHeaders.d.ts +9 -0
- package/dist-types/rateLimits/helpers/processStreamRateLimitException.d.ts +3 -0
- package/dist-types/rateLimits/helpers/processStreamRateLimitHeaders.d.ts +6 -0
- package/dist-types/rateLimits/helpers/rateLimitSnapshot.d.ts +3 -0
- package/dist-types/rateLimits/helpers/registerStreamBudgetInterceptors.d.ts +2 -0
- package/dist-types/rateLimits/helpers/registerStreamInterceptors.d.ts +2 -0
- package/dist-types/rateLimits/helpers/splitCallArgsAndOptions.d.ts +6 -0
- package/dist-types/rateLimits/index.d.ts +4 -0
- package/dist-types/rateLimits/types.d.ts +68 -0
- package/dist-types/rateLimits/withStreamRateLimitOptions.d.ts +6 -0
- package/dist-types/streamRateLimitHandlingProxy/constants.d.ts +29 -0
- package/dist-types/streamRateLimitHandlingProxy/createRateLimitedStreamProxy.d.ts +11 -0
- package/dist-types/streamRateLimitHandlingProxy/generatedMethodNames.d.ts +11 -0
- package/dist-types/streamRateLimitHandlingProxy/helpers/RedisCooldown.d.ts +11 -0
- package/dist-types/streamRateLimitHandlingProxy/helpers/createRateLimitExceededException.d.ts +10 -0
- package/dist-types/streamRateLimitHandlingProxy/helpers/executeWithRateLimitHandling.d.ts +2 -0
- package/dist-types/streamRateLimitHandlingProxy/helpers/headerUtils.d.ts +4 -0
- package/dist-types/streamRateLimitHandlingProxy/helpers/isStreamChannelLike.d.ts +6 -0
- package/dist-types/streamRateLimitHandlingProxy/helpers/processStreamBudgetHeaders.d.ts +9 -0
- package/dist-types/streamRateLimitHandlingProxy/helpers/processStreamRateLimitException.d.ts +3 -0
- package/dist-types/streamRateLimitHandlingProxy/helpers/processStreamRateLimitHeaders.d.ts +6 -0
- package/dist-types/streamRateLimitHandlingProxy/helpers/rateLimitSnapshot.d.ts +3 -0
- package/dist-types/streamRateLimitHandlingProxy/helpers/registerStreamInterceptors.d.ts +2 -0
- package/dist-types/streamRateLimitHandlingProxy/helpers/splitCallArgsAndOptions.d.ts +6 -0
- package/dist-types/streamRateLimitHandlingProxy/index.d.ts +2 -0
- package/dist-types/streamRateLimitHandlingProxy/types.d.ts +68 -0
- package/dist-types/streamRateLimitHandlingProxy/withStreamRateLimitOptions.d.ts +6 -0
- package/dist-types/types.d.ts +1 -1
- package/package.json +9 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeResetToEpochMs = exports.parseHeaderNumber = exports.getHeaderCaseInsensitive = void 0;
|
|
4
|
+
function getHeaderCaseInsensitive(headers, key) {
|
|
5
|
+
if (!headers) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const direct = headers[key];
|
|
9
|
+
if (direct !== undefined) {
|
|
10
|
+
return direct;
|
|
11
|
+
}
|
|
12
|
+
const normalizedKey = key.toLowerCase();
|
|
13
|
+
for (const [headerKey, headerValue] of Object.entries(headers)) {
|
|
14
|
+
if (headerKey.toLowerCase() === normalizedKey) {
|
|
15
|
+
return headerValue;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.getHeaderCaseInsensitive = getHeaderCaseInsensitive;
|
|
20
|
+
function parseHeaderNumber(headers, key) {
|
|
21
|
+
const raw = getHeaderCaseInsensitive(headers, key);
|
|
22
|
+
if (typeof raw === 'number' && Number.isFinite(raw)) {
|
|
23
|
+
return raw;
|
|
24
|
+
}
|
|
25
|
+
if (typeof raw === 'string') {
|
|
26
|
+
const parsed = Number(raw);
|
|
27
|
+
if (Number.isFinite(parsed)) {
|
|
28
|
+
return parsed;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.parseHeaderNumber = parseHeaderNumber;
|
|
33
|
+
function normalizeResetToEpochMs(reset) {
|
|
34
|
+
if (reset < 10000)
|
|
35
|
+
return Date.now() + Math.round(reset * 1000);
|
|
36
|
+
if (reset < 10000000000)
|
|
37
|
+
return Math.round(reset * 1000);
|
|
38
|
+
return Math.round(reset);
|
|
39
|
+
}
|
|
40
|
+
exports.normalizeResetToEpochMs = normalizeResetToEpochMs;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isStreamChannelLike = void 0;
|
|
4
|
+
const stream_chat_1 = require("stream-chat");
|
|
5
|
+
function hasFunctionProperty(value, key) {
|
|
6
|
+
return key in value && typeof value[key] === 'function';
|
|
7
|
+
}
|
|
8
|
+
function hasStringProperty(value, key) {
|
|
9
|
+
return key in value && typeof value[key] === 'string';
|
|
10
|
+
}
|
|
11
|
+
function isStreamChannelLike(value) {
|
|
12
|
+
if (typeof value !== 'object' || value === null) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
if (value instanceof stream_chat_1.Channel) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
return (hasStringProperty(value, 'cid') &&
|
|
19
|
+
hasFunctionProperty(value, 'sendMessage') &&
|
|
20
|
+
hasFunctionProperty(value, 'queryMembers') &&
|
|
21
|
+
hasFunctionProperty(value, 'updatePartial'));
|
|
22
|
+
}
|
|
23
|
+
exports.isStreamChannelLike = isStreamChannelLike;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.processStreamBudgetHeaders = void 0;
|
|
4
|
+
const constants_1 = require("../constants");
|
|
5
|
+
const headerUtils_1 = require("./headerUtils");
|
|
6
|
+
const RedisCooldown_1 = require("./RedisCooldown");
|
|
7
|
+
function extractBudgetHeaders(headers) {
|
|
8
|
+
return {
|
|
9
|
+
limitMs: (0, headerUtils_1.parseHeaderNumber)(headers ?? {}, 'x-budget-limit-ms'),
|
|
10
|
+
remainingMs: (0, headerUtils_1.parseHeaderNumber)(headers ?? {}, 'x-budget-remaining-ms'),
|
|
11
|
+
usedMs: (0, headerUtils_1.parseHeaderNumber)(headers ?? {}, 'x-budget-used-ms'),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function isBudgetThresholdReached(limitMs, remainingMs, usedMs) {
|
|
15
|
+
if (limitMs <= 0) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
if (typeof usedMs === 'number' && usedMs / limitMs >= constants_1.BUDGET_THRESHOLD) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
if (typeof remainingMs === 'number') {
|
|
22
|
+
const remainingThreshold = Math.ceil((1 - constants_1.BUDGET_THRESHOLD) * limitMs);
|
|
23
|
+
return remainingMs <= remainingThreshold;
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
function getRandomDelayInRange(minMs, maxMs) {
|
|
28
|
+
return Math.round(minMs + Math.random() * (maxMs - minMs));
|
|
29
|
+
}
|
|
30
|
+
function getBudgetUsage(limitMs, remainingMs, usedMs) {
|
|
31
|
+
if (limitMs <= 0) {
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
if (typeof usedMs === 'number' && Number.isFinite(usedMs) && usedMs >= 0) {
|
|
35
|
+
return usedMs / limitMs;
|
|
36
|
+
}
|
|
37
|
+
if (typeof remainingMs === 'number' && Number.isFinite(remainingMs)) {
|
|
38
|
+
return Math.max(0, 1 - remainingMs / limitMs);
|
|
39
|
+
}
|
|
40
|
+
return 0;
|
|
41
|
+
}
|
|
42
|
+
async function processStreamBudgetHeaders(headers, context) {
|
|
43
|
+
const { logger } = context;
|
|
44
|
+
const { limitMs, remainingMs, usedMs } = extractBudgetHeaders(headers);
|
|
45
|
+
if (limitMs === undefined && remainingMs === undefined && usedMs === undefined) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (typeof limitMs !== 'number' || !Number.isFinite(limitMs)) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const usage = getBudgetUsage(limitMs, remainingMs, usedMs);
|
|
52
|
+
const thresholdReached = isBudgetThresholdReached(limitMs, remainingMs, usedMs);
|
|
53
|
+
if (thresholdReached) {
|
|
54
|
+
const delayMs = getRandomDelayInRange(constants_1.BUDGET_DELAY_MIN_MS, constants_1.BUDGET_DELAY_MAX_MS);
|
|
55
|
+
await RedisCooldown_1.RedisCooldown.setRemainingMsExact(constants_1.STREAM_BUDGET_COOLDOWN_KEY, delayMs, context, {
|
|
56
|
+
keyPrefix: constants_1.STREAM_BUDGET_COOLDOWN_KEY_PREFIX,
|
|
57
|
+
});
|
|
58
|
+
logger.warn('Stream budget threshold reached, enabling cooldown', {
|
|
59
|
+
budgetLimitMs: limitMs,
|
|
60
|
+
budgetRemainingMs: remainingMs,
|
|
61
|
+
budgetUsedMs: usedMs,
|
|
62
|
+
budgetUsage: usage,
|
|
63
|
+
cooldownMs: delayMs,
|
|
64
|
+
});
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (usage < constants_1.BUDGET_CLEAR_USAGE_THRESHOLD) {
|
|
68
|
+
await RedisCooldown_1.RedisCooldown.clear(constants_1.STREAM_BUDGET_COOLDOWN_KEY, context, {
|
|
69
|
+
keyPrefix: constants_1.STREAM_BUDGET_COOLDOWN_KEY_PREFIX,
|
|
70
|
+
});
|
|
71
|
+
logger.info('Stream budget is low, clearing cooldown', {
|
|
72
|
+
budgetLimitMs: limitMs,
|
|
73
|
+
budgetRemainingMs: remainingMs,
|
|
74
|
+
budgetUsedMs: usedMs,
|
|
75
|
+
budgetUsage: usage,
|
|
76
|
+
});
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const delayMs = usage >= constants_1.BUDGET_RELAXED_USAGE_THRESHOLD
|
|
80
|
+
? getRandomDelayInRange(constants_1.BUDGET_RELAXED_DELAY_MIN_MS, constants_1.BUDGET_RELAXED_DELAY_MAX_MS)
|
|
81
|
+
: getRandomDelayInRange(constants_1.BUDGET_MINIMAL_DELAY_MIN_MS, constants_1.BUDGET_MINIMAL_DELAY_MAX_MS);
|
|
82
|
+
await RedisCooldown_1.RedisCooldown.setRemainingMsExact(constants_1.STREAM_BUDGET_COOLDOWN_KEY, delayMs, context, {
|
|
83
|
+
keyPrefix: constants_1.STREAM_BUDGET_COOLDOWN_KEY_PREFIX,
|
|
84
|
+
});
|
|
85
|
+
logger.info('Stream budget is below threshold, reducing cooldown', {
|
|
86
|
+
budgetLimitMs: limitMs,
|
|
87
|
+
budgetRemainingMs: remainingMs,
|
|
88
|
+
budgetUsedMs: usedMs,
|
|
89
|
+
budgetUsage: usage,
|
|
90
|
+
cooldownMs: delayMs,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
exports.processStreamBudgetHeaders = processStreamBudgetHeaders;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.processStreamRateLimitException = void 0;
|
|
4
|
+
const createRateLimitExceededException_1 = require("./createRateLimitExceededException");
|
|
5
|
+
const headerUtils_1 = require("./headerUtils");
|
|
6
|
+
function parseRetryAfterMs(headers) {
|
|
7
|
+
const retryAfter = (0, headerUtils_1.getHeaderCaseInsensitive)(headers, 'retry-after');
|
|
8
|
+
if (typeof retryAfter === 'number' && Number.isFinite(retryAfter) && retryAfter >= 0) {
|
|
9
|
+
return Math.round(retryAfter * 1000);
|
|
10
|
+
}
|
|
11
|
+
if (typeof retryAfter !== 'string') {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const asSeconds = Number(retryAfter);
|
|
15
|
+
if (Number.isFinite(asSeconds) && asSeconds >= 0) {
|
|
16
|
+
return Math.round(asSeconds * 1000);
|
|
17
|
+
}
|
|
18
|
+
const asDateMs = Date.parse(retryAfter);
|
|
19
|
+
if (!Number.isNaN(asDateMs)) {
|
|
20
|
+
return Math.max(0, asDateMs - Date.now());
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function getRateLimitFromHeaders(headers) {
|
|
24
|
+
const limit = (0, headerUtils_1.parseHeaderNumber)(headers, 'x-ratelimit-limit');
|
|
25
|
+
const remaining = (0, headerUtils_1.parseHeaderNumber)(headers, 'x-ratelimit-remaining');
|
|
26
|
+
const resetRaw = (0, headerUtils_1.parseHeaderNumber)(headers, 'x-ratelimit-reset');
|
|
27
|
+
if (limit === undefined || remaining === undefined || resetRaw === undefined) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
limit,
|
|
32
|
+
remaining,
|
|
33
|
+
reset: (0, headerUtils_1.normalizeResetToEpochMs)(resetRaw),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function isStreamHttpRateLimitError(error) {
|
|
37
|
+
return (typeof error === 'object' &&
|
|
38
|
+
error !== null &&
|
|
39
|
+
(('status' in error && error.status === 429) ||
|
|
40
|
+
('response' in error &&
|
|
41
|
+
typeof error.response === 'object' &&
|
|
42
|
+
error.response !== null &&
|
|
43
|
+
error.response?.status === 429)));
|
|
44
|
+
}
|
|
45
|
+
function processStreamRateLimitException(error, logger) {
|
|
46
|
+
if (isStreamHttpRateLimitError(error)) {
|
|
47
|
+
const headers = error.response?.headers ?? {};
|
|
48
|
+
const rateLimit = getRateLimitFromHeaders(headers);
|
|
49
|
+
const retryAfterMs = parseRetryAfterMs(headers);
|
|
50
|
+
const message = error.response?.data?.message || error.message || 'Rate limit exceeded';
|
|
51
|
+
logger.warn('Stream rate limit exceeded [HTTP 429]', {
|
|
52
|
+
status: error.status ?? error.response?.status,
|
|
53
|
+
retryAfterMs,
|
|
54
|
+
rateLimit,
|
|
55
|
+
});
|
|
56
|
+
return (0, createRateLimitExceededException_1.createRateLimitExceededException)({
|
|
57
|
+
rateLimit,
|
|
58
|
+
retryAfterMs,
|
|
59
|
+
message,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.processStreamRateLimitException = processStreamRateLimitException;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.processStreamRateLimitHeaders = void 0;
|
|
4
|
+
const constants_1 = require("../constants");
|
|
5
|
+
const headerUtils_1 = require("./headerUtils");
|
|
6
|
+
const RedisCooldown_1 = require("./RedisCooldown");
|
|
7
|
+
function computeSoftThrottleDelayMs(limit, remaining) {
|
|
8
|
+
if (limit <= 0) {
|
|
9
|
+
return 0;
|
|
10
|
+
}
|
|
11
|
+
const usage = 1 - remaining / limit;
|
|
12
|
+
if (usage >= constants_1.SOFT_THROTTLE_USAGE_HIGH) {
|
|
13
|
+
return constants_1.SOFT_THROTTLE_DELAY_HIGH_MS;
|
|
14
|
+
}
|
|
15
|
+
if (usage >= constants_1.SOFT_THROTTLE_USAGE_MEDIUM) {
|
|
16
|
+
return constants_1.SOFT_THROTTLE_DELAY_MEDIUM_MS;
|
|
17
|
+
}
|
|
18
|
+
if (usage >= constants_1.SOFT_THROTTLE_USAGE_LOW) {
|
|
19
|
+
return constants_1.SOFT_THROTTLE_DELAY_LOW_MS;
|
|
20
|
+
}
|
|
21
|
+
return 0;
|
|
22
|
+
}
|
|
23
|
+
async function processStreamRateLimitHeaders(headers, context) {
|
|
24
|
+
const { logger } = context;
|
|
25
|
+
const limit = (0, headerUtils_1.parseHeaderNumber)(headers ?? {}, 'x-ratelimit-limit');
|
|
26
|
+
const remaining = (0, headerUtils_1.parseHeaderNumber)(headers ?? {}, 'x-ratelimit-remaining');
|
|
27
|
+
if (limit === undefined || remaining === undefined) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (limit <= 0 || remaining < 0) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const delayMs = computeSoftThrottleDelayMs(limit, remaining);
|
|
34
|
+
if (delayMs > 0) {
|
|
35
|
+
await RedisCooldown_1.RedisCooldown.setRemainingMs(constants_1.STREAM_RATE_LIMIT_SOFT_COOLDOWN_KEY, delayMs, context, {
|
|
36
|
+
keyPrefix: constants_1.STREAM_RATE_LIMIT_COOLDOWN_KEY_PREFIX,
|
|
37
|
+
});
|
|
38
|
+
logger.debug('Enabling soft stream rate-limit cooldown', {
|
|
39
|
+
rateLimit: limit,
|
|
40
|
+
rateLimitRemaining: remaining,
|
|
41
|
+
cooldownMs: delayMs,
|
|
42
|
+
});
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
await RedisCooldown_1.RedisCooldown.clear(constants_1.STREAM_RATE_LIMIT_SOFT_COOLDOWN_KEY, context, {
|
|
46
|
+
keyPrefix: constants_1.STREAM_RATE_LIMIT_COOLDOWN_KEY_PREFIX,
|
|
47
|
+
});
|
|
48
|
+
logger.debug('Clearing soft stream rate-limit cooldown');
|
|
49
|
+
}
|
|
50
|
+
exports.processStreamRateLimitHeaders = processStreamRateLimitHeaders;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseRateLimitSnapshot = exports.serializeRateLimitSnapshot = void 0;
|
|
4
|
+
const RATE_LIMIT_SNAPSHOT_PARTS_COUNT = 3;
|
|
5
|
+
function serializeRateLimitSnapshot(snapshot) {
|
|
6
|
+
return `${snapshot.limit},${snapshot.remaining},${snapshot.reset}`;
|
|
7
|
+
}
|
|
8
|
+
exports.serializeRateLimitSnapshot = serializeRateLimitSnapshot;
|
|
9
|
+
function parseRateLimitSnapshot(snapshot) {
|
|
10
|
+
if (typeof snapshot !== 'string' || snapshot.length === 0) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const parts = snapshot.split(',');
|
|
14
|
+
if (parts.length !== RATE_LIMIT_SNAPSHOT_PARTS_COUNT) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const [limitRaw, remainingRaw, resetRaw] = parts;
|
|
18
|
+
const limit = Number(limitRaw);
|
|
19
|
+
const remaining = Number(remainingRaw);
|
|
20
|
+
const reset = Number(resetRaw);
|
|
21
|
+
if (!Number.isFinite(limit) ||
|
|
22
|
+
!Number.isFinite(remaining) ||
|
|
23
|
+
!Number.isFinite(reset) ||
|
|
24
|
+
limit < 0 ||
|
|
25
|
+
remaining < 0 ||
|
|
26
|
+
reset <= 0) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
return { limit, remaining, reset };
|
|
30
|
+
}
|
|
31
|
+
exports.parseRateLimitSnapshot = parseRateLimitSnapshot;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerStreamBudgetInterceptors = void 0;
|
|
4
|
+
const processStreamBudgetHeaders_1 = require("./processStreamBudgetHeaders");
|
|
5
|
+
const processStreamRateLimitHeaders_1 = require("./processStreamRateLimitHeaders");
|
|
6
|
+
const attachedInterceptors = new WeakSet();
|
|
7
|
+
function hasResponseInterceptors(value) {
|
|
8
|
+
return (typeof value === 'object' &&
|
|
9
|
+
value !== null &&
|
|
10
|
+
typeof value.interceptors?.response?.use === 'function');
|
|
11
|
+
}
|
|
12
|
+
function resolveAxiosInstance(stream) {
|
|
13
|
+
if (hasResponseInterceptors(stream)) {
|
|
14
|
+
return stream;
|
|
15
|
+
}
|
|
16
|
+
if (typeof stream !== 'object' || stream === null) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const maybeStream = stream;
|
|
20
|
+
if (hasResponseInterceptors(maybeStream.axiosInstance)) {
|
|
21
|
+
return maybeStream.axiosInstance;
|
|
22
|
+
}
|
|
23
|
+
if (hasResponseInterceptors(maybeStream._client?.axiosInstance)) {
|
|
24
|
+
return maybeStream._client?.axiosInstance;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function registerStreamBudgetInterceptors(stream, context) {
|
|
28
|
+
const { logger } = context;
|
|
29
|
+
const axiosInstance = resolveAxiosInstance(stream);
|
|
30
|
+
if (!axiosInstance || !axiosInstance.interceptors?.response?.use) {
|
|
31
|
+
logger.warn('Unable to register stream budget interceptor: axios instance not found');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (attachedInterceptors.has(axiosInstance)) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
axiosInstance.interceptors.response.use(async (response) => {
|
|
38
|
+
await (0, processStreamBudgetHeaders_1.processStreamBudgetHeaders)(response?.headers, context);
|
|
39
|
+
await (0, processStreamRateLimitHeaders_1.processStreamRateLimitHeaders)(response?.headers, context);
|
|
40
|
+
return response;
|
|
41
|
+
}, async (error) => {
|
|
42
|
+
await (0, processStreamBudgetHeaders_1.processStreamBudgetHeaders)(error?.response?.headers, context);
|
|
43
|
+
await (0, processStreamRateLimitHeaders_1.processStreamRateLimitHeaders)(error?.response?.headers, context);
|
|
44
|
+
return Promise.reject(error);
|
|
45
|
+
});
|
|
46
|
+
attachedInterceptors.add(axiosInstance);
|
|
47
|
+
}
|
|
48
|
+
exports.registerStreamBudgetInterceptors = registerStreamBudgetInterceptors;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerStreamInterceptors = void 0;
|
|
4
|
+
const processStreamBudgetHeaders_1 = require("./processStreamBudgetHeaders");
|
|
5
|
+
const processStreamRateLimitHeaders_1 = require("./processStreamRateLimitHeaders");
|
|
6
|
+
const attachedInterceptors = new WeakSet();
|
|
7
|
+
function hasResponseInterceptors(value) {
|
|
8
|
+
return (typeof value === 'object' &&
|
|
9
|
+
value !== null &&
|
|
10
|
+
typeof value.interceptors?.response?.use === 'function');
|
|
11
|
+
}
|
|
12
|
+
function resolveAxiosInstance(stream) {
|
|
13
|
+
if (hasResponseInterceptors(stream)) {
|
|
14
|
+
return stream;
|
|
15
|
+
}
|
|
16
|
+
if (typeof stream !== 'object' || stream === null) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const maybeStream = stream;
|
|
20
|
+
if (hasResponseInterceptors(maybeStream.axiosInstance)) {
|
|
21
|
+
return maybeStream.axiosInstance;
|
|
22
|
+
}
|
|
23
|
+
if (hasResponseInterceptors(maybeStream._client?.axiosInstance)) {
|
|
24
|
+
return maybeStream._client?.axiosInstance;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function registerStreamInterceptors(stream, context) {
|
|
28
|
+
const { logger } = context;
|
|
29
|
+
const axiosInstance = resolveAxiosInstance(stream);
|
|
30
|
+
if (!axiosInstance || !axiosInstance.interceptors?.response?.use) {
|
|
31
|
+
logger.warn('Unable to register stream budget & rate limit interceptors: axios instance not found');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (attachedInterceptors.has(axiosInstance)) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
attachedInterceptors.add(axiosInstance);
|
|
38
|
+
axiosInstance.interceptors.response.use(async (response) => {
|
|
39
|
+
try {
|
|
40
|
+
await (0, processStreamBudgetHeaders_1.processStreamBudgetHeaders)(response?.headers, context);
|
|
41
|
+
await (0, processStreamRateLimitHeaders_1.processStreamRateLimitHeaders)(response?.headers, context);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
logger.warn('Failed to process Stream response headers in interceptor', { error });
|
|
45
|
+
}
|
|
46
|
+
return response;
|
|
47
|
+
}, async (error) => {
|
|
48
|
+
try {
|
|
49
|
+
await (0, processStreamBudgetHeaders_1.processStreamBudgetHeaders)(error?.response?.headers, context);
|
|
50
|
+
await (0, processStreamRateLimitHeaders_1.processStreamRateLimitHeaders)(error?.response?.headers, context);
|
|
51
|
+
}
|
|
52
|
+
catch (interceptorError) {
|
|
53
|
+
logger.warn('Failed to process Stream error headers in interceptor', { error: interceptorError });
|
|
54
|
+
}
|
|
55
|
+
return Promise.reject(error);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
exports.registerStreamInterceptors = registerStreamInterceptors;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.splitCallArgsAndOptions = void 0;
|
|
4
|
+
const constants_1 = require("../constants");
|
|
5
|
+
function isStreamRateLimitOptionsToken(value) {
|
|
6
|
+
return typeof value === 'object' && value !== null && constants_1.STREAM_RATE_LIMIT_OPTIONS_MARKER in value;
|
|
7
|
+
}
|
|
8
|
+
function splitCallArgsAndOptions(args) {
|
|
9
|
+
if (args.length === 0) {
|
|
10
|
+
return { callArgs: args, callOptions: {} };
|
|
11
|
+
}
|
|
12
|
+
const lastArg = args[args.length - 1];
|
|
13
|
+
if (isStreamRateLimitOptionsToken(lastArg)) {
|
|
14
|
+
return {
|
|
15
|
+
callArgs: args.slice(0, -1),
|
|
16
|
+
callOptions: lastArg[constants_1.STREAM_RATE_LIMIT_OPTIONS_MARKER],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return { callArgs: args, callOptions: {} };
|
|
20
|
+
}
|
|
21
|
+
exports.splitCallArgsAndOptions = splitCallArgsAndOptions;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
tslib_1.__exportStar(require("./createRateLimitedStreamProxy"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./helpers/createRateLimitExceededException"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./helpers/processStreamRateLimitException"), exports);
|
|
7
|
+
tslib_1.__exportStar(require("./withStreamRateLimitOptions"), exports);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withStreamRateLimitOptions = void 0;
|
|
4
|
+
const constants_1 = require("./constants");
|
|
5
|
+
function withStreamRateLimitOptions(options) {
|
|
6
|
+
return {
|
|
7
|
+
[constants_1.STREAM_RATE_LIMIT_OPTIONS_MARKER]: options,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
exports.withStreamRateLimitOptions = withStreamRateLimitOptions;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_OPTIONS = exports.SOFT_THROTTLE_DELAY_HIGH_MS = exports.SOFT_THROTTLE_DELAY_MEDIUM_MS = exports.SOFT_THROTTLE_DELAY_LOW_MS = exports.SOFT_THROTTLE_USAGE_HIGH = exports.SOFT_THROTTLE_USAGE_MEDIUM = exports.SOFT_THROTTLE_USAGE_LOW = exports.BUDGET_COOLDOWN_SLEEP_CHUNK_MS = exports.BUDGET_MINIMAL_DELAY_MAX_MS = exports.BUDGET_MINIMAL_DELAY_MIN_MS = exports.BUDGET_RELAXED_DELAY_MAX_MS = exports.BUDGET_RELAXED_DELAY_MIN_MS = exports.BUDGET_RELAXED_USAGE_THRESHOLD = exports.BUDGET_CLEAR_USAGE_THRESHOLD = exports.BUDGET_DELAY_MAX_MS = exports.BUDGET_DELAY_MIN_MS = exports.BUDGET_THRESHOLD = exports.STREAM_BUDGET_COOLDOWN_KEY = exports.STREAM_RATE_LIMIT_METADATA_KEY_SUFFIX = exports.STREAM_RATE_LIMIT_SOFT_COOLDOWN_KEY = exports.STREAM_RATE_LIMIT_OPTIONS_MARKER = exports.STREAM_BUDGET_COOLDOWN_KEY_PREFIX = exports.STREAM_RATE_LIMIT_COOLDOWN_KEY_PREFIX = exports.RETRY_AFTER_BUFFER_MS = exports.DEFAULT_JITTER_MS = exports.DEFAULT_RETRY_AFTER_MS = exports.DEFAULT_MAX_DELAY_MS = exports.STREAM_SYNC_METHOD_NAMES = exports.STREAM_SYNC_CHANNEL_RESULT_METHOD_NAMES = exports.STREAM_METHOD_NAMES = exports.STREAM_CHANNEL_RESULT_METHOD_NAMES = exports.CHANNEL_SYNC_PROXY_RESULT_METHOD_NAMES = exports.CHANNEL_SYNC_METHOD_NAMES = exports.CHANNEL_METHOD_NAMES = void 0;
|
|
4
|
+
var generatedMethodNames_1 = require("./generatedMethodNames");
|
|
5
|
+
Object.defineProperty(exports, "CHANNEL_METHOD_NAMES", { enumerable: true, get: function () { return generatedMethodNames_1.CHANNEL_METHOD_NAMES; } });
|
|
6
|
+
Object.defineProperty(exports, "CHANNEL_SYNC_METHOD_NAMES", { enumerable: true, get: function () { return generatedMethodNames_1.CHANNEL_SYNC_METHOD_NAMES; } });
|
|
7
|
+
Object.defineProperty(exports, "CHANNEL_SYNC_PROXY_RESULT_METHOD_NAMES", { enumerable: true, get: function () { return generatedMethodNames_1.CHANNEL_SYNC_PROXY_RESULT_METHOD_NAMES; } });
|
|
8
|
+
Object.defineProperty(exports, "STREAM_CHANNEL_RESULT_METHOD_NAMES", { enumerable: true, get: function () { return generatedMethodNames_1.STREAM_CHANNEL_RESULT_METHOD_NAMES; } });
|
|
9
|
+
Object.defineProperty(exports, "STREAM_METHOD_NAMES", { enumerable: true, get: function () { return generatedMethodNames_1.STREAM_METHOD_NAMES; } });
|
|
10
|
+
Object.defineProperty(exports, "STREAM_SYNC_CHANNEL_RESULT_METHOD_NAMES", { enumerable: true, get: function () { return generatedMethodNames_1.STREAM_SYNC_CHANNEL_RESULT_METHOD_NAMES; } });
|
|
11
|
+
Object.defineProperty(exports, "STREAM_SYNC_METHOD_NAMES", { enumerable: true, get: function () { return generatedMethodNames_1.STREAM_SYNC_METHOD_NAMES; } });
|
|
12
|
+
exports.DEFAULT_MAX_DELAY_MS = 5000;
|
|
13
|
+
exports.DEFAULT_RETRY_AFTER_MS = 10000;
|
|
14
|
+
exports.DEFAULT_JITTER_MS = 150;
|
|
15
|
+
exports.RETRY_AFTER_BUFFER_MS = 1000;
|
|
16
|
+
exports.STREAM_RATE_LIMIT_COOLDOWN_KEY_PREFIX = 'stream-rate-limit-cooldown:';
|
|
17
|
+
exports.STREAM_BUDGET_COOLDOWN_KEY_PREFIX = 'stream-budget-cooldown:';
|
|
18
|
+
exports.STREAM_RATE_LIMIT_OPTIONS_MARKER = '__xbsRateLimitOptions';
|
|
19
|
+
exports.STREAM_RATE_LIMIT_SOFT_COOLDOWN_KEY = 'stream-rate-limit-soft-global';
|
|
20
|
+
exports.STREAM_RATE_LIMIT_METADATA_KEY_SUFFIX = ':meta';
|
|
21
|
+
exports.STREAM_BUDGET_COOLDOWN_KEY = 'stream-budget-global';
|
|
22
|
+
exports.BUDGET_THRESHOLD = 0.8;
|
|
23
|
+
exports.BUDGET_DELAY_MIN_MS = 30000;
|
|
24
|
+
exports.BUDGET_DELAY_MAX_MS = 60000;
|
|
25
|
+
exports.BUDGET_CLEAR_USAGE_THRESHOLD = 0.6;
|
|
26
|
+
exports.BUDGET_RELAXED_USAGE_THRESHOLD = 0.7;
|
|
27
|
+
exports.BUDGET_RELAXED_DELAY_MIN_MS = 5000;
|
|
28
|
+
exports.BUDGET_RELAXED_DELAY_MAX_MS = 10000;
|
|
29
|
+
exports.BUDGET_MINIMAL_DELAY_MIN_MS = 1000;
|
|
30
|
+
exports.BUDGET_MINIMAL_DELAY_MAX_MS = 2000;
|
|
31
|
+
exports.BUDGET_COOLDOWN_SLEEP_CHUNK_MS = 3000;
|
|
32
|
+
exports.SOFT_THROTTLE_USAGE_LOW = 0.7;
|
|
33
|
+
exports.SOFT_THROTTLE_USAGE_MEDIUM = 0.85;
|
|
34
|
+
exports.SOFT_THROTTLE_USAGE_HIGH = 0.95;
|
|
35
|
+
exports.SOFT_THROTTLE_DELAY_LOW_MS = 500;
|
|
36
|
+
exports.SOFT_THROTTLE_DELAY_MEDIUM_MS = 1500;
|
|
37
|
+
exports.SOFT_THROTTLE_DELAY_HIGH_MS = 3000;
|
|
38
|
+
exports.DEFAULT_OPTIONS = {
|
|
39
|
+
maxAttempts: 3,
|
|
40
|
+
enableCooldown: true,
|
|
41
|
+
maxDelayMs: 5000,
|
|
42
|
+
maxRetryableDelayMs: 10000,
|
|
43
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createRateLimitedStreamProxy = void 0;
|
|
4
|
+
const constants_1 = require("./constants");
|
|
5
|
+
const executeWithRateLimitHandling_1 = require("./helpers/executeWithRateLimitHandling");
|
|
6
|
+
const isStreamChannelLike_1 = require("./helpers/isStreamChannelLike");
|
|
7
|
+
const registerStreamInterceptors_1 = require("./helpers/registerStreamInterceptors");
|
|
8
|
+
const splitCallArgsAndOptions_1 = require("./helpers/splitCallArgsAndOptions");
|
|
9
|
+
const STREAM_SYNC_METHODS = new Set(constants_1.STREAM_SYNC_METHOD_NAMES);
|
|
10
|
+
const STREAM_CHANNEL_RESULT_METHODS = new Set(constants_1.STREAM_CHANNEL_RESULT_METHOD_NAMES);
|
|
11
|
+
const STREAM_SYNC_CHANNEL_RESULT_METHODS = new Set(constants_1.STREAM_SYNC_CHANNEL_RESULT_METHOD_NAMES);
|
|
12
|
+
const CHANNEL_SYNC_METHODS = new Set(constants_1.CHANNEL_SYNC_METHOD_NAMES);
|
|
13
|
+
const CHANNEL_SYNC_PROXY_RESULT_METHODS = new Set(constants_1.CHANNEL_SYNC_PROXY_RESULT_METHOD_NAMES);
|
|
14
|
+
function wrapKnownChannelResult(result, dependencies) {
|
|
15
|
+
if (Array.isArray(result)) {
|
|
16
|
+
return result.map((item) => wrapKnownChannelResult(item, dependencies));
|
|
17
|
+
}
|
|
18
|
+
if (typeof result !== 'object' || result === null) {
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
return wrapTarget(result, dependencies);
|
|
22
|
+
}
|
|
23
|
+
function normalizeStreamCallResult(result, dependencies, wrapAsChannelResult = false) {
|
|
24
|
+
if (wrapAsChannelResult) {
|
|
25
|
+
return wrapKnownChannelResult(result, dependencies);
|
|
26
|
+
}
|
|
27
|
+
if (Array.isArray(result)) {
|
|
28
|
+
return result.map((item) => normalizeStreamCallResult(item, dependencies));
|
|
29
|
+
}
|
|
30
|
+
if (!(0, isStreamChannelLike_1.isStreamChannelLike)(result)) {
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
return wrapTarget(result, dependencies);
|
|
34
|
+
}
|
|
35
|
+
function createRateLimitedMethodWrapper(target, value, operation, dependencies, wrapAsChannelResult) {
|
|
36
|
+
return async (...args) => {
|
|
37
|
+
const { callArgs, callOptions } = (0, splitCallArgsAndOptions_1.splitCallArgsAndOptions)(args);
|
|
38
|
+
const result = await (0, executeWithRateLimitHandling_1.executeWithRateLimitHandling)(() => Reflect.apply(value, target, callArgs), operation, dependencies.context, {
|
|
39
|
+
...constants_1.DEFAULT_OPTIONS,
|
|
40
|
+
...callOptions,
|
|
41
|
+
});
|
|
42
|
+
return normalizeStreamCallResult(result, dependencies, wrapAsChannelResult);
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function wrapTarget(target, dependencies) {
|
|
46
|
+
const cachedObject = dependencies.cache.get(target);
|
|
47
|
+
if (cachedObject) {
|
|
48
|
+
return cachedObject;
|
|
49
|
+
}
|
|
50
|
+
const proxy = new Proxy(target, {
|
|
51
|
+
get: (originalTarget, property, receiver) => {
|
|
52
|
+
const value = Reflect.get(originalTarget, property, receiver);
|
|
53
|
+
if (typeof property !== 'string' || typeof value !== 'function') {
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
if (STREAM_SYNC_CHANNEL_RESULT_METHODS.has(property)) {
|
|
57
|
+
return (...args) => {
|
|
58
|
+
const { callArgs } = (0, splitCallArgsAndOptions_1.splitCallArgsAndOptions)(args);
|
|
59
|
+
const result = Reflect.apply(value, originalTarget, callArgs);
|
|
60
|
+
return normalizeStreamCallResult(result, dependencies, true);
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (CHANNEL_SYNC_PROXY_RESULT_METHODS.has(property)) {
|
|
64
|
+
return (...args) => {
|
|
65
|
+
const { callArgs } = (0, splitCallArgsAndOptions_1.splitCallArgsAndOptions)(args);
|
|
66
|
+
const result = Reflect.apply(value, originalTarget, callArgs);
|
|
67
|
+
if (typeof result === 'object' && result !== null) {
|
|
68
|
+
return wrapTarget(result, dependencies);
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (STREAM_SYNC_METHODS.has(property) || CHANNEL_SYNC_METHODS.has(property)) {
|
|
74
|
+
return (...args) => {
|
|
75
|
+
const { callArgs } = (0, splitCallArgsAndOptions_1.splitCallArgsAndOptions)(args);
|
|
76
|
+
return Reflect.apply(value, originalTarget, callArgs);
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return createRateLimitedMethodWrapper(originalTarget, value, property, dependencies, STREAM_CHANNEL_RESULT_METHODS.has(property));
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
dependencies.cache.set(target, proxy);
|
|
83
|
+
return proxy;
|
|
84
|
+
}
|
|
85
|
+
function createRateLimitedStreamProxy(stream, context) {
|
|
86
|
+
(0, registerStreamInterceptors_1.registerStreamInterceptors)(stream, context);
|
|
87
|
+
return wrapTarget(stream, {
|
|
88
|
+
context,
|
|
89
|
+
cache: new WeakMap(),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
exports.createRateLimitedStreamProxy = createRateLimitedStreamProxy;
|