@qrlwallet/connect 3.1.1 → 3.3.0

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/dist/index.mjs CHANGED
@@ -554,6 +554,7 @@ var KeyExchange = class extends EventEmitter {
554
554
  import { io } from "socket.io-client";
555
555
  import EventEmitter2 from "eventemitter3";
556
556
  var PENDING_JOIN_TIMEOUT_MS = 2e4;
557
+ var SEND_FLUSH_TIMEOUT_MS = 600;
557
558
  var SocketClient = class extends EventEmitter2 {
558
559
  constructor(relayUrl, clientType) {
559
560
  super();
@@ -744,6 +745,48 @@ var SocketClient = class extends EventEmitter2 {
744
745
  );
745
746
  });
746
747
  }
748
+ /**
749
+ * Emit an event and resolve once the relay acks it, or after a bounded
750
+ * flush window. Lets a caller await transmission before tearing the
751
+ * socket down.
752
+ */
753
+ flushEmit(event, payload) {
754
+ return new Promise((resolve) => {
755
+ if (!this.socket?.connected) {
756
+ resolve();
757
+ return;
758
+ }
759
+ let settled = false;
760
+ const done = () => {
761
+ if (settled) return;
762
+ settled = true;
763
+ resolve();
764
+ };
765
+ const timer = setTimeout(done, SEND_FLUSH_TIMEOUT_MS);
766
+ this.socket.emit(event, payload, () => {
767
+ clearTimeout(timer);
768
+ done();
769
+ });
770
+ });
771
+ }
772
+ /**
773
+ * Explicitly terminate the channel on the relay (durable tombstone), as
774
+ * opposed to the transient leaveChannel(). Used when the session is dead
775
+ * in a way an encrypted TERMINATE cannot communicate (AEAD desync: the
776
+ * peer could not open it). Resolves once the close is flushed or the
777
+ * bounded window elapses, so the caller can safely disconnect afterwards.
778
+ */
779
+ closeChannel() {
780
+ const channelId = this.channelId;
781
+ this.channelId = null;
782
+ if (this.pendingJoin) {
783
+ clearTimeout(this.pendingJoin.watchdog);
784
+ this.pendingJoin.reject(new Error("Channel closed before join completed"));
785
+ this.pendingJoin = null;
786
+ }
787
+ if (!this.socket?.connected || !channelId) return Promise.resolve();
788
+ return this.flushEmit("close_channel", { channelId });
789
+ }
747
790
  /**
748
791
  * Leave the current channel.
749
792
  */
@@ -1044,11 +1087,13 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
1044
1087
  this.connectedAccounts = [];
1045
1088
  this.unresponsiveTimer = null;
1046
1089
  this.reconnectProbeTimer = null;
1090
+ this.restoreInFlight = null;
1047
1091
  this.failedReconnects = 0;
1048
1092
  this.walletPresent = false;
1049
1093
  this.pendingRestore = null;
1050
1094
  this.messageQueue = Promise.resolve();
1051
1095
  this.outboundQueue = Promise.resolve();
1096
+ this.consecutiveDecryptFailures = 0;
1052
1097
  this.dappMetadata = options.dappMetadata;
1053
1098
  this.relayUrl = options.relayUrl ?? DEFAULT_RELAY_URL;
1054
1099
  this.chainId = options.chainId ?? "0x0";
@@ -1167,6 +1212,7 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
1167
1212
  this.clearReconnectProbe();
1168
1213
  this.pendingRestore = null;
1169
1214
  this.walletPresent = false;
1215
+ this.consecutiveDecryptFailures = 0;
1170
1216
  if (this.keyExchange) {
1171
1217
  this.keyExchange.reset();
1172
1218
  } else {
@@ -1205,9 +1251,18 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
1205
1251
  * Returns false if there is nothing to restore.
1206
1252
  */
1207
1253
  async reconnect() {
1254
+ if (this.restoreInFlight) return this.restoreInFlight;
1255
+ const task = this.reconnectNow().finally(() => {
1256
+ this.restoreInFlight = null;
1257
+ });
1258
+ this.restoreInFlight = task;
1259
+ return task;
1260
+ }
1261
+ async reconnectNow() {
1208
1262
  if (!this.pendingRestore) return false;
1209
1263
  this.setStatus("reconnecting" /* RECONNECTING */);
1210
1264
  this.walletPresent = false;
1265
+ this.consecutiveDecryptFailures = 0;
1211
1266
  try {
1212
1267
  const session = await KeyExchange.sessionFromPersisted(this.pendingRestore.keyExchange);
1213
1268
  this.keyExchange = new KeyExchange(true, session);
@@ -1283,15 +1338,53 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
1283
1338
  }
1284
1339
  }
1285
1340
  }
