@formo/analytics 1.37.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 +66 -0
- package/dist/cjs/src/FormoAnalytics.js +176 -5
- package/dist/cjs/src/FormoAnalyticsProvider.d.ts +8 -0
- package/dist/cjs/src/FormoAnalyticsProvider.js +42 -32
- package/dist/cjs/src/evm/EvmEventTracker.d.ts +38 -10
- package/dist/cjs/src/evm/EvmEventTracker.js +182 -0
- package/dist/cjs/src/evm/EvmProviderRegistry.js +26 -4
- package/dist/cjs/src/evm/EvmRequestTracker.d.ts +40 -10
- package/dist/cjs/src/evm/EvmRequestTracker.js +219 -59
- package/dist/cjs/src/provider/detection.d.ts +30 -0
- package/dist/cjs/src/provider/detection.js +62 -0
- package/dist/cjs/src/provider/index.d.ts +1 -1
- package/dist/cjs/src/provider/index.js +3 -1
- package/dist/cjs/src/types/base.d.ts +23 -0
- package/dist/cjs/src/types/provider.d.ts +16 -0
- package/dist/cjs/src/types/provider.js +17 -1
- 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 +81 -0
- package/dist/cjs/src/wagmi/WagmiEventHandler.js +478 -38
- package/dist/esm/src/FormoAnalytics.d.ts +66 -0
- package/dist/esm/src/FormoAnalytics.js +176 -5
- package/dist/esm/src/FormoAnalyticsProvider.d.ts +8 -0
- package/dist/esm/src/FormoAnalyticsProvider.js +40 -31
- package/dist/esm/src/evm/EvmEventTracker.d.ts +38 -10
- package/dist/esm/src/evm/EvmEventTracker.js +183 -1
- package/dist/esm/src/evm/EvmProviderRegistry.js +27 -5
- package/dist/esm/src/evm/EvmRequestTracker.d.ts +40 -10
- package/dist/esm/src/evm/EvmRequestTracker.js +218 -58
- package/dist/esm/src/provider/detection.d.ts +30 -0
- package/dist/esm/src/provider/detection.js +60 -0
- package/dist/esm/src/provider/index.d.ts +1 -1
- package/dist/esm/src/provider/index.js +1 -1
- package/dist/esm/src/types/base.d.ts +23 -0
- package/dist/esm/src/types/provider.d.ts +16 -0
- package/dist/esm/src/types/provider.js +16 -0
- 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 +81 -0
- package/dist/esm/src/wagmi/WagmiEventHandler.js +478 -38
- package/dist/index.umd.min.js +1 -1
- package/package.json +2 -2
|
@@ -96,6 +96,8 @@ export declare class FormoAnalytics implements IFormoAnalytics {
|
|
|
96
96
|
* Call this when destroying the analytics instance
|
|
97
97
|
* @returns {void}
|
|
98
98
|
*/
|
|
99
|
+
/** Set by cleanup(); a torn-down instance refuses new registrations. */
|
|
100
|
+
private isCleanedUp;
|
|
99
101
|
cleanup(): void;
|
|
100
102
|
/**
|
|
101
103
|
* Emits a connect wallet event.
|
|
@@ -373,6 +375,70 @@ export declare class FormoAnalytics implements IFormoAnalytics {
|
|
|
373
375
|
*/
|
|
374
376
|
get solana(): SolanaManager;
|
|
375
377
|
private getCurrentChainId;
|
|
378
|
+
/**
|
|
379
|
+
* Track an EIP-1193 provider the page constructed itself.
|
|
380
|
+
*
|
|
381
|
+
* Discovery covers EIP-6963 announcements and `window.ethereum`, which is
|
|
382
|
+
* every injected wallet and nothing else. WalletConnect and Ledger
|
|
383
|
+
* providers are built by the app (`EthereumProvider.init(...)`) and
|
|
384
|
+
* announce nothing, so their sessions were invisible: connects,
|
|
385
|
+
* signatures, and transactions all silently missing. Hand the provider
|
|
386
|
+
* here once it exists and it takes the exact pipeline a discovered
|
|
387
|
+
* provider takes - detect event, lifecycle listeners, request wrapper -
|
|
388
|
+
* and a session that is already live is adopted from the provider's
|
|
389
|
+
* synchronous state.
|
|
390
|
+
*
|
|
391
|
+
* Metadata resolution order: the caller's `info` overrides win; then a
|
|
392
|
+
* WalletConnect session's peer metadata, which names the REAL wallet on
|
|
393
|
+
* the far side of the transport (for example "Ledger Live"); then flag
|
|
394
|
+
* sniffing; then a generic fallback. One deliberate exception: a caller
|
|
395
|
+
* name of exactly "WalletConnect" is the generic transport name, so the
|
|
396
|
+
* live peer still replaces it on events - name the provider anything
|
|
397
|
+
* else to pin it verbatim.
|
|
398
|
+
*
|
|
399
|
+
* No-op outside the EIP-1193 path: in wagmi mode the connector system
|
|
400
|
+
* already tracks these sessions, and wrapping the same provider twice
|
|
401
|
+
* would double-report every event.
|
|
402
|
+
*
|
|
403
|
+
* With several live SDK instances (multi write-key pages) registering
|
|
404
|
+
* the SAME provider, request-derived events (signatures, transactions)
|
|
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
|
+
* instance. Fanning request observations out to all instances is a
|
|
409
|
+
* separate feature.
|
|
410
|
+
*
|
|
411
|
+
* @returns true when the provider is (now) tracked, false when it was
|
|
412
|
+
* refused (wagmi mode, EVM disabled, or not a valid EIP-1193 provider).
|
|
413
|
+
*
|
|
414
|
+
* @example
|
|
415
|
+
* ```typescript
|
|
416
|
+
* const wcProvider = await EthereumProvider.init({ projectId, chains });
|
|
417
|
+
* formo.registerProvider(wcProvider);
|
|
418
|
+
* ```
|
|
419
|
+
*/
|
|
420
|
+
/**
|
|
421
|
+
* INTERNAL. Install the request wrapper on a wagmi connector's provider.
|
|
422
|
+
*
|
|
423
|
+
* Wagmi mode watches the store and caches, which see hook-driven calls
|
|
424
|
+
* only; imperative viem calls (walletClient.sendTransaction,
|
|
425
|
+
* .signMessage, .writeContract, raw request) create no mutation and were
|
|
426
|
+
* silently lost. Every viem client in a wagmi app is built on the
|
|
427
|
+
* connector's EIP-1193 provider, so wrapping that provider closes the
|
|
428
|
+
* gap. Lifecycle (connect/chain/disconnect) stays store-driven: only the
|
|
429
|
+
* request wrapper installs here. Double counting is prevented in the
|
|
430
|
+
* wrapper via `shouldSkipRequestCapture`.
|
|
431
|
+
*/
|
|
432
|
+
_wrapWagmiProvider(provider: EIP1193Provider, chainId?: number): boolean;
|
|
433
|
+
/** INTERNAL. Chain updates for the fallback-wrapped provider. */
|
|
434
|
+
_rememberWagmiProviderChain(provider: EIP1193Provider, chainId: number | undefined): void;
|
|
435
|
+
registerProvider(provider: EIP1193Provider, info?: {
|
|
436
|
+
name?: string;
|
|
437
|
+
rdns?: string;
|
|
438
|
+
icon?: `data:image/${string}`;
|
|
439
|
+
}): boolean;
|
|
440
|
+
/** Fallback uuid suffix for platforms without crypto.randomUUID. */
|
|
441
|
+
private static registeredProviderSeq;
|
|
376
442
|
getTrackedProvidersCount(): number;
|
|
377
443
|
/**
|
|
378
444
|
* Get current provider state for debugging
|
|
@@ -57,6 +57,7 @@ import { validateAddress, validateAndChecksumAddress } from "./utils/address";
|
|
|
57
57
|
import { TrackingPolicy, } from "./tracking/TrackingPolicy";
|
|
58
58
|
import { WalletStateStore } from "./wallet/WalletStateStore";
|
|
59
59
|
import { EvmProviderRegistry } from "./evm/EvmProviderRegistry";
|
|
60
|
+
import { detectInjectedProviderInfo, isValidProvider, readWalletConnectPeer, } from "./provider";
|
|
60
61
|
import { EvmEventTracker } from "./evm/EvmEventTracker";
|
|
61
62
|
import { EvmRequestTracker } from "./evm/EvmRequestTracker";
|
|
62
63
|
import { parseChainId } from "./utils/chain";
|
|
@@ -90,6 +91,13 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
90
91
|
this._currentUrl = "";
|
|
91
92
|
this._pageHooksDisposed = false;
|
|
92
93
|
this.currentUserId = "";
|
|
94
|
+
/**
|
|
95
|
+
* Clean up resources and event listeners
|
|
96
|
+
* Call this when destroying the analytics instance
|
|
97
|
+
* @returns {void}
|
|
98
|
+
*/
|
|
99
|
+
/** Set by cleanup(); a torn-down instance refuses new registrations. */
|
|
100
|
+
this.isCleanedUp = false;
|
|
93
101
|
this.config = {
|
|
94
102
|
writeKey: writeKey,
|
|
95
103
|
};
|
|
@@ -111,6 +119,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
111
119
|
this.transaction = this.transaction.bind(this);
|
|
112
120
|
this.detect = this.detect.bind(this);
|
|
113
121
|
this.track = this.track.bind(this);
|
|
122
|
+
this.registerProvider = this.registerProvider.bind(this);
|
|
114
123
|
this.page = this.page.bind(this);
|
|
115
124
|
this.reset = this.reset.bind(this);
|
|
116
125
|
this.cleanup = this.cleanup.bind(this);
|
|
@@ -168,6 +177,11 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
168
177
|
isAutocaptureEnabled: function (t) { return _this.isAutocaptureEnabled(t); },
|
|
169
178
|
signature: function (params, properties) { return _this.signature(params, properties); },
|
|
170
179
|
transaction: function (params, properties) { return _this.transaction(params, properties); },
|
|
180
|
+
// Hybrid capture: in wagmi mode the wrapper skips a request that a
|
|
181
|
+
// PENDING wagmi mutation already covers - the mutation handler
|
|
182
|
+
// captures it with ABI enrichment - and captures everything else
|
|
183
|
+
// (imperative viem calls that create no mutation).
|
|
184
|
+
shouldSkipRequestCapture: function (method, params) { var _a, _b; return (_b = (_a = _this.wagmiHandler) === null || _a === void 0 ? void 0 : _a.hasMatchingPendingMutation(method, params)) !== null && _b !== void 0 ? _b : false; },
|
|
171
185
|
});
|
|
172
186
|
this.evmEvents = new EvmEventTracker(this.wallet, this.evm, {
|
|
173
187
|
isAutocaptureEnabled: function (t) { return _this.isAutocaptureEnabled(t); },
|
|
@@ -373,12 +387,8 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
373
387
|
// re-attached to the next session's events.
|
|
374
388
|
session().remove(SESSION_TRAFFIC_SOURCE_KEY);
|
|
375
389
|
};
|
|
376
|
-
/**
|
|
377
|
-
* Clean up resources and event listeners
|
|
378
|
-
* Call this when destroying the analytics instance
|
|
379
|
-
* @returns {void}
|
|
380
|
-
*/
|
|
381
390
|
FormoAnalytics.prototype.cleanup = function () {
|
|
391
|
+
this.isCleanedUp = true;
|
|
382
392
|
logger.debug("FormoAnalytics: Cleaning up resources");
|
|
383
393
|
// Close the queue, don't just empty it. clear() only drops what is
|
|
384
394
|
// buffered at this instant; asynchronous work already in flight (event
|
|
@@ -1014,7 +1024,21 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1014
1024
|
* @returns {void}
|
|
1015
1025
|
*/
|
|
1016
1026
|
FormoAnalytics.prototype.optInTracking = function () {
|
|
1027
|
+
var _this = this;
|
|
1017
1028
|
var _a;
|
|
1029
|
+
// A provider registered while the visitor was opted out had its
|
|
1030
|
+
// session adoption refused; nothing else retries it. Guarded: a
|
|
1031
|
+
// cleanup() racing this timer must not drive the torn-down tracker.
|
|
1032
|
+
setTimeout(function () {
|
|
1033
|
+
if (_this.isCleanedUp)
|
|
1034
|
+
return;
|
|
1035
|
+
try {
|
|
1036
|
+
_this.evmEvents.retryExternalAdoptions();
|
|
1037
|
+
}
|
|
1038
|
+
catch (_a) {
|
|
1039
|
+
/* adoption retry must never break opt-in */
|
|
1040
|
+
}
|
|
1041
|
+
}, 0);
|
|
1018
1042
|
logger.info("Opting back into tracking");
|
|
1019
1043
|
// Remove opt-out flag
|
|
1020
1044
|
removeConsentFlag(this.writeKey, CONSENT_OPT_OUT_KEY);
|
|
@@ -1105,6 +1129,17 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1105
1129
|
return __awaiter(this, void 0, void 0, function () {
|
|
1106
1130
|
var _this = this;
|
|
1107
1131
|
return __generator(this, function (_a) {
|
|
1132
|
+
// A route change can end path-based suppression; a provider registered
|
|
1133
|
+
// while suppressed gets its refused session adoption retried here.
|
|
1134
|
+
// Idempotent and cheap when nothing is pending.
|
|
1135
|
+
if (!this.isCleanedUp) {
|
|
1136
|
+
try {
|
|
1137
|
+
this.evmEvents.retryExternalAdoptions();
|
|
1138
|
+
}
|
|
1139
|
+
catch (_b) {
|
|
1140
|
+
/* never let the retry break a page hit */
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1108
1143
|
if (!this.shouldTrack()) {
|
|
1109
1144
|
logger.info("Track page hit: Skipping event due to tracking configuration");
|
|
1110
1145
|
return [2 /*return*/];
|
|
@@ -1272,6 +1307,140 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1272
1307
|
};
|
|
1273
1308
|
// Explicitly untrack a provider: remove listeners, clear wrapper flag
|
|
1274
1309
|
// and tracking
|
|
1310
|
+
/**
|
|
1311
|
+
* Track an EIP-1193 provider the page constructed itself.
|
|
1312
|
+
*
|
|
1313
|
+
* Discovery covers EIP-6963 announcements and `window.ethereum`, which is
|
|
1314
|
+
* every injected wallet and nothing else. WalletConnect and Ledger
|
|
1315
|
+
* providers are built by the app (`EthereumProvider.init(...)`) and
|
|
1316
|
+
* announce nothing, so their sessions were invisible: connects,
|
|
1317
|
+
* signatures, and transactions all silently missing. Hand the provider
|
|
1318
|
+
* here once it exists and it takes the exact pipeline a discovered
|
|
1319
|
+
* provider takes - detect event, lifecycle listeners, request wrapper -
|
|
1320
|
+
* and a session that is already live is adopted from the provider's
|
|
1321
|
+
* synchronous state.
|
|
1322
|
+
*
|
|
1323
|
+
* Metadata resolution order: the caller's `info` overrides win; then a
|
|
1324
|
+
* WalletConnect session's peer metadata, which names the REAL wallet on
|
|
1325
|
+
* the far side of the transport (for example "Ledger Live"); then flag
|
|
1326
|
+
* sniffing; then a generic fallback. One deliberate exception: a caller
|
|
1327
|
+
* name of exactly "WalletConnect" is the generic transport name, so the
|
|
1328
|
+
* live peer still replaces it on events - name the provider anything
|
|
1329
|
+
* else to pin it verbatim.
|
|
1330
|
+
*
|
|
1331
|
+
* No-op outside the EIP-1193 path: in wagmi mode the connector system
|
|
1332
|
+
* already tracks these sessions, and wrapping the same provider twice
|
|
1333
|
+
* would double-report every event.
|
|
1334
|
+
*
|
|
1335
|
+
* With several live SDK instances (multi write-key pages) registering
|
|
1336
|
+
* the SAME provider, request-derived events (signatures, transactions)
|
|
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
|
+
* instance. Fanning request observations out to all instances is a
|
|
1341
|
+
* separate feature.
|
|
1342
|
+
*
|
|
1343
|
+
* @returns true when the provider is (now) tracked, false when it was
|
|
1344
|
+
* refused (wagmi mode, EVM disabled, or not a valid EIP-1193 provider).
|
|
1345
|
+
*
|
|
1346
|
+
* @example
|
|
1347
|
+
* ```typescript
|
|
1348
|
+
* const wcProvider = await EthereumProvider.init({ projectId, chains });
|
|
1349
|
+
* formo.registerProvider(wcProvider);
|
|
1350
|
+
* ```
|
|
1351
|
+
*/
|
|
1352
|
+
/**
|
|
1353
|
+
* INTERNAL. Install the request wrapper on a wagmi connector's provider.
|
|
1354
|
+
*
|
|
1355
|
+
* Wagmi mode watches the store and caches, which see hook-driven calls
|
|
1356
|
+
* only; imperative viem calls (walletClient.sendTransaction,
|
|
1357
|
+
* .signMessage, .writeContract, raw request) create no mutation and were
|
|
1358
|
+
* silently lost. Every viem client in a wagmi app is built on the
|
|
1359
|
+
* connector's EIP-1193 provider, so wrapping that provider closes the
|
|
1360
|
+
* gap. Lifecycle (connect/chain/disconnect) stays store-driven: only the
|
|
1361
|
+
* request wrapper installs here. Double counting is prevented in the
|
|
1362
|
+
* wrapper via `shouldSkipRequestCapture`.
|
|
1363
|
+
*/
|
|
1364
|
+
FormoAnalytics.prototype._wrapWagmiProvider = function (provider, chainId) {
|
|
1365
|
+
if (this.isCleanedUp || !isValidProvider(provider))
|
|
1366
|
+
return false;
|
|
1367
|
+
try {
|
|
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;
|
|
1378
|
+
}
|
|
1379
|
+
catch (e) {
|
|
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 */
|
|
1393
|
+
}
|
|
1394
|
+
};
|
|
1395
|
+
FormoAnalytics.prototype.registerProvider = function (provider, info) {
|
|
1396
|
+
var _a, _b, _c;
|
|
1397
|
+
if (this.isCleanedUp) {
|
|
1398
|
+
// Cleanup terminally closed the event queue; listeners attached now
|
|
1399
|
+
// would hold this instance forever and deliver nothing.
|
|
1400
|
+
logger.warn("registerProvider: instance is cleaned up; refusing");
|
|
1401
|
+
return false;
|
|
1402
|
+
}
|
|
1403
|
+
if (this.isEvmDisabled) {
|
|
1404
|
+
logger.warn("registerProvider: EVM tracking is disabled; refusing");
|
|
1405
|
+
return false;
|
|
1406
|
+
}
|
|
1407
|
+
if (this.isWagmiMode) {
|
|
1408
|
+
logger.warn("registerProvider: wagmi mode tracks connectors already; registering the provider here would double-report its events. Refusing.");
|
|
1409
|
+
return false;
|
|
1410
|
+
}
|
|
1411
|
+
if (!isValidProvider(provider)) {
|
|
1412
|
+
logger.warn("registerProvider: not a valid EIP-1193 provider; refusing");
|
|
1413
|
+
return false;
|
|
1414
|
+
}
|
|
1415
|
+
var detected = detectInjectedProviderInfo(provider);
|
|
1416
|
+
var peer = readWalletConnectPeer(provider);
|
|
1417
|
+
// A live peer identifies the session as WalletConnect even when the
|
|
1418
|
+
// provider carries no isWalletConnect flag (v2 providers often do not).
|
|
1419
|
+
var rdns = (_a = info === null || info === void 0 ? void 0 : info.rdns) !== null && _a !== void 0 ? _a : (peer && detected.rdns === "io.injected.provider"
|
|
1420
|
+
? "com.walletconnect"
|
|
1421
|
+
: detected.rdns);
|
|
1422
|
+
// The peer name is deliberately NOT stored: sessions change wallets,
|
|
1423
|
+
// and metadata frozen at registration would misname every later one.
|
|
1424
|
+
// `infoFor` resolves the peer live on each read, over the generic
|
|
1425
|
+
// transport name; a caller's explicit name still wins everywhere.
|
|
1426
|
+
var name = (_b = info === null || info === void 0 ? void 0 : info.name) !== null && _b !== void 0 ? _b : (peer ? "WalletConnect" : detected.name);
|
|
1427
|
+
// Per-instance uuid: EIP-6963 consumers (mipd included) deduplicate on
|
|
1428
|
+
// it, so two registered instances sharing an rdns-derived uuid would
|
|
1429
|
+
// collapse into one. Random when the platform provides it; a
|
|
1430
|
+
// monotonic suffix otherwise.
|
|
1431
|
+
var uuid = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function"
|
|
1432
|
+
? crypto.randomUUID()
|
|
1433
|
+
: "registered-".concat(rdns.replace(/[^a-zA-Z0-9]/g, "-"), "-").concat((FormoAnalytics.registeredProviderSeq += 1));
|
|
1434
|
+
return this.evmEvents.adoptExternalProvider({
|
|
1435
|
+
info: {
|
|
1436
|
+
name: name,
|
|
1437
|
+
rdns: rdns,
|
|
1438
|
+
uuid: uuid,
|
|
1439
|
+
icon: (_c = info === null || info === void 0 ? void 0 : info.icon) !== null && _c !== void 0 ? _c : detected.icon,
|
|
1440
|
+
},
|
|
1441
|
+
provider: provider,
|
|
1442
|
+
});
|
|
1443
|
+
};
|
|
1275
1444
|
// Debug/monitoring helpers
|
|
1276
1445
|
FormoAnalytics.prototype.getTrackedProvidersCount = function () {
|
|
1277
1446
|
return this.evm.counts.trackedProviders;
|
|
@@ -1283,6 +1452,8 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1283
1452
|
FormoAnalytics.prototype.getProviderState = function () {
|
|
1284
1453
|
return __assign(__assign({}, this.evm.counts), { activeProvider: !!this._provider });
|
|
1285
1454
|
};
|
|
1455
|
+
/** Fallback uuid suffix for platforms without crypto.randomUUID. */
|
|
1456
|
+
FormoAnalytics.registeredProviderSeq = 0;
|
|
1286
1457
|
return FormoAnalytics;
|
|
1287
1458
|
}());
|
|
1288
1459
|
export { FormoAnalytics };
|
|
@@ -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
|
|
@@ -55,6 +55,7 @@ var defaultContext = {
|
|
|
55
55
|
page: function () { return Promise.resolve(); },
|
|
56
56
|
reset: function () { },
|
|
57
57
|
cleanup: function () { },
|
|
58
|
+
registerProvider: function () { return false; },
|
|
58
59
|
detect: function () { return Promise.resolve(); },
|
|
59
60
|
connect: function () { return Promise.resolve(); },
|
|
60
61
|
disconnect: function () { return Promise.resolve(); },
|
|
@@ -68,6 +69,44 @@ var defaultContext = {
|
|
|
68
69
|
hasOptedOutTracking: function () { return false; },
|
|
69
70
|
};
|
|
70
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
|
+
};
|
|
71
110
|
export var FormoAnalyticsProvider = function (props) {
|
|
72
111
|
var writeKey = props.writeKey, _a = props.disabled, disabled = _a === void 0 ? false : _a, children = props.children;
|
|
73
112
|
// Keep the app running without analytics if no Write Key is provided or disabled
|
|
@@ -86,37 +125,7 @@ var InitializedAnalytics = function (_a) {
|
|
|
86
125
|
var _b = useState(defaultContext), sdk = _b[0], setSdk = _b[1];
|
|
87
126
|
var sdkRef = useRef(defaultContext);
|
|
88
127
|
initStorageManager(writeKey);
|
|
89
|
-
|
|
90
|
-
// We only care about serializable config values that would affect SDK behavior
|
|
91
|
-
var optionsKey = useMemo(function () {
|
|
92
|
-
if (!options)
|
|
93
|
-
return 'undefined';
|
|
94
|
-
// Extract only the serializable parts of options
|
|
95
|
-
var serializableOptions = {
|
|
96
|
-
tracking: options.tracking,
|
|
97
|
-
autocapture: options.autocapture,
|
|
98
|
-
crossSubdomainCookies: options.crossSubdomainCookies,
|
|
99
|
-
apiHost: options.apiHost,
|
|
100
|
-
flushAt: options.flushAt,
|
|
101
|
-
flushInterval: options.flushInterval,
|
|
102
|
-
retryCount: options.retryCount,
|
|
103
|
-
maxQueueSize: options.maxQueueSize,
|
|
104
|
-
logger: options.logger,
|
|
105
|
-
referral: options.referral,
|
|
106
|
-
// For complex objects, just track their presence, not their content
|
|
107
|
-
hasProvider: !!options.provider,
|
|
108
|
-
hasWagmi: !!options.wagmi,
|
|
109
|
-
hasReady: !!options.ready,
|
|
110
|
-
};
|
|
111
|
-
try {
|
|
112
|
-
return JSON.stringify(serializableOptions);
|
|
113
|
-
}
|
|
114
|
-
catch (error) {
|
|
115
|
-
// Fallback to timestamp if serialization fails
|
|
116
|
-
logger.warn('Failed to serialize options, using timestamp', error);
|
|
117
|
-
return Date.now().toString();
|
|
118
|
-
}
|
|
119
|
-
}, [options]);
|
|
128
|
+
var optionsKey = useMemo(function () { return computeOptionsKey(options); }, [options]);
|
|
120
129
|
useEffect(function () {
|
|
121
130
|
var isCleanedUp = false;
|
|
122
131
|
var initialize = function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
@@ -43,20 +43,19 @@ export interface EvmEventTrackerDeps {
|
|
|
43
43
|
*/
|
|
44
44
|
registerRequestListeners(provider: EIP1193Provider): boolean;
|
|
45
45
|
}
|
|
46
|
-
/**
|
|
47
|
-
* The EIP-1193 side of wallet tracking: which providers to watch, and what
|
|
48
|
-
* their events mean.
|
|
49
|
-
*
|
|
50
|
-
* Split out of `FormoAnalytics` (#336). It holds no wallet state and no
|
|
51
|
-
* provider registry of its own - those have owners already - so what is left
|
|
52
|
-
* here is the part that is genuinely about interpreting wallet events:
|
|
53
|
-
* deciding when a connect has to be reported, when a switch is stale, and
|
|
54
|
-
* when a provider has stopped being the one we follow.
|
|
55
|
-
*/
|
|
56
46
|
export declare class EvmEventTracker {
|
|
57
47
|
private readonly wallet;
|
|
58
48
|
private readonly registry;
|
|
59
49
|
private readonly deps;
|
|
50
|
+
/**
|
|
51
|
+
* Providers adopted through `registerProvider` rather than discovered.
|
|
52
|
+
* Announcement-driven cleanup must not touch them: they are never in an
|
|
53
|
+
* announcement list, so "missing from the announcement" is their normal
|
|
54
|
+
* state, not evidence of removal. A Set rather than a WeakSet because
|
|
55
|
+
* suppressed adoptions retry from it; the registry holds these providers
|
|
56
|
+
* strongly anyway, and untrack removes them.
|
|
57
|
+
*/
|
|
58
|
+
private externallyRegistered;
|
|
60
59
|
/**
|
|
61
60
|
* The connect this SDK has already reported for a provider.
|
|
62
61
|
*
|
|
@@ -91,6 +90,35 @@ export declare class EvmEventTracker {
|
|
|
91
90
|
*/
|
|
92
91
|
trackEIP1193Provider(provider: EIP1193Provider): void;
|
|
93
92
|
trackProviders(providers: readonly EIP6963ProviderDetail[]): void;
|
|
93
|
+
/**
|
|
94
|
+
* Adopt a provider the page constructed rather than announced.
|
|
95
|
+
*
|
|
96
|
+
* Discovery only ever sees EIP-6963 announcements and `window.ethereum`.
|
|
97
|
+
* A WalletConnect or Ledger provider is a constructed object that does
|
|
98
|
+
* neither, so without this entry point its whole session is invisible -
|
|
99
|
+
* the P-2403 gap. The pipeline from here on is the same one every
|
|
100
|
+
* discovered provider takes: registry, detect event, listeners, request
|
|
101
|
+
* wrapper.
|
|
102
|
+
*
|
|
103
|
+
* A session that already exists at registration is seeded from the
|
|
104
|
+
* provider's SYNCHRONOUS `accounts` state (WalletConnect exposes it), via
|
|
105
|
+
* the same accounts-arrival path a live `accountsChanged` takes. No RPC:
|
|
106
|
+
* nothing analytics-only may go on a wallet's transport, and
|
|
107
|
+
* WalletConnect's serialised relay socket is the very case that rule
|
|
108
|
+
* exists for.
|
|
109
|
+
*/
|
|
110
|
+
adoptExternalProvider(detail: EIP6963ProviderDetail): boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Re-run session adoption for every registered external provider.
|
|
113
|
+
*
|
|
114
|
+
* Registration while tracking was suppressed (opt-out, excluded route)
|
|
115
|
+
* reached the adoption path and was refused - and a provider whose
|
|
116
|
+
* session already exists may never emit another accountsChanged, so
|
|
117
|
+
* nothing would ever retry. Called when suppression can have ended
|
|
118
|
+
* (opt-in, page navigation). Idempotent: an already-adopted wallet is
|
|
119
|
+
* deduplicated by the same state and markers as any repeated signal.
|
|
120
|
+
*/
|
|
121
|
+
retryExternalAdoptions(): void;
|
|
94
122
|
private registerAccountsChangedListener;
|
|
95
123
|
private onAccountsChanged;
|
|
96
124
|
/**
|