@jack-kernel/sdk 1.0.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/cjs/index.js +125 -2
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/lifi/chain-map.js +39 -0
  4. package/dist/cjs/lifi/chain-map.js.map +1 -0
  5. package/dist/cjs/lifi/fallback.js +135 -0
  6. package/dist/cjs/lifi/fallback.js.map +1 -0
  7. package/dist/cjs/lifi/index.js +34 -0
  8. package/dist/cjs/lifi/index.js.map +1 -0
  9. package/dist/cjs/lifi/lifi-provider.js +496 -0
  10. package/dist/cjs/lifi/lifi-provider.js.map +1 -0
  11. package/dist/cjs/lifi/token-map.js +75 -0
  12. package/dist/cjs/lifi/token-map.js.map +1 -0
  13. package/dist/cjs/lifi/types.js +3 -0
  14. package/dist/cjs/lifi/types.js.map +1 -0
  15. package/dist/cjs/lifi/utils.js +45 -0
  16. package/dist/cjs/lifi/utils.js.map +1 -0
  17. package/dist/cjs/yellow/channel-state-manager.js +167 -0
  18. package/dist/cjs/yellow/channel-state-manager.js.map +1 -0
  19. package/dist/cjs/yellow/clear-node-connection.js +390 -0
  20. package/dist/cjs/yellow/clear-node-connection.js.map +1 -0
  21. package/dist/cjs/yellow/event-mapper.js +254 -0
  22. package/dist/cjs/yellow/event-mapper.js.map +1 -0
  23. package/dist/cjs/yellow/serialization.js +130 -0
  24. package/dist/cjs/yellow/serialization.js.map +1 -0
  25. package/dist/cjs/yellow/session-key-manager.js +308 -0
  26. package/dist/cjs/yellow/session-key-manager.js.map +1 -0
  27. package/dist/cjs/yellow/types.js +12 -0
  28. package/dist/cjs/yellow/types.js.map +1 -0
  29. package/dist/cjs/yellow/yellow-provider.js +1545 -0
  30. package/dist/cjs/yellow/yellow-provider.js.map +1 -0
  31. package/dist/esm/index.js +102 -1
  32. package/dist/esm/index.js.map +1 -1
  33. package/dist/esm/lifi/chain-map.js +35 -0
  34. package/dist/esm/lifi/chain-map.js.map +1 -0
  35. package/dist/esm/lifi/fallback.js +128 -0
  36. package/dist/esm/lifi/fallback.js.map +1 -0
  37. package/dist/esm/lifi/index.js +19 -0
  38. package/dist/esm/lifi/index.js.map +1 -0
  39. package/dist/esm/lifi/lifi-provider.js +492 -0
  40. package/dist/esm/lifi/lifi-provider.js.map +1 -0
  41. package/dist/esm/lifi/token-map.js +71 -0
  42. package/dist/esm/lifi/token-map.js.map +1 -0
  43. package/dist/esm/lifi/types.js +2 -0
  44. package/dist/esm/lifi/types.js.map +1 -0
  45. package/dist/esm/lifi/utils.js +41 -0
  46. package/dist/esm/lifi/utils.js.map +1 -0
  47. package/dist/esm/yellow/channel-state-manager.js +163 -0
  48. package/dist/esm/yellow/channel-state-manager.js.map +1 -0
  49. package/dist/esm/yellow/clear-node-connection.js +385 -0
  50. package/dist/esm/yellow/clear-node-connection.js.map +1 -0
  51. package/dist/esm/yellow/event-mapper.js +248 -0
  52. package/dist/esm/yellow/event-mapper.js.map +1 -0
  53. package/dist/esm/yellow/serialization.js +125 -0
  54. package/dist/esm/yellow/serialization.js.map +1 -0
  55. package/dist/esm/yellow/session-key-manager.js +302 -0
  56. package/dist/esm/yellow/session-key-manager.js.map +1 -0
  57. package/dist/esm/yellow/types.js +11 -0
  58. package/dist/esm/yellow/types.js.map +1 -0
  59. package/dist/esm/yellow/yellow-provider.js +1538 -0
  60. package/dist/esm/yellow/yellow-provider.js.map +1 -0
  61. package/dist/types/index.d.ts +104 -2
  62. package/dist/types/index.d.ts.map +1 -1
  63. package/dist/types/lifi/chain-map.d.ts +27 -0
  64. package/dist/types/lifi/chain-map.d.ts.map +1 -0
  65. package/dist/types/lifi/fallback.d.ts +58 -0
  66. package/dist/types/lifi/fallback.d.ts.map +1 -0
  67. package/dist/types/lifi/index.d.ts +18 -0
  68. package/dist/types/lifi/index.d.ts.map +1 -0
  69. package/dist/types/lifi/lifi-provider.d.ts +133 -0
  70. package/dist/types/lifi/lifi-provider.d.ts.map +1 -0
  71. package/dist/types/lifi/token-map.d.ts +34 -0
  72. package/dist/types/lifi/token-map.d.ts.map +1 -0
  73. package/dist/types/lifi/types.d.ts +52 -0
  74. package/dist/types/lifi/types.d.ts.map +1 -0
  75. package/dist/types/lifi/utils.d.ts +29 -0
  76. package/dist/types/lifi/utils.d.ts.map +1 -0
  77. package/dist/types/yellow/channel-state-manager.d.ts +106 -0
  78. package/dist/types/yellow/channel-state-manager.d.ts.map +1 -0
  79. package/dist/types/yellow/clear-node-connection.d.ts +202 -0
  80. package/dist/types/yellow/clear-node-connection.d.ts.map +1 -0
  81. package/dist/types/yellow/event-mapper.d.ts +74 -0
  82. package/dist/types/yellow/event-mapper.d.ts.map +1 -0
  83. package/dist/types/yellow/serialization.d.ts +52 -0
  84. package/dist/types/yellow/serialization.d.ts.map +1 -0
  85. package/dist/types/yellow/session-key-manager.d.ts +179 -0
  86. package/dist/types/yellow/session-key-manager.d.ts.map +1 -0
  87. package/dist/types/yellow/types.d.ts +177 -0
  88. package/dist/types/yellow/types.d.ts.map +1 -0
  89. package/dist/types/yellow/yellow-provider.d.ts +303 -0
  90. package/dist/types/yellow/yellow-provider.d.ts.map +1 -0
  91. package/package.json +4 -1
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Channel State Manager for Yellow Network Integration
3
+ *
4
+ * Tracks local channel state and provides on-chain query capabilities.
5
+ * Maintains a local cache of channel states from ClearNode messages and
6
+ * falls back to on-chain queries via viem PublicClient when the WebSocket
7
+ * is disconnected.
8
+ *
9
+ * Requirements: 7.1, 7.2, 7.3, 7.4
10
+ */
11
+ // ============================================================================
12
+ // Custody Contract ABI (minimal)
13
+ // ============================================================================
14
+ /**
15
+ * Minimal ABI for the custody contract's balance query function.
16
+ *
17
+ * The custody contract exposes a `getChannelBalances` function that returns
18
+ * the token balances held in a specific channel for the given token addresses.
19
+ *
20
+ * Function signature: getChannelBalances(bytes32 channelId, address[] tokens) → uint256[]
21
+ */
22
+ const CUSTODY_BALANCE_ABI = [
23
+ {
24
+ name: 'getChannelBalances',
25
+ type: 'function',
26
+ stateMutability: 'view',
27
+ inputs: [
28
+ { name: 'channelId', type: 'bytes32' },
29
+ { name: 'tokens', type: 'address[]' },
30
+ ],
31
+ outputs: [{ name: 'balances', type: 'uint256[]' }],
32
+ },
33
+ ];
34
+ // ============================================================================
35
+ // ChannelStateManager
36
+ // ============================================================================
37
+ /**
38
+ * Manages local channel state cache and provides on-chain query capabilities.
39
+ *
40
+ * The ChannelStateManager serves two purposes:
41
+ * 1. Maintains a local cache of channel states received from ClearNode messages,
42
+ * enabling fast lookups without network calls.
43
+ * 2. Provides on-chain balance queries via viem PublicClient for when the
44
+ * WebSocket connection is unavailable (Requirement 7.4) or when authoritative
45
+ * on-chain data is needed (Requirement 7.2).
46
+ *
47
+ * Requirements: 7.1, 7.2, 7.3, 7.4
48
+ */
49
+ export class ChannelStateManager {
50
+ publicClient;
51
+ custodyAddress;
52
+ channels;
53
+ /**
54
+ * @param publicClient - viem PublicClient for on-chain balance queries
55
+ * @param custodyAddress - Address of the custody contract holding channel collateral
56
+ */
57
+ constructor(publicClient, custodyAddress) {
58
+ this.publicClient = publicClient;
59
+ this.custodyAddress = custodyAddress;
60
+ this.channels = new Map();
61
+ }
62
+ /**
63
+ * Update the local cache with a channel state from a ClearNode response.
64
+ *
65
+ * This is called when the YellowProvider receives channel state updates
66
+ * from ClearNode messages (create, resize, close, or get_ledger_balances).
67
+ *
68
+ * Requirement 7.1: Maintain local channel state from ClearNode responses.
69
+ *
70
+ * @param channelId - The channel identifier
71
+ * @param state - The channel state to cache
72
+ */
73
+ updateChannel(channelId, state) {
74
+ this.channels.set(channelId, state);
75
+ }
76
+ /**
77
+ * Get a locally cached channel state by channel ID.
78
+ *
79
+ * Returns undefined if the channel is not in the local cache.
80
+ * For authoritative on-chain data, use queryOnChainBalances instead.
81
+ *
82
+ * Requirement 7.2: Query specific channel status by channel ID.
83
+ *
84
+ * @param channelId - The channel identifier
85
+ * @returns The cached channel state, or undefined if not found
86
+ */
87
+ getChannel(channelId) {
88
+ return this.channels.get(channelId);
89
+ }
90
+ /**
91
+ * Get all locally cached channel states.
92
+ *
93
+ * Returns a snapshot array of all channels currently in the cache.
94
+ * The returned array is a copy; modifications do not affect the cache.
95
+ *
96
+ * Requirement 7.1: Return list of channels with their statuses and allocations.
97
+ *
98
+ * @returns Array of all cached channel states
99
+ */
100
+ getAllChannels() {
101
+ return Array.from(this.channels.values());
102
+ }
103
+ /**
104
+ * Query on-chain channel balances from the custody contract.
105
+ *
106
+ * Uses viem PublicClient.readContract to call the custody contract's
107
+ * getChannelBalances function. This provides authoritative on-chain data
108
+ * and is used as a fallback when the ClearNode WebSocket is disconnected.
109
+ *
110
+ * Requirement 7.2: Query on-chain custody contract for channel balances.
111
+ * Requirement 7.4: Fall back to on-chain queries when WebSocket is disconnected.
112
+ *
113
+ * @param channelId - The channel identifier (bytes32 hex string)
114
+ * @param tokens - Array of token addresses to query balances for
115
+ * @returns Array of token balances as bigint values, one per token
116
+ * @throws Error if the on-chain query fails
117
+ */
118
+ async queryOnChainBalances(channelId, tokens) {
119
+ try {
120
+ const balances = await this.publicClient.readContract({
121
+ address: this.custodyAddress,
122
+ abi: CUSTODY_BALANCE_ABI,
123
+ functionName: 'getChannelBalances',
124
+ args: [channelId, tokens],
125
+ });
126
+ return [...balances];
127
+ }
128
+ catch (error) {
129
+ const message = error instanceof Error ? error.message : String(error);
130
+ throw new Error(`Failed to query on-chain balances for channel ${channelId}: ${message}`);
131
+ }
132
+ }
133
+ /**
134
+ * Find an open (ACTIVE) channel for a given token address.
135
+ *
136
+ * Searches the local cache for a channel with status 'ACTIVE' that
137
+ * matches the specified token address. Returns the first match found.
138
+ *
139
+ * This is used by the YellowProvider to reuse existing channels
140
+ * when executing intents, avoiding unnecessary channel creation.
141
+ *
142
+ * @param token - The token address to find an open channel for
143
+ * @returns The first matching ACTIVE channel, or undefined if none found
144
+ */
145
+ findOpenChannel(token) {
146
+ for (const channel of this.channels.values()) {
147
+ if (channel.status === 'ACTIVE' && channel.token === token) {
148
+ return channel;
149
+ }
150
+ }
151
+ return undefined;
152
+ }
153
+ /**
154
+ * Clear all cached channel states.
155
+ *
156
+ * This is typically called when the provider disconnects or when
157
+ * a full state refresh is needed.
158
+ */
159
+ clear() {
160
+ this.channels.clear();
161
+ }
162
+ }
163
+ //# sourceMappingURL=channel-state-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-state-manager.js","sourceRoot":"","sources":["../../../src/yellow/channel-state-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,+EAA+E;AAC/E,iCAAiC;AACjC,+EAA+E;AAE/E;;;;;;;GAOG;AACH,MAAM,mBAAmB,GAAG;IAC1B;QACE,IAAI,EAAE,oBAAoB;QAC1B,IAAI,EAAE,UAAU;QAChB,eAAe,EAAE,MAAM;QACvB,MAAM,EAAE;YACN,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE;YACtC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE;SACtC;QACD,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;KACnD;CACO,CAAC;AAEX,+EAA+E;AAC/E,sBAAsB;AACtB,+EAA+E;AAE/E;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,mBAAmB;IACb,YAAY,CAAe;IAC3B,cAAc,CAAgB;IAC9B,QAAQ,CAA4B;IAErD;;;OAGG;IACH,YAAY,YAA0B,EAAE,cAA6B;QACnE,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,QAAQ,GAAG,IAAI,GAAG,EAAwB,CAAC;IAClD,CAAC;IAED;;;;;;;;;;OAUG;IACH,aAAa,CAAC,SAAiB,EAAE,KAAmB;QAClD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IAED;;;;;;;;;;OAUG;IACH,UAAU,CAAC,SAAiB;QAC1B,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC;IAED;;;;;;;;;OASG;IACH,cAAc;QACZ,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,oBAAoB,CAAC,SAAiB,EAAE,MAAgB;QAC5D,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC;gBACpD,OAAO,EAAE,IAAI,CAAC,cAAc;gBAC5B,GAAG,EAAE,mBAAmB;gBACxB,YAAY,EAAE,oBAAoB;gBAClC,IAAI,EAAE,CAAC,SAA0B,EAAE,MAAyB,CAAC;aAC9D,CAAC,CAAC;YAEH,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC;QACvB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,IAAI,KAAK,CAAC,iDAAiD,SAAS,KAAK,OAAO,EAAE,CAAC,CAAC;QAC5F,CAAC;IACH,CAAC;IAED;;;;;;;;;;;OAWG;IACH,eAAe,CAAC,KAAa;QAC3B,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC7C,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;gBAC3D,OAAO,OAAO,CAAC;YACjB,CAAC;QACH,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;;;;OAKG;IACH,KAAK;QACH,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;CACF"}
@@ -0,0 +1,385 @@
1
+ /**
2
+ * ClearNode WebSocket Connection Manager
3
+ *
4
+ * Manages the WebSocket connection to Yellow Network's ClearNode relay server
5
+ * with reconnection logic, request-response correlation, and event emission.
6
+ *
7
+ * Key features:
8
+ * - Exponential backoff reconnection: delay = initialDelay * 2^(attempt-1)
9
+ * - Request-response correlation using method name matching
10
+ * - EventEmitter pattern for connected/disconnected events
11
+ * - Cleanup of all pending handlers on disconnect
12
+ *
13
+ * Requirements: 10.1, 10.2, 10.3, 10.4, 10.5
14
+ */
15
+ import { EventEmitter } from 'events';
16
+ /** WebSocket readyState constants */
17
+ const WS_OPEN = 1;
18
+ const WS_CONNECTING = 0;
19
+ /**
20
+ * Calculate the exponential backoff delay for a given reconnection attempt.
21
+ *
22
+ * Formula: delay = initialDelay * 2^(attempt - 1)
23
+ *
24
+ * This is exported as a pure function so it can be independently tested
25
+ * (including via property-based tests).
26
+ *
27
+ * @param initialDelay - The base delay in milliseconds
28
+ * @param attempt - The attempt number (1-based)
29
+ * @returns The delay in milliseconds before the given attempt
30
+ */
31
+ export function calculateBackoffDelay(initialDelay, attempt) {
32
+ if (attempt < 1) {
33
+ return initialDelay;
34
+ }
35
+ return initialDelay * Math.pow(2, attempt - 1);
36
+ }
37
+ /**
38
+ * Manages a WebSocket connection to Yellow Network's ClearNode.
39
+ *
40
+ * Provides:
41
+ * - connect/disconnect lifecycle management
42
+ * - send (fire-and-forget) and sendAndWait (request-response correlation)
43
+ * - Automatic reconnection with exponential backoff
44
+ * - Event emission for 'connected' and 'disconnected' events
45
+ * - Cleanup of pending handlers on disconnect
46
+ *
47
+ * Requirements: 10.1, 10.2, 10.3, 10.4, 10.5
48
+ */
49
+ export class ClearNodeConnection {
50
+ url;
51
+ maxReconnectAttempts;
52
+ reconnectDelay;
53
+ messageTimeout;
54
+ wsFactory;
55
+ ws = null;
56
+ emitter = new EventEmitter();
57
+ pendingRequests = [];
58
+ messageHandlers = [];
59
+ _isConnected = false;
60
+ _isDisposed = false;
61
+ reconnectAttempt = 0;
62
+ reconnectTimer = null;
63
+ /**
64
+ * @param url - The ClearNode WebSocket URL
65
+ * @param options - Connection configuration options
66
+ * @param wsFactory - Optional WebSocket factory for testing. Defaults to global WebSocket.
67
+ */
68
+ constructor(url, options, wsFactory) {
69
+ this.url = url;
70
+ this.maxReconnectAttempts = options?.maxReconnectAttempts ?? 5;
71
+ this.reconnectDelay = options?.reconnectDelay ?? 1000;
72
+ this.messageTimeout = options?.messageTimeout ?? 30000;
73
+ this.wsFactory = wsFactory ?? ((wsUrl) => new WebSocket(wsUrl));
74
+ }
75
+ /**
76
+ * Establish a WebSocket connection to ClearNode.
77
+ *
78
+ * Resolves when the connection is open and ready.
79
+ * Rejects if the connection fails to establish.
80
+ *
81
+ * Requirement 10.1: Emit a connected event on successful connection.
82
+ */
83
+ async connect() {
84
+ if (this._isDisposed) {
85
+ throw new Error('ClearNodeConnection has been disposed');
86
+ }
87
+ if (this._isConnected && this.ws) {
88
+ return;
89
+ }
90
+ return new Promise((resolve, reject) => {
91
+ let settled = false;
92
+ try {
93
+ this.ws = this.wsFactory(this.url);
94
+ }
95
+ catch (err) {
96
+ reject(new Error(`Failed to create WebSocket: ${err instanceof Error ? err.message : String(err)}`));
97
+ return;
98
+ }
99
+ this.ws.onopen = () => {
100
+ if (settled)
101
+ return;
102
+ settled = true;
103
+ this._isConnected = true;
104
+ this.reconnectAttempt = 0;
105
+ this.emitter.emit('connected');
106
+ resolve();
107
+ };
108
+ this.ws.onerror = (event) => {
109
+ if (settled)
110
+ return;
111
+ settled = true;
112
+ const errorMessage = event && typeof event === 'object' && 'message' in event
113
+ ? String(event.message)
114
+ : 'WebSocket connection failed';
115
+ reject(new Error(errorMessage));
116
+ };
117
+ this.ws.onclose = () => {
118
+ if (!settled) {
119
+ settled = true;
120
+ reject(new Error('WebSocket closed before connection was established'));
121
+ return;
122
+ }
123
+ this.handleDisconnect();
124
+ };
125
+ this.ws.onmessage = (event) => {
126
+ this.handleMessage(event.data);
127
+ };
128
+ });
129
+ }
130
+ /**
131
+ * Close the WebSocket connection and clean up all resources.
132
+ *
133
+ * Requirement 10.4: Clean up all pending message handlers on disconnect.
134
+ */
135
+ async disconnect() {
136
+ this._isDisposed = true;
137
+ this.cancelReconnect();
138
+ this.cleanupPendingRequests(new Error('Connection closed by client'));
139
+ this.messageHandlers = [];
140
+ if (this.ws) {
141
+ const ws = this.ws;
142
+ this.ws = null;
143
+ this._isConnected = false;
144
+ // Remove handlers to prevent reconnection attempts
145
+ ws.onopen = null;
146
+ ws.onclose = null;
147
+ ws.onerror = null;
148
+ ws.onmessage = null;
149
+ // Only close if not already closed/closing
150
+ if (ws.readyState === WS_OPEN || ws.readyState === WS_CONNECTING) {
151
+ ws.close();
152
+ }
153
+ this.emitter.emit('disconnected');
154
+ }
155
+ this._isConnected = false;
156
+ }
157
+ /**
158
+ * Send a message and wait for a correlated response.
159
+ *
160
+ * The response is matched by method name: when a message is received
161
+ * that contains a matching method field, the promise resolves with that response.
162
+ *
163
+ * Requirement 10.5: Request-response correlation using method name matching.
164
+ *
165
+ * @param message - The message string to send
166
+ * @param method - The method name to correlate the response with
167
+ * @param timeout - Optional timeout in ms (defaults to messageTimeout)
168
+ * @returns The parsed response data
169
+ * @throws Error if the connection is not open, the request times out, or the connection drops
170
+ */
171
+ async sendAndWait(message, method, timeout) {
172
+ if (!this._isConnected || !this.ws || this.ws.readyState !== WS_OPEN) {
173
+ throw new Error('WebSocket is not connected');
174
+ }
175
+ const effectiveTimeout = timeout ?? this.messageTimeout;
176
+ return new Promise((resolve, reject) => {
177
+ const timer = setTimeout(() => {
178
+ this.removePendingRequest(pending);
179
+ reject(new Error(`Request timed out waiting for response to method: ${method}`));
180
+ }, effectiveTimeout);
181
+ const pending = {
182
+ method,
183
+ resolve: resolve,
184
+ reject,
185
+ timer,
186
+ };
187
+ this.pendingRequests.push(pending);
188
+ this.ws.send(message);
189
+ });
190
+ }
191
+ /**
192
+ * Send a message without waiting for a response (fire-and-forget).
193
+ *
194
+ * @param message - The message string to send
195
+ * @throws Error if the connection is not open
196
+ */
197
+ send(message) {
198
+ if (!this._isConnected || !this.ws || this.ws.readyState !== WS_OPEN) {
199
+ throw new Error('WebSocket is not connected');
200
+ }
201
+ this.ws.send(message);
202
+ }
203
+ /**
204
+ * Register a handler for incoming messages.
205
+ *
206
+ * Handlers receive the parsed message data for all incoming messages
207
+ * (including those that match pending requests).
208
+ *
209
+ * @param handler - Callback invoked with the parsed message data
210
+ */
211
+ onMessage(handler) {
212
+ this.messageHandlers.push(handler);
213
+ }
214
+ /**
215
+ * Whether the WebSocket connection is currently open and ready.
216
+ */
217
+ get isConnected() {
218
+ return this._isConnected;
219
+ }
220
+ /**
221
+ * Register an event listener for connection lifecycle events.
222
+ *
223
+ * Supported events: 'connected', 'disconnected'
224
+ */
225
+ on(event, handler) {
226
+ this.emitter.on(event, handler);
227
+ }
228
+ /**
229
+ * Remove an event listener.
230
+ */
231
+ off(event, handler) {
232
+ this.emitter.off(event, handler);
233
+ }
234
+ // ============================================================================
235
+ // Private Methods
236
+ // ============================================================================
237
+ /**
238
+ * Handle an incoming WebSocket message.
239
+ *
240
+ * Parses the message as JSON and checks for method-based correlation
241
+ * with pending requests. Also dispatches to all registered message handlers.
242
+ */
243
+ handleMessage(rawData) {
244
+ let data;
245
+ try {
246
+ const text = typeof rawData === 'string' ? rawData : String(rawData);
247
+ data = JSON.parse(text);
248
+ }
249
+ catch {
250
+ // If the message is not valid JSON, pass the raw data
251
+ data = rawData;
252
+ }
253
+ // Dispatch to all registered message handlers
254
+ for (const handler of this.messageHandlers) {
255
+ try {
256
+ handler(data);
257
+ }
258
+ catch {
259
+ // Swallow handler errors to prevent one handler from breaking others
260
+ }
261
+ }
262
+ // Check for method-based correlation with pending requests
263
+ if (data && typeof data === 'object') {
264
+ const messageObj = data;
265
+ const responseMethod = this.extractMethod(messageObj);
266
+ if (responseMethod) {
267
+ const matchIndex = this.pendingRequests.findIndex((req) => req.method === responseMethod);
268
+ if (matchIndex !== -1) {
269
+ const pending = this.pendingRequests[matchIndex];
270
+ this.pendingRequests.splice(matchIndex, 1);
271
+ clearTimeout(pending.timer);
272
+ pending.resolve(data);
273
+ }
274
+ }
275
+ }
276
+ }
277
+ /**
278
+ * Extract the method name from a parsed message object.
279
+ *
280
+ * Supports common message formats:
281
+ * - { method: "..." }
282
+ * - { type: "..." }
283
+ * - Nested: { response: { method: "..." } }
284
+ */
285
+ extractMethod(obj) {
286
+ // Direct method field
287
+ if (typeof obj.method === 'string') {
288
+ return obj.method;
289
+ }
290
+ // Type field as fallback
291
+ if (typeof obj.type === 'string') {
292
+ return obj.type;
293
+ }
294
+ // Nested response object
295
+ if (obj.response && typeof obj.response === 'object') {
296
+ const response = obj.response;
297
+ if (typeof response.method === 'string') {
298
+ return response.method;
299
+ }
300
+ }
301
+ return undefined;
302
+ }
303
+ /**
304
+ * Handle an unexpected WebSocket disconnection.
305
+ *
306
+ * Cleans up state and initiates reconnection with exponential backoff.
307
+ *
308
+ * Requirement 10.2: Exponential backoff reconnection.
309
+ * Requirement 10.3: Emit disconnected and mark unavailable when retries exhausted.
310
+ */
311
+ handleDisconnect() {
312
+ const wasConnected = this._isConnected;
313
+ this._isConnected = false;
314
+ this.ws = null;
315
+ // If disposed, don't attempt reconnection
316
+ if (this._isDisposed) {
317
+ return;
318
+ }
319
+ // Attempt reconnection
320
+ if (wasConnected) {
321
+ this.attemptReconnect();
322
+ }
323
+ }
324
+ /**
325
+ * Attempt to reconnect with exponential backoff.
326
+ *
327
+ * Requirement 10.2: delay = initialDelay * 2^(attempt-1)
328
+ * Requirement 10.3: Mark provider unavailable when all retries exhausted.
329
+ */
330
+ attemptReconnect() {
331
+ this.reconnectAttempt++;
332
+ if (this.reconnectAttempt > this.maxReconnectAttempts) {
333
+ // All retries exhausted
334
+ this.cleanupPendingRequests(new Error('All reconnection attempts exhausted'));
335
+ this.emitter.emit('disconnected');
336
+ return;
337
+ }
338
+ const delay = calculateBackoffDelay(this.reconnectDelay, this.reconnectAttempt);
339
+ this.reconnectTimer = setTimeout(async () => {
340
+ this.reconnectTimer = null;
341
+ if (this._isDisposed) {
342
+ return;
343
+ }
344
+ try {
345
+ await this.connect();
346
+ }
347
+ catch {
348
+ // Connection failed, try again
349
+ this.attemptReconnect();
350
+ }
351
+ }, delay);
352
+ }
353
+ /**
354
+ * Cancel any pending reconnection timer.
355
+ */
356
+ cancelReconnect() {
357
+ if (this.reconnectTimer !== null) {
358
+ clearTimeout(this.reconnectTimer);
359
+ this.reconnectTimer = null;
360
+ }
361
+ }
362
+ /**
363
+ * Reject all pending requests with the given error and clean up timers.
364
+ *
365
+ * Requirement 10.4: Clean up all pending message handlers on disconnect.
366
+ */
367
+ cleanupPendingRequests(error) {
368
+ const pending = [...this.pendingRequests];
369
+ this.pendingRequests = [];
370
+ for (const req of pending) {
371
+ clearTimeout(req.timer);
372
+ req.reject(error);
373
+ }
374
+ }
375
+ /**
376
+ * Remove a specific pending request from the list.
377
+ */
378
+ removePendingRequest(request) {
379
+ const index = this.pendingRequests.indexOf(request);
380
+ if (index !== -1) {
381
+ this.pendingRequests.splice(index, 1);
382
+ }
383
+ }
384
+ }
385
+ //# sourceMappingURL=clear-node-connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clear-node-connection.js","sourceRoot":"","sources":["../../../src/yellow/clear-node-connection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AA8BtC,qCAAqC;AACrC,MAAM,OAAO,GAAG,CAAC,CAAC;AAClB,MAAM,aAAa,GAAG,CAAC,CAAC;AAmBxB;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,qBAAqB,CAAC,YAAoB,EAAE,OAAe;IACzE,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,OAAO,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC;AACjD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,mBAAmB;IACb,GAAG,CAAS;IACZ,oBAAoB,CAAS;IAC7B,cAAc,CAAS;IACvB,cAAc,CAAS;IACvB,SAAS,CAAmB;IAErC,EAAE,GAAsB,IAAI,CAAC;IAC7B,OAAO,GAAiB,IAAI,YAAY,EAAE,CAAC;IAC3C,eAAe,GAAqB,EAAE,CAAC;IACvC,eAAe,GAAmC,EAAE,CAAC;IACrD,YAAY,GAAY,KAAK,CAAC;IAC9B,WAAW,GAAY,KAAK,CAAC;IAC7B,gBAAgB,GAAW,CAAC,CAAC;IAC7B,cAAc,GAAyC,IAAI,CAAC;IAEpE;;;;OAIG;IACH,YAAY,GAAW,EAAE,OAA2B,EAAE,SAA4B;QAChF,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,oBAAoB,GAAG,OAAO,EAAE,oBAAoB,IAAI,CAAC,CAAC;QAC/D,IAAI,CAAC,cAAc,GAAG,OAAO,EAAE,cAAc,IAAI,IAAI,CAAC;QACtD,IAAI,CAAC,cAAc,GAAG,OAAO,EAAE,cAAc,IAAI,KAAK,CAAC;QACvD,IAAI,CAAC,SAAS,GAAG,SAAS,IAAI,CAAC,CAAC,KAAa,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC,KAAK,CAA0B,CAAC,CAAC;IACnG,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC3D,CAAC;QAED,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;YACjC,OAAO;QACT,CAAC;QAED,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC3C,IAAI,OAAO,GAAG,KAAK,CAAC;YAEpB,IAAI,CAAC;gBACH,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACrC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,IAAI,KAAK,CAAC,+BAA+B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;gBACrG,OAAO;YACT,CAAC;YAED,IAAI,CAAC,EAAE,CAAC,MAAM,GAAG,GAAG,EAAE;gBACpB,IAAI,OAAO;oBAAE,OAAO;gBACpB,OAAO,GAAG,IAAI,CAAC;gBACf,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;gBACzB,IAAI,CAAC,gBAAgB,GAAG,CAAC,CAAC;gBAC1B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;gBAC/B,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC;YAEF,IAAI,CAAC,EAAE,CAAC,OAAO,GAAG,CAAC,KAAc,EAAE,EAAE;gBACnC,IAAI,OAAO;oBAAE,OAAO;gBACpB,OAAO,GAAG,IAAI,CAAC;gBACf,MAAM,YAAY,GAChB,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,SAAS,IAAI,KAAK;oBACtD,CAAC,CAAC,MAAM,CAAE,KAA8B,CAAC,OAAO,CAAC;oBACjD,CAAC,CAAC,6BAA6B,CAAC;gBACpC,MAAM,CAAC,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;YAClC,CAAC,CAAC;YAEF,IAAI,CAAC,EAAE,CAAC,OAAO,GAAG,GAAG,EAAE;gBACrB,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,GAAG,IAAI,CAAC;oBACf,MAAM,CAAC,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC,CAAC;oBACxE,OAAO;gBACT,CAAC;gBACD,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,CAAC,CAAC;YAEF,IAAI,CAAC,EAAE,CAAC,SAAS,GAAG,CAAC,KAAwB,EAAE,EAAE;gBAC/C,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACjC,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,UAAU;QACd,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,IAAI,CAAC,sBAAsB,CAAC,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC,CAAC;QACtE,IAAI,CAAC,eAAe,GAAG,EAAE,CAAC;QAE1B,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;YACnB,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC;YACf,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;YAE1B,mDAAmD;YACnD,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC;YACjB,EAAE,CAAC,OAAO,GAAG,IAAI,CAAC;YAClB,EAAE,CAAC,OAAO,GAAG,IAAI,CAAC;YAClB,EAAE,CAAC,SAAS,GAAG,IAAI,CAAC;YAEpB,2CAA2C;YAC3C,IAAI,EAAE,CAAC,UAAU,KAAK,OAAO,IAAI,EAAE,CAAC,UAAU,KAAK,aAAa,EAAE,CAAC;gBACjE,EAAE,CAAC,KAAK,EAAE,CAAC;YACb,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACpC,CAAC;QAED,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;IAC5B,CAAC;IAED;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,WAAW,CAAI,OAAe,EAAE,MAAc,EAAE,OAAgB;QACpE,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC,UAAU,KAAK,OAAO,EAAE,CAAC;YACrE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAChD,CAAC;QAED,MAAM,gBAAgB,GAAG,OAAO,IAAI,IAAI,CAAC,cAAc,CAAC;QAExD,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACxC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC;gBACnC,MAAM,CAAC,IAAI,KAAK,CAAC,qDAAqD,MAAM,EAAE,CAAC,CAAC,CAAC;YACnF,CAAC,EAAE,gBAAgB,CAAC,CAAC;YAErB,MAAM,OAAO,GAAmB;gBAC9B,MAAM;gBACN,OAAO,EAAE,OAAmC;gBAC5C,MAAM;gBACN,KAAK;aACN,CAAC;YAEF,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACnC,IAAI,CAAC,EAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACH,IAAI,CAAC,OAAe;QAClB,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC,UAAU,KAAK,OAAO,EAAE,CAAC;YACrE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAChD,CAAC;QACD,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC;IAED;;;;;;;OAOG;IACH,SAAS,CAAC,OAAgC;QACxC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;IAED;;OAEG;IACH,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED;;;;OAIG;IACH,EAAE,CAAC,KAAa,EAAE,OAAqC;QACrD,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,KAAa,EAAE,OAAqC;QACtD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACnC,CAAC;IAED,+EAA+E;IAC/E,kBAAkB;IAClB,+EAA+E;IAE/E;;;;;OAKG;IACK,aAAa,CAAC,OAAgB;QACpC,IAAI,IAAa,CAAC;QAClB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACrE,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,sDAAsD;YACtD,IAAI,GAAG,OAAO,CAAC;QACjB,CAAC;QAED,8CAA8C;QAC9C,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YAC3C,IAAI,CAAC;gBACH,OAAO,CAAC,IAAI,CAAC,CAAC;YAChB,CAAC;YAAC,MAAM,CAAC;gBACP,qEAAqE;YACvE,CAAC;QACH,CAAC;QAED,2DAA2D;QAC3D,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YACrC,MAAM,UAAU,GAAG,IAA+B,CAAC;YACnD,MAAM,cAAc,GAAG,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;YAEtD,IAAI,cAAc,EAAE,CAAC;gBACnB,MAAM,UAAU,GAAG,IAAI,CAAC,eAAe,CAAC,SAAS,CAC/C,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,KAAK,cAAc,CACvC,CAAC;gBAEF,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;oBACtB,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;oBACjD,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;oBAC3C,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;oBAC5B,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBACxB,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;;;OAOG;IACK,aAAa,CAAC,GAA4B;QAChD,sBAAsB;QACtB,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YACnC,OAAO,GAAG,CAAC,MAAM,CAAC;QACpB,CAAC;QAED,yBAAyB;QACzB,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACjC,OAAO,GAAG,CAAC,IAAI,CAAC;QAClB,CAAC;QAED,yBAAyB;QACzB,IAAI,GAAG,CAAC,QAAQ,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACrD,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAmC,CAAC;YACzD,IAAI,OAAO,QAAQ,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;gBACxC,OAAO,QAAQ,CAAC,MAAM,CAAC;YACzB,CAAC;QACH,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;;;;;;OAOG;IACK,gBAAgB;QACtB,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;QACvC,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;QAC1B,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC;QAEf,0CAA0C;QAC1C,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,OAAO;QACT,CAAC;QAED,uBAAuB;QACvB,IAAI,YAAY,EAAE,CAAC;YACjB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC1B,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACK,gBAAgB;QACtB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAExB,IAAI,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,oBAAoB,EAAE,CAAC;YACtD,wBAAwB;YACxB,IAAI,CAAC,sBAAsB,CAAC,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC,CAAC;YAC9E,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAClC,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAG,qBAAqB,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAEhF,IAAI,CAAC,cAAc,GAAG,UAAU,CAAC,KAAK,IAAI,EAAE;YAC1C,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;YAE3B,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBACrB,OAAO;YACT,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YACvB,CAAC;YAAC,MAAM,CAAC;gBACP,+BAA+B;gBAC/B,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,CAAC;QACH,CAAC,EAAE,KAAK,CAAC,CAAC;IACZ,CAAC;IAED;;OAEG;IACK,eAAe;QACrB,IAAI,IAAI,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;YACjC,YAAY,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAClC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC7B,CAAC;IACH,CAAC;IAED;;;;OAIG;IACK,sBAAsB,CAAC,KAAY;QACzC,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC;QAC1C,IAAI,CAAC,eAAe,GAAG,EAAE,CAAC;QAE1B,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YAC1B,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACxB,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IAED;;OAEG;IACK,oBAAoB,CAAC,OAAuB;QAClD,MAAM,KAAK,GAAG,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACpD,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;YACjB,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;CACF"}