@metamask-previews/network-controller 36.0.0-preview-abca9bcea → 37.0.0-preview-0a30e47
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 +24 -4
- package/dist/{NetworkController-method-action-types.d.mts → NetworkController-method-action-types.d.ts} +2 -2
- package/dist/NetworkController-method-action-types.d.ts.map +1 -0
- package/dist/{NetworkController-method-action-types.mjs → NetworkController-method-action-types.js} +1 -1
- package/dist/NetworkController-method-action-types.js.map +1 -0
- package/dist/{NetworkController.d.cts → NetworkController.d.ts} +37 -29
- package/dist/NetworkController.d.ts.map +1 -0
- package/dist/{NetworkController.mjs → NetworkController.js} +716 -612
- package/dist/NetworkController.js.map +1 -0
- package/dist/{constants.d.cts → constants.d.ts} +1 -1
- package/dist/constants.d.ts.map +1 -0
- package/dist/{constants.mjs → constants.js} +1 -1
- package/dist/constants.js.map +1 -0
- package/dist/{create-auto-managed-network-client.d.cts → create-auto-managed-network-client.d.ts} +7 -7
- package/dist/create-auto-managed-network-client.d.ts.map +1 -0
- package/dist/{create-auto-managed-network-client.mjs → create-auto-managed-network-client.js} +4 -4
- package/dist/create-auto-managed-network-client.js.map +1 -0
- package/dist/{create-network-client.d.cts → create-network-client.d.ts} +7 -7
- package/dist/create-network-client.d.ts.map +1 -0
- package/dist/{create-network-client.mjs → create-network-client.js} +17 -14
- package/dist/create-network-client.js.map +1 -0
- package/dist/{index.d.cts → index.d.ts} +16 -16
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +4 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +4 -0
- package/dist/logger.js.map +1 -0
- package/dist/rpc-service/{abstract-rpc-service.d.cts → abstract-rpc-service.d.ts} +2 -2
- package/dist/rpc-service/abstract-rpc-service.d.ts.map +1 -0
- package/dist/rpc-service/abstract-rpc-service.js +2 -0
- package/dist/rpc-service/abstract-rpc-service.js.map +1 -0
- package/dist/rpc-service/{rpc-service-chain.d.cts → rpc-service-chain.d.ts} +6 -6
- package/dist/rpc-service/rpc-service-chain.d.ts.map +1 -0
- package/dist/rpc-service/{rpc-service-chain.mjs → rpc-service-chain.js} +57 -70
- package/dist/rpc-service/rpc-service-chain.js.map +1 -0
- package/dist/rpc-service/{rpc-service-requestable.d.cts → rpc-service-requestable.d.ts} +4 -4
- package/dist/rpc-service/rpc-service-requestable.d.ts.map +1 -0
- package/dist/rpc-service/rpc-service-requestable.js +2 -0
- package/dist/rpc-service/rpc-service-requestable.js.map +1 -0
- package/dist/rpc-service/{rpc-service.d.mts → rpc-service.d.ts} +6 -6
- package/dist/rpc-service/rpc-service.d.ts.map +1 -0
- package/dist/rpc-service/{rpc-service.mjs → rpc-service.js} +206 -197
- package/dist/rpc-service/rpc-service.js.map +1 -0
- package/dist/rpc-service/{shared.d.cts → shared.d.ts} +2 -2
- package/dist/rpc-service/shared.d.ts.map +1 -0
- package/dist/rpc-service/shared.js +2 -0
- package/dist/rpc-service/shared.js.map +1 -0
- package/dist/{rpc-service-analytics.d.mts → rpc-service-analytics.d.ts} +3 -3
- package/dist/rpc-service-analytics.d.ts.map +1 -0
- package/dist/{rpc-service-analytics.mjs → rpc-service-analytics.js} +4 -4
- package/dist/rpc-service-analytics.js.map +1 -0
- package/dist/{selectors.d.mts → selectors.d.ts} +2 -2
- package/dist/selectors.d.ts.map +1 -0
- package/dist/{selectors.mjs → selectors.js} +1 -1
- package/dist/selectors.js.map +1 -0
- package/dist/{types.d.cts → types.d.ts} +5 -5
- package/dist/types.d.ts.map +1 -0
- package/dist/{types.mjs → types.js} +1 -1
- package/dist/types.js.map +1 -0
- package/package.json +26 -30
- package/dist/NetworkController-method-action-types.cjs +0 -7
- package/dist/NetworkController-method-action-types.cjs.map +0 -1
- package/dist/NetworkController-method-action-types.d.cts +0 -268
- package/dist/NetworkController-method-action-types.d.cts.map +0 -1
- package/dist/NetworkController-method-action-types.d.mts.map +0 -1
- package/dist/NetworkController-method-action-types.mjs.map +0 -1
- package/dist/NetworkController.cjs +0 -1597
- package/dist/NetworkController.cjs.map +0 -1
- package/dist/NetworkController.d.cts.map +0 -1
- package/dist/NetworkController.d.mts +0 -956
- package/dist/NetworkController.d.mts.map +0 -1
- package/dist/NetworkController.mjs.map +0 -1
- package/dist/constants.cjs +0 -46
- package/dist/constants.cjs.map +0 -1
- package/dist/constants.d.cts.map +0 -1
- package/dist/constants.d.mts +0 -42
- package/dist/constants.d.mts.map +0 -1
- package/dist/constants.mjs.map +0 -1
- package/dist/create-auto-managed-network-client.cjs +0 -149
- package/dist/create-auto-managed-network-client.cjs.map +0 -1
- package/dist/create-auto-managed-network-client.d.cts.map +0 -1
- package/dist/create-auto-managed-network-client.d.mts +0 -74
- package/dist/create-auto-managed-network-client.d.mts.map +0 -1
- package/dist/create-auto-managed-network-client.mjs.map +0 -1
- package/dist/create-network-client.cjs +0 -439
- package/dist/create-network-client.cjs.map +0 -1
- package/dist/create-network-client.d.cts.map +0 -1
- package/dist/create-network-client.d.mts +0 -60
- package/dist/create-network-client.d.mts.map +0 -1
- package/dist/create-network-client.mjs.map +0 -1
- package/dist/index.cjs +0 -31
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts.map +0 -1
- package/dist/index.d.mts +0 -16
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs +0 -6
- package/dist/index.mjs.map +0 -1
- package/dist/logger.cjs +0 -7
- package/dist/logger.cjs.map +0 -1
- package/dist/logger.d.cts +0 -5
- package/dist/logger.d.cts.map +0 -1
- package/dist/logger.d.mts +0 -5
- package/dist/logger.d.mts.map +0 -1
- package/dist/logger.mjs +0 -4
- package/dist/logger.mjs.map +0 -1
- package/dist/rpc-service/abstract-rpc-service.cjs +0 -3
- package/dist/rpc-service/abstract-rpc-service.cjs.map +0 -1
- package/dist/rpc-service/abstract-rpc-service.d.cts.map +0 -1
- package/dist/rpc-service/abstract-rpc-service.d.mts +0 -17
- package/dist/rpc-service/abstract-rpc-service.d.mts.map +0 -1
- package/dist/rpc-service/abstract-rpc-service.mjs +0 -2
- package/dist/rpc-service/abstract-rpc-service.mjs.map +0 -1
- package/dist/rpc-service/rpc-service-chain.cjs +0 -304
- package/dist/rpc-service/rpc-service-chain.cjs.map +0 -1
- package/dist/rpc-service/rpc-service-chain.d.cts.map +0 -1
- package/dist/rpc-service/rpc-service-chain.d.mts +0 -169
- package/dist/rpc-service/rpc-service-chain.d.mts.map +0 -1
- package/dist/rpc-service/rpc-service-chain.mjs.map +0 -1
- package/dist/rpc-service/rpc-service-requestable.cjs +0 -3
- package/dist/rpc-service/rpc-service-requestable.cjs.map +0 -1
- package/dist/rpc-service/rpc-service-requestable.d.cts.map +0 -1
- package/dist/rpc-service/rpc-service-requestable.d.mts +0 -63
- package/dist/rpc-service/rpc-service-requestable.d.mts.map +0 -1
- package/dist/rpc-service/rpc-service-requestable.mjs +0 -2
- package/dist/rpc-service/rpc-service-requestable.mjs.map +0 -1
- package/dist/rpc-service/rpc-service.cjs +0 -540
- package/dist/rpc-service/rpc-service.cjs.map +0 -1
- package/dist/rpc-service/rpc-service.d.cts +0 -247
- package/dist/rpc-service/rpc-service.d.cts.map +0 -1
- package/dist/rpc-service/rpc-service.d.mts.map +0 -1
- package/dist/rpc-service/rpc-service.mjs.map +0 -1
- package/dist/rpc-service/shared.cjs +0 -3
- package/dist/rpc-service/shared.cjs.map +0 -1
- package/dist/rpc-service/shared.d.cts.map +0 -1
- package/dist/rpc-service/shared.d.mts +0 -32
- package/dist/rpc-service/shared.d.mts.map +0 -1
- package/dist/rpc-service/shared.mjs +0 -2
- package/dist/rpc-service/shared.mjs.map +0 -1
- package/dist/rpc-service-analytics.cjs +0 -180
- package/dist/rpc-service-analytics.cjs.map +0 -1
- package/dist/rpc-service-analytics.d.cts +0 -85
- package/dist/rpc-service-analytics.d.cts.map +0 -1
- package/dist/rpc-service-analytics.d.mts.map +0 -1
- package/dist/rpc-service-analytics.mjs.map +0 -1
- package/dist/selectors.cjs +0 -16
- package/dist/selectors.cjs.map +0 -1
- package/dist/selectors.d.cts +0 -22
- package/dist/selectors.d.cts.map +0 -1
- package/dist/selectors.d.mts.map +0 -1
- package/dist/selectors.mjs.map +0 -1
- package/dist/types.cjs +0 -12
- package/dist/types.cjs.map +0 -1
- package/dist/types.d.cts.map +0 -1
- package/dist/types.d.mts +0 -49
- package/dist/types.d.mts.map +0 -1
- package/dist/types.mjs.map +0 -1
|
@@ -1,28 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
};
|
|
7
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
-
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");
|
|
10
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
-
};
|
|
12
|
-
var _RpcService_instances, _RpcService_currentRpcMethodName, _RpcService_currentTraceId, _RpcService_fetch, _RpcService_fetchOptions, _RpcService_logger, _RpcService_policy, _RpcService_getDefaultFetchOptions, _RpcService_getCompleteFetchOptions, _RpcService_executeAndProcessRequest;
|
|
13
|
-
function $importDefault(module) {
|
|
14
|
-
if (module?.__esModule) {
|
|
15
|
-
return module.default;
|
|
16
|
-
}
|
|
17
|
-
return module;
|
|
18
|
-
}
|
|
19
|
-
import { BrokenCircuitError, HttpError, createServicePolicy, handleWhen } from "@metamask/controller-utils";
|
|
20
|
-
import { JsonRpcError, rpcErrors } from "@metamask/rpc-errors";
|
|
21
|
-
import { Duration, getErrorMessage, hasProperty } from "@metamask/utils";
|
|
22
|
-
import { CircuitState } from "cockatiel";
|
|
23
|
-
import $deepmerge from "deepmerge";
|
|
24
|
-
const deepmerge = $importDefault($deepmerge);
|
|
25
|
-
import { projectLogger, createModuleLogger } from "../logger.mjs";
|
|
1
|
+
import { BrokenCircuitError, HttpError, createServicePolicy, handleWhen, } from '@metamask/controller-utils';
|
|
2
|
+
import { JsonRpcError, rpcErrors } from '@metamask/rpc-errors';
|
|
3
|
+
import { Duration, getErrorMessage, hasProperty } from '@metamask/utils';
|
|
4
|
+
import deepmerge from 'deepmerge';
|
|
5
|
+
import { projectLogger, createModuleLogger } from '../logger.js';
|
|
26
6
|
const log = createModuleLogger(projectLogger, 'RpcService');
|
|
27
7
|
/**
|
|
28
8
|
* The maximum number of times that a failing service should be re-run before
|
|
@@ -232,55 +212,63 @@ function isServiceFailureInfura(error) {
|
|
|
232
212
|
* which can used to respond to slow requests.
|
|
233
213
|
*/
|
|
234
214
|
export class RpcService {
|
|
215
|
+
/**
|
|
216
|
+
* The URL of the RPC endpoint.
|
|
217
|
+
*/
|
|
218
|
+
endpointUrl;
|
|
219
|
+
/**
|
|
220
|
+
* The last error that the retry policy captured (or `undefined` if the last
|
|
221
|
+
* execution of the service was successful).
|
|
222
|
+
*/
|
|
223
|
+
lastError;
|
|
224
|
+
/**
|
|
225
|
+
* The RPC method name of the current request being processed. This is passed
|
|
226
|
+
* to `onDegraded` event listeners.
|
|
227
|
+
*
|
|
228
|
+
* Initialised to `''` so the type is `string` throughout the event chain.
|
|
229
|
+
* The empty string is unreachable in practice because the method name is
|
|
230
|
+
* guaranteed to be set after the current request is completed but before
|
|
231
|
+
* any `onDegraded` callbacks are called.
|
|
232
|
+
*/
|
|
233
|
+
#currentRpcMethodName = '';
|
|
234
|
+
/**
|
|
235
|
+
* The trace ID from the `X-Trace-Id` response header of the most recent
|
|
236
|
+
* request. Passed to `onDegraded` event listeners for debugging.
|
|
237
|
+
*
|
|
238
|
+
* `undefined` when no response has been received yet or when the response
|
|
239
|
+
* did not include the header.
|
|
240
|
+
*/
|
|
241
|
+
#currentTraceId;
|
|
242
|
+
/**
|
|
243
|
+
* The function used to make an HTTP request.
|
|
244
|
+
*/
|
|
245
|
+
#fetch;
|
|
246
|
+
/**
|
|
247
|
+
* A common set of options that the request options will extend.
|
|
248
|
+
*/
|
|
249
|
+
#fetchOptions;
|
|
250
|
+
/**
|
|
251
|
+
* A `loglevel` logger.
|
|
252
|
+
*/
|
|
253
|
+
#logger;
|
|
254
|
+
/**
|
|
255
|
+
* The policy that wraps the request.
|
|
256
|
+
*/
|
|
257
|
+
#policy;
|
|
235
258
|
/**
|
|
236
259
|
* Constructs a new RpcService object.
|
|
237
260
|
*
|
|
238
261
|
* @param options - The options. See {@link RpcServiceOptions}.
|
|
239
262
|
*/
|
|
240
263
|
constructor(options) {
|
|
241
|
-
_RpcService_instances.add(this);
|
|
242
|
-
/**
|
|
243
|
-
* The RPC method name of the current request being processed. This is passed
|
|
244
|
-
* to `onDegraded` event listeners.
|
|
245
|
-
*
|
|
246
|
-
* Initialised to `''` so the type is `string` throughout the event chain.
|
|
247
|
-
* The empty string is unreachable in practice because the method name is
|
|
248
|
-
* guaranteed to be set after the current request is completed but before
|
|
249
|
-
* any `onDegraded` callbacks are called.
|
|
250
|
-
*/
|
|
251
|
-
_RpcService_currentRpcMethodName.set(this, '');
|
|
252
|
-
/**
|
|
253
|
-
* The trace ID from the `X-Trace-Id` response header of the most recent
|
|
254
|
-
* request. Passed to `onDegraded` event listeners for debugging.
|
|
255
|
-
*
|
|
256
|
-
* `undefined` when no response has been received yet or when the response
|
|
257
|
-
* did not include the header.
|
|
258
|
-
*/
|
|
259
|
-
_RpcService_currentTraceId.set(this, void 0);
|
|
260
|
-
/**
|
|
261
|
-
* The function used to make an HTTP request.
|
|
262
|
-
*/
|
|
263
|
-
_RpcService_fetch.set(this, void 0);
|
|
264
|
-
/**
|
|
265
|
-
* A common set of options that the request options will extend.
|
|
266
|
-
*/
|
|
267
|
-
_RpcService_fetchOptions.set(this, void 0);
|
|
268
|
-
/**
|
|
269
|
-
* A `loglevel` logger.
|
|
270
|
-
*/
|
|
271
|
-
_RpcService_logger.set(this, void 0);
|
|
272
|
-
/**
|
|
273
|
-
* The policy that wraps the request.
|
|
274
|
-
*/
|
|
275
|
-
_RpcService_policy.set(this, void 0);
|
|
276
264
|
const { btoa: givenBtoa, endpointUrl, fetch: givenFetch, logger, fetchOptions = {}, policyOptions = {}, isOffline, } = options;
|
|
277
|
-
|
|
265
|
+
this.#fetch = givenFetch;
|
|
278
266
|
const normalizedUrl = getNormalizedEndpointUrl(endpointUrl);
|
|
279
|
-
|
|
267
|
+
this.#fetchOptions = this.#getDefaultFetchOptions(normalizedUrl, fetchOptions, givenBtoa);
|
|
280
268
|
this.endpointUrl = stripCredentialsFromUrl(normalizedUrl);
|
|
281
|
-
|
|
269
|
+
this.#logger = logger;
|
|
282
270
|
const isInfura = normalizedUrl.hostname.endsWith('.infura.io');
|
|
283
|
-
|
|
271
|
+
this.#policy = createServicePolicy({
|
|
284
272
|
maxRetries: DEFAULT_MAX_RETRIES,
|
|
285
273
|
maxConsecutiveFailures: DEFAULT_MAX_CONSECUTIVE_FAILURES,
|
|
286
274
|
...policyOptions,
|
|
@@ -303,7 +291,7 @@ export class RpcService {
|
|
|
303
291
|
// Ignore connection reset errors
|
|
304
292
|
isConnectionResetError(error));
|
|
305
293
|
}),
|
|
306
|
-
})
|
|
294
|
+
});
|
|
307
295
|
}
|
|
308
296
|
/**
|
|
309
297
|
* Resets the underlying composite Cockatiel policy.
|
|
@@ -313,13 +301,13 @@ export class RpcService {
|
|
|
313
301
|
* primary recovers.
|
|
314
302
|
*/
|
|
315
303
|
resetPolicy() {
|
|
316
|
-
|
|
304
|
+
this.#policy.reset();
|
|
317
305
|
}
|
|
318
306
|
/**
|
|
319
307
|
* @returns The state of the underlying circuit.
|
|
320
308
|
*/
|
|
321
309
|
getCircuitState() {
|
|
322
|
-
return
|
|
310
|
+
return this.#policy.getCircuitState();
|
|
323
311
|
}
|
|
324
312
|
/**
|
|
325
313
|
* Listens for when the RPC service retries the request.
|
|
@@ -329,7 +317,7 @@ export class RpcService {
|
|
|
329
317
|
* @see {@link createServicePolicy}
|
|
330
318
|
*/
|
|
331
319
|
onRetry(listener) {
|
|
332
|
-
return
|
|
320
|
+
return this.#policy.onRetry((data) => {
|
|
333
321
|
listener({ ...data, endpointUrl: this.endpointUrl.toString() });
|
|
334
322
|
});
|
|
335
323
|
}
|
|
@@ -342,7 +330,7 @@ export class RpcService {
|
|
|
342
330
|
* @see {@link createServicePolicy}
|
|
343
331
|
*/
|
|
344
332
|
onBreak(listener) {
|
|
345
|
-
return
|
|
333
|
+
return this.#policy.onBreak((data) => {
|
|
346
334
|
// `{ isolated: true }` is a special object that shows up when `isolate`
|
|
347
335
|
// is called on the circuit breaker. Usually `isolate` is used to hold the
|
|
348
336
|
// circuit open, but we (ab)use this method in `createServicePolicy` to
|
|
@@ -371,12 +359,12 @@ export class RpcService {
|
|
|
371
359
|
* @see {@link createServicePolicy}
|
|
372
360
|
*/
|
|
373
361
|
onDegraded(listener) {
|
|
374
|
-
return
|
|
362
|
+
return this.#policy.onDegraded((data) => {
|
|
375
363
|
listener({
|
|
376
364
|
...data,
|
|
377
365
|
endpointUrl: this.endpointUrl.toString(),
|
|
378
|
-
rpcMethodName:
|
|
379
|
-
traceId:
|
|
366
|
+
rpcMethodName: this.#currentRpcMethodName,
|
|
367
|
+
traceId: this.#currentTraceId,
|
|
380
368
|
});
|
|
381
369
|
});
|
|
382
370
|
}
|
|
@@ -388,148 +376,169 @@ export class RpcService {
|
|
|
388
376
|
* @see {@link createServicePolicy}
|
|
389
377
|
*/
|
|
390
378
|
onAvailable(listener) {
|
|
391
|
-
return
|
|
379
|
+
return this.#policy.onAvailable(() => {
|
|
392
380
|
listener({ endpointUrl: this.endpointUrl.toString() });
|
|
393
381
|
});
|
|
394
382
|
}
|
|
395
383
|
async request(
|
|
396
384
|
// The request object may be frozen and must not be mutated.
|
|
397
385
|
jsonRpcRequest, fetchOptions = {}) {
|
|
398
|
-
const completeFetchOptions =
|
|
399
|
-
return await
|
|
386
|
+
const completeFetchOptions = this.#getCompleteFetchOptions(jsonRpcRequest, fetchOptions);
|
|
387
|
+
return await this.#executeAndProcessRequest(completeFetchOptions, jsonRpcRequest.method);
|
|
400
388
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
389
|
+
/**
|
|
390
|
+
* Constructs a default set of options to `fetch`.
|
|
391
|
+
*
|
|
392
|
+
* If a username and password are present in the URL, they are extracted to an
|
|
393
|
+
* Authorization header.
|
|
394
|
+
*
|
|
395
|
+
* @param endpointUrl - The endpoint URL.
|
|
396
|
+
* @param fetchOptions - The options to `fetch`.
|
|
397
|
+
* @param givenBtoa - An implementation of `btoa`.
|
|
398
|
+
* @returns The default fetch options.
|
|
399
|
+
*/
|
|
400
|
+
#getDefaultFetchOptions(endpointUrl, fetchOptions, givenBtoa) {
|
|
401
|
+
if (endpointUrl.username && endpointUrl.password) {
|
|
402
|
+
const authString = `${endpointUrl.username}:${endpointUrl.password}`;
|
|
403
|
+
const encodedCredentials = givenBtoa(authString);
|
|
404
|
+
return deepmerge(fetchOptions, {
|
|
405
|
+
headers: { Authorization: `Basic ${encodedCredentials}` },
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
return fetchOptions;
|
|
409
409
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
* fetch options passed to the constructor
|
|
434
|
-
* @param rpcMethodName - The JSON-RPC method name of the current request.
|
|
435
|
-
* @returns The decoded JSON-RPC response from the endpoint.
|
|
436
|
-
* @throws An "authorized" JSON-RPC error (code -32006) if the response HTTP status is 401.
|
|
437
|
-
* @throws A "rate limiting" JSON-RPC error (code -32005) if the response HTTP status is 429.
|
|
438
|
-
* @throws A "resource unavailable" JSON-RPC error (code -32002) if the response HTTP status is 402, 404, or any 5xx.
|
|
439
|
-
* @throws A generic HTTP client JSON-RPC error (code -32050) for any other 4xx HTTP status codes.
|
|
440
|
-
* @throws A "parse" JSON-RPC error (code -32700) if the response is not valid JSON.
|
|
441
|
-
*/
|
|
442
|
-
async function _RpcService_executeAndProcessRequest(fetchOptions, rpcMethodName) {
|
|
443
|
-
let response;
|
|
444
|
-
try {
|
|
445
|
-
log(`[${this.endpointUrl}] Circuit state`, __classPrivateFieldGet(this, _RpcService_policy, "f").getCircuitState());
|
|
446
|
-
const jsonDecodedResponse = await __classPrivateFieldGet(this, _RpcService_policy, "f").execute(async (context) => {
|
|
447
|
-
// Reset response so that if this attempt throws before
|
|
448
|
-
// assigning a new response, the finally block does not read
|
|
449
|
-
// a stale response from a previous attempt.
|
|
450
|
-
response = undefined;
|
|
451
|
-
try {
|
|
452
|
-
log('REQUEST INITIATED:', this.endpointUrl.toString(), '::', fetchOptions,
|
|
453
|
-
// @ts-expect-error This property _is_ here, the type of
|
|
454
|
-
// ServicePolicy is just wrong.
|
|
455
|
-
`(attempt ${context.attempt + 1})`);
|
|
456
|
-
response = await __classPrivateFieldGet(this, _RpcService_fetch, "f").call(this, this.endpointUrl, fetchOptions);
|
|
457
|
-
if (!response.ok) {
|
|
458
|
-
throw new HttpError(response.status);
|
|
459
|
-
}
|
|
460
|
-
log('REQUEST SUCCESSFUL:', this.endpointUrl.toString(), response.status);
|
|
461
|
-
return await response.json();
|
|
462
|
-
}
|
|
463
|
-
finally {
|
|
464
|
-
// Track the RPC method and trace ID for the request that has just
|
|
465
|
-
// taken place. We pass these properties to `onDegraded` event
|
|
466
|
-
// listeners.
|
|
467
|
-
//
|
|
468
|
-
// We set these properties after the request completes and not
|
|
469
|
-
// before the request starts to account for race conditions. That
|
|
470
|
-
// is, if there are two requests that are being performed
|
|
471
|
-
// concurrently, and the second request fails fast but the first
|
|
472
|
-
// request succeeds slowly, when `onDegraded` is called we want it
|
|
473
|
-
// to include the first request as the RPC method, not the second.
|
|
474
|
-
//
|
|
475
|
-
// Also, we set these properties within a `finally` block inside of
|
|
476
|
-
// the function passed to `policy.execute` to ensure that they are
|
|
477
|
-
// set before `onDegraded` gets called, no matter the outcome of
|
|
478
|
-
// the request.
|
|
479
|
-
__classPrivateFieldSet(this, _RpcService_currentRpcMethodName, rpcMethodName, "f");
|
|
480
|
-
__classPrivateFieldSet(this, _RpcService_currentTraceId, response?.headers.get('X-Trace-Id') ?? undefined, "f");
|
|
481
|
-
}
|
|
410
|
+
/**
|
|
411
|
+
* Constructs a final set of options to pass to `fetch`. Note that the method
|
|
412
|
+
* defaults to `post`, and the JSON-RPC request is automatically JSON-encoded.
|
|
413
|
+
*
|
|
414
|
+
* @param jsonRpcRequest - The JSON-RPC request.
|
|
415
|
+
* @param fetchOptions - Custom `fetch` options.
|
|
416
|
+
* @returns The complete set of `fetch` options.
|
|
417
|
+
*/
|
|
418
|
+
#getCompleteFetchOptions(jsonRpcRequest, fetchOptions) {
|
|
419
|
+
const defaultOptions = {
|
|
420
|
+
method: 'POST',
|
|
421
|
+
headers: {
|
|
422
|
+
Accept: 'application/json',
|
|
423
|
+
'Content-Type': 'application/json',
|
|
424
|
+
},
|
|
425
|
+
};
|
|
426
|
+
const mergedOptions = deepmerge(defaultOptions, deepmerge(this.#fetchOptions, fetchOptions));
|
|
427
|
+
const { id, jsonrpc, method, params } = jsonRpcRequest;
|
|
428
|
+
const body = JSON.stringify({
|
|
429
|
+
id,
|
|
430
|
+
jsonrpc,
|
|
431
|
+
method,
|
|
432
|
+
params,
|
|
482
433
|
});
|
|
483
|
-
|
|
484
|
-
return jsonDecodedResponse;
|
|
434
|
+
return { ...mergedOptions, body };
|
|
485
435
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
436
|
+
/**
|
|
437
|
+
* Makes the request using the Cockatiel policy that this service creates.
|
|
438
|
+
*
|
|
439
|
+
* @param fetchOptions - The options for `fetch`; will be combined with the
|
|
440
|
+
* fetch options passed to the constructor
|
|
441
|
+
* @param rpcMethodName - The JSON-RPC method name of the current request.
|
|
442
|
+
* @returns The decoded JSON-RPC response from the endpoint.
|
|
443
|
+
* @throws An "authorized" JSON-RPC error (code -32006) if the response HTTP status is 401.
|
|
444
|
+
* @throws A "rate limiting" JSON-RPC error (code -32005) if the response HTTP status is 429.
|
|
445
|
+
* @throws A "resource unavailable" JSON-RPC error (code -32002) if the response HTTP status is 402, 404, or any 5xx.
|
|
446
|
+
* @throws A generic HTTP client JSON-RPC error (code -32050) for any other 4xx HTTP status codes.
|
|
447
|
+
* @throws A "parse" JSON-RPC error (code -32700) if the response is not valid JSON.
|
|
448
|
+
*/
|
|
449
|
+
async #executeAndProcessRequest(fetchOptions, rpcMethodName) {
|
|
450
|
+
let response;
|
|
451
|
+
try {
|
|
452
|
+
log(`[${this.endpointUrl}] Circuit state`, this.#policy.getCircuitState());
|
|
453
|
+
const jsonDecodedResponse = await this.#policy.execute(async (context) => {
|
|
454
|
+
// Reset response so that if this attempt throws before
|
|
455
|
+
// assigning a new response, the finally block does not read
|
|
456
|
+
// a stale response from a previous attempt.
|
|
457
|
+
response = undefined;
|
|
458
|
+
try {
|
|
459
|
+
log('REQUEST INITIATED:', this.endpointUrl.toString(), '::', fetchOptions,
|
|
460
|
+
// @ts-expect-error This property _is_ here, the type of
|
|
461
|
+
// ServicePolicy is just wrong.
|
|
462
|
+
`(attempt ${context.attempt + 1})`);
|
|
463
|
+
response = await this.#fetch(this.endpointUrl, fetchOptions);
|
|
464
|
+
if (!response.ok) {
|
|
465
|
+
throw new HttpError(response.status);
|
|
466
|
+
}
|
|
467
|
+
log('REQUEST SUCCESSFUL:', this.endpointUrl.toString(), response.status);
|
|
468
|
+
return await response.json();
|
|
469
|
+
}
|
|
470
|
+
finally {
|
|
471
|
+
// Track the RPC method and trace ID for the request that has just
|
|
472
|
+
// taken place. We pass these properties to `onDegraded` event
|
|
473
|
+
// listeners.
|
|
474
|
+
//
|
|
475
|
+
// We set these properties after the request completes and not
|
|
476
|
+
// before the request starts to account for race conditions. That
|
|
477
|
+
// is, if there are two requests that are being performed
|
|
478
|
+
// concurrently, and the second request fails fast but the first
|
|
479
|
+
// request succeeds slowly, when `onDegraded` is called we want it
|
|
480
|
+
// to include the first request as the RPC method, not the second.
|
|
481
|
+
//
|
|
482
|
+
// Also, we set these properties within a `finally` block inside of
|
|
483
|
+
// the function passed to `policy.execute` to ensure that they are
|
|
484
|
+
// set before `onDegraded` gets called, no matter the outcome of
|
|
485
|
+
// the request.
|
|
486
|
+
this.#currentRpcMethodName = rpcMethodName;
|
|
487
|
+
this.#currentTraceId =
|
|
488
|
+
response?.headers.get('X-Trace-Id') ?? undefined;
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
this.lastError = undefined;
|
|
492
|
+
return jsonDecodedResponse;
|
|
493
|
+
}
|
|
494
|
+
catch (error) {
|
|
495
|
+
log('REQUEST ERROR:', this.endpointUrl.toString(), error);
|
|
496
|
+
this.lastError =
|
|
497
|
+
error instanceof Error ? error : new Error(getErrorMessage(error));
|
|
498
|
+
if (error instanceof HttpError) {
|
|
499
|
+
const status = error.httpStatus;
|
|
500
|
+
if (status === 401) {
|
|
501
|
+
throw new JsonRpcError(CUSTOM_RPC_ERRORS.unauthorized, 'Unauthorized.', {
|
|
502
|
+
httpStatus: status,
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
if (status === 429) {
|
|
506
|
+
throw rpcErrors.limitExceeded({
|
|
507
|
+
message: 'Request is being rate limited.',
|
|
508
|
+
data: {
|
|
509
|
+
httpStatus: status,
|
|
510
|
+
},
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
if (status >= 500 || status === 402 || status === 404) {
|
|
514
|
+
throw rpcErrors.resourceUnavailable({
|
|
515
|
+
message: 'RPC endpoint not found or unavailable.',
|
|
516
|
+
data: {
|
|
517
|
+
httpStatus: status,
|
|
518
|
+
},
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
// Handle all other 4xx errors as generic HTTP client errors
|
|
522
|
+
throw new JsonRpcError(CUSTOM_RPC_ERRORS.httpClientError, 'RPC endpoint returned HTTP client error.', {
|
|
494
523
|
httpStatus: status,
|
|
495
524
|
});
|
|
496
525
|
}
|
|
497
|
-
if (
|
|
498
|
-
throw rpcErrors.
|
|
499
|
-
message: '
|
|
500
|
-
data: {
|
|
501
|
-
httpStatus: status,
|
|
502
|
-
},
|
|
526
|
+
else if (isJsonParseError(error)) {
|
|
527
|
+
throw rpcErrors.parse({
|
|
528
|
+
message: 'RPC endpoint did not return JSON.',
|
|
503
529
|
});
|
|
504
530
|
}
|
|
505
|
-
if (
|
|
531
|
+
else if (error instanceof BrokenCircuitError) {
|
|
532
|
+
this.#logger?.warn(error);
|
|
533
|
+
const remainingCircuitOpenDuration = this.#policy.getRemainingCircuitOpenDuration();
|
|
534
|
+
const formattedRemainingCircuitOpenDuration = Intl.NumberFormat(undefined, { maximumFractionDigits: 2 }).format((remainingCircuitOpenDuration ?? this.#policy.circuitBreakDuration) /
|
|
535
|
+
Duration.Minute);
|
|
506
536
|
throw rpcErrors.resourceUnavailable({
|
|
507
|
-
message:
|
|
508
|
-
data: {
|
|
509
|
-
httpStatus: status,
|
|
510
|
-
},
|
|
537
|
+
message: `RPC endpoint returned too many errors, retrying in ${formattedRemainingCircuitOpenDuration} minutes. Consider using a different RPC endpoint.`,
|
|
511
538
|
});
|
|
512
539
|
}
|
|
513
|
-
|
|
514
|
-
throw new JsonRpcError(CUSTOM_RPC_ERRORS.httpClientError, 'RPC endpoint returned HTTP client error.', {
|
|
515
|
-
httpStatus: status,
|
|
516
|
-
});
|
|
517
|
-
}
|
|
518
|
-
else if (isJsonParseError(error)) {
|
|
519
|
-
throw rpcErrors.parse({
|
|
520
|
-
message: 'RPC endpoint did not return JSON.',
|
|
521
|
-
});
|
|
540
|
+
throw error;
|
|
522
541
|
}
|
|
523
|
-
else if (error instanceof BrokenCircuitError) {
|
|
524
|
-
__classPrivateFieldGet(this, _RpcService_logger, "f")?.warn(error);
|
|
525
|
-
const remainingCircuitOpenDuration = __classPrivateFieldGet(this, _RpcService_policy, "f").getRemainingCircuitOpenDuration();
|
|
526
|
-
const formattedRemainingCircuitOpenDuration = Intl.NumberFormat(undefined, { maximumFractionDigits: 2 }).format((remainingCircuitOpenDuration ?? __classPrivateFieldGet(this, _RpcService_policy, "f").circuitBreakDuration) /
|
|
527
|
-
Duration.Minute);
|
|
528
|
-
throw rpcErrors.resourceUnavailable({
|
|
529
|
-
message: `RPC endpoint returned too many errors, retrying in ${formattedRemainingCircuitOpenDuration} minutes. Consider using a different RPC endpoint.`,
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
|
-
throw error;
|
|
533
542
|
}
|
|
534
|
-
}
|
|
535
|
-
//# sourceMappingURL=rpc-service.
|
|
543
|
+
}
|
|
544
|
+
//# sourceMappingURL=rpc-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-service.js","sourceRoot":"","sources":["../../src/rpc-service/rpc-service.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CreateServicePolicyOptions,\n ServicePolicy,\n} from '@metamask/controller-utils';\nimport {\n BrokenCircuitError,\n HttpError,\n createServicePolicy,\n handleWhen,\n} from '@metamask/controller-utils';\nimport { JsonRpcError, rpcErrors } from '@metamask/rpc-errors';\nimport { Duration, getErrorMessage, hasProperty } from '@metamask/utils';\nimport type {\n Json,\n JsonRpcParams,\n JsonRpcRequest,\n JsonRpcResponse,\n} from '@metamask/utils';\nimport { CircuitState } from 'cockatiel';\nimport deepmerge from 'deepmerge';\nimport type { Logger } from 'loglevel';\n\nimport { projectLogger, createModuleLogger } from '../logger.js';\nimport type {\n CockatielEventToEventListenerWithData,\n ExcludeCockatielEventData,\n ExtendCockatielEventData,\n ExtractCockatielEventData,\n FetchOptions,\n} from './shared.js';\n\n/**\n * Options for the RpcService constructor with some properties omitted and made optional as they have defaults.\n */\nexport type RpcServiceOptionsWithDefaults = Omit<\n RpcServiceOptions,\n 'failoverService' | 'endpointUrl' | 'isOffline' | 'btoa' | 'fetch'\n> &\n Partial<Pick<RpcServiceOptions, 'isOffline' | 'btoa' | 'fetch'>>;\n\n/**\n * Options for the RpcService constructor.\n */\nexport type RpcServiceOptions = {\n /**\n * A function that can be used to convert a binary string into a\n * base64-encoded ASCII string. Used to encode authorization credentials.\n */\n btoa: typeof btoa;\n /**\n * The URL of the RPC endpoint to hit.\n */\n endpointUrl: URL | string;\n /**\n * A function that can be used to make an HTTP request. If your JavaScript\n * environment supports `fetch` natively, you'll probably want to pass that;\n * otherwise you can pass an equivalent (such as `fetch` via `node-fetch`).\n */\n fetch: typeof fetch;\n /**\n * A common set of options that will be used to make every request. Can be\n * overridden on the request level (e.g. to add headers).\n */\n fetchOptions?: FetchOptions;\n /**\n * A `loglevel` logger.\n */\n logger?: Pick<Logger, 'warn'>;\n /**\n * Options to pass to `createServicePolicy`. Note that `retryFilterPolicy` and `isServiceFailure`\n * are not accepted, as they are overwritten. See {@link createServicePolicy}.\n */\n policyOptions?: Omit<\n CreateServicePolicyOptions,\n 'retryFilterPolicy' | 'isServiceFailure'\n >;\n /**\n * A function that checks if the user is currently offline. If it returns true,\n * connection errors will not be retried, preventing degraded and break\n * callbacks from being triggered.\n */\n isOffline: () => boolean;\n};\n\nconst log = createModuleLogger(projectLogger, 'RpcService');\n\n/**\n * The maximum number of times that a failing service should be re-run before\n * giving up.\n *\n * Note: This is not used in production and should be removed.\n */\nexport const DEFAULT_MAX_RETRIES = 4;\n\n/**\n * The maximum number of times that the service is allowed to fail before\n * pausing further retries. This is set to a value such that if given a\n * service that continually fails, the policy needs to be executed 3 times\n * before further retries are paused.\n */\nexport const DEFAULT_MAX_CONSECUTIVE_FAILURES = (1 + DEFAULT_MAX_RETRIES) * 3;\n\n/**\n * The list of error messages that represent a failure to connect to the network.\n *\n * This list was derived from Sindre Sorhus's `is-network-error` package:\n * <https://github.com/sindresorhus/is-network-error/blob/7bbfa8be9482ce1427a21fbff60e3ee1650dd091/index.js>\n */\nexport const CONNECTION_ERRORS = [\n // Chrome\n {\n constructorName: 'TypeError',\n pattern: /network error/u,\n },\n // Chrome\n {\n constructorName: 'TypeError',\n pattern: /Failed to fetch/u,\n },\n // Firefox\n {\n constructorName: 'TypeError',\n pattern: /NetworkError when attempting to fetch resource\\./u,\n },\n // Safari 16\n {\n constructorName: 'TypeError',\n pattern: /The Internet connection appears to be offline\\./u,\n },\n // Safari 17+\n {\n constructorName: 'TypeError',\n pattern: /Load failed/u,\n },\n // `cross-fetch`\n {\n constructorName: 'TypeError',\n pattern: /Network request failed/u,\n },\n // `node-fetch`\n {\n constructorName: 'FetchError',\n pattern: /request to (.+) failed/u,\n },\n // Undici (Node.js)\n {\n constructorName: 'TypeError',\n pattern: /fetch failed/u,\n },\n // Undici (Node.js)\n {\n constructorName: 'TypeError',\n pattern: /terminated/u,\n },\n];\n\n/**\n * Custom JSON-RPC error codes for specific cases.\n *\n * These should be moved to `@metamask/rpc-errors` eventually.\n */\nexport const CUSTOM_RPC_ERRORS = {\n unauthorized: -32006,\n httpClientError: -32080,\n} as const;\n\n/**\n * Determines whether the given error represents a failure to reach the network\n * after request parameters have been validated.\n *\n * This is somewhat difficult to verify because JavaScript engines (and in\n * some cases libraries) produce slightly different error messages for this\n * particular scenario, and we need to account for this.\n *\n * @param error - The error.\n * @returns True if the error indicates that the network cannot be connected to,\n * and false otherwise.\n */\nexport function isConnectionError(error: unknown): boolean {\n if (!(typeof error === 'object' && error !== null && 'message' in error)) {\n return false;\n }\n\n const { message } = error;\n\n return (\n typeof message === 'string' &&\n !isNockError(message) &&\n CONNECTION_ERRORS.some(({ constructorName, pattern }) => {\n return (\n error.constructor.name === constructorName && pattern.test(message)\n );\n })\n );\n}\n\n/**\n * Determines whether the given error message refers to a Nock error.\n *\n * It's important that if we failed to mock a request in a test, the resulting\n * error does not cause the request to be retried so that we can see it right\n * away.\n *\n * @param message - The error message to test.\n * @returns True if the message indicates a missing Nock mock, false otherwise.\n */\nfunction isNockError(message: string): boolean {\n return message.includes('Nock:');\n}\n\n/**\n * Determine whether the given error message indicates a failure to parse JSON.\n *\n * This is different in tests vs. implementation code because it may manifest as\n * a FetchError or a SyntaxError.\n *\n * @param error - The error object to test.\n * @returns True if the error indicates a JSON parse error, false otherwise.\n */\nexport function isJsonParseError(error: unknown): boolean {\n return (\n error instanceof SyntaxError ||\n /invalid json/iu.test(getErrorMessage(error))\n );\n}\n\n/**\n * Determines whether the given error represents a HTTP server error\n * (502, 503, or 504) that should be retried.\n *\n * @param error - The error object to test.\n * @returns True if the error has an httpStatus of 502, 503, or 504.\n */\nexport function isHttpServerError(error: Error): boolean {\n return (\n 'httpStatus' in error &&\n (error.httpStatus === 502 ||\n error.httpStatus === 503 ||\n error.httpStatus === 504)\n );\n}\n\n/**\n * Determines whether the given error has a `code` property of `ETIMEDOUT`.\n *\n * @param error - The error object to test.\n * @returns True if the error code is `ETIMEDOUT`.\n */\nexport function isTimeoutError(error: Error): boolean {\n return hasProperty(error, 'code') && error.code === 'ETIMEDOUT';\n}\n\n/**\n * Determines whether the given error has a `code` property of `ECONNRESET`.\n *\n * @param error - The error object to test.\n * @returns True if the error code is `ECONNRESET`.\n */\nexport function isConnectionResetError(error: Error): boolean {\n return hasProperty(error, 'code') && error.code === 'ECONNRESET';\n}\n\n/**\n * Guarantees a URL, even given a string. This is useful for checking components\n * of that URL.\n *\n * @param endpointUrlOrUrlString - Either a URL object or a string that\n * represents the URL of an endpoint.\n * @returns A URL object.\n */\nfunction getNormalizedEndpointUrl(endpointUrlOrUrlString: URL | string): URL {\n return endpointUrlOrUrlString instanceof URL\n ? endpointUrlOrUrlString\n : new URL(endpointUrlOrUrlString);\n}\n\n/**\n * Strips username and password from a URL.\n *\n * @param url - The URL to strip credentials from.\n * @returns A new URL object with credentials removed.\n */\nfunction stripCredentialsFromUrl(url: URL): URL {\n const strippedUrl = new URL(url.toString());\n strippedUrl.username = '';\n strippedUrl.password = '';\n return strippedUrl;\n}\n\nconst INFURA_NON_FAILURE_HTTP_STATUS_CODES = [400, 429];\n\n/**\n * Predicate function that determines if an error from Infura is treated as a service failure.\n *\n * @param error - The error.\n * @returns True if the error should be treated as a service policy failure. Most errors are treated like failures,\n * with the exception of certain HTTP status codes.\n */\nfunction isServiceFailureInfura(error: unknown): boolean {\n if (\n typeof error === 'object' &&\n error !== null &&\n hasProperty(error, 'httpStatus') &&\n typeof error.httpStatus === 'number'\n ) {\n return !INFURA_NON_FAILURE_HTTP_STATUS_CODES.includes(error.httpStatus);\n }\n\n // If the error is not an object, or doesn't have a numeric httpStatus\n // property, consider it a service failure (e.g., network errors, timeouts,\n // etc.)\n return true;\n}\n\n/**\n * This class is responsible for making a request to an endpoint that implements\n * the JSON-RPC protocol. It is designed to gracefully handle network and server\n * failures, retrying requests using exponential backoff. It also offers a hook\n * which can used to respond to slow requests.\n */\nexport class RpcService {\n /**\n * The URL of the RPC endpoint.\n */\n readonly endpointUrl: URL;\n\n /**\n * The last error that the retry policy captured (or `undefined` if the last\n * execution of the service was successful).\n */\n lastError: Error | undefined;\n\n /**\n * The RPC method name of the current request being processed. This is passed\n * to `onDegraded` event listeners.\n *\n * Initialised to `''` so the type is `string` throughout the event chain.\n * The empty string is unreachable in practice because the method name is\n * guaranteed to be set after the current request is completed but before\n * any `onDegraded` callbacks are called.\n */\n #currentRpcMethodName = '';\n\n /**\n * The trace ID from the `X-Trace-Id` response header of the most recent\n * request. Passed to `onDegraded` event listeners for debugging.\n *\n * `undefined` when no response has been received yet or when the response\n * did not include the header.\n */\n #currentTraceId: string | undefined;\n\n /**\n * The function used to make an HTTP request.\n */\n readonly #fetch: typeof fetch;\n\n /**\n * A common set of options that the request options will extend.\n */\n readonly #fetchOptions: FetchOptions;\n\n /**\n * A `loglevel` logger.\n */\n readonly #logger: RpcServiceOptions['logger'];\n\n /**\n * The policy that wraps the request.\n */\n readonly #policy: ServicePolicy;\n\n /**\n * Constructs a new RpcService object.\n *\n * @param options - The options. See {@link RpcServiceOptions}.\n */\n constructor(options: RpcServiceOptions) {\n const {\n btoa: givenBtoa,\n endpointUrl,\n fetch: givenFetch,\n logger,\n fetchOptions = {},\n policyOptions = {},\n isOffline,\n } = options;\n\n this.#fetch = givenFetch;\n const normalizedUrl = getNormalizedEndpointUrl(endpointUrl);\n this.#fetchOptions = this.#getDefaultFetchOptions(\n normalizedUrl,\n fetchOptions,\n givenBtoa,\n );\n this.endpointUrl = stripCredentialsFromUrl(normalizedUrl);\n this.#logger = logger;\n\n const isInfura = normalizedUrl.hostname.endsWith('.infura.io');\n\n this.#policy = createServicePolicy({\n maxRetries: DEFAULT_MAX_RETRIES,\n maxConsecutiveFailures: DEFAULT_MAX_CONSECUTIVE_FAILURES,\n ...policyOptions,\n isServiceFailure: isInfura ? isServiceFailureInfura : undefined,\n retryFilterPolicy: handleWhen((error) => {\n // If user is offline, don't retry any errors\n // This prevents degraded/break callbacks from being triggered\n if (isOffline()) {\n return false;\n }\n\n return (\n // Ignore errors where the request failed to establish\n isConnectionError(error) ||\n // Ignore server sent HTML error pages or truncated JSON responses\n isJsonParseError(error) ||\n // Ignore server overload errors\n isHttpServerError(error) ||\n // Ignore timeout errors\n isTimeoutError(error) ||\n // Ignore connection reset errors\n isConnectionResetError(error)\n );\n }),\n });\n }\n\n /**\n * Resets the underlying composite Cockatiel policy.\n *\n * This is useful in a collection of RpcServices where some act as failovers\n * for others where you effectively want to invalidate the failovers when the\n * primary recovers.\n */\n resetPolicy(): void {\n this.#policy.reset();\n }\n\n /**\n * @returns The state of the underlying circuit.\n */\n getCircuitState(): CircuitState {\n return this.#policy.getCircuitState();\n }\n\n /**\n * Listens for when the RPC service retries the request.\n *\n * @param listener - The callback to be called when the retry occurs.\n * @returns What {@link ServicePolicy.onRetry} returns.\n * @see {@link createServicePolicy}\n */\n onRetry(\n listener: CockatielEventToEventListenerWithData<\n ServicePolicy['onRetry'],\n { endpointUrl: string }\n >,\n ): ReturnType<ServicePolicy['onRetry']> {\n return this.#policy.onRetry((data) => {\n listener({ ...data, endpointUrl: this.endpointUrl.toString() });\n });\n }\n\n /**\n * Listens for when the RPC service retries the request too many times in a\n * row, causing the underlying circuit to break.\n *\n * @param listener - The callback to be called when the circuit is broken.\n * @returns What {@link ServicePolicy.onBreak} returns.\n * @see {@link createServicePolicy}\n */\n onBreak(\n listener: (\n data: ExcludeCockatielEventData<\n ExtendCockatielEventData<\n ExtractCockatielEventData<ServicePolicy['onBreak']>,\n { endpointUrl: string }\n >,\n 'isolated'\n >,\n ) => void,\n ): ReturnType<ServicePolicy['onBreak']> {\n return this.#policy.onBreak((data) => {\n // `{ isolated: true }` is a special object that shows up when `isolate`\n // is called on the circuit breaker. Usually `isolate` is used to hold the\n // circuit open, but we (ab)use this method in `createServicePolicy` to\n // reset the circuit breaker policy. When we do this, we don't want to\n // call `onBreak` handlers, because then it causes\n // `NetworkController:rpcEndpointUnavailable` and\n // `NetworkController:rpcEndpointChainUnavailable` to be published. So we\n // have to ignore that object here. The consequence is that `isolate`\n // doesn't function the way it is intended, at least in the context of an\n // RpcService. However, we are making a bet that we won't need to use it\n // other than how we are already using it.\n if (!('isolated' in data)) {\n listener({\n ...data,\n endpointUrl: this.endpointUrl.toString(),\n });\n }\n });\n }\n\n /**\n * Listens for when the policy underlying this RPC service detects a slow\n * request.\n *\n * @param listener - The callback to be called when the request is slow.\n * @returns What {@link ServicePolicy.onDegraded} returns.\n * @see {@link createServicePolicy}\n */\n onDegraded(\n listener: CockatielEventToEventListenerWithData<\n ServicePolicy['onDegraded'],\n {\n duration?: number;\n endpointUrl: string;\n rpcMethodName: string;\n traceId?: string;\n }\n >,\n ): ReturnType<ServicePolicy['onDegraded']> {\n return this.#policy.onDegraded((data) => {\n listener({\n ...data,\n endpointUrl: this.endpointUrl.toString(),\n rpcMethodName: this.#currentRpcMethodName,\n traceId: this.#currentTraceId,\n });\n });\n }\n\n /**\n * Listens for when the policy underlying this RPC service is available.\n *\n * @param listener - The callback to be called when the request is available.\n * @returns What {@link ServicePolicy.onAvailable} returns.\n * @see {@link createServicePolicy}\n */\n onAvailable(\n listener: CockatielEventToEventListenerWithData<\n ServicePolicy['onAvailable'],\n { endpointUrl: string }\n >,\n ): ReturnType<ServicePolicy['onAvailable']> {\n return this.#policy.onAvailable(() => {\n listener({ endpointUrl: this.endpointUrl.toString() });\n });\n }\n\n /**\n * Makes a request to the RPC endpoint.\n *\n * This overload is specifically designed for `eth_getBlockByNumber`, which\n * can return a `result` of `null` despite an expected `Result` being\n * provided.\n *\n * @param jsonRpcRequest - The JSON-RPC request to send to the endpoint.\n * @param fetchOptions - An options bag for {@link fetch} which further\n * specifies the request.\n * @returns The decoded JSON-RPC response from the endpoint.\n * @throws An \"authorized\" JSON-RPC error (code -32006) if the response HTTP status is 401.\n * @throws A \"rate limiting\" JSON-RPC error (code -32005) if the response HTTP status is 429.\n * @throws A \"resource unavailable\" JSON-RPC error (code -32002) if the response HTTP status is 402, 404, or any 5xx.\n * @throws A generic HTTP client JSON-RPC error (code -32050) for any other 4xx HTTP status codes.\n * @throws A \"parse\" JSON-RPC error (code -32700) if the response is not valid JSON.\n */\n async request<Params extends JsonRpcParams, Result extends Json>(\n jsonRpcRequest: JsonRpcRequest<Params> & { method: 'eth_getBlockByNumber' },\n fetchOptions?: FetchOptions,\n ): Promise<JsonRpcResponse<Result> | JsonRpcResponse<null>>;\n\n /**\n * Makes a request to the RPC endpoint.\n *\n * This overload is designed for all RPC methods except for\n * `eth_getBlockByNumber`, which are expected to return a `result` of the\n * expected `Result`.\n *\n * @param jsonRpcRequest - The JSON-RPC request to send to the endpoint.\n * @param fetchOptions - An options bag for {@link fetch} which further\n * specifies the request.\n * @returns The decoded JSON-RPC response from the endpoint.\n * @throws An \"authorized\" JSON-RPC error (code -32006) if the response HTTP status is 401.\n * @throws A \"rate limiting\" JSON-RPC error (code -32005) if the response HTTP status is 429.\n * @throws A \"resource unavailable\" JSON-RPC error (code -32002) if the response HTTP status is 402, 404, or any 5xx.\n * @throws A generic HTTP client JSON-RPC error (code -32050) for any other 4xx HTTP status codes.\n * @throws A \"parse\" JSON-RPC error (code -32700) if the response is not valid JSON.\n */\n async request<Params extends JsonRpcParams, Result extends Json>(\n jsonRpcRequest: JsonRpcRequest<Params>,\n fetchOptions?: FetchOptions,\n ): Promise<JsonRpcResponse<Result>>;\n\n async request<Params extends JsonRpcParams, Result extends Json>(\n // The request object may be frozen and must not be mutated.\n jsonRpcRequest: Readonly<JsonRpcRequest<Params>>,\n fetchOptions: FetchOptions = {},\n ): Promise<JsonRpcResponse<Result | null>> {\n const completeFetchOptions = this.#getCompleteFetchOptions(\n jsonRpcRequest,\n fetchOptions,\n );\n return await this.#executeAndProcessRequest<Result>(\n completeFetchOptions,\n jsonRpcRequest.method,\n );\n }\n\n /**\n * Constructs a default set of options to `fetch`.\n *\n * If a username and password are present in the URL, they are extracted to an\n * Authorization header.\n *\n * @param endpointUrl - The endpoint URL.\n * @param fetchOptions - The options to `fetch`.\n * @param givenBtoa - An implementation of `btoa`.\n * @returns The default fetch options.\n */\n #getDefaultFetchOptions(\n endpointUrl: URL,\n fetchOptions: FetchOptions,\n givenBtoa: (stringToEncode: string) => string,\n ): FetchOptions {\n if (endpointUrl.username && endpointUrl.password) {\n const authString = `${endpointUrl.username}:${endpointUrl.password}`;\n const encodedCredentials = givenBtoa(authString);\n return deepmerge(fetchOptions, {\n headers: { Authorization: `Basic ${encodedCredentials}` },\n });\n }\n\n return fetchOptions;\n }\n\n /**\n * Constructs a final set of options to pass to `fetch`. Note that the method\n * defaults to `post`, and the JSON-RPC request is automatically JSON-encoded.\n *\n * @param jsonRpcRequest - The JSON-RPC request.\n * @param fetchOptions - Custom `fetch` options.\n * @returns The complete set of `fetch` options.\n */\n #getCompleteFetchOptions<Params extends JsonRpcParams>(\n jsonRpcRequest: Readonly<JsonRpcRequest<Params>>,\n fetchOptions: FetchOptions,\n ): FetchOptions {\n const defaultOptions = {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n },\n };\n const mergedOptions = deepmerge(\n defaultOptions,\n deepmerge(this.#fetchOptions, fetchOptions),\n );\n\n const { id, jsonrpc, method, params } = jsonRpcRequest;\n const body = JSON.stringify({\n id,\n jsonrpc,\n method,\n params,\n });\n\n return { ...mergedOptions, body };\n }\n\n /**\n * Makes the request using the Cockatiel policy that this service creates.\n *\n * @param fetchOptions - The options for `fetch`; will be combined with the\n * fetch options passed to the constructor\n * @param rpcMethodName - The JSON-RPC method name of the current request.\n * @returns The decoded JSON-RPC response from the endpoint.\n * @throws An \"authorized\" JSON-RPC error (code -32006) if the response HTTP status is 401.\n * @throws A \"rate limiting\" JSON-RPC error (code -32005) if the response HTTP status is 429.\n * @throws A \"resource unavailable\" JSON-RPC error (code -32002) if the response HTTP status is 402, 404, or any 5xx.\n * @throws A generic HTTP client JSON-RPC error (code -32050) for any other 4xx HTTP status codes.\n * @throws A \"parse\" JSON-RPC error (code -32700) if the response is not valid JSON.\n */\n async #executeAndProcessRequest<Result extends Json>(\n fetchOptions: FetchOptions,\n rpcMethodName: string,\n ): Promise<JsonRpcResponse<Result> | JsonRpcResponse<null>> {\n let response: Response | undefined;\n try {\n log(\n `[${this.endpointUrl}] Circuit state`,\n this.#policy.getCircuitState(),\n );\n const jsonDecodedResponse = await this.#policy.execute(\n async (context) => {\n // Reset response so that if this attempt throws before\n // assigning a new response, the finally block does not read\n // a stale response from a previous attempt.\n response = undefined;\n try {\n log(\n 'REQUEST INITIATED:',\n this.endpointUrl.toString(),\n '::',\n fetchOptions,\n // @ts-expect-error This property _is_ here, the type of\n // ServicePolicy is just wrong.\n `(attempt ${context.attempt + 1})`,\n );\n response = await this.#fetch(this.endpointUrl, fetchOptions);\n if (!response.ok) {\n throw new HttpError(response.status);\n }\n log(\n 'REQUEST SUCCESSFUL:',\n this.endpointUrl.toString(),\n response.status,\n );\n return await response.json();\n } finally {\n // Track the RPC method and trace ID for the request that has just\n // taken place. We pass these properties to `onDegraded` event\n // listeners.\n //\n // We set these properties after the request completes and not\n // before the request starts to account for race conditions. That\n // is, if there are two requests that are being performed\n // concurrently, and the second request fails fast but the first\n // request succeeds slowly, when `onDegraded` is called we want it\n // to include the first request as the RPC method, not the second.\n //\n // Also, we set these properties within a `finally` block inside of\n // the function passed to `policy.execute` to ensure that they are\n // set before `onDegraded` gets called, no matter the outcome of\n // the request.\n this.#currentRpcMethodName = rpcMethodName;\n this.#currentTraceId =\n response?.headers.get('X-Trace-Id') ?? undefined;\n }\n },\n );\n this.lastError = undefined;\n return jsonDecodedResponse;\n } catch (error) {\n log('REQUEST ERROR:', this.endpointUrl.toString(), error);\n\n this.lastError =\n error instanceof Error ? error : new Error(getErrorMessage(error));\n\n if (error instanceof HttpError) {\n const status = error.httpStatus;\n if (status === 401) {\n throw new JsonRpcError(\n CUSTOM_RPC_ERRORS.unauthorized,\n 'Unauthorized.',\n {\n httpStatus: status,\n },\n );\n }\n if (status === 429) {\n throw rpcErrors.limitExceeded({\n message: 'Request is being rate limited.',\n data: {\n httpStatus: status,\n },\n });\n }\n if (status >= 500 || status === 402 || status === 404) {\n throw rpcErrors.resourceUnavailable({\n message: 'RPC endpoint not found or unavailable.',\n data: {\n httpStatus: status,\n },\n });\n }\n\n // Handle all other 4xx errors as generic HTTP client errors\n throw new JsonRpcError(\n CUSTOM_RPC_ERRORS.httpClientError,\n 'RPC endpoint returned HTTP client error.',\n {\n httpStatus: status,\n },\n );\n } else if (isJsonParseError(error)) {\n throw rpcErrors.parse({\n message: 'RPC endpoint did not return JSON.',\n });\n } else if (error instanceof BrokenCircuitError) {\n this.#logger?.warn(error);\n const remainingCircuitOpenDuration =\n this.#policy.getRemainingCircuitOpenDuration();\n const formattedRemainingCircuitOpenDuration = Intl.NumberFormat(\n undefined,\n { maximumFractionDigits: 2 },\n ).format(\n (remainingCircuitOpenDuration ?? this.#policy.circuitBreakDuration) /\n Duration.Minute,\n );\n throw rpcErrors.resourceUnavailable({\n message: `RPC endpoint returned too many errors, retrying in ${formattedRemainingCircuitOpenDuration} minutes. Consider using a different RPC endpoint.`,\n });\n }\n throw error;\n }\n }\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CockatielEvent, CockatielEventEmitter } from
|
|
1
|
+
import type { CockatielEvent, CockatielEventEmitter } from '@metamask/controller-utils';
|
|
2
2
|
/**
|
|
3
3
|
* Equivalent to the built-in `FetchOptions` type, but renamed for clarity.
|
|
4
4
|
*/
|
|
@@ -29,4 +29,4 @@ export type CockatielEventToEventListenerWithData<Event, Data> = (data: ExtendCo
|
|
|
29
29
|
* Converts a Cockatiel event listener type to an event emitter type.
|
|
30
30
|
*/
|
|
31
31
|
export type CockatielEventToEventEmitterWithData<Event, Data> = CockatielEventEmitter<ExtendCockatielEventData<ExtractCockatielEventData<Event>, Data>>;
|
|
32
|
-
//# sourceMappingURL=shared.d.
|
|
32
|
+
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/rpc-service/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EACtB,MAAM,4BAA4B,CAAC;AAEpC;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC;AAEvC;;GAEG;AACH,MAAM,MAAM,4BAA4B,CAAC,KAAK,IAC5C,KAAK,SAAS,cAAc,CAAC,MAAM,SAAS,CAAC,GACzC,qBAAqB,CAAC,SAAS,CAAC,GAChC,KAAK,CAAC;AAEZ;;GAEG;AACH,MAAM,MAAM,yBAAyB,CAAC,6BAA6B,IACjE,6BAA6B,SAAS,cAAc,CAAC,MAAM,IAAI,CAAC,GAC5D,IAAI,GACJ,6BAA6B,SAAS,CAAC,IAAI,EAAE,MAAM,IAAI,KAAK,IAAI,GAC9D,IAAI,GACJ,KAAK,CAAC;AAEd;;;GAGG;AACH,MAAM,MAAM,wBAAwB,CAAC,YAAY,EAAE,cAAc,IAC/D,YAAY,SAAS,IAAI,GAAG,cAAc,GAAG,YAAY,GAAG,cAAc,CAAC;AAE7E;;GAEG;AACH,MAAM,MAAM,yBAAyB,CACnC,YAAY,EACZ,IAAI,SAAS,WAAW,IACtB,YAAY,SAAS,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;AAEhE;;;GAGG;AACH,MAAM,MAAM,qCAAqC,CAAC,KAAK,EAAE,IAAI,IAAI,CAC/D,IAAI,EAAE,wBAAwB,CAAC,yBAAyB,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,KACnE,IAAI,CAAC;AAEV;;GAEG;AACH,MAAM,MAAM,oCAAoC,CAAC,KAAK,EAAE,IAAI,IAC1D,qBAAqB,CACnB,wBAAwB,CAAC,yBAAyB,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CACjE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/rpc-service/shared.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n CockatielEvent,\n CockatielEventEmitter,\n} from '@metamask/controller-utils';\n\n/**\n * Equivalent to the built-in `FetchOptions` type, but renamed for clarity.\n */\nexport type FetchOptions = RequestInit;\n\n/**\n * Converts a Cockatiel event type to an event emitter type.\n */\nexport type CockatielEventToEventEmitter<Event> =\n Event extends CockatielEvent<infer EventData>\n ? CockatielEventEmitter<EventData>\n : never;\n\n/**\n * Obtains the event data type from a Cockatiel event or event listener type.\n */\nexport type ExtractCockatielEventData<CockatielEventOrEventListener> =\n CockatielEventOrEventListener extends CockatielEvent<infer Data>\n ? Data\n : CockatielEventOrEventListener extends (data: infer Data) => void\n ? Data\n : never;\n\n/**\n * Extends the data that a Cockatiel event listener is called with additional\n * data.\n */\nexport type ExtendCockatielEventData<OriginalData, AdditionalData> =\n OriginalData extends void ? AdditionalData : OriginalData & AdditionalData;\n\n/**\n * Removes keys from the data that a Cockatiel event listner is called with.\n */\nexport type ExcludeCockatielEventData<\n OriginalData,\n Keys extends PropertyKey,\n> = OriginalData extends void ? void : Omit<OriginalData, Keys>;\n\n/**\n * Converts a Cockatiel event type to an event listener type, but adding the\n * requested data.\n */\nexport type CockatielEventToEventListenerWithData<Event, Data> = (\n data: ExtendCockatielEventData<ExtractCockatielEventData<Event>, Data>,\n) => void;\n\n/**\n * Converts a Cockatiel event listener type to an event emitter type.\n */\nexport type CockatielEventToEventEmitterWithData<Event, Data> =\n CockatielEventEmitter<\n ExtendCockatielEventData<ExtractCockatielEventData<Event>, Data>\n >;\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AnalyticsTrackingEvent } from
|
|
2
|
-
import type { NetworkControllerMessenger, NetworkControllerRpcEndpointDegradedEvent, NetworkControllerRpcEndpointUnavailableEvent } from
|
|
1
|
+
import type { AnalyticsTrackingEvent } from '@metamask/analytics-controller';
|
|
2
|
+
import type { NetworkControllerMessenger, NetworkControllerRpcEndpointDegradedEvent, NetworkControllerRpcEndpointUnavailableEvent } from './NetworkController.js';
|
|
3
3
|
type RpcEndpointUnavailablePayload = NetworkControllerRpcEndpointUnavailableEvent['payload'][0];
|
|
4
4
|
type RpcEndpointDegradedPayload = NetworkControllerRpcEndpointDegradedEvent['payload'][0];
|
|
5
5
|
/**
|
|
@@ -82,4 +82,4 @@ export declare function trackRpcServiceUnavailable(messenger: NetworkControllerM
|
|
|
82
82
|
*/
|
|
83
83
|
export declare function trackRpcServiceDegraded(messenger: NetworkControllerMessenger, analyticsOptions: ResolvedNetworkControllerAnalyticsOptions, payload: RpcEndpointDegradedPayload): void;
|
|
84
84
|
export {};
|
|
85
|
-
//# sourceMappingURL=rpc-service-analytics.d.
|
|
85
|
+
//# sourceMappingURL=rpc-service-analytics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-service-analytics.d.ts","sourceRoot":"","sources":["../src/rpc-service-analytics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAW7E,OAAO,KAAK,EACV,0BAA0B,EAC1B,yCAAyC,EACzC,4CAA4C,EAC7C,MAAM,wBAAwB,CAAC;AAGhC,KAAK,6BAA6B,GAChC,4CAA4C,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;AAE7D,KAAK,0BAA0B,GAC7B,yCAAyC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;AAE1D;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAC3B,yBAAyB,GACzB,sBAAsB,CAAC;AAE3B;;;;;;;;GAQG;AACH,MAAM,MAAM,iCAAiC,GAAG;IAC9C;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC;IAC1D;;;;;OAKG;IACH,0BAA0B,CAAC,EAAE,MAAM,CAAC;CACrC,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,yCAAyC,GACnD,QAAQ,CAAC,iCAAiC,CAAC,CAAC;AAE9C;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,OAAO,GAChB,MAAM,CAUR;AA+DD;;;;;;;GAOG;AACH,wBAAgB,gDAAgD,CAC9D,OAAO,EAAE,6BAA6B,EACtC,sBAAsB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,GACvD,sBAAsB,CAUxB;AAED;;;;;;;GAOG;AACH,wBAAgB,6CAA6C,CAC3D,OAAO,EAAE,0BAA0B,EACnC,sBAAsB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,GACvD,sBAAsB,CAiBxB;AAqDD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,0BAA0B,EACrC,gBAAgB,EAAE,yCAAyC,EAC3D,OAAO,EAAE,6BAA6B,GACrC,IAAI,CAWN;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,0BAA0B,EACrC,gBAAgB,EAAE,yCAAyC,EAC3D,OAAO,EAAE,0BAA0B,GAClC,IAAI,CAWN"}
|