1341
+ /**
1342
+ * Make sure the socket is open and joined to the paired session's channel
1343
+ * so an outbound request can be routed - or relay-buffered if the wallet is
1344
+ * currently absent (its socket dies within seconds of the app
1345
+ * backgrounding; the relay holds channel traffic for it). Revives the
1346
+ * socket after a probe-timeout teardown and hydrates a cold stored
1347
+ * session. Returns false when there is no session to revive (never paired,
1348
+ * explicit disconnect, or the channel was tombstoned).
1349
+ */
1350
+ async ensureChannelJoined() {
1351
+ if (this.keyExchange?.areKeysExchanged()) {
1352
+ if (this.socketClient.isConnected() && this.socketClient.getChannelId()) {
1353
+ return true;
1354
+ }
1355
+ this.setStatus("reconnecting" /* RECONNECTING */);
1356
+ this.walletPresent = false;
1357
+ return this.joinAndSettle();
1358
+ }
1359
+ if (this.pendingRestore) {
1360
+ return this.reconnect();
1361
+ }
1362
+ return false;
1363
+ }
1364
+ /**
1365
+ * True while a pairing exists (handshake complete, session keys in
1366
+ * memory), even when the wallet's socket is momentarily out of the
1367
+ * channel. Survives the reconnect-probe teardown; false after an explicit
1368
+ * disconnect or a relay tombstone.
1369
+ */
1370
+ isPaired() {
1371
+ return this.keyExchange?.areKeysExchanged() ?? false;
1372
+ }
1373
+ /** True while the relay roster last showed the wallet in the channel. */
1374
+ isWalletPresent() {
1375
+ return this.walletPresent;
1376
+ }
1286
1377
  /**
1287
1378
  * A session was terminated for good (wallet sent a relay 'close', or the
1288
1379
  * join ack reported a tombstone). Clear local state and surface
1289
1380
  * DISCONNECTED so the consumer drops to a fresh-pairing UI.
1290
1381
  */
1291
1382
  handleSessionTerminated() {
1383
+ this.emit("session_terminated");
1292
1384
  this.clearReconnectProbe();
1293
1385
  this.clearUnresponsiveTimer();
1294
1386
  this.walletPresent = false;
1387
+ this.consecutiveDecryptFailures = 0;
1295
1388
  this.pendingRestore = null;
1296
1389
  this.keyExchange = null;
1297
1390
  this.socketClient.leaveChannel();
@@ -1320,22 +1413,28 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
1320
1413
  }
1321
1414
  }
1322
1415
  /**
1323
- * Send a JSON-RPC request to the wallet (async, fire-and-forget).
1416
+ * Send a JSON-RPC request to the wallet. Returns the outbound send promise
1417
+ * (settled on the relay's ack) so callers that must sequence on delivery -
1418
+ * e.g. the provider's wallet-wake redirect, which cannot navigate away
1419
+ * before the ciphertext reaches the relay - can await it. Callers may also
1420
+ * ignore it: failures are logged here either way.
1324
1421
  */
