@formo/analytics 1.11.9 → 1.11.11
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 +10 -4
- package/dist/cjs/src/FormoAnalytics.d.ts.map +1 -1
- package/dist/cjs/src/FormoAnalytics.js +176 -73
- package/dist/cjs/src/FormoAnalytics.js.map +1 -1
- package/dist/cjs/src/FormoAnalyticsProvider.d.ts +1 -1
- package/dist/cjs/src/FormoAnalyticsProvider.d.ts.map +1 -1
- package/dist/cjs/src/FormoAnalyticsProvider.js +2 -2
- package/dist/cjs/src/FormoAnalyticsProvider.js.map +1 -1
- package/dist/cjs/src/types/base.d.ts +0 -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 +10 -4
- package/dist/esm/src/FormoAnalytics.d.ts.map +1 -1
- package/dist/esm/src/FormoAnalytics.js +176 -73
- package/dist/esm/src/FormoAnalytics.js.map +1 -1
- package/dist/esm/src/FormoAnalyticsProvider.d.ts +1 -1
- package/dist/esm/src/FormoAnalyticsProvider.d.ts.map +1 -1
- package/dist/esm/src/FormoAnalyticsProvider.js +2 -2
- package/dist/esm/src/FormoAnalyticsProvider.js.map +1 -1
- package/dist/esm/src/types/base.d.ts +0 -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 +1 -1
- package/src/FormoAnalytics.ts +159 -84
- package/src/FormoAnalyticsProvider.tsx +1 -2
- package/src/types/base.ts +0 -1
package/package.json
CHANGED
package/src/FormoAnalytics.ts
CHANGED
|
@@ -12,11 +12,7 @@ interface IFormoAnalytics {
|
|
|
12
12
|
/**
|
|
13
13
|
* Initializes the FormoAnalytics instance with the provided API key and project ID.
|
|
14
14
|
*/
|
|
15
|
-
init(
|
|
16
|
-
apiKey: string,
|
|
17
|
-
projectId: string,
|
|
18
|
-
options?: Options
|
|
19
|
-
): Promise<FormoAnalytics>;
|
|
15
|
+
init(apiKey: string, options?: Options): Promise<FormoAnalytics>;
|
|
20
16
|
|
|
21
17
|
/**
|
|
22
18
|
* Tracks page visit events.
|
|
@@ -63,7 +59,6 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
63
59
|
|
|
64
60
|
private constructor(
|
|
65
61
|
public readonly apiKey: string,
|
|
66
|
-
public readonly projectId: string,
|
|
67
62
|
public options: Options = {}
|
|
68
63
|
) {
|
|
69
64
|
this.config = {
|
|
@@ -79,13 +74,12 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
79
74
|
|
|
80
75
|
static async init(
|
|
81
76
|
apiKey: string,
|
|
82
|
-
projectId: string,
|
|
83
77
|
options?: Options
|
|
84
78
|
): Promise<FormoAnalytics> {
|
|
85
79
|
const config = {
|
|
86
80
|
token: apiKey,
|
|
87
81
|
};
|
|
88
|
-
const instance = new FormoAnalytics(apiKey,
|
|
82
|
+
const instance = new FormoAnalytics(apiKey, options);
|
|
89
83
|
instance.config = config;
|
|
90
84
|
|
|
91
85
|
return instance;
|
|
@@ -197,39 +191,76 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
197
191
|
|
|
198
192
|
// Function to track page hits
|
|
199
193
|
private trackPageHit() {
|
|
200
|
-
if (
|
|
201
|
-
return;
|
|
194
|
+
if (this.isAutomationEnvironment()) return;
|
|
202
195
|
|
|
203
|
-
|
|
204
|
-
|
|
196
|
+
const location = this.getUserLocation();
|
|
197
|
+
const language = this.getUserLanguage();
|
|
198
|
+
|
|
199
|
+
setTimeout(async () => {
|
|
200
|
+
const eventData = await this.buildPageEventData(location, language);
|
|
201
|
+
this.trackEvent(Event.PAGE, eventData);
|
|
202
|
+
}, 300);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private isAutomationEnvironment(): boolean {
|
|
206
|
+
return (
|
|
207
|
+
window.__nightmare ||
|
|
208
|
+
window.navigator.webdriver ||
|
|
209
|
+
window.Cypress ||
|
|
210
|
+
false
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
private getUserLocation(): string | undefined {
|
|
205
215
|
try {
|
|
206
216
|
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
217
|
+
return this.timezoneToCountry[timezone];
|
|
218
|
+
} catch (error) {
|
|
219
|
+
console.error('Error resolving timezone:', error);
|
|
220
|
+
return undefined;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private getUserLanguage(): string {
|
|
225
|
+
try {
|
|
226
|
+
return (
|
|
227
|
+
(navigator.languages && navigator.languages.length
|
|
210
228
|
? navigator.languages[0]
|
|
211
|
-
: navigator.language || 'en'
|
|
229
|
+
: navigator.language) || 'en'
|
|
230
|
+
);
|
|
212
231
|
} catch (error) {
|
|
213
|
-
console.error('Error resolving
|
|
232
|
+
console.error('Error resolving language:', error);
|
|
233
|
+
return 'en';
|
|
214
234
|
}
|
|
235
|
+
}
|
|
215
236
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
237
|
+
async buildPageEventData(location: string | undefined, language: string): Promise<Record<string, unknown>> {
|
|
238
|
+
const url = new URL(window.location.href);
|
|
239
|
+
const params = new URLSearchParams(url.search);
|
|
240
|
+
|
|
241
|
+
const address = await this.getAndStoreConnectedAddress();
|
|
242
|
+
if (address === null) {
|
|
243
|
+
console.warn('Wallet address could not be retrieved.');
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const eventData: Record<string, unknown> = {
|
|
247
|
+
'user-agent': window.navigator.userAgent,
|
|
248
|
+
locale: language,
|
|
249
|
+
location,
|
|
250
|
+
referrer: document.referrer,
|
|
251
|
+
pathname: window.location.pathname,
|
|
252
|
+
href: window.location.href,
|
|
253
|
+
utm_source: params.get('utm_source'),
|
|
254
|
+
utm_medium: params.get('utm_medium'),
|
|
255
|
+
utm_campaign: params.get('utm_campaign'),
|
|
256
|
+
ref: params.get('ref'),
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
if (address !== null) {
|
|
260
|
+
eventData['address'] = address;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return eventData;
|
|
233
264
|
}
|
|
234
265
|
|
|
235
266
|
private trackProvider(provider: EIP1193Provider) {
|
|
@@ -260,6 +291,23 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
260
291
|
this.registerChainChangedListener();
|
|
261
292
|
}
|
|
262
293
|
|
|
294
|
+
private async getAndStoreConnectedAddress(): Promise<string | null> {
|
|
295
|
+
console.warn(
|
|
296
|
+
'Session data missing. Attempting to fetch address from provider.'
|
|
297
|
+
);
|
|
298
|
+
try {
|
|
299
|
+
const accounts = await this.fetchAccounts();
|
|
300
|
+
if (accounts && accounts.length > 0) {
|
|
301
|
+
const address = accounts[0];
|
|
302
|
+
this.storeWalletAddress(address);
|
|
303
|
+
return address;
|
|
304
|
+
}
|
|
305
|
+
} catch (err) {
|
|
306
|
+
console.error('Failed to fetch accounts from provider:', err);
|
|
307
|
+
}
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
|
|
263
311
|
private async getCurrentWallet() {
|
|
264
312
|
if (!this.provider) {
|
|
265
313
|
console.warn('FormoAnalytics::getCurrentWallet: the provider is not set');
|
|
@@ -269,22 +317,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
269
317
|
const sessionData = sessionStorage.getItem(this.walletAddressSessionKey);
|
|
270
318
|
|
|
271
319
|
if (!sessionData) {
|
|
272
|
-
|
|
273
|
-
'Session data missing. Attempting to fetch address from provider.'
|
|
274
|
-
);
|
|
275
|
-
try {
|
|
276
|
-
const accounts = await this.provider.request<string[]>({
|
|
277
|
-
method: 'eth_accounts',
|
|
278
|
-
});
|
|
279
|
-
if (accounts && accounts.length > 0) {
|
|
280
|
-
const address = accounts[0];
|
|
281
|
-
this.storeWalletAddress(address);
|
|
282
|
-
return address;
|
|
283
|
-
}
|
|
284
|
-
} catch (err) {
|
|
285
|
-
console.error('Failed to fetch accounts from provider:', err);
|
|
286
|
-
}
|
|
287
|
-
return null;
|
|
320
|
+
return await this.getAndStoreConnectedAddress();
|
|
288
321
|
}
|
|
289
322
|
|
|
290
323
|
const parsedData = JSON.parse(sessionData);
|
|
@@ -301,13 +334,61 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
301
334
|
return parsedData.address || '';
|
|
302
335
|
}
|
|
303
336
|
|
|
337
|
+
// Utility to fetch accounts
|
|
338
|
+
private async fetchAccounts(): Promise<string[] | null> {
|
|
339
|
+
try {
|
|
340
|
+
const res: string[] | null | undefined = await this.provider?.request({
|
|
341
|
+
method: 'eth_accounts',
|
|
342
|
+
});
|
|
343
|
+
if (!res || res.length === 0) {
|
|
344
|
+
console.error(
|
|
345
|
+
'error',
|
|
346
|
+
'FormoAnalytics::fetchAccounts: unable to get account. eth_accounts returned empty'
|
|
347
|
+
);
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
return res;
|
|
351
|
+
} catch (err) {
|
|
352
|
+
if ((err as any).code !== 4001) {
|
|
353
|
+
console.error(
|
|
354
|
+
'error',
|
|
355
|
+
'FormoAnalytics::fetchAccounts: eth_accounts threw an error',
|
|
356
|
+
err
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Utility to fetch chain ID
|
|
364
|
+
private async fetchChainId(): Promise<string | null> {
|
|
365
|
+
try {
|
|
366
|
+
const chainIdHex = await this.provider?.request<string>({
|
|
367
|
+
method: 'eth_chainId',
|
|
368
|
+
});
|
|
369
|
+
if (!chainIdHex) {
|
|
370
|
+
console.error(
|
|
371
|
+
'FormoAnalytics::fetchChainId: chainIdHex is null or undefined'
|
|
372
|
+
);
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
return chainIdHex;
|
|
376
|
+
} catch (err) {
|
|
377
|
+
console.error(
|
|
378
|
+
'error',
|
|
379
|
+
'FormoAnalytics::fetchChainId: eth_chainId threw an error',
|
|
380
|
+
err
|
|
381
|
+
);
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
304
386
|
private async getCurrentChainId(): Promise<string> {
|
|
305
387
|
if (!this.provider) {
|
|
306
388
|
console.error('FormoAnalytics::getCurrentChainId: provider not set');
|
|
307
389
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
});
|
|
390
|
+
|
|
391
|
+
const chainIdHex = await this.fetchChainId();
|
|
311
392
|
// Because we're connected, the chainId cannot be null
|
|
312
393
|
if (!chainIdHex) {
|
|
313
394
|
console.error(
|
|
@@ -369,7 +450,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
369
450
|
|
|
370
451
|
const payload = {
|
|
371
452
|
chain_id: this.currentChainId,
|
|
372
|
-
address: this.
|
|
453
|
+
address: this.getAndStoreConnectedAddress(),
|
|
373
454
|
};
|
|
374
455
|
this.currentChainId = undefined;
|
|
375
456
|
this.currentConnectedAddress = undefined;
|
|
@@ -389,37 +470,31 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
389
470
|
return;
|
|
390
471
|
}
|
|
391
472
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
);
|
|
401
|
-
return;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
this.currentConnectedAddress = res[0];
|
|
405
|
-
} catch (err) {
|
|
406
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
407
|
-
if ((err as any).code !== 4001) {
|
|
408
|
-
// 4001: The request is rejected by the user , see https://docs.metamask.io/wallet/reference/provider-api/#errors
|
|
409
|
-
console.error(
|
|
410
|
-
'error',
|
|
411
|
-
`FormoAnalytics::onChainChanged: unable to get account. eth_accounts threw an error`,
|
|
412
|
-
err
|
|
413
|
-
);
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
473
|
+
// Attempt to fetch and store the connected address
|
|
474
|
+
const address = await this.getAndStoreConnectedAddress();
|
|
475
|
+
if (!address) {
|
|
476
|
+
console.error(
|
|
477
|
+
'error',
|
|
478
|
+
'FormoAnalytics::onChainChanged: Unable to fetch or store connected address'
|
|
479
|
+
);
|
|
480
|
+
return;
|
|
416
481
|
}
|
|
482
|
+
|
|
483
|
+
this.currentConnectedAddress = address[0];
|
|
417
484
|
}
|
|
418
485
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
486
|
+
// Proceed only if the address exists
|
|
487
|
+
if (this.currentConnectedAddress) {
|
|
488
|
+
return this.chain({
|
|
489
|
+
chainId: this.currentChainId,
|
|
490
|
+
address: this.currentConnectedAddress,
|
|
491
|
+
});
|
|
492
|
+
} else {
|
|
493
|
+
console.error(
|
|
494
|
+
'error',
|
|
495
|
+
'FormoAnalytics::onChainChanged: currentConnectedAddress is null despite fetch attempt'
|
|
496
|
+
);
|
|
497
|
+
}
|
|
423
498
|
}
|
|
424
499
|
|
|
425
500
|
/**
|
|
@@ -450,8 +525,8 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
450
525
|
sessionStorage.removeItem(this.walletAddressSessionKey);
|
|
451
526
|
}
|
|
452
527
|
|
|
453
|
-
init(apiKey: string,
|
|
454
|
-
const instance = new FormoAnalytics(apiKey,
|
|
528
|
+
init(apiKey: string, options: Options): Promise<FormoAnalytics> {
|
|
529
|
+
const instance = new FormoAnalytics(apiKey, options);
|
|
455
530
|
return Promise.resolve(instance);
|
|
456
531
|
}
|
|
457
532
|
|
|
@@ -19,7 +19,6 @@ export const FormoAnalyticsContext = createContext<FormoAnalytics | undefined>(
|
|
|
19
19
|
export const FormoAnalyticsProvider = ({
|
|
20
20
|
apiKey,
|
|
21
21
|
options,
|
|
22
|
-
projectId,
|
|
23
22
|
disabled,
|
|
24
23
|
children,
|
|
25
24
|
}: FormoAnalyticsProviderProps) => {
|
|
@@ -62,7 +61,7 @@ export const FormoAnalyticsProvider = ({
|
|
|
62
61
|
|
|
63
62
|
// Initialize FormoAnalytics
|
|
64
63
|
try {
|
|
65
|
-
const sdkInstance = await FormoAnalytics.init(apiKey,
|
|
64
|
+
const sdkInstance = await FormoAnalytics.init(apiKey, options);
|
|
66
65
|
setSdk(sdkInstance);
|
|
67
66
|
console.log('FormoAnalytics SDK initialized successfully');
|
|
68
67
|
} catch (error) {
|