@luxexchange/websocket 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/package.json +2 -2
- package/project.json +1 -1
- package/.depcheckrc +0 -15
- package/.eslintrc.js +0 -20
- package/README.md +0 -275
- package/src/client/__tests__/MockWebSocket.ts +0 -54
- package/src/client/__tests__/createWebSocketClient.integration.test.ts +0 -831
- package/src/client/__tests__/testUtils.ts +0 -113
- package/src/client/createWebSocketClient.ts +0 -262
- package/src/index.ts +0 -22
- package/src/store/createZustandConnectionStore.test.ts +0 -162
- package/src/store/createZustandConnectionStore.ts +0 -74
- package/src/store/types.ts +0 -17
- package/src/subscriptions/SubscriptionManager.test.ts +0 -556
- package/src/subscriptions/SubscriptionManager.ts +0 -274
- package/src/subscriptions/types.ts +0 -20
- package/src/types.ts +0 -88
- package/src/utils/backoff.test.ts +0 -48
- package/src/utils/backoff.ts +0 -15
- package/tsconfig.lint.json +0 -8
- package/vitest.config.ts +0 -17
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
SubscribeInput,
|
|
3
|
-
SubscriptionEntry,
|
|
4
|
-
SubscriptionManagerOptions,
|
|
5
|
-
} from '@luxexchange/websocket/src/subscriptions/types'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Manages subscription lifecycle with reference counting and microtask batching.
|
|
9
|
-
*
|
|
10
|
-
* Key features:
|
|
11
|
-
* - Reference counting: Only calls REST subscribe on first subscriber, unsubscribe on last
|
|
12
|
-
* - Microtask batching: Coalesces subscribe/unsubscribe calls within the same microtask
|
|
13
|
-
* - Auto-resubscribe: On reconnect, resubscribes all active subscriptions with new connectionId
|
|
14
|
-
* - Deduplication: Multiple subscribers to same params share one subscription
|
|
15
|
-
* - Message routing: Routes incoming messages to appropriate callbacks
|
|
16
|
-
*/
|
|
17
|
-
export class SubscriptionManager<TParams, TMessage> {
|
|
18
|
-
private subscriptions = new Map<string, SubscriptionEntry<TParams, TMessage>>()
|
|
19
|
-
private readonly handler: SubscriptionManagerOptions<TParams>['handler']
|
|
20
|
-
private readonly createKey: SubscriptionManagerOptions<TParams>['createKey']
|
|
21
|
-
private readonly onError?: SubscriptionManagerOptions<TParams>['onError']
|
|
22
|
-
private readonly onSubscriptionCountChange?: SubscriptionManagerOptions<TParams>['onSubscriptionCountChange']
|
|
23
|
-
private connectionId: string | null = null
|
|
24
|
-
|
|
25
|
-
private pendingSubscribes = new Map<string, TParams>()
|
|
26
|
-
private pendingUnsubscribes = new Map<string, TParams>()
|
|
27
|
-
private subscribeFlushScheduled = false
|
|
28
|
-
private unsubscribeFlushScheduled = false
|
|
29
|
-
|
|
30
|
-
constructor(options: SubscriptionManagerOptions<TParams>) {
|
|
31
|
-
this.handler = options.handler
|
|
32
|
-
this.createKey = options.createKey
|
|
33
|
-
this.onError = options.onError
|
|
34
|
-
this.onSubscriptionCountChange = options.onSubscriptionCountChange
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Set the current connection ID. Called when connection is established.
|
|
39
|
-
*/
|
|
40
|
-
setConnectionId(connectionId: string | null): void {
|
|
41
|
-
this.connectionId = connectionId
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Get the current connection ID.
|
|
46
|
-
*/
|
|
47
|
-
getConnectionId(): string | null {
|
|
48
|
-
return this.connectionId
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Subscribe to a channel with given params.
|
|
53
|
-
* Synchronous — returns an unsubscribe function immediately.
|
|
54
|
-
* The actual REST subscribe call is batched via queueMicrotask.
|
|
55
|
-
*/
|
|
56
|
-
subscribe(input: SubscribeInput<TParams, TMessage>): () => void {
|
|
57
|
-
const { channel, params, callback } = input
|
|
58
|
-
const key = this.createKey(channel, params)
|
|
59
|
-
let entry = this.subscriptions.get(key)
|
|
60
|
-
let isNewEntry = false
|
|
61
|
-
|
|
62
|
-
if (entry) {
|
|
63
|
-
if (callback) {
|
|
64
|
-
entry.callbacks.add(callback)
|
|
65
|
-
}
|
|
66
|
-
} else {
|
|
67
|
-
isNewEntry = true
|
|
68
|
-
entry = {
|
|
69
|
-
channel,
|
|
70
|
-
params,
|
|
71
|
-
callbacks: new Set(callback ? [callback] : []),
|
|
72
|
-
}
|
|
73
|
-
this.subscriptions.set(key, entry)
|
|
74
|
-
|
|
75
|
-
// Queue the REST subscribe call
|
|
76
|
-
this.pendingSubscribes.set(key, params)
|
|
77
|
-
this.scheduleSubscribeFlush()
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (isNewEntry) {
|
|
81
|
-
this.onSubscriptionCountChange?.(this.subscriptions.size)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return () => {
|
|
85
|
-
this.handleUnsubscribe(key, callback)
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
private handleUnsubscribe(key: string, callback: ((message: TMessage) => void) | undefined): void {
|
|
90
|
-
const entry = this.subscriptions.get(key)
|
|
91
|
-
if (!entry) {
|
|
92
|
-
return
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (callback) {
|
|
96
|
-
entry.callbacks.delete(callback)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// If no more callbacks, remove subscription entirely
|
|
100
|
-
if (entry.callbacks.size === 0) {
|
|
101
|
-
this.subscriptions.delete(key)
|
|
102
|
-
|
|
103
|
-
// Queue the REST unsubscribe call
|
|
104
|
-
this.pendingUnsubscribes.set(key, entry.params)
|
|
105
|
-
this.scheduleUnsubscribeFlush()
|
|
106
|
-
|
|
107
|
-
this.onSubscriptionCountChange?.(this.subscriptions.size)
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
private scheduleSubscribeFlush(): void {
|
|
112
|
-
if (!this.subscribeFlushScheduled) {
|
|
113
|
-
this.subscribeFlushScheduled = true
|
|
114
|
-
queueMicrotask(() => {
|
|
115
|
-
// Cancel out keys that appear in both pending subscribe and unsubscribe
|
|
116
|
-
for (const key of this.pendingUnsubscribes.keys()) {
|
|
117
|
-
if (this.pendingSubscribes.has(key)) {
|
|
118
|
-
this.pendingSubscribes.delete(key)
|
|
119
|
-
this.pendingUnsubscribes.delete(key)
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
const params = [...this.pendingSubscribes.values()]
|
|
123
|
-
this.pendingSubscribes.clear()
|
|
124
|
-
this.subscribeFlushScheduled = false
|
|
125
|
-
if (params.length > 0 && this.connectionId) {
|
|
126
|
-
this.executeBatchSubscribe(params)
|
|
127
|
-
}
|
|
128
|
-
})
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
private scheduleUnsubscribeFlush(): void {
|
|
133
|
-
if (!this.unsubscribeFlushScheduled) {
|
|
134
|
-
this.unsubscribeFlushScheduled = true
|
|
135
|
-
queueMicrotask(() => {
|
|
136
|
-
// Cancel out keys that appear in both pending subscribe and unsubscribe
|
|
137
|
-
for (const key of this.pendingSubscribes.keys()) {
|
|
138
|
-
if (this.pendingUnsubscribes.has(key)) {
|
|
139
|
-
this.pendingUnsubscribes.delete(key)
|
|
140
|
-
this.pendingSubscribes.delete(key)
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
const params = [...this.pendingUnsubscribes.values()]
|
|
144
|
-
this.pendingUnsubscribes.clear()
|
|
145
|
-
this.unsubscribeFlushScheduled = false
|
|
146
|
-
if (params.length > 0 && this.connectionId) {
|
|
147
|
-
this.executeBatchUnsubscribe(params)
|
|
148
|
-
}
|
|
149
|
-
})
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
private executeBatchSubscribe(params: TParams[]): void {
|
|
154
|
-
const { connectionId } = this
|
|
155
|
-
if (!connectionId) {
|
|
156
|
-
return
|
|
157
|
-
}
|
|
158
|
-
if (this.handler.subscribeBatch) {
|
|
159
|
-
this.handler.subscribeBatch(connectionId, params).catch((error) => {
|
|
160
|
-
this.onError?.(error, 'subscribe')
|
|
161
|
-
})
|
|
162
|
-
} else {
|
|
163
|
-
Promise.all(params.map((p) => this.handler.subscribe(connectionId, p))).catch((error) => {
|
|
164
|
-
this.onError?.(error, 'subscribe')
|
|
165
|
-
})
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
private executeBatchUnsubscribe(params: TParams[]): void {
|
|
170
|
-
const { connectionId } = this
|
|
171
|
-
if (!connectionId) {
|
|
172
|
-
return
|
|
173
|
-
}
|
|
174
|
-
if (this.handler.unsubscribeBatch) {
|
|
175
|
-
this.handler.unsubscribeBatch(connectionId, params).catch((error) => {
|
|
176
|
-
this.onError?.(error, 'unsubscribe')
|
|
177
|
-
})
|
|
178
|
-
} else {
|
|
179
|
-
Promise.all(params.map((p) => this.handler.unsubscribe(connectionId, p))).catch((error) => {
|
|
180
|
-
this.onError?.(error, 'unsubscribe')
|
|
181
|
-
})
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Resubscribe all active subscriptions with a new connection ID.
|
|
187
|
-
* Called after reconnection. Uses subscribeBatch when available.
|
|
188
|
-
*/
|
|
189
|
-
async resubscribeAll(connectionId: string): Promise<void> {
|
|
190
|
-
this.connectionId = connectionId
|
|
191
|
-
|
|
192
|
-
const allParams = Array.from(this.subscriptions.values()).map((entry) => entry.params)
|
|
193
|
-
if (allParams.length === 0) {
|
|
194
|
-
return
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (this.handler.subscribeBatch) {
|
|
198
|
-
try {
|
|
199
|
-
await this.handler.subscribeBatch(connectionId, allParams)
|
|
200
|
-
} catch (error) {
|
|
201
|
-
this.onError?.(error, 'resubscribe')
|
|
202
|
-
}
|
|
203
|
-
} else {
|
|
204
|
-
const subscribePromises = allParams.map((params) =>
|
|
205
|
-
this.handler.subscribe(connectionId, params).catch((error) => {
|
|
206
|
-
this.onError?.(error, 'resubscribe')
|
|
207
|
-
}),
|
|
208
|
-
)
|
|
209
|
-
await Promise.all(subscribePromises)
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Dispatch an incoming message to appropriate callbacks.
|
|
215
|
-
*/
|
|
216
|
-
dispatch(key: string, message: TMessage): void {
|
|
217
|
-
const entry = this.subscriptions.get(key)
|
|
218
|
-
if (!entry) {
|
|
219
|
-
return
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
for (const callback of entry.callbacks) {
|
|
223
|
-
try {
|
|
224
|
-
callback(message)
|
|
225
|
-
} catch (error) {
|
|
226
|
-
this.onError?.(error, 'dispatch')
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Get all active subscriptions.
|
|
233
|
-
*/
|
|
234
|
-
getActiveSubscriptions(): Array<{ channel: string; params: TParams; subscriberCount: number }> {
|
|
235
|
-
return Array.from(this.subscriptions.entries()).map(([, entry]) => ({
|
|
236
|
-
channel: entry.channel,
|
|
237
|
-
params: entry.params,
|
|
238
|
-
subscriberCount: entry.callbacks.size,
|
|
239
|
-
}))
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Check if there are any active subscriptions.
|
|
244
|
-
*/
|
|
245
|
-
hasActiveSubscriptions(): boolean {
|
|
246
|
-
return this.subscriptions.size > 0
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Clear all subscriptions without calling unsubscribe API.
|
|
251
|
-
* Used on disconnect.
|
|
252
|
-
*/
|
|
253
|
-
clear(): void {
|
|
254
|
-
this.subscriptions.clear()
|
|
255
|
-
this.pendingSubscribes.clear()
|
|
256
|
-
this.pendingUnsubscribes.clear()
|
|
257
|
-
this.connectionId = null
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Refresh the session if the handler supports it.
|
|
262
|
-
*/
|
|
263
|
-
async refreshSession(): Promise<void> {
|
|
264
|
-
if (!this.connectionId || !this.handler.refreshSession) {
|
|
265
|
-
return
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
try {
|
|
269
|
-
await this.handler.refreshSession(this.connectionId)
|
|
270
|
-
} catch (error) {
|
|
271
|
-
this.onError?.(error, 'refreshSession')
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { SubscriptionHandler } from '@luxexchange/websocket/src/types'
|
|
2
|
-
|
|
3
|
-
export interface SubscriptionEntry<TParams, TMessage> {
|
|
4
|
-
channel: string
|
|
5
|
-
params: TParams
|
|
6
|
-
callbacks: Set<(message: TMessage) => void>
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface SubscriptionManagerOptions<TParams> {
|
|
10
|
-
handler: SubscriptionHandler<TParams>
|
|
11
|
-
createKey: (channel: string, params: TParams) => string
|
|
12
|
-
onError?: (error: unknown, operation: string) => void
|
|
13
|
-
onSubscriptionCountChange?: (count: number) => void
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface SubscribeInput<TParams, TMessage> {
|
|
17
|
-
channel: string
|
|
18
|
-
params: TParams
|
|
19
|
-
callback?: (message: TMessage) => void
|
|
20
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Core types for generic WebSocket client functionality
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { ConnectionStore } from '@luxexchange/websocket/src/store/types'
|
|
6
|
-
|
|
7
|
-
export type ConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'reconnecting'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Minimal interface for WebSocket-like objects.
|
|
11
|
-
* Allows injection of mock WebSocket implementations for testing.
|
|
12
|
-
*/
|
|
13
|
-
export interface WebSocketLike {
|
|
14
|
-
readyState: number
|
|
15
|
-
addEventListener(event: string, handler: (event: unknown) => void): void
|
|
16
|
-
close(): void
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Options for creating a WebSocket connection.
|
|
21
|
-
* These match the options expected by PartySocket.
|
|
22
|
-
*/
|
|
23
|
-
export interface SocketFactoryOptions {
|
|
24
|
-
maxReconnectionDelay: number
|
|
25
|
-
minReconnectionDelay: number
|
|
26
|
-
reconnectionDelayGrowFactor: number
|
|
27
|
-
connectionTimeout: number
|
|
28
|
-
maxRetries: number
|
|
29
|
-
debug: boolean
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface ConnectionConfig {
|
|
33
|
-
url: string
|
|
34
|
-
maxReconnectionDelay?: number
|
|
35
|
-
minReconnectionDelay?: number
|
|
36
|
-
connectionTimeout?: number
|
|
37
|
-
maxRetries?: number
|
|
38
|
-
debug?: boolean
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Subscription handler - consumers provide REST API implementation
|
|
43
|
-
* for managing server-side subscription lifecycle
|
|
44
|
-
*/
|
|
45
|
-
export interface SubscriptionHandler<TParams = unknown> {
|
|
46
|
-
subscribe: (connectionId: string, params: TParams) => Promise<void>
|
|
47
|
-
unsubscribe: (connectionId: string, params: TParams) => Promise<void>
|
|
48
|
-
subscribeBatch?: (connectionId: string, params: TParams[]) => Promise<void>
|
|
49
|
-
unsubscribeBatch?: (connectionId: string, params: TParams[]) => Promise<void>
|
|
50
|
-
refreshSession?: (connectionId: string) => Promise<void>
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export interface SubscriptionOptions<TParams, TMessage> {
|
|
54
|
-
channel: string
|
|
55
|
-
params: TParams
|
|
56
|
-
onMessage?: (message: TMessage) => void
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface WebSocketClient<TParams = unknown, TMessage = unknown> {
|
|
60
|
-
isConnected: () => boolean
|
|
61
|
-
getConnectionStatus: () => ConnectionStatus
|
|
62
|
-
getConnectionId: () => string | null
|
|
63
|
-
subscribe: (options: SubscriptionOptions<TParams, TMessage>) => () => void
|
|
64
|
-
onStatusChange: (callback: (status: ConnectionStatus) => void) => () => void
|
|
65
|
-
onConnectionEstablished: (callback: (connectionId: string) => void) => () => void
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export interface CreateWebSocketClientOptions<TParams, TMessage> {
|
|
69
|
-
config: ConnectionConfig
|
|
70
|
-
connectionStore: ConnectionStore
|
|
71
|
-
subscriptionHandler: SubscriptionHandler<TParams>
|
|
72
|
-
parseMessage: (raw: unknown) => { channel: string; key: string; data: TMessage } | null
|
|
73
|
-
parseConnectionMessage: (raw: unknown) => { connectionId: string } | null
|
|
74
|
-
createSubscriptionKey: (channel: string, params: TParams) => string
|
|
75
|
-
onError?: (error: unknown) => void
|
|
76
|
-
onRawMessage?: (message: unknown) => void
|
|
77
|
-
/**
|
|
78
|
-
* Interval (ms) for automatically refreshing the server session.
|
|
79
|
-
* When set, the client starts a timer on connect and stops it on disconnect.
|
|
80
|
-
* Requires the subscriptionHandler to implement refreshSession.
|
|
81
|
-
*/
|
|
82
|
-
sessionRefreshIntervalMs?: number
|
|
83
|
-
/**
|
|
84
|
-
* Optional factory for creating WebSocket instances.
|
|
85
|
-
* Defaults to PartySocket. Primarily used for testing with mock WebSockets.
|
|
86
|
-
*/
|
|
87
|
-
socketFactory?: (url: string, options: SocketFactoryOptions) => WebSocketLike
|
|
88
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { addJitter, getDefaultJitteredDelay } from '@luxexchange/websocket/src/utils/backoff'
|
|
2
|
-
import { describe, expect, it, vi } from 'vitest'
|
|
3
|
-
|
|
4
|
-
describe('backoff utilities', () => {
|
|
5
|
-
describe('addJitter', () => {
|
|
6
|
-
it('returns value in range [minDelay, minDelay + jitterRange]', () => {
|
|
7
|
-
const minDelay = 1000
|
|
8
|
-
const jitterRange = 4000
|
|
9
|
-
|
|
10
|
-
// Run multiple times to test randomness
|
|
11
|
-
for (let i = 0; i < 100; i++) {
|
|
12
|
-
const result = addJitter(minDelay, jitterRange)
|
|
13
|
-
expect(result).toBeGreaterThanOrEqual(minDelay)
|
|
14
|
-
expect(result).toBeLessThan(minDelay + jitterRange)
|
|
15
|
-
}
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
it('returns exact minDelay when Math.random returns 0', () => {
|
|
19
|
-
vi.spyOn(Math, 'random').mockReturnValue(0)
|
|
20
|
-
|
|
21
|
-
const result = addJitter(1000, 4000)
|
|
22
|
-
|
|
23
|
-
expect(result).toBe(1000)
|
|
24
|
-
|
|
25
|
-
vi.restoreAllMocks()
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
it('returns near max when Math.random returns near 1', () => {
|
|
29
|
-
vi.spyOn(Math, 'random').mockReturnValue(0.999)
|
|
30
|
-
|
|
31
|
-
const result = addJitter(1000, 4000)
|
|
32
|
-
|
|
33
|
-
expect(result).toBeCloseTo(4996, 0)
|
|
34
|
-
|
|
35
|
-
vi.restoreAllMocks()
|
|
36
|
-
})
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
describe('getDefaultJitteredDelay', () => {
|
|
40
|
-
it('returns value between 1000 and 5000', () => {
|
|
41
|
-
for (let i = 0; i < 100; i++) {
|
|
42
|
-
const result = getDefaultJitteredDelay()
|
|
43
|
-
expect(result).toBeGreaterThanOrEqual(1000)
|
|
44
|
-
expect(result).toBeLessThan(5000)
|
|
45
|
-
}
|
|
46
|
-
})
|
|
47
|
-
})
|
|
48
|
-
})
|
package/src/utils/backoff.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Add jitter to a delay to prevent thundering herd on reconnect.
|
|
3
|
-
* Returns a value between minDelay and minDelay + jitterRange.
|
|
4
|
-
*/
|
|
5
|
-
export function addJitter(minDelay: number, jitterRange: number): number {
|
|
6
|
-
return minDelay + Math.random() * jitterRange
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Default jitter configuration for WebSocket reconnection.
|
|
11
|
-
* Base delay of 1000ms with up to 4000ms additional jitter.
|
|
12
|
-
*/
|
|
13
|
-
export function getDefaultJitteredDelay(): number {
|
|
14
|
-
return addJitter(1000, 4000)
|
|
15
|
-
}
|
package/tsconfig.lint.json
DELETED
package/vitest.config.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'vitest/config'
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
test: {
|
|
5
|
-
environment: 'node',
|
|
6
|
-
coverage: {
|
|
7
|
-
exclude: [
|
|
8
|
-
'**/__generated__/**',
|
|
9
|
-
'**/node_modules/**',
|
|
10
|
-
'**/dist/**',
|
|
11
|
-
'**/*.config.*',
|
|
12
|
-
'**/scripts/**',
|
|
13
|
-
'**/.eslintrc.*',
|
|
14
|
-
],
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
})
|