@lifi/perps-sdk 1.5.5 → 1.6.0
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/client/PerpsClient.d.ts +1 -1
- package/dist/cjs/client/PerpsClient.d.ts.map +1 -1
- package/dist/cjs/client/PerpsClient.js +6 -0
- package/dist/cjs/client/PerpsClient.js.map +1 -1
- package/dist/cjs/registry/assetRegistry.d.ts +1 -1
- package/dist/cjs/registry/assetRegistry.d.ts.map +1 -1
- package/dist/cjs/registry/assetRegistry.js +2 -2
- package/dist/cjs/registry/assetRegistry.js.map +1 -1
- package/dist/cjs/registry/marketRegistry.d.ts +1 -1
- package/dist/cjs/registry/marketRegistry.d.ts.map +1 -1
- package/dist/cjs/registry/marketRegistry.js +2 -2
- package/dist/cjs/registry/marketRegistry.js.map +1 -1
- package/dist/cjs/registry/referenceDataRegistry.d.ts +1 -3
- package/dist/cjs/registry/referenceDataRegistry.d.ts.map +1 -1
- package/dist/cjs/registry/referenceDataRegistry.js +3 -14
- package/dist/cjs/registry/referenceDataRegistry.js.map +1 -1
- package/dist/cjs/services/getMarket.d.ts.map +1 -1
- package/dist/cjs/services/getMarket.js +9 -1
- package/dist/cjs/services/getMarket.js.map +1 -1
- package/dist/cjs/utils/accountSummary.d.ts.map +1 -1
- package/dist/cjs/utils/accountSummary.js +2 -1
- package/dist/cjs/utils/accountSummary.js.map +1 -1
- package/dist/cjs/utils/calculations.d.ts +1 -1
- package/dist/cjs/utils/calculations.d.ts.map +1 -1
- package/dist/cjs/utils/calculations.js +7 -1
- package/dist/cjs/utils/calculations.js.map +1 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/websocket/ReconnectingWebSocket.d.ts.map +1 -1
- package/dist/cjs/websocket/ReconnectingWebSocket.js +4 -0
- package/dist/cjs/websocket/ReconnectingWebSocket.js.map +1 -1
- package/dist/cjs/websocket/WsProviderBase.d.ts.map +1 -1
- package/dist/cjs/websocket/WsProviderBase.js +9 -1
- package/dist/cjs/websocket/WsProviderBase.js.map +1 -1
- package/dist/esm/client/PerpsClient.d.ts +5 -1
- package/dist/esm/client/PerpsClient.d.ts.map +1 -1
- package/dist/esm/client/PerpsClient.js +16 -0
- package/dist/esm/client/PerpsClient.js.map +1 -1
- package/dist/esm/registry/assetRegistry.d.ts +1 -1
- package/dist/esm/registry/assetRegistry.d.ts.map +1 -1
- package/dist/esm/registry/assetRegistry.js +2 -2
- package/dist/esm/registry/assetRegistry.js.map +1 -1
- package/dist/esm/registry/marketRegistry.d.ts +1 -1
- package/dist/esm/registry/marketRegistry.d.ts.map +1 -1
- package/dist/esm/registry/marketRegistry.js +2 -2
- package/dist/esm/registry/marketRegistry.js.map +1 -1
- package/dist/esm/registry/referenceDataRegistry.d.ts +5 -11
- package/dist/esm/registry/referenceDataRegistry.d.ts.map +1 -1
- package/dist/esm/registry/referenceDataRegistry.js +6 -23
- package/dist/esm/registry/referenceDataRegistry.js.map +1 -1
- package/dist/esm/services/getMarket.d.ts +4 -3
- package/dist/esm/services/getMarket.d.ts.map +1 -1
- package/dist/esm/services/getMarket.js +13 -4
- package/dist/esm/services/getMarket.js.map +1 -1
- package/dist/esm/utils/accountSummary.d.ts +7 -3
- package/dist/esm/utils/accountSummary.d.ts.map +1 -1
- package/dist/esm/utils/accountSummary.js +5 -1
- package/dist/esm/utils/accountSummary.js.map +1 -1
- package/dist/esm/utils/calculations.d.ts +5 -1
- package/dist/esm/utils/calculations.d.ts.map +1 -1
- package/dist/esm/utils/calculations.js +11 -1
- package/dist/esm/utils/calculations.js.map +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/websocket/ReconnectingWebSocket.d.ts +7 -4
- package/dist/esm/websocket/ReconnectingWebSocket.d.ts.map +1 -1
- package/dist/esm/websocket/ReconnectingWebSocket.js +11 -4
- package/dist/esm/websocket/ReconnectingWebSocket.js.map +1 -1
- package/dist/esm/websocket/WsProviderBase.d.ts.map +1 -1
- package/dist/esm/websocket/WsProviderBase.js +14 -1
- package/dist/esm/websocket/WsProviderBase.js.map +1 -1
- package/dist/esm/websocket/types.d.ts +3 -3
- package/dist/types/client/PerpsClient.d.ts +5 -1
- package/dist/types/client/PerpsClient.d.ts.map +1 -1
- package/dist/types/registry/assetRegistry.d.ts +1 -1
- package/dist/types/registry/assetRegistry.d.ts.map +1 -1
- package/dist/types/registry/marketRegistry.d.ts +1 -1
- package/dist/types/registry/marketRegistry.d.ts.map +1 -1
- package/dist/types/registry/referenceDataRegistry.d.ts +5 -11
- package/dist/types/registry/referenceDataRegistry.d.ts.map +1 -1
- package/dist/types/services/getMarket.d.ts +4 -3
- package/dist/types/services/getMarket.d.ts.map +1 -1
- package/dist/types/utils/accountSummary.d.ts +7 -3
- package/dist/types/utils/accountSummary.d.ts.map +1 -1
- package/dist/types/utils/calculations.d.ts +5 -1
- package/dist/types/utils/calculations.d.ts.map +1 -1
- package/dist/types/version.d.ts +1 -1
- package/dist/types/websocket/ReconnectingWebSocket.d.ts +7 -4
- package/dist/types/websocket/ReconnectingWebSocket.d.ts.map +1 -1
- package/dist/types/websocket/WsProviderBase.d.ts.map +1 -1
- package/dist/types/websocket/types.d.ts +3 -3
- package/package.json +2 -2
- package/src/client/PerpsClient.ts +20 -1
- package/src/registry/assetRegistry.ts +4 -6
- package/src/registry/marketRegistry.ts +4 -6
- package/src/registry/referenceDataRegistry.ts +8 -32
- package/src/services/getMarket.ts +16 -4
- package/src/utils/accountSummary.ts +14 -4
- package/src/utils/calculations.ts +22 -9
- package/src/version.ts +1 -1
- package/src/websocket/ReconnectingWebSocket.ts +11 -4
- package/src/websocket/WsProviderBase.ts +14 -1
- package/src/websocket/types.ts +3 -3
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
import type { PerpsSDKClient } from '../types/provider.js'
|
|
2
2
|
|
|
3
|
-
/** Minimum gap between unknown-id-triggered, HTTP-cache-bypassing refetches. */
|
|
4
|
-
const REFRESH_COOLDOWN_MS = 60_000
|
|
5
|
-
|
|
6
3
|
/**
|
|
7
4
|
* Per-provider hash index over one of the provider's reference-data lists
|
|
8
5
|
* (`/markets`, `/assets`), keyed by the list's own primary key.
|
|
9
6
|
*
|
|
10
7
|
* NOT a cache: it holds no freshness policy of its own. Every {@link sync}
|
|
11
8
|
* refetches through the HTTP layer, whose `cache-control` headers decide
|
|
12
|
-
* whether the response comes from disk or the network. A lookup miss
|
|
13
|
-
*
|
|
14
|
-
* cache — the id may have listed after the cached snapshot.
|
|
9
|
+
* whether the response comes from disk or the network. A lookup miss warns
|
|
10
|
+
* once and returns `undefined`; the next {@link sync} reconciles the index.
|
|
15
11
|
*
|
|
16
12
|
* @internal
|
|
17
13
|
*/
|
|
@@ -20,7 +16,6 @@ export abstract class ReferenceDataRegistry<T> {
|
|
|
20
16
|
private current: readonly T[] = []
|
|
21
17
|
private inflight: Promise<readonly T[]> | undefined
|
|
22
18
|
private warnedIds = new Set<string>()
|
|
23
|
-
private refreshAfter = 0
|
|
24
19
|
|
|
25
20
|
protected constructor(
|
|
26
21
|
protected readonly client: PerpsSDKClient,
|
|
@@ -28,8 +23,8 @@ export abstract class ReferenceDataRegistry<T> {
|
|
|
28
23
|
private readonly kind: string
|
|
29
24
|
) {}
|
|
30
25
|
|
|
31
|
-
/** Fetch the provider's full list
|
|
32
|
-
protected abstract fetchItems(
|
|
26
|
+
/** Fetch the provider's full list through the HTTP layer. */
|
|
27
|
+
protected abstract fetchItems(): Promise<T[]>
|
|
33
28
|
|
|
34
29
|
/** The item's primary key — what {@link get} is keyed by. */
|
|
35
30
|
protected abstract keyOf(item: T): string
|
|
@@ -41,7 +36,7 @@ export abstract class ReferenceDataRegistry<T> {
|
|
|
41
36
|
*/
|
|
42
37
|
sync(): Promise<readonly T[]> {
|
|
43
38
|
if (!this.inflight) {
|
|
44
|
-
this.inflight = this.load(
|
|
39
|
+
this.inflight = this.load().finally(() => {
|
|
45
40
|
this.inflight = undefined
|
|
46
41
|
})
|
|
47
42
|
}
|
|
@@ -53,10 +48,7 @@ export abstract class ReferenceDataRegistry<T> {
|
|
|
53
48
|
return this.current
|
|
54
49
|
}
|
|
55
50
|
|
|
56
|
-
/**
|
|
57
|
-
* O(1) lookup by primary key. A miss warns once per id and schedules the
|
|
58
|
-
* cooldown-gated, cache-bypassing background refetch.
|
|
59
|
-
*/
|
|
51
|
+
/** O(1) lookup by primary key. A miss warns once per id. */
|
|
60
52
|
get(id: string): T | undefined {
|
|
61
53
|
const item = this.index.get(id)
|
|
62
54
|
if (item !== undefined) {
|
|
@@ -66,30 +58,14 @@ export abstract class ReferenceDataRegistry<T> {
|
|
|
66
58
|
this.warnedIds.add(id)
|
|
67
59
|
console.warn(`[${this.provider}] unknown ${this.kind} id '${id}'`)
|
|
68
60
|
}
|
|
69
|
-
this.scheduleRefresh()
|
|
70
61
|
return undefined
|
|
71
62
|
}
|
|
72
63
|
|
|
73
|
-
private async load(
|
|
74
|
-
const items = await this.fetchItems(
|
|
64
|
+
private async load(): Promise<readonly T[]> {
|
|
65
|
+
const items = await this.fetchItems()
|
|
75
66
|
this.index = new Map(items.map((item) => [this.keyOf(item), item]))
|
|
76
67
|
this.current = items
|
|
77
68
|
this.warnedIds.clear()
|
|
78
69
|
return items
|
|
79
70
|
}
|
|
80
|
-
|
|
81
|
-
private scheduleRefresh(): void {
|
|
82
|
-
const now = Date.now()
|
|
83
|
-
if (now < this.refreshAfter) {
|
|
84
|
-
return
|
|
85
|
-
}
|
|
86
|
-
// Set before any await so concurrent misses cannot trigger a refetch storm.
|
|
87
|
-
this.refreshAfter = now + REFRESH_COOLDOWN_MS
|
|
88
|
-
this.load(true).catch((error) =>
|
|
89
|
-
console.error(
|
|
90
|
-
`[${this.provider}] ${this.kind} registry refresh failed`,
|
|
91
|
-
error
|
|
92
|
-
)
|
|
93
|
-
)
|
|
94
|
-
}
|
|
95
71
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { Market, MarketsResponse } from '@lifi/perps-types'
|
|
2
|
+
import { PerpsErrorCode } from '@lifi/perps-types'
|
|
3
|
+
import { PerpsError } from '../errors/PerpsError.js'
|
|
2
4
|
import { buildUrl, request } from '../transport/request.js'
|
|
3
5
|
import type { SDKRequestOptions } from '../types/config.js'
|
|
4
6
|
import type { PerpsSDKClient } from '../types/provider.js'
|
|
@@ -21,10 +23,11 @@ export interface GetMarketParams {
|
|
|
21
23
|
/**
|
|
22
24
|
* Get a specific market by its opaque marketId. Requests the single market
|
|
23
25
|
* from the backend `/markets` route (filtered by `marketIds`) and returns the
|
|
24
|
-
* first match;
|
|
25
|
-
*
|
|
26
|
+
* first match; throws a `MarketNotFound` {@link PerpsError} both when the
|
|
27
|
+
* backend responds 404 and when it responds 200 with an empty list, matching
|
|
28
|
+
* `MarketRegistry.require`'s miss behaviour.
|
|
26
29
|
*
|
|
27
|
-
* @throws {PerpsError} On backend (e.g. 404), network, or parsing errors.
|
|
30
|
+
* @throws {PerpsError} On backend (e.g. 404 or empty match), network, or parsing errors.
|
|
28
31
|
* @example
|
|
29
32
|
* ```ts
|
|
30
33
|
* const client = createPerpsClient({ integrator: 'my-app' })
|
|
@@ -48,5 +51,14 @@ export async function getMarket(
|
|
|
48
51
|
{},
|
|
49
52
|
options
|
|
50
53
|
)
|
|
51
|
-
|
|
54
|
+
const market = markets[0]
|
|
55
|
+
if (market === undefined) {
|
|
56
|
+
const error = new PerpsError(
|
|
57
|
+
PerpsErrorCode.MarketNotFound,
|
|
58
|
+
`No ${params.provider} market found for marketId '${params.marketId}'`
|
|
59
|
+
)
|
|
60
|
+
error.tool = params.provider
|
|
61
|
+
throw error
|
|
62
|
+
}
|
|
63
|
+
return market
|
|
52
64
|
}
|
|
@@ -13,11 +13,15 @@ const sumValueUsd = (balances: AccountResponse['balances']): number =>
|
|
|
13
13
|
* positions' locked margin and unrealized PnL:
|
|
14
14
|
*
|
|
15
15
|
* - `'free'` — free collateral only; locked margin and unrealized PnL are
|
|
16
|
-
* both carried by the positions.
|
|
16
|
+
* both carried by the positions. Available margin is the free collateral
|
|
17
|
+
* as-is; unrealized PnL is not counted toward it.
|
|
17
18
|
* - `'gross'` — locked margin included, unrealized PnL carried by the
|
|
18
|
-
* positions (e.g. spot holdings backing a unified account).
|
|
19
|
+
* positions (e.g. spot holdings backing a unified account). The venue
|
|
20
|
+
* counts unrealized PnL toward buying power, so it is added to available
|
|
21
|
+
* margin on top of the gross collateral.
|
|
19
22
|
* - `'equity'` — total equity: locked margin AND unrealized PnL included
|
|
20
|
-
* (e.g. a venue-reported account value).
|
|
23
|
+
* (e.g. a venue-reported account value). Unrealized PnL is thus already
|
|
24
|
+
* counted toward available margin.
|
|
21
25
|
*
|
|
22
26
|
* @public
|
|
23
27
|
*/
|
|
@@ -51,9 +55,15 @@ export function summarizeAccount(
|
|
|
51
55
|
const equity =
|
|
52
56
|
semantics === 'equity' ? grossCollateral : grossCollateral + unrealizedPnl
|
|
53
57
|
|
|
58
|
+
// Buying power is equity net of locked margin, so unrealized PnL counts
|
|
59
|
+
// toward it. The `'free'` rows exclude uPnL from available margin, holding
|
|
60
|
+
// it purely as free collateral.
|
|
61
|
+
const availableMargin =
|
|
62
|
+
semantics === 'free' ? grossCollateral - marginUsed : equity - marginUsed
|
|
63
|
+
|
|
54
64
|
return {
|
|
55
65
|
portfolioValue: (balances + equity).toString(),
|
|
56
|
-
availableMargin:
|
|
66
|
+
availableMargin: availableMargin.toString(),
|
|
57
67
|
marginUsed: marginUsed.toString(),
|
|
58
68
|
unrealizedPnl: unrealizedPnl.toString(),
|
|
59
69
|
}
|
|
@@ -6,15 +6,17 @@
|
|
|
6
6
|
* values for critical financial parameters.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import
|
|
10
|
-
FeeTier,
|
|
11
|
-
Market,
|
|
12
|
-
MarketContext,
|
|
13
|
-
OrderbookLevel,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
import {
|
|
10
|
+
type FeeTier,
|
|
11
|
+
type Market,
|
|
12
|
+
type MarketContext,
|
|
13
|
+
type OrderbookLevel,
|
|
14
|
+
PerpsErrorCode,
|
|
15
|
+
type Quote,
|
|
16
|
+
type QuoteSide,
|
|
17
|
+
type TradeType,
|
|
17
18
|
} from '@lifi/perps-types'
|
|
19
|
+
import { PerpsError } from '../errors/PerpsError.js'
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* Calculate position size in asset units from margin.
|
|
@@ -319,6 +321,8 @@ interface BookWalk {
|
|
|
319
321
|
* notional, the walk stops at the last level and flags `insufficientLiquidity`,
|
|
320
322
|
* returning the best obtainable fill.
|
|
321
323
|
*
|
|
324
|
+
* @throws {PerpsError} `ValidationError` when a level's `price` or `size`
|
|
325
|
+
* does not parse to a finite number.
|
|
322
326
|
* @public
|
|
323
327
|
*/
|
|
324
328
|
export function walkOrderbook(
|
|
@@ -333,7 +337,14 @@ export function walkOrderbook(
|
|
|
333
337
|
break
|
|
334
338
|
}
|
|
335
339
|
const price = Number.parseFloat(level.price)
|
|
336
|
-
const
|
|
340
|
+
const size = Number.parseFloat(level.size)
|
|
341
|
+
if (!Number.isFinite(price) || !Number.isFinite(size)) {
|
|
342
|
+
throw new PerpsError(
|
|
343
|
+
PerpsErrorCode.ValidationError,
|
|
344
|
+
`Malformed orderbook level: price='${level.price}', size='${level.size}'`
|
|
345
|
+
)
|
|
346
|
+
}
|
|
347
|
+
const levelNotional = size * price
|
|
337
348
|
const take = Math.min(remaining, levelNotional)
|
|
338
349
|
filledNotional += take
|
|
339
350
|
baseSize += take / price
|
|
@@ -370,6 +381,8 @@ interface BuildQuoteInput {
|
|
|
370
381
|
* versus mark, applies the base taker fee on the filled notional, and carries
|
|
371
382
|
* the market's `funding` (`null` for spot, which has none).
|
|
372
383
|
*
|
|
384
|
+
* @throws {PerpsError} `ValidationError` when a book level does not parse to
|
|
385
|
+
* a finite number — see {@link walkOrderbook}.
|
|
373
386
|
* @public
|
|
374
387
|
*/
|
|
375
388
|
export function buildQuote(input: BuildQuoteInput): Quote {
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const name = '@lifi/perps-sdk'
|
|
2
|
-
export const version = '1.
|
|
2
|
+
export const version = '1.6.0'
|
|
@@ -238,16 +238,21 @@ export class ReconnectingWebSocket {
|
|
|
238
238
|
|
|
239
239
|
/**
|
|
240
240
|
* Send `data`, buffering it (bounded) for replay when the socket is not yet
|
|
241
|
-
* open.
|
|
241
|
+
* open. Throws once {@link ReconnectingWebSocket.close} has been called —
|
|
242
|
+
* there is no socket left to buffer against.
|
|
242
243
|
*
|
|
243
244
|
* @public
|
|
244
245
|
*/
|
|
245
246
|
send(data: string) {
|
|
247
|
+
if (this.closed) {
|
|
248
|
+
throw new Error(CLOSED_ERROR)
|
|
249
|
+
}
|
|
246
250
|
this.socket.send(data)
|
|
247
251
|
}
|
|
248
252
|
|
|
249
253
|
/**
|
|
250
|
-
* Permanently close the socket
|
|
254
|
+
* Permanently close the socket, suppress further reconnection, and set
|
|
255
|
+
* status to terminal `disconnected`.
|
|
251
256
|
*
|
|
252
257
|
* @public
|
|
253
258
|
*/
|
|
@@ -256,6 +261,7 @@ export class ReconnectingWebSocket {
|
|
|
256
261
|
this.stopPing()
|
|
257
262
|
this.clearWatchdog()
|
|
258
263
|
this.socket.close()
|
|
264
|
+
this.setStatus('disconnected')
|
|
259
265
|
for (const { reject } of this.readyResolvers) {
|
|
260
266
|
reject(new Error(CLOSED_ERROR))
|
|
261
267
|
}
|
|
@@ -300,8 +306,9 @@ export class ReconnectingWebSocket {
|
|
|
300
306
|
|
|
301
307
|
/**
|
|
302
308
|
* Current connection health. `reconnecting` until the first open;
|
|
303
|
-
* `disconnected` once auto-reconnect is abandoned
|
|
304
|
-
*
|
|
309
|
+
* `disconnected` once auto-reconnect is abandoned or {@link
|
|
310
|
+
* ReconnectingWebSocket.close} is called (terminal until {@link
|
|
311
|
+
* ReconnectingWebSocket.reconnect} is called).
|
|
305
312
|
*
|
|
306
313
|
* @public
|
|
307
314
|
*/
|
|
@@ -94,7 +94,12 @@ export abstract class WsProviderBase<TSub = unknown> implements WsProvider {
|
|
|
94
94
|
onStatus(this.rws.getStatus())
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
let released = false
|
|
97
98
|
return () => {
|
|
99
|
+
if (released) {
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
released = true
|
|
98
103
|
if (onStatus) {
|
|
99
104
|
const remaining = (this.statusListeners.get(onStatus) ?? 0) - 1
|
|
100
105
|
if (remaining > 0) {
|
|
@@ -188,7 +193,15 @@ export abstract class WsProviderBase<TSub = unknown> implements WsProvider {
|
|
|
188
193
|
}
|
|
189
194
|
|
|
190
195
|
private async replaySubs(): Promise<void> {
|
|
191
|
-
|
|
196
|
+
// Snapshot at replay start, and re-check the live registry before each
|
|
197
|
+
// send. An async sendSubscribe (e.g. Lighter's auth-token fetch) suspends
|
|
198
|
+
// the loop; status is already 'connected' while open listeners run, so a
|
|
199
|
+
// subscribe landing mid-replay is sent inline by registerSub — it must not
|
|
200
|
+
// be revisited here, and an entry unregistered mid-replay must be skipped.
|
|
201
|
+
for (const [key, state] of [...this.wireSubs]) {
|
|
202
|
+
if (this.wireSubs.get(key) !== state) {
|
|
203
|
+
continue
|
|
204
|
+
}
|
|
192
205
|
// Isolate each resubscribe: a send can reject (e.g. an auth-token fetch
|
|
193
206
|
// after reconnect), and one sub's failure must not abort the rest of
|
|
194
207
|
// the loop or escape as an unhandled rejection.
|
package/src/websocket/types.ts
CHANGED
|
@@ -28,9 +28,9 @@ export type SubscriptionListener = (event: SubscriptionEvent) => void
|
|
|
28
28
|
* - `connected` — socket open, live data flowing.
|
|
29
29
|
* - `reconnecting` — socket dropped; auto-reconnect with backoff in progress.
|
|
30
30
|
* Data may be stale until it returns to `connected`.
|
|
31
|
-
* - `disconnected` — reconnection abandoned after the retry cap
|
|
32
|
-
* connection
|
|
33
|
-
* subscription's data is stale. Terminal.
|
|
31
|
+
* - `disconnected` — reconnection abandoned after the retry cap, or the
|
|
32
|
+
* connection was explicitly closed. The connection is permanently dead and
|
|
33
|
+
* will not recover on its own; the subscription's data is stale. Terminal.
|
|
34
34
|
*
|
|
35
35
|
* @public
|
|
36
36
|
*/
|