@meshconnect/uwc-wallet-connect-connector 0.14.0 → 0.14.2

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.
@@ -5,6 +5,7 @@ import type { WalletConnectConfig } from '@meshconnect/uwc-types';
5
5
  * guard, not a permanent ledger — bounding it keeps memory flat on a long-lived
6
6
  * session even if messages are high-cardinality (timestamps / urls).
7
7
  */
8
+ export declare const MAX_RELAY_ERROR_DEDUP_KEYS = 100;
8
9
  export declare const MAX_REPORTED_RELAY_ERRORS = 100;
9
10
  /**
10
11
  * Cap on relay transport-lifecycle emissions PER EVENT TYPE (disconnect and
@@ -27,10 +28,12 @@ export declare const MAX_REPORTED_RELAY_LIFECYCLE = 50;
27
28
  export interface WalletConnectTelemetryObserver {
28
29
  /**
29
30
  * Relay-socket failure: the relayer re-emits an abnormal WebSocket close under
30
- * `RELAYER_EVENTS.error` (e.g. code 3000 "JWT Token is not yet valid" on clock
31
- * skew), detached from the connect() promise. Deduped per message.
31
+ * `RELAYER_EVENTS.error` (e.g. code 3000 JWT validation error on clock skew),
32
+ * detached from the connect() promise. Deduped by a stable error-family key.
32
33
  */
33
34
  onRelayError?: (error: unknown) => void;
35
+ /** Relay-error telemetry reached its per-client emission budget. */
36
+ onRelayErrorCapped?: () => void;
34
37
  /**
35
38
  * `SignClient.init` failure (storage/crypto, NOT relay). Telemetry-only — the
36
39
  * init rejection is always rethrown so `getClient()` callers see it unchanged.
@@ -67,6 +70,10 @@ export declare class WalletConnectClient {
67
70
  private initPromise;
68
71
  /** Dedupe relay-error reports so a reconnect storm surfaces once, not per tick. */
69
72
  private readonly relayErrorDedup;
73
+ /** Hard limit for relay-error telemetry when messages are high-cardinality. */
74
+ private relayErrorCount;
75
+ /** Ensures the telemetry-cap signal is emitted once per client. */
76
+ private relayErrorCapped;
70
77
  /** Independent bounded counters so drop/recover telemetry can never starve each other. */
71
78
  private relayDisconnectCount;
72
79
  private relayReconnectCount;
