@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
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
2
13
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
14
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
15
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -35,6 +46,15 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
35
46
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
47
|
}
|
|
37
48
|
};
|
|
49
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
50
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
51
|
+
if (ar || !(i in from)) {
|
|
52
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
53
|
+
ar[i] = from[i];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
57
|
+
};
|
|
38
58
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
59
|
exports.EvmEventTracker = void 0;
|
|
40
60
|
var mipd_1 = require("mipd");
|
|
@@ -58,11 +78,71 @@ var PROVIDER_SWITCH_REASONS = {
|
|
|
58
78
|
* deciding when a connect has to be reported, when a switch is stale, and
|
|
59
79
|
* when a provider has stopped being the one we follow.
|
|
60
80
|
*/
|
|
81
|
+
/**
|
|
82
|
+
* A registered provider's current accounts, from its synchronous state.
|
|
83
|
+
*
|
|
84
|
+
* `provider.accounts` first - but a LIVE MetaMask Mobile session over
|
|
85
|
+
* WalletConnect has been observed with `accounts` EMPTY while the session's
|
|
86
|
+
* namespaces held the approved account ("eip155:11155111:0xabc..."), which
|
|
87
|
+
* silently defeated adoption. The namespaces are the session's ground
|
|
88
|
+
* truth, so they are the fallback. Still purely synchronous property
|
|
89
|
+
* reads; nothing goes on the wallet transport.
|
|
90
|
+
*/
|
|
91
|
+
function readProviderAccounts(provider) {
|
|
92
|
+
var _a;
|
|
93
|
+
var direct = provider.accounts;
|
|
94
|
+
if (Array.isArray(direct) &&
|
|
95
|
+
direct.length > 0 &&
|
|
96
|
+
direct.every(function (a) { return typeof a === "string"; })) {
|
|
97
|
+
return direct;
|
|
98
|
+
}
|
|
99
|
+
var session = provider.session;
|
|
100
|
+
// eip155 ONLY: a session can also carry Solana or other namespaces, and
|
|
101
|
+
// feeding a non-EVM address into the EVM adoption path would make
|
|
102
|
+
// validation reject it and drop the whole adoption. A session can also
|
|
103
|
+
// authorize DIFFERENT accounts per chain, so entries for the provider's
|
|
104
|
+
// active chain come first - the adopted address should be the one this
|
|
105
|
+
// chain actually authorized.
|
|
106
|
+
var ns = (_a = session === null || session === void 0 ? void 0 : session.namespaces) === null || _a === void 0 ? void 0 : _a.eip155;
|
|
107
|
+
var chainId = provider.chainId;
|
|
108
|
+
var parsed = typeof chainId === "number"
|
|
109
|
+
? chainId
|
|
110
|
+
: typeof chainId === "string"
|
|
111
|
+
? (0, chain_1.parseChainId)(chainId)
|
|
112
|
+
: undefined;
|
|
113
|
+
var activePrefix = parsed ? "eip155:".concat(parsed, ":") : undefined;
|
|
114
|
+
var forChain = [];
|
|
115
|
+
var others = [];
|
|
116
|
+
if (Array.isArray(ns === null || ns === void 0 ? void 0 : ns.accounts)) {
|
|
117
|
+
for (var _i = 0, _b = ns.accounts; _i < _b.length; _i++) {
|
|
118
|
+
var entry = _b[_i];
|
|
119
|
+
if (typeof entry !== "string" || !entry.startsWith("eip155:"))
|
|
120
|
+
continue;
|
|
121
|
+
var address = entry.split(":")[2];
|
|
122
|
+
if (!address)
|
|
123
|
+
continue;
|
|
124
|
+
var bucket = activePrefix && entry.startsWith(activePrefix) ? forChain : others;
|
|
125
|
+
if (!bucket.includes(address))
|
|
126
|
+
bucket.push(address);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
var out = __spreadArray(__spreadArray([], forChain, true), others.filter(function (a) { return !forChain.includes(a); }), true);
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
61
132
|
var EvmEventTracker = /** @class */ (function () {
|
|
62
133
|
function EvmEventTracker(wallet, registry, deps) {
|
|
63
134
|
this.wallet = wallet;
|
|
64
135
|
this.registry = registry;
|
|
65
136
|
this.deps = deps;
|
|
137
|
+
/**
|
|
138
|
+
* Providers adopted through `registerProvider` rather than discovered.
|
|
139
|
+
* Announcement-driven cleanup must not touch them: they are never in an
|
|
140
|
+
* announcement list, so "missing from the announcement" is their normal
|
|
141
|
+
* state, not evidence of removal. A Set rather than a WeakSet because
|
|
142
|
+
* suppressed adoptions retry from it; the registry holds these providers
|
|
143
|
+
* strongly anyway, and untrack removes them.
|
|
144
|
+
*/
|
|
145
|
+
this.externallyRegistered = new Set();
|
|
66
146
|
/**
|
|
67
147
|
* The connect this SDK has already reported for a provider.
|
|
68
148
|
*
|
|
@@ -190,6 +270,103 @@ var EvmEventTracker = /** @class */ (function () {
|
|
|
190
270
|
logger_1.logger.error("Failed to track EIP-6963 providers during initialization:", error);
|
|
191
271
|
}
|
|
192
272
|
};
|
|
273
|
+
/**
|
|
274
|
+
* Adopt a provider the page constructed rather than announced.
|
|
275
|
+
*
|
|
276
|
+
* Discovery only ever sees EIP-6963 announcements and `window.ethereum`.
|
|
277
|
+
* A WalletConnect or Ledger provider is a constructed object that does
|
|
278
|
+
* neither, so without this entry point its whole session is invisible -
|
|
279
|
+
* the P-2403 gap. The pipeline from here on is the same one every
|
|
280
|
+
* discovered provider takes: registry, detect event, listeners, request
|
|
281
|
+
* wrapper.
|
|
282
|
+
*
|
|
283
|
+
* A session that already exists at registration is seeded from the
|
|
284
|
+
* provider's SYNCHRONOUS `accounts` state (WalletConnect exposes it), via
|
|
285
|
+
* the same accounts-arrival path a live `accountsChanged` takes. No RPC:
|
|
286
|
+
* nothing analytics-only may go on a wallet's transport, and
|
|
287
|
+
* WalletConnect's serialised relay socket is the very case that rule
|
|
288
|
+
* exists for.
|
|
289
|
+
*/
|
|
290
|
+
EvmEventTracker.prototype.adoptExternalProvider = function (detail) {
|
|
291
|
+
var provider = detail.provider;
|
|
292
|
+
// Exempt from announcement-driven cleanup BEFORE tracking: a registered
|
|
293
|
+
// provider is never in an EIP-6963 announcement, so without this the
|
|
294
|
+
// next wallet announcement would untrack it and its events would stop.
|
|
295
|
+
this.externallyRegistered.add(provider);
|
|
296
|
+
this.registry.add(detail);
|
|
297
|
+
this.trackProviders([detail]);
|
|
298
|
+
// Adoption and success both hinge on the wrapper actually installing:
|
|
299
|
+
// reporting success for a provider whose requests stay invisible would
|
|
300
|
+
// recreate the silent loss this API exists to close.
|
|
301
|
+
if (!this.registry.isTracked(provider)) {
|
|
302
|
+
this.externallyRegistered.delete(provider);
|
|
303
|
+
// Tracking got partway: lifecycle listeners may already be attached
|
|
304
|
+
// even though the request wrapper failed. Leaving them would leak
|
|
305
|
+
// callbacks that hold this instance for the life of the page.
|
|
306
|
+
this.untrackProvider(provider);
|
|
307
|
+
logger_1.logger.warn("adoptExternalProvider: provider could not be tracked");
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
// A provider that was ALREADY tracked skips the pipeline above, and
|
|
311
|
+
// "tracked" means lifecycle listeners - it says nothing about the
|
|
312
|
+
// request wrapper, which a wallet can have replaced since. Re-verify
|
|
313
|
+
// it on every registration: the call reinstalls a displaced wrapper,
|
|
314
|
+
// rebinds ownership of an intact one, and refuses when it cannot -
|
|
315
|
+
// and success here must mean capture actually works.
|
|
316
|
+
if (!this.deps.registerRequestListeners(provider)) {
|
|
317
|
+
this.externallyRegistered.delete(provider);
|
|
318
|
+
this.untrackProvider(provider);
|
|
319
|
+
logger_1.logger.warn("adoptExternalProvider: request wrapper could not be ensured");
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
// Detect with the LIVE name (peer-resolved when a session exists);
|
|
323
|
+
// the stored metadata stays generic so later sessions rename freely.
|
|
324
|
+
void this.detectWallets([
|
|
325
|
+
__assign(__assign({}, detail), { info: __assign(__assign({}, detail.info), this.registry.infoFor(provider)) }),
|
|
326
|
+
]);
|
|
327
|
+
var accounts = readProviderAccounts(provider);
|
|
328
|
+
if (accounts.length > 0) {
|
|
329
|
+
void this.onAccountsChanged(provider, accounts);
|
|
330
|
+
}
|
|
331
|
+
return true;
|
|
332
|
+
};
|
|
333
|
+
/**
|
|
334
|
+
* Re-run session adoption for every registered external provider.
|
|
335
|
+
*
|
|
336
|
+
* Registration while tracking was suppressed (opt-out, excluded route)
|
|
337
|
+
* reached the adoption path and was refused - and a provider whose
|
|
338
|
+
* session already exists may never emit another accountsChanged, so
|
|
339
|
+
* nothing would ever retry. Called when suppression can have ended
|
|
340
|
+
* (opt-in, page navigation). Idempotent: an already-adopted wallet is
|
|
341
|
+
* deduplicated by the same state and markers as any repeated signal.
|
|
342
|
+
*/
|
|
343
|
+
EvmEventTracker.prototype.retryExternalAdoptions = function () {
|
|
344
|
+
var _this = this;
|
|
345
|
+
this.externallyRegistered.forEach(function (provider) {
|
|
346
|
+
// A flagless provider registered BEFORE pairing detected as the
|
|
347
|
+
// generic injected identity; once the session's peer exists, the
|
|
348
|
+
// live identity differs and the corrected detect fires. The
|
|
349
|
+
// session-scoped rdns dedup keeps this from repeating.
|
|
350
|
+
var live = _this.registry.infoFor(provider);
|
|
351
|
+
if (live.rdns === "com.walletconnect") {
|
|
352
|
+
void _this.detectWallets([
|
|
353
|
+
{
|
|
354
|
+
info: {
|
|
355
|
+
name: live.name,
|
|
356
|
+
rdns: live.rdns,
|
|
357
|
+
uuid: "corrected-com-walletconnect",
|
|
358
|
+
icon: provider_1.DEFAULT_PROVIDER_ICON,
|
|
359
|
+
},
|
|
360
|
+
provider: provider,
|
|
361
|
+
},
|
|
362
|
+
]);
|
|
363
|
+
}
|
|
364
|
+
var accounts = readProviderAccounts(provider);
|
|
365
|
+
if (accounts.length > 0) {
|
|
366
|
+
void _this.onAccountsChanged(provider, accounts);
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
};
|
|
193
370
|
EvmEventTracker.prototype.registerAccountsChangedListener = function (provider) {
|
|
194
371
|
var _this = this;
|
|
195
372
|
logger_1.logger.info("registerAccountsChangedListener");
|
|
@@ -1004,6 +1181,11 @@ var EvmEventTracker = /** @class */ (function () {
|
|
|
1004
1181
|
var currentProviderInstances = new Set(current.map(function (detail) { return detail.provider; }));
|
|
1005
1182
|
for (var _i = 0, _a = this.registry.trackedProviders(); _i < _a.length; _i++) {
|
|
1006
1183
|
var provider = _a[_i];
|
|
1184
|
+
// A registered external provider is never announced over EIP-6963;
|
|
1185
|
+
// its absence from an announcement list says nothing about it.
|
|
1186
|
+
if (this.externallyRegistered.has(provider)) {
|
|
1187
|
+
continue;
|
|
1188
|
+
}
|
|
1007
1189
|
if (!currentProviderInstances.has(provider)) {
|
|
1008
1190
|
logger_1.logger.info("Cleaning up unavailable provider: ".concat(provider.constructor.name));
|
|
1009
1191
|
this.untrackProvider(provider);
|
|
@@ -143,11 +143,33 @@ var EvmProviderRegistry = /** @class */ (function () {
|
|
|
143
143
|
*/
|
|
144
144
|
EvmProviderRegistry.prototype.infoFor = function (provider) {
|
|
145
145
|
var announced = this.details.find(function (p) { return p.provider === provider; });
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
var info = announced
|
|
147
|
+
? { name: announced.info.name, rdns: announced.info.rdns }
|
|
148
|
+
: (function () {
|
|
149
|
+
var injected = (0, provider_1.detectInjectedProviderInfo)(provider);
|
|
150
|
+
return { name: injected.name, rdns: injected.rdns };
|
|
151
|
+
})();
|
|
152
|
+
// WalletConnect names the TRANSPORT; the session's peer names the
|
|
153
|
+
// wallet. Resolved live, per read: a session established after the
|
|
154
|
+
// provider was registered still gets its signer's name onto every
|
|
155
|
+
// event from then on. Only GENERIC names are replaced - a caller who
|
|
156
|
+
// registered an explicit display name keeps it. "Injected Provider"
|
|
157
|
+
// is included because a flagless WalletConnect-compatible provider
|
|
158
|
+
// registered BEFORE its session exists detects as nothing at all;
|
|
159
|
+
// the peer appearing later is itself the proof of what it was, so
|
|
160
|
+
// the rdns upgrades with it.
|
|
161
|
+
if (info.name === "WalletConnect" || info.name === "Injected Provider") {
|
|
162
|
+
var peer = (0, provider_1.readWalletConnectPeer)(provider);
|
|
163
|
+
if (peer === null || peer === void 0 ? void 0 : peer.name) {
|
|
164
|
+
return {
|
|
165
|
+
name: peer.name,
|
|
166
|
+
rdns: info.rdns === "io.injected.provider"
|
|
167
|
+
? "com.walletconnect"
|
|
168
|
+
: info.rdns,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
148
171
|
}
|
|
149
|
-
|
|
150
|
-
return { name: injected.name, rdns: injected.rdns };
|
|
172
|
+
return info;
|
|
151
173
|
};
|
|
152
174
|
// ── listener bookkeeping ─────────────────────────────────────────────────
|
|
153
175
|
EvmProviderRegistry.prototype.addListener = function (provider, event, listener) {
|
|
@@ -5,6 +5,13 @@ import { AutocaptureEventType } from "../tracking/TrackingPolicy";
|
|
|
5
5
|
/** What the request tracker needs from the SDK that owns it. */
|
|
6
6
|
export interface EvmRequestTrackerDeps {
|
|
7
7
|
isAutocaptureEnabled(eventType: AutocaptureEventType): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Hybrid-capture dedup: true when a PENDING wagmi mutation already covers
|
|
10
|
+
* this request, so the mutation handler owns the capture. TanStack sets a
|
|
11
|
+
* mutation pending BEFORE its mutationFn issues the wallet call, so a
|
|
12
|
+
* hook-driven request always matches; an imperative one never does.
|
|
13
|
+
*/
|
|
14
|
+
shouldSkipRequestCapture?(method: string, params: unknown[]): boolean;
|
|
8
15
|
signature(params: {
|
|
9
16
|
status: SignatureStatus;
|
|
10
17
|
chainId?: ChainID;
|
|
@@ -23,16 +30,6 @@ export interface EvmRequestTrackerDeps {
|
|
|
23
30
|
function_args?: Record<string, unknown>;
|
|
24
31
|
}, properties?: IFormoEventProperties): Promise<void>;
|
|
25
32
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Autocapture for signatures and transactions, by wrapping a provider's
|
|
28
|
-
* `request`.
|
|
29
|
-
*
|
|
30
|
-
* The wrapper is deliberately thin: it observes the call the dapp was already
|
|
31
|
-
* making and never issues one of its own. That rule is why the chain a
|
|
32
|
-
* request ran on is read from `eth_chainId` calls the app makes, rather than
|
|
33
|
-
* probed - an SDK-issued lookup on a serialising transport can wedge the
|
|
34
|
-
* user's wallet, which is never an acceptable price for a label.
|
|
35
|
-
*/
|
|
36
33
|
export declare class EvmRequestTracker {
|
|
37
34
|
private readonly wallet;
|
|
38
35
|
private readonly registry;
|
|
@@ -50,6 +47,8 @@ export declare class EvmRequestTracker {
|
|
|
50
47
|
constructor(wallet: WalletStateStore, registry: EvmProviderRegistry, deps: EvmRequestTrackerDeps);
|
|
51
48
|
/** Stop every poll in flight. Terminal, like the event queue's close(). */
|
|
52
49
|
cleanup(): void;
|
|
50
|
+
/** Providers whose owner list includes this instance; pruned on cleanup. */
|
|
51
|
+
private wrappedProviders;
|
|
53
52
|
/** Re-arm a poll, unless this tracker has been torn down. */
|
|
54
53
|
private schedulePoll;
|
|
55
54
|
/**
|
|
@@ -58,7 +57,38 @@ export declare class EvmRequestTracker {
|
|
|
58
57
|
* never retry it, and every signature and transaction from that wallet
|
|
59
58
|
* would be missed for the rest of the session.
|
|
60
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;
|
|
61
72
|
registerRequestListeners(provider: EIP1193Provider): boolean;
|
|
73
|
+
/** Install the wrapper function onto the provider; separated so the
|
|
74
|
+
* routing shim above stays small. */
|
|
75
|
+
private installWrappedRequest;
|
|
76
|
+
/**
|
|
77
|
+
* The wrapper body proper: everything a request observation does, run
|
|
78
|
+
* against THIS instance's registry, wallet state, and event queue. Kept
|
|
79
|
+
* as a method so a surviving wrapper installed by a previous SDK
|
|
80
|
+
* instance can hand calls to the current one.
|
|
81
|
+
*/
|
|
82
|
+
private dispatchWrappedRequest;
|
|
83
|
+
/**
|
|
84
|
+
* Wallet attribution for request-derived events.
|
|
85
|
+
*
|
|
86
|
+
* Live per read through the registry, so a WalletConnect session names
|
|
87
|
+
* its actual signer ("MetaMask Wallet", "Ledger Live") - the live-test
|
|
88
|
+
* rows had provider_name EMPTY on every signature and transaction, which
|
|
89
|
+
* made per-wallet activity unanswerable in the warehouse.
|
|
90
|
+
*/
|
|
91
|
+
private attributionFor;
|
|
62
92
|
private buildSignatureEventPayload;
|
|
63
93
|
private buildTransactionEventPayload;
|
|
64
94
|
/**
|