@formo/analytics 1.38.0 → 1.38.1
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/cjs/src/FormoAnalytics.d.ts +6 -4
- package/dist/cjs/src/FormoAnalytics.js +27 -6
- package/dist/cjs/src/FormoAnalyticsProvider.d.ts +8 -0
- package/dist/cjs/src/FormoAnalyticsProvider.js +41 -32
- package/dist/cjs/src/evm/EvmRequestTracker.d.ts +12 -0
- package/dist/cjs/src/evm/EvmRequestTracker.js +28 -12
- package/dist/cjs/src/version.d.ts +1 -1
- package/dist/cjs/src/version.js +1 -1
- package/dist/cjs/src/wagmi/WagmiEventHandler.d.ts +37 -0
- package/dist/cjs/src/wagmi/WagmiEventHandler.js +207 -39
- package/dist/esm/src/FormoAnalytics.d.ts +6 -4
- package/dist/esm/src/FormoAnalytics.js +27 -6
- package/dist/esm/src/FormoAnalyticsProvider.d.ts +8 -0
- package/dist/esm/src/FormoAnalyticsProvider.js +39 -31
- package/dist/esm/src/evm/EvmRequestTracker.d.ts +12 -0
- package/dist/esm/src/evm/EvmRequestTracker.js +28 -12
- package/dist/esm/src/version.d.ts +1 -1
- package/dist/esm/src/version.js +1 -1
- package/dist/esm/src/wagmi/WagmiEventHandler.d.ts +37 -0
- package/dist/esm/src/wagmi/WagmiEventHandler.js +207 -39
- package/dist/index.umd.min.js +1 -1
- package/package.json +2 -2
|
@@ -402,9 +402,9 @@ export declare class FormoAnalytics implements IFormoAnalytics {
|
|
|
402
402
|
*
|
|
403
403
|
* With several live SDK instances (multi write-key pages) registering
|
|
404
404
|
* the SAME provider, request-derived events (signatures, transactions)
|
|
405
|
-
* go to the most recently
|
|
406
|
-
*
|
|
407
|
-
* wrapper. Lifecycle events (connect, chain, disconnect) reach every
|
|
405
|
+
* go to the most recently CREATED live instance, regardless of the
|
|
406
|
+
* order registrations happen to land in - the same single-observer
|
|
407
|
+
* semantics discovery has always had for the request wrapper. Lifecycle events (connect, chain, disconnect) reach every
|
|
408
408
|
* instance. Fanning request observations out to all instances is a
|
|
409
409
|
* separate feature.
|
|
410
410
|
*
|
|
@@ -429,7 +429,9 @@ export declare class FormoAnalytics implements IFormoAnalytics {
|
|
|
429
429
|
* request wrapper installs here. Double counting is prevented in the
|
|
430
430
|
* wrapper via `shouldSkipRequestCapture`.
|
|
431
431
|
*/
|
|
432
|
-
_wrapWagmiProvider(provider: EIP1193Provider):
|
|
432
|
+
_wrapWagmiProvider(provider: EIP1193Provider, chainId?: number): boolean;
|
|
433
|
+
/** INTERNAL. Chain updates for the fallback-wrapped provider. */
|
|
434
|
+
_rememberWagmiProviderChain(provider: EIP1193Provider, chainId: number | undefined): void;
|
|
433
435
|
registerProvider(provider: EIP1193Provider, info?: {
|
|
434
436
|
name?: string;
|
|
435
437
|
rdns?: string;
|
|
@@ -1334,9 +1334,9 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1334
1334
|
*
|
|
1335
1335
|
* With several live SDK instances (multi write-key pages) registering
|
|
1336
1336
|
* the SAME provider, request-derived events (signatures, transactions)
|
|
1337
|
-
* go to the most recently
|
|
1338
|
-
*
|
|
1339
|
-
* wrapper. Lifecycle events (connect, chain, disconnect) reach every
|
|
1337
|
+
* go to the most recently CREATED live instance, regardless of the
|
|
1338
|
+
* order registrations happen to land in - the same single-observer
|
|
1339
|
+
* semantics discovery has always had for the request wrapper. Lifecycle events (connect, chain, disconnect) reach every
|
|
1340
1340
|
* instance. Fanning request observations out to all instances is a
|
|
1341
1341
|
* separate feature.
|
|
1342
1342
|
*
|
|
@@ -1361,14 +1361,35 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1361
1361
|
* request wrapper installs here. Double counting is prevented in the
|
|
1362
1362
|
* wrapper via `shouldSkipRequestCapture`.
|
|
1363
1363
|
*/
|
|
1364
|
-
FormoAnalytics.prototype._wrapWagmiProvider = function (provider) {
|
|
1364
|
+
FormoAnalytics.prototype._wrapWagmiProvider = function (provider, chainId) {
|
|
1365
1365
|
if (this.isCleanedUp || !isValidProvider(provider))
|
|
1366
|
-
return;
|
|
1366
|
+
return false;
|
|
1367
1367
|
try {
|
|
1368
|
-
|
|
1368
|
+
// The tracker reports refusals (frozen provider, unrebindable
|
|
1369
|
+
// wrapper) by returning false rather than throwing; treat those as
|
|
1370
|
+
// failures too so the caller can retry later.
|
|
1371
|
+
if (!this.evmRequests.registerRequestListeners(provider)) {
|
|
1372
|
+
return false;
|
|
1373
|
+
}
|
|
1374
|
+
if (chainId !== undefined) {
|
|
1375
|
+
this.evm.rememberChain(provider, chainId);
|
|
1376
|
+
}
|
|
1377
|
+
return true;
|
|
1369
1378
|
}
|
|
1370
1379
|
catch (e) {
|
|
1371
1380
|
logger.warn("Failed to wrap wagmi provider for hybrid capture", e);
|
|
1381
|
+
return false;
|
|
1382
|
+
}
|
|
1383
|
+
};
|
|
1384
|
+
/** INTERNAL. Chain updates for the fallback-wrapped provider. */
|
|
1385
|
+
FormoAnalytics.prototype._rememberWagmiProviderChain = function (provider, chainId) {
|
|
1386
|
+
if (this.isCleanedUp)
|
|
1387
|
+
return;
|
|
1388
|
+
try {
|
|
1389
|
+
this.evm.rememberChain(provider, chainId);
|
|
1390
|
+
}
|
|
1391
|
+
catch (_a) {
|
|
1392
|
+
/* bookkeeping only */
|
|
1372
1393
|
}
|
|
1373
1394
|
};
|
|
1374
1395
|
FormoAnalytics.prototype.registerProvider = function (provider, info) {
|
|
@@ -7,6 +7,14 @@ export interface FormoAnalyticsProviderProps {
|
|
|
7
7
|
children: ReactNode;
|
|
8
8
|
}
|
|
9
9
|
export declare const FormoAnalyticsContext: import("react").Context<IFormoAnalytics>;
|
|
10
|
+
/**
|
|
11
|
+
* A stable key over the serializable parts of Options. The provider effect
|
|
12
|
+
* re-initialises the SDK when this key changes; anything that alters SDK
|
|
13
|
+
* behaviour must be represented here, or a runtime change to it is silently
|
|
14
|
+
* ignored. Complex objects are tracked by presence, plus the flags that
|
|
15
|
+
* change what the SDK does with them.
|
|
16
|
+
*/
|
|
17
|
+
export declare const computeOptionsKey: (options?: Options) => string;
|
|
10
18
|
export declare const FormoAnalyticsProvider: FC<FormoAnalyticsProviderProps>;
|
|
11
19
|
export declare const useFormo: () => IFormoAnalytics;
|
|
12
20
|
//# sourceMappingURL=FormoAnalyticsProvider.d.ts.map
|
|
@@ -69,6 +69,44 @@ var defaultContext = {
|
|
|
69
69
|
hasOptedOutTracking: function () { return false; },
|
|
70
70
|
};
|
|
71
71
|
export var FormoAnalyticsContext = createContext(defaultContext);
|
|
72
|
+
/**
|
|
73
|
+
* A stable key over the serializable parts of Options. The provider effect
|
|
74
|
+
* re-initialises the SDK when this key changes; anything that alters SDK
|
|
75
|
+
* behaviour must be represented here, or a runtime change to it is silently
|
|
76
|
+
* ignored. Complex objects are tracked by presence, plus the flags that
|
|
77
|
+
* change what the SDK does with them.
|
|
78
|
+
*/
|
|
79
|
+
export var computeOptionsKey = function (options) {
|
|
80
|
+
var _a;
|
|
81
|
+
if (!options)
|
|
82
|
+
return 'undefined';
|
|
83
|
+
var serializableOptions = {
|
|
84
|
+
tracking: options.tracking,
|
|
85
|
+
autocapture: options.autocapture,
|
|
86
|
+
crossSubdomainCookies: options.crossSubdomainCookies,
|
|
87
|
+
apiHost: options.apiHost,
|
|
88
|
+
flushAt: options.flushAt,
|
|
89
|
+
flushInterval: options.flushInterval,
|
|
90
|
+
retryCount: options.retryCount,
|
|
91
|
+
maxQueueSize: options.maxQueueSize,
|
|
92
|
+
logger: options.logger,
|
|
93
|
+
referral: options.referral,
|
|
94
|
+
evm: options.evm,
|
|
95
|
+
// For complex objects, just track their presence, not their content
|
|
96
|
+
hasProvider: !!options.provider,
|
|
97
|
+
hasWagmi: !!options.wagmi,
|
|
98
|
+
wagmiEip1193Fallback: !!((_a = options.wagmi) === null || _a === void 0 ? void 0 : _a.eip1193Fallback),
|
|
99
|
+
hasReady: !!options.ready,
|
|
100
|
+
};
|
|
101
|
+
try {
|
|
102
|
+
return JSON.stringify(serializableOptions);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
// Fallback to timestamp if serialization fails
|
|
106
|
+
logger.warn('Failed to serialize options, using timestamp', error);
|
|
107
|
+
return Date.now().toString();
|
|
108
|
+
}
|
|
109
|
+
};
|
|
72
110
|
export var FormoAnalyticsProvider = function (props) {
|
|
73
111
|
var writeKey = props.writeKey, _a = props.disabled, disabled = _a === void 0 ? false : _a, children = props.children;
|
|
74
112
|
// Keep the app running without analytics if no Write Key is provided or disabled
|
|
@@ -87,37 +125,7 @@ var InitializedAnalytics = function (_a) {
|
|
|
87
125
|
var _b = useState(defaultContext), sdk = _b[0], setSdk = _b[1];
|
|
88
126
|
var sdkRef = useRef(defaultContext);
|
|
89
127
|
initStorageManager(writeKey);
|
|
90
|
-
|
|
91
|
-
// We only care about serializable config values that would affect SDK behavior
|
|
92
|
-
var optionsKey = useMemo(function () {
|
|
93
|
-
if (!options)
|
|
94
|
-
return 'undefined';
|
|
95
|
-
// Extract only the serializable parts of options
|
|
96
|
-
var serializableOptions = {
|
|
97
|
-
tracking: options.tracking,
|
|
98
|
-
autocapture: options.autocapture,
|
|
99
|
-
crossSubdomainCookies: options.crossSubdomainCookies,
|
|
100
|
-
apiHost: options.apiHost,
|
|
101
|
-
flushAt: options.flushAt,
|
|
102
|
-
flushInterval: options.flushInterval,
|
|
103
|
-
retryCount: options.retryCount,
|
|
104
|
-
maxQueueSize: options.maxQueueSize,
|
|
105
|
-
logger: options.logger,
|
|
106
|
-
referral: options.referral,
|
|
107
|
-
// For complex objects, just track their presence, not their content
|
|
108
|
-
hasProvider: !!options.provider,
|
|
109
|
-
hasWagmi: !!options.wagmi,
|
|
110
|
-
hasReady: !!options.ready,
|
|
111
|
-
};
|
|
112
|
-
try {
|
|
113
|
-
return JSON.stringify(serializableOptions);
|
|
114
|
-
}
|
|
115
|
-
catch (error) {
|
|
116
|
-
// Fallback to timestamp if serialization fails
|
|
117
|
-
logger.warn('Failed to serialize options, using timestamp', error);
|
|
118
|
-
return Date.now().toString();
|
|
119
|
-
}
|
|
120
|
-
}, [options]);
|
|
128
|
+
var optionsKey = useMemo(function () { return computeOptionsKey(options); }, [options]);
|
|
121
129
|
useEffect(function () {
|
|
122
130
|
var isCleanedUp = false;
|
|
123
131
|
var initialize = function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
@@ -57,6 +57,18 @@ export declare class EvmRequestTracker {
|
|
|
57
57
|
* never retry it, and every signature and transaction from that wallet
|
|
58
58
|
* would be missed for the rest of the session.
|
|
59
59
|
*/
|
|
60
|
+
/** Monotonic creation stamp; owner precedence is decided by it. */
|
|
61
|
+
private static nextCreationSeq;
|
|
62
|
+
readonly creationSeq: number;
|
|
63
|
+
/**
|
|
64
|
+
* Put this tracker into an owner list at its CREATION-ORDER position.
|
|
65
|
+
* Registration can arrive out of order - an older instance's async wrap
|
|
66
|
+
* kick may resolve after a newer instance's - and dispatch picks from
|
|
67
|
+
* the END of the list, so append-on-registration would let resolution
|
|
68
|
+
* order decide which instance owns capture. Creation order is what the
|
|
69
|
+
* newest-live contract promises.
|
|
70
|
+
*/
|
|
71
|
+
private insertBySeniority;
|
|
60
72
|
registerRequestListeners(provider: EIP1193Provider): boolean;
|
|
61
73
|
/** Install the wrapper function onto the provider; separated so the
|
|
62
74
|
* routing shim above stays small. */
|
|
@@ -137,6 +137,7 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
137
137
|
this.disposed = false;
|
|
138
138
|
/** Providers whose owner list includes this instance; pruned on cleanup. */
|
|
139
139
|
this.wrappedProviders = new Set();
|
|
140
|
+
this.creationSeq = EvmRequestTracker.nextCreationSeq++;
|
|
140
141
|
}
|
|
141
142
|
/** Stop every poll in flight. Terminal, like the event queue's close(). */
|
|
142
143
|
EvmRequestTracker.prototype.cleanup = function () {
|
|
@@ -170,11 +171,24 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
170
171
|
this.polls.add(timer);
|
|
171
172
|
};
|
|
172
173
|
/**
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
174
|
+
* Put this tracker into an owner list at its CREATION-ORDER position.
|
|
175
|
+
* Registration can arrive out of order - an older instance's async wrap
|
|
176
|
+
* kick may resolve after a newer instance's - and dispatch picks from
|
|
177
|
+
* the END of the list, so append-on-registration would let resolution
|
|
178
|
+
* order decide which instance owns capture. Creation order is what the
|
|
179
|
+
* newest-live contract promises.
|
|
177
180
|
*/
|
|
181
|
+
EvmRequestTracker.prototype.insertBySeniority = function (owners) {
|
|
182
|
+
var _this = this;
|
|
183
|
+
var idx = owners.indexOf(this);
|
|
184
|
+
if (idx !== -1)
|
|
185
|
+
owners.splice(idx, 1);
|
|
186
|
+
var insertAt = owners.findIndex(function (o) { return o.creationSeq > _this.creationSeq; });
|
|
187
|
+
if (insertAt === -1)
|
|
188
|
+
owners.push(this);
|
|
189
|
+
else
|
|
190
|
+
owners.splice(insertAt, 0, this);
|
|
191
|
+
};
|
|
178
192
|
EvmRequestTracker.prototype.registerRequestListeners = function (provider) {
|
|
179
193
|
var _this = this;
|
|
180
194
|
logger.info("registerRequestListeners");
|
|
@@ -196,10 +210,7 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
196
210
|
logger.warn("wrapped without owner list; cannot rebind");
|
|
197
211
|
return false;
|
|
198
212
|
}
|
|
199
|
-
|
|
200
|
-
if (idx !== -1)
|
|
201
|
-
owners.splice(idx, 1);
|
|
202
|
-
owners.push(this);
|
|
213
|
+
this.insertBySeniority(owners);
|
|
203
214
|
this.wrappedProviders.add(provider);
|
|
204
215
|
logger.info("Provider already wrapped; rebinding the wrapper to this instance.");
|
|
205
216
|
return true;
|
|
@@ -240,10 +251,7 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
240
251
|
var slot = provider;
|
|
241
252
|
var prior = slot[WRAPPED_REQUEST_OWNER_SYMBOL];
|
|
242
253
|
var owners = Array.isArray(prior) ? prior : [];
|
|
243
|
-
|
|
244
|
-
if (idx !== -1)
|
|
245
|
-
owners.splice(idx, 1);
|
|
246
|
-
owners.push(this);
|
|
254
|
+
this.insertBySeniority(owners);
|
|
247
255
|
if (!Array.isArray(prior)) {
|
|
248
256
|
slot[WRAPPED_REQUEST_OWNER_SYMBOL] = owners;
|
|
249
257
|
}
|
|
@@ -848,6 +856,14 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
848
856
|
});
|
|
849
857
|
});
|
|
850
858
|
};
|
|
859
|
+
/**
|
|
860
|
+
* Wrap a provider's `request`. Returns whether the wrapper is installed:
|
|
861
|
+
* a caller that marks the provider as tracked on a false return would
|
|
862
|
+
* never retry it, and every signature and transaction from that wallet
|
|
863
|
+
* would be missed for the rest of the session.
|
|
864
|
+
*/
|
|
865
|
+
/** Monotonic creation stamp; owner precedence is decided by it. */
|
|
866
|
+
EvmRequestTracker.nextCreationSeq = 0;
|
|
851
867
|
return EvmRequestTracker;
|
|
852
868
|
}());
|
|
853
869
|
export { EvmRequestTracker };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const version = "1.38.
|
|
1
|
+
export declare const version = "1.38.1";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/esm/src/version.js
CHANGED
|
@@ -362,6 +362,43 @@ export declare class WagmiEventHandler {
|
|
|
362
362
|
* dedup. Fire-and-forget per connection; a provider that cannot be
|
|
363
363
|
* produced simply keeps mutation-only capture.
|
|
364
364
|
*/
|
|
365
|
+
/**
|
|
366
|
+
* Connections THIS instance has wrapped. Deliberately per-instance, not
|
|
367
|
+
* module-level: a module-level guard let a rebuilt SDK instance skip
|
|
368
|
+
* re-wrapping, so ownership stayed with the torn-down instance and every
|
|
369
|
+
* capture died in its closed queue - and it also stopped a second
|
|
370
|
+
* write-key instance from ever registering. Re-wrapping is safe and
|
|
371
|
+
* cheap: the request tracker rebinds ownership of an intact wrapper.
|
|
372
|
+
*/
|
|
373
|
+
/**
|
|
374
|
+
* Latest wrap attempt per connector. Every kick re-resolves
|
|
375
|
+
* getProvider() - a connector can hand out a REPLACEMENT provider after
|
|
376
|
+
* a reconnect, so a once-only guard would leave the new session's
|
|
377
|
+
* provider unwrapped forever. Re-wrapping is idempotent: the request
|
|
378
|
+
* tracker rebinds ownership of an intact wrapper. The epoch lets a slow
|
|
379
|
+
* resolution from a superseded kick (an earlier session, an earlier
|
|
380
|
+
* chain) be discarded instead of overwriting fresher state.
|
|
381
|
+
*/
|
|
382
|
+
private wrapEpochs;
|
|
383
|
+
/** Consecutive automatic wrap retries per connector; see below. */
|
|
384
|
+
private wrapRetryCounts;
|
|
385
|
+
/** Pending retry timers, cancelled by cleanup(). */
|
|
386
|
+
private wrapRetryTimers;
|
|
387
|
+
/**
|
|
388
|
+
* Bumped on every observed disconnect. Every wrap attempt captures it
|
|
389
|
+
* at kick time and its resolution stands down on a mismatch, so a
|
|
390
|
+
* disconnect invalidates ALL pending wraps at once - including ones
|
|
391
|
+
* whose getProvider() had not resolved yet, which no per-connector
|
|
392
|
+
* bookkeeping can reach because nothing has been recorded for them.
|
|
393
|
+
*/
|
|
394
|
+
private wrapSessionGeneration;
|
|
395
|
+
/** The active connector this instance wrapped, and its provider, for
|
|
396
|
+
* chain updates. Kept as a pair so a chain report is only ever applied
|
|
397
|
+
* to the provider of the connector it describes. Keyed by connector,
|
|
398
|
+
* not by connection record: wagmi REPLACES the connection object on
|
|
399
|
+
* every account or chain update, so record identity is not stable. */
|
|
400
|
+
private fallbackConnector?;
|
|
401
|
+
private fallbackProvider?;
|
|
365
402
|
private wrapActiveConnectorProvider;
|
|
366
403
|
private kickWalletConnectPeerLookup;
|
|
367
404
|
/**
|
|
@@ -238,8 +238,6 @@ var walletConnectPeerLookups = new WeakSet();
|
|
|
238
238
|
* newest kicked one, so a slow resolution from a PREVIOUS session cannot
|
|
239
239
|
* land after the current session's and overwrite it. */
|
|
240
240
|
var walletConnectPeerLatest = new WeakMap();
|
|
241
|
-
/** Connections whose provider already has the hybrid-capture wrapper. */
|
|
242
|
-
var wagmiWrappedConnections = new WeakSet();
|
|
243
241
|
/**
|
|
244
242
|
* Pending transactions, shared per destination rather than per handler.
|
|
245
243
|
*
|
|
@@ -424,6 +422,57 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
424
422
|
* Key format: `${queryHash}:${status}`
|
|
425
423
|
*/
|
|
426
424
|
this.processedQueries = new Set();
|
|
425
|
+
/**
|
|
426
|
+
* Start resolving the wallet behind a WalletConnect connection.
|
|
427
|
+
*
|
|
428
|
+
* Fire-and-forget on purpose: emission paths are synchronous by design
|
|
429
|
+
* and must never wait (see the connect marker comment). Called at the
|
|
430
|
+
* START of the status/address flows rather than only at read time - the
|
|
431
|
+
* lookup is one microtask for an initialised connector, and the emission
|
|
432
|
+
* sits behind several awaits, so kicking early usually means even the
|
|
433
|
+
* FIRST connect names the real wallet. When the race is lost the event
|
|
434
|
+
* honestly says "WalletConnect" and every later event names the peer.
|
|
435
|
+
*/
|
|
436
|
+
/**
|
|
437
|
+
* Install the request wrapper on the active connector's provider.
|
|
438
|
+
*
|
|
439
|
+
* This is what lets wagmi mode capture IMPERATIVE viem calls
|
|
440
|
+
* (walletClient.sendTransaction / .signMessage / .writeContract / raw
|
|
441
|
+
* request), which create no mutation and were silently lost. Hook-driven
|
|
442
|
+
* calls stay owned by the mutation handlers via the pending-mutation
|
|
443
|
+
* dedup. Fire-and-forget per connection; a provider that cannot be
|
|
444
|
+
* produced simply keeps mutation-only capture.
|
|
445
|
+
*/
|
|
446
|
+
/**
|
|
447
|
+
* Connections THIS instance has wrapped. Deliberately per-instance, not
|
|
448
|
+
* module-level: a module-level guard let a rebuilt SDK instance skip
|
|
449
|
+
* re-wrapping, so ownership stayed with the torn-down instance and every
|
|
450
|
+
* capture died in its closed queue - and it also stopped a second
|
|
451
|
+
* write-key instance from ever registering. Re-wrapping is safe and
|
|
452
|
+
* cheap: the request tracker rebinds ownership of an intact wrapper.
|
|
453
|
+
*/
|
|
454
|
+
/**
|
|
455
|
+
* Latest wrap attempt per connector. Every kick re-resolves
|
|
456
|
+
* getProvider() - a connector can hand out a REPLACEMENT provider after
|
|
457
|
+
* a reconnect, so a once-only guard would leave the new session's
|
|
458
|
+
* provider unwrapped forever. Re-wrapping is idempotent: the request
|
|
459
|
+
* tracker rebinds ownership of an intact wrapper. The epoch lets a slow
|
|
460
|
+
* resolution from a superseded kick (an earlier session, an earlier
|
|
461
|
+
* chain) be discarded instead of overwriting fresher state.
|
|
462
|
+
*/
|
|
463
|
+
this.wrapEpochs = new WeakMap();
|
|
464
|
+
/** Consecutive automatic wrap retries per connector; see below. */
|
|
465
|
+
this.wrapRetryCounts = new WeakMap();
|
|
466
|
+
/** Pending retry timers, cancelled by cleanup(). */
|
|
467
|
+
this.wrapRetryTimers = new Set();
|
|
468
|
+
/**
|
|
469
|
+
* Bumped on every observed disconnect. Every wrap attempt captures it
|
|
470
|
+
* at kick time and its resolution stands down on a mismatch, so a
|
|
471
|
+
* disconnect invalidates ALL pending wraps at once - including ones
|
|
472
|
+
* whose getProvider() had not resolved yet, which no per-connector
|
|
473
|
+
* bookkeeping can reach because nothing has been recorded for them.
|
|
474
|
+
*/
|
|
475
|
+
this.wrapSessionGeneration = 0;
|
|
427
476
|
this.formo = formoAnalytics;
|
|
428
477
|
this.wagmiConfig = wagmiConfig;
|
|
429
478
|
this.queryClient = queryClient;
|
|
@@ -598,6 +647,10 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
598
647
|
var connectionUnsubscribe = this.wagmiConfig.subscribe(function (state) { var _a, _b; return "".concat((_a = state.current) !== null && _a !== void 0 ? _a : "", "|").concat((_b = _this.getConnectedAddress(state)) !== null && _b !== void 0 ? _b : ""); }, function (key, prevKey) {
|
|
599
648
|
var _a = key.split("|"), address = _a[1];
|
|
600
649
|
var _b = (prevKey !== null && prevKey !== void 0 ? prevKey : "|").split("|"), prevAddress = _b[1];
|
|
650
|
+
// A connector switch keeps status "connected", so the status
|
|
651
|
+
// subscription never re-wraps; the NEW active connection's provider
|
|
652
|
+
// must be instrumented from here or its imperative calls are lost.
|
|
653
|
+
_this.wrapActiveConnectorProvider(_this.getState());
|
|
601
654
|
_this.handleActiveAddressChange(address || undefined, prevAddress || undefined);
|
|
602
655
|
});
|
|
603
656
|
this.unsubscribers.push(connectionUnsubscribe);
|
|
@@ -903,6 +956,19 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
903
956
|
return __generator(this, function (_e) {
|
|
904
957
|
switch (_e.label) {
|
|
905
958
|
case 0:
|
|
959
|
+
if (status === "disconnected" || status === "reconnecting") {
|
|
960
|
+
// The wrapped session is over - or, for "reconnecting", about to be
|
|
961
|
+
// replaced without ever passing through "disconnected". This must
|
|
962
|
+
// run for EVERY such transition, before the processing lock and
|
|
963
|
+
// regardless of whether a wallet was being tracked, or a rapid
|
|
964
|
+
// same-connector reconnect retains the old provider and the chain
|
|
965
|
+
// callback writes the new session's chain onto it. The generation
|
|
966
|
+
// bump invalidates every wrap still in flight from the ended
|
|
967
|
+
// session, resolved or not. The "connected" that follows re-wraps.
|
|
968
|
+
this.wrapSessionGeneration += 1;
|
|
969
|
+
this.fallbackConnector = undefined;
|
|
970
|
+
this.fallbackProvider = undefined;
|
|
971
|
+
}
|
|
906
972
|
// Prevent concurrent processing
|
|
907
973
|
if (this.trackingState.isProcessing) {
|
|
908
974
|
// Dropped, not queued - but remember that a disconnect went past.
|
|
@@ -1439,10 +1505,31 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
1439
1505
|
*/
|
|
1440
1506
|
WagmiEventHandler.prototype.handleChainChange = function (chainId, prevChainId) {
|
|
1441
1507
|
return __awaiter(this, void 0, void 0, function () {
|
|
1442
|
-
var state, address, hadAddress, wasAnnounced, announcedNow;
|
|
1443
|
-
|
|
1444
|
-
|
|
1508
|
+
var live, activeConnector, state, address, hadAddress, wasAnnounced, announcedNow;
|
|
1509
|
+
var _a, _b, _c;
|
|
1510
|
+
return __generator(this, function (_d) {
|
|
1511
|
+
switch (_d.label) {
|
|
1445
1512
|
case 0:
|
|
1513
|
+
// Keep the fallback-wrapped provider's registry chain in step with the
|
|
1514
|
+
// store, so request-derived events are labelled correctly even when the
|
|
1515
|
+
// provider exposes no synchronous chainId. Only when the report is
|
|
1516
|
+
// about the connection that provider belongs to: after a connector
|
|
1517
|
+
// switch the store's chain describes the NEW connection, and writing it
|
|
1518
|
+
// to the old provider would mislabel that wallet's events.
|
|
1519
|
+
if (this.fallbackProvider !== undefined) {
|
|
1520
|
+
live = this.getState();
|
|
1521
|
+
activeConnector = live.current
|
|
1522
|
+
? (_a = live.connections.get(live.current)) === null || _a === void 0 ? void 0 : _a.connector
|
|
1523
|
+
: undefined;
|
|
1524
|
+
if (activeConnector === this.fallbackConnector) {
|
|
1525
|
+
try {
|
|
1526
|
+
(_c = (_b = this.formo)._rememberWagmiProviderChain) === null || _c === void 0 ? void 0 : _c.call(_b, this.fallbackProvider, chainId);
|
|
1527
|
+
}
|
|
1528
|
+
catch (_e) {
|
|
1529
|
+
/* never let bookkeeping break the chain flow */
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1446
1533
|
if (chainId === prevChainId || chainId === undefined) {
|
|
1447
1534
|
return [2 /*return*/];
|
|
1448
1535
|
}
|
|
@@ -1488,7 +1575,7 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
1488
1575
|
});
|
|
1489
1576
|
return [4 /*yield*/, this.applyChainForTrackedWallet(address, chainId)];
|
|
1490
1577
|
case 1:
|
|
1491
|
-
|
|
1578
|
+
_d.sent();
|
|
1492
1579
|
return [2 /*return*/];
|
|
1493
1580
|
}
|
|
1494
1581
|
});
|
|
@@ -2267,30 +2354,10 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
2267
2354
|
this.kickWalletConnectPeerLookup(state);
|
|
2268
2355
|
return connector.name;
|
|
2269
2356
|
};
|
|
2270
|
-
|
|
2271
|
-
* Start resolving the wallet behind a WalletConnect connection.
|
|
2272
|
-
*
|
|
2273
|
-
* Fire-and-forget on purpose: emission paths are synchronous by design
|
|
2274
|
-
* and must never wait (see the connect marker comment). Called at the
|
|
2275
|
-
* START of the status/address flows rather than only at read time - the
|
|
2276
|
-
* lookup is one microtask for an initialised connector, and the emission
|
|
2277
|
-
* sits behind several awaits, so kicking early usually means even the
|
|
2278
|
-
* FIRST connect names the real wallet. When the race is lost the event
|
|
2279
|
-
* honestly says "WalletConnect" and every later event names the peer.
|
|
2280
|
-
*/
|
|
2281
|
-
/**
|
|
2282
|
-
* Install the request wrapper on the active connector's provider.
|
|
2283
|
-
*
|
|
2284
|
-
* This is what lets wagmi mode capture IMPERATIVE viem calls
|
|
2285
|
-
* (walletClient.sendTransaction / .signMessage / .writeContract / raw
|
|
2286
|
-
* request), which create no mutation and were silently lost. Hook-driven
|
|
2287
|
-
* calls stay owned by the mutation handlers via the pending-mutation
|
|
2288
|
-
* dedup. Fire-and-forget per connection; a provider that cannot be
|
|
2289
|
-
* produced simply keeps mutation-only capture.
|
|
2290
|
-
*/
|
|
2291
|
-
WagmiEventHandler.prototype.wrapActiveConnectorProvider = function (state) {
|
|
2357
|
+
WagmiEventHandler.prototype.wrapActiveConnectorProvider = function (state, isRetry) {
|
|
2292
2358
|
var _this = this;
|
|
2293
|
-
var _a, _b;
|
|
2359
|
+
var _a, _b, _c;
|
|
2360
|
+
if (isRetry === void 0) { isRetry = false; }
|
|
2294
2361
|
// OPT-IN only. Wagmi mode's baseline never touches the signing
|
|
2295
2362
|
// transport; instrumenting the provider is an explicit integrator
|
|
2296
2363
|
// decision (options.wagmi.eip1193Fallback), made auditable in
|
|
@@ -2299,25 +2366,124 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
2299
2366
|
if (!optedIn) {
|
|
2300
2367
|
return;
|
|
2301
2368
|
}
|
|
2369
|
+
if (state.status !== "connected") {
|
|
2370
|
+
// Status kicks run for every transition; only a connected snapshot
|
|
2371
|
+
// describes a session worth wrapping.
|
|
2372
|
+
return;
|
|
2373
|
+
}
|
|
2302
2374
|
var connection = state.current
|
|
2303
2375
|
? state.connections.get(state.current)
|
|
2304
2376
|
: undefined;
|
|
2305
2377
|
var connector = connection === null || connection === void 0 ? void 0 : connection.connector;
|
|
2306
|
-
if (!connection ||
|
|
2307
|
-
typeof (connector === null || connector === void 0 ? void 0 : connector.getProvider) !== "function" ||
|
|
2308
|
-
wagmiWrappedConnections.has(connection)) {
|
|
2378
|
+
if (!connection || typeof (connector === null || connector === void 0 ? void 0 : connector.getProvider) !== "function") {
|
|
2309
2379
|
return;
|
|
2310
2380
|
}
|
|
2311
|
-
|
|
2312
|
-
connector
|
|
2313
|
-
|
|
2381
|
+
var epoch = ((_c = this.wrapEpochs.get(connector)) !== null && _c !== void 0 ? _c : 0) + 1;
|
|
2382
|
+
this.wrapEpochs.set(connector, epoch);
|
|
2383
|
+
var session = this.wrapSessionGeneration;
|
|
2384
|
+
// A store-driven kick resets the retry budget; automatic retries
|
|
2385
|
+
// (below) spend it.
|
|
2386
|
+
if (!isRetry) {
|
|
2387
|
+
this.wrapRetryCounts.delete(connector);
|
|
2388
|
+
}
|
|
2389
|
+
// When the NEWEST attempt fails, older in-flight resolutions have
|
|
2390
|
+
// been epoch-discarded (they may describe an earlier session) and
|
|
2391
|
+
// nothing else would wrap the provider until the next store update.
|
|
2392
|
+
// Recover by re-kicking fresh with live state, bounded so a
|
|
2393
|
+
// persistently failing connector cannot loop: after the budget is
|
|
2394
|
+
// spent, recovery waits for a real store update, which resets it.
|
|
2395
|
+
var retryAfterFailure = function () {
|
|
2396
|
+
var _a;
|
|
2397
|
+
if (_this.disposed)
|
|
2398
|
+
return;
|
|
2399
|
+
if (_this.wrapEpochs.get(connector) !== epoch) {
|
|
2400
|
+
// Superseded: the newer attempt owns recovery.
|
|
2401
|
+
return;
|
|
2402
|
+
}
|
|
2403
|
+
var failures = ((_a = _this.wrapRetryCounts.get(connector)) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
2404
|
+
_this.wrapRetryCounts.set(connector, failures);
|
|
2405
|
+
if (failures > 3)
|
|
2406
|
+
return;
|
|
2407
|
+
var timer = setTimeout(function () {
|
|
2408
|
+
var _a;
|
|
2409
|
+
_this.wrapRetryTimers.delete(timer);
|
|
2410
|
+
if (_this.disposed)
|
|
2411
|
+
return;
|
|
2412
|
+
// Re-kick only while THIS connector is still the active one and
|
|
2413
|
+
// no newer attempt superseded this one. A retry for a connector
|
|
2414
|
+
// the user has left must not fire at the current connector: with
|
|
2415
|
+
// the retry flag it would skip the budget reset and, worse, its
|
|
2416
|
+
// fresh epoch would discard the current connector's own in-flight
|
|
2417
|
+
// resolution.
|
|
2418
|
+
var live = _this.getState();
|
|
2419
|
+
var stillCurrent = live.current !== undefined &&
|
|
2420
|
+
((_a = live.connections.get(live.current)) === null || _a === void 0 ? void 0 : _a.connector) === connector;
|
|
2421
|
+
if (stillCurrent && _this.wrapEpochs.get(connector) === epoch) {
|
|
2422
|
+
_this.wrapActiveConnectorProvider(live, true);
|
|
2423
|
+
}
|
|
2424
|
+
}, 25 * failures);
|
|
2425
|
+
_this.wrapRetryTimers.add(timer);
|
|
2426
|
+
};
|
|
2427
|
+
var resolution;
|
|
2428
|
+
try {
|
|
2429
|
+
resolution = connector.getProvider();
|
|
2430
|
+
}
|
|
2431
|
+
catch (_d) {
|
|
2432
|
+
// A synchronous throw must not escape into the subscription
|
|
2433
|
+
// callback: address-change handling runs after this kick and a
|
|
2434
|
+
// propagated throw would silently skip it.
|
|
2435
|
+
retryAfterFailure();
|
|
2436
|
+
return;
|
|
2437
|
+
}
|
|
2438
|
+
resolution
|
|
2314
2439
|
.then(function (provider) {
|
|
2315
|
-
var _a, _b;
|
|
2316
|
-
|
|
2440
|
+
var _a, _b, _c, _d;
|
|
2441
|
+
if (_this.wrapEpochs.get(connector) !== epoch) {
|
|
2442
|
+
// A newer kick is in flight or already resolved; this answer may
|
|
2443
|
+
// describe an earlier session. Let the newest one win.
|
|
2444
|
+
return;
|
|
2445
|
+
}
|
|
2446
|
+
// The fallback installs only the request wrapper, so a provider
|
|
2447
|
+
// without a synchronous chainId property would never teach the
|
|
2448
|
+
// registry its chain and its events would carry chain 0. The wagmi
|
|
2449
|
+
// store knows; feed it here and on every later chain change. Read
|
|
2450
|
+
// the chain from LIVE state, not from a snapshot taken before this
|
|
2451
|
+
// asynchronous resolution: a switch that lands while getProvider is
|
|
2452
|
+
// in flight fires handleChainChange before fallbackProvider exists,
|
|
2453
|
+
// so a pre-resolution snapshot would stick as the recorded chain.
|
|
2454
|
+
// Activity is judged by CONNECTOR identity: wagmi replaces the
|
|
2455
|
+
// connection record itself on every account or chain update.
|
|
2456
|
+
var live = _this.getState();
|
|
2457
|
+
// A wrap only describes a CONNECTED session. Status changes kick
|
|
2458
|
+
// this path for every transition, and a resolution landing while
|
|
2459
|
+
// disconnected would re-point the fallback pair at a provider
|
|
2460
|
+
// whose session is over.
|
|
2461
|
+
var stillActive = !_this.disposed &&
|
|
2462
|
+
_this.wrapSessionGeneration === session &&
|
|
2463
|
+
live.status === "connected" &&
|
|
2464
|
+
live.current !== undefined &&
|
|
2465
|
+
((_a = live.connections.get(live.current)) === null || _a === void 0 ? void 0 : _a.connector) === connector;
|
|
2466
|
+
if (!stillActive) {
|
|
2467
|
+
// The user moved on while getProvider was in flight. Wrapping
|
|
2468
|
+
// now would instrument a provider whose chain nobody feeds -
|
|
2469
|
+
// its requests would report chain 0. Switching back re-kicks.
|
|
2470
|
+
return;
|
|
2471
|
+
}
|
|
2472
|
+
var chainId = (_b = _this.getActiveConnectionChainId(live)) !== null && _b !== void 0 ? _b : live.chainId;
|
|
2473
|
+
var wrapped = (_d = (_c = _this.formo)._wrapWagmiProvider) === null || _d === void 0 ? void 0 : _d.call(_c, provider, typeof chainId === "number" ? chainId : undefined);
|
|
2474
|
+
if (wrapped !== true) {
|
|
2475
|
+
// The provider was refused (invalid shape, frozen provider,
|
|
2476
|
+
// torn-down SDK).
|
|
2477
|
+
retryAfterFailure();
|
|
2478
|
+
return;
|
|
2479
|
+
}
|
|
2480
|
+
_this.wrapRetryCounts.delete(connector);
|
|
2481
|
+
_this.fallbackConnector = connector;
|
|
2482
|
+
_this.fallbackProvider = provider;
|
|
2317
2483
|
})
|
|
2318
2484
|
.catch(function () {
|
|
2319
|
-
// Mutation-only capture remains
|
|
2320
|
-
|
|
2485
|
+
// Mutation-only capture remains.
|
|
2486
|
+
retryAfterFailure();
|
|
2321
2487
|
});
|
|
2322
2488
|
};
|
|
2323
2489
|
WagmiEventHandler.prototype.kickWalletConnectPeerLookup = function (state) {
|
|
@@ -2407,6 +2573,8 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
2407
2573
|
* Clean up all subscriptions
|
|
2408
2574
|
*/
|
|
2409
2575
|
WagmiEventHandler.prototype.cleanup = function () {
|
|
2576
|
+
this.wrapRetryTimers.forEach(function (t) { return clearTimeout(t); });
|
|
2577
|
+
this.wrapRetryTimers.clear();
|
|
2410
2578
|
logger.debug("WagmiEventHandler: Cleaning up subscriptions");
|
|
2411
2579
|
if (!this.disposed) {
|
|
2412
2580
|
this.disposed = true;
|