@@ -1 +1 @@
1
- {"version":3,"file":"walletconnect-client.d.ts","sourceRoot":"","sources":["../../src/services/walletconnect-client.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,4BAA4B,CAAA;AAGnD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAEjE;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,MAAM,CAAA;AAE5C;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,KAAK,CAAA;AAE9C;;;;;;;GAOG;AACH,MAAM,WAAW,8BAA8B;IAC7C;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IACvC;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IACtC;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAA;IAC9B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAA;CAC9B;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,sBAAsB,CAAC,EACnB,8BAA8B,GAC9B,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,EAC9B,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GACrC,8BAA8B,CAsBhC;AAED;;GAEG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,WAAW,CAAmC;IACtD,mFAAmF;IACnF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAE/B;IACD,0FAA0F;IAC1F,OAAO,CAAC,oBAAoB,CAAI;IAChC,OAAO,CAAC,mBAAmB,CAAI;IAC/B,sFAAsF;IACtF,OAAO,CAAC,oBAAoB,CAAQ;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAgC;IAElE;;;;OAIG;gBAED,MAAM,EAAE,mBAAmB,EAC3B,iBAAiB,CAAC,EAAE,8BAA8B;IAEpD;;;;OAIG;gBAED,MAAM,EAAE,mBAAmB,EAC3B,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,EACvC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI;IAgBxC;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,UAAU,CAAC;IAmBtC;;OAEG;YACW,UAAU;IAiDxB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,wBAAwB;IAkBhC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,6BAA6B;IA6CrC;;;;OAIG;IACH,iBAAiB,IAAI,UAAU,GAAG,IAAI;IAItC;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBpC;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAW/C"}
1
+ {"version":3,"file":"walletconnect-client.d.ts","sourceRoot":"","sources":["../../src/services/walletconnect-client.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,4BAA4B,CAAA;AAGnD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAEjE;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,MAAM,CAAA;AAC7C,eAAO,MAAM,yBAAyB,MAAM,CAAA;AAa5C;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,KAAK,CAAA;AAE9C;;;;;;;GAOG;AACH,MAAM,WAAW,8BAA8B;IAC7C;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IACvC,oEAAoE;IACpE,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAA;IAC/B;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IACtC;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAA;IAC9B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAA;CAC9B;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,sBAAsB,CAAC,EACnB,8BAA8B,GAC9B,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,EAC9B,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GACrC,8BAA8B,CAsBhC;AAED;;GAEG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,WAAW,CAAmC;IACtD,mFAAmF;IACnF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAE/B;IACD,+EAA+E;IAC/E,OAAO,CAAC,eAAe,CAAI;IAC3B,mEAAmE;IACnE,OAAO,CAAC,gBAAgB,CAAQ;IAChC,0FAA0F;IAC1F,OAAO,CAAC,oBAAoB,CAAI;IAChC,OAAO,CAAC,mBAAmB,CAAI;IAC/B,sFAAsF;IACtF,OAAO,CAAC,oBAAoB,CAAQ;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAgC;IAElE;;;;OAIG;gBAED,MAAM,EAAE,mBAAmB,EAC3B,iBAAiB,CAAC,EAAE,8BAA8B;IAEpD;;;;OAIG;gBAED,MAAM,EAAE,mBAAmB,EAC3B,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,EACvC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI;IAgBxC;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,UAAU,CAAC;IAmBtC;;OAEG;YACW,UAAU;IAiDxB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,wBAAwB;IAoChC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,6BAA6B;IA6CrC;;;;OAIG;IACH,iBAAiB,IAAI,UAAU,GAAG,IAAI;IAItC;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBpC;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAW/C"}
@@ -6,7 +6,15 @@ import { createBoundedDedup, safeErrorMessage } from '@meshconnect/uwc-types';
6
6
  * guard, not a permanent ledger — bounding it keeps memory flat on a long-lived
7
7
  * session even if messages are high-cardinality (timestamps / urls).
8
8
  */
9
+ export const MAX_RELAY_ERROR_DEDUP_KEYS = 100;
9
10
  export const MAX_REPORTED_RELAY_ERRORS = 100;
11
+ const JWT_VALIDATION_RELAY_ERROR = /^WebSocket connection closed abnormally with code: 3000 \(JWT validation error: .+\)$/;
12
+ const JWT_VALIDATION_RELAY_ERROR_KEY = 'walletconnect:jwt-validation:code-3000';
13
+ function relayErrorDedupKey(message) {
14
+ return JWT_VALIDATION_RELAY_ERROR.test(message)
15
+ ? JWT_VALIDATION_RELAY_ERROR_KEY
16
+ : message;
17
+ }
10
18
  /**
11
19
  * Cap on relay transport-lifecycle emissions PER EVENT TYPE (disconnect and
12
20
  * reconnect budgeted independently) per client. A flapping socket IS the signal
@@ -54,7 +62,11 @@ export class WalletConnectClient {
54
62
  client = null;
55
63
  initPromise = null;
56
64
  /** Dedupe relay-error reports so a reconnect storm surfaces once, not per tick. */
57
- relayErrorDedup = createBoundedDedup(MAX_REPORTED_RELAY_ERRORS);
65
+ relayErrorDedup = createBoundedDedup(MAX_RELAY_ERROR_DEDUP_KEYS);
66
+ /** Hard limit for relay-error telemetry when messages are high-cardinality. */
67
+ relayErrorCount = 0;
68
+ /** Ensures the telemetry-cap signal is emitted once per client. */
69
+ relayErrorCapped = false;
58
70
  /** Independent bounded counters so drop/recover telemetry can never starve each other. */
59
71
  relayDisconnectCount = 0;
60
72
  relayReconnectCount = 0;
@@ -144,15 +156,33 @@ export class WalletConnectClient {
144
156
  * drops the socket without emitting an `error`). Telemetry-only; never changes behaviour.
145
157
  */
146
158
  attachRelayErrorListener(client) {
147
- if (!this.telemetryObserver.onRelayError)
159
+ if (!this.telemetryObserver.onRelayError &&
160
+ !this.telemetryObserver.onRelayErrorCapped) {
148
161
  return;
162
+ }
149
163
  client.core?.relayer?.on(RELAYER_EVENTS.error, (error) => {
164
+ if (this.relayErrorCount >= MAX_REPORTED_RELAY_ERRORS) {
165
+ if (this.relayErrorCapped)
166
+ return;
167
+ this.relayErrorCapped = true;
168
+ try {
169
+ this.telemetryObserver.onRelayErrorCapped?.();
170
+ }
171
+ catch {
172
+ // Telemetry must never break the relay.
173
+ }
174
+ return;
175
+ }
150
176
  // safeErrorMessage: this runs INSIDE the SDK's relayer dispatch, before
151
177
  // the sink guard below — a throwing toString() here would break the
152
178
  // emitter just like a throwing sink; the dedup key stays bounded too.
153
179
  const message = safeErrorMessage(error);
154
- if (!this.relayErrorDedup.firstSeen(message))
180
+ const dedupKey = relayErrorDedupKey(message);
181
+ if (!this.relayErrorDedup.firstSeen(dedupKey))
182
+ return;
183
+ if (this.relayErrorCount >= MAX_REPORTED_RELAY_ERRORS)
155
184
  return;
185
+ this.relayErrorCount++;
156
186
  try {
157
187
  this.telemetryObserver.onRelayError?.(error);
158
188
  }
@@ -1 +1 @@
1
- {"version":3,"file":"walletconnect-client.js","sourceRoot":"","sources":["../../src/services/walletconnect-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"walletconnect-client.js","sourceRoot":"","sources":["../../src/services/walletconnect-client.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meshconnect/uwc-wallet-connect-connector",
3
- "version": "0.14.0",
3
+ "version": "0.14.2",
4
4
  "description": "WalletConnect connector for Universal Wallet Connector",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -25,7 +25,7 @@
25
25
  "bs58": "6.0.0",
26
26
  "buffer": "^6.0.3",
27
27
  "viem": "^2.54.6",
28
- "@meshconnect/uwc-types": "0.26.0"
28
+ "@meshconnect/uwc-types": "0.26.1"
29
29
  },
30
30
  "devDependencies": {
31
31
  "typescript": "^5.9.3"
@@ -71,6 +71,36 @@ describe('WalletConnectClient relay-error surfacing', () => {
71
71
  expect(errors).toHaveLength(1)
72
72
  })
73
73
 
74
+ it('dedupes dynamic code-3000 JWT validation errors', async () => {
75
+ const errors: unknown[] = []
76
+ const client = new WalletConnectClient(CONFIG, {
77
+ onRelayError: e => errors.push(e)
78
+ })
79
+ await client.getClient()
80
+
81
+ const fire = (issuedAt: number, relayNow: number) =>
82
+ relayerHandlers[RELAYER_EVENTS.error]?.(
83
+ new Error(
84
+ `WebSocket connection closed abnormally with code: 3000 (JWT validation error: JWT Token is not yet valid: basic.iat: ${issuedAt}, now + time_leeway: ${relayNow}, time_leeway: 120)`
85
+ )
86
+ )
87
+
88
+ fire(1784405170, 1784405120)
89
+ relayerHandlers[RELAYER_EVENTS.error]?.(
90
+ new Error(
91
+ 'WebSocket connection closed abnormally with code: 3000 (JWT validation error: JWT Token is expired: Some(1784227017))'
92
+ )
93
+ )
94
+ relayerHandlers[RELAYER_EVENTS.error]?.(
95
+ new Error(
96
+ 'WebSocket connection closed abnormally with code: 3000 (JWT validation error: JWT Token is not yet valid: basic.iat: 1784405173, now + time_leeway: 1784405123, time_leeway: 90)'
97
+ )
98
+ )
99
+
100
+ expect(errors).toHaveLength(1)
101
+ expect((errors[0] as Error).message).toContain('basic.iat: 1784405170')
102
+ })
103
+
74
104
  it('swallows a throwing onRelayError sink — must not break the relayer emitter', async () => {
75
105
  const client = new WalletConnectClient(CONFIG, {
76
106
  onRelayError: () => {
@@ -110,7 +140,7 @@ describe('WalletConnectClient relay-error surfacing', () => {
110
140
  expect(relayerHandlers[RELAYER_EVENTS.error]).toBeUndefined()
111
141
  })
112
142
 
113
- it('bounds the dedup set — an evicted message can report again past the cap', async () => {
143
+ it('does not emit after the relay-error telemetry cap is reached', async () => {
114
144
  const errors: unknown[] = []
115
145
  const client = new WalletConnectClient(CONFIG, {
116
146
  onRelayError: e => errors.push(e)
@@ -127,13 +157,49 @@ describe('WalletConnectClient relay-error surfacing', () => {
127
157
  fire('err-0')
128
158
  expect(errors).toHaveLength(MAX_REPORTED_RELAY_ERRORS)
129
159
 
130
- // One more distinct message trips the cap → the set is evicted → it reports.
160
+ // Distinct messages can cycle the bounded dedup cache, but must not bypass
161
+ // the hard telemetry cap.
131
162
  fire('over-cap')
132
- expect(errors).toHaveLength(MAX_REPORTED_RELAY_ERRORS + 1)
163
+ expect(errors).toHaveLength(MAX_REPORTED_RELAY_ERRORS)
133
164
 
134
- // err-0 was evicted, so it reports again — proving the set stays bounded.
165
+ // A previously evicted key must remain capped too.
135
166
  fire('err-0')
136
- expect(errors).toHaveLength(MAX_REPORTED_RELAY_ERRORS + 2)
167
+ expect(errors).toHaveLength(MAX_REPORTED_RELAY_ERRORS)
168
+ })
169
+
170
+ it('caps relay-error telemetry when every message is distinct', async () => {
171
+ const errors: unknown[] = []
172
+ let capped = 0
173
+ const client = new WalletConnectClient(CONFIG, {
174
+ onRelayError: e => errors.push(e),
175
+ onRelayErrorCapped: () => {
176
+ capped++
177
+ }
178
+ })
179
+ await client.getClient()
180
+
181
+ for (let i = 0; i <= MAX_REPORTED_RELAY_ERRORS; i++) {
182
+ relayerHandlers[RELAYER_EVENTS.error]?.(new Error(`err-${i}`))
183
+ }
184
+
185
+ expect(errors).toHaveLength(MAX_REPORTED_RELAY_ERRORS)
186
+ expect(capped).toBe(1)
187
+ })
188
+
189
+ it('emits the cap signal when it is the only relay-error sink', async () => {
190
+ let capped = 0
191
+ const client = new WalletConnectClient(CONFIG, {
192
+ onRelayErrorCapped: () => {
193
+ capped++
194
+ }
195
+ })
196
+ await client.getClient()
197
+
198
+ for (let i = 0; i <= MAX_REPORTED_RELAY_ERRORS; i++) {
199
+ relayerHandlers[RELAYER_EVENTS.error]?.(new Error(`err-${i}`))
200
+ }
201
+
202
+ expect(capped).toBe(1)
137
203
  })
138
204
  })
139
205
 
@@ -8,8 +8,20 @@ import type { WalletConnectConfig } from '@meshconnect/uwc-types'
8
8
  * guard, not a permanent ledger — bounding it keeps memory flat on a long-lived
9
9
  * session even if messages are high-cardinality (timestamps / urls).
10
10
  */
11
+ export const MAX_RELAY_ERROR_DEDUP_KEYS = 100
11
12
  export const MAX_REPORTED_RELAY_ERRORS = 100
12
13
 
14
+ const JWT_VALIDATION_RELAY_ERROR =
15
+ /^WebSocket connection closed abnormally with code: 3000 \(JWT validation error: .+\)$/
16
+
17
+ const JWT_VALIDATION_RELAY_ERROR_KEY = 'walletconnect:jwt-validation:code-3000'
18
+
19
+ function relayErrorDedupKey(message: string): string {
20
+ return JWT_VALIDATION_RELAY_ERROR.test(message)
21
+ ? JWT_VALIDATION_RELAY_ERROR_KEY
22
+ : message
23
+ }
24
+
13
25
  /**
14
26
  * Cap on relay transport-lifecycle emissions PER EVENT TYPE (disconnect and
15
27
  * reconnect budgeted independently) per client. A flapping socket IS the signal
@@ -32,10 +44,12 @@ export const MAX_REPORTED_RELAY_LIFECYCLE = 50
32
44
  export interface WalletConnectTelemetryObserver {
33
45
  /**
34
46
  * Relay-socket failure: the relayer re-emits an abnormal WebSocket close under
35
- * `RELAYER_EVENTS.error` (e.g. code 3000 "JWT Token is not yet valid" on clock
36
- * skew), detached from the connect() promise. Deduped per message.
47
+ * `RELAYER_EVENTS.error` (e.g. code 3000 JWT validation error on clock skew),
48
+ * detached from the connect() promise. Deduped by a stable error-family key.
37
49
  */
38
50
  onRelayError?: (error: unknown) => void
51
+ /** Relay-error telemetry reached its per-client emission budget. */
52
+ onRelayErrorCapped?: () => void
39
53
  /**
40
54
  * `SignClient.init` failure (storage/crypto, NOT relay). Telemetry-only — the
41
55
  * init rejection is always rethrown so `getClient()` callers see it unchanged.
@@ -101,8 +115,12 @@ export class WalletConnectClient {
101
115
  private initPromise: Promise<SignClient> | null = null
102
116
  /** Dedupe relay-error reports so a reconnect storm surfaces once, not per tick. */
103
117
  private readonly relayErrorDedup = createBoundedDedup(
104
- MAX_REPORTED_RELAY_ERRORS
118
+ MAX_RELAY_ERROR_DEDUP_KEYS
105
119
  )
120
+ /** Hard limit for relay-error telemetry when messages are high-cardinality. */
121
+ private relayErrorCount = 0
122
+ /** Ensures the telemetry-cap signal is emitted once per client. */
123
+ private relayErrorCapped = false
106
124
  /** Independent bounded counters so drop/recover telemetry can never starve each other. */
107
125
  private relayDisconnectCount = 0
108
126
  private relayReconnectCount = 0
@@ -230,13 +248,31 @@ export class WalletConnectClient {
230
248
  * drops the socket without emitting an `error`). Telemetry-only; never changes behaviour.
231
249
  */
232
250
  private attachRelayErrorListener(client: SignClient): void {
233
- if (!this.telemetryObserver.onRelayError) return
251
+ if (
252
+ !this.telemetryObserver.onRelayError &&
253
+ !this.telemetryObserver.onRelayErrorCapped
254
+ ) {
255
+ return
256
+ }
234
257
  client.core?.relayer?.on(RELAYER_EVENTS.error, (error: unknown) => {
258
+ if (this.relayErrorCount >= MAX_REPORTED_RELAY_ERRORS) {
259
+ if (this.relayErrorCapped) return
260
+ this.relayErrorCapped = true
261
+ try {
262
+ this.telemetryObserver.onRelayErrorCapped?.()
263
+ } catch {
264
+ // Telemetry must never break the relay.
265
+ }
266
+ return
267
+ }
235
268
  // safeErrorMessage: this runs INSIDE the SDK's relayer dispatch, before
236
269
  // the sink guard below — a throwing toString() here would break the
237
270
  // emitter just like a throwing sink; the dedup key stays bounded too.
238
271
  const message = safeErrorMessage(error)
239
- if (!this.relayErrorDedup.firstSeen(message)) return
272
+ const dedupKey = relayErrorDedupKey(message)
273
+ if (!this.relayErrorDedup.firstSeen(dedupKey)) return
274
+ if (this.relayErrorCount >= MAX_REPORTED_RELAY_ERRORS) return
275
+ this.relayErrorCount++
240
276
  try {
241
277
  this.telemetryObserver.onRelayError?.(error)
242
278
  } catch {