@wildix/xbees-conversations-utils 1.1.86 → 1.1.88
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cjs/streamRateLimitHandlingProxy/helpers/executeWithRateLimitHandling.js +1 -1
- package/dist-cjs/streamRateLimitHandlingProxy/helpers/processStreamRateLimitHeaders.js +26 -3
- package/dist-cjs/streamRateLimitHandlingProxy/helpers/registerStreamInterceptors.js +50 -8
- package/dist-es/streamRateLimitHandlingProxy/helpers/executeWithRateLimitHandling.js +1 -1
- package/dist-es/streamRateLimitHandlingProxy/helpers/processStreamRateLimitHeaders.js +26 -3
- package/dist-es/streamRateLimitHandlingProxy/helpers/registerStreamInterceptors.js +50 -8
- package/dist-types/streamRateLimitHandlingProxy/helpers/processStreamRateLimitHeaders.d.ts +1 -1
- package/dist-types/streamRateLimitHandlingProxy/types.d.ts +5 -0
- package/package.json +1 -1
|
@@ -70,7 +70,7 @@ async function executeWithRateLimitHandling(execute, operation, context, options
|
|
|
70
70
|
}
|
|
71
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
72
|
if (softCooldownMs > 0) {
|
|
73
|
-
logger.
|
|
73
|
+
logger.info('Delaying stream request due to active soft rate-limit cooldown', {
|
|
74
74
|
operation,
|
|
75
75
|
cooldown: softCooldownMs,
|
|
76
76
|
});
|
|
@@ -26,14 +26,28 @@ function computeSoftThrottleDelayMs(limit, remaining) {
|
|
|
26
26
|
}
|
|
27
27
|
return 0;
|
|
28
28
|
}
|
|
29
|
-
async function processStreamRateLimitHeaders(headers, context) {
|
|
29
|
+
async function processStreamRateLimitHeaders(headers, context, requestUrl) {
|
|
30
30
|
const { logger } = context;
|
|
31
31
|
const limit = (0, headerUtils_1.parseHeaderNumber)(headers ?? {}, 'x-ratelimit-limit');
|
|
32
32
|
const remaining = (0, headerUtils_1.parseHeaderNumber)(headers ?? {}, 'x-ratelimit-remaining');
|
|
33
33
|
if (limit === undefined || remaining === undefined) {
|
|
34
|
+
logger.info('Observed Stream soft throttle rate-limit state', {
|
|
35
|
+
requestUrl,
|
|
36
|
+
rateLimit: limit,
|
|
37
|
+
rateLimitRemaining: remaining,
|
|
38
|
+
rateLimitUsagePercent: undefined,
|
|
39
|
+
softThrottleHeadersAvailable: false,
|
|
40
|
+
});
|
|
34
41
|
return;
|
|
35
42
|
}
|
|
36
43
|
if (limit <= 0 || remaining < 0) {
|
|
44
|
+
logger.info('Observed Stream soft throttle rate-limit state', {
|
|
45
|
+
requestUrl,
|
|
46
|
+
rateLimit: limit,
|
|
47
|
+
rateLimitRemaining: remaining,
|
|
48
|
+
rateLimitUsagePercent: undefined,
|
|
49
|
+
softThrottleHeadersAvailable: false,
|
|
50
|
+
});
|
|
37
51
|
return;
|
|
38
52
|
}
|
|
39
53
|
const usage = computeSoftThrottleUsage(limit, remaining);
|
|
@@ -42,11 +56,20 @@ async function processStreamRateLimitHeaders(headers, context) {
|
|
|
42
56
|
const currentCooldownMs = await RedisCooldown_1.RedisCooldown.getRemainingMs(constants_1.STREAM_RATE_LIMIT_SOFT_COOLDOWN_KEY, context, {
|
|
43
57
|
keyPrefix: constants_1.STREAM_RATE_LIMIT_COOLDOWN_KEY_PREFIX,
|
|
44
58
|
});
|
|
59
|
+
logger.info('Observed Stream soft throttle rate-limit state', {
|
|
60
|
+
requestUrl,
|
|
61
|
+
rateLimit: limit,
|
|
62
|
+
rateLimitRemaining: remaining,
|
|
63
|
+
rateLimitUsage: usage,
|
|
64
|
+
rateLimitUsagePercent: usagePercent,
|
|
65
|
+
softThrottleCooldownMs: delayMs,
|
|
66
|
+
softThrottleActive: delayMs > 0,
|
|
67
|
+
});
|
|
45
68
|
if (delayMs > 0) {
|
|
46
69
|
await RedisCooldown_1.RedisCooldown.setRemainingMs(constants_1.STREAM_RATE_LIMIT_SOFT_COOLDOWN_KEY, delayMs, context, {
|
|
47
70
|
keyPrefix: constants_1.STREAM_RATE_LIMIT_COOLDOWN_KEY_PREFIX,
|
|
48
71
|
});
|
|
49
|
-
logger.
|
|
72
|
+
logger.info(currentCooldownMs > 0 ? 'Updating soft stream rate-limit cooldown' : 'Enabling soft stream rate-limit cooldown', {
|
|
50
73
|
rateLimit: limit,
|
|
51
74
|
rateLimitRemaining: remaining,
|
|
52
75
|
rateLimitUsage: usage,
|
|
@@ -60,7 +83,7 @@ async function processStreamRateLimitHeaders(headers, context) {
|
|
|
60
83
|
await RedisCooldown_1.RedisCooldown.clear(constants_1.STREAM_RATE_LIMIT_SOFT_COOLDOWN_KEY, context, {
|
|
61
84
|
keyPrefix: constants_1.STREAM_RATE_LIMIT_COOLDOWN_KEY_PREFIX,
|
|
62
85
|
});
|
|
63
|
-
logger.
|
|
86
|
+
logger.info('Clearing soft stream rate-limit cooldown', {
|
|
64
87
|
rateLimit: limit,
|
|
65
88
|
rateLimitRemaining: remaining,
|
|
66
89
|
rateLimitUsage: usage,
|
|
@@ -11,34 +11,73 @@ function hasResponseInterceptors(value) {
|
|
|
11
11
|
}
|
|
12
12
|
function resolveAxiosInstance(stream) {
|
|
13
13
|
if (hasResponseInterceptors(stream)) {
|
|
14
|
-
return
|
|
14
|
+
return {
|
|
15
|
+
axiosInstance: stream,
|
|
16
|
+
source: 'stream',
|
|
17
|
+
};
|
|
15
18
|
}
|
|
16
19
|
if (typeof stream !== 'object' || stream === null) {
|
|
17
|
-
return;
|
|
20
|
+
return {};
|
|
18
21
|
}
|
|
19
22
|
const maybeStream = stream;
|
|
20
23
|
if (hasResponseInterceptors(maybeStream.axiosInstance)) {
|
|
21
|
-
return
|
|
24
|
+
return {
|
|
25
|
+
axiosInstance: maybeStream.axiosInstance,
|
|
26
|
+
source: 'stream.axiosInstance',
|
|
27
|
+
};
|
|
22
28
|
}
|
|
23
29
|
if (hasResponseInterceptors(maybeStream._client?.axiosInstance)) {
|
|
24
|
-
return
|
|
30
|
+
return {
|
|
31
|
+
axiosInstance: maybeStream._client?.axiosInstance,
|
|
32
|
+
source: 'stream._client.axiosInstance',
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
function getOwnKeysPreview(value) {
|
|
38
|
+
if (typeof value !== 'object' || value === null) {
|
|
39
|
+
return;
|
|
25
40
|
}
|
|
41
|
+
return Object.keys(value).slice(0, 20);
|
|
42
|
+
}
|
|
43
|
+
function resolveRequestUrl(responseOrError) {
|
|
44
|
+
return responseOrError.config?.url ?? responseOrError.response?.config?.url;
|
|
26
45
|
}
|
|
27
46
|
function registerStreamInterceptors(stream, context) {
|
|
28
47
|
const { logger } = context;
|
|
29
|
-
const axiosInstance = resolveAxiosInstance(stream);
|
|
48
|
+
const { axiosInstance, source } = resolveAxiosInstance(stream);
|
|
30
49
|
if (!axiosInstance || !axiosInstance.interceptors?.response?.use) {
|
|
31
|
-
logger.warn('Unable to register stream budget & rate limit interceptors: axios instance not found'
|
|
50
|
+
logger.warn('Unable to register stream budget & rate limit interceptors: axios instance not found', {
|
|
51
|
+
streamConstructorName: typeof stream === 'object' && stream !== null ? stream.constructor?.name : undefined,
|
|
52
|
+
streamKeysPreview: getOwnKeysPreview(stream),
|
|
53
|
+
hasAxiosInstanceField: typeof stream === 'object' && stream !== null && 'axiosInstance' in stream,
|
|
54
|
+
hasNestedClientField: typeof stream === 'object' && stream !== null && '_client' in stream,
|
|
55
|
+
nestedClientKeysPreview: typeof stream === 'object' && stream !== null
|
|
56
|
+
? getOwnKeysPreview(stream._client)
|
|
57
|
+
: undefined,
|
|
58
|
+
});
|
|
32
59
|
return;
|
|
33
60
|
}
|
|
61
|
+
logger.info('Resolved axios instance for Stream interceptors', {
|
|
62
|
+
source,
|
|
63
|
+
streamConstructorName: typeof stream === 'object' && stream !== null ? stream.constructor?.name : undefined,
|
|
64
|
+
axiosConstructorName: typeof axiosInstance === 'object' && axiosInstance !== null ? axiosInstance.constructor?.name : undefined,
|
|
65
|
+
axiosKeysPreview: getOwnKeysPreview(axiosInstance),
|
|
66
|
+
});
|
|
34
67
|
if (attachedInterceptors.has(axiosInstance)) {
|
|
68
|
+
logger.info('Stream interceptors already attached', {
|
|
69
|
+
source,
|
|
70
|
+
});
|
|
35
71
|
return;
|
|
36
72
|
}
|
|
37
73
|
attachedInterceptors.add(axiosInstance);
|
|
74
|
+
logger.info('Attaching Stream response interceptors', {
|
|
75
|
+
source,
|
|
76
|
+
});
|
|
38
77
|
axiosInstance.interceptors.response.use(async (response) => {
|
|
39
78
|
try {
|
|
40
79
|
await (0, processStreamBudgetHeaders_1.processStreamBudgetHeaders)(response?.headers, context);
|
|
41
|
-
await (0, processStreamRateLimitHeaders_1.processStreamRateLimitHeaders)(response?.headers, context);
|
|
80
|
+
await (0, processStreamRateLimitHeaders_1.processStreamRateLimitHeaders)(response?.headers, context, resolveRequestUrl(response));
|
|
42
81
|
}
|
|
43
82
|
catch (error) {
|
|
44
83
|
logger.warn('Failed to process Stream response headers in interceptor', { error });
|
|
@@ -47,12 +86,15 @@ function registerStreamInterceptors(stream, context) {
|
|
|
47
86
|
}, async (error) => {
|
|
48
87
|
try {
|
|
49
88
|
await (0, processStreamBudgetHeaders_1.processStreamBudgetHeaders)(error?.response?.headers, context);
|
|
50
|
-
await (0, processStreamRateLimitHeaders_1.processStreamRateLimitHeaders)(error?.response?.headers, context);
|
|
89
|
+
await (0, processStreamRateLimitHeaders_1.processStreamRateLimitHeaders)(error?.response?.headers, context, resolveRequestUrl(error));
|
|
51
90
|
}
|
|
52
91
|
catch (interceptorError) {
|
|
53
92
|
logger.warn('Failed to process Stream error headers in interceptor', { error: interceptorError });
|
|
54
93
|
}
|
|
55
94
|
return Promise.reject(error);
|
|
56
95
|
});
|
|
96
|
+
logger.info('Stream response interceptors attached', {
|
|
97
|
+
source,
|
|
98
|
+
});
|
|
57
99
|
}
|
|
58
100
|
exports.registerStreamInterceptors = registerStreamInterceptors;
|
|
@@ -67,7 +67,7 @@ export async function executeWithRateLimitHandling(execute, operation, context,
|
|
|
67
67
|
}
|
|
68
68
|
const softCooldownMs = await RedisCooldown.getRemainingMs(STREAM_RATE_LIMIT_SOFT_COOLDOWN_KEY, { redis, logger }, { keyPrefix: STREAM_RATE_LIMIT_COOLDOWN_KEY_PREFIX });
|
|
69
69
|
if (softCooldownMs > 0) {
|
|
70
|
-
logger.
|
|
70
|
+
logger.info('Delaying stream request due to active soft rate-limit cooldown', {
|
|
71
71
|
operation,
|
|
72
72
|
cooldown: softCooldownMs,
|
|
73
73
|
});
|
|
@@ -23,14 +23,28 @@ function computeSoftThrottleDelayMs(limit, remaining) {
|
|
|
23
23
|
}
|
|
24
24
|
return 0;
|
|
25
25
|
}
|
|
26
|
-
export async function processStreamRateLimitHeaders(headers, context) {
|
|
26
|
+
export async function processStreamRateLimitHeaders(headers, context, requestUrl) {
|
|
27
27
|
const { logger } = context;
|
|
28
28
|
const limit = parseHeaderNumber(headers ?? {}, 'x-ratelimit-limit');
|
|
29
29
|
const remaining = parseHeaderNumber(headers ?? {}, 'x-ratelimit-remaining');
|
|
30
30
|
if (limit === undefined || remaining === undefined) {
|
|
31
|
+
logger.info('Observed Stream soft throttle rate-limit state', {
|
|
32
|
+
requestUrl,
|
|
33
|
+
rateLimit: limit,
|
|
34
|
+
rateLimitRemaining: remaining,
|
|
35
|
+
rateLimitUsagePercent: undefined,
|
|
36
|
+
softThrottleHeadersAvailable: false,
|
|
37
|
+
});
|
|
31
38
|
return;
|
|
32
39
|
}
|
|
33
40
|
if (limit <= 0 || remaining < 0) {
|
|
41
|
+
logger.info('Observed Stream soft throttle rate-limit state', {
|
|
42
|
+
requestUrl,
|
|
43
|
+
rateLimit: limit,
|
|
44
|
+
rateLimitRemaining: remaining,
|
|
45
|
+
rateLimitUsagePercent: undefined,
|
|
46
|
+
softThrottleHeadersAvailable: false,
|
|
47
|
+
});
|
|
34
48
|
return;
|
|
35
49
|
}
|
|
36
50
|
const usage = computeSoftThrottleUsage(limit, remaining);
|
|
@@ -39,11 +53,20 @@ export async function processStreamRateLimitHeaders(headers, context) {
|
|
|
39
53
|
const currentCooldownMs = await RedisCooldown.getRemainingMs(STREAM_RATE_LIMIT_SOFT_COOLDOWN_KEY, context, {
|
|
40
54
|
keyPrefix: STREAM_RATE_LIMIT_COOLDOWN_KEY_PREFIX,
|
|
41
55
|
});
|
|
56
|
+
logger.info('Observed Stream soft throttle rate-limit state', {
|
|
57
|
+
requestUrl,
|
|
58
|
+
rateLimit: limit,
|
|
59
|
+
rateLimitRemaining: remaining,
|
|
60
|
+
rateLimitUsage: usage,
|
|
61
|
+
rateLimitUsagePercent: usagePercent,
|
|
62
|
+
softThrottleCooldownMs: delayMs,
|
|
63
|
+
softThrottleActive: delayMs > 0,
|
|
64
|
+
});
|
|
42
65
|
if (delayMs > 0) {
|
|
43
66
|
await RedisCooldown.setRemainingMs(STREAM_RATE_LIMIT_SOFT_COOLDOWN_KEY, delayMs, context, {
|
|
44
67
|
keyPrefix: STREAM_RATE_LIMIT_COOLDOWN_KEY_PREFIX,
|
|
45
68
|
});
|
|
46
|
-
logger.
|
|
69
|
+
logger.info(currentCooldownMs > 0 ? 'Updating soft stream rate-limit cooldown' : 'Enabling soft stream rate-limit cooldown', {
|
|
47
70
|
rateLimit: limit,
|
|
48
71
|
rateLimitRemaining: remaining,
|
|
49
72
|
rateLimitUsage: usage,
|
|
@@ -57,7 +80,7 @@ export async function processStreamRateLimitHeaders(headers, context) {
|
|
|
57
80
|
await RedisCooldown.clear(STREAM_RATE_LIMIT_SOFT_COOLDOWN_KEY, context, {
|
|
58
81
|
keyPrefix: STREAM_RATE_LIMIT_COOLDOWN_KEY_PREFIX,
|
|
59
82
|
});
|
|
60
|
-
logger.
|
|
83
|
+
logger.info('Clearing soft stream rate-limit cooldown', {
|
|
61
84
|
rateLimit: limit,
|
|
62
85
|
rateLimitRemaining: remaining,
|
|
63
86
|
rateLimitUsage: usage,
|
|
@@ -8,34 +8,73 @@ function hasResponseInterceptors(value) {
|
|
|
8
8
|
}
|
|
9
9
|
function resolveAxiosInstance(stream) {
|
|
10
10
|
if (hasResponseInterceptors(stream)) {
|
|
11
|
-
return
|
|
11
|
+
return {
|
|
12
|
+
axiosInstance: stream,
|
|
13
|
+
source: 'stream',
|
|
14
|
+
};
|
|
12
15
|
}
|
|
13
16
|
if (typeof stream !== 'object' || stream === null) {
|
|
14
|
-
return;
|
|
17
|
+
return {};
|
|
15
18
|
}
|
|
16
19
|
const maybeStream = stream;
|
|
17
20
|
if (hasResponseInterceptors(maybeStream.axiosInstance)) {
|
|
18
|
-
return
|
|
21
|
+
return {
|
|
22
|
+
axiosInstance: maybeStream.axiosInstance,
|
|
23
|
+
source: 'stream.axiosInstance',
|
|
24
|
+
};
|
|
19
25
|
}
|
|
20
26
|
if (hasResponseInterceptors(maybeStream._client?.axiosInstance)) {
|
|
21
|
-
return
|
|
27
|
+
return {
|
|
28
|
+
axiosInstance: maybeStream._client?.axiosInstance,
|
|
29
|
+
source: 'stream._client.axiosInstance',
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
34
|
+
function getOwnKeysPreview(value) {
|
|
35
|
+
if (typeof value !== 'object' || value === null) {
|
|
36
|
+
return;
|
|
22
37
|
}
|
|
38
|
+
return Object.keys(value).slice(0, 20);
|
|
39
|
+
}
|
|
40
|
+
function resolveRequestUrl(responseOrError) {
|
|
41
|
+
return responseOrError.config?.url ?? responseOrError.response?.config?.url;
|
|
23
42
|
}
|
|
24
43
|
export function registerStreamInterceptors(stream, context) {
|
|
25
44
|
const { logger } = context;
|
|
26
|
-
const axiosInstance = resolveAxiosInstance(stream);
|
|
45
|
+
const { axiosInstance, source } = resolveAxiosInstance(stream);
|
|
27
46
|
if (!axiosInstance || !axiosInstance.interceptors?.response?.use) {
|
|
28
|
-
logger.warn('Unable to register stream budget & rate limit interceptors: axios instance not found'
|
|
47
|
+
logger.warn('Unable to register stream budget & rate limit interceptors: axios instance not found', {
|
|
48
|
+
streamConstructorName: typeof stream === 'object' && stream !== null ? stream.constructor?.name : undefined,
|
|
49
|
+
streamKeysPreview: getOwnKeysPreview(stream),
|
|
50
|
+
hasAxiosInstanceField: typeof stream === 'object' && stream !== null && 'axiosInstance' in stream,
|
|
51
|
+
hasNestedClientField: typeof stream === 'object' && stream !== null && '_client' in stream,
|
|
52
|
+
nestedClientKeysPreview: typeof stream === 'object' && stream !== null
|
|
53
|
+
? getOwnKeysPreview(stream._client)
|
|
54
|
+
: undefined,
|
|
55
|
+
});
|
|
29
56
|
return;
|
|
30
57
|
}
|
|
58
|
+
logger.info('Resolved axios instance for Stream interceptors', {
|
|
59
|
+
source,
|
|
60
|
+
streamConstructorName: typeof stream === 'object' && stream !== null ? stream.constructor?.name : undefined,
|
|
61
|
+
axiosConstructorName: typeof axiosInstance === 'object' && axiosInstance !== null ? axiosInstance.constructor?.name : undefined,
|
|
62
|
+
axiosKeysPreview: getOwnKeysPreview(axiosInstance),
|
|
63
|
+
});
|
|
31
64
|
if (attachedInterceptors.has(axiosInstance)) {
|
|
65
|
+
logger.info('Stream interceptors already attached', {
|
|
66
|
+
source,
|
|
67
|
+
});
|
|
32
68
|
return;
|
|
33
69
|
}
|
|
34
70
|
attachedInterceptors.add(axiosInstance);
|
|
71
|
+
logger.info('Attaching Stream response interceptors', {
|
|
72
|
+
source,
|
|
73
|
+
});
|
|
35
74
|
axiosInstance.interceptors.response.use(async (response) => {
|
|
36
75
|
try {
|
|
37
76
|
await processStreamBudgetHeaders(response?.headers, context);
|
|
38
|
-
await processStreamRateLimitHeaders(response?.headers, context);
|
|
77
|
+
await processStreamRateLimitHeaders(response?.headers, context, resolveRequestUrl(response));
|
|
39
78
|
}
|
|
40
79
|
catch (error) {
|
|
41
80
|
logger.warn('Failed to process Stream response headers in interceptor', { error });
|
|
@@ -44,11 +83,14 @@ export function registerStreamInterceptors(stream, context) {
|
|
|
44
83
|
}, async (error) => {
|
|
45
84
|
try {
|
|
46
85
|
await processStreamBudgetHeaders(error?.response?.headers, context);
|
|
47
|
-
await processStreamRateLimitHeaders(error?.response?.headers, context);
|
|
86
|
+
await processStreamRateLimitHeaders(error?.response?.headers, context, resolveRequestUrl(error));
|
|
48
87
|
}
|
|
49
88
|
catch (interceptorError) {
|
|
50
89
|
logger.warn('Failed to process Stream error headers in interceptor', { error: interceptorError });
|
|
51
90
|
}
|
|
52
91
|
return Promise.reject(error);
|
|
53
92
|
});
|
|
93
|
+
logger.info('Stream response interceptors attached', {
|
|
94
|
+
source,
|
|
95
|
+
});
|
|
54
96
|
}
|
|
@@ -3,4 +3,4 @@ import { LoggerContext, RedisContext, StreamHeaders } from '../types';
|
|
|
3
3
|
* Applies soft global throttle from x-ratelimit-* headers to smooth bursts
|
|
4
4
|
* before hard 429 throttling starts.
|
|
5
5
|
*/
|
|
6
|
-
export declare function processStreamRateLimitHeaders(headers: StreamHeaders | undefined, context: LoggerContext & RedisContext): Promise<void>;
|
|
6
|
+
export declare function processStreamRateLimitHeaders(headers: StreamHeaders | undefined, context: LoggerContext & RedisContext, requestUrl?: string): Promise<void>;
|
|
@@ -30,6 +30,9 @@ export type RedisCooldownOptions = {
|
|
|
30
30
|
};
|
|
31
31
|
/** Generic HTTP headers shape returned by the Stream SDK transport layer. */
|
|
32
32
|
export type StreamHeaders = Record<string, unknown>;
|
|
33
|
+
export type StreamRequestConfig = {
|
|
34
|
+
url?: string;
|
|
35
|
+
};
|
|
33
36
|
/** Parsed GetStream budget headers used to manage app-wide cooldown. */
|
|
34
37
|
export type StreamBudgetHeaders = {
|
|
35
38
|
limitMs?: number;
|
|
@@ -44,9 +47,11 @@ export type StreamRateLimitSnapshot = {
|
|
|
44
47
|
/** Axios response shape used by budget interceptor. */
|
|
45
48
|
export type StreamBudgetInterceptorResponse = {
|
|
46
49
|
headers?: StreamHeaders;
|
|
50
|
+
config?: StreamRequestConfig;
|
|
47
51
|
};
|
|
48
52
|
/** Axios error shape used by budget interceptor. */
|
|
49
53
|
export type StreamBudgetInterceptorError = {
|
|
54
|
+
config?: StreamRequestConfig;
|
|
50
55
|
response?: StreamBudgetInterceptorResponse;
|
|
51
56
|
};
|
|
52
57
|
/** Minimal axios response interceptor manager contract required by this package. */
|