@formo/analytics 1.19.7 → 1.19.8

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.
@@ -62,6 +62,14 @@ import { toChecksumAddress } from "./utils";
62
62
  import { getValidAddress } from "./utils/address";
63
63
  import { isLocalhost } from "./validators";
64
64
  import { parseChainId } from "./utils/chain";
65
+ /**
66
+ * Constants for provider switching reasons
67
+ */
68
+ var PROVIDER_SWITCH_REASONS = {
69
+ ADDRESS_MISMATCH: "Address mismatch indicates wallet switch",
70
+ NO_ACCOUNTS: "Current provider has no accounts",
71
+ CHECK_FAILED: "Could not check current provider accounts"
72
+ };
65
73
  var FormoAnalytics = /** @class */ (function () {
66
74
  function FormoAnalytics(writeKey, options) {
67
75
  if (options === void 0) { options = {}; }
@@ -83,6 +91,8 @@ var FormoAnalytics = /** @class */ (function () {
83
91
  * - A provider can be tracked but later removed from discovery
84
92
  */
85
93
  this._trackedProviders = new Set();
94
+ // Flag to prevent concurrent processing of accountsChanged events
95
+ this._processingAccountsChanged = false;
86
96
  // Set to efficiently track seen providers for deduplication and O(1) lookup
87
97
  this._seenProviders = new Set();
88
98
  this.currentUserId = "";
@@ -282,7 +292,8 @@ var FormoAnalytics = /** @class */ (function () {
282
292
  _a.sent();
283
293
  this.currentAddress = undefined;
284
294
  this.currentChainId = undefined;
285
- logger.info("Wallet disconnected: Cleared currentAddress and currentChainId");
295
+ this._provider = undefined;
296
+ logger.info("Wallet disconnected: Cleared currentAddress, currentChainId, and provider");
286
297
  return [2 /*return*/];
287
298
  }
288
299
  });
@@ -580,12 +591,49 @@ var FormoAnalytics = /** @class */ (function () {
580
591
  };
581
592
  FormoAnalytics.prototype.onAccountsChanged = function (provider, accounts) {
582
593
  return __awaiter(this, void 0, void 0, function () {
583
- var error_1, address, nextChainId, wasDisconnected, isProviderSwitch, hadPreviousConnection, error_2, providerInfo, effectiveChainId;
584
- var _a, _b, _c;
585
- return __generator(this, function (_d) {
586
- switch (_d.label) {
594
+ return __generator(this, function (_a) {
595
+ switch (_a.label) {
587
596
  case 0:
588
597
  logger.info("onAccountsChanged", accounts);
598
+ // Prevent concurrent processing of accountsChanged events to avoid race conditions
599
+ if (this._processingAccountsChanged) {
600
+ logger.debug("OnAccountsChanged: Already processing accountsChanged, skipping", {
601
+ provider: this.getProviderInfo(provider).name
602
+ });
603
+ return [2 /*return*/];
604
+ }
605
+ this._processingAccountsChanged = true;
606
+ _a.label = 1;
607
+ case 1:
608
+ _a.trys.push([1, , 3, 4]);
609
+ return [4 /*yield*/, this._handleAccountsChanged(provider, accounts)];
610
+ case 2:
611
+ _a.sent();
612
+ return [3 /*break*/, 4];
613
+ case 3:
614
+ this._processingAccountsChanged = false;
615
+ return [7 /*endfinally*/];
616
+ case 4: return [2 /*return*/];
617
+ }
618
+ });
619
+ });
620
+ };
621
+ /**
622
+ * Handles changes to the accounts of a given EIP-1193 provider.
623
+ *
624
+ * @param provider - The EIP-1193 provider whose accounts have changed.
625
+ * @param accounts - The new array of account addresses. An empty array indicates a disconnect.
626
+ * @returns A promise that resolves when the account change has been processed.
627
+ *
628
+ * If the accounts array is empty and the provider is the active provider, this method triggers
629
+ * a disconnect flow. Otherwise, it updates the state to reflect the new accounts as needed.
630
+ */
631
+ FormoAnalytics.prototype._handleAccountsChanged = function (provider, accounts) {
632
+ return __awaiter(this, void 0, void 0, function () {
633
+ var error_1, address, currentStoredAddress, newProviderAddress, activeProviderAccounts, error_2, nextChainId, wasDisconnected, providerInfo, effectiveChainId;
634
+ return __generator(this, function (_a) {
635
+ switch (_a.label) {
636
+ case 0:
589
637
  if (!(accounts.length === 0)) return [3 /*break*/, 7];
590
638
  if (!(this._provider === provider)) return [3 /*break*/, 5];
591
639
  logger.info("OnAccountsChanged: Detecting disconnect, current state:", {
@@ -593,9 +641,9 @@ var FormoAnalytics = /** @class */ (function () {
593
641
  currentChainId: this.currentChainId,
594
642
  providerMatch: this._provider === provider
595
643
  });
596
- _d.label = 1;
644
+ _a.label = 1;
597
645
  case 1:
598
- _d.trys.push([1, 3, , 4]);
646
+ _a.trys.push([1, 3, , 4]);
599
647
  // Pass current state explicitly to ensure we have the data for the disconnect event
600
648
  return [4 /*yield*/, this.disconnect({
601
649
  chainId: this.currentChainId,
@@ -603,16 +651,16 @@ var FormoAnalytics = /** @class */ (function () {
603
651
  })];
604
652
  case 2:
605
653
  // Pass current state explicitly to ensure we have the data for the disconnect event
606
- _d.sent();
654
+ _a.sent();
607
655
  return [3 /*break*/, 4];
608
656
  case 3:
609
- error_1 = _d.sent();
657
+ error_1 = _a.sent();
610
658
  logger.error("Failed to disconnect provider on accountsChanged", error_1);
611
659
  return [3 /*break*/, 4];
612
660
  case 4: return [3 /*break*/, 6];
613
661
  case 5:
614
662
  logger.info("OnAccountsChanged: Ignoring disconnect for non-active provider");
615
- _d.label = 6;
663
+ _a.label = 6;
616
664
  case 6: return [2 /*return*/];
617
665
  case 7:
618
666
  address = this.validateAndChecksumAddress(accounts[0]);
@@ -620,54 +668,106 @@ var FormoAnalytics = /** @class */ (function () {
620
668
  logger.warn("onAccountsChanged: Invalid address received", accounts[0]);
621
669
  return [2 /*return*/];
622
670
  }
623
- // If both the provider and address are the same, no-op. Allow provider switches even if address is the same.
624
- if (this._provider === provider && address === this.currentAddress) {
625
- return [2 /*return*/];
626
- }
627
- return [4 /*yield*/, this.getCurrentChainId(provider)];
628
- case 8:
629
- nextChainId = _d.sent();
630
- wasDisconnected = !this.currentAddress;
631
- isProviderSwitch = this._provider && this._provider !== provider;
632
- logger.info("OnAccountsChanged: Provider switching analysis:", {
633
- currentProvider: ((_b = (_a = this._provider) === null || _a === void 0 ? void 0 : _a.constructor) === null || _b === void 0 ? void 0 : _b.name) || 'none',
634
- newProvider: ((_c = provider === null || provider === void 0 ? void 0 : provider.constructor) === null || _c === void 0 ? void 0 : _c.name) || 'unknown',
635
- currentAddress: this.currentAddress,
636
- wasDisconnected: wasDisconnected,
637
- isProviderMismatch: isProviderSwitch
671
+ if (!(this._provider && this._provider !== provider)) return [3 /*break*/, 18];
672
+ currentStoredAddress = this.currentAddress;
673
+ newProviderAddress = this.validateAndChecksumAddress(address);
674
+ logger.info("OnAccountsChanged: Different provider attempting to connect", {
675
+ activeProvider: this.getProviderInfo(this._provider).name,
676
+ eventProvider: this.getProviderInfo(provider).name,
677
+ currentStoredAddress: currentStoredAddress,
678
+ newProviderAddress: newProviderAddress
638
679
  });
639
- if (!isProviderSwitch) return [3 /*break*/, 13];
640
- hadPreviousConnection = !!this.currentAddress;
641
- if (!hadPreviousConnection) return [3 /*break*/, 12];
642
- logger.info("OnAccountsChanged: Detected provider switch with existing connection - emitting disconnect for previous provider");
643
- _d.label = 9;
680
+ _a.label = 8;
681
+ case 8:
682
+ _a.trys.push([8, 16, , 18]);
683
+ return [4 /*yield*/, this.getAccounts(this._provider)];
644
684
  case 9:
645
- _d.trys.push([9, 11, , 12]);
685
+ activeProviderAccounts = _a.sent();
686
+ logger.info("OnAccountsChanged: Checking current provider accounts", {
687
+ activeProvider: this.getProviderInfo(this._provider).name,
688
+ accountsLength: activeProviderAccounts ? activeProviderAccounts.length : 0,
689
+ accounts: activeProviderAccounts
690
+ });
691
+ if (!(activeProviderAccounts && activeProviderAccounts.length > 0)) return [3 /*break*/, 13];
692
+ if (!(newProviderAddress && currentStoredAddress && newProviderAddress !== currentStoredAddress)) return [3 /*break*/, 11];
693
+ logger.info("OnAccountsChanged: Different address detected, switching providers despite current provider having accounts", {
694
+ activeProvider: this.getProviderInfo(this._provider).name,
695
+ eventProvider: this.getProviderInfo(provider).name,
696
+ currentAddress: currentStoredAddress,
697
+ newAddress: newProviderAddress,
698
+ reason: PROVIDER_SWITCH_REASONS.ADDRESS_MISMATCH
699
+ });
700
+ // Emit disconnect for the old provider
646
701
  return [4 /*yield*/, this.disconnect({
647
702
  chainId: this.currentChainId,
648
703
  address: this.currentAddress
649
704
  })];
650
705
  case 10:
651
- _d.sent();
706
+ // Emit disconnect for the old provider
707
+ _a.sent();
708
+ // Clear state and let the new provider become active
709
+ this._provider = undefined;
652
710
  return [3 /*break*/, 12];
653
711
  case 11:
654
- error_2 = _d.sent();
655
- logger.error("Failed to emit disconnect during provider switch:", error_2);
656
- return [3 /*break*/, 12];
657
- case 12:
658
- // Validate that the new provider is in a valid state before switching
659
- if (this.isProviderInValidState(provider)) {
660
- logger.info("OnAccountsChanged: Handling provider mismatch - switching providers");
661
- this.handleProviderMismatch(provider);
712
+ logger.info("OnAccountsChanged: Current provider still has accounts and same address, ignoring new provider", {
713
+ activeProvider: this.getProviderInfo(this._provider).name,
714
+ eventProvider: this.getProviderInfo(provider).name,
715
+ activeProviderAccountsCount: activeProviderAccounts.length,
716
+ currentAddress: currentStoredAddress,
717
+ newAddress: newProviderAddress
718
+ });
719
+ return [2 /*return*/];
720
+ case 12: return [3 /*break*/, 15];
721
+ case 13:
722
+ logger.info("OnAccountsChanged: Current provider has no accounts, switching to new provider", {
723
+ oldProvider: this.getProviderInfo(this._provider).name,
724
+ newProvider: this.getProviderInfo(provider).name,
725
+ reason: PROVIDER_SWITCH_REASONS.NO_ACCOUNTS
726
+ });
727
+ // Emit disconnect for the old provider that didn't signal properly
728
+ return [4 /*yield*/, this.disconnect({
729
+ chainId: this.currentChainId,
730
+ address: this.currentAddress
731
+ })];
732
+ case 14:
733
+ // Emit disconnect for the old provider that didn't signal properly
734
+ _a.sent();
735
+ // Clear state and let the new provider become active
736
+ this._provider = undefined;
737
+ _a.label = 15;
738
+ case 15: return [3 /*break*/, 18];
739
+ case 16:
740
+ error_2 = _a.sent();
741
+ logger.warn("OnAccountsChanged: Could not check current provider accounts, switching to new provider", {
742
+ error: error_2 instanceof Error ? error_2.message : String(error_2),
743
+ errorType: error_2 instanceof Error ? error_2.constructor.name : typeof error_2,
744
+ oldProvider: this._provider ? this.getProviderInfo(this._provider).name : 'unknown',
745
+ newProvider: this.getProviderInfo(provider).name,
746
+ reason: PROVIDER_SWITCH_REASONS.CHECK_FAILED
747
+ });
748
+ // If we can't check the current provider, assume it's disconnected
749
+ return [4 /*yield*/, this.disconnect({
750
+ chainId: this.currentChainId,
751
+ address: this.currentAddress
752
+ })];
753
+ case 17:
754
+ // If we can't check the current provider, assume it's disconnected
755
+ _a.sent();
756
+ this._provider = undefined;
757
+ return [3 /*break*/, 18];
758
+ case 18:
759
+ // Set provider if none exists (first connection)
760
+ if (!this._provider) {
761
+ this._provider = provider;
662
762
  }
663
- else {
664
- logger.warn("Provider switching blocked: new provider is not in a valid state");
763
+ // If both the provider and address are the same, no-op
764
+ if (this._provider === provider && address === this.currentAddress) {
665
765
  return [2 /*return*/];
666
766
  }
667
- _d.label = 13;
668
- case 13:
669
- // Commit new active provider and state
670
- this._provider = provider;
767
+ return [4 /*yield*/, this.getCurrentChainId(provider)];
768
+ case 19:
769
+ nextChainId = _a.sent();
770
+ wasDisconnected = !this.currentAddress;
671
771
  this.currentAddress = address;
672
772
  this.currentChainId = nextChainId;
673
773
  providerInfo = this.getProviderInfo(provider);
@@ -675,7 +775,6 @@ var FormoAnalytics = /** @class */ (function () {
675
775
  chainId: nextChainId,
676
776
  address: address,
677
777
  wasDisconnected: wasDisconnected,
678
- isProviderSwitch: isProviderSwitch,
679
778
  providerName: providerInfo.name,
680
779
  rdns: providerInfo.rdns,
681
780
  hasChainId: !!nextChainId
@@ -798,7 +897,7 @@ var FormoAnalytics = /** @class */ (function () {
798
897
  };
799
898
  FormoAnalytics.prototype.onConnected = function (provider, connection) {
800
899
  return __awaiter(this, void 0, void 0, function () {
801
- var chainId, address, wasDisconnected, providerInfo, effectiveChainId, e_3;
900
+ var chainId, address, wasDisconnected, isActiveProvider, providerInfo, effectiveChainId, providerInfo, e_3;
802
901
  return __generator(this, function (_a) {
803
902
  switch (_a.label) {
804
903
  case 0:
@@ -818,17 +917,21 @@ var FormoAnalytics = /** @class */ (function () {
818
917
  if (!this._provider) {
819
918
  this._provider = provider;
820
919
  }
821
- this.currentChainId = chainId;
822
- this.currentAddress = this.validateAndChecksumAddress(address) || undefined;
823
- // Always emit connect event for better analytics capture
824
- if (this.currentAddress) {
920
+ isActiveProvider = this._provider === provider;
921
+ // Only update global state (chainId/address) from the active provider
922
+ if (isActiveProvider) {
923
+ this.currentChainId = chainId;
924
+ this.currentAddress = this.validateAndChecksumAddress(address) || undefined;
925
+ }
926
+ if (isActiveProvider && this.currentAddress) {
825
927
  providerInfo = this.getProviderInfo(provider);
826
928
  logger.info("OnConnected: Detected wallet connection, emitting connect event", {
827
929
  chainId: chainId,
828
930
  wasDisconnected: wasDisconnected,
829
931
  providerName: providerInfo.name,
830
932
  rdns: providerInfo.rdns,
831
- hasChainId: !!chainId
933
+ hasChainId: !!chainId,
934
+ isActiveProvider: isActiveProvider
832
935
  });
833
936
  effectiveChainId = chainId || 0;
834
937
  if (effectiveChainId === 0) {
@@ -844,6 +947,16 @@ var FormoAnalytics = /** @class */ (function () {
844
947
  logger.error("Failed to track connect event during provider connection:", error);
845
948
  });
846
949
  }
950
+ else if (address && !isActiveProvider) {
951
+ providerInfo = this.getProviderInfo(provider);
952
+ logger.debug("OnConnected: Skipping connect event for non-active provider", {
953
+ chainId: chainId,
954
+ providerName: providerInfo.name,
955
+ rdns: providerInfo.rdns,
956
+ isActiveProvider: isActiveProvider,
957
+ activeProviderInfo: this._provider ? this.getProviderInfo(this._provider) : null
958
+ });
959
+ }
847
960
  }
848
961
  return [3 /*break*/, 4];
849
962
  case 3:
@@ -1,2 +1,2 @@
1
- export declare const version = "1.19.5";
1
+ export declare const version = "1.19.7";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1,3 +1,3 @@
1
1
  // Generated by genversion.
2
- export var version = '1.19.5';
2
+ export var version = '1.19.7';
3
3
  //# sourceMappingURL=version.js.map
@@ -1,4 +1,13 @@
1
1
  export var getActionDescriptor = function (type, properties) {
2
- return "".concat(type).concat((properties === null || properties === void 0 ? void 0 : properties.status) ? " ".concat(properties === null || properties === void 0 ? void 0 : properties.status) : "");
2
+ var descriptor = type;
3
+ // Add status for events that have it (e.g., signature, transaction)
4
+ if (properties === null || properties === void 0 ? void 0 : properties.status) {
5
+ descriptor += " ".concat(properties.status);
6
+ }
7
+ // Add RDNS for connect/disconnect events to identify the wallet provider
8
+ if ((type === 'connect' || type === 'disconnect') && (properties === null || properties === void 0 ? void 0 : properties.rdns)) {
9
+ descriptor += " (".concat(properties.rdns, ")");
10
+ }
11
+ return descriptor;
3
12
  };
4
13
  //# sourceMappingURL=base.js.map