@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.
@@ -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;IAM1B,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;IAZjC,OAAO,CAAC,eAAe,CAAyB;IAChD,+EAA+E;IAC/E,OAAO,CAAC,iBAAiB,CAA0B;gBAGzC,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;IAiHhB,qFAAqF;IACrF,OAAO,CAAC,eAAe;IA8CvB;;;;;;OAMG;IACH,iBAAiB,IAAI,IAAI;IAKzB;;;;;;;OAOG;IACH,iBAAiB,IAAI,IAAI;YAqBX,kBAAkB;IAqDhC,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;IAqBzD,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"}
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
- // Find the wallet
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: options?.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), options?.signal);
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, options?.signal), options?.signal);
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
- options?.signal?.throwIfAborted();
108
+ signal.throwIfAborted();
80
109
  }
81
110
  catch (error) {
82
- this.activeConnector = null;
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
- const uri = this.getConnectionURI();
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";
1
+ export declare const UWC_CORE_VERSION = "1.1.2";
2
2
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // Generated from package.json by scripts/generate-version.mjs — do not edit.
2
- export const UWC_CORE_VERSION = '1.1.1';
2
+ export const UWC_CORE_VERSION = '1.1.2';
3
3
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meshconnect/uwc-core",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Core functionality for Universal Wallet Connector",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -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
- // Find the wallet
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: options?.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
- options?.signal
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
- options?.signal
142
+ signal
114
143
  ),
115
- options?.signal
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
- options?.signal?.throwIfAborted()
155
+ signal.throwIfAborted()
127
156
  } catch (error) {
128
- this.activeConnector = null
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
- const uri = this.getConnectionURI()
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.1'
2
+ export const UWC_CORE_VERSION = '1.1.2'