@metamask-previews/core-backend 6.2.2-preview-fbc0aed37 → 6.2.2-preview-e5cef32c6
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 +14 -0
- package/dist/api/base-client.cjs +5 -3
- package/dist/api/base-client.cjs.map +1 -1
- package/dist/api/base-client.d.cts +1 -1
- package/dist/api/base-client.d.cts.map +1 -1
- package/dist/api/base-client.d.mts +1 -1
- package/dist/api/base-client.d.mts.map +1 -1
- package/dist/api/base-client.mjs +5 -3
- package/dist/api/base-client.mjs.map +1 -1
- package/dist/index.cjs +10 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +6 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +6 -2
- package/dist/index.mjs.map +1 -1
- package/dist/ws/AccountActivityService-method-action-types.cjs.map +1 -0
- package/dist/ws/AccountActivityService-method-action-types.d.cts.map +1 -0
- package/dist/ws/AccountActivityService-method-action-types.d.mts.map +1 -0
- package/dist/ws/AccountActivityService-method-action-types.mjs.map +1 -0
- package/dist/{AccountActivityService.cjs → ws/AccountActivityService.cjs} +1 -1
- package/dist/ws/AccountActivityService.cjs.map +1 -0
- package/dist/{AccountActivityService.d.cts → ws/AccountActivityService.d.cts} +1 -1
- package/dist/ws/AccountActivityService.d.cts.map +1 -0
- package/dist/{AccountActivityService.d.mts → ws/AccountActivityService.d.mts} +1 -1
- package/dist/ws/AccountActivityService.d.mts.map +1 -0
- package/dist/{AccountActivityService.mjs → ws/AccountActivityService.mjs} +1 -1
- package/dist/ws/AccountActivityService.mjs.map +1 -0
- package/dist/ws/BackendWebSocketService-method-action-types.cjs.map +1 -0
- package/dist/ws/BackendWebSocketService-method-action-types.d.cts.map +1 -0
- package/dist/ws/BackendWebSocketService-method-action-types.d.mts.map +1 -0
- package/dist/ws/BackendWebSocketService-method-action-types.mjs.map +1 -0
- package/dist/{BackendWebSocketService.cjs → ws/BackendWebSocketService.cjs} +1 -1
- package/dist/ws/BackendWebSocketService.cjs.map +1 -0
- package/dist/ws/BackendWebSocketService.d.cts.map +1 -0
- package/dist/ws/BackendWebSocketService.d.mts.map +1 -0
- package/dist/{BackendWebSocketService.mjs → ws/BackendWebSocketService.mjs} +1 -1
- package/dist/ws/BackendWebSocketService.mjs.map +1 -0
- package/dist/ws/ohlcv/OHLCVService-method-action-types.cjs +7 -0
- package/dist/ws/ohlcv/OHLCVService-method-action-types.cjs.map +1 -0
- package/dist/ws/ohlcv/OHLCVService-method-action-types.d.cts +35 -0
- package/dist/ws/ohlcv/OHLCVService-method-action-types.d.cts.map +1 -0
- package/dist/ws/ohlcv/OHLCVService-method-action-types.d.mts +35 -0
- package/dist/ws/ohlcv/OHLCVService-method-action-types.d.mts.map +1 -0
- package/dist/ws/ohlcv/OHLCVService-method-action-types.mjs +6 -0
- package/dist/ws/ohlcv/OHLCVService-method-action-types.mjs.map +1 -0
- package/dist/ws/ohlcv/OHLCVService.cjs +359 -0
- package/dist/ws/ohlcv/OHLCVService.cjs.map +1 -0
- package/dist/ws/ohlcv/OHLCVService.d.cts +109 -0
- package/dist/ws/ohlcv/OHLCVService.d.cts.map +1 -0
- package/dist/ws/ohlcv/OHLCVService.d.mts +109 -0
- package/dist/ws/ohlcv/OHLCVService.d.mts.map +1 -0
- package/dist/ws/ohlcv/OHLCVService.mjs +355 -0
- package/dist/ws/ohlcv/OHLCVService.mjs.map +1 -0
- package/dist/ws/ohlcv/index.cjs +9 -0
- package/dist/ws/ohlcv/index.cjs.map +1 -0
- package/dist/ws/ohlcv/index.d.cts +5 -0
- package/dist/ws/ohlcv/index.d.cts.map +1 -0
- package/dist/ws/ohlcv/index.d.mts +5 -0
- package/dist/ws/ohlcv/index.d.mts.map +1 -0
- package/dist/ws/ohlcv/index.mjs +3 -0
- package/dist/ws/ohlcv/index.mjs.map +1 -0
- package/dist/ws/ohlcv/types.cjs +6 -0
- package/dist/ws/ohlcv/types.cjs.map +1 -0
- package/dist/ws/ohlcv/types.d.cts +32 -0
- package/dist/ws/ohlcv/types.d.cts.map +1 -0
- package/dist/ws/ohlcv/types.d.mts +32 -0
- package/dist/ws/ohlcv/types.d.mts.map +1 -0
- package/dist/ws/ohlcv/types.mjs +5 -0
- package/dist/ws/ohlcv/types.mjs.map +1 -0
- package/package.json +3 -2
- package/dist/AccountActivityService-method-action-types.cjs.map +0 -1
- package/dist/AccountActivityService-method-action-types.d.cts.map +0 -1
- package/dist/AccountActivityService-method-action-types.d.mts.map +0 -1
- package/dist/AccountActivityService-method-action-types.mjs.map +0 -1
- package/dist/AccountActivityService.cjs.map +0 -1
- package/dist/AccountActivityService.d.cts.map +0 -1
- package/dist/AccountActivityService.d.mts.map +0 -1
- package/dist/AccountActivityService.mjs.map +0 -1
- package/dist/BackendWebSocketService-method-action-types.cjs.map +0 -1
- package/dist/BackendWebSocketService-method-action-types.d.cts.map +0 -1
- package/dist/BackendWebSocketService-method-action-types.d.mts.map +0 -1
- package/dist/BackendWebSocketService-method-action-types.mjs.map +0 -1
- package/dist/BackendWebSocketService.cjs.map +0 -1
- package/dist/BackendWebSocketService.d.cts.map +0 -1
- package/dist/BackendWebSocketService.d.mts.map +0 -1
- package/dist/BackendWebSocketService.mjs.map +0 -1
- /package/dist/{AccountActivityService-method-action-types.cjs → ws/AccountActivityService-method-action-types.cjs} +0 -0
- /package/dist/{AccountActivityService-method-action-types.d.cts → ws/AccountActivityService-method-action-types.d.cts} +0 -0
- /package/dist/{AccountActivityService-method-action-types.d.mts → ws/AccountActivityService-method-action-types.d.mts} +0 -0
- /package/dist/{AccountActivityService-method-action-types.mjs → ws/AccountActivityService-method-action-types.mjs} +0 -0
- /package/dist/{BackendWebSocketService-method-action-types.cjs → ws/BackendWebSocketService-method-action-types.cjs} +0 -0
- /package/dist/{BackendWebSocketService-method-action-types.d.cts → ws/BackendWebSocketService-method-action-types.d.cts} +0 -0
- /package/dist/{BackendWebSocketService-method-action-types.d.mts → ws/BackendWebSocketService-method-action-types.d.mts} +0 -0
- /package/dist/{BackendWebSocketService-method-action-types.mjs → ws/BackendWebSocketService-method-action-types.mjs} +0 -0
- /package/dist/{BackendWebSocketService.d.cts → ws/BackendWebSocketService.d.cts} +0 -0
- /package/dist/{BackendWebSocketService.d.mts → ws/BackendWebSocketService.d.mts} +0 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is auto generated.
|
|
3
|
+
* Do not edit manually.
|
|
4
|
+
*/
|
|
5
|
+
import type { OHLCVService } from "./OHLCVService.mjs";
|
|
6
|
+
/**
|
|
7
|
+
* Subscribe to an OHLCV channel. If this is the first subscriber for the
|
|
8
|
+
* given asset/interval/currency combination a WebSocket subscription is
|
|
9
|
+
* created. Additional calls for the same combination only bump the reference
|
|
10
|
+
* count.
|
|
11
|
+
*
|
|
12
|
+
* @param options - The subscription parameters.
|
|
13
|
+
* @returns A promise that resolves once the subscription is established.
|
|
14
|
+
*/
|
|
15
|
+
export type OHLCVServiceSubscribeAction = {
|
|
16
|
+
type: `OHLCVService:subscribe`;
|
|
17
|
+
handler: OHLCVService['subscribe'];
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Unsubscribe from an OHLCV channel. Decrements the reference count and,
|
|
21
|
+
* when it reaches zero, starts a grace-period timer before actually
|
|
22
|
+
* unsubscribing from the WebSocket to absorb rapid navigation patterns.
|
|
23
|
+
*
|
|
24
|
+
* @param options - The subscription parameters to unsubscribe from.
|
|
25
|
+
* @returns A promise that resolves once the unsubscription is processed.
|
|
26
|
+
*/
|
|
27
|
+
export type OHLCVServiceUnsubscribeAction = {
|
|
28
|
+
type: `OHLCVService:unsubscribe`;
|
|
29
|
+
handler: OHLCVService['unsubscribe'];
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Union of all OHLCVService action types.
|
|
33
|
+
*/
|
|
34
|
+
export type OHLCVServiceMethodActions = OHLCVServiceSubscribeAction | OHLCVServiceUnsubscribeAction;
|
|
35
|
+
//# sourceMappingURL=OHLCVService-method-action-types.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OHLCVService-method-action-types.d.mts","sourceRoot":"","sources":["../../../src/ws/ohlcv/OHLCVService-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,2BAAuB;AAEnD;;;;;;;;GAQG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,EAAE,wBAAwB,CAAC;IAC/B,OAAO,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC;CACpC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,6BAA6B,GAAG;IAC1C,IAAI,EAAE,0BAA0B,CAAC;IACjC,OAAO,EAAE,YAAY,CAAC,aAAa,CAAC,CAAC;CACtC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,yBAAyB,GACjC,2BAA2B,GAC3B,6BAA6B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OHLCVService-method-action-types.mjs","sourceRoot":"","sources":["../../../src/ws/ohlcv/OHLCVService-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { OHLCVService } from './OHLCVService';\n\n/**\n * Subscribe to an OHLCV channel. If this is the first subscriber for the\n * given asset/interval/currency combination a WebSocket subscription is\n * created. Additional calls for the same combination only bump the reference\n * count.\n *\n * @param options - The subscription parameters.\n * @returns A promise that resolves once the subscription is established.\n */\nexport type OHLCVServiceSubscribeAction = {\n type: `OHLCVService:subscribe`;\n handler: OHLCVService['subscribe'];\n};\n\n/**\n * Unsubscribe from an OHLCV channel. Decrements the reference count and,\n * when it reaches zero, starts a grace-period timer before actually\n * unsubscribing from the WebSocket to absorb rapid navigation patterns.\n *\n * @param options - The subscription parameters to unsubscribe from.\n * @returns A promise that resolves once the unsubscription is processed.\n */\nexport type OHLCVServiceUnsubscribeAction = {\n type: `OHLCVService:unsubscribe`;\n handler: OHLCVService['unsubscribe'];\n};\n\n/**\n * Union of all OHLCVService action types.\n */\nexport type OHLCVServiceMethodActions =\n | OHLCVServiceSubscribeAction\n | OHLCVServiceUnsubscribeAction;\n"]}
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OHLCV Service for real-time candlestick data streaming via WebSocket.
|
|
4
|
+
*
|
|
5
|
+
* Wraps {@link BackendWebSocketService} through the messenger pattern to
|
|
6
|
+
* provide subscribe/unsubscribe semantics for OHLCV market-data channels.
|
|
7
|
+
* Includes reference counting, grace-period unsubscribe, idempotency checks,
|
|
8
|
+
* chain-status forwarding, and automatic resubscription on reconnect.
|
|
9
|
+
*/
|
|
10
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
11
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
12
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
13
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
14
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
15
|
+
};
|
|
16
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
17
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
18
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
19
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
20
|
+
};
|
|
21
|
+
var _OHLCVService_instances, _OHLCVService_messenger, _OHLCVService_trace, _OHLCVService_channels, _OHLCVService_mutex, _OHLCVService_chainsUp, _OHLCVService_subscribeInner, _OHLCVService_unsubscribeInner, _OHLCVService_performUnsubscribe, _OHLCVService_resubscribeActiveChannels, _OHLCVService_handleBarUpdate, _OHLCVService_handleSystemNotification, _OHLCVService_handleWebSocketStateChange, _OHLCVService_buildChannel;
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.OHLCVService = exports.OHLCV_SERVICE_ALLOWED_EVENTS = exports.OHLCV_SERVICE_ALLOWED_ACTIONS = void 0;
|
|
24
|
+
const async_mutex_1 = require("async-mutex");
|
|
25
|
+
const logger_1 = require("../../logger.cjs");
|
|
26
|
+
const BackendWebSocketService_1 = require("../BackendWebSocketService.cjs");
|
|
27
|
+
// =============================================================================
|
|
28
|
+
// Constants
|
|
29
|
+
// =============================================================================
|
|
30
|
+
const SERVICE_NAME = 'OHLCVService';
|
|
31
|
+
const log = (0, logger_1.createModuleLogger)(logger_1.projectLogger, SERVICE_NAME);
|
|
32
|
+
const MESSENGER_EXPOSED_METHODS = ['subscribe', 'unsubscribe'];
|
|
33
|
+
const SUBSCRIPTION_NAMESPACE = 'market-data.v1';
|
|
34
|
+
const SYSTEM_NOTIFICATIONS_CHANNEL = `system-notifications.v1.${SUBSCRIPTION_NAMESPACE}`;
|
|
35
|
+
/** Delay before actually unsubscribing from a channel after refCount reaches 0. */
|
|
36
|
+
const GRACE_PERIOD_MS = 3000;
|
|
37
|
+
exports.OHLCV_SERVICE_ALLOWED_ACTIONS = [
|
|
38
|
+
'BackendWebSocketService:connect',
|
|
39
|
+
'BackendWebSocketService:forceReconnection',
|
|
40
|
+
'BackendWebSocketService:subscribe',
|
|
41
|
+
'BackendWebSocketService:getConnectionInfo',
|
|
42
|
+
'BackendWebSocketService:channelHasSubscription',
|
|
43
|
+
'BackendWebSocketService:getSubscriptionsByChannel',
|
|
44
|
+
'BackendWebSocketService:findSubscriptionsByChannelPrefix',
|
|
45
|
+
'BackendWebSocketService:addChannelCallback',
|
|
46
|
+
'BackendWebSocketService:removeChannelCallback',
|
|
47
|
+
];
|
|
48
|
+
exports.OHLCV_SERVICE_ALLOWED_EVENTS = [
|
|
49
|
+
'BackendWebSocketService:connectionStateChanged',
|
|
50
|
+
];
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// Main Service Class
|
|
53
|
+
// =============================================================================
|
|
54
|
+
/**
|
|
55
|
+
* Service for real-time OHLCV candlestick streaming via the backend WebSocket
|
|
56
|
+
* gateway. Communicates with {@link BackendWebSocketService} exclusively
|
|
57
|
+
* through the messenger — no direct import of the class.
|
|
58
|
+
*
|
|
59
|
+
* Features:
|
|
60
|
+
* - Reference counting: multiple UI consumers share one WebSocket subscription
|
|
61
|
+
* - Grace-period unsubscribe: avoids rapid unsub/resub during navigation
|
|
62
|
+
* - Idempotency: duplicate subscribe calls for the same channel are no-ops
|
|
63
|
+
* - Reconnect resilience: resubscribes all active channels on reconnect
|
|
64
|
+
* - Chain-status forwarding: listens to system-notifications for chain up/down
|
|
65
|
+
*
|
|
66
|
+
*/
|
|
67
|
+
class OHLCVService {
|
|
68
|
+
// =============================================================================
|
|
69
|
+
// Constructor
|
|
70
|
+
// =============================================================================
|
|
71
|
+
constructor(options) {
|
|
72
|
+
_OHLCVService_instances.add(this);
|
|
73
|
+
this.name = SERVICE_NAME;
|
|
74
|
+
_OHLCVService_messenger.set(this, void 0);
|
|
75
|
+
_OHLCVService_trace.set(this, void 0);
|
|
76
|
+
_OHLCVService_channels.set(this, new Map());
|
|
77
|
+
_OHLCVService_mutex.set(this, new async_mutex_1.Mutex());
|
|
78
|
+
_OHLCVService_chainsUp.set(this, new Set());
|
|
79
|
+
__classPrivateFieldSet(this, _OHLCVService_messenger, options.messenger, "f");
|
|
80
|
+
__classPrivateFieldSet(this, _OHLCVService_trace, options.traceFn ??
|
|
81
|
+
((_request, fn) => fn?.()), "f");
|
|
82
|
+
__classPrivateFieldGet(this, _OHLCVService_messenger, "f").registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
|
|
83
|
+
__classPrivateFieldGet(this, _OHLCVService_messenger, "f").subscribe('BackendWebSocketService:connectionStateChanged',
|
|
84
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
85
|
+
(connectionInfo) => __classPrivateFieldGet(this, _OHLCVService_instances, "m", _OHLCVService_handleWebSocketStateChange).call(this, connectionInfo));
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Register the system-notifications channel callback.
|
|
89
|
+
*/
|
|
90
|
+
init() {
|
|
91
|
+
log('OHLCV-WS: Initializing — registering system-notifications callback');
|
|
92
|
+
__classPrivateFieldGet(this, _OHLCVService_messenger, "f").call('BackendWebSocketService:addChannelCallback', {
|
|
93
|
+
channelName: SYSTEM_NOTIFICATIONS_CHANNEL,
|
|
94
|
+
callback: (notification) => __classPrivateFieldGet(this, _OHLCVService_instances, "m", _OHLCVService_handleSystemNotification).call(this, notification),
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
// =============================================================================
|
|
98
|
+
// Public — Subscribe / Unsubscribe
|
|
99
|
+
// =============================================================================
|
|
100
|
+
/**
|
|
101
|
+
* Subscribe to an OHLCV channel. If this is the first subscriber for the
|
|
102
|
+
* given asset/interval/currency combination a WebSocket subscription is
|
|
103
|
+
* created. Additional calls for the same combination only bump the reference
|
|
104
|
+
* count.
|
|
105
|
+
*
|
|
106
|
+
* @param options - The subscription parameters.
|
|
107
|
+
* @returns A promise that resolves once the subscription is established.
|
|
108
|
+
*/
|
|
109
|
+
async subscribe(options) {
|
|
110
|
+
const channel = __classPrivateFieldGet(this, _OHLCVService_instances, "m", _OHLCVService_buildChannel).call(this, options);
|
|
111
|
+
const releaseLock = await __classPrivateFieldGet(this, _OHLCVService_mutex, "f").acquire();
|
|
112
|
+
try {
|
|
113
|
+
await __classPrivateFieldGet(this, _OHLCVService_instances, "m", _OHLCVService_subscribeInner).call(this, channel);
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
releaseLock();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Unsubscribe from an OHLCV channel. Decrements the reference count and,
|
|
121
|
+
* when it reaches zero, starts a grace-period timer before actually
|
|
122
|
+
* unsubscribing from the WebSocket to absorb rapid navigation patterns.
|
|
123
|
+
*
|
|
124
|
+
* @param options - The subscription parameters to unsubscribe from.
|
|
125
|
+
* @returns A promise that resolves once the unsubscription is processed.
|
|
126
|
+
*/
|
|
127
|
+
async unsubscribe(options) {
|
|
128
|
+
const channel = __classPrivateFieldGet(this, _OHLCVService_instances, "m", _OHLCVService_buildChannel).call(this, options);
|
|
129
|
+
const releaseLock = await __classPrivateFieldGet(this, _OHLCVService_mutex, "f").acquire();
|
|
130
|
+
try {
|
|
131
|
+
await __classPrivateFieldGet(this, _OHLCVService_instances, "m", _OHLCVService_unsubscribeInner).call(this, channel);
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
releaseLock();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// =============================================================================
|
|
138
|
+
// Public — Cleanup
|
|
139
|
+
// =============================================================================
|
|
140
|
+
/**
|
|
141
|
+
* Destroy the service and clean up all resources.
|
|
142
|
+
*/
|
|
143
|
+
destroy() {
|
|
144
|
+
for (const entry of __classPrivateFieldGet(this, _OHLCVService_channels, "f").values()) {
|
|
145
|
+
if (entry.gracePeriodTimer) {
|
|
146
|
+
clearTimeout(entry.gracePeriodTimer);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
__classPrivateFieldGet(this, _OHLCVService_channels, "f").clear();
|
|
150
|
+
__classPrivateFieldGet(this, _OHLCVService_chainsUp, "f").clear();
|
|
151
|
+
__classPrivateFieldGet(this, _OHLCVService_messenger, "f").call('BackendWebSocketService:removeChannelCallback', SYSTEM_NOTIFICATIONS_CHANNEL);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
exports.OHLCVService = OHLCVService;
|
|
155
|
+
_OHLCVService_messenger = new WeakMap(), _OHLCVService_trace = new WeakMap(), _OHLCVService_channels = new WeakMap(), _OHLCVService_mutex = new WeakMap(), _OHLCVService_chainsUp = new WeakMap(), _OHLCVService_instances = new WeakSet(), _OHLCVService_subscribeInner = async function _OHLCVService_subscribeInner(channel) {
|
|
156
|
+
const entry = __classPrivateFieldGet(this, _OHLCVService_channels, "f").get(channel);
|
|
157
|
+
if (entry?.gracePeriodTimer) {
|
|
158
|
+
clearTimeout(entry.gracePeriodTimer);
|
|
159
|
+
entry.gracePeriodTimer = undefined;
|
|
160
|
+
log('OHLCV-WS: Cancelled grace-period unsubscribe', {
|
|
161
|
+
channel,
|
|
162
|
+
});
|
|
163
|
+
if (__classPrivateFieldGet(this, _OHLCVService_messenger, "f").call('BackendWebSocketService:channelHasSubscription', channel)) {
|
|
164
|
+
entry.refCount += 1;
|
|
165
|
+
log('OHLCV-WS: WS subscription still alive, bumped refCount', {
|
|
166
|
+
channel,
|
|
167
|
+
refCount: entry.refCount,
|
|
168
|
+
});
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
// WS subscription was lost (e.g. after disconnect/reconnect) — fall
|
|
172
|
+
// through to recreate it. refCount is bumped only after success below.
|
|
173
|
+
}
|
|
174
|
+
else if (entry && entry.refCount > 0) {
|
|
175
|
+
entry.refCount += 1;
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
await __classPrivateFieldGet(this, _OHLCVService_messenger, "f").call('BackendWebSocketService:connect');
|
|
180
|
+
if (__classPrivateFieldGet(this, _OHLCVService_messenger, "f").call('BackendWebSocketService:channelHasSubscription', channel)) {
|
|
181
|
+
log('OHLCV-WS: Channel already has WS subscription (idempotency), skipping', {
|
|
182
|
+
channel,
|
|
183
|
+
});
|
|
184
|
+
__classPrivateFieldGet(this, _OHLCVService_channels, "f").set(channel, { refCount: 1 });
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
await __classPrivateFieldGet(this, _OHLCVService_messenger, "f").call('BackendWebSocketService:subscribe', {
|
|
188
|
+
channels: [channel],
|
|
189
|
+
channelType: SUBSCRIPTION_NAMESPACE,
|
|
190
|
+
callback: (notification) => {
|
|
191
|
+
__classPrivateFieldGet(this, _OHLCVService_instances, "m", _OHLCVService_handleBarUpdate).call(this, channel, notification);
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
__classPrivateFieldGet(this, _OHLCVService_channels, "f").set(channel, { refCount: 1 });
|
|
195
|
+
log('OHLCV-WS: Subscribe succeeded — new WS subscription created', {
|
|
196
|
+
channel,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
log('OHLCV-WS: Subscription failed', { channel, error });
|
|
201
|
+
__classPrivateFieldGet(this, _OHLCVService_channels, "f").delete(channel);
|
|
202
|
+
__classPrivateFieldGet(this, _OHLCVService_messenger, "f").publish('OHLCVService:subscriptionError', {
|
|
203
|
+
channel,
|
|
204
|
+
error: String(error),
|
|
205
|
+
operation: 'subscribe',
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}, _OHLCVService_unsubscribeInner = async function _OHLCVService_unsubscribeInner(channel) {
|
|
209
|
+
const entry = __classPrivateFieldGet(this, _OHLCVService_channels, "f").get(channel);
|
|
210
|
+
if (!entry || entry.refCount <= 0) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
entry.refCount -= 1;
|
|
214
|
+
if (entry.refCount > 0) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
entry.gracePeriodTimer = setTimeout(() => {
|
|
218
|
+
entry.gracePeriodTimer = undefined;
|
|
219
|
+
__classPrivateFieldGet(this, _OHLCVService_instances, "m", _OHLCVService_performUnsubscribe).call(this, channel).catch(() => {
|
|
220
|
+
// no-op
|
|
221
|
+
});
|
|
222
|
+
}, GRACE_PERIOD_MS);
|
|
223
|
+
}, _OHLCVService_performUnsubscribe =
|
|
224
|
+
// =============================================================================
|
|
225
|
+
// Private — WebSocket Subscription Helpers
|
|
226
|
+
// =============================================================================
|
|
227
|
+
async function _OHLCVService_performUnsubscribe(channel) {
|
|
228
|
+
const releaseLock = await __classPrivateFieldGet(this, _OHLCVService_mutex, "f").acquire();
|
|
229
|
+
try {
|
|
230
|
+
const entry = __classPrivateFieldGet(this, _OHLCVService_channels, "f").get(channel);
|
|
231
|
+
if (entry && entry.refCount > 0) {
|
|
232
|
+
log('OHLCV-WS: Skipping unsubscribe — new subscriber arrived while queued', { channel, refCount: entry.refCount });
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
log('OHLCV-WS: Grace period expired — performing actual WS unsubscribe', {
|
|
236
|
+
channel,
|
|
237
|
+
});
|
|
238
|
+
__classPrivateFieldGet(this, _OHLCVService_channels, "f").delete(channel);
|
|
239
|
+
try {
|
|
240
|
+
const subscriptions = __classPrivateFieldGet(this, _OHLCVService_messenger, "f").call('BackendWebSocketService:getSubscriptionsByChannel', channel);
|
|
241
|
+
for (const sub of subscriptions) {
|
|
242
|
+
await sub.unsubscribe();
|
|
243
|
+
}
|
|
244
|
+
log('OHLCV-WS: WS unsubscribe completed', { channel });
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
log('OHLCV-WS: Unsubscription failed', { channel, error });
|
|
248
|
+
__classPrivateFieldGet(this, _OHLCVService_messenger, "f").publish('OHLCVService:subscriptionError', {
|
|
249
|
+
channel,
|
|
250
|
+
error: String(error),
|
|
251
|
+
operation: 'unsubscribe',
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
finally {
|
|
256
|
+
releaseLock();
|
|
257
|
+
}
|
|
258
|
+
}, _OHLCVService_resubscribeActiveChannels =
|
|
259
|
+
/**
|
|
260
|
+
* Resubscribe all channels that were active before a disconnect.
|
|
261
|
+
* Called when WebSocket transitions to CONNECTED.
|
|
262
|
+
*/
|
|
263
|
+
async function _OHLCVService_resubscribeActiveChannels() {
|
|
264
|
+
const releaseLock = await __classPrivateFieldGet(this, _OHLCVService_mutex, "f").acquire();
|
|
265
|
+
try {
|
|
266
|
+
const channelCount = __classPrivateFieldGet(this, _OHLCVService_channels, "f").size;
|
|
267
|
+
log('OHLCV-WS: Resubscribing active channels after reconnect', {
|
|
268
|
+
count: channelCount,
|
|
269
|
+
});
|
|
270
|
+
for (const [channel, entry] of __classPrivateFieldGet(this, _OHLCVService_channels, "f").entries()) {
|
|
271
|
+
if (entry.refCount === 0) {
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
if (__classPrivateFieldGet(this, _OHLCVService_messenger, "f").call('BackendWebSocketService:channelHasSubscription', channel)) {
|
|
276
|
+
log('OHLCV-WS: Channel already subscribed on server, skipping resubscribe', {
|
|
277
|
+
channel,
|
|
278
|
+
});
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
await __classPrivateFieldGet(this, _OHLCVService_messenger, "f").call('BackendWebSocketService:subscribe', {
|
|
282
|
+
channels: [channel],
|
|
283
|
+
channelType: SUBSCRIPTION_NAMESPACE,
|
|
284
|
+
callback: (notification) => {
|
|
285
|
+
__classPrivateFieldGet(this, _OHLCVService_instances, "m", _OHLCVService_handleBarUpdate).call(this, channel, notification);
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
log('OHLCV-WS: Resubscription succeeded', { channel });
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
log('OHLCV-WS: Resubscription failed for channel', {
|
|
292
|
+
channel,
|
|
293
|
+
error,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
finally {
|
|
299
|
+
releaseLock();
|
|
300
|
+
}
|
|
301
|
+
}, _OHLCVService_handleBarUpdate = function _OHLCVService_handleBarUpdate(channel, notification) {
|
|
302
|
+
const bar = notification.data;
|
|
303
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
304
|
+
__classPrivateFieldGet(this, _OHLCVService_trace, "f").call(this, {
|
|
305
|
+
name: `${SERVICE_NAME} Bar Update`,
|
|
306
|
+
data: { channel, timestamp: bar.timestamp },
|
|
307
|
+
tags: { service: SERVICE_NAME },
|
|
308
|
+
}, () => {
|
|
309
|
+
__classPrivateFieldGet(this, _OHLCVService_messenger, "f").publish('OHLCVService:barUpdated', { channel, bar });
|
|
310
|
+
});
|
|
311
|
+
}, _OHLCVService_handleSystemNotification = function _OHLCVService_handleSystemNotification(notification) {
|
|
312
|
+
const data = notification.data;
|
|
313
|
+
const { timestamp } = notification;
|
|
314
|
+
if (!data.chainIds || !Array.isArray(data.chainIds) || !data.status) {
|
|
315
|
+
throw new Error('Invalid system notification data: missing chainIds or status');
|
|
316
|
+
}
|
|
317
|
+
if (data.status === 'up') {
|
|
318
|
+
for (const chainId of data.chainIds) {
|
|
319
|
+
__classPrivateFieldGet(this, _OHLCVService_chainsUp, "f").add(chainId);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
for (const chainId of data.chainIds) {
|
|
324
|
+
__classPrivateFieldGet(this, _OHLCVService_chainsUp, "f").delete(chainId);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
__classPrivateFieldGet(this, _OHLCVService_messenger, "f").publish('OHLCVService:chainStatusChanged', {
|
|
328
|
+
chainIds: data.chainIds,
|
|
329
|
+
status: data.status,
|
|
330
|
+
timestamp,
|
|
331
|
+
});
|
|
332
|
+
log(`OHLCV-WS: Chain status change: ${data.status}`, {
|
|
333
|
+
chains: data.chainIds,
|
|
334
|
+
status: data.status,
|
|
335
|
+
});
|
|
336
|
+
}, _OHLCVService_handleWebSocketStateChange = async function _OHLCVService_handleWebSocketStateChange(connectionInfo) {
|
|
337
|
+
const { state } = connectionInfo;
|
|
338
|
+
if (state === BackendWebSocketService_1.WebSocketState.CONNECTED) {
|
|
339
|
+
await __classPrivateFieldGet(this, _OHLCVService_instances, "m", _OHLCVService_resubscribeActiveChannels).call(this);
|
|
340
|
+
}
|
|
341
|
+
else if (state === BackendWebSocketService_1.WebSocketState.DISCONNECTED) {
|
|
342
|
+
const chainsToMarkDown = Array.from(__classPrivateFieldGet(this, _OHLCVService_chainsUp, "f"));
|
|
343
|
+
if (chainsToMarkDown.length > 0) {
|
|
344
|
+
__classPrivateFieldGet(this, _OHLCVService_messenger, "f").publish('OHLCVService:chainStatusChanged', {
|
|
345
|
+
chainIds: chainsToMarkDown,
|
|
346
|
+
status: 'down',
|
|
347
|
+
timestamp: Date.now(),
|
|
348
|
+
});
|
|
349
|
+
log('OHLCV-WS: WebSocket disconnection — marked tracked chains as down', {
|
|
350
|
+
count: chainsToMarkDown.length,
|
|
351
|
+
chains: chainsToMarkDown,
|
|
352
|
+
});
|
|
353
|
+
__classPrivateFieldGet(this, _OHLCVService_chainsUp, "f").clear();
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}, _OHLCVService_buildChannel = function _OHLCVService_buildChannel(options) {
|
|
357
|
+
return `${SUBSCRIPTION_NAMESPACE}.${options.assetId}.${options.interval}.${options.currency}`;
|
|
358
|
+
};
|
|
359
|
+
//# sourceMappingURL=OHLCVService.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OHLCVService.cjs","sourceRoot":"","sources":["../../../src/ws/ohlcv/OHLCVService.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * OHLCV Service for real-time candlestick data streaming via WebSocket.\n *\n * Wraps {@link BackendWebSocketService} through the messenger pattern to\n * provide subscribe/unsubscribe semantics for OHLCV market-data channels.\n * Includes reference counting, grace-period unsubscribe, idempotency checks,\n * chain-status forwarding, and automatic resubscription on reconnect.\n */\n\nimport type {\n TraceCallback,\n TraceContext,\n TraceRequest,\n} from '@metamask/controller-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport { Mutex } from 'async-mutex';\n\nimport { projectLogger, createModuleLogger } from '../../logger';\nimport type {\n WebSocketConnectionInfo,\n BackendWebSocketServiceConnectionStateChangedEvent,\n ServerNotificationMessage,\n} from '../BackendWebSocketService';\nimport { WebSocketState } from '../BackendWebSocketService';\nimport type { BackendWebSocketServiceMethodActions } from '../BackendWebSocketService-method-action-types';\nimport type { OHLCVServiceMethodActions } from './OHLCVService-method-action-types';\nimport type { OHLCVBar, OHLCVSubscriptionOptions } from './types';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst SERVICE_NAME = 'OHLCVService';\n\nconst log = createModuleLogger(projectLogger, SERVICE_NAME);\n\nconst MESSENGER_EXPOSED_METHODS = ['subscribe', 'unsubscribe'] as const;\n\nconst SUBSCRIPTION_NAMESPACE = 'market-data.v1';\n\nconst SYSTEM_NOTIFICATIONS_CHANNEL = `system-notifications.v1.${SUBSCRIPTION_NAMESPACE}`;\n\n/** Delay before actually unsubscribing from a channel after refCount reaches 0. */\nconst GRACE_PERIOD_MS = 3_000;\n\n// =============================================================================\n// Types — Channel Tracking\n// =============================================================================\n\ntype ChannelEntry = {\n refCount: number;\n gracePeriodTimer?: ReturnType<typeof setTimeout>;\n};\n\n// =============================================================================\n// Types — System Notifications\n// =============================================================================\n\n/**\n * System notification data for chain status updates on market-data channels.\n */\nexport type OHLCVSystemNotificationData = {\n chainIds: string[];\n status: 'down' | 'up';\n timestamp?: number;\n};\n\n// =============================================================================\n// Types — Service Options\n// =============================================================================\n\n/**\n * Configuration options for the OHLCV service.\n */\nexport type OHLCVServiceOptions = {\n /** Optional callback to trace performance of OHLCV operations (default: no-op) */\n traceFn?: TraceCallback;\n};\n\n// =============================================================================\n// Action and Event Types\n// =============================================================================\n\nexport type OHLCVServiceActions = OHLCVServiceMethodActions;\n\nexport const OHLCV_SERVICE_ALLOWED_ACTIONS = [\n 'BackendWebSocketService:connect',\n 'BackendWebSocketService:forceReconnection',\n 'BackendWebSocketService:subscribe',\n 'BackendWebSocketService:getConnectionInfo',\n 'BackendWebSocketService:channelHasSubscription',\n 'BackendWebSocketService:getSubscriptionsByChannel',\n 'BackendWebSocketService:findSubscriptionsByChannelPrefix',\n 'BackendWebSocketService:addChannelCallback',\n 'BackendWebSocketService:removeChannelCallback',\n] as const;\n\nexport const OHLCV_SERVICE_ALLOWED_EVENTS = [\n 'BackendWebSocketService:connectionStateChanged',\n] as const;\n\nexport type AllowedActions = BackendWebSocketServiceMethodActions;\n\n// Events published by OHLCVService\n\nexport type OHLCVServiceBarUpdatedEvent = {\n type: `OHLCVService:barUpdated`;\n payload: [{ channel: string; bar: OHLCVBar }];\n};\n\nexport type OHLCVServiceChainStatusChangedEvent = {\n type: `OHLCVService:chainStatusChanged`;\n payload: [{ chainIds: string[]; status: 'up' | 'down'; timestamp?: number }];\n};\n\nexport type OHLCVServiceSubscriptionErrorEvent = {\n type: `OHLCVService:subscriptionError`;\n payload: [{ channel: string; error: string; operation: string }];\n};\n\nexport type OHLCVServiceEvents =\n | OHLCVServiceBarUpdatedEvent\n | OHLCVServiceChainStatusChangedEvent\n | OHLCVServiceSubscriptionErrorEvent;\n\nexport type AllowedEvents = BackendWebSocketServiceConnectionStateChangedEvent;\n\nexport type OHLCVServiceMessenger = Messenger<\n typeof SERVICE_NAME,\n OHLCVServiceActions | AllowedActions,\n OHLCVServiceEvents | AllowedEvents\n>;\n\n// =============================================================================\n// Main Service Class\n// =============================================================================\n\n/**\n * Service for real-time OHLCV candlestick streaming via the backend WebSocket\n * gateway. Communicates with {@link BackendWebSocketService} exclusively\n * through the messenger — no direct import of the class.\n *\n * Features:\n * - Reference counting: multiple UI consumers share one WebSocket subscription\n * - Grace-period unsubscribe: avoids rapid unsub/resub during navigation\n * - Idempotency: duplicate subscribe calls for the same channel are no-ops\n * - Reconnect resilience: resubscribes all active channels on reconnect\n * - Chain-status forwarding: listens to system-notifications for chain up/down\n *\n */\nexport class OHLCVService {\n readonly name = SERVICE_NAME;\n\n readonly #messenger: OHLCVServiceMessenger;\n\n readonly #trace: TraceCallback;\n\n readonly #channels = new Map<string, ChannelEntry>();\n\n readonly #mutex = new Mutex();\n\n readonly #chainsUp = new Set<string>();\n\n // =============================================================================\n // Constructor\n // =============================================================================\n\n constructor(\n options: OHLCVServiceOptions & { messenger: OHLCVServiceMessenger },\n ) {\n this.#messenger = options.messenger;\n\n this.#trace =\n options.traceFn ??\n ((<Result>(\n _request: TraceRequest,\n fn?: (context?: TraceContext) => Result,\n ) => fn?.()) as TraceCallback);\n\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n\n this.#messenger.subscribe(\n 'BackendWebSocketService:connectionStateChanged',\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n (connectionInfo: WebSocketConnectionInfo) =>\n this.#handleWebSocketStateChange(connectionInfo),\n );\n }\n\n /**\n * Register the system-notifications channel callback.\n */\n init(): void {\n log('OHLCV-WS: Initializing — registering system-notifications callback');\n this.#messenger.call('BackendWebSocketService:addChannelCallback', {\n channelName: SYSTEM_NOTIFICATIONS_CHANNEL,\n callback: (notification: ServerNotificationMessage) =>\n this.#handleSystemNotification(notification),\n });\n }\n\n // =============================================================================\n // Public — Subscribe / Unsubscribe\n // =============================================================================\n\n /**\n * Subscribe to an OHLCV channel. If this is the first subscriber for the\n * given asset/interval/currency combination a WebSocket subscription is\n * created. Additional calls for the same combination only bump the reference\n * count.\n *\n * @param options - The subscription parameters.\n * @returns A promise that resolves once the subscription is established.\n */\n async subscribe(options: OHLCVSubscriptionOptions): Promise<void> {\n const channel = this.#buildChannel(options);\n const releaseLock = await this.#mutex.acquire();\n try {\n await this.#subscribeInner(channel);\n } finally {\n releaseLock();\n }\n }\n\n async #subscribeInner(channel: string): Promise<void> {\n const entry = this.#channels.get(channel);\n\n if (entry?.gracePeriodTimer) {\n clearTimeout(entry.gracePeriodTimer);\n entry.gracePeriodTimer = undefined;\n log('OHLCV-WS: Cancelled grace-period unsubscribe', {\n channel,\n });\n\n if (\n this.#messenger.call(\n 'BackendWebSocketService:channelHasSubscription',\n channel,\n )\n ) {\n entry.refCount += 1;\n log('OHLCV-WS: WS subscription still alive, bumped refCount', {\n channel,\n refCount: entry.refCount,\n });\n return;\n }\n // WS subscription was lost (e.g. after disconnect/reconnect) — fall\n // through to recreate it. refCount is bumped only after success below.\n } else if (entry && entry.refCount > 0) {\n entry.refCount += 1;\n return;\n }\n try {\n await this.#messenger.call('BackendWebSocketService:connect');\n\n if (\n this.#messenger.call(\n 'BackendWebSocketService:channelHasSubscription',\n channel,\n )\n ) {\n log(\n 'OHLCV-WS: Channel already has WS subscription (idempotency), skipping',\n {\n channel,\n },\n );\n this.#channels.set(channel, { refCount: 1 });\n return;\n }\n\n await this.#messenger.call('BackendWebSocketService:subscribe', {\n channels: [channel],\n channelType: SUBSCRIPTION_NAMESPACE,\n callback: (notification: ServerNotificationMessage) => {\n this.#handleBarUpdate(channel, notification);\n },\n });\n\n this.#channels.set(channel, { refCount: 1 });\n log('OHLCV-WS: Subscribe succeeded — new WS subscription created', {\n channel,\n });\n } catch (error) {\n log('OHLCV-WS: Subscription failed', { channel, error });\n this.#channels.delete(channel);\n this.#messenger.publish('OHLCVService:subscriptionError', {\n channel,\n error: String(error),\n operation: 'subscribe',\n });\n }\n }\n\n /**\n * Unsubscribe from an OHLCV channel. Decrements the reference count and,\n * when it reaches zero, starts a grace-period timer before actually\n * unsubscribing from the WebSocket to absorb rapid navigation patterns.\n *\n * @param options - The subscription parameters to unsubscribe from.\n * @returns A promise that resolves once the unsubscription is processed.\n */\n async unsubscribe(options: OHLCVSubscriptionOptions): Promise<void> {\n const channel = this.#buildChannel(options);\n const releaseLock = await this.#mutex.acquire();\n try {\n await this.#unsubscribeInner(channel);\n } finally {\n releaseLock();\n }\n }\n\n async #unsubscribeInner(channel: string): Promise<void> {\n const entry = this.#channels.get(channel);\n\n if (!entry || entry.refCount <= 0) {\n return;\n }\n\n entry.refCount -= 1;\n\n if (entry.refCount > 0) {\n return;\n }\n\n entry.gracePeriodTimer = setTimeout(() => {\n entry.gracePeriodTimer = undefined;\n this.#performUnsubscribe(channel).catch(() => {\n // no-op\n });\n }, GRACE_PERIOD_MS);\n }\n\n // =============================================================================\n // Private — WebSocket Subscription Helpers\n // =============================================================================\n\n async #performUnsubscribe(channel: string): Promise<void> {\n const releaseLock = await this.#mutex.acquire();\n try {\n const entry = this.#channels.get(channel);\n if (entry && entry.refCount > 0) {\n log(\n 'OHLCV-WS: Skipping unsubscribe — new subscriber arrived while queued',\n { channel, refCount: entry.refCount },\n );\n return;\n }\n\n log('OHLCV-WS: Grace period expired — performing actual WS unsubscribe', {\n channel,\n });\n this.#channels.delete(channel);\n\n try {\n const subscriptions = this.#messenger.call(\n 'BackendWebSocketService:getSubscriptionsByChannel',\n channel,\n );\n\n for (const sub of subscriptions) {\n await sub.unsubscribe();\n }\n log('OHLCV-WS: WS unsubscribe completed', { channel });\n } catch (error) {\n log('OHLCV-WS: Unsubscription failed', { channel, error });\n this.#messenger.publish('OHLCVService:subscriptionError', {\n channel,\n error: String(error),\n operation: 'unsubscribe',\n });\n }\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Resubscribe all channels that were active before a disconnect.\n * Called when WebSocket transitions to CONNECTED.\n */\n async #resubscribeActiveChannels(): Promise<void> {\n const releaseLock = await this.#mutex.acquire();\n try {\n const channelCount = this.#channels.size;\n log('OHLCV-WS: Resubscribing active channels after reconnect', {\n count: channelCount,\n });\n\n for (const [channel, entry] of this.#channels.entries()) {\n if (entry.refCount === 0) {\n continue;\n }\n\n try {\n if (\n this.#messenger.call(\n 'BackendWebSocketService:channelHasSubscription',\n channel,\n )\n ) {\n log(\n 'OHLCV-WS: Channel already subscribed on server, skipping resubscribe',\n {\n channel,\n },\n );\n continue;\n }\n\n await this.#messenger.call('BackendWebSocketService:subscribe', {\n channels: [channel],\n channelType: SUBSCRIPTION_NAMESPACE,\n callback: (notification: ServerNotificationMessage) => {\n this.#handleBarUpdate(channel, notification);\n },\n });\n log('OHLCV-WS: Resubscription succeeded', { channel });\n } catch (error) {\n log('OHLCV-WS: Resubscription failed for channel', {\n channel,\n error,\n });\n }\n }\n } finally {\n releaseLock();\n }\n }\n\n // =============================================================================\n // Private — Message Handlers\n // =============================================================================\n\n #handleBarUpdate(\n channel: string,\n notification: ServerNotificationMessage,\n ): void {\n const bar = notification.data as OHLCVBar;\n\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.#trace(\n {\n name: `${SERVICE_NAME} Bar Update`,\n data: { channel, timestamp: bar.timestamp },\n tags: { service: SERVICE_NAME },\n },\n () => {\n this.#messenger.publish('OHLCVService:barUpdated', { channel, bar });\n },\n );\n }\n\n #handleSystemNotification(notification: ServerNotificationMessage): void {\n const data = notification.data as OHLCVSystemNotificationData;\n const { timestamp } = notification;\n\n if (!data.chainIds || !Array.isArray(data.chainIds) || !data.status) {\n throw new Error(\n 'Invalid system notification data: missing chainIds or status',\n );\n }\n\n if (data.status === 'up') {\n for (const chainId of data.chainIds) {\n this.#chainsUp.add(chainId);\n }\n } else {\n for (const chainId of data.chainIds) {\n this.#chainsUp.delete(chainId);\n }\n }\n\n this.#messenger.publish('OHLCVService:chainStatusChanged', {\n chainIds: data.chainIds,\n status: data.status,\n timestamp,\n });\n\n log(`OHLCV-WS: Chain status change: ${data.status}`, {\n chains: data.chainIds,\n status: data.status,\n });\n }\n\n async #handleWebSocketStateChange(\n connectionInfo: WebSocketConnectionInfo,\n ): Promise<void> {\n const { state } = connectionInfo;\n\n if (state === WebSocketState.CONNECTED) {\n await this.#resubscribeActiveChannels();\n } else if (state === WebSocketState.DISCONNECTED) {\n const chainsToMarkDown = Array.from(this.#chainsUp);\n\n if (chainsToMarkDown.length > 0) {\n this.#messenger.publish('OHLCVService:chainStatusChanged', {\n chainIds: chainsToMarkDown,\n status: 'down',\n timestamp: Date.now(),\n });\n\n log(\n 'OHLCV-WS: WebSocket disconnection — marked tracked chains as down',\n {\n count: chainsToMarkDown.length,\n chains: chainsToMarkDown,\n },\n );\n\n this.#chainsUp.clear();\n }\n }\n }\n\n // =============================================================================\n // Private — Utility\n // =============================================================================\n\n #buildChannel(options: OHLCVSubscriptionOptions): string {\n return `${SUBSCRIPTION_NAMESPACE}.${options.assetId}.${options.interval}.${options.currency}`;\n }\n\n // =============================================================================\n // Public — Cleanup\n // =============================================================================\n\n /**\n * Destroy the service and clean up all resources.\n */\n destroy(): void {\n for (const entry of this.#channels.values()) {\n if (entry.gracePeriodTimer) {\n clearTimeout(entry.gracePeriodTimer);\n }\n }\n this.#channels.clear();\n this.#chainsUp.clear();\n\n this.#messenger.call(\n 'BackendWebSocketService:removeChannelCallback',\n SYSTEM_NOTIFICATIONS_CHANNEL,\n );\n }\n}\n"]}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OHLCV Service for real-time candlestick data streaming via WebSocket.
|
|
3
|
+
*
|
|
4
|
+
* Wraps {@link BackendWebSocketService} through the messenger pattern to
|
|
5
|
+
* provide subscribe/unsubscribe semantics for OHLCV market-data channels.
|
|
6
|
+
* Includes reference counting, grace-period unsubscribe, idempotency checks,
|
|
7
|
+
* chain-status forwarding, and automatic resubscription on reconnect.
|
|
8
|
+
*/
|
|
9
|
+
import type { TraceCallback } from "@metamask/controller-utils";
|
|
10
|
+
import type { Messenger } from "@metamask/messenger";
|
|
11
|
+
import type { BackendWebSocketServiceConnectionStateChangedEvent } from "../BackendWebSocketService.cjs";
|
|
12
|
+
import type { BackendWebSocketServiceMethodActions } from "../BackendWebSocketService-method-action-types.cjs";
|
|
13
|
+
import type { OHLCVServiceMethodActions } from "./OHLCVService-method-action-types.cjs";
|
|
14
|
+
import type { OHLCVBar, OHLCVSubscriptionOptions } from "./types.cjs";
|
|
15
|
+
declare const SERVICE_NAME = "OHLCVService";
|
|
16
|
+
/**
|
|
17
|
+
* System notification data for chain status updates on market-data channels.
|
|
18
|
+
*/
|
|
19
|
+
export type OHLCVSystemNotificationData = {
|
|
20
|
+
chainIds: string[];
|
|
21
|
+
status: 'down' | 'up';
|
|
22
|
+
timestamp?: number;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Configuration options for the OHLCV service.
|
|
26
|
+
*/
|
|
27
|
+
export type OHLCVServiceOptions = {
|
|
28
|
+
/** Optional callback to trace performance of OHLCV operations (default: no-op) */
|
|
29
|
+
traceFn?: TraceCallback;
|
|
30
|
+
};
|
|
31
|
+
export type OHLCVServiceActions = OHLCVServiceMethodActions;
|
|
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"];
|
|
34
|
+
export type AllowedActions = BackendWebSocketServiceMethodActions;
|
|
35
|
+
export type OHLCVServiceBarUpdatedEvent = {
|
|
36
|
+
type: `OHLCVService:barUpdated`;
|
|
37
|
+
payload: [{
|
|
38
|
+
channel: string;
|
|
39
|
+
bar: OHLCVBar;
|
|
40
|
+
}];
|
|
41
|
+
};
|
|
42
|
+
export type OHLCVServiceChainStatusChangedEvent = {
|
|
43
|
+
type: `OHLCVService:chainStatusChanged`;
|
|
44
|
+
payload: [{
|
|
45
|
+
chainIds: string[];
|
|
46
|
+
status: 'up' | 'down';
|
|
47
|
+
timestamp?: number;
|
|
48
|
+
}];
|
|
49
|
+
};
|
|
50
|
+
export type OHLCVServiceSubscriptionErrorEvent = {
|
|
51
|
+
type: `OHLCVService:subscriptionError`;
|
|
52
|
+
payload: [{
|
|
53
|
+
channel: string;
|
|
54
|
+
error: string;
|
|
55
|
+
operation: string;
|
|
56
|
+
}];
|
|
57
|
+
};
|
|
58
|
+
export type OHLCVServiceEvents = OHLCVServiceBarUpdatedEvent | OHLCVServiceChainStatusChangedEvent | OHLCVServiceSubscriptionErrorEvent;
|
|
59
|
+
export type AllowedEvents = BackendWebSocketServiceConnectionStateChangedEvent;
|
|
60
|
+
export type OHLCVServiceMessenger = Messenger<typeof SERVICE_NAME, OHLCVServiceActions | AllowedActions, OHLCVServiceEvents | AllowedEvents>;
|
|
61
|
+
/**
|
|
62
|
+
* Service for real-time OHLCV candlestick streaming via the backend WebSocket
|
|
63
|
+
* gateway. Communicates with {@link BackendWebSocketService} exclusively
|
|
64
|
+
* through the messenger — no direct import of the class.
|
|
65
|
+
*
|
|
66
|
+
* Features:
|
|
67
|
+
* - Reference counting: multiple UI consumers share one WebSocket subscription
|
|
68
|
+
* - Grace-period unsubscribe: avoids rapid unsub/resub during navigation
|
|
69
|
+
* - Idempotency: duplicate subscribe calls for the same channel are no-ops
|
|
70
|
+
* - Reconnect resilience: resubscribes all active channels on reconnect
|
|
71
|
+
* - Chain-status forwarding: listens to system-notifications for chain up/down
|
|
72
|
+
*
|
|
73
|
+
*/
|
|
74
|
+
export declare class OHLCVService {
|
|
75
|
+
#private;
|
|
76
|
+
readonly name = "OHLCVService";
|
|
77
|
+
constructor(options: OHLCVServiceOptions & {
|
|
78
|
+
messenger: OHLCVServiceMessenger;
|
|
79
|
+
});
|
|
80
|
+
/**
|
|
81
|
+
* Register the system-notifications channel callback.
|
|
82
|
+
*/
|
|
83
|
+
init(): void;
|
|
84
|
+
/**
|
|
85
|
+
* Subscribe to an OHLCV channel. If this is the first subscriber for the
|
|
86
|
+
* given asset/interval/currency combination a WebSocket subscription is
|
|
87
|
+
* created. Additional calls for the same combination only bump the reference
|
|
88
|
+
* count.
|
|
89
|
+
*
|
|
90
|
+
* @param options - The subscription parameters.
|
|
91
|
+
* @returns A promise that resolves once the subscription is established.
|
|
92
|
+
*/
|
|
93
|
+
subscribe(options: OHLCVSubscriptionOptions): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Unsubscribe from an OHLCV channel. Decrements the reference count and,
|
|
96
|
+
* when it reaches zero, starts a grace-period timer before actually
|
|
97
|
+
* unsubscribing from the WebSocket to absorb rapid navigation patterns.
|
|
98
|
+
*
|
|
99
|
+
* @param options - The subscription parameters to unsubscribe from.
|
|
100
|
+
* @returns A promise that resolves once the unsubscription is processed.
|
|
101
|
+
*/
|
|
102
|
+
unsubscribe(options: OHLCVSubscriptionOptions): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Destroy the service and clean up all resources.
|
|
105
|
+
*/
|
|
106
|
+
destroy(): void;
|
|
107
|
+
}
|
|
108
|
+
export {};
|
|
109
|
+
//# sourceMappingURL=OHLCVService.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OHLCVService.d.cts","sourceRoot":"","sources":["../../../src/ws/ohlcv/OHLCVService.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,aAAa,EAGd,mCAAmC;AACpC,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAIrD,OAAO,KAAK,EAEV,kDAAkD,EAEnD,uCAAmC;AAEpC,OAAO,KAAK,EAAE,oCAAoC,EAAE,2DAAuD;AAC3G,OAAO,KAAK,EAAE,yBAAyB,EAAE,+CAA2C;AACpF,OAAO,KAAK,EAAE,QAAQ,EAAE,wBAAwB,EAAE,oBAAgB;AAMlE,QAAA,MAAM,YAAY,iBAAiB,CAAC;AA0BpC;;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,+aAUhC,CAAC;AAEX,eAAO,MAAM,4BAA4B,6DAE/B,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;;;;;;;;;;;;GAYG;AACH,qBAAa,YAAY;;IACvB,QAAQ,CAAC,IAAI,kBAAgB;gBAiB3B,OAAO,EAAE,mBAAmB,GAAG;QAAE,SAAS,EAAE,qBAAqB,CAAA;KAAE;IAwBrE;;OAEG;IACH,IAAI,IAAI,IAAI;IAaZ;;;;;;;;OAQG;IACG,SAAS,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAiFjE;;;;;;;OAOG;IACG,WAAW,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAiOnE;;OAEG;IACH,OAAO,IAAI,IAAI;CAchB"}
|