1325
1422
  sendJsonRpc(request) {
1326
1423
  if (!this.keyExchange?.areKeysExchanged()) {
1327
1424
  throw new Error("Not connected: key exchange not complete");
1328
1425
  }
1329
- this.sendEncrypted({
1426
+ const sent = this.sendEncrypted({
1330
1427
  type: "jsonrpc" /* JSONRPC */,
1331
1428
  jsonrpc: "2.0",
1332
1429
  id: request.id,
1333
1430
  method: request.method,
1334
1431
  params: request.params
1335
- }).catch((err) => {
1432
+ });
1433
+ sent.catch((err) => {
1336
1434
  error("ConnectionManager", "Failed to send JSON-RPC:", err);
1337
1435
  });
1338
1436
  this.startUnresponsiveTimer();
1437
+ return sent;
1339
1438
  }
1340
1439
  getStatus() {
1341
1440
  return this.status;
@@ -1449,8 +1548,19 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
1449
1548
  }
1450
1549
  }
1451
1550
  if (typeof message === "string" && this.keyExchange?.areKeysExchanged()) {
1551
+ let decrypted;
1552
+ try {
1553
+ decrypted = await this.keyExchange.decryptMessage(message);
1554
+ } catch (err) {
1555
+ error("ConnectionManager", "Failed to decrypt message:", err);
1556
+ this.consecutiveDecryptFailures++;
1557
+ if (this.consecutiveDecryptFailures >= _ConnectionManager.MAX_DECRYPT_FAILURES) {
1558
+ await this.teardownDesyncedSession();
1559
+ }
1560
+ return;
1561
+ }
1562
+ this.consecutiveDecryptFailures = 0;
1452
1563
  try {
1453
- const decrypted = await this.keyExchange.decryptMessage(message);
1454
1564
  await this.persistSession();
1455
1565
  const parsed = JSON.parse(decrypted);
1456
1566
  if (isRecord(parsed)) {
@@ -1459,10 +1569,24 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
1459
1569
  warn("ConnectionManager", "Dropping non-object decrypted payload");
1460
1570
  }
1461
1571
  } catch (err) {
1462
- error("ConnectionManager", "Failed to decrypt message:", err);
1572
+ error("ConnectionManager", "Failed to handle decrypted message:", err);
1463
1573
  }
1464
1574
  }
1465
1575
  }
1576
+ /**
1577
+ * The receive stream is cryptographically unrecoverable: the peer's send
1578
+ * counter is ahead of our recv counter for good (relay buffer TTL/cap
1579
+ * dropped a ciphertext), so every future message would fail its tag.
1580
+ * An encrypted TERMINATE cannot communicate this (the desynced peer could
1581
+ * not open it either); tombstone the channel on the relay instead so the
1582
+ * wallet learns the pairing is dead even if it only re-joins later, then
1583
+ * clear local state and surface the standard terminated teardown.
1584
+ */
1585
+ async teardownDesyncedSession() {
1586
+ warn("ConnectionManager", "AEAD stream desynced beyond recovery; terminating session");
1587
+ await this.socketClient.closeChannel();
1588
+ this.handleSessionTerminated();
1589
+ }
1466
1590
  async handleSynAck(msg) {
1467
1591
  if (!this.keyExchange) return;
1468
1592
  this.setStatus("key_exchange" /* KEY_EXCHANGE */);
@@ -1516,6 +1640,7 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
1516
1640
  }
1517
1641
  case "terminate" /* TERMINATE */: {
1518
1642
  log("ConnectionManager", "Received terminate from wallet");
1643
+ this.emit("session_terminated");
1519
1644
  void this.disconnect();
1520
1645
  break;
1521
1646
  }
@@ -1635,6 +1760,12 @@ var _ConnectionManager = class _ConnectionManager extends EventEmitter3 {
1635
1760
  }
1636
1761
  };
1637
1762
  _ConnectionManager.MAX_RECONNECT_FAILURES = 5;
1763
+ // Nonces derive from the recv counter and there is no gap tolerance, so a
1764
+ // genuinely desynced stream (peer advanced past us, e.g. the relay's
1765
+ // buffer TTL dropped a ciphertext) fails EVERY subsequent open. Two in a
1766
+ // row cannot happen on a healthy stream; requiring the second guards
1767
+ // against a one-off injected junk ciphertext killing a live session.
1768
+ _ConnectionManager.MAX_DECRYPT_FAILURES = 2;
1638
1769
  var ConnectionManager = _ConnectionManager;
1639
1770
 
1640
1771
  // src/utils/platform.ts
@@ -1648,6 +1779,41 @@ function getAppStoreUrl() {
1648
1779
  }
1649
1780
  return "https://apps.apple.com/app/myqrlwallet/id6742219498";
1650
1781
  }
