@formo/analytics 1.38.0 → 1.38.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/cjs/src/FormoAnalytics.d.ts +15 -4
- package/dist/cjs/src/FormoAnalytics.js +37 -6
- package/dist/cjs/src/FormoAnalyticsProvider.d.ts +8 -0
- package/dist/cjs/src/FormoAnalyticsProvider.js +41 -32
- package/dist/cjs/src/evm/EvmEventTracker.d.ts +100 -9
- package/dist/cjs/src/evm/EvmEventTracker.js +357 -19
- package/dist/cjs/src/evm/EvmProviderRegistry.d.ts +34 -2
- package/dist/cjs/src/evm/EvmProviderRegistry.js +61 -4
- package/dist/cjs/src/evm/EvmRequestTracker.d.ts +21 -4
- package/dist/cjs/src/evm/EvmRequestTracker.js +54 -28
- 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 +67 -1
- package/dist/cjs/src/wagmi/WagmiEventHandler.js +286 -39
- package/dist/cjs/src/wagmi/types.d.ts +6 -0
- package/dist/cjs/src/wagmi/utils.js +9 -1
- package/dist/esm/src/FormoAnalytics.d.ts +15 -4
- package/dist/esm/src/FormoAnalytics.js +37 -6
- package/dist/esm/src/FormoAnalyticsProvider.d.ts +8 -0
- package/dist/esm/src/FormoAnalyticsProvider.js +39 -31
- package/dist/esm/src/evm/EvmEventTracker.d.ts +100 -9
- package/dist/esm/src/evm/EvmEventTracker.js +357 -19
- package/dist/esm/src/evm/EvmProviderRegistry.d.ts +34 -2
- package/dist/esm/src/evm/EvmProviderRegistry.js +61 -4
- package/dist/esm/src/evm/EvmRequestTracker.d.ts +21 -4
- package/dist/esm/src/evm/EvmRequestTracker.js +54 -28
- 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 +67 -1
- package/dist/esm/src/wagmi/WagmiEventHandler.js +286 -39
- package/dist/esm/src/wagmi/types.d.ts +6 -0
- package/dist/esm/src/wagmi/utils.js +9 -1
- package/dist/index.umd.min.js +1 -1
- package/package.json +2 -2
|
@@ -75,6 +75,16 @@ var RESERVED_FIELDS = new Set([
|
|
|
75
75
|
"transactionHash",
|
|
76
76
|
"function_name",
|
|
77
77
|
"function_args",
|
|
78
|
+
// Wallet attribution. Function args are spread AFTER the attribution
|
|
79
|
+
// properties, so an ABI input named `providerName` would silently
|
|
80
|
+
// relabel the wallet on every broadcast and receipt event. `rdns` is
|
|
81
|
+
// reserved with it even though THIS path (wagmi mutation attribution)
|
|
82
|
+
// emits only the name: elsewhere in the SDK - the EIP-1193 request path
|
|
83
|
+
// and connect events - `rdns` is the wallet-identity key, so an ABI
|
|
84
|
+
// input by that name would land in the same warehouse column as real
|
|
85
|
+
// wallet identities.
|
|
86
|
+
"providerName",
|
|
87
|
+
"rdns",
|
|
78
88
|
]);
|
|
79
89
|
/**
|
|
80
90
|
* Connections already announced to a destination during this page load.
|
|
@@ -242,8 +252,6 @@ var walletConnectPeerLookups = new WeakSet();
|
|
|
242
252
|
* newest kicked one, so a slow resolution from a PREVIOUS session cannot
|
|
243
253
|
* land after the current session's and overwrite it. */
|
|
244
254
|
var walletConnectPeerLatest = new WeakMap();
|
|
245
|
-
/** Connections whose provider already has the hybrid-capture wrapper. */
|
|
246
|
-
var wagmiWrappedConnections = new WeakSet();
|
|
247
255
|
/**
|
|
248
256
|
* Pending transactions, shared per destination rather than per handler.
|
|
249
257
|
*
|
|
@@ -428,6 +436,57 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
428
436
|
* Key format: `${queryHash}:${status}`
|
|
429
437
|
*/
|
|
430
438
|
this.processedQueries = new Set();
|
|
439
|
+
/**
|
|
440
|
+
* Start resolving the wallet behind a WalletConnect connection.
|
|
441
|
+
*
|
|
442
|
+
* Fire-and-forget on purpose: emission paths are synchronous by design
|
|
443
|
+
* and must never wait (see the connect marker comment). Called at the
|
|
444
|
+
* START of the status/address flows rather than only at read time - the
|
|
445
|
+
* lookup is one microtask for an initialised connector, and the emission
|
|
446
|
+
* sits behind several awaits, so kicking early usually means even the
|
|
447
|
+
* FIRST connect names the real wallet. When the race is lost the event
|
|
448
|
+
* honestly says "WalletConnect" and every later event names the peer.
|
|
449
|
+
*/
|
|
450
|
+
/**
|
|
451
|
+
* Install the request wrapper on the active connector's provider.
|
|
452
|
+
*
|
|
453
|
+
* This is what lets wagmi mode capture IMPERATIVE viem calls
|
|
454
|
+
* (walletClient.sendTransaction / .signMessage / .writeContract / raw
|
|
455
|
+
* request), which create no mutation and were silently lost. Hook-driven
|
|
456
|
+
* calls stay owned by the mutation handlers via the pending-mutation
|
|
457
|
+
* dedup. Fire-and-forget per connection; a provider that cannot be
|
|
458
|
+
* produced simply keeps mutation-only capture.
|
|
459
|
+
*/
|
|
460
|
+
/**
|
|
461
|
+
* Connections THIS instance has wrapped. Deliberately per-instance, not
|
|
462
|
+
* module-level: a module-level guard let a rebuilt SDK instance skip
|
|
463
|
+
* re-wrapping, so ownership stayed with the torn-down instance and every
|
|
464
|
+
* capture died in its closed queue - and it also stopped a second
|
|
465
|
+
* write-key instance from ever registering. Re-wrapping is safe and
|
|
466
|
+
* cheap: the request tracker rebinds ownership of an intact wrapper.
|
|
467
|
+
*/
|
|
468
|
+
/**
|
|
469
|
+
* Latest wrap attempt per connector. Every kick re-resolves
|
|
470
|
+
* getProvider() - a connector can hand out a REPLACEMENT provider after
|
|
471
|
+
* a reconnect, so a once-only guard would leave the new session's
|
|
472
|
+
* provider unwrapped forever. Re-wrapping is idempotent: the request
|
|
473
|
+
* tracker rebinds ownership of an intact wrapper. The epoch lets a slow
|
|
474
|
+
* resolution from a superseded kick (an earlier session, an earlier
|
|
475
|
+
* chain) be discarded instead of overwriting fresher state.
|
|
476
|
+
*/
|
|
477
|
+
this.wrapEpochs = new WeakMap();
|
|
478
|
+
/** Consecutive automatic wrap retries per connector; see below. */
|
|
479
|
+
this.wrapRetryCounts = new WeakMap();
|
|
480
|
+
/** Pending retry timers, cancelled by cleanup(). */
|
|
481
|
+
this.wrapRetryTimers = new Set();
|
|
482
|
+
/**
|
|
483
|
+
* Bumped on every observed disconnect. Every wrap attempt captures it
|
|
484
|
+
* at kick time and its resolution stands down on a mismatch, so a
|
|
485
|
+
* disconnect invalidates ALL pending wraps at once - including ones
|
|
486
|
+
* whose getProvider() had not resolved yet, which no per-connector
|
|
487
|
+
* bookkeeping can reach because nothing has been recorded for them.
|
|
488
|
+
*/
|
|
489
|
+
this.wrapSessionGeneration = 0;
|
|
431
490
|
this.formo = formoAnalytics;
|
|
432
491
|
this.wagmiConfig = wagmiConfig;
|
|
433
492
|
this.queryClient = queryClient;
|
|
@@ -602,6 +661,10 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
602
661
|
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) {
|
|
603
662
|
var _a = key.split("|"), address = _a[1];
|
|
604
663
|
var _b = (prevKey !== null && prevKey !== void 0 ? prevKey : "|").split("|"), prevAddress = _b[1];
|
|
664
|
+
// A connector switch keeps status "connected", so the status
|
|
665
|
+
// subscription never re-wraps; the NEW active connection's provider
|
|
666
|
+
// must be instrumented from here or its imperative calls are lost.
|
|
667
|
+
_this.wrapActiveConnectorProvider(_this.getState());
|
|
605
668
|
_this.handleActiveAddressChange(address || undefined, prevAddress || undefined);
|
|
606
669
|
});
|
|
607
670
|
this.unsubscribers.push(connectionUnsubscribe);
|
|
@@ -907,6 +970,19 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
907
970
|
return __generator(this, function (_e) {
|
|
908
971
|
switch (_e.label) {
|
|
909
972
|
case 0:
|
|
973
|
+
if (status === "disconnected" || status === "reconnecting") {
|
|
974
|
+
// The wrapped session is over - or, for "reconnecting", about to be
|
|
975
|
+
// replaced without ever passing through "disconnected". This must
|
|
976
|
+
// run for EVERY such transition, before the processing lock and
|
|
977
|
+
// regardless of whether a wallet was being tracked, or a rapid
|
|
978
|
+
// same-connector reconnect retains the old provider and the chain
|
|
979
|
+
// callback writes the new session's chain onto it. The generation
|
|
980
|
+
// bump invalidates every wrap still in flight from the ended
|
|
981
|
+
// session, resolved or not. The "connected" that follows re-wraps.
|
|
982
|
+
this.wrapSessionGeneration += 1;
|
|
983
|
+
this.fallbackConnector = undefined;
|
|
984
|
+
this.fallbackProvider = undefined;
|
|
985
|
+
}
|
|
910
986
|
// Prevent concurrent processing
|
|
911
987
|
if (this.trackingState.isProcessing) {
|
|
912
988
|
// Dropped, not queued - but remember that a disconnect went past.
|
|
@@ -1443,10 +1519,31 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
1443
1519
|
*/
|
|
1444
1520
|
WagmiEventHandler.prototype.handleChainChange = function (chainId, prevChainId) {
|
|
1445
1521
|
return __awaiter(this, void 0, void 0, function () {
|
|
1446
|
-
var state, address, hadAddress, wasAnnounced, announcedNow;
|
|
1447
|
-
|
|
1448
|
-
|
|
1522
|
+
var live, activeConnector, state, address, hadAddress, wasAnnounced, announcedNow;
|
|
1523
|
+
var _a, _b, _c;
|
|
1524
|
+
return __generator(this, function (_d) {
|
|
1525
|
+
switch (_d.label) {
|
|
1449
1526
|
case 0:
|
|
1527
|
+
// Keep the fallback-wrapped provider's registry chain in step with the
|
|
1528
|
+
// store, so request-derived events are labelled correctly even when the
|
|
1529
|
+
// provider exposes no synchronous chainId. Only when the report is
|
|
1530
|
+
// about the connection that provider belongs to: after a connector
|
|
1531
|
+
// switch the store's chain describes the NEW connection, and writing it
|
|
1532
|
+
// to the old provider would mislabel that wallet's events.
|
|
1533
|
+
if (this.fallbackProvider !== undefined) {
|
|
1534
|
+
live = this.getState();
|
|
1535
|
+
activeConnector = live.current
|
|
1536
|
+
? (_a = live.connections.get(live.current)) === null || _a === void 0 ? void 0 : _a.connector
|
|
1537
|
+
: undefined;
|
|
1538
|
+
if (activeConnector === this.fallbackConnector) {
|
|
1539
|
+
try {
|
|
1540
|
+
(_c = (_b = this.formo)._rememberWagmiProviderChain) === null || _c === void 0 ? void 0 : _c.call(_b, this.fallbackProvider, chainId);
|
|
1541
|
+
}
|
|
1542
|
+
catch (_e) {
|
|
1543
|
+
/* never let bookkeeping break the chain flow */
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1450
1547
|
if (chainId === prevChainId || chainId === undefined) {
|
|
1451
1548
|
return [2 /*return*/];
|
|
1452
1549
|
}
|
|
@@ -1492,7 +1589,7 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
1492
1589
|
});
|
|
1493
1590
|
return [4 /*yield*/, this.applyChainForTrackedWallet(address, chainId)];
|
|
1494
1591
|
case 1:
|
|
1495
|
-
|
|
1592
|
+
_d.sent();
|
|
1496
1593
|
return [2 /*return*/];
|
|
1497
1594
|
}
|
|
1498
1595
|
});
|
|
@@ -2250,6 +2347,75 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
2250
2347
|
});
|
|
2251
2348
|
return found;
|
|
2252
2349
|
};
|
|
2350
|
+
/**
|
|
2351
|
+
* A specific connector's name and rdns, for the fallback-wrapped
|
|
2352
|
+
* provider's request-derived events.
|
|
2353
|
+
*
|
|
2354
|
+
* The NAME is exactly what the hook path emits for that connector
|
|
2355
|
+
* (`getConnectorName`: the live WalletConnect peer for a WalletConnect
|
|
2356
|
+
* connector, the connector's own name otherwise), so a branded connector
|
|
2357
|
+
* keeps its name on both paths and a session that changes wallets
|
|
2358
|
+
* renames on both. The rdns is the connector's when wagmi knows it as
|
|
2359
|
+
* ONE value (EIP-6963 discovered connectors carry theirs, some as a
|
|
2360
|
+
* one-element list); a connector that matches several rdns values does
|
|
2361
|
+
* not say which one this provider is, so nothing is passed and the
|
|
2362
|
+
* registry keeps the sniffed one.
|
|
2363
|
+
*/
|
|
2364
|
+
WagmiEventHandler.prototype.attributionForConnector = function (connector, state) {
|
|
2365
|
+
var name = this.nameForConnector(connector, state);
|
|
2366
|
+
if (!name) {
|
|
2367
|
+
return undefined;
|
|
2368
|
+
}
|
|
2369
|
+
var raw = connector.rdns;
|
|
2370
|
+
var rdns = typeof raw === "string" && raw.length > 0
|
|
2371
|
+
? raw
|
|
2372
|
+
: Array.isArray(raw) && raw.length === 1 && typeof raw[0] === "string" && raw[0].length > 0
|
|
2373
|
+
? raw[0]
|
|
2374
|
+
: undefined;
|
|
2375
|
+
return __assign({ name: name }, (rdns && { rdns: rdns }));
|
|
2376
|
+
};
|
|
2377
|
+
/**
|
|
2378
|
+
* Resolver handed to the registry at wrap time, read at each request
|
|
2379
|
+
* start. BOUND to the connector whose `getProvider` resolved to the
|
|
2380
|
+
* wrapped provider, not to whichever connector is current: an app can
|
|
2381
|
+
* keep a wallet client over a previous connector's provider and issue a
|
|
2382
|
+
* request through it after switching, and that request belongs to the
|
|
2383
|
+
* wallet that handles it. The peer name is still read live. A provider
|
|
2384
|
+
* SHARED by two connectors is re-bound when the new connector's
|
|
2385
|
+
* resolution lands (one microtask for an injected connector); a request
|
|
2386
|
+
* issued inside that gap keeps the previous label.
|
|
2387
|
+
*/
|
|
2388
|
+
WagmiEventHandler.prototype.boundConnectorAttribution = function (connector) {
|
|
2389
|
+
var _this = this;
|
|
2390
|
+
return function () {
|
|
2391
|
+
if (_this.disposed)
|
|
2392
|
+
return undefined;
|
|
2393
|
+
try {
|
|
2394
|
+
return _this.attributionForConnector(connector, _this.getState());
|
|
2395
|
+
}
|
|
2396
|
+
catch (_a) {
|
|
2397
|
+
return undefined;
|
|
2398
|
+
}
|
|
2399
|
+
};
|
|
2400
|
+
};
|
|
2401
|
+
/** `getConnectorName` for a SPECIFIC connector, current or not. */
|
|
2402
|
+
WagmiEventHandler.prototype.nameForConnector = function (connector, state) {
|
|
2403
|
+
var _a;
|
|
2404
|
+
var cached = walletConnectPeerNames.get(connector);
|
|
2405
|
+
if (cached) {
|
|
2406
|
+
return cached;
|
|
2407
|
+
}
|
|
2408
|
+
var current = state.current
|
|
2409
|
+
? (_a = state.connections.get(state.current)) === null || _a === void 0 ? void 0 : _a.connector
|
|
2410
|
+
: undefined;
|
|
2411
|
+
if (current === connector) {
|
|
2412
|
+
// Backstop kick, as in getConnectorName; only the current connection
|
|
2413
|
+
// has a session to look up.
|
|
2414
|
+
this.kickWalletConnectPeerLookup(state);
|
|
2415
|
+
}
|
|
2416
|
+
var name = connector.name;
|
|
2417
|
+
return typeof name === "string" && name.length > 0 ? name : undefined;
|
|
2418
|
+
};
|
|
2253
2419
|
/**
|
|
2254
2420
|
* Get the connector name from Wagmi state
|
|
2255
2421
|
*/
|
|
@@ -2271,30 +2437,10 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
2271
2437
|
this.kickWalletConnectPeerLookup(state);
|
|
2272
2438
|
return connector.name;
|
|
2273
2439
|
};
|
|
2274
|
-
|
|
2275
|
-
* Start resolving the wallet behind a WalletConnect connection.
|
|
2276
|
-
*
|
|
2277
|
-
* Fire-and-forget on purpose: emission paths are synchronous by design
|
|
2278
|
-
* and must never wait (see the connect marker comment). Called at the
|
|
2279
|
-
* START of the status/address flows rather than only at read time - the
|
|
2280
|
-
* lookup is one microtask for an initialised connector, and the emission
|
|
2281
|
-
* sits behind several awaits, so kicking early usually means even the
|
|
2282
|
-
* FIRST connect names the real wallet. When the race is lost the event
|
|
2283
|
-
* honestly says "WalletConnect" and every later event names the peer.
|
|
2284
|
-
*/
|
|
2285
|
-
/**
|
|
2286
|
-
* Install the request wrapper on the active connector's provider.
|
|
2287
|
-
*
|
|
2288
|
-
* This is what lets wagmi mode capture IMPERATIVE viem calls
|
|
2289
|
-
* (walletClient.sendTransaction / .signMessage / .writeContract / raw
|
|
2290
|
-
* request), which create no mutation and were silently lost. Hook-driven
|
|
2291
|
-
* calls stay owned by the mutation handlers via the pending-mutation
|
|
2292
|
-
* dedup. Fire-and-forget per connection; a provider that cannot be
|
|
2293
|
-
* produced simply keeps mutation-only capture.
|
|
2294
|
-
*/
|
|
2295
|
-
WagmiEventHandler.prototype.wrapActiveConnectorProvider = function (state) {
|
|
2440
|
+
WagmiEventHandler.prototype.wrapActiveConnectorProvider = function (state, isRetry) {
|
|
2296
2441
|
var _this = this;
|
|
2297
|
-
var _a, _b;
|
|
2442
|
+
var _a, _b, _c;
|
|
2443
|
+
if (isRetry === void 0) { isRetry = false; }
|
|
2298
2444
|
// OPT-IN only. Wagmi mode's baseline never touches the signing
|
|
2299
2445
|
// transport; instrumenting the provider is an explicit integrator
|
|
2300
2446
|
// decision (options.wagmi.eip1193Fallback), made auditable in
|
|
@@ -2303,25 +2449,124 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
2303
2449
|
if (!optedIn) {
|
|
2304
2450
|
return;
|
|
2305
2451
|
}
|
|
2452
|
+
if (state.status !== "connected") {
|
|
2453
|
+
// Status kicks run for every transition; only a connected snapshot
|
|
2454
|
+
// describes a session worth wrapping.
|
|
2455
|
+
return;
|
|
2456
|
+
}
|
|
2306
2457
|
var connection = state.current
|
|
2307
2458
|
? state.connections.get(state.current)
|
|
2308
2459
|
: undefined;
|
|
2309
2460
|
var connector = connection === null || connection === void 0 ? void 0 : connection.connector;
|
|
2310
|
-
if (!connection ||
|
|
2311
|
-
typeof (connector === null || connector === void 0 ? void 0 : connector.getProvider) !== "function" ||
|
|
2312
|
-
wagmiWrappedConnections.has(connection)) {
|
|
2461
|
+
if (!connection || typeof (connector === null || connector === void 0 ? void 0 : connector.getProvider) !== "function") {
|
|
2313
2462
|
return;
|
|
2314
2463
|
}
|
|
2315
|
-
|
|
2316
|
-
connector
|
|
2317
|
-
|
|
2464
|
+
var epoch = ((_c = this.wrapEpochs.get(connector)) !== null && _c !== void 0 ? _c : 0) + 1;
|
|
2465
|
+
this.wrapEpochs.set(connector, epoch);
|
|
2466
|
+
var session = this.wrapSessionGeneration;
|
|
2467
|
+
// A store-driven kick resets the retry budget; automatic retries
|
|
2468
|
+
// (below) spend it.
|
|
2469
|
+
if (!isRetry) {
|
|
2470
|
+
this.wrapRetryCounts.delete(connector);
|
|
2471
|
+
}
|
|
2472
|
+
// When the NEWEST attempt fails, older in-flight resolutions have
|
|
2473
|
+
// been epoch-discarded (they may describe an earlier session) and
|
|
2474
|
+
// nothing else would wrap the provider until the next store update.
|
|
2475
|
+
// Recover by re-kicking fresh with live state, bounded so a
|
|
2476
|
+
// persistently failing connector cannot loop: after the budget is
|
|
2477
|
+
// spent, recovery waits for a real store update, which resets it.
|
|
2478
|
+
var retryAfterFailure = function () {
|
|
2479
|
+
var _a;
|
|
2480
|
+
if (_this.disposed)
|
|
2481
|
+
return;
|
|
2482
|
+
if (_this.wrapEpochs.get(connector) !== epoch) {
|
|
2483
|
+
// Superseded: the newer attempt owns recovery.
|
|
2484
|
+
return;
|
|
2485
|
+
}
|
|
2486
|
+
var failures = ((_a = _this.wrapRetryCounts.get(connector)) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
2487
|
+
_this.wrapRetryCounts.set(connector, failures);
|
|
2488
|
+
if (failures > 3)
|
|
2489
|
+
return;
|
|
2490
|
+
var timer = setTimeout(function () {
|
|
2491
|
+
var _a;
|
|
2492
|
+
_this.wrapRetryTimers.delete(timer);
|
|
2493
|
+
if (_this.disposed)
|
|
2494
|
+
return;
|
|
2495
|
+
// Re-kick only while THIS connector is still the active one and
|
|
2496
|
+
// no newer attempt superseded this one. A retry for a connector
|
|
2497
|
+
// the user has left must not fire at the current connector: with
|
|
2498
|
+
// the retry flag it would skip the budget reset and, worse, its
|
|
2499
|
+
// fresh epoch would discard the current connector's own in-flight
|
|
2500
|
+
// resolution.
|
|
2501
|
+
var live = _this.getState();
|
|
2502
|
+
var stillCurrent = live.current !== undefined &&
|
|
2503
|
+
((_a = live.connections.get(live.current)) === null || _a === void 0 ? void 0 : _a.connector) === connector;
|
|
2504
|
+
if (stillCurrent && _this.wrapEpochs.get(connector) === epoch) {
|
|
2505
|
+
_this.wrapActiveConnectorProvider(live, true);
|
|
2506
|
+
}
|
|
2507
|
+
}, 25 * failures);
|
|
2508
|
+
_this.wrapRetryTimers.add(timer);
|
|
2509
|
+
};
|
|
2510
|
+
var resolution;
|
|
2511
|
+
try {
|
|
2512
|
+
resolution = connector.getProvider();
|
|
2513
|
+
}
|
|
2514
|
+
catch (_d) {
|
|
2515
|
+
// A synchronous throw must not escape into the subscription
|
|
2516
|
+
// callback: address-change handling runs after this kick and a
|
|
2517
|
+
// propagated throw would silently skip it.
|
|
2518
|
+
retryAfterFailure();
|
|
2519
|
+
return;
|
|
2520
|
+
}
|
|
2521
|
+
resolution
|
|
2318
2522
|
.then(function (provider) {
|
|
2319
|
-
var _a, _b;
|
|
2320
|
-
|
|
2523
|
+
var _a, _b, _c, _d;
|
|
2524
|
+
if (_this.wrapEpochs.get(connector) !== epoch) {
|
|
2525
|
+
// A newer kick is in flight or already resolved; this answer may
|
|
2526
|
+
// describe an earlier session. Let the newest one win.
|
|
2527
|
+
return;
|
|
2528
|
+
}
|
|
2529
|
+
// The fallback installs only the request wrapper, so a provider
|
|
2530
|
+
// without a synchronous chainId property would never teach the
|
|
2531
|
+
// registry its chain and its events would carry chain 0. The wagmi
|
|
2532
|
+
// store knows; feed it here and on every later chain change. Read
|
|
2533
|
+
// the chain from LIVE state, not from a snapshot taken before this
|
|
2534
|
+
// asynchronous resolution: a switch that lands while getProvider is
|
|
2535
|
+
// in flight fires handleChainChange before fallbackProvider exists,
|
|
2536
|
+
// so a pre-resolution snapshot would stick as the recorded chain.
|
|
2537
|
+
// Activity is judged by CONNECTOR identity: wagmi replaces the
|
|
2538
|
+
// connection record itself on every account or chain update.
|
|
2539
|
+
var live = _this.getState();
|
|
2540
|
+
// A wrap only describes a CONNECTED session. Status changes kick
|
|
2541
|
+
// this path for every transition, and a resolution landing while
|
|
2542
|
+
// disconnected would re-point the fallback pair at a provider
|
|
2543
|
+
// whose session is over.
|
|
2544
|
+
var stillActive = !_this.disposed &&
|
|
2545
|
+
_this.wrapSessionGeneration === session &&
|
|
2546
|
+
live.status === "connected" &&
|
|
2547
|
+
live.current !== undefined &&
|
|
2548
|
+
((_a = live.connections.get(live.current)) === null || _a === void 0 ? void 0 : _a.connector) === connector;
|
|
2549
|
+
if (!stillActive) {
|
|
2550
|
+
// The user moved on while getProvider was in flight. Wrapping
|
|
2551
|
+
// now would instrument a provider whose chain nobody feeds -
|
|
2552
|
+
// its requests would report chain 0. Switching back re-kicks.
|
|
2553
|
+
return;
|
|
2554
|
+
}
|
|
2555
|
+
var chainId = (_b = _this.getActiveConnectionChainId(live)) !== null && _b !== void 0 ? _b : live.chainId;
|
|
2556
|
+
var wrapped = (_d = (_c = _this.formo)._wrapWagmiProvider) === null || _d === void 0 ? void 0 : _d.call(_c, provider, typeof chainId === "number" ? chainId : undefined, _this.boundConnectorAttribution(connector));
|
|
2557
|
+
if (wrapped !== true) {
|
|
2558
|
+
// The provider was refused (invalid shape, frozen provider,
|
|
2559
|
+
// torn-down SDK).
|
|
2560
|
+
retryAfterFailure();
|
|
2561
|
+
return;
|
|
2562
|
+
}
|
|
2563
|
+
_this.wrapRetryCounts.delete(connector);
|
|
2564
|
+
_this.fallbackConnector = connector;
|
|
2565
|
+
_this.fallbackProvider = provider;
|
|
2321
2566
|
})
|
|
2322
2567
|
.catch(function () {
|
|
2323
|
-
// Mutation-only capture remains
|
|
2324
|
-
|
|
2568
|
+
// Mutation-only capture remains.
|
|
2569
|
+
retryAfterFailure();
|
|
2325
2570
|
});
|
|
2326
2571
|
};
|
|
2327
2572
|
WagmiEventHandler.prototype.kickWalletConnectPeerLookup = function (state) {
|
|
@@ -2411,6 +2656,8 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
2411
2656
|
* Clean up all subscriptions
|
|
2412
2657
|
*/
|
|
2413
2658
|
WagmiEventHandler.prototype.cleanup = function () {
|
|
2659
|
+
this.wrapRetryTimers.forEach(function (t) { return clearTimeout(t); });
|
|
2660
|
+
this.wrapRetryTimers.clear();
|
|
2414
2661
|
logger_1.logger.debug("WagmiEventHandler: Cleaning up subscriptions");
|
|
2415
2662
|
if (!this.disposed) {
|
|
2416
2663
|
this.disposed = true;
|
|
@@ -31,6 +31,12 @@ export interface WagmiConnector {
|
|
|
31
31
|
name: string;
|
|
32
32
|
type: string;
|
|
33
33
|
uid: string;
|
|
34
|
+
/**
|
|
35
|
+
* Any connector implementation may expose an rdns; EIP-6963 discovered
|
|
36
|
+
* connectors are one source. A connector matching several rdns values
|
|
37
|
+
* lists them all.
|
|
38
|
+
*/
|
|
39
|
+
rdns?: string | readonly string[];
|
|
34
40
|
}
|
|
35
41
|
/**
|
|
36
42
|
* Wagmi config interface
|
|
@@ -192,7 +192,15 @@ function buildSafeFunctionArgs(functionArgs, reservedFields) {
|
|
|
192
192
|
var result = {};
|
|
193
193
|
for (var _i = 0, _a = Object.entries(functionArgs); _i < _a.length; _i++) {
|
|
194
194
|
var _b = _a[_i], key = _b[0], val = _b[1];
|
|
195
|
-
|
|
195
|
+
// A prefixed key must not land on another argument's own name: an ABI
|
|
196
|
+
// with both `to` and `arg_to` would otherwise keep only one of them,
|
|
197
|
+
// whichever came later. Keep prefixing until the name is free.
|
|
198
|
+
var safeKey = key;
|
|
199
|
+
if (reservedFields.has(key)) {
|
|
200
|
+
do {
|
|
201
|
+
safeKey = "arg_".concat(safeKey);
|
|
202
|
+
} while (safeKey in functionArgs);
|
|
203
|
+
}
|
|
196
204
|
result[safeKey] = val;
|
|
197
205
|
// If the value is a nested object (struct), flatten it
|
|
198
206
|
// Skip flattened keys that would overwrite existing top-level args
|
|
@@ -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
|
*
|
|
@@ -428,8 +428,19 @@ export declare class FormoAnalytics implements IFormoAnalytics {
|
|
|
428
428
|
* gap. Lifecycle (connect/chain/disconnect) stays store-driven: only the
|
|
429
429
|
* request wrapper installs here. Double counting is prevented in the
|
|
430
430
|
* wrapper via `shouldSkipRequestCapture`.
|
|
431
|
+
*
|
|
432
|
+
* `attribution` resolves, live, the name and rdns of the connector this
|
|
433
|
+
* provider was wrapped for. Request-derived events are named from the
|
|
434
|
+
* registry, which for an unannounced provider falls back to flag
|
|
435
|
+
* sniffing; recording the connector's resolver here keeps them in
|
|
436
|
+
* agreement with the hook-driven events for that connector.
|
|
431
437
|
*/
|
|
432
|
-
_wrapWagmiProvider(provider: EIP1193Provider)
|
|
438
|
+
_wrapWagmiProvider(provider: EIP1193Provider, chainId?: number, attribution?: () => {
|
|
439
|
+
name: string;
|
|
440
|
+
rdns?: string;
|
|
441
|
+
} | undefined): boolean;
|
|
442
|
+
/** INTERNAL. Chain updates for the fallback-wrapped provider. */
|
|
443
|
+
_rememberWagmiProviderChain(provider: EIP1193Provider, chainId: number | undefined): void;
|
|
433
444
|
registerProvider(provider: EIP1193Provider, info?: {
|
|
434
445
|
name?: string;
|
|
435
446
|
rdns?: string;
|
|
@@ -1015,6 +1015,9 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1015
1015
|
// Drop anything already buffered so a pending timer/pagehide flush
|
|
1016
1016
|
// cannot ship events after consent withdrawal.
|
|
1017
1017
|
this.eventManager.clear();
|
|
1018
|
+
// Identity is purged below; registered sessions must be re-learned
|
|
1019
|
+
// on opt-in, and nothing else would retry an already-adopted one.
|
|
1020
|
+
this.evmEvents.markRegisteredAdoptionsPending();
|
|
1018
1021
|
this.reset();
|
|
1019
1022
|
logger.info("Successfully opted out of tracking");
|
|
1020
1023
|
};
|
|
@@ -1334,9 +1337,9 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1334
1337
|
*
|
|
1335
1338
|
* With several live SDK instances (multi write-key pages) registering
|
|
1336
1339
|
* the SAME provider, request-derived events (signatures, transactions)
|
|
1337
|
-
* go to the most recently
|
|
1338
|
-
*
|
|
1339
|
-
* wrapper. Lifecycle events (connect, chain, disconnect) reach every
|
|
1340
|
+
* go to the most recently CREATED live instance, regardless of the
|
|
1341
|
+
* order registrations happen to land in - the same single-observer
|
|
1342
|
+
* semantics discovery has always had for the request wrapper. Lifecycle events (connect, chain, disconnect) reach every
|
|
1340
1343
|
* instance. Fanning request observations out to all instances is a
|
|
1341
1344
|
* separate feature.
|
|
1342
1345
|
*
|
|
@@ -1360,15 +1363,43 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1360
1363
|
* gap. Lifecycle (connect/chain/disconnect) stays store-driven: only the
|
|
1361
1364
|
* request wrapper installs here. Double counting is prevented in the
|
|
1362
1365
|
* wrapper via `shouldSkipRequestCapture`.
|
|
1366
|
+
*
|
|
1367
|
+
* `attribution` resolves, live, the name and rdns of the connector this
|
|
1368
|
+
* provider was wrapped for. Request-derived events are named from the
|
|
1369
|
+
* registry, which for an unannounced provider falls back to flag
|
|
1370
|
+
* sniffing; recording the connector's resolver here keeps them in
|
|
1371
|
+
* agreement with the hook-driven events for that connector.
|
|
1363
1372
|
*/
|
|
1364
|
-
FormoAnalytics.prototype._wrapWagmiProvider = function (provider) {
|
|
1373
|
+
FormoAnalytics.prototype._wrapWagmiProvider = function (provider, chainId, attribution) {
|
|
1365
1374
|
if (this.isCleanedUp || !isValidProvider(provider))
|
|
1366
|
-
return;
|
|
1375
|
+
return false;
|
|
1367
1376
|
try {
|
|
1368
|
-
|
|
1377
|
+
// The tracker reports refusals (frozen provider, unrebindable
|
|
1378
|
+
// wrapper) by returning false rather than throwing; treat those as
|
|
1379
|
+
// failures too so the caller can retry later.
|
|
1380
|
+
if (!this.evmRequests.registerRequestListeners(provider)) {
|
|
1381
|
+
return false;
|
|
1382
|
+
}
|
|
1383
|
+
this.evm.rememberAttribution(provider, attribution);
|
|
1384
|
+
if (chainId !== undefined) {
|
|
1385
|
+
this.evm.rememberChain(provider, chainId);
|
|
1386
|
+
}
|
|
1387
|
+
return true;
|
|
1369
1388
|
}
|
|
1370
1389
|
catch (e) {
|
|
1371
1390
|
logger.warn("Failed to wrap wagmi provider for hybrid capture", e);
|
|
1391
|
+
return false;
|
|
1392
|
+
}
|
|
1393
|
+
};
|
|
1394
|
+
/** INTERNAL. Chain updates for the fallback-wrapped provider. */
|
|
1395
|
+
FormoAnalytics.prototype._rememberWagmiProviderChain = function (provider, chainId) {
|
|
1396
|
+
if (this.isCleanedUp)
|
|
1397
|
+
return;
|
|
1398
|
+
try {
|
|
1399
|
+
this.evm.rememberChain(provider, chainId);
|
|
1400
|
+
}
|
|
1401
|
+
catch (_a) {
|
|
1402
|
+
/* bookkeeping only */
|
|
1372
1403
|
}
|
|
1373
1404
|
};
|
|
1374
1405
|
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 () {
|