@formo/analytics 1.13.4 → 1.14.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/.github/workflows/ci.yml +51 -0
- package/CONTRIBUTING.md +1 -1
- package/dist/cjs/src/FormoAnalytics.d.ts +6 -5
- package/dist/cjs/src/FormoAnalytics.d.ts.map +1 -1
- package/dist/cjs/src/FormoAnalytics.js +88 -83
- package/dist/cjs/src/FormoAnalytics.js.map +1 -1
- package/dist/cjs/src/FormoAnalyticsProvider.d.ts +4 -5
- package/dist/cjs/src/FormoAnalyticsProvider.d.ts.map +1 -1
- package/dist/cjs/src/FormoAnalyticsProvider.js +39 -62
- package/dist/cjs/src/FormoAnalyticsProvider.js.map +1 -1
- package/dist/cjs/src/constants/base.d.ts +3 -0
- package/dist/cjs/src/constants/base.d.ts.map +1 -0
- package/dist/cjs/src/constants/base.js +6 -0
- package/dist/cjs/src/constants/base.js.map +1 -0
- package/dist/cjs/src/constants/config.d.ts +1 -1
- package/dist/cjs/src/constants/config.js +58 -58
- package/dist/cjs/src/constants/config.js.map +1 -1
- package/dist/cjs/src/constants/index.d.ts +4 -2
- package/dist/cjs/src/constants/index.d.ts.map +1 -1
- package/dist/cjs/src/constants/index.js +2 -0
- package/dist/cjs/src/constants/index.js.map +1 -1
- package/dist/cjs/src/constants/regex.d.ts +4 -0
- package/dist/cjs/src/constants/regex.d.ts.map +1 -0
- package/dist/cjs/src/constants/regex.js +7 -0
- package/dist/cjs/src/constants/regex.js.map +1 -0
- package/dist/cjs/src/lib/fingerprint.d.ts +4 -0
- package/dist/cjs/src/lib/fingerprint.d.ts.map +1 -0
- package/dist/cjs/src/lib/fingerprint.js +63 -0
- package/dist/cjs/src/lib/fingerprint.js.map +1 -0
- package/dist/cjs/src/lib/index.d.ts +3 -0
- package/dist/cjs/src/lib/index.d.ts.map +1 -0
- package/dist/cjs/src/lib/index.js +24 -0
- package/dist/cjs/src/lib/index.js.map +1 -0
- package/dist/cjs/src/lib/queue.d.ts +33 -0
- package/dist/cjs/src/lib/queue.d.ts.map +1 -0
- package/dist/cjs/src/lib/queue.js +319 -0
- package/dist/cjs/src/lib/queue.js.map +1 -0
- package/dist/cjs/src/lib/session-storage.d.ts +11 -0
- package/dist/cjs/src/lib/session-storage.d.ts.map +1 -0
- package/dist/cjs/src/lib/session-storage.js +52 -0
- package/dist/cjs/src/lib/session-storage.js.map +1 -0
- package/dist/cjs/src/lib/utils.d.ts +6 -0
- package/dist/cjs/src/lib/utils.d.ts.map +1 -1
- package/dist/cjs/src/lib/utils.js +32 -0
- package/dist/cjs/src/lib/utils.js.map +1 -1
- package/dist/cjs/src/types/base.d.ts +8 -2
- package/dist/cjs/src/types/base.d.ts.map +1 -1
- package/dist/cjs/src/types/events.d.ts +7 -0
- package/dist/cjs/src/types/events.d.ts.map +1 -1
- package/dist/cjs/src/types/events.js.map +1 -1
- package/dist/cjs/test/lib.spec.js +11 -3
- package/dist/cjs/test/lib.spec.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/src/FormoAnalytics.d.ts +6 -5
- package/dist/esm/src/FormoAnalytics.d.ts.map +1 -1
- package/dist/esm/src/FormoAnalytics.js +87 -79
- package/dist/esm/src/FormoAnalytics.js.map +1 -1
- package/dist/esm/src/FormoAnalyticsProvider.d.ts +4 -5
- package/dist/esm/src/FormoAnalyticsProvider.d.ts.map +1 -1
- package/dist/esm/src/FormoAnalyticsProvider.js +40 -63
- package/dist/esm/src/FormoAnalyticsProvider.js.map +1 -1
- package/dist/esm/src/constants/base.d.ts +3 -0
- package/dist/esm/src/constants/base.d.ts.map +1 -0
- package/dist/esm/src/constants/base.js +3 -0
- package/dist/esm/src/constants/base.js.map +1 -0
- package/dist/esm/src/constants/config.d.ts +1 -1
- package/dist/esm/src/constants/config.js +58 -58
- package/dist/esm/src/constants/config.js.map +1 -1
- package/dist/esm/src/constants/index.d.ts +4 -2
- package/dist/esm/src/constants/index.d.ts.map +1 -1
- package/dist/esm/src/constants/index.js +4 -2
- package/dist/esm/src/constants/index.js.map +1 -1
- package/dist/esm/src/constants/regex.d.ts +4 -0
- package/dist/esm/src/constants/regex.d.ts.map +1 -0
- package/dist/esm/src/constants/regex.js +4 -0
- package/dist/esm/src/constants/regex.js.map +1 -0
- package/dist/esm/src/lib/fingerprint.d.ts +4 -0
- package/dist/esm/src/lib/fingerprint.d.ts.map +1 -0
- package/dist/esm/src/lib/fingerprint.js +60 -0
- package/dist/esm/src/lib/fingerprint.js.map +1 -0
- package/dist/esm/src/lib/index.d.ts +3 -0
- package/dist/esm/src/lib/index.d.ts.map +1 -0
- package/dist/esm/src/lib/index.js +3 -0
- package/dist/esm/src/lib/index.js.map +1 -0
- package/dist/esm/src/lib/queue.d.ts +33 -0
- package/dist/esm/src/lib/queue.d.ts.map +1 -0
- package/dist/esm/src/lib/queue.js +313 -0
- package/dist/esm/src/lib/queue.js.map +1 -0
- package/dist/esm/src/lib/session-storage.d.ts +11 -0
- package/dist/esm/src/lib/session-storage.d.ts.map +1 -0
- package/dist/esm/src/lib/session-storage.js +49 -0
- package/dist/esm/src/lib/session-storage.js.map +1 -0
- package/dist/esm/src/lib/utils.d.ts +6 -0
- package/dist/esm/src/lib/utils.d.ts.map +1 -1
- package/dist/esm/src/lib/utils.js +25 -0
- package/dist/esm/src/lib/utils.js.map +1 -1
- package/dist/esm/src/types/base.d.ts +8 -2
- package/dist/esm/src/types/base.d.ts.map +1 -1
- package/dist/esm/src/types/events.d.ts +7 -0
- package/dist/esm/src/types/events.d.ts.map +1 -1
- package/dist/esm/src/types/events.js.map +1 -1
- package/dist/esm/test/lib.spec.js +9 -1
- package/dist/esm/test/lib.spec.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/index.umd.min.js +1 -1
- package/dist/index.umd.min.js.LICENSE.txt +0 -12
- package/dist/index.umd.min.js.map +1 -1
- package/package.json +7 -6
- package/src/FormoAnalytics.ts +86 -66
- package/src/FormoAnalyticsProvider.tsx +32 -67
- package/src/constants/base.ts +2 -0
- package/src/constants/config.ts +58 -58
- package/src/constants/index.ts +4 -2
- package/src/constants/regex.ts +3 -0
- package/src/lib/fingerprint.ts +9 -0
- package/src/lib/index.ts +2 -0
- package/src/lib/queue.ts +310 -0
- package/src/lib/session-storage.ts +53 -0
- package/src/lib/utils.ts +31 -0
- package/src/types/base.ts +12 -5
- package/src/types/events.ts +15 -7
- package/test/lib.spec.ts +13 -1
- package/dist/387.index.umd.min.js +0 -125
- package/dist/387.index.umd.min.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formo/analytics",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.2",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
|
-
"url": "https://github.com/getformo/sdk.git"
|
|
6
|
+
"url": "git+https://github.com/getformo/sdk.git"
|
|
7
7
|
},
|
|
8
8
|
"main": "dist/cjs/src/index.js",
|
|
9
9
|
"types": "dist/esm/src/index.d.ts",
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
},
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@
|
|
25
|
-
"
|
|
26
|
-
"
|
|
24
|
+
"@fingerprintjs/fingerprintjs": "^4.6.0",
|
|
25
|
+
"fetch-retry": "^6.0.0",
|
|
26
|
+
"is-network-error": "^1.1.0",
|
|
27
27
|
"mipd": "^0.0.7"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
@@ -77,7 +77,8 @@
|
|
|
77
77
|
"test": "mocha --require ts-node/register test/**/*.ts",
|
|
78
78
|
"test-watch": "nodemon --config test.nodemon.json",
|
|
79
79
|
"prepare": "husky install",
|
|
80
|
-
"commit": "git add . && cz"
|
|
80
|
+
"commit": "git add . && cz",
|
|
81
|
+
"pub": "yarn build && npm publish"
|
|
81
82
|
},
|
|
82
83
|
"peerDependencies": {
|
|
83
84
|
"@types/react": ">=16.14.34",
|
package/src/FormoAnalytics.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import axios from "axios";
|
|
2
1
|
import { createStore, EIP6963ProviderDetail } from "mipd";
|
|
3
2
|
import {
|
|
4
3
|
COUNTRY_LIST,
|
|
@@ -6,7 +5,6 @@ import {
|
|
|
6
5
|
EVENTS_API_URL,
|
|
7
6
|
Event,
|
|
8
7
|
} from "./constants";
|
|
9
|
-
import { H } from "highlight.run";
|
|
10
8
|
import {
|
|
11
9
|
ChainID,
|
|
12
10
|
Address,
|
|
@@ -17,8 +15,11 @@ import {
|
|
|
17
15
|
RPCError,
|
|
18
16
|
SignatureStatus,
|
|
19
17
|
TransactionStatus,
|
|
18
|
+
RequestEvent,
|
|
20
19
|
} from "./types";
|
|
21
|
-
import { toSnakeCase } from "./lib
|
|
20
|
+
import { session, isLocalhost, toSnakeCase, isAddress } from "./lib";
|
|
21
|
+
import { SESSION_IDENTIFIED_KEY } from "./constants";
|
|
22
|
+
import { EventQueue } from "./lib/queue";
|
|
22
23
|
|
|
23
24
|
interface IFormoAnalytics {
|
|
24
25
|
page(): void;
|
|
@@ -62,19 +63,32 @@ interface IFormoAnalytics {
|
|
|
62
63
|
export class FormoAnalytics implements IFormoAnalytics {
|
|
63
64
|
private _provider?: EIP1193Provider;
|
|
64
65
|
private _providerListeners: Record<string, (...args: unknown[]) => void> = {};
|
|
66
|
+
private session: FormoAnalyticsSession;
|
|
67
|
+
private eventQueue: EventQueue;
|
|
65
68
|
|
|
66
69
|
config: Config;
|
|
67
70
|
currentChainId?: ChainID;
|
|
68
71
|
currentConnectedAddress?: Address;
|
|
69
72
|
|
|
70
73
|
private constructor(
|
|
71
|
-
public readonly
|
|
74
|
+
public readonly writeKey: string,
|
|
72
75
|
public options: Options = {}
|
|
73
76
|
) {
|
|
74
77
|
this.config = {
|
|
75
|
-
|
|
78
|
+
writeKey,
|
|
79
|
+
trackLocalhost: options.trackLocalhost || false,
|
|
76
80
|
};
|
|
77
81
|
|
|
82
|
+
this.session = new FormoAnalyticsSession();
|
|
83
|
+
|
|
84
|
+
this.eventQueue = new EventQueue(this.config.writeKey, {
|
|
85
|
+
url: EVENTS_API_URL,
|
|
86
|
+
flushAt: options.flushAt,
|
|
87
|
+
retryCount: options.retryCount,
|
|
88
|
+
maxQueueSize: options.maxQueueSize,
|
|
89
|
+
flushInterval: options.flushInterval,
|
|
90
|
+
});
|
|
91
|
+
|
|
78
92
|
// TODO: replace with eip6963
|
|
79
93
|
const provider = options.provider || window?.ethereum;
|
|
80
94
|
if (provider) {
|
|
@@ -86,10 +100,10 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
86
100
|
}
|
|
87
101
|
|
|
88
102
|
static async init(
|
|
89
|
-
|
|
103
|
+
writeKey: string,
|
|
90
104
|
options?: Options
|
|
91
105
|
): Promise<FormoAnalytics> {
|
|
92
|
-
const analytics = new FormoAnalytics(
|
|
106
|
+
const analytics = new FormoAnalytics(writeKey, options);
|
|
93
107
|
|
|
94
108
|
// Identify
|
|
95
109
|
const providers = await analytics.getProviders();
|
|
@@ -264,7 +278,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
264
278
|
}
|
|
265
279
|
|
|
266
280
|
/**
|
|
267
|
-
* Emits
|
|
281
|
+
* Emits an identify event with current wallet address.
|
|
268
282
|
* @param {Address} params.address
|
|
269
283
|
* @returns {Promise<void>}
|
|
270
284
|
*/
|
|
@@ -273,17 +287,21 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
273
287
|
providerName,
|
|
274
288
|
rdns,
|
|
275
289
|
}: {
|
|
276
|
-
address: Address;
|
|
290
|
+
address: Address | null;
|
|
277
291
|
providerName?: string;
|
|
278
292
|
rdns?: string;
|
|
279
293
|
}): Promise<void> {
|
|
280
|
-
if (
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
294
|
+
if (this.session.isIdentified())
|
|
295
|
+
return console.warn(
|
|
296
|
+
"FormoAnalytics::identify: Wallet already identified in this session"
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
this.session.identify();
|
|
300
|
+
await this.trackEvent(Event.IDENTIFY, {
|
|
301
|
+
address,
|
|
302
|
+
providerName,
|
|
303
|
+
rdns,
|
|
304
|
+
});
|
|
287
305
|
}
|
|
288
306
|
|
|
289
307
|
/**
|
|
@@ -302,7 +320,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
302
320
|
|
|
303
321
|
private trackProvider(provider: EIP1193Provider): void {
|
|
304
322
|
if (provider === this._provider) {
|
|
305
|
-
console.
|
|
323
|
+
console.warn("FormoAnalytics::trackProvider: Provider already tracked.");
|
|
306
324
|
return;
|
|
307
325
|
}
|
|
308
326
|
|
|
@@ -535,8 +553,8 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
535
553
|
}
|
|
536
554
|
|
|
537
555
|
private async trackFirstPageHit(): Promise<void> {
|
|
538
|
-
if (
|
|
539
|
-
|
|
556
|
+
if (session.get(CURRENT_URL_KEY) === null) {
|
|
557
|
+
session.set(CURRENT_URL_KEY, window.location.href);
|
|
540
558
|
}
|
|
541
559
|
|
|
542
560
|
return this.trackPageHit();
|
|
@@ -562,70 +580,48 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
562
580
|
}
|
|
563
581
|
|
|
564
582
|
private async onLocationChange(): Promise<void> {
|
|
565
|
-
const currentUrl =
|
|
583
|
+
const currentUrl = session.get(CURRENT_URL_KEY);
|
|
566
584
|
|
|
567
585
|
if (currentUrl !== window.location.href) {
|
|
568
|
-
|
|
586
|
+
session.set(CURRENT_URL_KEY, window.location.href);
|
|
569
587
|
this.trackPageHit();
|
|
570
588
|
}
|
|
571
589
|
}
|
|
572
590
|
|
|
573
591
|
private trackPageHit(): void {
|
|
574
592
|
const pathname = window.location.pathname;
|
|
575
|
-
const href = window.location.href;
|
|
576
593
|
const hash = window.location.hash;
|
|
577
594
|
|
|
595
|
+
if (!this.config.trackLocalhost && isLocalhost()) {
|
|
596
|
+
return console.warn(
|
|
597
|
+
"FormoAnalytics::trackPageHit: Ignoring event because website is running locally"
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
|
|
578
601
|
setTimeout(async () => {
|
|
579
602
|
this.trackEvent(Event.PAGE, {
|
|
580
603
|
pathname,
|
|
581
|
-
href,
|
|
582
604
|
hash,
|
|
583
605
|
});
|
|
584
606
|
}, 300);
|
|
585
607
|
}
|
|
586
608
|
|
|
587
|
-
// TODO: refactor this with event queue and flushing
|
|
588
|
-
// https://linear.app/getformo/issue/P-835/sdk-refactor-retries-with-event-queue-and-batching
|
|
589
609
|
private async trackEvent(action: string, payload: any): Promise<void> {
|
|
590
610
|
const address = await this.getAddress();
|
|
591
611
|
|
|
592
|
-
const requestData = {
|
|
593
|
-
address
|
|
612
|
+
const requestData: RequestEvent = {
|
|
613
|
+
address,
|
|
594
614
|
timestamp: new Date().toISOString(),
|
|
595
615
|
action,
|
|
596
616
|
version: "1",
|
|
597
617
|
payload: await this.buildEventPayload(toSnakeCase(payload)),
|
|
598
618
|
};
|
|
599
619
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
headers: {
|
|
606
|
-
"Content-Type": "application/json",
|
|
607
|
-
Authorization: `Bearer ${this.config.apiKey}`,
|
|
608
|
-
},
|
|
609
|
-
}
|
|
610
|
-
);
|
|
611
|
-
|
|
612
|
-
if (response.status >= 200 && response.status < 300) {
|
|
613
|
-
console.log(
|
|
614
|
-
`Event sent successfully: ${this.getActionDescriptor(
|
|
615
|
-
action,
|
|
616
|
-
payload
|
|
617
|
-
)}`
|
|
618
|
-
);
|
|
619
|
-
} else {
|
|
620
|
-
throw new Error(`Failed with status: ${response.status}`);
|
|
621
|
-
}
|
|
622
|
-
} catch (error) {
|
|
623
|
-
H.consumeError(
|
|
624
|
-
error as Error,
|
|
625
|
-
`Request data: ${JSON.stringify(requestData)}`
|
|
626
|
-
);
|
|
627
|
-
console.error(`Event "${action}" failed. Error: ${error}`);
|
|
628
|
-
}
|
|
620
|
+
await this.eventQueue.enqueue(requestData, (err, _, data) => {
|
|
621
|
+
if (err) {
|
|
622
|
+
console.error(err);
|
|
623
|
+
} else console.log(`Events sent successfully: ${data.length} events`);
|
|
624
|
+
});
|
|
629
625
|
}
|
|
630
626
|
|
|
631
627
|
/*
|
|
@@ -646,9 +642,10 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
646
642
|
}
|
|
647
643
|
|
|
648
644
|
private async identifyAll(providers: EIP6963ProviderDetail[]): Promise<void> {
|
|
649
|
-
|
|
650
|
-
|
|
645
|
+
try {
|
|
646
|
+
for (const { provider, info } of providers) {
|
|
651
647
|
const accounts = await this.getAccounts(provider);
|
|
648
|
+
// Identify with accounts
|
|
652
649
|
if (accounts && accounts.length > 0) {
|
|
653
650
|
for (const address of accounts) {
|
|
654
651
|
await this.identify({
|
|
@@ -657,8 +654,17 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
657
654
|
rdns: info.rdns,
|
|
658
655
|
});
|
|
659
656
|
}
|
|
657
|
+
} else {
|
|
658
|
+
// Identify without accounts
|
|
659
|
+
await this.identify({
|
|
660
|
+
address: null,
|
|
661
|
+
providerName: info.name,
|
|
662
|
+
rdns: info.rdns,
|
|
663
|
+
});
|
|
660
664
|
}
|
|
661
|
-
}
|
|
665
|
+
}
|
|
666
|
+
} catch (err) {
|
|
667
|
+
console.log("identifying all => err", err);
|
|
662
668
|
}
|
|
663
669
|
}
|
|
664
670
|
|
|
@@ -668,7 +674,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
668
674
|
|
|
669
675
|
private async getAddress(): Promise<Address | null> {
|
|
670
676
|
if (this.currentConnectedAddress) return this.currentConnectedAddress;
|
|
671
|
-
if (!this
|
|
677
|
+
if (!this?.provider) {
|
|
672
678
|
console.log("FormoAnalytics::getAddress: the provider is not set");
|
|
673
679
|
return null;
|
|
674
680
|
}
|
|
@@ -676,7 +682,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
676
682
|
try {
|
|
677
683
|
const accounts = await this.getAccounts();
|
|
678
684
|
if (accounts && accounts.length > 0) {
|
|
679
|
-
return accounts[0];
|
|
685
|
+
return isAddress(accounts[0]) ? accounts[0] : null;
|
|
680
686
|
}
|
|
681
687
|
} catch (err) {
|
|
682
688
|
console.log("Failed to fetch accounts from provider:", err);
|
|
@@ -694,7 +700,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
694
700
|
method: "eth_accounts",
|
|
695
701
|
});
|
|
696
702
|
if (!res || res.length === 0) return null;
|
|
697
|
-
return res;
|
|
703
|
+
return res.filter(isAddress);
|
|
698
704
|
} catch (err) {
|
|
699
705
|
if ((err as any).code !== 4001) {
|
|
700
706
|
console.log(
|
|
@@ -764,18 +770,19 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
764
770
|
|
|
765
771
|
const location = this.getLocation();
|
|
766
772
|
const language = this.getLanguage();
|
|
767
|
-
const address = await this.getAddress();
|
|
768
773
|
|
|
769
774
|
// common browser properties
|
|
770
775
|
return {
|
|
771
776
|
"user-agent": window.navigator.userAgent,
|
|
772
|
-
|
|
777
|
+
href: url.href,
|
|
773
778
|
locale: language,
|
|
774
779
|
location,
|
|
775
780
|
referrer: document.referrer,
|
|
776
781
|
utm_source: params.get("utm_source"),
|
|
777
782
|
utm_medium: params.get("utm_medium"),
|
|
778
783
|
utm_campaign: params.get("utm_campaign"),
|
|
784
|
+
utm_content: params.get("utm_content"),
|
|
785
|
+
utm_term: params.get("utm_term"),
|
|
779
786
|
ref: params.get("ref"),
|
|
780
787
|
...eventSpecificPayload,
|
|
781
788
|
};
|
|
@@ -828,8 +835,21 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
828
835
|
value,
|
|
829
836
|
};
|
|
830
837
|
}
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
interface IFormoAnalyticsSession {
|
|
841
|
+
isIdentified(): boolean;
|
|
842
|
+
identify(): void;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
class FormoAnalyticsSession implements IFormoAnalyticsSession {
|
|
846
|
+
constructor() {}
|
|
847
|
+
|
|
848
|
+
public isIdentified(): boolean {
|
|
849
|
+
return session.get(SESSION_IDENTIFIED_KEY) === true;
|
|
850
|
+
}
|
|
831
851
|
|
|
832
|
-
|
|
833
|
-
|
|
852
|
+
public identify(): void {
|
|
853
|
+
session.set(SESSION_IDENTIFIED_KEY, true);
|
|
834
854
|
}
|
|
835
855
|
}
|
|
@@ -1,96 +1,61 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
useEffect,
|
|
5
|
-
useState,
|
|
6
|
-
useRef,
|
|
7
|
-
} from 'react';
|
|
8
|
-
import { FormoAnalytics } from './FormoAnalytics';
|
|
9
|
-
import { FormoAnalyticsProviderProps } from './types';
|
|
10
|
-
import { ErrorBoundary } from '@highlight-run/react';
|
|
11
|
-
import { H } from 'highlight.run';
|
|
12
|
-
|
|
13
|
-
const HIGHLIGHT_PROJECT_ID = process.env.REACT_APP_HIGHLIGHT_PROJECT_ID;
|
|
1
|
+
import { createContext, useContext, useEffect, useState, useRef } from "react";
|
|
2
|
+
import { FormoAnalytics } from "./FormoAnalytics";
|
|
3
|
+
import { FormoAnalyticsProviderProps } from "./types";
|
|
14
4
|
|
|
15
5
|
export const FormoAnalyticsContext = createContext<FormoAnalytics | undefined>(
|
|
16
6
|
undefined
|
|
17
7
|
);
|
|
18
8
|
|
|
19
|
-
export const FormoAnalyticsProvider = ({
|
|
20
|
-
|
|
9
|
+
export const FormoAnalyticsProvider = (props: FormoAnalyticsProviderProps) => {
|
|
10
|
+
const { writeKey, disabled, children } = props;
|
|
11
|
+
|
|
12
|
+
// Keep the app running without analytics if no Write Key is provided or disabled
|
|
13
|
+
if (!writeKey) {
|
|
14
|
+
console.error("FormoAnalyticsProvider: No Write Key provided");
|
|
15
|
+
return children;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (disabled) {
|
|
19
|
+
console.warn("FormoAnalytics is disabled");
|
|
20
|
+
return children;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return <InitializedAnalytics {...props} />;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const InitializedAnalytics = ({
|
|
27
|
+
writeKey,
|
|
21
28
|
options,
|
|
22
|
-
disabled,
|
|
23
29
|
children,
|
|
24
30
|
}: FormoAnalyticsProviderProps) => {
|
|
25
31
|
const [sdk, setSdk] = useState<FormoAnalytics | undefined>();
|
|
26
|
-
const [isInitialized, setIsInitialized] = useState(false);
|
|
27
32
|
const initializedStartedRef = useRef(false);
|
|
28
33
|
|
|
29
|
-
const
|
|
30
|
-
if (HIGHLIGHT_PROJECT_ID) {
|
|
31
|
-
try {
|
|
32
|
-
H.init(HIGHLIGHT_PROJECT_ID, {
|
|
33
|
-
serviceName: 'formo-analytics-sdk',
|
|
34
|
-
tracingOrigins: true,
|
|
35
|
-
networkRecording: {
|
|
36
|
-
enabled: true,
|
|
37
|
-
recordHeadersAndBody: true,
|
|
38
|
-
urlBlocklist: [
|
|
39
|
-
'https://www.googleapis.com/identitytoolkit',
|
|
40
|
-
'https://securetoken.googleapis.com',
|
|
41
|
-
],
|
|
42
|
-
},
|
|
43
|
-
});
|
|
44
|
-
console.log('Highlight.run initialized successfully');
|
|
45
|
-
} catch (error) {
|
|
46
|
-
console.error('Failed to initialize Highlight.run', error);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const initializeFormoAnalytics = async (apiKey: string, options: any) => {
|
|
34
|
+
const initializeFormoAnalytics = async (writeKey: string, options: any) => {
|
|
52
35
|
try {
|
|
53
|
-
const sdkInstance = await FormoAnalytics.init(
|
|
36
|
+
const sdkInstance = await FormoAnalytics.init(writeKey, options);
|
|
54
37
|
setSdk(sdkInstance);
|
|
55
|
-
console.log(
|
|
38
|
+
console.log("FormoAnalytics SDK initialized successfully");
|
|
56
39
|
} catch (error) {
|
|
57
|
-
console.error(
|
|
58
|
-
} finally {
|
|
59
|
-
setIsInitialized(true); // Ensure UI renders even after failure
|
|
40
|
+
console.error("Failed to initialize FormoAnalytics SDK", error);
|
|
60
41
|
}
|
|
61
42
|
};
|
|
62
43
|
|
|
63
44
|
useEffect(() => {
|
|
64
45
|
const initialize = async () => {
|
|
65
|
-
if (!apiKey) {
|
|
66
|
-
console.error('FormoAnalyticsProvider: No API key provided');
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
if (disabled) {
|
|
70
|
-
console.warn('FormoAnalytics is disabled');
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
46
|
if (initializedStartedRef.current) return;
|
|
74
47
|
initializedStartedRef.current = true;
|
|
75
48
|
|
|
76
|
-
await
|
|
77
|
-
await initializeFormoAnalytics(apiKey, options);
|
|
49
|
+
await initializeFormoAnalytics(writeKey!, options);
|
|
78
50
|
};
|
|
79
51
|
|
|
80
52
|
initialize();
|
|
81
|
-
}, [
|
|
82
|
-
|
|
83
|
-
if (!isInitialized) {
|
|
84
|
-
// Optionally show a loading state until initialization attempt finishes
|
|
85
|
-
return <div>Loading analytics...</div>;
|
|
86
|
-
}
|
|
53
|
+
}, [writeKey, options]);
|
|
87
54
|
|
|
88
55
|
return (
|
|
89
|
-
<
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
</FormoAnalyticsContext.Provider>
|
|
93
|
-
</ErrorBoundary>
|
|
56
|
+
<FormoAnalyticsContext.Provider value={sdk}>
|
|
57
|
+
{children}
|
|
58
|
+
</FormoAnalyticsContext.Provider>
|
|
94
59
|
);
|
|
95
60
|
};
|
|
96
61
|
|
|
@@ -98,7 +63,7 @@ export const useFormoAnalytics = () => {
|
|
|
98
63
|
const context = useContext(FormoAnalyticsContext);
|
|
99
64
|
|
|
100
65
|
if (!context) {
|
|
101
|
-
console.warn(
|
|
66
|
+
console.warn("useFormoAnalytics called without a valid context");
|
|
102
67
|
}
|
|
103
68
|
|
|
104
69
|
return context; // Return undefined if SDK is not initialized, handle accordingly in consumer
|