1782
+ function attemptWalletRedirect(uri, timeoutMs = 1800) {
1783
+ if (typeof window === "undefined" || typeof document === "undefined") {
1784
+ return Promise.resolve(false);
1785
+ }
1786
+ if (!uri.trim().toLowerCase().startsWith("qrlconnect:")) {
1787
+ return Promise.resolve(false);
1788
+ }
1789
+ return new Promise((resolve) => {
1790
+ let settled = false;
1791
+ const finish = (opened) => {
1792
+ if (settled) return;
1793
+ settled = true;
1794
+ document.removeEventListener("visibilitychange", onVisibility);
1795
+ window.removeEventListener("pagehide", onHide);
1796
+ window.clearTimeout(timer);
1797
+ resolve(opened);
1798
+ };
1799
+ const onVisibility = () => {
1800
+ if (document.visibilityState === "hidden") finish(true);
1801
+ };
1802
+ const onHide = () => {
1803
+ finish(true);
1804
+ };
1805
+ document.addEventListener("visibilitychange", onVisibility);
1806
+ window.addEventListener("pagehide", onHide);
1807
+ const timer = window.setTimeout(() => {
1808
+ finish(false);
1809
+ }, timeoutMs);
1810
+ try {
1811
+ window.location.href = uri;
1812
+ } catch {
1813
+ finish(false);
1814
+ }
1815
+ });
1816
+ }
1651
1817
 
1652
1818
  // src/QRLConnectProvider.ts
