@teneo-protocol/sdk 1.0.0 → 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.
- package/dist/core/websocket-client.d.ts +1 -0
- package/dist/core/websocket-client.d.ts.map +1 -1
- package/dist/core/websocket-client.js +12 -1
- package/dist/core/websocket-client.js.map +1 -1
- package/dist/handlers/message-handlers/regular-message-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/regular-message-handler.js +1 -0
- package/dist/handlers/message-handlers/regular-message-handler.js.map +1 -1
- package/dist/managers/message-router.d.ts +1 -1
- package/dist/managers/message-router.d.ts.map +1 -1
- package/dist/managers/message-router.js +41 -4
- package/dist/managers/message-router.js.map +1 -1
- package/dist/managers/room-manager.d.ts.map +1 -1
- package/dist/managers/room-manager.js +1 -1
- package/dist/managers/room-manager.js.map +1 -1
- package/dist/teneo-sdk.d.ts +29 -1
- package/dist/teneo-sdk.d.ts.map +1 -1
- package/dist/teneo-sdk.js +29 -6
- package/dist/teneo-sdk.js.map +1 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +1 -1
- package/dist/types/config.js.map +1 -1
- package/dist/types/validation.d.ts.map +1 -1
- package/dist/types/validation.js +1 -1
- package/dist/types/validation.js.map +1 -1
- package/dist/utils/bounded-queue.d.ts +1 -1
- package/dist/utils/bounded-queue.js +6 -6
- package/dist/utils/circuit-breaker.d.ts.map +1 -1
- package/dist/utils/circuit-breaker.js.map +1 -1
- package/dist/utils/event-waiter.d.ts.map +1 -1
- package/dist/utils/event-waiter.js +2 -1
- package/dist/utils/event-waiter.js.map +1 -1
- package/dist/utils/rate-limiter.d.ts.map +1 -1
- package/dist/utils/rate-limiter.js +4 -6
- package/dist/utils/rate-limiter.js.map +1 -1
- package/dist/utils/secure-private-key.d.ts.map +1 -1
- package/dist/utils/secure-private-key.js +9 -15
- package/dist/utils/secure-private-key.js.map +1 -1
- package/dist/utils/signature-verifier.d.ts +2 -2
- package/dist/utils/signature-verifier.d.ts.map +1 -1
- package/dist/utils/signature-verifier.js +5 -5
- package/dist/utils/signature-verifier.js.map +1 -1
- package/examples/claude-agent-x-follower/.env.example +117 -0
- package/examples/claude-agent-x-follower/QUICKSTART.md +243 -0
- package/examples/claude-agent-x-follower/README.md +540 -0
- package/examples/claude-agent-x-follower/index.ts +248 -0
- package/examples/claude-agent-x-follower/package.json +37 -0
- package/examples/claude-agent-x-follower/tsconfig.json +20 -0
- package/examples/n8n-teneo/.env.example +127 -0
- package/examples/n8n-teneo/Dockerfile +42 -0
- package/examples/n8n-teneo/README.md +564 -0
- package/examples/n8n-teneo/docker-compose.yml +71 -0
- package/examples/n8n-teneo/index.ts +177 -0
- package/examples/n8n-teneo/package.json +22 -0
- package/examples/n8n-teneo/tsconfig.json +12 -0
- package/examples/n8n-teneo/workflows/x-timeline.json +66 -0
- package/examples/openai-teneo/.env.example +130 -0
- package/examples/openai-teneo/README.md +635 -0
- package/examples/openai-teneo/index.ts +280 -0
- package/examples/openai-teneo/package.json +24 -0
- package/examples/openai-teneo/tsconfig.json +16 -0
- package/examples/production-dashboard/.env.example +5 -3
- package/examples/production-dashboard/README.md +762 -0
- package/examples/production-dashboard/pnpm-lock.yaml +92 -0
- package/examples/production-dashboard/public/dashboard.html +84 -10
- package/examples/production-dashboard/server.ts +83 -9
- package/examples/usage/.env.example +17 -0
- package/examples/usage/01-connect.ts +116 -0
- package/examples/usage/02-list-agents.ts +153 -0
- package/examples/usage/03-pick-agent.ts +201 -0
- package/examples/usage/04-find-by-capability.ts +237 -0
- package/examples/usage/05-webhook-example.ts +319 -0
- package/examples/usage/06-simple-api-server.ts +396 -0
- package/examples/usage/07-event-listener.ts +402 -0
- package/examples/usage/README.md +383 -0
- package/examples/usage/package.json +42 -0
- package/package.json +5 -3
- package/src/core/websocket-client.ts +17 -7
- package/src/formatters/response-formatter.test.ts +8 -2
- package/src/handlers/message-handlers/regular-message-handler.ts +1 -0
- package/src/handlers/webhook-handler.test.ts +13 -10
- package/src/managers/message-router.ts +48 -6
- package/src/managers/room-manager.ts +9 -2
- package/src/teneo-sdk.ts +39 -7
- package/src/types/config.ts +3 -1
- package/src/types/validation.ts +4 -1
- package/src/utils/bounded-queue.ts +9 -9
- package/src/utils/circuit-breaker.ts +4 -1
- package/src/utils/deduplication-cache.test.ts +2 -6
- package/src/utils/event-waiter.test.ts +4 -1
- package/src/utils/event-waiter.ts +5 -7
- package/src/utils/rate-limiter.test.ts +5 -17
- package/src/utils/rate-limiter.ts +6 -9
- package/src/utils/secure-private-key.test.ts +66 -59
- package/src/utils/secure-private-key.ts +10 -16
- package/src/utils/signature-verifier.test.ts +75 -70
- package/src/utils/signature-verifier.ts +7 -8
- package/src/utils/ssrf-validator.test.ts +3 -3
|
@@ -5,7 +5,14 @@
|
|
|
5
5
|
|
|
6
6
|
import { EventEmitter } from "eventemitter3";
|
|
7
7
|
import { WebSocketClient } from "../core/websocket-client";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
Room,
|
|
10
|
+
createSubscribe,
|
|
11
|
+
createUnsubscribe,
|
|
12
|
+
createListRooms,
|
|
13
|
+
Logger,
|
|
14
|
+
RoomInfo
|
|
15
|
+
} from "../types";
|
|
9
16
|
import { SDKEvents, SDKError } from "../types/events";
|
|
10
17
|
import { ErrorCode } from "../types/error-codes";
|
|
11
18
|
import { RoomIdSchema } from "../types/validation";
|
|
@@ -98,7 +105,7 @@ export class RoomManager extends EventEmitter<SDKEvents> {
|
|
|
98
105
|
*/
|
|
99
106
|
public updateSubscriptions(subscriptions: string[]): void {
|
|
100
107
|
this.subscribedRooms.clear();
|
|
101
|
-
subscriptions.forEach(roomId => this.subscribedRooms.add(roomId));
|
|
108
|
+
subscriptions.forEach((roomId) => this.subscribedRooms.add(roomId));
|
|
102
109
|
this.logger.debug("RoomManager: Subscriptions updated", {
|
|
103
110
|
count: subscriptions.length,
|
|
104
111
|
rooms: subscriptions
|
package/src/teneo-sdk.ts
CHANGED
|
@@ -290,8 +290,11 @@ export class TeneoSDK extends EventEmitter<SDKEvents> {
|
|
|
290
290
|
* });
|
|
291
291
|
* ```
|
|
292
292
|
*/
|
|
293
|
-
public async sendDirectCommand(
|
|
294
|
-
|
|
293
|
+
public async sendDirectCommand(
|
|
294
|
+
command: AgentCommand,
|
|
295
|
+
waitForResponse: boolean = false
|
|
296
|
+
): Promise<FormattedResponse | void> {
|
|
297
|
+
return this.messages.sendDirectCommand(command, waitForResponse);
|
|
295
298
|
}
|
|
296
299
|
|
|
297
300
|
/**
|
|
@@ -719,6 +722,39 @@ export class TeneoSDK extends EventEmitter<SDKEvents> {
|
|
|
719
722
|
};
|
|
720
723
|
}
|
|
721
724
|
|
|
725
|
+
/**
|
|
726
|
+
* Gets the current message deduplication cache status (CB-4).
|
|
727
|
+
* Returns statistics about the deduplication cache including size, TTL, and capacity.
|
|
728
|
+
* Useful for monitoring deduplication behavior and cache health.
|
|
729
|
+
* Returns undefined if deduplication is not configured or disabled.
|
|
730
|
+
*
|
|
731
|
+
* @returns Deduplication cache status object, or undefined if not configured
|
|
732
|
+
* @returns {number} returns.cacheSize - Number of message IDs currently cached
|
|
733
|
+
* @returns {number} returns.ttl - Time-to-live for cache entries in milliseconds
|
|
734
|
+
* @returns {number} returns.maxSize - Maximum cache size capacity
|
|
735
|
+
*
|
|
736
|
+
* @example
|
|
737
|
+
* ```typescript
|
|
738
|
+
* const status = sdk.getDeduplicationStatus();
|
|
739
|
+
* if (status) {
|
|
740
|
+
* console.log(`Cache: ${status.cacheSize}/${status.maxSize}`);
|
|
741
|
+
* console.log(`Utilization: ${(status.cacheSize / status.maxSize * 100).toFixed(1)}%`);
|
|
742
|
+
* console.log(`TTL: ${status.ttl}ms`);
|
|
743
|
+
* } else {
|
|
744
|
+
* console.log('Deduplication not enabled');
|
|
745
|
+
* }
|
|
746
|
+
* ```
|
|
747
|
+
*/
|
|
748
|
+
public getDeduplicationStatus():
|
|
749
|
+
| {
|
|
750
|
+
cacheSize: number;
|
|
751
|
+
ttl: number;
|
|
752
|
+
maxSize: number;
|
|
753
|
+
}
|
|
754
|
+
| undefined {
|
|
755
|
+
return this.wsClient.getDeduplicationStatus();
|
|
756
|
+
}
|
|
757
|
+
|
|
722
758
|
/**
|
|
723
759
|
* Retries all failed webhook deliveries in the queue.
|
|
724
760
|
* Resets attempt counters and immediately attempts to deliver all failed webhooks.
|
|
@@ -953,14 +989,10 @@ export class TeneoSDK extends EventEmitter<SDKEvents> {
|
|
|
953
989
|
this.agents.updateAgents(agents);
|
|
954
990
|
});
|
|
955
991
|
|
|
956
|
-
// Forward room events from WebSocketClient (emitted by handlers)
|
|
992
|
+
// Forward room events from WebSocketClient (emitted by room subscription handlers)
|
|
957
993
|
this.wsClient.on("room:subscribed", (data) => this.emit("room:subscribed", data));
|
|
958
994
|
this.wsClient.on("room:unsubscribed", (data) => this.emit("room:unsubscribed", data));
|
|
959
995
|
|
|
960
|
-
// Forward room events from RoomManager (if any direct emissions are added later)
|
|
961
|
-
this.rooms.on("room:subscribed", (data) => this.emit("room:subscribed", data));
|
|
962
|
-
this.rooms.on("room:unsubscribed", (data) => this.emit("room:unsubscribed", data));
|
|
963
|
-
|
|
964
996
|
// Forward webhook events from WebhookHandler
|
|
965
997
|
this.webhookHandler.on("webhook:sent", (payload, url) =>
|
|
966
998
|
this.emit("webhook:sent", payload, url)
|
package/src/types/config.ts
CHANGED
|
@@ -47,7 +47,9 @@ const PrivateKeySchema = z.union([
|
|
|
47
47
|
z.custom<SecurePrivateKey>(
|
|
48
48
|
(val) => {
|
|
49
49
|
// Check if it's a SecurePrivateKey instance
|
|
50
|
-
return
|
|
50
|
+
return (
|
|
51
|
+
val && typeof val === "object" && "use" in val && "destroy" in val && "isDestroyed" in val
|
|
52
|
+
);
|
|
51
53
|
},
|
|
52
54
|
{ message: "Must be a string or SecurePrivateKey instance" }
|
|
53
55
|
)
|
package/src/types/validation.ts
CHANGED
|
@@ -24,7 +24,10 @@ export const RoomIdSchema = z
|
|
|
24
24
|
.string()
|
|
25
25
|
.min(1, "Room ID cannot be empty")
|
|
26
26
|
.max(100, "Room ID must be 100 characters or less")
|
|
27
|
-
.regex(
|
|
27
|
+
.regex(
|
|
28
|
+
/^[a-zA-Z0-9_ -]+$/,
|
|
29
|
+
"Room ID can only contain letters, numbers, spaces, dashes, and underscores"
|
|
30
|
+
)
|
|
28
31
|
.trim();
|
|
29
32
|
|
|
30
33
|
/**
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
* Strategy for handling queue overflow when at max capacity
|
|
12
12
|
*/
|
|
13
13
|
export type OverflowStrategy =
|
|
14
|
-
|
|
|
15
|
-
|
|
|
16
|
-
|
|
|
14
|
+
| "drop-oldest" // Remove oldest item to make room (FIFO eviction)
|
|
15
|
+
| "drop-newest" // Reject new item, keep existing (preserve old data)
|
|
16
|
+
| "reject"; // Throw error, let caller handle (fail-fast)
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* A queue with a maximum size limit and configurable overflow behavior
|
|
@@ -48,10 +48,10 @@ export class BoundedQueue<T> {
|
|
|
48
48
|
*/
|
|
49
49
|
constructor(
|
|
50
50
|
private readonly maxSize: number,
|
|
51
|
-
private readonly strategy: OverflowStrategy =
|
|
51
|
+
private readonly strategy: OverflowStrategy = "drop-oldest"
|
|
52
52
|
) {
|
|
53
53
|
if (maxSize < 1) {
|
|
54
|
-
throw new Error(
|
|
54
|
+
throw new Error("BoundedQueue maxSize must be at least 1");
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -165,17 +165,17 @@ export class BoundedQueue<T> {
|
|
|
165
165
|
*/
|
|
166
166
|
private handleOverflow(item: T): boolean {
|
|
167
167
|
switch (this.strategy) {
|
|
168
|
-
case
|
|
168
|
+
case "drop-oldest":
|
|
169
169
|
// Remove oldest item and add new one
|
|
170
170
|
this.queue.shift();
|
|
171
171
|
this.queue.push(item);
|
|
172
172
|
return true;
|
|
173
173
|
|
|
174
|
-
case
|
|
174
|
+
case "drop-newest":
|
|
175
175
|
// Reject the new item
|
|
176
176
|
return false;
|
|
177
177
|
|
|
178
|
-
case
|
|
178
|
+
case "reject":
|
|
179
179
|
// Throw error - let caller handle
|
|
180
180
|
throw new QueueOverflowError(
|
|
181
181
|
`Queue is full (max size: ${this.maxSize}). Cannot add more items.`
|
|
@@ -195,7 +195,7 @@ export class BoundedQueue<T> {
|
|
|
195
195
|
export class QueueOverflowError extends Error {
|
|
196
196
|
constructor(message: string) {
|
|
197
197
|
super(message);
|
|
198
|
-
this.name =
|
|
198
|
+
this.name = "QueueOverflowError";
|
|
199
199
|
|
|
200
200
|
// Maintains proper stack trace for where error was thrown (V8 only)
|
|
201
201
|
if (Error.captureStackTrace) {
|
|
@@ -31,7 +31,10 @@ export interface CircuitBreakerOptions {
|
|
|
31
31
|
* Error thrown when circuit is open
|
|
32
32
|
*/
|
|
33
33
|
export class CircuitBreakerError extends Error {
|
|
34
|
-
constructor(
|
|
34
|
+
constructor(
|
|
35
|
+
message: string,
|
|
36
|
+
public readonly state: CircuitState
|
|
37
|
+
) {
|
|
35
38
|
super(message);
|
|
36
39
|
this.name = "CircuitBreakerError";
|
|
37
40
|
|
|
@@ -25,15 +25,11 @@ describe("DeduplicationCache", () => {
|
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
it("should throw error if TTL is less than 1000ms", () => {
|
|
28
|
-
expect(() => new DeduplicationCache(999)).toThrow(
|
|
29
|
-
"TTL must be at least 1000ms"
|
|
30
|
-
);
|
|
28
|
+
expect(() => new DeduplicationCache(999)).toThrow("TTL must be at least 1000ms");
|
|
31
29
|
});
|
|
32
30
|
|
|
33
31
|
it("should throw error if maxSize is less than 1", () => {
|
|
34
|
-
expect(() => new DeduplicationCache(5000, 0)).toThrow(
|
|
35
|
-
"maxSize must be at least 1"
|
|
36
|
-
);
|
|
32
|
+
expect(() => new DeduplicationCache(5000, 0)).toThrow("maxSize must be at least 1");
|
|
37
33
|
});
|
|
38
34
|
});
|
|
39
35
|
|
|
@@ -376,6 +376,9 @@ describe("waitForAllEvents", () => {
|
|
|
376
376
|
emitter.emit("data", { id: 1, value: "first" });
|
|
377
377
|
|
|
378
378
|
const results = await promise;
|
|
379
|
-
expect(results).toEqual([
|
|
379
|
+
expect(results).toEqual([
|
|
380
|
+
{ id: 1, value: "first" },
|
|
381
|
+
{ id: 2, value: "second" }
|
|
382
|
+
]);
|
|
380
383
|
});
|
|
381
384
|
});
|
|
@@ -102,7 +102,9 @@ export async function waitForEvent<T = any>(
|
|
|
102
102
|
// Timeout handler - rejects after timeout
|
|
103
103
|
timeoutHandle = setTimeout(() => {
|
|
104
104
|
cleanup();
|
|
105
|
-
const message =
|
|
105
|
+
const message =
|
|
106
|
+
options.timeoutMessage ||
|
|
107
|
+
`Timeout waiting for event '${eventName}' after ${options.timeout}ms`;
|
|
106
108
|
reject(new Error(message));
|
|
107
109
|
}, options.timeout);
|
|
108
110
|
|
|
@@ -135,9 +137,7 @@ export async function waitForAnyEvent<T = any>(
|
|
|
135
137
|
}>
|
|
136
138
|
): Promise<T> {
|
|
137
139
|
return Promise.race(
|
|
138
|
-
waiters.map(({ emitter, eventName, options }) =>
|
|
139
|
-
waitForEvent<T>(emitter, eventName, options)
|
|
140
|
-
)
|
|
140
|
+
waiters.map(({ emitter, eventName, options }) => waitForEvent<T>(emitter, eventName, options))
|
|
141
141
|
);
|
|
142
142
|
}
|
|
143
143
|
|
|
@@ -165,8 +165,6 @@ export async function waitForAllEvents<T = any>(
|
|
|
165
165
|
}>
|
|
166
166
|
): Promise<T[]> {
|
|
167
167
|
return Promise.all(
|
|
168
|
-
waiters.map(({ emitter, eventName, options }) =>
|
|
169
|
-
waitForEvent<T>(emitter, eventName, options)
|
|
170
|
-
)
|
|
168
|
+
waiters.map(({ emitter, eventName, options }) => waitForEvent<T>(emitter, eventName, options))
|
|
171
169
|
);
|
|
172
170
|
}
|
|
@@ -17,21 +17,15 @@ describe("TokenBucketRateLimiter", () => {
|
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
it("should throw error if tokensPerSecond is less than 1", () => {
|
|
20
|
-
expect(() => new TokenBucketRateLimiter(0, 10)).toThrow(
|
|
21
|
-
"tokensPerSecond must be at least 1"
|
|
22
|
-
);
|
|
20
|
+
expect(() => new TokenBucketRateLimiter(0, 10)).toThrow("tokensPerSecond must be at least 1");
|
|
23
21
|
expect(() => new TokenBucketRateLimiter(-1, 10)).toThrow(
|
|
24
22
|
"tokensPerSecond must be at least 1"
|
|
25
23
|
);
|
|
26
24
|
});
|
|
27
25
|
|
|
28
26
|
it("should throw error if maxBurst is less than 1", () => {
|
|
29
|
-
expect(() => new TokenBucketRateLimiter(10, 0)).toThrow(
|
|
30
|
-
|
|
31
|
-
);
|
|
32
|
-
expect(() => new TokenBucketRateLimiter(10, -1)).toThrow(
|
|
33
|
-
"maxBurst must be at least 1"
|
|
34
|
-
);
|
|
27
|
+
expect(() => new TokenBucketRateLimiter(10, 0)).toThrow("maxBurst must be at least 1");
|
|
28
|
+
expect(() => new TokenBucketRateLimiter(10, -1)).toThrow("maxBurst must be at least 1");
|
|
35
29
|
});
|
|
36
30
|
});
|
|
37
31
|
|
|
@@ -159,9 +153,7 @@ describe("TokenBucketRateLimiter", () => {
|
|
|
159
153
|
// Reset and try again to verify error message
|
|
160
154
|
limiter.reset();
|
|
161
155
|
limiter.tryConsume();
|
|
162
|
-
await expect(limiter.consume(10)).rejects.toThrow(
|
|
163
|
-
/Rate limit timeout/
|
|
164
|
-
);
|
|
156
|
+
await expect(limiter.consume(10)).rejects.toThrow(/Rate limit timeout/);
|
|
165
157
|
});
|
|
166
158
|
|
|
167
159
|
it("should succeed within timeout if token becomes available", async () => {
|
|
@@ -309,11 +301,7 @@ describe("TokenBucketRateLimiter", () => {
|
|
|
309
301
|
limiter.tryConsume();
|
|
310
302
|
|
|
311
303
|
// Start multiple blocking consume calls
|
|
312
|
-
const promises = [
|
|
313
|
-
limiter.consume(1000),
|
|
314
|
-
limiter.consume(1000),
|
|
315
|
-
limiter.consume(1000)
|
|
316
|
-
];
|
|
304
|
+
const promises = [limiter.consume(1000), limiter.consume(1000), limiter.consume(1000)];
|
|
317
305
|
|
|
318
306
|
// All should eventually succeed as tokens refill
|
|
319
307
|
await expect(Promise.all(promises)).resolves.toBeDefined();
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
export class RateLimitError extends Error {
|
|
14
14
|
constructor(message: string) {
|
|
15
15
|
super(message);
|
|
16
|
-
this.name =
|
|
16
|
+
this.name = "RateLimitError";
|
|
17
17
|
|
|
18
18
|
if (Error.captureStackTrace) {
|
|
19
19
|
Error.captureStackTrace(this, RateLimitError);
|
|
@@ -64,10 +64,10 @@ export class TokenBucketRateLimiter {
|
|
|
64
64
|
private readonly maxBurst: number
|
|
65
65
|
) {
|
|
66
66
|
if (tokensPerSecond < 1) {
|
|
67
|
-
throw new Error(
|
|
67
|
+
throw new Error("TokenBucketRateLimiter tokensPerSecond must be at least 1");
|
|
68
68
|
}
|
|
69
69
|
if (maxBurst < 1) {
|
|
70
|
-
throw new Error(
|
|
70
|
+
throw new Error("TokenBucketRateLimiter maxBurst must be at least 1");
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
this.tokens = maxBurst; // Start with full bucket
|
|
@@ -131,17 +131,14 @@ export class TokenBucketRateLimiter {
|
|
|
131
131
|
// Check timeout
|
|
132
132
|
const elapsed = Date.now() - startTime;
|
|
133
133
|
if (timeout !== undefined && elapsed >= timeout) {
|
|
134
|
-
throw new RateLimitError(
|
|
135
|
-
`Rate limit timeout: no token available after ${timeout}ms`
|
|
136
|
-
);
|
|
134
|
+
throw new RateLimitError(`Rate limit timeout: no token available after ${timeout}ms`);
|
|
137
135
|
}
|
|
138
136
|
|
|
139
137
|
// Calculate wait time until next token
|
|
140
138
|
// If timeout is specified, don't wait longer than remaining timeout
|
|
141
139
|
const baseWaitTime = Math.min(this.refillInterval, 100);
|
|
142
|
-
const waitTime =
|
|
143
|
-
? Math.min(baseWaitTime, timeout - elapsed)
|
|
144
|
-
: baseWaitTime;
|
|
140
|
+
const waitTime =
|
|
141
|
+
timeout !== undefined ? Math.min(baseWaitTime, timeout - elapsed) : baseWaitTime;
|
|
145
142
|
|
|
146
143
|
// If waitTime is very small or negative, check timeout immediately
|
|
147
144
|
if (waitTime <= 0) {
|