@meshconnect/uwc-core 1.1.1 → 1.1.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.
- package/dist/services/connection-service.d.ts +8 -0
- package/dist/services/connection-service.d.ts.map +1 -1
- package/dist/services/connection-service.js +75 -10
- package/dist/services/connection-service.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/src/services/connection-service.reentrancy.test.ts +437 -0
- package/src/services/connection-service.ts +80 -14
- package/src/version.ts +1 -1
|
@@ -15,6 +15,14 @@ export declare class ConnectionService {
|
|
|
15
15
|
private activeConnector;
|
|
16
16
|
/** Tears down the post-connect lifecycle subscription; set while connected. */
|
|
17
17
|
private lifecycleTeardown;
|
|
18
|
+
/**
|
|
19
|
+
* Abort handle for the CURRENT in-flight connect attempt. A re-entrant
|
|
20
|
+
* connect() aborts this before starting, so a superseded attempt's URI poll
|
|
21
|
+
* and handoff visibility listener are torn down instead of left running
|
|
22
|
+
* alongside the new one (MFS-602: orphaned polls/handoffs otherwise stack,
|
|
23
|
+
* each reading the shared activeConnector, and flood telemetry).
|
|
24
|
+
*/
|
|
25
|
+
private inFlightAbort;
|
|
18
26
|
constructor(networks: Network[], wallets: WalletMetadata[], sessionManager: SessionManager, connectors: Map<ConnectionMode, Connector>, usingIntegratedBrowser: boolean, eventManager: EventManager, walletResponseTimeoutMs: number);
|
|
19
27
|
connect(connectionMode: ConnectionMode, walletId: string, networkId?: NetworkId, options?: ServiceOptions): Promise<void>;
|
|
20
28
|
/** Wire the active connector's lifecycle stream onto the bus (or signal absence). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection-service.d.ts","sourceRoot":"","sources":["../../src/services/connection-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,SAAS,EAET,cAAc,EACd,cAAc,EACd,SAAS,EAMV,MAAM,wBAAwB,CAAA;AAE/B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAK7D,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED,qBAAa,iBAAiB;
|
|
1
|
+
{"version":3,"file":"connection-service.d.ts","sourceRoot":"","sources":["../../src/services/connection-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,SAAS,EAET,cAAc,EACd,cAAc,EACd,SAAS,EAMV,MAAM,wBAAwB,CAAA;AAE/B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAK7D,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED,qBAAa,iBAAiB;IAc1B,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,sBAAsB;IAC9B,OAAO,CAAC,YAAY;IAEpB,OAAO,CAAC,uBAAuB;IApBjC,OAAO,CAAC,eAAe,CAAyB;IAChD,+EAA+E;IAC/E,OAAO,CAAC,iBAAiB,CAA0B;IACnD;;;;;;OAMG;IACH,OAAO,CAAC,aAAa,CAA+B;gBAG1C,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE,cAAc,EAAE,EACzB,cAAc,EAAE,cAAc,EAC9B,UAAU,EAAE,GAAG,CAAC,cAAc,EAAE,SAAS,CAAC,EAC1C,sBAAsB,EAAE,OAAO,EAC/B,YAAY,EAAE,YAAY,EAE1B,uBAAuB,EAAE,MAAM;IAGnC,OAAO,CACX,cAAc,EAAE,cAAc,EAC9B,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,SAAS,EACrB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,IAAI,CAAC;IAuJhB,qFAAqF;IACrF,OAAO,CAAC,eAAe;IA8CvB;;;;;;OAMG;IACH,iBAAiB,IAAI,IAAI;IAKzB;;;;;;;OAOG;IACH,iBAAiB,IAAI,IAAI;YAqBX,kBAAkB;IAgEhC,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,kBAAkB;IA2C1B,OAAO,CAAC,2BAA2B;IAiD7B,UAAU,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BzD,gBAAgB,IAAI,MAAM,GAAG,SAAS;IAiBtC,UAAU,IAAI,cAAc,EAAE;IAI9B,aAAa,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,IAAI;IAI9C,WAAW,IAAI,OAAO,EAAE;IAIxB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,wBAAwB;CAiBjC"}
|
|
@@ -13,6 +13,14 @@ export class ConnectionService {
|
|
|
13
13
|
activeConnector = null;
|
|
14
14
|
/** Tears down the post-connect lifecycle subscription; set while connected. */
|
|
15
15
|
lifecycleTeardown;
|
|
16
|
+
/**
|
|
17
|
+
* Abort handle for the CURRENT in-flight connect attempt. A re-entrant
|
|
18
|
+
* connect() aborts this before starting, so a superseded attempt's URI poll
|
|
19
|
+
* and handoff visibility listener are torn down instead of left running
|
|
20
|
+
* alongside the new one (MFS-602: orphaned polls/handoffs otherwise stack,
|
|
21
|
+
* each reading the shared activeConnector, and flood telemetry).
|
|
22
|
+
*/
|
|
23
|
+
inFlightAbort = null;
|
|
16
24
|
constructor(networks, wallets, sessionManager, connectors, usingIntegratedBrowser, eventManager,
|
|
17
25
|
// Observational wallet-response deadline for the handoff timeout marker.
|
|
18
26
|
walletResponseTimeoutMs) {
|
|
@@ -26,14 +34,35 @@ export class ConnectionService {
|
|
|
26
34
|
}
|
|
27
35
|
async connect(connectionMode, walletId, networkId, options) {
|
|
28
36
|
options?.signal?.throwIfAborted();
|
|
29
|
-
//
|
|
37
|
+
// Validate BEFORE touching any in-flight state. A lookup/validation failure
|
|
38
|
+
// (unknown walletId, unsupported mode, bad network) must not abort a valid
|
|
39
|
+
// prior in-flight attempt, nor leave a dangling `inFlightAbort` that a later
|
|
40
|
+
// connect() would spuriously abort (MFS-602).
|
|
30
41
|
const wallet = this.findWallet(walletId);
|
|
31
|
-
// Get the appropriate provider based on connection mode
|
|
32
42
|
const provider = this.getProviderForMode(wallet, connectionMode);
|
|
33
|
-
// Validate and determine the network
|
|
34
43
|
const network = this.validateAndDetermineNetwork(networkId, provider);
|
|
35
|
-
// Get the appropriate connector
|
|
36
44
|
const connector = this.getConnector(connectionMode);
|
|
45
|
+
// Supersede any prior in-flight attempt: abort it so its URI poll and
|
|
46
|
+
// handoff visibility listeners tear down (via the abort paths below) BEFORE
|
|
47
|
+
// this attempt starts, instead of running alongside it (MFS-602). The
|
|
48
|
+
// caller's own signal is composed in, so a caller abort still cancels this
|
|
49
|
+
// attempt while a re-entrant connect() supersedes it.
|
|
50
|
+
this.inFlightAbort?.abort(new DOMException('Superseded by a newer connection attempt', 'AbortError'));
|
|
51
|
+
const attemptAbort = new AbortController();
|
|
52
|
+
this.inFlightAbort = attemptAbort;
|
|
53
|
+
const callerSignal = options?.signal;
|
|
54
|
+
let removeCallerAbortListener;
|
|
55
|
+
if (callerSignal) {
|
|
56
|
+
if (callerSignal.aborted) {
|
|
57
|
+
attemptAbort.abort(callerSignal.reason);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
const onCallerAbort = () => attemptAbort.abort(callerSignal.reason);
|
|
61
|
+
callerSignal.addEventListener('abort', onCallerAbort, { once: true });
|
|
62
|
+
removeCallerAbortListener = () => callerSignal.removeEventListener('abort', onCallerAbort);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const signal = attemptAbort.signal;
|
|
37
66
|
// Emit AFTER validation so a thrown lookup/validation error doesn't leave
|
|
38
67
|
// listeners seeing a "connecting" state for an attempt that never started.
|
|
39
68
|
this.eventManager.emit('connecting', { connectionMode, walletId });
|
|
@@ -49,7 +78,7 @@ export class ConnectionService {
|
|
|
49
78
|
result = await instrumentHandoff({
|
|
50
79
|
eventManager: this.eventManager,
|
|
51
80
|
timeoutMs: this.walletResponseTimeoutMs,
|
|
52
|
-
signal
|
|
81
|
+
signal
|
|
53
82
|
}, {
|
|
54
83
|
operation: 'connect',
|
|
55
84
|
connectionMode,
|
|
@@ -62,11 +91,11 @@ export class ConnectionService {
|
|
|
62
91
|
// rejects the caller's await promptly. The underlying wallet prompt
|
|
63
92
|
// itself can't be cancelled, but the dapp UI is no longer blocked on it.
|
|
64
93
|
if (connectionMode === 'injected') {
|
|
65
|
-
return await raceAbort(connector.connect(network, provider),
|
|
94
|
+
return await raceAbort(connector.connect(network, provider), signal);
|
|
66
95
|
}
|
|
67
96
|
else if (connectionMode === 'tonConnect' ||
|
|
68
97
|
connectionMode === 'walletConnect') {
|
|
69
|
-
return await raceAbort(this.connectWithURIPoll(connector, network, provider, connectionMode,
|
|
98
|
+
return await raceAbort(this.connectWithURIPoll(connector, network, provider, connectionMode, signal), signal);
|
|
70
99
|
}
|
|
71
100
|
else {
|
|
72
101
|
throw new Error(`Unsupported connection mode: ${connectionMode}`);
|
|
@@ -76,12 +105,29 @@ export class ConnectionService {
|
|
|
76
105
|
// and this point, still bail before mutating session. Inside the try so
|
|
77
106
|
// the half-attached connector is released on this late abort too — a
|
|
78
107
|
// stale activeConnector would keep serving the aborted attempt's URI.
|
|
79
|
-
|
|
108
|
+
signal.throwIfAborted();
|
|
80
109
|
}
|
|
81
110
|
catch (error) {
|
|
82
|
-
|
|
111
|
+
// Only release if WE are still the in-flight attempt. Gate BOTH releases
|
|
112
|
+
// on the identity-unique `attemptAbort` token, never on
|
|
113
|
+
// `this.activeConnector === connector`: `connectors` is a shared singleton
|
|
114
|
+
// Map, so a same-mode re-entrant connect() reuses the IDENTICAL connector
|
|
115
|
+
// object — a reference check would pass here and wrongly detach the newer,
|
|
116
|
+
// now-live attempt's connector, breaking its URI polling and leaving a
|
|
117
|
+
// later disconnect() unable to tear down the real wallet session (MFS-602).
|
|
118
|
+
if (this.inFlightAbort === attemptAbort) {
|
|
119
|
+
this.activeConnector = null;
|
|
120
|
+
this.inFlightAbort = null;
|
|
121
|
+
}
|
|
122
|
+
removeCallerAbortListener?.();
|
|
83
123
|
throw error;
|
|
84
124
|
}
|
|
125
|
+
// This attempt reached a successful handoff without being superseded (a
|
|
126
|
+
// supersession aborts `signal`, which the belt-and-suspenders check above
|
|
127
|
+
// turns into a throw). Release the in-flight handle + caller-signal listener.
|
|
128
|
+
if (this.inFlightAbort === attemptAbort)
|
|
129
|
+
this.inFlightAbort = null;
|
|
130
|
+
removeCallerAbortListener?.();
|
|
85
131
|
// Filter available addresses to only include those for configured networks
|
|
86
132
|
const filteredAvailableAddresses = result.availableAddresses.filter(addr => this.networks.some(n => n.id === addr.networkId));
|
|
87
133
|
this.sessionManager.updateSession({
|
|
@@ -178,7 +224,19 @@ export class ConnectionService {
|
|
|
178
224
|
let pollingStopped = false;
|
|
179
225
|
let emittedUri;
|
|
180
226
|
const pollForURI = setInterval(() => {
|
|
181
|
-
|
|
227
|
+
// Read the URI from THIS attempt's own connector, never the shared
|
|
228
|
+
// this.activeConnector: a still-running poll from a superseded attempt
|
|
229
|
+
// would otherwise read — and emit — a newer attempt's URI under this
|
|
230
|
+
// attempt's connectionMode (MFS-602 cross-talk). Mirror the public
|
|
231
|
+
// getConnectionURI guard: the getter is optional and may throw before a
|
|
232
|
+
// URI exists.
|
|
233
|
+
let uri;
|
|
234
|
+
try {
|
|
235
|
+
uri = connector.getConnectionURI?.();
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
uri = undefined;
|
|
239
|
+
}
|
|
182
240
|
if (uri && uri !== emittedUri) {
|
|
183
241
|
emittedUri = uri;
|
|
184
242
|
this.eventManager.emit('connectionUri', { uri, connectionMode });
|
|
@@ -289,6 +347,13 @@ export class ConnectionService {
|
|
|
289
347
|
}
|
|
290
348
|
async disconnect(options) {
|
|
291
349
|
options?.signal?.throwIfAborted();
|
|
350
|
+
// Cancel any in-flight connect() first. Without this, a wallet that responds
|
|
351
|
+
// AFTER the user disconnects would let the pending connect() proceed past its
|
|
352
|
+
// `signal.throwIfAborted()` guard into updateSession()/`connected`, resurrecting
|
|
353
|
+
// the session just torn down. Aborting the shared in-flight token makes that
|
|
354
|
+
// attempt bail via raceAbort (its late result is discarded). No-op on the
|
|
355
|
+
// normal path: a settled connect() already nulled `inFlightAbort` (MFS-602).
|
|
356
|
+
this.inFlightAbort?.abort(new DOMException('Connection cancelled by disconnect', 'AbortError'));
|
|
292
357
|
// Remove the lifecycle listeners first so a provider 'disconnect' fired during
|
|
293
358
|
// teardown doesn't re-enter as a wallet-initiated `walletDisconnected`.
|
|
294
359
|
this.teardownLifecycle();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection-service.js","sourceRoot":"","sources":["../../src/services/connection-service.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"connection-service.js","sourceRoot":"","sources":["../../src/services/connection-service.ts"],"names":[],"mappings":"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"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const UWC_CORE_VERSION = "1.1.
|
|
1
|
+
export declare const UWC_CORE_VERSION = "1.1.2";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
2
|
+
import { ConnectionService } from './connection-service'
|
|
3
|
+
import { SessionManager } from '../managers/session-manager'
|
|
4
|
+
import { EventManager } from '../managers/event-manager'
|
|
5
|
+
import type {
|
|
6
|
+
Network,
|
|
7
|
+
WalletMetadata,
|
|
8
|
+
ConnectionMode,
|
|
9
|
+
Connector,
|
|
10
|
+
ConnectorResult
|
|
11
|
+
} from '@meshconnect/uwc-types'
|
|
12
|
+
|
|
13
|
+
// MFS-602: connect() must be re-entrancy safe.
|
|
14
|
+
//
|
|
15
|
+
// A second connect() entered while a prior URI-poll handoff is still live must
|
|
16
|
+
// SUPERSEDE the prior attempt — tear down its poll interval and its
|
|
17
|
+
// visibility listener — rather than run alongside it. Without this, orphaned
|
|
18
|
+
// polls stack (each reading the SHARED activeConnector) and orphaned handoffs
|
|
19
|
+
// each fire on every tab-visibility change. That is the mechanism behind the
|
|
20
|
+
// prod burst seen after PR #340 wired UWC telemetry to Datadog RUM
|
|
21
|
+
// (uwc.connectionUri 63k/48k + uwc.pageHiddenDuringHandoff 11.8k, one session).
|
|
22
|
+
//
|
|
23
|
+
// The existing "does not emit connectionUri with the aborted session's
|
|
24
|
+
// connectionMode for a later connect" test (connection-service.test.ts) proves
|
|
25
|
+
// the CALLER-abort path is clean. These tests prove the same invariants hold
|
|
26
|
+
// when the prior attempt is superseded by re-entry with NO explicit abort.
|
|
27
|
+
describe('MFS-602: ConnectionService.connect() re-entrancy safety', () => {
|
|
28
|
+
let connectionService: ConnectionService
|
|
29
|
+
let sessionManager: SessionManager
|
|
30
|
+
let eventManager: EventManager
|
|
31
|
+
let network: Network
|
|
32
|
+
let wallets: WalletMetadata[]
|
|
33
|
+
let connectors: Map<ConnectionMode, Connector>
|
|
34
|
+
let wcConnector: Connector
|
|
35
|
+
let tonConnector: Connector
|
|
36
|
+
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
sessionManager = new SessionManager()
|
|
39
|
+
eventManager = new EventManager()
|
|
40
|
+
|
|
41
|
+
network = {
|
|
42
|
+
id: 'tvm:-239',
|
|
43
|
+
internalId: -239,
|
|
44
|
+
name: 'TON',
|
|
45
|
+
namespace: 'tvm',
|
|
46
|
+
logoUrl: 'https://ton.org/logo.svg',
|
|
47
|
+
networkType: 'tvm',
|
|
48
|
+
nativeCurrency: { name: 'Toncoin', symbol: 'TON', decimals: 9 }
|
|
49
|
+
} as unknown as Network
|
|
50
|
+
|
|
51
|
+
wallets = [
|
|
52
|
+
{
|
|
53
|
+
id: 'tonkeeper',
|
|
54
|
+
name: 'Tonkeeper',
|
|
55
|
+
metadata: { icon: '', downloadUrl: '' },
|
|
56
|
+
walletConnectProvider: { supportedNetworkIds: ['tvm:-239'] },
|
|
57
|
+
tonConnectProvider: { supportedNetworkIds: ['tvm:-239'] }
|
|
58
|
+
} as unknown as WalletMetadata
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
// connect() never settles on its own — models a wallet prompt / WC proposal
|
|
62
|
+
// left pending, so a poll is only ever stopped by supersession, abort, or
|
|
63
|
+
// the 5-min watchdog.
|
|
64
|
+
wcConnector = {
|
|
65
|
+
connect: vi.fn().mockReturnValue(new Promise(() => {})),
|
|
66
|
+
disconnect: vi.fn(),
|
|
67
|
+
switchNetwork: vi.fn(),
|
|
68
|
+
getConnectionURI: vi.fn().mockReturnValue('wc://uri')
|
|
69
|
+
} as unknown as Connector
|
|
70
|
+
|
|
71
|
+
tonConnector = {
|
|
72
|
+
connect: vi.fn().mockReturnValue(new Promise(() => {})),
|
|
73
|
+
disconnect: vi.fn(),
|
|
74
|
+
switchNetwork: vi.fn(),
|
|
75
|
+
getConnectionURI: vi.fn().mockReturnValue('tc://ton-uri')
|
|
76
|
+
} as unknown as Connector
|
|
77
|
+
|
|
78
|
+
connectors = new Map()
|
|
79
|
+
connectors.set('walletConnect', wcConnector)
|
|
80
|
+
connectors.set('tonConnect', tonConnector)
|
|
81
|
+
|
|
82
|
+
// Explicit `0` wallet-response timeout: `instrumentHandoff` only arms the
|
|
83
|
+
// marker when `timeoutMs > 0`, so 0 disables it — leaving the poll interval
|
|
84
|
+
// + its watchdog as the only timers, which keeps getTimerCount() unambiguous.
|
|
85
|
+
connectionService = new ConnectionService(
|
|
86
|
+
[network],
|
|
87
|
+
wallets,
|
|
88
|
+
sessionManager,
|
|
89
|
+
connectors,
|
|
90
|
+
false,
|
|
91
|
+
eventManager,
|
|
92
|
+
0
|
|
93
|
+
)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
afterEach(async () => {
|
|
97
|
+
// Drop fake timers first so any still-armed poll interval/watchdog from a
|
|
98
|
+
// never-settling connect() is discarded rather than leaked into later tests.
|
|
99
|
+
vi.useRealTimers()
|
|
100
|
+
// A pending connect() also keeps its handoff focus/visibility listeners
|
|
101
|
+
// attached (instrumentHandoff only detaches them in its finally, which never
|
|
102
|
+
// runs while the wallet op hangs). disconnect() aborts the in-flight attempt,
|
|
103
|
+
// so that finally runs and stopFocus() releases them.
|
|
104
|
+
await connectionService.disconnect().catch(() => {})
|
|
105
|
+
await Promise.resolve()
|
|
106
|
+
// Restore the visibility override the fan-out test installs.
|
|
107
|
+
Object.defineProperty(document, 'visibilityState', {
|
|
108
|
+
value: 'visible',
|
|
109
|
+
configurable: true
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('supersedes a prior pending attempt: only one poll stays live (timers bounded)', async () => {
|
|
114
|
+
vi.useFakeTimers()
|
|
115
|
+
|
|
116
|
+
// Baseline: a single attempt arms exactly one poll (interval + watchdog).
|
|
117
|
+
connectionService.connect('walletConnect', 'tonkeeper').catch(() => {})
|
|
118
|
+
await vi.advanceTimersByTimeAsync(150)
|
|
119
|
+
const timersOneAttempt = vi.getTimerCount()
|
|
120
|
+
|
|
121
|
+
// A second, overlapping attempt (different mode) must supersede the first,
|
|
122
|
+
// not stack. Live timer count must stay at the single-attempt level.
|
|
123
|
+
connectionService.connect('tonConnect', 'tonkeeper').catch(() => {})
|
|
124
|
+
await vi.advanceTimersByTimeAsync(150)
|
|
125
|
+
|
|
126
|
+
expect(vi.getTimerCount()).toBe(timersOneAttempt)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('rejects the superseded attempt’s promise (no pending-promise leak)', async () => {
|
|
130
|
+
vi.useFakeTimers()
|
|
131
|
+
|
|
132
|
+
// NB: asserting getConnectionURI call-count on the superseded connector is
|
|
133
|
+
// NOT a valid detector — the orphaned poll reads the SHARED activeConnector,
|
|
134
|
+
// so it stops hitting its own connector's mock even while the interval is
|
|
135
|
+
// still alive (that stop IS the cross-talk). The observable, leak-relevant
|
|
136
|
+
// invariant is that the caller's await for the superseded attempt settles.
|
|
137
|
+
let priorRejected = false
|
|
138
|
+
connectionService.connect('walletConnect', 'tonkeeper').then(
|
|
139
|
+
() => {},
|
|
140
|
+
() => {
|
|
141
|
+
priorRejected = true
|
|
142
|
+
}
|
|
143
|
+
)
|
|
144
|
+
await vi.advanceTimersByTimeAsync(150)
|
|
145
|
+
|
|
146
|
+
// Re-enter — the prior attempt must be superseded, not left pending forever.
|
|
147
|
+
connectionService.connect('tonConnect', 'tonkeeper').catch(() => {})
|
|
148
|
+
await vi.advanceTimersByTimeAsync(10)
|
|
149
|
+
|
|
150
|
+
expect(priorRejected).toBe(true)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('never emits a connectionUri tagged with a superseded attempt’s mode (no cross-talk)', async () => {
|
|
154
|
+
vi.useFakeTimers()
|
|
155
|
+
|
|
156
|
+
// WC attempt has no URI of its own, so any WC-tagged emission can only come
|
|
157
|
+
// from the orphaned poll reading a later attempt's connector.
|
|
158
|
+
wcConnector.getConnectionURI = vi.fn().mockReturnValue(undefined)
|
|
159
|
+
|
|
160
|
+
const uriEmits: Array<{ uri: string; connectionMode: ConnectionMode }> = []
|
|
161
|
+
eventManager.on('connectionUri', e =>
|
|
162
|
+
uriEmits.push(e as { uri: string; connectionMode: ConnectionMode })
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
// 1) walletConnect attempt, pending, NEVER aborted.
|
|
166
|
+
connectionService.connect('walletConnect', 'tonkeeper').catch(() => {})
|
|
167
|
+
await vi.advanceTimersByTimeAsync(150)
|
|
168
|
+
|
|
169
|
+
// 2) A later tonConnect attempt supersedes it and has a URI.
|
|
170
|
+
connectionService.connect('tonConnect', 'tonkeeper').catch(() => {})
|
|
171
|
+
await vi.advanceTimersByTimeAsync(300)
|
|
172
|
+
|
|
173
|
+
const mislabeled = uriEmits.find(e => e.connectionMode === 'walletConnect')
|
|
174
|
+
expect(mislabeled).toBeUndefined()
|
|
175
|
+
|
|
176
|
+
// Sanity: the live TON attempt still emits its own URI, correctly tagged.
|
|
177
|
+
expect(
|
|
178
|
+
uriEmits.some(
|
|
179
|
+
e => e.connectionMode === 'tonConnect' && e.uri === 'tc://ton-uri'
|
|
180
|
+
)
|
|
181
|
+
).toBe(true)
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('a re-entrant connect that fails validation does not abort a valid prior attempt', async () => {
|
|
185
|
+
vi.useFakeTimers()
|
|
186
|
+
|
|
187
|
+
// A valid attempt is in flight (poll armed).
|
|
188
|
+
let priorSettled = false
|
|
189
|
+
connectionService.connect('walletConnect', 'tonkeeper').then(
|
|
190
|
+
() => {
|
|
191
|
+
priorSettled = true
|
|
192
|
+
},
|
|
193
|
+
() => {
|
|
194
|
+
priorSettled = true
|
|
195
|
+
}
|
|
196
|
+
)
|
|
197
|
+
await vi.advanceTimersByTimeAsync(150)
|
|
198
|
+
const timersBefore = vi.getTimerCount()
|
|
199
|
+
|
|
200
|
+
// Re-enter with an unknown walletId: validation throws (findWallet) BEFORE
|
|
201
|
+
// any supersede/abort. The prior attempt must be left untouched — not
|
|
202
|
+
// aborted, not left with a dangling inFlightAbort.
|
|
203
|
+
await expect(
|
|
204
|
+
connectionService.connect('walletConnect', 'does-not-exist')
|
|
205
|
+
).rejects.toThrow(/not found/)
|
|
206
|
+
await vi.advanceTimersByTimeAsync(10)
|
|
207
|
+
|
|
208
|
+
// Prior valid attempt is still pending and its poll still live.
|
|
209
|
+
expect(priorSettled).toBe(false)
|
|
210
|
+
expect(vi.getTimerCount()).toBe(timersBefore)
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
// SAME-MODE supersession is the gap the cross-mode tests above miss. Because
|
|
214
|
+
// `connectors` is a shared singleton Map, a re-entrant connect() on the SAME
|
|
215
|
+
// mode reuses the IDENTICAL connector object. The superseded attempt's catch
|
|
216
|
+
// block must NOT release the shared `activeConnector` — the reference check
|
|
217
|
+
// that guarded the cross-mode path (`activeConnector === connector`) passes
|
|
218
|
+
// here and would detach the newer, now-live attempt's connector. Both invariants
|
|
219
|
+
// below FAIL on the pre-fix `if (this.activeConnector === connector)` guard.
|
|
220
|
+
it('same-mode supersede: live attempt keeps activeConnector (getConnectionURI still resolves)', async () => {
|
|
221
|
+
vi.useFakeTimers()
|
|
222
|
+
|
|
223
|
+
// First walletConnect attempt, pending, then superseded by a SECOND
|
|
224
|
+
// walletConnect attempt — both hold the identical wcConnector object.
|
|
225
|
+
connectionService.connect('walletConnect', 'tonkeeper').catch(() => {})
|
|
226
|
+
await vi.advanceTimersByTimeAsync(150)
|
|
227
|
+
|
|
228
|
+
connectionService.connect('walletConnect', 'tonkeeper').catch(() => {})
|
|
229
|
+
// Flush the superseded attempt's abort rejection so its catch block runs.
|
|
230
|
+
await vi.advanceTimersByTimeAsync(150)
|
|
231
|
+
|
|
232
|
+
// The live attempt's connector must still be installed. Pre-fix, the
|
|
233
|
+
// superseded attempt nulled it (shared object), so this returns undefined.
|
|
234
|
+
expect(connectionService.getConnectionURI()).toBe('wc://uri')
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
it('same-mode supersede: disconnect() still tears down the real wallet session', async () => {
|
|
238
|
+
vi.useFakeTimers()
|
|
239
|
+
|
|
240
|
+
connectionService.connect('walletConnect', 'tonkeeper').catch(() => {})
|
|
241
|
+
await vi.advanceTimersByTimeAsync(150)
|
|
242
|
+
|
|
243
|
+
connectionService.connect('walletConnect', 'tonkeeper').catch(() => {})
|
|
244
|
+
await vi.advanceTimersByTimeAsync(150)
|
|
245
|
+
|
|
246
|
+
// Pre-fix: activeConnector was nulled out from under the live attempt, so
|
|
247
|
+
// disconnect() skips connector.disconnect() and orphans the wallet session.
|
|
248
|
+
await connectionService.disconnect()
|
|
249
|
+
expect(wcConnector.disconnect).toHaveBeenCalledTimes(1)
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
// MFS-602 #2: disconnect() must cancel an in-flight connect(). Otherwise a
|
|
253
|
+
// wallet that responds AFTER the user disconnects drives the pending connect()
|
|
254
|
+
// past its abort guard into updateSession()/`connected`, resurrecting the
|
|
255
|
+
// session just torn down. This FAILS pre-fix (disconnect never aborted the
|
|
256
|
+
// in-flight token, so `connected` fires and the caller's connect resolves).
|
|
257
|
+
it('disconnect() cancels an in-flight connect so a late wallet response cannot resurrect the session', async () => {
|
|
258
|
+
vi.useFakeTimers()
|
|
259
|
+
|
|
260
|
+
// A connect whose wallet response we control, so we can deliver it AFTER
|
|
261
|
+
// the disconnect — the exact ordering that resurrects a session.
|
|
262
|
+
let resolveConnect: (r: {
|
|
263
|
+
address: string
|
|
264
|
+
availableAddresses: []
|
|
265
|
+
}) => void = () => {}
|
|
266
|
+
wcConnector.connect = vi.fn().mockReturnValue(
|
|
267
|
+
new Promise<{ address: string; availableAddresses: [] }>(r => {
|
|
268
|
+
resolveConnect = r
|
|
269
|
+
})
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
const connectedEvents: unknown[] = []
|
|
273
|
+
eventManager.on('connected', e => connectedEvents.push(e))
|
|
274
|
+
|
|
275
|
+
let connectRejected = false
|
|
276
|
+
connectionService.connect('walletConnect', 'tonkeeper').then(
|
|
277
|
+
() => {},
|
|
278
|
+
() => {
|
|
279
|
+
connectRejected = true
|
|
280
|
+
}
|
|
281
|
+
)
|
|
282
|
+
await vi.advanceTimersByTimeAsync(150)
|
|
283
|
+
|
|
284
|
+
// User disconnects while the wallet prompt is still open.
|
|
285
|
+
await connectionService.disconnect()
|
|
286
|
+
|
|
287
|
+
// Wallet finally responds AFTER the disconnect — must be a no-op.
|
|
288
|
+
resolveConnect({ address: '0xabc', availableAddresses: [] })
|
|
289
|
+
await vi.advanceTimersByTimeAsync(10)
|
|
290
|
+
|
|
291
|
+
expect(connectRejected).toBe(true)
|
|
292
|
+
expect(connectedEvents).toHaveLength(0)
|
|
293
|
+
expect(sessionManager.getSession().connectionMode).toBeFalsy()
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
// Interaction between the two fixes: disconnect() aborts the in-flight attempt,
|
|
297
|
+
// which schedules that attempt's catch block (it nulls activeConnector) as a
|
|
298
|
+
// microtask. disconnect() must still read activeConnector and call the real
|
|
299
|
+
// connector.disconnect() SYNCHRONOUSLY — before that microtask runs — so the
|
|
300
|
+
// wallet session is actually torn down and not silently orphaned. Guards the
|
|
301
|
+
// ordering that would otherwise be a silent teardown regression.
|
|
302
|
+
it('disconnect() still tears down the real connector even while cancelling an in-flight connect', async () => {
|
|
303
|
+
vi.useFakeTimers()
|
|
304
|
+
|
|
305
|
+
connectionService.connect('walletConnect', 'tonkeeper').catch(() => {})
|
|
306
|
+
await vi.advanceTimersByTimeAsync(150)
|
|
307
|
+
|
|
308
|
+
await connectionService.disconnect()
|
|
309
|
+
|
|
310
|
+
// Synchronously within disconnect(), BEFORE the cancelled connect's catch
|
|
311
|
+
// microtask runs: the live wallet session was actually closed and the
|
|
312
|
+
// session connector released. This is the teardown-not-orphaned invariant.
|
|
313
|
+
expect(wcConnector.disconnect).toHaveBeenCalledTimes(1)
|
|
314
|
+
expect(connectionService['activeConnector']).toBeNull()
|
|
315
|
+
|
|
316
|
+
// Let the cancelled connect's rejection propagate; its catch then clears the
|
|
317
|
+
// (already-aborted) in-flight token, so no controller is leaked afterwards.
|
|
318
|
+
await vi.advanceTimersByTimeAsync(10)
|
|
319
|
+
expect(connectionService['inFlightAbort']).toBeNull()
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
// The strongest same-mode case: the re-entrant attempt actually SUCCEEDS.
|
|
323
|
+
// Pre-fix, the superseded attempt's catch (shared connector ref === match)
|
|
324
|
+
// nulls activeConnector WHILE the winning attempt is still awaiting its
|
|
325
|
+
// wallet, so the subsequent updateSession()/attachLifecycle wire a broken
|
|
326
|
+
// session (null connector → no URI, no lifecycle, dead disconnect). Post-fix
|
|
327
|
+
// the identity gate leaves the winner's connector intact.
|
|
328
|
+
it('same-mode supersede where the winning attempt connects: session wires cleanly', async () => {
|
|
329
|
+
vi.useFakeTimers()
|
|
330
|
+
|
|
331
|
+
// First WC attempt hangs; the second (superseding) WC attempt resolves.
|
|
332
|
+
let resolveWinner: (r: ConnectorResult) => void = () => {}
|
|
333
|
+
wcConnector.connect = vi
|
|
334
|
+
.fn()
|
|
335
|
+
.mockReturnValueOnce(new Promise(() => {}))
|
|
336
|
+
.mockReturnValueOnce(
|
|
337
|
+
new Promise<ConnectorResult>(r => {
|
|
338
|
+
resolveWinner = r
|
|
339
|
+
})
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
const connected: Array<{ session: unknown }> = []
|
|
343
|
+
eventManager.on('connected', e => connected.push(e as { session: unknown }))
|
|
344
|
+
|
|
345
|
+
connectionService.connect('walletConnect', 'tonkeeper').catch(() => {})
|
|
346
|
+
await vi.advanceTimersByTimeAsync(150)
|
|
347
|
+
|
|
348
|
+
connectionService.connect('walletConnect', 'tonkeeper').catch(() => {})
|
|
349
|
+
// Let the superseded attempt's catch run before the winner resolves.
|
|
350
|
+
await vi.advanceTimersByTimeAsync(150)
|
|
351
|
+
|
|
352
|
+
resolveWinner({
|
|
353
|
+
networkId: 'tvm:-239',
|
|
354
|
+
address: 'EQwinner',
|
|
355
|
+
availableAddresses: [{ address: 'EQwinner', networkId: 'tvm:-239' }]
|
|
356
|
+
} as ConnectorResult)
|
|
357
|
+
await vi.advanceTimersByTimeAsync(10)
|
|
358
|
+
|
|
359
|
+
// The winner owns the session, its connector is still installed, and it
|
|
360
|
+
// connected exactly once (no duplicate/leaked emission from the loser).
|
|
361
|
+
expect(connected).toHaveLength(1)
|
|
362
|
+
expect(sessionManager.getSession().connectionMode).toBe('walletConnect')
|
|
363
|
+
expect(sessionManager.getSession().activeAddress).toBe('EQwinner')
|
|
364
|
+
expect(connectionService['activeConnector']).toBe(wcConnector)
|
|
365
|
+
})
|
|
366
|
+
|
|
367
|
+
// Depth: three same-mode attempts, each superseding the last. Only the final
|
|
368
|
+
// attempt may stay live — no earlier catch may detach the survivor's shared
|
|
369
|
+
// connector. Pre-fix the middle attempt's catch nulls it.
|
|
370
|
+
it('deep same-mode supersede chain leaves only the last attempt live', async () => {
|
|
371
|
+
vi.useFakeTimers()
|
|
372
|
+
|
|
373
|
+
connectionService.connect('walletConnect', 'tonkeeper').catch(() => {})
|
|
374
|
+
await vi.advanceTimersByTimeAsync(60)
|
|
375
|
+
connectionService.connect('walletConnect', 'tonkeeper').catch(() => {})
|
|
376
|
+
await vi.advanceTimersByTimeAsync(60)
|
|
377
|
+
connectionService.connect('walletConnect', 'tonkeeper').catch(() => {})
|
|
378
|
+
await vi.advanceTimersByTimeAsync(150)
|
|
379
|
+
|
|
380
|
+
// Survivor's connector still installed → URI still resolvable, and exactly
|
|
381
|
+
// one poll remains armed (interval + watchdog).
|
|
382
|
+
expect(connectionService.getConnectionURI()).toBe('wc://uri')
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
// Cross-mode regression guard: a superseded attempt's LATE catch must never
|
|
386
|
+
// detach a DIFFERENT attempt that has since connected successfully.
|
|
387
|
+
it('a superseded attempt cannot detach a newer attempt that already connected (cross-mode)', async () => {
|
|
388
|
+
vi.useFakeTimers()
|
|
389
|
+
|
|
390
|
+
// WC attempt hangs; the superseding TON attempt resolves.
|
|
391
|
+
let resolveTon: (r: ConnectorResult) => void = () => {}
|
|
392
|
+
tonConnector.connect = vi.fn().mockReturnValue(
|
|
393
|
+
new Promise<ConnectorResult>(r => {
|
|
394
|
+
resolveTon = r
|
|
395
|
+
})
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
connectionService.connect('walletConnect', 'tonkeeper').catch(() => {})
|
|
399
|
+
await vi.advanceTimersByTimeAsync(150)
|
|
400
|
+
|
|
401
|
+
connectionService.connect('tonConnect', 'tonkeeper').catch(() => {})
|
|
402
|
+
await vi.advanceTimersByTimeAsync(150)
|
|
403
|
+
|
|
404
|
+
resolveTon({
|
|
405
|
+
networkId: 'tvm:-239',
|
|
406
|
+
address: 'EQton',
|
|
407
|
+
availableAddresses: [{ address: 'EQton', networkId: 'tvm:-239' }]
|
|
408
|
+
} as ConnectorResult)
|
|
409
|
+
await vi.advanceTimersByTimeAsync(10)
|
|
410
|
+
|
|
411
|
+
expect(sessionManager.getSession().connectionMode).toBe('tonConnect')
|
|
412
|
+
expect(connectionService['activeConnector']).toBe(tonConnector)
|
|
413
|
+
})
|
|
414
|
+
|
|
415
|
+
it('does not fan out one tab-hide across superseded handoffs (at most one live handoff)', async () => {
|
|
416
|
+
vi.useFakeTimers()
|
|
417
|
+
|
|
418
|
+
const hidden: unknown[] = []
|
|
419
|
+
eventManager.on('pageHiddenDuringHandoff', e => hidden.push(e))
|
|
420
|
+
|
|
421
|
+
// Fire several overlapping connects; each must supersede the previous, so
|
|
422
|
+
// only the last handoff's visibility listener remains attached.
|
|
423
|
+
for (let i = 0; i < 5; i++) {
|
|
424
|
+
connectionService.connect('walletConnect', 'tonkeeper').catch(() => {})
|
|
425
|
+
}
|
|
426
|
+
// Flush microtasks so superseded handoffs finish tearing down.
|
|
427
|
+
await vi.advanceTimersByTimeAsync(10)
|
|
428
|
+
|
|
429
|
+
Object.defineProperty(document, 'visibilityState', {
|
|
430
|
+
value: 'hidden',
|
|
431
|
+
configurable: true
|
|
432
|
+
})
|
|
433
|
+
document.dispatchEvent(new Event('visibilitychange'))
|
|
434
|
+
|
|
435
|
+
expect(hidden).toHaveLength(1)
|
|
436
|
+
})
|
|
437
|
+
})
|
|
@@ -26,6 +26,14 @@ export class ConnectionService {
|
|
|
26
26
|
private activeConnector: Connector | null = null
|
|
27
27
|
/** Tears down the post-connect lifecycle subscription; set while connected. */
|
|
28
28
|
private lifecycleTeardown: (() => void) | undefined
|
|
29
|
+
/**
|
|
30
|
+
* Abort handle for the CURRENT in-flight connect attempt. A re-entrant
|
|
31
|
+
* connect() aborts this before starting, so a superseded attempt's URI poll
|
|
32
|
+
* and handoff visibility listener are torn down instead of left running
|
|
33
|
+
* alongside the new one (MFS-602: orphaned polls/handoffs otherwise stack,
|
|
34
|
+
* each reading the shared activeConnector, and flood telemetry).
|
|
35
|
+
*/
|
|
36
|
+
private inFlightAbort: AbortController | null = null
|
|
29
37
|
|
|
30
38
|
constructor(
|
|
31
39
|
private networks: Network[],
|
|
@@ -46,18 +54,39 @@ export class ConnectionService {
|
|
|
46
54
|
): Promise<void> {
|
|
47
55
|
options?.signal?.throwIfAborted()
|
|
48
56
|
|
|
49
|
-
//
|
|
57
|
+
// Validate BEFORE touching any in-flight state. A lookup/validation failure
|
|
58
|
+
// (unknown walletId, unsupported mode, bad network) must not abort a valid
|
|
59
|
+
// prior in-flight attempt, nor leave a dangling `inFlightAbort` that a later
|
|
60
|
+
// connect() would spuriously abort (MFS-602).
|
|
50
61
|
const wallet = this.findWallet(walletId)
|
|
51
|
-
|
|
52
|
-
// Get the appropriate provider based on connection mode
|
|
53
62
|
const provider = this.getProviderForMode(wallet, connectionMode)
|
|
54
|
-
|
|
55
|
-
// Validate and determine the network
|
|
56
63
|
const network = this.validateAndDetermineNetwork(networkId, provider)
|
|
57
|
-
|
|
58
|
-
// Get the appropriate connector
|
|
59
64
|
const connector = this.getConnector(connectionMode)
|
|
60
65
|
|
|
66
|
+
// Supersede any prior in-flight attempt: abort it so its URI poll and
|
|
67
|
+
// handoff visibility listeners tear down (via the abort paths below) BEFORE
|
|
68
|
+
// this attempt starts, instead of running alongside it (MFS-602). The
|
|
69
|
+
// caller's own signal is composed in, so a caller abort still cancels this
|
|
70
|
+
// attempt while a re-entrant connect() supersedes it.
|
|
71
|
+
this.inFlightAbort?.abort(
|
|
72
|
+
new DOMException('Superseded by a newer connection attempt', 'AbortError')
|
|
73
|
+
)
|
|
74
|
+
const attemptAbort = new AbortController()
|
|
75
|
+
this.inFlightAbort = attemptAbort
|
|
76
|
+
const callerSignal = options?.signal
|
|
77
|
+
let removeCallerAbortListener: (() => void) | undefined
|
|
78
|
+
if (callerSignal) {
|
|
79
|
+
if (callerSignal.aborted) {
|
|
80
|
+
attemptAbort.abort(callerSignal.reason)
|
|
81
|
+
} else {
|
|
82
|
+
const onCallerAbort = () => attemptAbort.abort(callerSignal.reason)
|
|
83
|
+
callerSignal.addEventListener('abort', onCallerAbort, { once: true })
|
|
84
|
+
removeCallerAbortListener = () =>
|
|
85
|
+
callerSignal.removeEventListener('abort', onCallerAbort)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const signal = attemptAbort.signal
|
|
89
|
+
|
|
61
90
|
// Emit AFTER validation so a thrown lookup/validation error doesn't leave
|
|
62
91
|
// listeners seeing a "connecting" state for an attempt that never started.
|
|
63
92
|
this.eventManager.emit('connecting', { connectionMode, walletId })
|
|
@@ -76,7 +105,7 @@ export class ConnectionService {
|
|
|
76
105
|
{
|
|
77
106
|
eventManager: this.eventManager,
|
|
78
107
|
timeoutMs: this.walletResponseTimeoutMs,
|
|
79
|
-
signal
|
|
108
|
+
signal
|
|
80
109
|
},
|
|
81
110
|
{
|
|
82
111
|
operation: 'connect',
|
|
@@ -98,7 +127,7 @@ export class ConnectionService {
|
|
|
98
127
|
| ExtensionInjectedProvider
|
|
99
128
|
| IntegratedBrowserInjectedProvider
|
|
100
129
|
),
|
|
101
|
-
|
|
130
|
+
signal
|
|
102
131
|
)
|
|
103
132
|
} else if (
|
|
104
133
|
connectionMode === 'tonConnect' ||
|
|
@@ -110,9 +139,9 @@ export class ConnectionService {
|
|
|
110
139
|
network,
|
|
111
140
|
provider,
|
|
112
141
|
connectionMode,
|
|
113
|
-
|
|
142
|
+
signal
|
|
114
143
|
),
|
|
115
|
-
|
|
144
|
+
signal
|
|
116
145
|
)
|
|
117
146
|
} else {
|
|
118
147
|
throw new Error(`Unsupported connection mode: ${connectionMode}`)
|
|
@@ -123,12 +152,29 @@ export class ConnectionService {
|
|
|
123
152
|
// and this point, still bail before mutating session. Inside the try so
|
|
124
153
|
// the half-attached connector is released on this late abort too — a
|
|
125
154
|
// stale activeConnector would keep serving the aborted attempt's URI.
|
|
126
|
-
|
|
155
|
+
signal.throwIfAborted()
|
|
127
156
|
} catch (error) {
|
|
128
|
-
|
|
157
|
+
// Only release if WE are still the in-flight attempt. Gate BOTH releases
|
|
158
|
+
// on the identity-unique `attemptAbort` token, never on
|
|
159
|
+
// `this.activeConnector === connector`: `connectors` is a shared singleton
|
|
160
|
+
// Map, so a same-mode re-entrant connect() reuses the IDENTICAL connector
|
|
161
|
+
// object — a reference check would pass here and wrongly detach the newer,
|
|
162
|
+
// now-live attempt's connector, breaking its URI polling and leaving a
|
|
163
|
+
// later disconnect() unable to tear down the real wallet session (MFS-602).
|
|
164
|
+
if (this.inFlightAbort === attemptAbort) {
|
|
165
|
+
this.activeConnector = null
|
|
166
|
+
this.inFlightAbort = null
|
|
167
|
+
}
|
|
168
|
+
removeCallerAbortListener?.()
|
|
129
169
|
throw error
|
|
130
170
|
}
|
|
131
171
|
|
|
172
|
+
// This attempt reached a successful handoff without being superseded (a
|
|
173
|
+
// supersession aborts `signal`, which the belt-and-suspenders check above
|
|
174
|
+
// turns into a throw). Release the in-flight handle + caller-signal listener.
|
|
175
|
+
if (this.inFlightAbort === attemptAbort) this.inFlightAbort = null
|
|
176
|
+
removeCallerAbortListener?.()
|
|
177
|
+
|
|
132
178
|
// Filter available addresses to only include those for configured networks
|
|
133
179
|
const filteredAvailableAddresses = result.availableAddresses.filter(addr =>
|
|
134
180
|
this.networks.some(n => n.id === addr.networkId)
|
|
@@ -257,7 +303,18 @@ export class ConnectionService {
|
|
|
257
303
|
let emittedUri: string | undefined
|
|
258
304
|
|
|
259
305
|
const pollForURI = setInterval(() => {
|
|
260
|
-
|
|
306
|
+
// Read the URI from THIS attempt's own connector, never the shared
|
|
307
|
+
// this.activeConnector: a still-running poll from a superseded attempt
|
|
308
|
+
// would otherwise read — and emit — a newer attempt's URI under this
|
|
309
|
+
// attempt's connectionMode (MFS-602 cross-talk). Mirror the public
|
|
310
|
+
// getConnectionURI guard: the getter is optional and may throw before a
|
|
311
|
+
// URI exists.
|
|
312
|
+
let uri: string | undefined
|
|
313
|
+
try {
|
|
314
|
+
uri = connector.getConnectionURI?.()
|
|
315
|
+
} catch {
|
|
316
|
+
uri = undefined
|
|
317
|
+
}
|
|
261
318
|
if (uri && uri !== emittedUri) {
|
|
262
319
|
emittedUri = uri
|
|
263
320
|
this.eventManager.emit('connectionUri', { uri, connectionMode })
|
|
@@ -409,6 +466,15 @@ export class ConnectionService {
|
|
|
409
466
|
|
|
410
467
|
async disconnect(options?: ServiceOptions): Promise<void> {
|
|
411
468
|
options?.signal?.throwIfAborted()
|
|
469
|
+
// Cancel any in-flight connect() first. Without this, a wallet that responds
|
|
470
|
+
// AFTER the user disconnects would let the pending connect() proceed past its
|
|
471
|
+
// `signal.throwIfAborted()` guard into updateSession()/`connected`, resurrecting
|
|
472
|
+
// the session just torn down. Aborting the shared in-flight token makes that
|
|
473
|
+
// attempt bail via raceAbort (its late result is discarded). No-op on the
|
|
474
|
+
// normal path: a settled connect() already nulled `inFlightAbort` (MFS-602).
|
|
475
|
+
this.inFlightAbort?.abort(
|
|
476
|
+
new DOMException('Connection cancelled by disconnect', 'AbortError')
|
|
477
|
+
)
|
|
412
478
|
// Remove the lifecycle listeners first so a provider 'disconnect' fired during
|
|
413
479
|
// teardown doesn't re-enter as a wallet-initiated `walletDisconnected`.
|
|
414
480
|
this.teardownLifecycle()
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated from package.json by scripts/generate-version.mjs — do not edit.
|
|
2
|
-
export const UWC_CORE_VERSION = '1.1.
|
|
2
|
+
export const UWC_CORE_VERSION = '1.1.2'
|