@formo/analytics 1.11.13 → 1.11.14
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/README.md +1 -1
- package/dist/cjs/src/FormoAnalytics.d.ts +9 -3
- package/dist/cjs/src/FormoAnalytics.d.ts.map +1 -1
- package/dist/cjs/src/FormoAnalytics.js +201 -86
- package/dist/cjs/src/FormoAnalytics.js.map +1 -1
- package/dist/cjs/src/types/base.d.ts +1 -1
- package/dist/cjs/src/types/base.d.ts.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/src/FormoAnalytics.d.ts +9 -3
- package/dist/esm/src/FormoAnalytics.d.ts.map +1 -1
- package/dist/esm/src/FormoAnalytics.js +201 -86
- package/dist/esm/src/FormoAnalytics.js.map +1 -1
- package/dist/esm/src/types/base.d.ts +1 -1
- package/dist/esm/src/types/base.d.ts.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/index.umd.min.js +1 -1
- package/dist/index.umd.min.js.map +1 -1
- package/package.json +2 -2
- package/src/FormoAnalytics.ts +158 -61
- package/src/types/base.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formo/analytics",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.14",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/getformo/sdk.git"
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"sinon-chai": "^3.7.0",
|
|
64
64
|
"ts-loader": "^9.3.1",
|
|
65
65
|
"ts-node": "^10.8.2",
|
|
66
|
-
"typescript": "^5.
|
|
66
|
+
"typescript": "^5.7.2",
|
|
67
67
|
"webpack": "^5.74.0",
|
|
68
68
|
"webpack-cli": "^4.10.0"
|
|
69
69
|
},
|
package/src/FormoAnalytics.ts
CHANGED
|
@@ -12,7 +12,7 @@ interface IFormoAnalytics {
|
|
|
12
12
|
/**
|
|
13
13
|
* Initializes the FormoAnalytics instance with the provided API key and project ID.
|
|
14
14
|
*/
|
|
15
|
-
init(apiKey: string, options
|
|
15
|
+
init(apiKey: string, options?: Options): Promise<FormoAnalytics>;
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Tracks page visit events.
|
|
@@ -72,7 +72,10 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
static async init(
|
|
75
|
+
static async init(
|
|
76
|
+
apiKey: string,
|
|
77
|
+
options?: Options
|
|
78
|
+
): Promise<FormoAnalytics> {
|
|
76
79
|
const config = {
|
|
77
80
|
token: apiKey,
|
|
78
81
|
};
|
|
@@ -139,11 +142,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
139
142
|
timestamp: new Date().toISOString(),
|
|
140
143
|
action,
|
|
141
144
|
version: "1",
|
|
142
|
-
payload:
|
|
143
|
-
// common fields
|
|
144
|
-
...this.getCommonTrackingFields(),
|
|
145
|
-
...payload,
|
|
146
|
-
},
|
|
145
|
+
payload: await this.buildEventPayload(...payload),
|
|
147
146
|
};
|
|
148
147
|
|
|
149
148
|
const sendRequest = async (): Promise<void> => {
|
|
@@ -190,24 +189,71 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
190
189
|
await sendRequest();
|
|
191
190
|
}
|
|
192
191
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
192
|
+
// Function to track page hits
|
|
193
|
+
private trackPageHit() {
|
|
194
|
+
if (this.isAutomationEnvironment()) return;
|
|
195
|
+
|
|
196
|
+
const pathname = window.location.pathname;
|
|
197
|
+
const href = window.location.href;
|
|
198
|
+
|
|
199
|
+
setTimeout(async () => {
|
|
200
|
+
this.trackEvent(Event.PAGE, {
|
|
201
|
+
pathname,
|
|
202
|
+
href,
|
|
203
|
+
});
|
|
204
|
+
}, 300);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private isAutomationEnvironment(): boolean {
|
|
208
|
+
return (
|
|
209
|
+
window.__nightmare ||
|
|
210
|
+
window.navigator.webdriver ||
|
|
211
|
+
window.Cypress ||
|
|
212
|
+
false
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private getUserLocation(): string | undefined {
|
|
196
217
|
try {
|
|
197
218
|
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
219
|
+
return this.timezoneToCountry[timezone];
|
|
220
|
+
} catch (error) {
|
|
221
|
+
console.error("Error resolving timezone:", error);
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private getUserLanguage(): string {
|
|
227
|
+
try {
|
|
228
|
+
return (
|
|
229
|
+
(navigator.languages && navigator.languages.length
|
|
201
230
|
? navigator.languages[0]
|
|
202
|
-
: navigator.language || "en"
|
|
231
|
+
: navigator.language) || "en"
|
|
232
|
+
);
|
|
203
233
|
} catch (error) {
|
|
204
|
-
console.error("Error resolving
|
|
234
|
+
console.error("Error resolving language:", error);
|
|
235
|
+
return "en";
|
|
205
236
|
}
|
|
237
|
+
}
|
|
206
238
|
|
|
239
|
+
async buildEventPayload(
|
|
240
|
+
eventSpecificPayload: Record<string, unknown> = {}
|
|
241
|
+
): Promise<Record<string, unknown>> {
|
|
207
242
|
const url = new URL(window.location.href);
|
|
208
243
|
const params = new URLSearchParams(url.search);
|
|
244
|
+
|
|
245
|
+
const location = this.getUserLocation();
|
|
246
|
+
const language = this.getUserLanguage();
|
|
247
|
+
|
|
248
|
+
const address = await this.getAndStoreConnectedAddress();
|
|
249
|
+
if (address === null) {
|
|
250
|
+
console.warn("Wallet address could not be retrieved.");
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// common fields
|
|
209
254
|
return {
|
|
210
255
|
"user-agent": window.navigator.userAgent,
|
|
256
|
+
address,
|
|
211
257
|
locale: language,
|
|
212
258
|
location,
|
|
213
259
|
referrer: document.referrer,
|
|
@@ -215,24 +261,13 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
215
261
|
utm_medium: params.get("utm_medium"),
|
|
216
262
|
utm_campaign: params.get("utm_campaign"),
|
|
217
263
|
ref: params.get("ref"),
|
|
264
|
+
...eventSpecificPayload,
|
|
218
265
|
};
|
|
219
266
|
}
|
|
220
267
|
|
|
221
|
-
// Function to track page hits
|
|
222
|
-
private trackPageHit() {
|
|
223
|
-
if (window.__nightmare || window.navigator.webdriver || window.Cypress)
|
|
224
|
-
return;
|
|
225
|
-
|
|
226
|
-
setTimeout(() => {
|
|
227
|
-
this.trackEvent(Event.PAGE, {
|
|
228
|
-
pathname: window.location.pathname,
|
|
229
|
-
href: window.location.href,
|
|
230
|
-
});
|
|
231
|
-
}, 300);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
268
|
private trackProvider(provider: EIP1193Provider) {
|
|
235
269
|
if (provider === this._provider) {
|
|
270
|
+
console.log("Provider already tracked.");
|
|
236
271
|
return;
|
|
237
272
|
}
|
|
238
273
|
|
|
@@ -250,6 +285,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
250
285
|
}
|
|
251
286
|
}
|
|
252
287
|
|
|
288
|
+
console.log("Tracking new provider:", provider);
|
|
253
289
|
this._provider = provider;
|
|
254
290
|
|
|
255
291
|
this.getCurrentWallet();
|
|
@@ -257,14 +293,33 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
257
293
|
this.registerChainChangedListener();
|
|
258
294
|
}
|
|
259
295
|
|
|
296
|
+
private async getAndStoreConnectedAddress(): Promise<string | null> {
|
|
297
|
+
console.log(
|
|
298
|
+
"Session data missing. Attempting to fetch address from provider."
|
|
299
|
+
);
|
|
300
|
+
try {
|
|
301
|
+
const accounts = await this.fetchAccounts();
|
|
302
|
+
if (accounts && accounts.length > 0) {
|
|
303
|
+
const address = accounts[0];
|
|
304
|
+
this.storeWalletAddress(address);
|
|
305
|
+
return address;
|
|
306
|
+
}
|
|
307
|
+
} catch (err) {
|
|
308
|
+
console.error("Failed to fetch accounts from provider:", err);
|
|
309
|
+
}
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
|
|
260
313
|
private async getCurrentWallet() {
|
|
261
314
|
if (!this.provider) {
|
|
262
315
|
console.warn("FormoAnalytics::getCurrentWallet: the provider is not set");
|
|
263
316
|
return;
|
|
264
317
|
}
|
|
318
|
+
|
|
265
319
|
const sessionData = sessionStorage.getItem(this.walletAddressSessionKey);
|
|
320
|
+
|
|
266
321
|
if (!sessionData) {
|
|
267
|
-
return
|
|
322
|
+
return await this.getAndStoreConnectedAddress();
|
|
268
323
|
}
|
|
269
324
|
|
|
270
325
|
const parsedData = JSON.parse(sessionData);
|
|
@@ -281,13 +336,61 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
281
336
|
return parsedData.address || "";
|
|
282
337
|
}
|
|
283
338
|
|
|
339
|
+
// Utility to fetch accounts
|
|
340
|
+
private async fetchAccounts(): Promise<string[] | null> {
|
|
341
|
+
try {
|
|
342
|
+
const res: string[] | null | undefined = await this.provider?.request({
|
|
343
|
+
method: "eth_accounts",
|
|
344
|
+
});
|
|
345
|
+
if (!res || res.length === 0) {
|
|
346
|
+
console.error(
|
|
347
|
+
"error",
|
|
348
|
+
"FormoAnalytics::fetchAccounts: unable to get account. eth_accounts returned empty"
|
|
349
|
+
);
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
352
|
+
return res;
|
|
353
|
+
} catch (err) {
|
|
354
|
+
if ((err as any).code !== 4001) {
|
|
355
|
+
console.error(
|
|
356
|
+
"error",
|
|
357
|
+
"FormoAnalytics::fetchAccounts: eth_accounts threw an error",
|
|
358
|
+
err
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Utility to fetch chain ID
|
|
366
|
+
private async fetchChainId(): Promise<string | null> {
|
|
367
|
+
try {
|
|
368
|
+
const chainIdHex = await this.provider?.request<string>({
|
|
369
|
+
method: "eth_chainId",
|
|
370
|
+
});
|
|
371
|
+
if (!chainIdHex) {
|
|
372
|
+
console.error(
|
|
373
|
+
"FormoAnalytics::fetchChainId: chainIdHex is null or undefined"
|
|
374
|
+
);
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
return chainIdHex;
|
|
378
|
+
} catch (err) {
|
|
379
|
+
console.error(
|
|
380
|
+
"error",
|
|
381
|
+
"FormoAnalytics::fetchChainId: eth_chainId threw an error",
|
|
382
|
+
err
|
|
383
|
+
);
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
284
388
|
private async getCurrentChainId(): Promise<string> {
|
|
285
389
|
if (!this.provider) {
|
|
286
390
|
console.error("FormoAnalytics::getCurrentChainId: provider not set");
|
|
287
391
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
});
|
|
392
|
+
|
|
393
|
+
const chainIdHex = await this.fetchChainId();
|
|
291
394
|
// Because we're connected, the chainId cannot be null
|
|
292
395
|
if (!chainIdHex) {
|
|
293
396
|
console.error(
|
|
@@ -349,7 +452,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
349
452
|
|
|
350
453
|
const payload = {
|
|
351
454
|
chain_id: this.currentChainId,
|
|
352
|
-
address: this.
|
|
455
|
+
address: this.getAndStoreConnectedAddress(),
|
|
353
456
|
};
|
|
354
457
|
this.currentChainId = undefined;
|
|
355
458
|
this.currentConnectedAddress = undefined;
|
|
@@ -369,37 +472,31 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
369
472
|
return;
|
|
370
473
|
}
|
|
371
474
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
);
|
|
381
|
-
return;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
this.currentConnectedAddress = res[0];
|
|
385
|
-
} catch (err) {
|
|
386
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
387
|
-
if ((err as any).code !== 4001) {
|
|
388
|
-
// 4001: The request is rejected by the user , see https://docs.metamask.io/wallet/reference/provider-api/#errors
|
|
389
|
-
console.error(
|
|
390
|
-
"error",
|
|
391
|
-
`FormoAnalytics::onChainChanged: unable to get account. eth_accounts threw an error`,
|
|
392
|
-
err
|
|
393
|
-
);
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
475
|
+
// Attempt to fetch and store the connected address
|
|
476
|
+
const address = await this.getAndStoreConnectedAddress();
|
|
477
|
+
if (!address) {
|
|
478
|
+
console.error(
|
|
479
|
+
"error",
|
|
480
|
+
"FormoAnalytics::onChainChanged: Unable to fetch or store connected address"
|
|
481
|
+
);
|
|
482
|
+
return;
|
|
396
483
|
}
|
|
484
|
+
|
|
485
|
+
this.currentConnectedAddress = address[0];
|
|
397
486
|
}
|
|
398
487
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
488
|
+
// Proceed only if the address exists
|
|
489
|
+
if (this.currentConnectedAddress) {
|
|
490
|
+
return this.chain({
|
|
491
|
+
chainId: this.currentChainId,
|
|
492
|
+
address: this.currentConnectedAddress,
|
|
493
|
+
});
|
|
494
|
+
} else {
|
|
495
|
+
console.error(
|
|
496
|
+
"error",
|
|
497
|
+
"FormoAnalytics::onChainChanged: currentConnectedAddress is null despite fetch attempt"
|
|
498
|
+
);
|
|
499
|
+
}
|
|
403
500
|
}
|
|
404
501
|
|
|
405
502
|
/**
|