@formo/analytics 1.15.2 → 1.16.0

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.
Files changed (44) hide show
  1. package/dist/cjs/src/FormoAnalytics.d.ts +9 -3
  2. package/dist/cjs/src/FormoAnalytics.d.ts.map +1 -1
  3. package/dist/cjs/src/FormoAnalytics.js +109 -113
  4. package/dist/cjs/src/FormoAnalytics.js.map +1 -1
  5. package/dist/cjs/src/constants/base.d.ts +1 -1
  6. package/dist/cjs/src/constants/base.d.ts.map +1 -1
  7. package/dist/cjs/src/constants/base.js +2 -2
  8. package/dist/cjs/src/constants/base.js.map +1 -1
  9. package/dist/cjs/src/constants/events.d.ts +1 -0
  10. package/dist/cjs/src/constants/events.d.ts.map +1 -1
  11. package/dist/cjs/src/constants/events.js +1 -0
  12. package/dist/cjs/src/constants/events.js.map +1 -1
  13. package/dist/cjs/src/lib/queue.d.ts.map +1 -1
  14. package/dist/cjs/src/lib/queue.js +3 -2
  15. package/dist/cjs/src/lib/queue.js.map +1 -1
  16. package/dist/cjs/src/types/events.d.ts +1 -1
  17. package/dist/cjs/src/types/events.d.ts.map +1 -1
  18. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  19. package/dist/esm/src/FormoAnalytics.d.ts +9 -3
  20. package/dist/esm/src/FormoAnalytics.d.ts.map +1 -1
  21. package/dist/esm/src/FormoAnalytics.js +112 -116
  22. package/dist/esm/src/FormoAnalytics.js.map +1 -1
  23. package/dist/esm/src/constants/base.d.ts +1 -1
  24. package/dist/esm/src/constants/base.d.ts.map +1 -1
  25. package/dist/esm/src/constants/base.js +1 -1
  26. package/dist/esm/src/constants/base.js.map +1 -1
  27. package/dist/esm/src/constants/events.d.ts +1 -0
  28. package/dist/esm/src/constants/events.d.ts.map +1 -1
  29. package/dist/esm/src/constants/events.js +1 -0
  30. package/dist/esm/src/constants/events.js.map +1 -1
  31. package/dist/esm/src/lib/queue.d.ts.map +1 -1
  32. package/dist/esm/src/lib/queue.js +3 -2
  33. package/dist/esm/src/lib/queue.js.map +1 -1
  34. package/dist/esm/src/types/events.d.ts +1 -1
  35. package/dist/esm/src/types/events.d.ts.map +1 -1
  36. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  37. package/dist/index.umd.min.js +1 -1
  38. package/dist/index.umd.min.js.map +1 -1
  39. package/package.json +1 -1
  40. package/src/FormoAnalytics.ts +93 -75
  41. package/src/constants/base.ts +3 -2
  42. package/src/constants/events.ts +8 -7
  43. package/src/lib/queue.ts +2 -1
  44. package/src/types/events.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@formo/analytics",
3
- "version": "1.15.2",
3
+ "version": "1.16.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/getformo/sdk.git"
@@ -6,8 +6,6 @@ import {
6
6
  EVENTS_API_URL,
7
7
  Event,
8
8
  SESSION_USER_ID_KEY,
9
- EVENTS_API_REQUEST_HEADER,
10
- USER_API_URL,
11
9
  } from "./constants";
