@metamask-previews/core-backend 12.3.0-preview-313288d71 → 12.3.0-preview-35f016caa
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/CHANGELOG.md +0 -7
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -8
- package/dist/index.js.map +1 -1
- package/dist/ws/ohlcv/OHLCVService.d.ts +4 -7
- package/dist/ws/ohlcv/OHLCVService.d.ts.map +1 -1
- package/dist/ws/ohlcv/OHLCVService.js +329 -42
- package/dist/ws/ohlcv/OHLCVService.js.map +1 -1
- package/package.json +2 -2
- package/dist/ws/ChannelSubscriptionManager.d.ts +0 -105
- package/dist/ws/ChannelSubscriptionManager.d.ts.map +0 -1
- package/dist/ws/ChannelSubscriptionManager.js +0 -413
- package/dist/ws/ChannelSubscriptionManager.js.map +0 -1
- package/dist/ws/launchpad/LaunchpadTokenDetailsService-method-action-types.d.ts +0 -35
- package/dist/ws/launchpad/LaunchpadTokenDetailsService-method-action-types.d.ts.map +0 -1
- package/dist/ws/launchpad/LaunchpadTokenDetailsService-method-action-types.js +0 -6
- package/dist/ws/launchpad/LaunchpadTokenDetailsService-method-action-types.js.map +0 -1
- package/dist/ws/launchpad/LaunchpadTokenDetailsService.d.ts +0 -115
- package/dist/ws/launchpad/LaunchpadTokenDetailsService.d.ts.map +0 -1
- package/dist/ws/launchpad/LaunchpadTokenDetailsService.js +0 -185
- package/dist/ws/launchpad/LaunchpadTokenDetailsService.js.map +0 -1
- package/dist/ws/launchpad/index.d.ts +0 -6
- package/dist/ws/launchpad/index.d.ts.map +0 -1
- package/dist/ws/launchpad/index.js +0 -4
- package/dist/ws/launchpad/index.js.map +0 -1
- package/dist/ws/launchpad/normalize.d.ts +0 -38
- package/dist/ws/launchpad/normalize.d.ts.map +0 -1
- package/dist/ws/launchpad/normalize.js +0 -134
- package/dist/ws/launchpad/normalize.js.map +0 -1
- package/dist/ws/launchpad/types.d.ts +0 -141
- package/dist/ws/launchpad/types.d.ts.map +0 -1
- package/dist/ws/launchpad/types.js +0 -11
- package/dist/ws/launchpad/types.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,21 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
11
|
|
|
12
|
-
- Add `LaunchpadTokenDetailsService` for real-time launchpad token detail streaming over the shared backend WebSocket, exposing `subscribe`/`unsubscribe` by `assetId` and publishing `tokenUpdated`, `statusChanged`, and `subscriptionError` events, along with `LAUNCHPAD_TDP_SERVICE_ALLOWED_ACTIONS`/`LAUNCHPAD_TDP_SERVICE_ALLOWED_EVENTS` and launchpad event types/normalizers (`parseBatch`, `normalizeEvent`, `buildChannel`) ([#10884](https://github.com/MetaMask/core/pull/10884))
|
|
13
|
-
- Add `ChannelSubscriptionManager`, a reusable per-channel WebSocket subscription lifecycle helper (reference counting, grace-period unsubscribe, unsubscribe retry, and resubscribe on reconnect) shared by `OHLCVService` and `LaunchpadTokenDetailsService` ([#10884](https://github.com/MetaMask/core/pull/10884))
|
|
14
12
|
- Add support for Node.js 26 ([#10981](https://github.com/MetaMask/core/pull/10981))
|
|
15
13
|
|
|
16
14
|
### Changed
|
|
17
15
|
|
|
18
|
-
- **BREAKING:** Remove the unused `BackendWebSocketService:getConnectionInfo` and `BackendWebSocketService:findSubscriptionsByChannelPrefix` actions from `OHLCV_SERVICE_ALLOWED_ACTIONS` ([#10884](https://github.com/MetaMask/core/pull/10884))
|
|
19
|
-
- `OHLCV_SERVICE_ALLOWED_ACTIONS` / `OHLCV_SERVICE_ALLOWED_EVENTS` and `LAUNCHPAD_TDP_SERVICE_ALLOWED_ACTIONS` / `LAUNCHPAD_TDP_SERVICE_ALLOWED_EVENTS` are now sourced from a single shared set (the `BackendWebSocketService` actions/events the `ChannelSubscriptionManager` actually calls), so both services grant identical, minimal permissions.
|
|
20
|
-
- If you build the `OHLCVService` restricted messenger from `OHLCV_SERVICE_ALLOWED_ACTIONS`, it will no longer be granted those two actions. Neither action was ever called by `OHLCVService`, so no runtime behavior changes; grant them explicitly only if your own code calls them through that messenger.
|
|
21
|
-
- Refactor `OHLCVService` to delegate its subscription lifecycle to the shared `ChannelSubscriptionManager` via composition, with no change to its public methods or published events ([#10884](https://github.com/MetaMask/core/pull/10884))
|
|
22
16
|
- Stop publishing the `api/test-utils` test helper, which was never reachable through the package's `exports` field ([#10916](https://github.com/MetaMask/core/pull/10916))
|
|
23
17
|
- Bump `@metamask/account-tree-controller` from `^11.0.1` to `^11.2.0` ([#10891](https://github.com/MetaMask/core/pull/10891), [#10925](https://github.com/MetaMask/core/pull/10925), [#11092](https://github.com/MetaMask/core/pull/11092))
|
|
24
18
|
- Bump `@metamask/profile-sync-controller` from `^34.0.3` to `^36.0.0` ([#10905](https://github.com/MetaMask/core/pull/10905), [#10925](https://github.com/MetaMask/core/pull/10925), [#11092](https://github.com/MetaMask/core/pull/11092))
|
|
25
19
|
- Bump `@metamask/utils` from `^12.0.0` to `^12.0.1` ([#11107](https://github.com/MetaMask/core/pull/11107))
|
|
26
|
-
- Bump `uuid` from `^11.1.1` to `^14.0.0` ([#10941](https://github.com/MetaMask/core/pull/10941))
|
|
27
20
|
|
|
28
21
|
## [12.3.0]
|
|
29
22
|
|
package/dist/index.d.ts
CHANGED
|
@@ -7,10 +7,6 @@ export { ApiPlatformClientService, apiPlatformClientServiceName, } from './ApiPl
|
|
|
7
7
|
export type { ApiPlatformClientServiceOptions, ApiPlatformClientServiceActions, ApiPlatformClientServiceEvents, ApiPlatformClientServiceMessenger, } from './ApiPlatformClientService.js';
|
|
8
8
|
export { OHLCVService, OHLCV_SERVICE_ALLOWED_ACTIONS, OHLCV_SERVICE_ALLOWED_EVENTS, } from './ws/ohlcv/index.js';
|
|
9
9
|
export type { OHLCVBar, OHLCVSubscriptionOptions, OHLCVSystemNotificationData, OHLCVServiceOptions, OHLCVServiceActions, OHLCVServiceAllowedActions, OHLCVServiceBarUpdatedEvent, OHLCVServiceChainStatusChangedEvent, OHLCVServiceSubscriptionErrorEvent, OHLCVServiceEvents, OHLCVServiceAllowedEvents, OHLCVServiceMessenger, } from './ws/ohlcv/index.js';
|
|
10
|
-
export { ChannelSubscriptionManager } from './ws/ChannelSubscriptionManager.js';
|
|
11
|
-
export type { ChannelSubscriptionManagerMessenger, ChannelSubscriptionManagerOptions, ChannelSubscriptionOperation, } from './ws/ChannelSubscriptionManager.js';
|
|
12
|
-
export { LaunchpadTokenDetailsService, LAUNCHPAD_TDP_SERVICE_ALLOWED_ACTIONS, LAUNCHPAD_TDP_SERVICE_ALLOWED_EVENTS, buildChannel, normalizeEvent, parseBatch, LAUNCHPAD_SUBSCRIPTION_NAMESPACE, } from './ws/launchpad/index.js';
|
|
13
|
-
export type { LaunchpadSystemNotificationData, LaunchpadTokenDetailsServiceOptions, LaunchpadTokenDetailsServiceActions, LaunchpadTokenDetailsServiceAllowedActions, LaunchpadTokenDetailsServiceTokenUpdatedEvent, LaunchpadTokenDetailsServiceStatusChangedEvent, LaunchpadTokenDetailsServiceSubscriptionErrorEvent, LaunchpadTokenDetailsServiceEvents, LaunchpadTokenDetailsServiceAllowedEvents, LaunchpadTokenDetailsServiceMessenger, LaunchpadEventType, RawLaunchpadTokenEvent, LaunchpadTokenCreatedEvent, LaunchpadTokenStatsEvent, LaunchpadTokenMigratedEvent, LaunchpadTokenEvent, LaunchpadTokenDetailsSubscriptionOptions, } from './ws/launchpad/index.js';
|
|
14
10
|
export { ApiPlatformClient, createApiPlatformClient, AccountsApiClient, PricesApiClient, TokenApiClient, TokensApiClient, API_URLS, STALE_TIMES, GC_TIMES, V6_DEFI_POSITION_TYPES, calculateRetryDelay, getQueryOptionsOverrides, shouldRetry, HttpError, } from './api/index.js';
|
|
15
11
|
export type { ApiPlatformClientOptions, ApiUrls, FetchOptions, PageInfo, SupportedCurrency, MarketDataDetails, V5BalanceItem, V5BalancesResponse, V2BalanceItem, V2BalancesResponse, V4BalancesResponse, V6VsCurrency, V6DeFiPositionType, V6BalanceMetadata, V6StellarTokenBalanceMetadata, V6TokenBalanceMetadata, V6BalanceItem, V6BalancesResponse, V1SupportedNetworksResponse, V2SupportedNetworksResponse, V2ActiveNetworksResponse, V1TransactionByHashResponse, V1AccountTransactionsResponse, V4MultiAccountTransactionsResponse, ValueTransfer, V1AccountRelationshipResult, NftItem, V2NftsResponse, TokenDiscoveryItem, V2TokensResponse, V3SpotPricesResponse, CoinGeckoSpotPrice, ExchangeRateInfo, V1ExchangeRatesResponse, PriceSupportedNetworksResponse, PriceV1SupportedNetworksResponse, PriceV2SupportedNetworksResponse, V1HistoricalPricesResponse, V3HistoricalPricesResponse, TokenMetadata, TokenListQueryOptions, TokenV2SupportedNetworksResponse, V1TokenDescriptionResponse, NetworkInfo, TopAsset, TrendingSortBy, TrendingToken, TopGainersSortOption, TrendingSortOption, V1SuggestedOccurrenceFloorsResponse, V1TokenSupportedNetworksResponse, V2TokenSupportedNetworksResponse, V3AssetResponse, } from './api/index.js';
|
|
16
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,cAAc,EACd,kBAAkB,GACnB,MAAM,iCAAiC,CAAC;AAEzC,YAAY,EACV,8BAA8B,EAC9B,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,EACzB,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,uBAAuB,EACvB,8BAA8B,EAC9B,kDAAkD,EAClD,6BAA6B,EAC7B,gCAAgC,GACjC,MAAM,iCAAiC,CAAC;AAMzC,OAAO,EACL,sBAAsB,EACtB,wCAAwC,EACxC,uCAAuC,GACxC,MAAM,gCAAgC,CAAC;AAExC,YAAY,EACV,sBAAsB,EACtB,mBAAmB,EACnB,6BAA6B,EAC7B,6BAA6B,EAC7B,cAAc,IAAI,oCAAoC,EACtD,6CAA6C,EAC7C,yCAAyC,EACzC,4CAA4C,EAC5C,wCAAwC,EACxC,4BAA4B,EAC5B,aAAa,IAAI,mCAAmC,EACpD,+BAA+B,GAChC,MAAM,gCAAgC,CAAC;AAMxC,YAAY,EACV,WAAW,EACX,KAAK,EACL,OAAO,EACP,QAAQ,EACR,aAAa,EACb,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAMpB,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,+BAA+B,CAAC;AAEvC,YAAY,EACV,+BAA+B,EAC/B,+BAA+B,EAC/B,8BAA8B,EAC9B,iCAAiC,GAClC,MAAM,+BAA+B,CAAC;AAMvC,OAAO,EACL,YAAY,EACZ,6BAA6B,EAC7B,4BAA4B,GAC7B,MAAM,qBAAqB,CAAC;AAE7B,YAAY,EACV,QAAQ,EACR,wBAAwB,EACxB,2BAA2B,EAC3B,mBAAmB,EACnB,mBAAmB,EACnB,0BAA0B,EAC1B,2BAA2B,EAC3B,mCAAmC,EACnC,kCAAkC,EAClC,kBAAkB,EAClB,yBAAyB,EACzB,qBAAqB,GACtB,MAAM,qBAAqB,CAAC;AAM7B,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,cAAc,EACd,kBAAkB,GACnB,MAAM,iCAAiC,CAAC;AAEzC,YAAY,EACV,8BAA8B,EAC9B,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,EACzB,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,uBAAuB,EACvB,8BAA8B,EAC9B,kDAAkD,EAClD,6BAA6B,EAC7B,gCAAgC,GACjC,MAAM,iCAAiC,CAAC;AAMzC,OAAO,EACL,sBAAsB,EACtB,wCAAwC,EACxC,uCAAuC,GACxC,MAAM,gCAAgC,CAAC;AAExC,YAAY,EACV,sBAAsB,EACtB,mBAAmB,EACnB,6BAA6B,EAC7B,6BAA6B,EAC7B,cAAc,IAAI,oCAAoC,EACtD,6CAA6C,EAC7C,yCAAyC,EACzC,4CAA4C,EAC5C,wCAAwC,EACxC,4BAA4B,EAC5B,aAAa,IAAI,mCAAmC,EACpD,+BAA+B,GAChC,MAAM,gCAAgC,CAAC;AAMxC,YAAY,EACV,WAAW,EACX,KAAK,EACL,OAAO,EACP,QAAQ,EACR,aAAa,EACb,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAMpB,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,+BAA+B,CAAC;AAEvC,YAAY,EACV,+BAA+B,EAC/B,+BAA+B,EAC/B,8BAA8B,EAC9B,iCAAiC,GAClC,MAAM,+BAA+B,CAAC;AAMvC,OAAO,EACL,YAAY,EACZ,6BAA6B,EAC7B,4BAA4B,GAC7B,MAAM,qBAAqB,CAAC;AAE7B,YAAY,EACV,QAAQ,EACR,wBAAwB,EACxB,2BAA2B,EAC3B,mBAAmB,EACnB,mBAAmB,EACnB,0BAA0B,EAC1B,2BAA2B,EAC3B,mCAAmC,EACnC,kCAAkC,EAClC,kBAAkB,EAClB,yBAAyB,EACzB,qBAAqB,GACtB,MAAM,qBAAqB,CAAC;AAM7B,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EAEvB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,eAAe,EAEf,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,sBAAsB,EAEtB,mBAAmB,EACnB,wBAAwB,EACxB,WAAW,EAEX,SAAS,GACV,MAAM,gBAAgB,CAAC;AAMxB,YAAY,EAEV,wBAAwB,EACxB,OAAO,EACP,YAAY,EAEZ,QAAQ,EACR,iBAAiB,EACjB,iBAAiB,EAEjB,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,6BAA6B,EAC7B,sBAAsB,EACtB,aAAa,EACb,kBAAkB,EAClB,2BAA2B,EAC3B,2BAA2B,EAC3B,wBAAwB,EACxB,2BAA2B,EAC3B,6BAA6B,EAC7B,kCAAkC,EAClC,aAAa,EACb,2BAA2B,EAC3B,OAAO,EACP,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAEhB,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,uBAAuB,EACvB,8BAA8B,EAC9B,gCAAgC,EAChC,gCAAgC,EAChC,0BAA0B,EAC1B,0BAA0B,EAE1B,aAAa,EACb,qBAAqB,EACrB,gCAAgC,EAChC,0BAA0B,EAC1B,WAAW,EACX,QAAQ,EACR,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,kBAAkB,EAClB,mCAAmC,EAEnC,gCAAgC,EAChC,gCAAgC,EAChC,eAAe,GAChB,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -16,14 +16,6 @@ export { ApiPlatformClientService, apiPlatformClientServiceName, } from './ApiPl
|
|
|
16
16
|
// ============================================================================
|
|
17
17
|
export { OHLCVService, OHLCV_SERVICE_ALLOWED_ACTIONS, OHLCV_SERVICE_ALLOWED_EVENTS, } from './ws/ohlcv/index.js';
|
|
18
18
|
// ============================================================================
|
|
19
|
-
// CHANNEL SUBSCRIPTION MANAGER
|
|
20
|
-
// ============================================================================
|
|
21
|
-
export { ChannelSubscriptionManager } from './ws/ChannelSubscriptionManager.js';
|
|
22
|
-
// ============================================================================
|
|
23
|
-
// LAUNCHPAD TOKEN DETAILS SERVICE
|
|
24
|
-
// ============================================================================
|
|
25
|
-
export { LaunchpadTokenDetailsService, LAUNCHPAD_TDP_SERVICE_ALLOWED_ACTIONS, LAUNCHPAD_TDP_SERVICE_ALLOWED_EVENTS, buildChannel, normalizeEvent, parseBatch, LAUNCHPAD_SUBSCRIPTION_NAMESPACE, } from './ws/launchpad/index.js';
|
|
26
|
-
// ============================================================================
|
|
27
19
|
// API PLATFORM CLIENT
|
|
28
20
|
// ============================================================================
|
|
29
21
|
export { ApiPlatformClient, createApiPlatformClient,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAE/B,+EAA+E;AAC/E,4BAA4B;AAC5B,+EAA+E;AAE/E,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,cAAc,EACd,kBAAkB,GACnB,MAAM,iCAAiC,CAAC;AAiBzC,+EAA+E;AAC/E,2BAA2B;AAC3B,+EAA+E;AAE/E,OAAO,EACL,sBAAsB,EACtB,wCAAwC,EACxC,uCAAuC,GACxC,MAAM,gCAAgC,CAAC;AA8BxC,+EAA+E;AAC/E,8BAA8B;AAC9B,+EAA+E;AAE/E,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,+BAA+B,CAAC;AASvC,+EAA+E;AAC/E,gBAAgB;AAChB,+EAA+E;AAE/E,OAAO,EACL,YAAY,EACZ,6BAA6B,EAC7B,4BAA4B,GAC7B,MAAM,qBAAqB,CAAC;AAiB7B,+EAA+E;AAC/E
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAE/B,+EAA+E;AAC/E,4BAA4B;AAC5B,+EAA+E;AAE/E,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,cAAc,EACd,kBAAkB,GACnB,MAAM,iCAAiC,CAAC;AAiBzC,+EAA+E;AAC/E,2BAA2B;AAC3B,+EAA+E;AAE/E,OAAO,EACL,sBAAsB,EACtB,wCAAwC,EACxC,uCAAuC,GACxC,MAAM,gCAAgC,CAAC;AA8BxC,+EAA+E;AAC/E,8BAA8B;AAC9B,+EAA+E;AAE/E,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,+BAA+B,CAAC;AASvC,+EAA+E;AAC/E,gBAAgB;AAChB,+EAA+E;AAE/E,OAAO,EACL,YAAY,EACZ,6BAA6B,EAC7B,4BAA4B,GAC7B,MAAM,qBAAqB,CAAC;AAiB7B,+EAA+E;AAC/E,sBAAsB;AACtB,+EAA+E;AAE/E,OAAO,EACL,iBAAiB,EACjB,uBAAuB;AACvB,yBAAyB;AACzB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,eAAe;AACf,YAAY;AACZ,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,sBAAsB;AACtB,UAAU;AACV,mBAAmB,EACnB,wBAAwB,EACxB,WAAW;AACX,SAAS;AACT,SAAS,GACV,MAAM,gBAAgB,CAAC","sourcesContent":["// Core Backend Package Exports\n\n// ============================================================================\n// BACKEND WEBSOCKET SERVICE\n// ============================================================================\n\nexport {\n BackendWebSocketService,\n getCloseReason,\n WebSocketState,\n WebSocketEventType,\n} from './ws/BackendWebSocketService.js';\n\nexport type {\n BackendWebSocketServiceOptions,\n ClientRequestMessage,\n ServerResponseMessage,\n ServerNotificationMessage,\n WebSocketMessage,\n ChannelCallback,\n WebSocketSubscription,\n WebSocketConnectionInfo,\n BackendWebSocketServiceActions,\n BackendWebSocketServiceConnectionStateChangedEvent,\n BackendWebSocketServiceEvents,\n BackendWebSocketServiceMessenger,\n} from './ws/BackendWebSocketService.js';\n\n// ============================================================================\n// ACCOUNT ACTIVITY SERVICE\n// ============================================================================\n\nexport {\n AccountActivityService,\n ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS,\n ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS,\n} from './ws/AccountActivityService.js';\n\nexport type {\n SystemNotificationData,\n SubscriptionOptions,\n AccountActivityServiceOptions,\n AccountActivityServiceActions,\n AllowedActions as AccountActivityServiceAllowedActions,\n AccountActivityServiceTransactionUpdatedEvent,\n AccountActivityServiceBalanceUpdatedEvent,\n AccountActivityServiceSubscriptionErrorEvent,\n AccountActivityServiceStatusChangedEvent,\n AccountActivityServiceEvents,\n AllowedEvents as AccountActivityServiceAllowedEvents,\n AccountActivityServiceMessenger,\n} from './ws/AccountActivityService.js';\n\n// ============================================================================\n// SHARED TYPES\n// ============================================================================\n\nexport type {\n Transaction,\n Asset,\n Balance,\n Transfer,\n BalanceUpdate,\n AccountActivityMessage,\n} from './types.js';\n\n// ============================================================================\n// API PLATFORM CLIENT SERVICE\n// ============================================================================\n\nexport {\n ApiPlatformClientService,\n apiPlatformClientServiceName,\n} from './ApiPlatformClientService.js';\n\nexport type {\n ApiPlatformClientServiceOptions,\n ApiPlatformClientServiceActions,\n ApiPlatformClientServiceEvents,\n ApiPlatformClientServiceMessenger,\n} from './ApiPlatformClientService.js';\n\n// ============================================================================\n// OHLCV SERVICE\n// ============================================================================\n\nexport {\n OHLCVService,\n OHLCV_SERVICE_ALLOWED_ACTIONS,\n OHLCV_SERVICE_ALLOWED_EVENTS,\n} from './ws/ohlcv/index.js';\n\nexport type {\n OHLCVBar,\n OHLCVSubscriptionOptions,\n OHLCVSystemNotificationData,\n OHLCVServiceOptions,\n OHLCVServiceActions,\n OHLCVServiceAllowedActions,\n OHLCVServiceBarUpdatedEvent,\n OHLCVServiceChainStatusChangedEvent,\n OHLCVServiceSubscriptionErrorEvent,\n OHLCVServiceEvents,\n OHLCVServiceAllowedEvents,\n OHLCVServiceMessenger,\n} from './ws/ohlcv/index.js';\n\n// ============================================================================\n// API PLATFORM CLIENT\n// ============================================================================\n\nexport {\n ApiPlatformClient,\n createApiPlatformClient,\n // Individual API clients\n AccountsApiClient,\n PricesApiClient,\n TokenApiClient,\n TokensApiClient,\n // Constants\n API_URLS,\n STALE_TIMES,\n GC_TIMES,\n V6_DEFI_POSITION_TYPES,\n // Helpers\n calculateRetryDelay,\n getQueryOptionsOverrides,\n shouldRetry,\n // Errors\n HttpError,\n} from './api/index.js';\n\n// ============================================================================\n// API PLATFORM CLIENT TYPES\n// ============================================================================\n\nexport type {\n // Client options\n ApiPlatformClientOptions,\n ApiUrls,\n FetchOptions,\n // Shared types\n PageInfo,\n SupportedCurrency,\n MarketDataDetails,\n // Accounts API types\n V5BalanceItem,\n V5BalancesResponse,\n V2BalanceItem,\n V2BalancesResponse,\n V4BalancesResponse,\n V6VsCurrency,\n V6DeFiPositionType,\n V6BalanceMetadata,\n V6StellarTokenBalanceMetadata,\n V6TokenBalanceMetadata,\n V6BalanceItem,\n V6BalancesResponse,\n V1SupportedNetworksResponse,\n V2SupportedNetworksResponse,\n V2ActiveNetworksResponse,\n V1TransactionByHashResponse,\n V1AccountTransactionsResponse,\n V4MultiAccountTransactionsResponse,\n ValueTransfer,\n V1AccountRelationshipResult,\n NftItem,\n V2NftsResponse,\n TokenDiscoveryItem,\n V2TokensResponse,\n // Prices API types\n V3SpotPricesResponse,\n CoinGeckoSpotPrice,\n ExchangeRateInfo,\n V1ExchangeRatesResponse,\n PriceSupportedNetworksResponse,\n PriceV1SupportedNetworksResponse,\n PriceV2SupportedNetworksResponse,\n V1HistoricalPricesResponse,\n V3HistoricalPricesResponse,\n // Token API types\n TokenMetadata,\n TokenListQueryOptions,\n TokenV2SupportedNetworksResponse,\n V1TokenDescriptionResponse,\n NetworkInfo,\n TopAsset,\n TrendingSortBy,\n TrendingToken,\n TopGainersSortOption,\n TrendingSortOption,\n V1SuggestedOccurrenceFloorsResponse,\n // Tokens API types\n V1TokenSupportedNetworksResponse,\n V2TokenSupportedNetworksResponse,\n V3AssetResponse,\n} from './api/index.js';\n"]}
|
|
@@ -3,11 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Wraps {@link BackendWebSocketService} through the messenger pattern to
|
|
5
5
|
* provide subscribe/unsubscribe semantics for OHLCV market-data channels.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* shared {@link ChannelSubscriptionManager} by composition; this service keeps
|
|
9
|
-
* only the OHLCV domain concerns (channel-string building, bar forwarding, and
|
|
10
|
-
* chain-status system notifications).
|
|
6
|
+
* Includes reference counting, grace-period unsubscribe, idempotency checks,
|
|
7
|
+
* chain-status forwarding, and automatic resubscription on reconnect.
|
|
11
8
|
*/
|
|
12
9
|
import type { TraceCallback } from '@metamask/controller-utils';
|
|
13
10
|
import type { Messenger } from '@metamask/messenger';
|
|
@@ -32,8 +29,8 @@ export type OHLCVServiceOptions = {
|
|
|
32
29
|
traceFn?: TraceCallback;
|
|
33
30
|
};
|
|
34
31
|
export type OHLCVServiceActions = OHLCVServiceMethodActions;
|
|
35
|
-
export declare const OHLCV_SERVICE_ALLOWED_ACTIONS: readonly [
|
|
36
|
-
export declare const OHLCV_SERVICE_ALLOWED_EVENTS: readonly [
|
|
32
|
+
export declare const OHLCV_SERVICE_ALLOWED_ACTIONS: readonly ['BackendWebSocketService:connect', 'BackendWebSocketService:forceReconnection', 'BackendWebSocketService:subscribe', 'BackendWebSocketService:getConnectionInfo', 'BackendWebSocketService:channelHasSubscription', 'BackendWebSocketService:getSubscriptionsByChannel', 'BackendWebSocketService:findSubscriptionsByChannelPrefix', 'BackendWebSocketService:addChannelCallback', 'BackendWebSocketService:removeChannelCallback'];
|
|
33
|
+
export declare const OHLCV_SERVICE_ALLOWED_EVENTS: readonly ['BackendWebSocketService:connectionStateChanged'];
|
|
37
34
|
export type AllowedActions = BackendWebSocketServiceMethodActions;
|
|
38
35
|
export type OHLCVServiceBarUpdatedEvent = {
|
|
39
36
|
type: `OHLCVService:barUpdated`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OHLCVService.d.ts","sourceRoot":"","sources":["../../../src/ws/ohlcv/OHLCVService.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"OHLCVService.d.ts","sourceRoot":"","sources":["../../../src/ws/ohlcv/OHLCVService.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,aAAa,EAGd,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAKrD,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,mDAAmD,CAAC;AAC9G,OAAO,KAAK,EAEV,kDAAkD,EAEnD,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,uCAAuC,CAAC;AACvF,OAAO,KAAK,EAAE,QAAQ,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAMrE,QAAA,MAAM,YAAY,iBAAiB,CAAC;AAqCpC;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAMF;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,kFAAkF;IAClF,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB,CAAC;AAMF,MAAM,MAAM,mBAAmB,GAAG,yBAAyB,CAAC;AAE5D,eAAO,MAAM,6BAA6B,YACxC,iCAAiC,EACjC,2CAA2C,EAC3C,mCAAmC,EACnC,2CAA2C,EAC3C,gDAAgD,EAChD,mDAAmD,EACnD,0DAA0D,EAC1D,4CAA4C,EAC5C,+CAA+C,CACvC,CAAC;AAEX,eAAO,MAAM,4BAA4B,YACvC,gDAAgD,CACxC,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,oCAAoC,CAAC;AAIlE,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,EAAE,yBAAyB,CAAC;IAChC,OAAO,EAAE,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,QAAQ,CAAA;KAAE,CAAC,CAAC;CAC/C,CAAC;AAEF,MAAM,MAAM,mCAAmC,GAAG;IAChD,IAAI,EAAE,iCAAiC,CAAC;IACxC,OAAO,EAAE,CAAC;QAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,IAAI,GAAG,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC9E,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAAG;IAC/C,IAAI,EAAE,gCAAgC,CAAC;IACvC,OAAO,EAAE,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAC1B,2BAA2B,GAC3B,mCAAmC,GACnC,kCAAkC,CAAC;AAEvC,MAAM,MAAM,aAAa,GAAG,kDAAkD,CAAC;AAE/E,MAAM,MAAM,qBAAqB,GAAG,SAAS,CAC3C,OAAO,YAAY,EACnB,mBAAmB,GAAG,cAAc,EACpC,kBAAkB,GAAG,aAAa,CACnC,CAAC;AAMF;;;;;;;;;;;;;;GAcG;AACH,qBAAa,YAAY;;IACvB,QAAQ,CAAC,IAAI,kBAAgB;IAgB7B,YACE,OAAO,EAAE,mBAAmB,GAAG;QAAE,SAAS,EAAE,qBAAqB,CAAA;KAAE,EAsBpE;IAED;;OAEG;IACH,IAAI,IAAI,IAAI,CAOX;IAMD;;;;;;;;OAQG;IACG,SAAS,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQhE;IA+FD;;;;;;;OAOG;IACG,WAAW,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQlE;IAoUD;;OAEG;IACH,OAAO,IAAI,IAAI,CAWd;CACF"}
|
|
@@ -3,15 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Wraps {@link BackendWebSocketService} through the messenger pattern to
|
|
5
5
|
* provide subscribe/unsubscribe semantics for OHLCV market-data channels.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* shared {@link ChannelSubscriptionManager} by composition; this service keeps
|
|
9
|
-
* only the OHLCV domain concerns (channel-string building, bar forwarding, and
|
|
10
|
-
* chain-status system notifications).
|
|
6
|
+
* Includes reference counting, grace-period unsubscribe, idempotency checks,
|
|
7
|
+
* chain-status forwarding, and automatic resubscription on reconnect.
|
|
11
8
|
*/
|
|
9
|
+
import { Mutex } from 'async-mutex';
|
|
10
|
+
import { handleAll, IterableBackoff, retry } from 'cockatiel';
|
|
12
11
|
import { projectLogger, createModuleLogger } from '../../logger.js';
|
|
13
12
|
import { WebSocketState } from '../BackendWebSocketService.js';
|
|
14
|
-
import { ChannelSubscriptionManager, CHANNEL_SUBSCRIPTION_MANAGER_ALLOWED_ACTIONS, CHANNEL_SUBSCRIPTION_MANAGER_ALLOWED_EVENTS, } from '../ChannelSubscriptionManager.js';
|
|
15
13
|
// =============================================================================
|
|
16
14
|
// Constants
|
|
17
15
|
// =============================================================================
|
|
@@ -20,11 +18,29 @@ const log = createModuleLogger(projectLogger, SERVICE_NAME);
|
|
|
20
18
|
const MESSENGER_EXPOSED_METHODS = ['subscribe', 'unsubscribe'];
|
|
21
19
|
const SUBSCRIPTION_NAMESPACE = 'market-data.v1';
|
|
22
20
|
const SYSTEM_NOTIFICATIONS_CHANNEL = `system-notifications.v1.${SUBSCRIPTION_NAMESPACE}`;
|
|
21
|
+
/** Delay before actually unsubscribing from a channel after refCount reaches 0. */
|
|
22
|
+
const GRACE_PERIOD_MS = 3_000;
|
|
23
|
+
/** Backoff delays between failed WebSocket unsubscribe attempts. */
|
|
24
|
+
const UNSUB_RETRY_DELAYS_MS = [1_000, 2_000, 4_000];
|
|
25
|
+
const unsubRetryPolicy = retry(handleAll, {
|
|
26
|
+
// Cockatiel stops retrying once the failure index reaches `maxAttempts`, so
|
|
27
|
+
// `length` here yields one initial attempt plus three delayed retries (4 total).
|
|
28
|
+
maxAttempts: UNSUB_RETRY_DELAYS_MS.length,
|
|
29
|
+
backoff: new IterableBackoff([...UNSUB_RETRY_DELAYS_MS]),
|
|
30
|
+
});
|
|
23
31
|
export const OHLCV_SERVICE_ALLOWED_ACTIONS = [
|
|
24
|
-
|
|
32
|
+
'BackendWebSocketService:connect',
|
|
33
|
+
'BackendWebSocketService:forceReconnection',
|
|
34
|
+
'BackendWebSocketService:subscribe',
|
|
35
|
+
'BackendWebSocketService:getConnectionInfo',
|
|
36
|
+
'BackendWebSocketService:channelHasSubscription',
|
|
37
|
+
'BackendWebSocketService:getSubscriptionsByChannel',
|
|
38
|
+
'BackendWebSocketService:findSubscriptionsByChannelPrefix',
|
|
39
|
+
'BackendWebSocketService:addChannelCallback',
|
|
40
|
+
'BackendWebSocketService:removeChannelCallback',
|
|
25
41
|
];
|
|
26
42
|
export const OHLCV_SERVICE_ALLOWED_EVENTS = [
|
|
27
|
-
|
|
43
|
+
'BackendWebSocketService:connectionStateChanged',
|
|
28
44
|
];
|
|
29
45
|
// =============================================================================
|
|
30
46
|
// Main Service Class
|
|
@@ -48,7 +64,8 @@ export class OHLCVService {
|
|
|
48
64
|
name = SERVICE_NAME;
|
|
49
65
|
#messenger;
|
|
50
66
|
#trace;
|
|
51
|
-
#
|
|
67
|
+
#channels = new Map();
|
|
68
|
+
#mutex = new Mutex();
|
|
52
69
|
#chainsUp = new Set();
|
|
53
70
|
// =============================================================================
|
|
54
71
|
// Constructor
|
|
@@ -58,27 +75,20 @@ export class OHLCVService {
|
|
|
58
75
|
this.#trace =
|
|
59
76
|
options.traceFn ??
|
|
60
77
|
((_request, fn) => fn?.());
|
|
61
|
-
this.#manager = new ChannelSubscriptionManager({
|
|
62
|
-
messenger: this.#messenger,
|
|
63
|
-
channelType: SUBSCRIPTION_NAMESPACE,
|
|
64
|
-
logPrefix: 'OHLCV-WS',
|
|
65
|
-
onMessage: (channel, notification) => this.#handleBarUpdate(channel, notification),
|
|
66
|
-
onSubscriptionError: (channel, error, operation) => this.#messenger.publish('OHLCVService:subscriptionError', {
|
|
67
|
-
channel,
|
|
68
|
-
error,
|
|
69
|
-
operation,
|
|
70
|
-
}),
|
|
71
|
-
systemNotificationsChannel: SYSTEM_NOTIFICATIONS_CHANNEL,
|
|
72
|
-
onSystemNotification: (notification) => this.#handleSystemNotification(notification),
|
|
73
|
-
onConnectionStateChange: (connectionInfo) => this.#handleConnectionStateChange(connectionInfo),
|
|
74
|
-
});
|
|
75
78
|
this.#messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
|
|
79
|
+
this.#messenger.subscribe('BackendWebSocketService:connectionStateChanged',
|
|
80
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
81
|
+
(connectionInfo) => this.#handleWebSocketStateChange(connectionInfo));
|
|
76
82
|
}
|
|
77
83
|
/**
|
|
78
84
|
* Register the system-notifications channel callback.
|
|
79
85
|
*/
|
|
80
86
|
init() {
|
|
81
|
-
|
|
87
|
+
log('OHLCV-WS: Initializing — registering system-notifications callback');
|
|
88
|
+
this.#messenger.call('BackendWebSocketService:addChannelCallback', {
|
|
89
|
+
channelName: SYSTEM_NOTIFICATIONS_CHANNEL,
|
|
90
|
+
callback: (notification) => this.#handleSystemNotification(notification),
|
|
91
|
+
});
|
|
82
92
|
}
|
|
83
93
|
// =============================================================================
|
|
84
94
|
// Public — Subscribe / Unsubscribe
|
|
@@ -93,7 +103,83 @@ export class OHLCVService {
|
|
|
93
103
|
* @returns A promise that resolves once the subscription is established.
|
|
94
104
|
*/
|
|
95
105
|
async subscribe(options) {
|
|
96
|
-
|
|
106
|
+
const channel = this.#buildChannel(options);
|
|
107
|
+
const releaseLock = await this.#mutex.acquire();
|
|
108
|
+
try {
|
|
109
|
+
await this.#subscribeInner(channel);
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
releaseLock();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async #subscribeInner(channel) {
|
|
116
|
+
const entry = this.#channels.get(channel);
|
|
117
|
+
if (entry?.retryAbort) {
|
|
118
|
+
entry.retryAbort.abort();
|
|
119
|
+
entry.retryAbort = undefined;
|
|
120
|
+
entry.refCount = 1;
|
|
121
|
+
if (this.#messenger.call('BackendWebSocketService:channelHasSubscription', channel)) {
|
|
122
|
+
await this.#flushOtherChannels(channel);
|
|
123
|
+
log('OHLCV-WS: Cancelled unsubscribe retry — reusing WS subscription', {
|
|
124
|
+
channel,
|
|
125
|
+
});
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
// WS subscription was lost — fall through to recreate it.
|
|
129
|
+
}
|
|
130
|
+
else if (entry?.gracePeriodTimer) {
|
|
131
|
+
clearTimeout(entry.gracePeriodTimer);
|
|
132
|
+
entry.gracePeriodTimer = undefined;
|
|
133
|
+
log('OHLCV-WS: Cancelled grace-period unsubscribe', {
|
|
134
|
+
channel,
|
|
135
|
+
});
|
|
136
|
+
if (this.#messenger.call('BackendWebSocketService:channelHasSubscription', channel)) {
|
|
137
|
+
await this.#flushOtherChannels(channel);
|
|
138
|
+
entry.refCount += 1;
|
|
139
|
+
log('OHLCV-WS: WS subscription still alive, bumped refCount', {
|
|
140
|
+
channel,
|
|
141
|
+
refCount: entry.refCount,
|
|
142
|
+
});
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// WS subscription was lost (e.g. after disconnect/reconnect) — fall
|
|
146
|
+
// through to recreate it. refCount is bumped only after success below.
|
|
147
|
+
}
|
|
148
|
+
else if (entry && entry.refCount > 0) {
|
|
149
|
+
entry.refCount += 1;
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
await this.#flushOtherChannels(channel);
|
|
153
|
+
try {
|
|
154
|
+
await this.#messenger.call('BackendWebSocketService:connect');
|
|
155
|
+
if (this.#messenger.call('BackendWebSocketService:channelHasSubscription', channel)) {
|
|
156
|
+
log('OHLCV-WS: Channel already has WS subscription (idempotency), skipping', {
|
|
157
|
+
channel,
|
|
158
|
+
});
|
|
159
|
+
this.#channels.set(channel, { refCount: 1 });
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
await this.#messenger.call('BackendWebSocketService:subscribe', {
|
|
163
|
+
channels: [channel],
|
|
164
|
+
channelType: SUBSCRIPTION_NAMESPACE,
|
|
165
|
+
callback: (notification) => {
|
|
166
|
+
this.#handleBarUpdate(channel, notification);
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
this.#channels.set(channel, { refCount: 1 });
|
|
170
|
+
log('OHLCV-WS: Subscribe succeeded — new WS subscription created', {
|
|
171
|
+
channel,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
log('OHLCV-WS: Subscription failed', { channel, error });
|
|
176
|
+
this.#channels.delete(channel);
|
|
177
|
+
this.#messenger.publish('OHLCVService:subscriptionError', {
|
|
178
|
+
channel,
|
|
179
|
+
error: String(error),
|
|
180
|
+
operation: 'subscribe',
|
|
181
|
+
});
|
|
182
|
+
}
|
|
97
183
|
}
|
|
98
184
|
/**
|
|
99
185
|
* Unsubscribe from an OHLCV channel. Decrements the reference count and,
|
|
@@ -104,7 +190,201 @@ export class OHLCVService {
|
|
|
104
190
|
* @returns A promise that resolves once the unsubscription is processed.
|
|
105
191
|
*/
|
|
106
192
|
async unsubscribe(options) {
|
|
107
|
-
|
|
193
|
+
const channel = this.#buildChannel(options);
|
|
194
|
+
const releaseLock = await this.#mutex.acquire();
|
|
195
|
+
try {
|
|
196
|
+
await this.#unsubscribeInner(channel);
|
|
197
|
+
}
|
|
198
|
+
finally {
|
|
199
|
+
releaseLock();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async #unsubscribeInner(channel) {
|
|
203
|
+
const entry = this.#channels.get(channel);
|
|
204
|
+
if (!entry || entry.refCount <= 0) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
entry.refCount -= 1;
|
|
208
|
+
if (entry.refCount > 0) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
entry.gracePeriodTimer = setTimeout(() => {
|
|
212
|
+
entry.gracePeriodTimer = undefined;
|
|
213
|
+
this.#performUnsubscribe(channel).catch(() => {
|
|
214
|
+
// no-op: retry scheduling and force-reconnection are handled internally
|
|
215
|
+
});
|
|
216
|
+
}, GRACE_PERIOD_MS);
|
|
217
|
+
}
|
|
218
|
+
// =============================================================================
|
|
219
|
+
// Private — WebSocket Subscription Helpers
|
|
220
|
+
// =============================================================================
|
|
221
|
+
/**
|
|
222
|
+
* Immediately unsubscribe other channels in grace or failed-cleanup state.
|
|
223
|
+
* Called while the subscribe mutex is held before opening a new channel.
|
|
224
|
+
*
|
|
225
|
+
* @param exceptChannel - Channel being subscribed; excluded from flush.
|
|
226
|
+
*/
|
|
227
|
+
async #flushOtherChannels(exceptChannel) {
|
|
228
|
+
for (const [channel, channelEntry] of this.#channels.entries()) {
|
|
229
|
+
if (channel === exceptChannel || channelEntry.refCount > 0) {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
this.#clearChannelTimers(channelEntry);
|
|
233
|
+
log('OHLCV-WS: Flushing grace-period channel before new subscribe', {
|
|
234
|
+
flushedChannel: channel,
|
|
235
|
+
newChannel: exceptChannel,
|
|
236
|
+
});
|
|
237
|
+
const success = await this.#unsubscribeChannelOnServer(channel);
|
|
238
|
+
if (success) {
|
|
239
|
+
this.#channels.delete(channel);
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
this.#scheduleUnsubscribeRetry(channel);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
#clearChannelTimers(entry) {
|
|
247
|
+
if (entry.gracePeriodTimer) {
|
|
248
|
+
clearTimeout(entry.gracePeriodTimer);
|
|
249
|
+
entry.gracePeriodTimer = undefined;
|
|
250
|
+
}
|
|
251
|
+
entry.retryAbort?.abort();
|
|
252
|
+
entry.retryAbort = undefined;
|
|
253
|
+
}
|
|
254
|
+
async #unsubscribeChannelOnServer(channel) {
|
|
255
|
+
try {
|
|
256
|
+
const subscriptions = this.#messenger.call('BackendWebSocketService:getSubscriptionsByChannel', channel);
|
|
257
|
+
for (const sub of subscriptions) {
|
|
258
|
+
await sub.unsubscribe();
|
|
259
|
+
}
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
log('OHLCV-WS: Unsubscription failed', { channel, error });
|
|
264
|
+
this.#messenger.publish('OHLCVService:subscriptionError', {
|
|
265
|
+
channel,
|
|
266
|
+
error: String(error),
|
|
267
|
+
operation: 'unsubscribe',
|
|
268
|
+
});
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
#scheduleUnsubscribeRetry(channel) {
|
|
273
|
+
const entry = this.#channels.get(channel);
|
|
274
|
+
if (!entry) {
|
|
275
|
+
// The channel was removed (e.g. destroy() or reconnect cleanup) while the
|
|
276
|
+
// unsubscribe was in flight. Do not resurrect it or start a new retry loop
|
|
277
|
+
// with an AbortController that teardown can no longer cancel — that could
|
|
278
|
+
// force a reconnection on the shared WebSocket after teardown.
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
entry.retryAbort?.abort();
|
|
282
|
+
entry.retryAbort = new AbortController();
|
|
283
|
+
const { signal } = entry.retryAbort;
|
|
284
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
285
|
+
this.#runUnsubRetryLoop(channel, signal);
|
|
286
|
+
}
|
|
287
|
+
async #runUnsubRetryLoop(channel, signal) {
|
|
288
|
+
try {
|
|
289
|
+
await unsubRetryPolicy.execute(async () => {
|
|
290
|
+
const releaseLock = await this.#mutex.acquire();
|
|
291
|
+
try {
|
|
292
|
+
const current = this.#channels.get(channel);
|
|
293
|
+
if (current && current.refCount > 0) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
const success = await this.#unsubscribeChannelOnServer(channel);
|
|
297
|
+
if (!success) {
|
|
298
|
+
throw new Error('unsubscribe failed');
|
|
299
|
+
}
|
|
300
|
+
this.#channels.delete(channel);
|
|
301
|
+
log('OHLCV-WS: WS unsubscribe completed', { channel });
|
|
302
|
+
}
|
|
303
|
+
finally {
|
|
304
|
+
releaseLock();
|
|
305
|
+
}
|
|
306
|
+
}, signal);
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
if (signal.aborted) {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
log('OHLCV-WS: Unsubscribe retries exhausted — forcing reconnection', {
|
|
313
|
+
channel,
|
|
314
|
+
});
|
|
315
|
+
this.#channels.delete(channel);
|
|
316
|
+
// Last resort: reconnects the shared BackendWebSocketService instance
|
|
317
|
+
// (AccountActivityService and other consumers share this connection).
|
|
318
|
+
// They resubscribe on CONNECTED; OHLCV only resubscribes refCount > 0.
|
|
319
|
+
await this.#messenger
|
|
320
|
+
.call('BackendWebSocketService:forceReconnection')
|
|
321
|
+
.catch(() => {
|
|
322
|
+
// no-op
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
async #performUnsubscribe(channel) {
|
|
327
|
+
const releaseLock = await this.#mutex.acquire();
|
|
328
|
+
try {
|
|
329
|
+
const entry = this.#channels.get(channel);
|
|
330
|
+
if (entry && entry.refCount > 0) {
|
|
331
|
+
log('OHLCV-WS: Skipping unsubscribe — new subscriber arrived while queued', { channel, refCount: entry.refCount });
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
log('OHLCV-WS: Grace period expired — performing actual WS unsubscribe', {
|
|
335
|
+
channel,
|
|
336
|
+
});
|
|
337
|
+
this.#clearChannelTimers(this.#channels.get(channel) ?? { refCount: 0 });
|
|
338
|
+
}
|
|
339
|
+
finally {
|
|
340
|
+
releaseLock();
|
|
341
|
+
}
|
|
342
|
+
this.#scheduleUnsubscribeRetry(channel);
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Resubscribe all channels that were active before a disconnect.
|
|
346
|
+
* Called when WebSocket transitions to CONNECTED.
|
|
347
|
+
*/
|
|
348
|
+
async #resubscribeActiveChannels() {
|
|
349
|
+
const releaseLock = await this.#mutex.acquire();
|
|
350
|
+
try {
|
|
351
|
+
const channelCount = this.#channels.size;
|
|
352
|
+
log('OHLCV-WS: Resubscribing active channels after reconnect', {
|
|
353
|
+
count: channelCount,
|
|
354
|
+
});
|
|
355
|
+
for (const [channel, entry] of [...this.#channels.entries()]) {
|
|
356
|
+
if (entry.refCount === 0) {
|
|
357
|
+
this.#clearChannelTimers(entry);
|
|
358
|
+
this.#channels.delete(channel);
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
try {
|
|
362
|
+
if (this.#messenger.call('BackendWebSocketService:channelHasSubscription', channel)) {
|
|
363
|
+
log('OHLCV-WS: Channel already subscribed on server, skipping resubscribe', {
|
|
364
|
+
channel,
|
|
365
|
+
});
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
await this.#messenger.call('BackendWebSocketService:subscribe', {
|
|
369
|
+
channels: [channel],
|
|
370
|
+
channelType: SUBSCRIPTION_NAMESPACE,
|
|
371
|
+
callback: (notification) => {
|
|
372
|
+
this.#handleBarUpdate(channel, notification);
|
|
373
|
+
},
|
|
374
|
+
});
|
|
375
|
+
log('OHLCV-WS: Resubscription succeeded', { channel });
|
|
376
|
+
}
|
|
377
|
+
catch (error) {
|
|
378
|
+
log('OHLCV-WS: Resubscription failed for channel', {
|
|
379
|
+
channel,
|
|
380
|
+
error,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
finally {
|
|
386
|
+
releaseLock();
|
|
387
|
+
}
|
|
108
388
|
}
|
|
109
389
|
// =============================================================================
|
|
110
390
|
// Private — Message Handlers
|
|
@@ -146,22 +426,25 @@ export class OHLCVService {
|
|
|
146
426
|
status: data.status,
|
|
147
427
|
});
|
|
148
428
|
}
|
|
149
|
-
#
|
|
150
|
-
|
|
151
|
-
|
|
429
|
+
async #handleWebSocketStateChange(connectionInfo) {
|
|
430
|
+
const { state } = connectionInfo;
|
|
431
|
+
if (state === WebSocketState.CONNECTED) {
|
|
432
|
+
await this.#resubscribeActiveChannels();
|
|
152
433
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
434
|
+
else if (state === WebSocketState.DISCONNECTED) {
|
|
435
|
+
const chainsToMarkDown = Array.from(this.#chainsUp);
|
|
436
|
+
if (chainsToMarkDown.length > 0) {
|
|
437
|
+
this.#messenger.publish('OHLCVService:chainStatusChanged', {
|
|
438
|
+
chainIds: chainsToMarkDown,
|
|
439
|
+
status: 'down',
|
|
440
|
+
timestamp: Date.now(),
|
|
441
|
+
});
|
|
442
|
+
log('OHLCV-WS: WebSocket disconnection — marked tracked chains as down', {
|
|
443
|
+
count: chainsToMarkDown.length,
|
|
444
|
+
chains: chainsToMarkDown,
|
|
445
|
+
});
|
|
446
|
+
this.#chainsUp.clear();
|
|
447
|
+
}
|
|
165
448
|
}
|
|
166
449
|
}
|
|
167
450
|
// =============================================================================
|
|
@@ -177,8 +460,12 @@ export class OHLCVService {
|
|
|
177
460
|
* Destroy the service and clean up all resources.
|
|
178
461
|
*/
|
|
179
462
|
destroy() {
|
|
463
|
+
for (const entry of this.#channels.values()) {
|
|
464
|
+
this.#clearChannelTimers(entry);
|
|
465
|
+
}
|
|
466
|
+
this.#channels.clear();
|
|
180
467
|
this.#chainsUp.clear();
|
|
181
|
-
this.#
|
|
468
|
+
this.#messenger.call('BackendWebSocketService:removeChannelCallback', SYSTEM_NOTIFICATIONS_CHANNEL);
|
|
182
469
|
}
|
|
183
470
|
}
|
|
184
471
|
//# sourceMappingURL=OHLCVService.js.map
|