@formo/analytics 1.33.1 → 1.34.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.
- package/README.md +3 -0
- package/dist/cjs/src/FormoAnalytics.d.ts +65 -19
- package/dist/cjs/src/FormoAnalytics.js +187 -94
- package/dist/cjs/src/core.d.ts +2 -2
- package/dist/cjs/src/core.js +2 -1
- package/dist/cjs/src/event/EventFactory.d.ts +1 -1
- package/dist/cjs/src/event/EventFactory.js +32 -16
- package/dist/cjs/src/event/sanitize.d.ts +13 -0
- package/dist/cjs/src/event/sanitize.js +94 -0
- package/dist/cjs/src/privy/index.d.ts +8 -3
- package/dist/cjs/src/privy/index.js +8 -3
- package/dist/cjs/src/privy/types.d.ts +25 -2
- package/dist/cjs/src/privy/utils.d.ts +100 -0
- package/dist/cjs/src/privy/utils.js +375 -16
- package/dist/cjs/src/session/index.d.ts +73 -6
- package/dist/cjs/src/session/index.js +309 -12
- package/dist/cjs/src/solana/SolanaManager.d.ts +1 -1
- package/dist/cjs/src/solana/SolanaManager.js +1 -1
- package/dist/cjs/src/solana/storeTypes.d.ts +1 -1
- package/dist/cjs/src/solana/storeTypes.js +1 -1
- package/dist/cjs/src/solana/types.d.ts +2 -2
- package/dist/cjs/src/types/base.d.ts +17 -1
- package/dist/cjs/src/version.d.ts +1 -1
- package/dist/cjs/src/version.js +1 -1
- package/dist/esm/src/FormoAnalytics.d.ts +65 -19
- package/dist/esm/src/FormoAnalytics.js +187 -94
- package/dist/esm/src/core.d.ts +2 -2
- package/dist/esm/src/core.js +1 -1
- package/dist/esm/src/event/EventFactory.d.ts +1 -1
- package/dist/esm/src/event/EventFactory.js +32 -16
- package/dist/esm/src/event/sanitize.d.ts +13 -0
- package/dist/esm/src/event/sanitize.js +88 -0
- package/dist/esm/src/privy/index.d.ts +8 -3
- package/dist/esm/src/privy/index.js +7 -3
- package/dist/esm/src/privy/types.d.ts +25 -2
- package/dist/esm/src/privy/utils.d.ts +100 -0
- package/dist/esm/src/privy/utils.js +374 -16
- package/dist/esm/src/session/index.d.ts +73 -6
- package/dist/esm/src/session/index.js +309 -12
- package/dist/esm/src/solana/SolanaManager.d.ts +1 -1
- package/dist/esm/src/solana/SolanaManager.js +1 -1
- package/dist/esm/src/solana/storeTypes.d.ts +1 -1
- package/dist/esm/src/solana/storeTypes.js +1 -1
- package/dist/esm/src/solana/types.d.ts +2 -2
- package/dist/esm/src/types/base.d.ts +17 -1
- package/dist/esm/src/version.d.ts +1 -1
- package/dist/esm/src/version.js +1 -1
- package/dist/index.umd.min.js +1 -1
- package/package.json +7 -7
|
@@ -72,6 +72,7 @@ import { parseChainId } from "./utils/chain";
|
|
|
72
72
|
import { WagmiEventHandler } from "./wagmi";
|
|
73
73
|
import { isSolanaChainId } from "./solana";
|
|
74
74
|
import { SolanaManager } from "./solana/SolanaManager";
|
|
75
|
+
import { identifyPrivyUser } from "./privy";
|
|
75
76
|
/**
|
|
76
77
|
* Constants for provider switching reasons
|
|
77
78
|
*/
|
|
@@ -87,7 +88,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
87
88
|
var _a, _b, _c;
|
|
88
89
|
this.writeKey = writeKey;
|
|
89
90
|
this.options = options;
|
|
90
|
-
// Per-chain namespace state
|
|
91
|
+
// Per-chain namespace state - isolates EVM and Solana connection state
|
|
91
92
|
this._chainState = {
|
|
92
93
|
evm: {},
|
|
93
94
|
solana: {},
|
|
@@ -203,7 +204,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
203
204
|
this.trackPageHits();
|
|
204
205
|
}
|
|
205
206
|
Object.defineProperty(FormoAnalytics.prototype, "_provider", {
|
|
206
|
-
// EVM state accessors
|
|
207
|
+
// EVM state accessors - EVM listener paths must use these instead of
|
|
207
208
|
// currentAddress/currentChainId to avoid cross-namespace reads.
|
|
208
209
|
get: function () {
|
|
209
210
|
return this._chainState.evm.provider;
|
|
@@ -393,7 +394,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
393
394
|
return [2 /*return*/];
|
|
394
395
|
}
|
|
395
396
|
// connect() persists wallet/chain state (active-wallet cookie,
|
|
396
|
-
// currentAddress/currentChainId) before trackEvent's consent check
|
|
397
|
+
// currentAddress/currentChainId) before trackEvent's consent check -
|
|
397
398
|
// gate the whole method so a suppressed visitor or excluded environment
|
|
398
399
|
// (opt-out / timezone / host / path) leaves no session state.
|
|
399
400
|
if (this.isTrackingSuppressed()) {
|
|
@@ -550,79 +551,85 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
550
551
|
});
|
|
551
552
|
});
|
|
552
553
|
};
|
|
553
|
-
|
|
554
|
-
* Emits an identify event with current wallet address and provider info.
|
|
555
|
-
*
|
|
556
|
-
* @param {string} params.address - Wallet address
|
|
557
|
-
* @param {string} params.userId - External user ID
|
|
558
|
-
* @param {string} params.rdns - Provider reverse domain name
|
|
559
|
-
* @param {string} params.providerName - Provider display name
|
|
560
|
-
* @param {IFormoEventProperties} properties - Additional properties to include with the identify event
|
|
561
|
-
* @param {IFormoEventContext} context
|
|
562
|
-
* @param {(...args: unknown[]) => void} callback
|
|
563
|
-
* @returns {Promise<void>}
|
|
564
|
-
*
|
|
565
|
-
* @example
|
|
566
|
-
* ```ts
|
|
567
|
-
* // Basic identify
|
|
568
|
-
* formo.identify({ address: '0x...', userId: 'user123' });
|
|
569
|
-
*
|
|
570
|
-
* // With Privy user
|
|
571
|
-
* import { parsePrivyProperties } from '@formo/analytics';
|
|
572
|
-
* const { user } = usePrivy();
|
|
573
|
-
* if (user) {
|
|
574
|
-
* const { properties, wallets } = parsePrivyProperties(user);
|
|
575
|
-
* for (const wallet of wallets) {
|
|
576
|
-
* formo.identify({ address: wallet.address, userId: user.id }, properties);
|
|
577
|
-
* }
|
|
578
|
-
* }
|
|
579
|
-
* ```
|
|
580
|
-
*/
|
|
581
|
-
FormoAnalytics.prototype.identify = function (params, properties, context, callback) {
|
|
554
|
+
FormoAnalytics.prototype.identify = function (paramsOrUser, propertiesOrOptions, context, callback) {
|
|
582
555
|
return __awaiter(this, void 0, void 0, function () {
|
|
583
|
-
var _i, _a, providerDetail, provider, address_1, validAddress_1, err_1, address, providerName, userId, rdns, validAddress, domain, isAlreadyIdentified, e_1;
|
|
556
|
+
var maybeUser, opts, params, properties, _i, _a, providerDetail, provider, address_1, validAddress_1, err_1, address, providerName, userId, rdns, setActive, validAddress, domain, isAlreadyIdentified, e_1;
|
|
584
557
|
var _b, _c;
|
|
585
558
|
return __generator(this, function (_d) {
|
|
586
559
|
switch (_d.label) {
|
|
587
560
|
case 0:
|
|
588
|
-
_d.trys.push([0,
|
|
561
|
+
_d.trys.push([0, 14, , 15]);
|
|
562
|
+
maybeUser = paramsOrUser;
|
|
563
|
+
if (!(maybeUser &&
|
|
564
|
+
typeof maybeUser.id === "string" &&
|
|
565
|
+
maybeUser.address === undefined)) return [3 /*break*/, 2];
|
|
566
|
+
opts = (propertiesOrOptions !== null && propertiesOrOptions !== void 0 ? propertiesOrOptions : {});
|
|
567
|
+
// identifyPrivyUser records every linked wallet for clustering WITHOUT
|
|
568
|
+
// touching active state (internal setActive:false), promotes only the
|
|
569
|
+
// resolved active wallet, and reconciles the chain id with that wallet's
|
|
570
|
+
// namespace before emitting. It reads this.currentAddress itself to
|
|
571
|
+
// preserve an already-connected wallet, so this dispatch is a thin
|
|
572
|
+
// pass-through and both entry points behave identically.
|
|
573
|
+
return [4 /*yield*/, identifyPrivyUser(this, maybeUser, {
|
|
574
|
+
activeAddress: opts.activeAddress,
|
|
575
|
+
properties: opts.properties,
|
|
576
|
+
})];
|
|
577
|
+
case 1:
|
|
578
|
+
// identifyPrivyUser records every linked wallet for clustering WITHOUT
|
|
579
|
+
// touching active state (internal setActive:false), promotes only the
|
|
580
|
+
// resolved active wallet, and reconciles the chain id with that wallet's
|
|
581
|
+
// namespace before emitting. It reads this.currentAddress itself to
|
|
582
|
+
// preserve an already-connected wallet, so this dispatch is a thin
|
|
583
|
+
// pass-through and both entry points behave identically.
|
|
584
|
+
_d.sent();
|
|
585
|
+
return [2 /*return*/];
|
|
586
|
+
case 2:
|
|
587
|
+
params = paramsOrUser;
|
|
588
|
+
properties = propertiesOrOptions;
|
|
589
589
|
// identify() writes the user-id cookie and marks wallet
|
|
590
|
-
// identification before trackEvent's consent check
|
|
590
|
+
// identification before trackEvent's consent check - gate the whole
|
|
591
591
|
// method so a suppressed visitor or excluded environment (opt-out /
|
|
592
592
|
// timezone / host / path) gets no identity persistence.
|
|
593
|
-
|
|
594
|
-
|
|
593
|
+
//
|
|
594
|
+
// The chain check belongs here and NOT in isTrackingSuppressed(), which
|
|
595
|
+
// identifyPrivyUser calls before it reconciles the chain. Folding it in
|
|
596
|
+
// there would block a Privy sync that reconciliation was about to make
|
|
597
|
+
// valid (activating a Solana wallet while an excluded EVM chain id is
|
|
598
|
+
// still current). By the time the Privy path reaches this guard it has
|
|
599
|
+
// already reconciled, so each inner identify is judged on the right chain.
|
|
600
|
+
if (this.isTrackingSuppressed() || this.isCurrentChainExcluded()) {
|
|
601
|
+
logger.info("identify() skipped: tracking is suppressed for this visitor, environment, or chain");
|
|
595
602
|
return [2 /*return*/];
|
|
596
603
|
}
|
|
597
|
-
if (!!params) return [3 /*break*/,
|
|
604
|
+
if (!!params) return [3 /*break*/, 12];
|
|
598
605
|
// If no params provided, auto-identify
|
|
599
606
|
logger.info("Auto-identifying with providers:", this._providers.map(function (p) { return p.info.name; }));
|
|
600
607
|
_i = 0, _a = this._providers;
|
|
601
|
-
_d.label =
|
|
602
|
-
case
|
|
603
|
-
if (!(_i < _a.length)) return [3 /*break*/,
|
|
608
|
+
_d.label = 3;
|
|
609
|
+
case 3:
|
|
610
|
+
if (!(_i < _a.length)) return [3 /*break*/, 11];
|
|
604
611
|
providerDetail = _a[_i];
|
|
605
612
|
provider = providerDetail.provider;
|
|
606
613
|
if (!provider)
|
|
607
|
-
return [3 /*break*/,
|
|
608
|
-
_d.label =
|
|
609
|
-
case
|
|
610
|
-
_d.trys.push([
|
|
614
|
+
return [3 /*break*/, 10];
|
|
615
|
+
_d.label = 4;
|
|
616
|
+
case 4:
|
|
617
|
+
_d.trys.push([4, 9, , 10]);
|
|
611
618
|
return [4 /*yield*/, this.getAddress(provider)];
|
|
612
|
-
case
|
|
619
|
+
case 5:
|
|
613
620
|
address_1 = _d.sent();
|
|
614
|
-
if (!address_1) return [3 /*break*/,
|
|
621
|
+
if (!address_1) return [3 /*break*/, 8];
|
|
615
622
|
validAddress_1 = validateAndChecksumAddress(address_1);
|
|
616
623
|
logger.info("Auto-identify: Checking deduplication", {
|
|
617
624
|
validAddress: validAddress_1,
|
|
618
625
|
rdns: providerDetail.info.rdns,
|
|
619
626
|
providerName: providerDetail.info.name,
|
|
620
627
|
isAlreadyIdentified: validAddress_1
|
|
621
|
-
? this.session.isWalletIdentified(validAddress_1, providerDetail.info.rdns)
|
|
628
|
+
? this.session.isWalletIdentified(validAddress_1, providerDetail.info.rdns, undefined, properties)
|
|
622
629
|
: false,
|
|
623
630
|
});
|
|
624
631
|
if (!(validAddress_1 &&
|
|
625
|
-
!this.session.isWalletIdentified(validAddress_1, providerDetail.info.rdns))) return [3 /*break*/,
|
|
632
|
+
!this.session.isWalletIdentified(validAddress_1, providerDetail.info.rdns, undefined, properties))) return [3 /*break*/, 7];
|
|
626
633
|
logger.info("Auto-identifying", validAddress_1, providerDetail.info.name, providerDetail.info.rdns);
|
|
627
634
|
// NOTE: do not set this.currentAddress without explicit connect or identify
|
|
628
635
|
return [4 /*yield*/, this.identify({
|
|
@@ -630,26 +637,26 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
630
637
|
providerName: providerDetail.info.name,
|
|
631
638
|
rdns: providerDetail.info.rdns,
|
|
632
639
|
}, properties, context, callback)];
|
|
633
|
-
case
|
|
640
|
+
case 6:
|
|
634
641
|
// NOTE: do not set this.currentAddress without explicit connect or identify
|
|
635
642
|
_d.sent();
|
|
636
|
-
return [3 /*break*/,
|
|
637
|
-
case
|
|
643
|
+
return [3 /*break*/, 8];
|
|
644
|
+
case 7:
|
|
638
645
|
if (validAddress_1) {
|
|
639
646
|
logger.info("Auto-identify: Skipping already identified wallet", validAddress_1, providerDetail.info.name, providerDetail.info.rdns);
|
|
640
647
|
}
|
|
641
|
-
_d.label =
|
|
642
|
-
case
|
|
643
|
-
case
|
|
648
|
+
_d.label = 8;
|
|
649
|
+
case 8: return [3 /*break*/, 10];
|
|
650
|
+
case 9:
|
|
644
651
|
err_1 = _d.sent();
|
|
645
652
|
logger.error("Failed to identify provider ".concat(providerDetail.info.name, ":"), err_1);
|
|
646
|
-
return [3 /*break*/,
|
|
647
|
-
case 8:
|
|
648
|
-
_i++;
|
|
649
|
-
return [3 /*break*/, 1];
|
|
650
|
-
case 9: return [2 /*return*/];
|
|
653
|
+
return [3 /*break*/, 10];
|
|
651
654
|
case 10:
|
|
652
|
-
|
|
655
|
+
_i++;
|
|
656
|
+
return [3 /*break*/, 3];
|
|
657
|
+
case 11: return [2 /*return*/];
|
|
658
|
+
case 12:
|
|
659
|
+
address = params.address, providerName = params.providerName, userId = params.userId, rdns = params.rdns, setActive = params.setActive;
|
|
653
660
|
// Runtime validation: address is required
|
|
654
661
|
if (!address) {
|
|
655
662
|
(_b = logger.warn) === null || _b === void 0 ? void 0 : _b.call(logger, "identify() called without address - address is required");
|
|
@@ -658,20 +665,26 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
658
665
|
// Explicit identify
|
|
659
666
|
logger.info("Identify", address, userId, providerName, rdns);
|
|
660
667
|
validAddress = validateAddress(address);
|
|
661
|
-
if (validAddress) {
|
|
662
|
-
this.currentAddress = validAddress;
|
|
663
|
-
this.persistActiveWallet();
|
|
664
|
-
}
|
|
665
|
-
else {
|
|
668
|
+
if (!validAddress) {
|
|
666
669
|
(_c = logger.warn) === null || _c === void 0 ? void 0 : _c.call(logger, "Invalid address provided to identify:", address);
|
|
667
670
|
return [2 /*return*/];
|
|
668
671
|
}
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
672
|
+
// Promote this wallet to the SDK's active identity - the (currentAddress,
|
|
673
|
+
// currentUserId) pair later events are attributed to - unless the caller
|
|
674
|
+
// opts out with setActive:false. A non-active identify still emits its
|
|
675
|
+
// event and marks dedup below (for clustering), it just doesn't repoint
|
|
676
|
+
// attribution. Gating address and userId together prevents leaving the
|
|
677
|
+
// active address paired with a different wallet's user id.
|
|
678
|
+
if (setActive !== false) {
|
|
679
|
+
this.currentAddress = validAddress;
|
|
680
|
+
this.persistActiveWallet();
|
|
681
|
+
if (userId) {
|
|
682
|
+
this.currentUserId = userId;
|
|
683
|
+
domain = getIdentityCookieDomain(this.crossSubdomainCookies);
|
|
684
|
+
cookie().set(SESSION_USER_ID_KEY, userId, __assign(__assign({ path: "/" }, getIdentityCookieSecurity()), (domain ? { domain: domain } : {})));
|
|
685
|
+
}
|
|
673
686
|
}
|
|
674
|
-
isAlreadyIdentified = this.session.isWalletIdentified(validAddress, rdns || "");
|
|
687
|
+
isAlreadyIdentified = this.session.isWalletIdentified(validAddress, rdns || "", userId, properties);
|
|
675
688
|
logger.debug("Identify: Checking deduplication", {
|
|
676
689
|
validAddress: validAddress,
|
|
677
690
|
rdns: rdns,
|
|
@@ -684,25 +697,77 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
684
697
|
return [2 /*return*/];
|
|
685
698
|
}
|
|
686
699
|
// Mark as identified before emitting the event
|
|
687
|
-
this.session.markWalletIdentified(validAddress, rdns || "");
|
|
700
|
+
this.session.markWalletIdentified(validAddress, rdns || "", userId, properties);
|
|
688
701
|
return [4 /*yield*/, this.trackEvent(EventType.IDENTIFY, {
|
|
689
702
|
address: validAddress,
|
|
690
703
|
providerName: providerName,
|
|
691
704
|
userId: userId,
|
|
692
705
|
rdns: rdns,
|
|
693
706
|
}, properties, context, callback)];
|
|
694
|
-
case
|
|
707
|
+
case 13:
|
|
695
708
|
_d.sent();
|
|
696
|
-
return [3 /*break*/,
|
|
697
|
-
case
|
|
709
|
+
return [3 /*break*/, 15];
|
|
710
|
+
case 14:
|
|
698
711
|
e_1 = _d.sent();
|
|
699
712
|
logger.log("identify error", e_1);
|
|
700
|
-
return [3 /*break*/,
|
|
701
|
-
case
|
|
713
|
+
return [3 /*break*/, 15];
|
|
714
|
+
case 15: return [2 /*return*/];
|
|
702
715
|
}
|
|
703
716
|
});
|
|
704
717
|
});
|
|
705
718
|
};
|
|
719
|
+
/**
|
|
720
|
+
* Reconcile currentChainId with a newly-activated Privy wallet's chain
|
|
721
|
+
* namespace. identify() sets currentAddress but never touches the chain id
|
|
722
|
+
* (that comes from connect()/chain()/wagmi), so activating e.g. a Solana
|
|
723
|
+
* wallet while an EVM chain id is current would leave the address paired with
|
|
724
|
+
* a mismatched chain in events, excludeChains, and the active-wallet cookie.
|
|
725
|
+
*
|
|
726
|
+
* We can't infer the wallet's specific chain id from Privy's chainType, so on
|
|
727
|
+
* a namespace mismatch we clear the chain id rather than assert a wrong one; a
|
|
728
|
+
* real wallet connect will set the correct chain. Same-namespace activations
|
|
729
|
+
* (and wallets whose namespace can't be determined) leave the chain id alone.
|
|
730
|
+
*
|
|
731
|
+
* Privy doesn't always supply `chainType`: a `smart_wallet` entry is
|
|
732
|
+
* `{ type, address, smartWalletType }`, and `cross_app` wallets are bare
|
|
733
|
+
* `{ address }`. A `0x`-prefixed 20-byte address is unambiguously EVM though,
|
|
734
|
+
* so fall back to the address shape - otherwise activating an EVM smart
|
|
735
|
+
* wallet while a Solana chain id is current would leave the address paired
|
|
736
|
+
* with the wrong chain, and an `excludeChains` gate could drop the identify
|
|
737
|
+
* after it was already dedup-marked.
|
|
738
|
+
*
|
|
739
|
+
* @internal Not part of the public IFormoAnalytics contract - invoked by
|
|
740
|
+
* `identifyPrivyUser` (via a structural cast) before it emits, so both the
|
|
741
|
+
* `identify(user,{privy:true})` and direct `identifyPrivyUser()` paths
|
|
742
|
+
* reconcile the chain.
|
|
743
|
+
*/
|
|
744
|
+
FormoAnalytics.prototype.syncPrivyActiveChain = function (chainType, address) {
|
|
745
|
+
if (this.currentChainId === undefined || this.currentChainId === null)
|
|
746
|
+
return;
|
|
747
|
+
var walletIsSolana;
|
|
748
|
+
var namespace = chainType === null || chainType === void 0 ? void 0 : chainType.toLowerCase();
|
|
749
|
+
// Only recognized namespaces decide. An unknown or future chainType
|
|
750
|
+
// ("bitcoin", "cosmos", …) must stay undecided rather than defaulting to
|
|
751
|
+
// EVM, which would wrongly clear a valid Solana chain id.
|
|
752
|
+
if (namespace === "solana") {
|
|
753
|
+
walletIsSolana = true;
|
|
754
|
+
}
|
|
755
|
+
else if (namespace === "ethereum") {
|
|
756
|
+
walletIsSolana = false;
|
|
757
|
+
}
|
|
758
|
+
else if (!namespace && address && /^0x[0-9a-f]{40}$/i.test(address)) {
|
|
759
|
+
// Only the EVM shape is inferable: a non-0x address could be Solana,
|
|
760
|
+
// Bitcoin, Cosmos, … so absence of 0x proves nothing.
|
|
761
|
+
walletIsSolana = false;
|
|
762
|
+
}
|
|
763
|
+
if (walletIsSolana === undefined)
|
|
764
|
+
return;
|
|
765
|
+
var currentIsSolana = isSolanaChainId(this.currentChainId);
|
|
766
|
+
if (walletIsSolana !== currentIsSolana) {
|
|
767
|
+
this.currentChainId = undefined;
|
|
768
|
+
this.persistActiveWallet();
|
|
769
|
+
}
|
|
770
|
+
};
|
|
706
771
|
/**
|
|
707
772
|
* Emits a detect wallet event with current wallet provider info.
|
|
708
773
|
* @param {string} params.providerName
|
|
@@ -719,7 +784,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
719
784
|
switch (_c.label) {
|
|
720
785
|
case 0:
|
|
721
786
|
// detect() marks wallet detection (a cookie write) before
|
|
722
|
-
// trackEvent's consent check
|
|
787
|
+
// trackEvent's consent check - gate it for a suppressed visitor or
|
|
723
788
|
// excluded environment (opt-out / timezone / host / path).
|
|
724
789
|
if (this.isTrackingSuppressed()) {
|
|
725
790
|
logger.info("detect() skipped: tracking is suppressed for this visitor or environment");
|
|
@@ -1088,7 +1153,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1088
1153
|
wasDisconnected = !this._evmAddress;
|
|
1089
1154
|
// Update state regardless of whether connect *event* tracking is enabled,
|
|
1090
1155
|
// so disconnect events keep valid address/chainId values. (excludeChains is
|
|
1091
|
-
// NOT suppression
|
|
1156
|
+
// NOT suppression - it still updates state so currentChainId can gate
|
|
1092
1157
|
// events.)
|
|
1093
1158
|
if (this.isTrackingSuppressed()) {
|
|
1094
1159
|
this.clearStaleEvmWalletOnSwitchWhileSuppressed(address);
|
|
@@ -1269,7 +1334,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1269
1334
|
}
|
|
1270
1335
|
isActiveProvider = this._provider === provider;
|
|
1271
1336
|
// Update state from active provider so disconnect events keep valid
|
|
1272
|
-
// address/chainId values
|
|
1337
|
+
// address/chainId values - except while suppressed, where we must not
|
|
1273
1338
|
// LEARN identity (only drop a stale EVM wallet on a switch).
|
|
1274
1339
|
if (isActiveProvider) {
|
|
1275
1340
|
if (this.isTrackingSuppressed()) {
|
|
@@ -1540,7 +1605,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1540
1605
|
var _this = this;
|
|
1541
1606
|
// Install a single, instance-agnostic wrapper around history.pushState /
|
|
1542
1607
|
// replaceState so concurrent SDK instances (React Strict Mode, HMR) don't
|
|
1543
|
-
// each stack their own wrapper
|
|
1608
|
+
// each stack their own wrapper - which would dispatch N synthetic events
|
|
1544
1609
|
// per navigation and produce O(N^2) onLocationChange calls. The wrapper
|
|
1545
1610
|
// dispatches once; per-instance bookkeeping is done by per-instance
|
|
1546
1611
|
// listeners that each register/unregister themselves.
|
|
@@ -1653,22 +1718,50 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1653
1718
|
* Visitor-level tracking suppression.
|
|
1654
1719
|
*
|
|
1655
1720
|
* Returns true when the SDK must not persist any identity/session/chain
|
|
1656
|
-
* state or send any events for this visitor
|
|
1721
|
+
* state or send any events for this visitor - i.e. an explicit opt-out or a
|
|
1657
1722
|
* jurisdiction/timezone exclusion. Public entry points that write state
|
|
1658
1723
|
* before reaching the `shouldTrack()` event gate (identify/connect/detect)
|
|
1659
1724
|
* check this first so suppressed visitors leave no cookies or session state.
|
|
1660
1725
|
* @returns {boolean} True if all tracking and persistence must be suppressed
|
|
1726
|
+
* @internal Also read by `identifyPrivyUser` (via a structural cast) so the
|
|
1727
|
+
* Privy sync skips chain reconciliation and emission for suppressed visitors.
|
|
1661
1728
|
*/
|
|
1662
1729
|
FormoAnalytics.prototype.isTrackingSuppressed = function () {
|
|
1663
1730
|
return this.hasOptedOutTracking() || this.isCurrentEnvironmentExcluded();
|
|
1664
1731
|
};
|
|
1665
1732
|
/**
|
|
1666
|
-
* Whether the current
|
|
1733
|
+
* Whether the current chain id is in `tracking.excludeChains`.
|
|
1734
|
+
*
|
|
1735
|
+
* Split out from `shouldTrack()` so `identify()` can check it *before*
|
|
1736
|
+
* mutating identity state. `trackEvent()` drops an excluded event silently
|
|
1737
|
+
* and returns void, but `identify()` marks the wallet as identified first, so
|
|
1738
|
+
* without this guard an identify on an excluded chain is dedup-marked and
|
|
1739
|
+
* then discarded, and the wallet never re-emits for the rest of the session
|
|
1740
|
+
* even after switching to an allowed chain. On the Privy path that loses the
|
|
1741
|
+
* user's whole cluster at once rather than a single wallet.
|
|
1742
|
+
*
|
|
1743
|
+
* Mirrors the chain rule in `shouldTrack()`: only applies when `tracking` is
|
|
1744
|
+
* an options object with `excludeChains` set, and only once a chain id is
|
|
1745
|
+
* known.
|
|
1746
|
+
*/
|
|
1747
|
+
FormoAnalytics.prototype.isCurrentChainExcluded = function () {
|
|
1748
|
+
if (this.options.tracking === null ||
|
|
1749
|
+
typeof this.options.tracking !== "object" ||
|
|
1750
|
+
Array.isArray(this.options.tracking)) {
|
|
1751
|
+
return false;
|
|
1752
|
+
}
|
|
1753
|
+
var _a = this.options.tracking.excludeChains, excludeChains = _a === void 0 ? [] : _a;
|
|
1754
|
+
return (excludeChains.length > 0 &&
|
|
1755
|
+
!!this.currentChainId &&
|
|
1756
|
+
excludeChains.includes(this.currentChainId));
|
|
1757
|
+
};
|
|
1758
|
+
/**
|
|
1759
|
+
* Whether the current environment is excluded from tracking - the visitor's
|
|
1667
1760
|
* timezone, the current hostname, or the current pathname matches a
|
|
1668
1761
|
* configured exclusion.
|
|
1669
1762
|
*
|
|
1670
1763
|
* Timezone is visitor/session-level (stable for the session); host/path are
|
|
1671
|
-
* current-page-level and transient
|
|
1764
|
+
* current-page-level and transient - if a SPA navigates to an allowed path,
|
|
1672
1765
|
* tracking resumes for future actions. Used as the "do not write identity or
|
|
1673
1766
|
* send events" gate at every entry point that would persist state before the
|
|
1674
1767
|
* `shouldTrack()` event gate.
|
|
@@ -1681,7 +1774,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1681
1774
|
};
|
|
1682
1775
|
/**
|
|
1683
1776
|
* Whether the current hostname matches a configured `tracking.excludeHosts`
|
|
1684
|
-
* entry (exact match). Current-page-level
|
|
1777
|
+
* entry (exact match). Current-page-level - see isCurrentEnvironmentExcluded.
|
|
1685
1778
|
* @returns {boolean} True if the current hostname is excluded
|
|
1686
1779
|
*/
|
|
1687
1780
|
FormoAnalytics.prototype.isHostExcluded = function () {
|
|
@@ -1699,7 +1792,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1699
1792
|
};
|
|
1700
1793
|
/**
|
|
1701
1794
|
* Whether the current pathname matches a configured `tracking.excludePaths`
|
|
1702
|
-
* entry (exact match). Current-page-level
|
|
1795
|
+
* entry (exact match). Current-page-level - see isCurrentEnvironmentExcluded.
|
|
1703
1796
|
* @returns {boolean} True if the current pathname is excluded
|
|
1704
1797
|
*/
|
|
1705
1798
|
FormoAnalytics.prototype.isPathExcluded = function () {
|
|
@@ -1716,8 +1809,8 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1716
1809
|
return excludePaths.includes(window.location.pathname);
|
|
1717
1810
|
};
|
|
1718
1811
|
/**
|
|
1719
|
-
* Whether the current call is in a visitor-level suppression state
|
|
1720
|
-
* or excluded timezone
|
|
1812
|
+
* Whether the current call is in a visitor-level suppression state - opt-out
|
|
1813
|
+
* or excluded timezone - for which any persisted identity cookie should be
|
|
1721
1814
|
* actively purged (not merely skipped). Host/path exclusions are
|
|
1722
1815
|
* deliberately excluded here: they are transient current-page states, so a
|
|
1723
1816
|
* cookie legitimately written on an allowed page must survive a visit to an
|
|
@@ -1730,7 +1823,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1730
1823
|
/**
|
|
1731
1824
|
* Whether the visitor's browser-resolved timezone matches a configured
|
|
1732
1825
|
* `tracking.excludeTimezones` entry (case-insensitive). Client-side and
|
|
1733
|
-
* best-effort
|
|
1826
|
+
* best-effort - see TrackingOptions.excludeTimezones.
|
|
1734
1827
|
* @returns {boolean} True if the current timezone is excluded
|
|
1735
1828
|
*/
|
|
1736
1829
|
FormoAnalytics.prototype.isTimezoneExcluded = function () {
|
|
@@ -1769,7 +1862,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1769
1862
|
typeof this.options.tracking === "object" &&
|
|
1770
1863
|
!Array.isArray(this.options.tracking)) {
|
|
1771
1864
|
var _a = this.options.tracking.excludeChains, excludeChains = _a === void 0 ? [] : _a;
|
|
1772
|
-
// Environment exclusions (timezone / host / path)
|
|
1865
|
+
// Environment exclusions (timezone / host / path) - no identify / connect
|
|
1773
1866
|
// / track events while excluded. Host/path are exact-match.
|
|
1774
1867
|
if (this.isCurrentEnvironmentExcluded()) {
|
|
1775
1868
|
return false;
|
|
@@ -2382,7 +2475,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
2382
2475
|
* WITHOUT emitting an event.
|
|
2383
2476
|
*
|
|
2384
2477
|
* Integrations (e.g. the wagmi handler) must call this on every
|
|
2385
|
-
* connect / chain-change / disconnect
|
|
2478
|
+
* connect / chain-change / disconnect - even when the corresponding
|
|
2386
2479
|
* autocapture event is disabled. Otherwise `currentChainId` stays
|
|
2387
2480
|
* stale/undefined and `shouldTrack()`'s `tracking.excludeChains`
|
|
2388
2481
|
* check (which keys off `currentChainId`, not the event payload) can
|
|
@@ -2396,7 +2489,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
2396
2489
|
var chainId = params.chainId, address = params.address;
|
|
2397
2490
|
if (this.isTrackingSuppressed()) {
|
|
2398
2491
|
// While suppressed (opt-out / timezone / excluded host or path) we must
|
|
2399
|
-
// never LEARN a new wallet
|
|
2492
|
+
// never LEARN a new wallet - but we must still CLEAR stale identity.
|
|
2400
2493
|
// Otherwise a disconnect or wallet switch observed on a suppressed route
|
|
2401
2494
|
// would leave the previously-learned address in memory and in the
|
|
2402
2495
|
// active-wallet cookie, attaching it to later allowed-page events.
|
|
@@ -2478,14 +2571,14 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
2478
2571
|
/**
|
|
2479
2572
|
* Persist (or clear) the current wallet snapshot in a cookie so that the
|
|
2480
2573
|
* SDK can repopulate `currentAddress`/`currentChainId` at init on the next
|
|
2481
|
-
* page load
|
|
2574
|
+
* page load - closing the gap between page-show and wagmi/EIP-1193
|
|
2482
2575
|
* reconnection during which track()/page() events would otherwise ship
|
|
2483
2576
|
* with an empty address.
|
|
2484
2577
|
*/
|
|
2485
2578
|
FormoAnalytics.prototype.persistActiveWallet = function () {
|
|
2486
2579
|
try {
|
|
2487
2580
|
// Visitor-level suppression (opt-out or excluded timezone): purge any
|
|
2488
|
-
// prior snapshot
|
|
2581
|
+
// prior snapshot - these are stable for the session, so deletion is safe.
|
|
2489
2582
|
if (this.isPersistedIdentityPurgeRequired()) {
|
|
2490
2583
|
cookie().remove(ACTIVE_WALLET_KEY);
|
|
2491
2584
|
return;
|
package/dist/esm/src/core.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export * from "./FormoAnalytics";
|
|
2
2
|
export * from "./types";
|
|
3
3
|
export { formofy } from "./initialization";
|
|
4
|
-
export { parsePrivyProperties } from "./privy";
|
|
5
|
-
export type { PrivyUser, PrivyLinkedAccount, PrivyAccountType, PrivyProfileProperties, PrivyWalletInfo, } from "./privy";
|
|
4
|
+
export { parsePrivyProperties, identifyPrivyUser } from "./privy";
|
|
5
|
+
export type { IdentifyPrivyUserOptions, PrivyUser, PrivyLinkedAccount, PrivyAccountType, PrivyProfileProperties, PrivyWalletInfo, } from "./privy";
|
|
6
6
|
export { SolanaManager } from "./solana";
|
|
7
7
|
export { SOLANA_CHAIN_IDS, DEFAULT_SOLANA_CHAIN_ID, isSolanaChainId, } from "./solana";
|
|
8
8
|
export type { SolanaOptions, SolanaCluster } from "./solana";
|
package/dist/esm/src/core.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
export * from "./FormoAnalytics";
|
|
8
8
|
export * from "./types";
|
|
9
9
|
export { formofy } from "./initialization";
|
|
10
|
-
export { parsePrivyProperties } from "./privy";
|
|
10
|
+
export { parsePrivyProperties, identifyPrivyUser } from "./privy";
|
|
11
11
|
export { SolanaManager } from "./solana";
|
|
12
12
|
export { SOLANA_CHAIN_IDS, DEFAULT_SOLANA_CHAIN_ID, isSolanaChainId, } from "./solana";
|
|
13
13
|
//# sourceMappingURL=core.js.map
|
|
@@ -41,7 +41,7 @@ declare class EventFactory implements IEventFactory {
|
|
|
41
41
|
/**
|
|
42
42
|
* Returns the document referrer with same-host referrers filtered out.
|
|
43
43
|
* Internal navigation populates `document.referrer` with the previous page
|
|
44
|
-
* on the same site, which is not an attribution signal
|
|
44
|
+
* on the same site, which is not an attribution signal - treating it as
|
|
45
45
|
* "external" would otherwise let an internal URL become the session's
|
|
46
46
|
* first-touch referrer after a direct landing.
|
|
47
47
|
*/
|