1653
1819
  var QRL_CONNECT_PROVIDER_INFO = {
@@ -1657,6 +1823,16 @@ var QRL_CONNECT_PROVIDER_INFO = {
1657
1823
  };
1658
1824
  var EIP6963_ANNOUNCE_EVENT = "eip6963:announceProvider";
1659
1825
  var EIP6963_REQUEST_EVENT = "eip6963:requestProvider";
1826
+ function isRecordObj(v) {
1827
+ return typeof v === "object" && v !== null && !Array.isArray(v);
1828
+ }
1829
+ function parseInflightRecord(v) {
1830
+ if (!isRecordObj(v)) return null;
1831
+ const { id, method, ts } = v;
1832
+ if (typeof id !== "string" && typeof id !== "number") return null;
1833
+ if (typeof method !== "string" || typeof ts !== "number") return null;
1834
+ return { id, method, ts };
1835
+ }
1660
1836
  var QRLConnectProvider = class extends EventEmitter4 {
1661
1837
  constructor(options) {
1662
1838
  super();
@@ -1665,6 +1841,7 @@ var QRLConnectProvider = class extends EventEmitter4 {
1665
1841
  this.eip6963RequestListener = null;
1666
1842
  this.resumeListener = null;
1667
1843
  this.resumeDebounce = null;
1844
+ this.orphanedRequests = /* @__PURE__ */ new Map();
1668
1845
  // Random per-instance prefix keeps request ids unique across page loads.
1669
1846
  // A bare counter restarts at 1 on reload, and the relay buffers messages
1670
1847
  // for 5 minutes, so a stale buffered response could otherwise be matched
@@ -1673,6 +1850,10 @@ var QRLConnectProvider = class extends EventEmitter4 {
1673
1850
  this.requestCounter = 0;
1674
1851
  this.isQRLConnect = true;
1675
1852
  this.options = options;
1853
+ this.inflightKey = `${options.storageKey ?? `${STORAGE_KEY_PREFIX}:session`}:inflight`;
1854
+ for (const rec of this.readInflight()) {
1855
+ this.orphanedRequests.set(rec.id, { method: rec.method });
1856
+ }
1676
1857
  if (options.debug) {
1677
1858
  setDebug(true);
1678
1859
  }
@@ -1785,6 +1966,16 @@ var QRLConnectProvider = class extends EventEmitter4 {
1785
1966
  this.connectionManager.on("jsonrpc_response", (response) => {
1786
1967
  const pending = this.pendingRequests.get(response.id);
1787
1968
  if (!pending) {
1969
+ const orphan = this.orphanedRequests.get(response.id);
1970
+ if (orphan) {
1971
+ this.settleInflight(response.id);
1972
+ this.emit("late_response", {
1973
+ id: response.id,
1974
+ method: orphan.method,
1975
+ ...response.error ? { error: response.error } : { result: response.result }
1976
+ });
1977
+ return;
1978
+ }
1788
1979
  warn("Provider", `No pending request for id ${response.id}`);
1789
1980
  return;
1790
1981
  }
@@ -1813,6 +2004,13 @@ var QRLConnectProvider = class extends EventEmitter4 {
1813
2004
  }
1814
2005
  this.pendingRequests.clear();
1815
2006
  });
2007
+ this.connectionManager.on("session_terminated", () => {
2008
+ for (const [, pending] of this.pendingRequests) {
2009
+ pending.reject(new Error("Session terminated by wallet"));
2010
+ }
2011
+ this.pendingRequests.clear();
2012
+ this.clearAllInflight();
2013
+ });
1816
2014
  }
1817
2015
  /**
1818
2016
  * Generate a connection URI for QR code display or deep link redirect.
@@ -1849,7 +2047,13 @@ var QRLConnectProvider = class extends EventEmitter4 {
1849
2047
  throw new Error(`Unsupported method: ${method}`);
1850
2048
  }
1851
2049
  if (this.connectionManager.getStatus() !== "connected" /* CONNECTED */) {
1852
- throw new Error("Not connected to QRL Wallet");
2050
+ if (method === "qrl_requestAccounts" && this.connectionManager.isPaired() && this.connectionManager.getAccounts().length > 0) {
2051
+ return this.connectionManager.getAccounts();
2052
+ }
2053
+ const joined = await this.connectionManager.ensureChannelJoined();
2054
+ if (!joined) {
2055
+ throw new Error("Not connected to QRL Wallet");
2056
+ }
1853
2057
  }
1854
2058
  const id = `${this.requestIdPrefix}-${++this.requestCounter}`;
1855
2059
  return new Promise((resolve, reject) => {
@@ -1872,20 +2076,109 @@ var QRLConnectProvider = class extends EventEmitter4 {
1872
2076
  const originalReject = pending.reject;
1873
2077
  pending.resolve = (result) => {
1874
2078
  clearTimeout(timeout);
2079
+ this.settleInflight(id);
1875
2080
  originalResolve(result);
1876
2081
  };
1877
2082
  pending.reject = (error2) => {
1878
2083
  clearTimeout(timeout);
2084
+ this.settleInflight(id);
1879
2085
  originalReject(error2);
1880
2086
  };
1881
- this.connectionManager.sendJsonRpc({
2087
+ if (RESTRICTED_METHODS.has(method)) {
2088
+ this.persistInflight(id, method);
2089
+ }
2090
+ const sent = this.connectionManager.sendJsonRpc({
1882
2091
  jsonrpc: "2.0",
1883
2092
  id,
1884
2093
  method,
1885
2094
  params
1886
2095
  });
2096
+ sent.catch((err) => {
2097
+ const failed = this.pendingRequests.get(id);
2098
+ if (!failed) return;
2099
+ this.pendingRequests.delete(id);
2100
+ failed.reject(err instanceof Error ? err : new Error(String(err)));
2101
+ });
2102
+ if (this.shouldRedirectToWallet(method)) {
2103
+ void sent.then(
2104
+ () => attemptWalletRedirect(this.walletWakeUri()),
2105
+ () => void 0
2106
+ );
2107
+ }
1887
2108
  });
1888
2109
  }
2110
+ /**
2111
+ * Redirect only for calls the user must approve, on mobile, and only when
2112
+ * the wallet is actually absent from the channel: right after pairing its
2113
+ * socket lingers and a pointless app flash would be jarring.
2114
+ *
2115
+ * qrl_requestAccounts is excluded even though it is restricted: dApps call
2116
+ * it on page load to restore a session, so it can run with no user gesture
2117
+ * (an unsolicited "Open in MyQRLWallet?" sheet on load) and pairing UIs
2118
+ * already own their own deep-link step.
2119
+ */
2120
+ shouldRedirectToWallet(method) {
2121
+ return this.options.walletRedirectOnRequest !== false && RESTRICTED_METHODS.has(method) && method !== "qrl_requestAccounts" && isMobileBrowser() && !this.connectionManager.isWalletPresent();
2122
+ }
2123
+ // ── In-flight persistence (survive the return-redirect reload) ──────
2124
+ readInflight() {
2125
+ if (typeof localStorage === "undefined") return [];
2126
+ try {
2127
+ const raw = localStorage.getItem(this.inflightKey);
2128
+ if (!raw) return [];
2129
+ const parsed = JSON.parse(raw);
2130
+ if (!Array.isArray(parsed)) return [];
2131
+ const now = Date.now();
2132
+ const records = [];
2133
+ for (const v of parsed) {
2134
+ const rec = parseInflightRecord(v);
2135
+ if (rec && now - rec.ts <= REQUEST_TIMEOUT_MS) records.push(rec);
2136
+ }
2137
+ return records;
2138
+ } catch {
2139
+ return [];
2140
+ }
2141
+ }
2142
+ writeInflight(records) {
2143
+ if (typeof localStorage === "undefined") return;
2144
+ try {
2145
+ if (records.length === 0) {
2146
+ localStorage.removeItem(this.inflightKey);
2147
+ } else {
2148
+ localStorage.setItem(this.inflightKey, JSON.stringify(records));
2149
+ }
2150
+ } catch {
2151
+ }
2152
+ }
2153
+ persistInflight(id, method) {
2154
+ const records = this.readInflight().filter((r) => r.id !== id);
2155
+ records.push({ id, method, ts: Date.now() });
2156
+ this.writeInflight(records);
2157
+ }
2158
+ settleInflight(id) {
2159
+ this.orphanedRequests.delete(id);
2160
+ this.writeInflight(this.readInflight().filter((r) => r.id !== id));
2161
+ }
2162
+ clearAllInflight() {
2163
+ this.orphanedRequests.clear();
2164
+ this.writeInflight([]);
2165
+ }
2166
+ /**
2167
+ * Wake URI for the wallet app. Carries no pairing payload: the app
2168
+ * foregrounds on any `qrlconnect:` URL and re-joins its sessions (draining
2169
+ * the buffered request); the cid lets a future wallet jump straight to the
2170
+ * right approval.
2171
+ *
2172
+ * MUST stay query-only (no host/path). The mobile app routes deep links
2173
+ * with expo-router, which maps anything after the scheme to an in-app
2174
+ * route: `qrlconnect://resume?...` lands on the "+not-found" screen and
2175
+ * unmounts the WebView, so the wake achieves nothing. The hostless form
2176
+ * routes to the WebView tab exactly like the pairing URIs
2177
+ * (`qrlconnect://?q=...`), which is the whole point of the wake.
2178
+ */
2179
+ walletWakeUri() {
2180
+ return `qrlconnect://?wake=${encodeURIComponent(this.connectionManager.getChannelId())}`;
2181
+ }
1889
2182
  /**
1890
2183
  * Get the current connection status.
1891
2184
  */
@@ -1916,6 +2209,19 @@ var QRLConnectProvider = class extends EventEmitter4 {
1916
2209
  hasStoredSession() {
1917
2210
  return this.connectionManager.hasStoredSession();
1918
2211
  }
2212
+ /**
2213
+ * True while a pairing exists (keys exchanged), even when the wallet app
2214
+ * is backgrounded and its socket is out of the channel. Combine with
2215
+ * getStatus() to tell "wallet away, session intact" (paired + WAITING)
2216
+ * apart from "waiting for a first scan" in pairing UIs.
2217
+ */
2218
+ isPaired() {
2219
+ return this.connectionManager.isPaired();
2220
+ }
2221
+ /** True while the wallet is actually present in the relay channel. */
2222
+ isWalletPresent() {
2223
+ return this.connectionManager.isWalletPresent();
2224
+ }
1919
2225
  /**
1920
2226
  * Reset the connection and start a fresh pairing with a new channel.
1921
2227
  * Use this when the user wants to create a new connection instead of
@@ -1926,6 +2232,7 @@ var QRLConnectProvider = class extends EventEmitter4 {
1926
2232
  pending.reject(new Error("Connection reset"));
1927
2233
  }
1928
2234
  this.pendingRequests.clear();
2235
+ this.clearAllInflight();
1929
2236
  await this.connectionManager.resetForNewChannel();
1930
2237
  return this.getConnectionURI();
1931
2238
  }
@@ -1940,6 +2247,7 @@ var QRLConnectProvider = class extends EventEmitter4 {
1940
2247
  pending.reject(new Error("Disconnected"));
1941
2248
  }
1942
2249
  this.pendingRequests.clear();
2250
+ this.clearAllInflight();
1943
2251
  this.teardownResumeListeners();
1944
2252
  await this.connectionManager.disconnect();
1945
2253
  }
@@ -2329,6 +2637,7 @@ export {
2329
2637
  SCHEME_VERSION_TYPED,
2330
2638
  SocketClient,
2331
2639
  UNRESTRICTED_METHODS,
2640
+ attemptWalletRedirect,
2332
2641
  bytesToHex,
2333
2642
  cidFromString,
2334
2643
  cidRandom,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qrlwallet/connect",
3
- "version": "3.1.1",
3
+ "version": "3.3.0",
4
4
  "description": "Connect your dApp to QRL Wallet via QR code or deep link",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",