12
10
  import {
13
11
  ChainID,
@@ -21,14 +19,14 @@ import {
21
19
  TransactionStatus,
22
20
  RequestEvent,
23
21
  } from "./types";
24
- import { session, local, logger, EventQueue, fetch, Logger } from "./lib";
22
+ import { session, local, logger, EventQueue, Logger } from "./lib";
25
23
  import {
26
24
  isLocalhost,
27
25
  isAddress,
28
26
  toSnakeCase,
29
27
  generateNativeUUID,
30
28
  } from "./utils";
31
- import { SESSION_IDENTIFIED_KEY } from "./constants";
29
+ import { SESSION_WALLET_DETECTED_KEY } from "./constants";
32
30
  import { UUID } from "crypto";
33
31
 
34
32
  interface IFormoAnalytics {
@@ -77,7 +75,7 @@ export class FormoAnalytics implements IFormoAnalytics {
77
75
  private session: FormoAnalyticsSession;
78
76
  private eventQueue: EventQueue;
79
77
  private anonymousId: UUID | null = null;
80
- private userId: UUID | null = null;
78
+ private userId: string | null = null;
81
79
 
82
80
  config: Config;
83
81
  currentChainId?: ChainID;
@@ -109,7 +107,7 @@ export class FormoAnalytics implements IFormoAnalytics {
109
107
  });
110
108
 
111
109
  this.anonymousId = this.getAnonymousId();
112
- this.getUserId(null).then((userId) => (this.userId = userId));
110
+ this.userId = session.get(SESSION_USER_ID_KEY) as string | null;
113
111
 
114
112
  // TODO: replace with eip6963
115
113
  const provider = options.provider || window?.ethereum;
@@ -127,9 +125,9 @@ export class FormoAnalytics implements IFormoAnalytics {
127
125
  ): Promise<FormoAnalytics> {
128
126
  const analytics = new FormoAnalytics(writeKey, options);
129
127
 
130
- // Identify
128
+ // Detect
131
129
  const providers = await analytics.getProviders();
132
- await analytics.identifyAll(providers);
130
+ await analytics.detectWallets(providers);
133
131
 
134
132
  return analytics;
135
133
  }
@@ -318,16 +316,15 @@ export class FormoAnalytics implements IFormoAnalytics {
318
316
  public async identify({
319
317
  address,
320
318
  providerName,
319
+ userId,
321
320
  rdns,
322
321
  }: {
323
322
  address: Address | null;
324
323
  providerName?: string;
324
+ userId?: string;
325
325
  rdns?: string;
326
326
  }): Promise<void> {
327
- if (this.session.isIdentified())
328
- return logger.warn("Identify: Wallet already identified in this session");
329
-
330
- this.session.identify();
327
+ if (userId) this.userId = userId || null;
331
328
  await this.trackEvent(Event.IDENTIFY, {
332
329
  address,
333
330
  providerName,
@@ -335,6 +332,30 @@ export class FormoAnalytics implements IFormoAnalytics {
335
332
  });
336
333
  }
337
334
 
335
+ /**
336
+ * Emits an identify event with current wallet address.
337
+ * @param {Address} params.address
338
+ * @returns {Promise<void>}
339
+ */
340
+ private async detectWallet({
341
+ providerName,
342
+ rdns,
343
+ }: {
344
+ providerName: string;
345
+ rdns: string;
346
+ }): Promise<void> {
347
+ if (this.session.isWalletDetected(rdns))
348
+ return logger.warn(
349
+ `detectWallet: Wallet ${providerName} already detected in this session`
350
+ );
351
+
352
+ this.session.markWalletdetected(rdns);
353
+ await this.trackEvent(Event.DETECT_WALLET, {
354
+ providerName,
355
+ rdns,
356
+ });
357
+ }
358
+
338
359
  /**
339
360
  * Emits a custom event with custom data.
340
361
  * @param {string} action
@@ -573,7 +594,6 @@ export class FormoAnalytics implements IFormoAnalytics {
573
594
  return Promise.resolve();
574
595
  }
575
596
  this.currentConnectedAddress = address;
576
- this.userId = await this.getUserId(address);
577
597
  }
578
598
 
579
599
  // Proceed only if the address exists
@@ -646,12 +666,16 @@ export class FormoAnalytics implements IFormoAnalytics {
646
666
  private async trackEvent(action: string, payload: any): Promise<void> {
647
667
  try {
648
668
  const address = await this.getAddress();
649
- const user_id = await this.getUserId(address);
669
+ const user_id = this.userId;
670
+
671
+ if (payload?.userId) {
672
+ delete payload.userId;
673
+ }
650
674
 
651
675
  const requestData: RequestEvent = {
652
676
  anonymous_id: this.anonymousId as UUID,
653
677
  user_id,
654
- address,
678
+ address: address?.toLowerCase() || null,
655
679
  timestamp: new Date().toISOString(),
656
680
  action,
657
681
  version: "1",
@@ -685,38 +709,54 @@ export class FormoAnalytics implements IFormoAnalytics {
685
709
  return providers;
686
710
  }
687
711
 
688
- private async identifyAll(
712
+ private async detectWallets(
689
713
  providers: readonly EIP6963ProviderDetail[]
690
714
  ): Promise<void> {
691
715
  try {
692
716
  for (const eip6963ProviderDetail of providers) {
693
- if (!eip6963ProviderDetail) continue;
694
- const accounts = await this.getAccounts(
695
- eip6963ProviderDetail?.provider
696
- );
697
- // Identify with accounts
698
- if (accounts && accounts.length > 0) {
699
- for (const address of accounts) {
700
- await this.identify({
701
- address,
702
- providerName: eip6963ProviderDetail?.info.name,
703
- rdns: eip6963ProviderDetail?.info.rdns,
704
- });
705
- }
706
- } else {
707
- // Identify without accounts
708
- await this.identify({
709
- address: null,
710
- providerName: eip6963ProviderDetail?.info.name,
711
- rdns: eip6963ProviderDetail?.info.rdns,
712
- });
713
- }
717
+ await this.detectWallet({
718
+ providerName: eip6963ProviderDetail?.info.name,
719
+ rdns: eip6963ProviderDetail?.info.rdns,
720
+ });
714
721
  }
715
722
  } catch (err) {
716
- logger.error("Error identifying all:", err);
723
+ logger.error("Error detect all wallets:", err);
717
724
  }
718
725
  }
719
726
 
727
+ // TODO: Refactoring => public this function as API
728
+ // private async identifyAll(
729
+ // providers: readonly EIP6963ProviderDetail[]
730
+ // ): Promise<void> {
731
+ // try {
732
+ // for (const eip6963ProviderDetail of providers) {
733
+ // if (!eip6963ProviderDetail) continue;
734
+ // const accounts = await this.getAccounts(
735
+ // eip6963ProviderDetail?.provider
736
+ // );
737
+ // // Identify with accounts
738
+ // if (accounts && accounts.length > 0) {
739
+ // for (const address of accounts) {
740
+ // await this.identify({
741
+ // address,
742
+ // providerName: eip6963ProviderDetail?.info.name,
743
+ // rdns: eip6963ProviderDetail?.info.rdns,
744
+ // });
745
+ // }
746
+ // } else {
747
+ // // Identify without accounts
748
+ // await this.identify({
749
+ // address: null,
750
+ // providerName: eip6963ProviderDetail?.info.name,
751
+ // rdns: eip6963ProviderDetail?.info.rdns,
752
+ // });
753
+ // }
754
+ // }
755
+ // } catch (err) {
756
+ // logger.error("Error identifying all:", err);
757
+ // }
758
+ // }
759
+
720
760
  get provider(): EIP1193Provider | undefined {
721
761
  return this._provider;
722
762
  }
@@ -730,31 +770,6 @@ export class FormoAnalytics implements IFormoAnalytics {
730
770
  return newAnonymousId;
731
771
  }
732
772
 
733
- private async getUserId(address: string | null): Promise<UUID | null> {
734
- const storedUserId = session.get(SESSION_USER_ID_KEY);
735
- if (storedUserId && typeof storedUserId === "string")
736
- return storedUserId as UUID;
737
-
738
- if (address) {
739
- const res = await fetch(`${USER_API_URL}?address=${address}`, {
740
- headers: EVENTS_API_REQUEST_HEADER(this.writeKey),
741
- method: "GET",
742
- });
743
- const data = await res.json();
744
- const userId = data?.data?.[0]?.user_id;
745
- if (userId) {
746
- session.set(SESSION_USER_ID_KEY, userId);
747
- return userId;
748
- }
749
-
750
- const newUserId = generateNativeUUID();
751
- session.set(SESSION_USER_ID_KEY, newUserId);
752
- return newUserId;
753
- }
754
-
755
- return null;
756
- }
757
-
758
773
  private async getAddress(): Promise<Address | null> {
759
774
  if (this.currentConnectedAddress) return this.currentConnectedAddress;
760
775
  if (!this?.provider) {
@@ -860,12 +875,12 @@ export class FormoAnalytics implements IFormoAnalytics {
860
875
  locale: language,
861
876
  location,
862
877
  referrer: document.referrer,
863
- utm_source: params.get("utm_source"),
864
- utm_medium: params.get("utm_medium"),
865
- utm_campaign: params.get("utm_campaign"),
866
- utm_content: params.get("utm_content"),
867
- utm_term: params.get("utm_term"),
868
- ref: params.get("ref"),
878
+ utm_source: params.get("utm_source")?.trim() || "",
879
+ utm_medium: params.get("utm_medium")?.trim() || "",
880
+ utm_campaign: params.get("utm_campaign")?.trim() || "",
881
+ utm_content: params.get("utm_content")?.trim() || "",
882
+ utm_term: params.get("utm_term")?.trim() || "",
883
+ ref: params.get("ref")?.trim() || "",
869
884
  ...eventSpecificPayload,
870
885
  };
871
886
  }
@@ -920,18 +935,21 @@ export class FormoAnalytics implements IFormoAnalytics {
920
935
  }
921
936
 
922
937
  interface IFormoAnalyticsSession {
923
- isIdentified(): boolean;
924
- identify(): void;
938
+ isWalletDetected(rdns: string): boolean;
939
+ markWalletdetected(rdns: string): void;
925
940
  }
926
941
 
927
942
  class FormoAnalyticsSession implements IFormoAnalyticsSession {
928
943
  constructor() {}
929
944
 
930
- public isIdentified(): boolean {
931
- return session.get(SESSION_IDENTIFIED_KEY) === true;
945
+ public isWalletDetected(rdns: string): boolean {
946
+ const rdnses = (session.get(SESSION_WALLET_DETECTED_KEY) as string[]) || [];
947
+ return rdnses.includes(rdns);
932
948
  }
933
949
 
934
- public identify(): void {
935
- session.set(SESSION_IDENTIFIED_KEY, true);
950
+ public markWalletdetected(rdns: string): void {
951
+ const rdnses = (session.get(SESSION_WALLET_DETECTED_KEY) as string[]) || [];
952
+ rdnses.push(rdns);
953
+ session.set(SESSION_WALLET_DETECTED_KEY, rdnses);
936
954
  }
937
955
  }
@@ -2,8 +2,9 @@ const STORAGE_PREFIX = "formo-";
2
2
 
3
3
  const generateStoragePrefix = (prefix: string) => `${STORAGE_PREFIX}${prefix}`;
4
4
 
5
- export const SESSION_IDENTIFIED_KEY =
6
- generateStoragePrefix("session-identified");
5
+ export const SESSION_WALLET_DETECTED_KEY = generateStoragePrefix(
6
+ "session-wallet-detected"
7
+ );
7
8
  export const SESSION_CURRENT_URL_KEY = generateStoragePrefix(
8
9
  "analytics-current-url"
9
10
  );
@@ -1,9 +1,10 @@
1
1
  export enum Event {
2
- PAGE = 'page_hit',
3
- IDENTIFY = 'identify',
4
- CONNECT = 'connect',
5
- DISCONNECT = 'disconnect',
6
- CHAIN_CHANGED = 'chain_changed',
7
- SIGNATURE = 'signature',
8
- TRANSACTION = 'transaction',
2
+ PAGE = "page_hit",
3
+ IDENTIFY = "identify",
4
+ DETECT_WALLET = "detect_wallet",
5
+ CONNECT = "connect",
6
+ DISCONNECT = "disconnect",
7
+ CHAIN_CHANGED = "chain_changed",
8
+ SIGNATURE = "signature",
9
+ TRANSACTION = "transaction",
9
10
  }
package/src/lib/queue.ts CHANGED
@@ -100,6 +100,7 @@ export class EventQueue {
100
100
  callback = callback || noop;
101
101
 
102
102
  const formattedTimestamp = toDateHourMinute(new Date(event.timestamp));
103
+ const originTimestamp = event.timestamp;
103
104
  event.timestamp = formattedTimestamp;
104
105
 
105
106
  const eventString = JSON.stringify(event);
@@ -114,7 +115,7 @@ export class EventQueue {
114
115
  return;
115
116
  }
116
117
 
117
- this.queue.push({ message: { ...event, id: eventId }, callback });
118
+ this.queue.push({ message: { ...event, timestamp: originTimestamp, id: eventId }, callback });
118
119
 
119
120
  logger.log(
120
121
  `Event enqueued: ${getActionDescriptor(event.action, event.payload)}`
@@ -2,7 +2,7 @@ import { UUID } from "crypto";
2
2
 
3
3
  export interface RequestEvent {
4
4
  anonymous_id: UUID;
5
- user_id: UUID | null;
5
+ user_id: string | null;
6
6
  action: string;
7
7
  payload: Record<string, unknown>;
8
8
  address: string | null;