@formo/analytics 1.38.2 → 1.40.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/dist/cjs/src/FormoAnalytics.d.ts +4 -1
- package/dist/cjs/src/FormoAnalytics.js +56 -36
- package/dist/cjs/src/FormoAnalyticsProvider.js +20 -0
- package/dist/cjs/src/event/EventFactory.d.ts +9 -1
- package/dist/cjs/src/event/EventFactory.js +31 -16
- package/dist/cjs/src/event/EventManager.d.ts +3 -1
- package/dist/cjs/src/event/EventManager.js +33 -4
- package/dist/cjs/src/event/cancellation.d.ts +4 -0
- package/dist/cjs/src/event/cancellation.js +7 -0
- package/dist/cjs/src/event/type.d.ts +1 -0
- package/dist/cjs/src/event/utils.d.ts +5 -1
- package/dist/cjs/src/event/utils.js +57 -2
- package/dist/cjs/src/evm/EvmEventTracker.d.ts +7 -0
- package/dist/cjs/src/evm/EvmEventTracker.js +31 -5
- package/dist/cjs/src/queue/EventQueue.d.ts +1 -0
- package/dist/cjs/src/queue/EventQueue.js +11 -6
- package/dist/cjs/src/solana/SolanaManager.d.ts +37 -9
- package/dist/cjs/src/solana/SolanaManager.js +135 -20
- package/dist/cjs/src/solana/SolanaStoreHandler.d.ts +6 -0
- package/dist/cjs/src/solana/SolanaStoreHandler.js +17 -8
- package/dist/cjs/src/solana/SolanaWalletStandardRegistry.d.ts +163 -0
- package/dist/cjs/src/solana/SolanaWalletStandardRegistry.js +447 -0
- package/dist/cjs/src/solana/index.d.ts +10 -5
- package/dist/cjs/src/solana/index.js +12 -6
- package/dist/cjs/src/solana/storeTypes.d.ts +20 -1
- package/dist/cjs/src/solana/types.d.ts +32 -9
- package/dist/cjs/src/solana/types.js +15 -0
- package/dist/cjs/src/solana/walletStandardTypes.d.ts +54 -0
- package/dist/cjs/src/solana/walletStandardTypes.js +21 -0
- package/dist/cjs/src/storage/StorageManager.d.ts +2 -0
- package/dist/cjs/src/storage/StorageManager.js +6 -0
- package/dist/cjs/src/storage/index.d.ts +1 -0
- package/dist/cjs/src/storage/index.js +7 -1
- package/dist/cjs/src/types/base.d.ts +15 -5
- package/dist/cjs/src/version.d.ts +1 -1
- package/dist/cjs/src/version.js +1 -1
- package/dist/esm/src/FormoAnalytics.d.ts +4 -1
- package/dist/esm/src/FormoAnalytics.js +54 -34
- package/dist/esm/src/FormoAnalyticsProvider.js +20 -0
- package/dist/esm/src/event/EventFactory.d.ts +9 -1
- package/dist/esm/src/event/EventFactory.js +31 -16
- package/dist/esm/src/event/EventManager.d.ts +3 -1
- package/dist/esm/src/event/EventManager.js +33 -4
- package/dist/esm/src/event/cancellation.d.ts +4 -0
- package/dist/esm/src/event/cancellation.js +4 -0
- package/dist/esm/src/event/type.d.ts +1 -0
- package/dist/esm/src/event/utils.d.ts +5 -1
- package/dist/esm/src/event/utils.js +56 -3
- package/dist/esm/src/evm/EvmEventTracker.d.ts +7 -0
- package/dist/esm/src/evm/EvmEventTracker.js +31 -5
- package/dist/esm/src/queue/EventQueue.d.ts +1 -0
- package/dist/esm/src/queue/EventQueue.js +11 -6
- package/dist/esm/src/solana/SolanaManager.d.ts +37 -9
- package/dist/esm/src/solana/SolanaManager.js +135 -20
- package/dist/esm/src/solana/SolanaStoreHandler.d.ts +6 -0
- package/dist/esm/src/solana/SolanaStoreHandler.js +18 -9
- package/dist/esm/src/solana/SolanaWalletStandardRegistry.d.ts +163 -0
- package/dist/esm/src/solana/SolanaWalletStandardRegistry.js +444 -0
- package/dist/esm/src/solana/index.d.ts +10 -5
- package/dist/esm/src/solana/index.js +10 -5
- package/dist/esm/src/solana/storeTypes.d.ts +20 -1
- package/dist/esm/src/solana/types.d.ts +32 -9
- package/dist/esm/src/solana/types.js +14 -0
- package/dist/esm/src/solana/walletStandardTypes.d.ts +54 -0
- package/dist/esm/src/solana/walletStandardTypes.js +18 -0
- package/dist/esm/src/storage/StorageManager.d.ts +2 -0
- package/dist/esm/src/storage/StorageManager.js +6 -0
- package/dist/esm/src/storage/index.d.ts +1 -0
- package/dist/esm/src/storage/index.js +5 -0
- package/dist/esm/src/types/base.d.ts +15 -5
- 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 +2 -2
|
@@ -54,6 +54,7 @@ export declare class FormoAnalytics implements IFormoAnalytics {
|
|
|
54
54
|
private _onPopStateListener?;
|
|
55
55
|
private _onLocationChangeListener?;
|
|
56
56
|
private _pageHooksDisposed;
|
|
57
|
+
private _pageGeneration;
|
|
57
58
|
config: Config;
|
|
58
59
|
/**
|
|
59
60
|
* The wallet later events are attributed to, derived from whichever
|
|
@@ -87,7 +88,8 @@ export declare class FormoAnalytics implements IFormoAnalytics {
|
|
|
87
88
|
*/
|
|
88
89
|
page(category?: string, name?: string, properties?: IFormoEventProperties, context?: IFormoEventContext, callback?: (...args: unknown[]) => void): Promise<void>;
|
|
89
90
|
/**
|
|
90
|
-
* Reset the
|
|
91
|
+
* Reset user and wallet state while preserving the browser's anonymous id.
|
|
92
|
+
* Use `optOutTracking()` to clear the anonymous id and the attribution.
|
|
91
93
|
* @returns {void}
|
|
92
94
|
*/
|
|
93
95
|
reset(): void;
|
|
@@ -142,6 +144,7 @@ export declare class FormoAnalytics implements IFormoAnalytics {
|
|
|
142
144
|
chainId?: ChainID;
|
|
143
145
|
address?: Address;
|
|
144
146
|
}): void;
|
|
147
|
+
private retryWalletDetection;
|
|
145
148
|
/** @see WalletStateStore.clearProvider */
|
|
146
149
|
private clearActiveProvider;
|
|
147
150
|
/** @see WalletStateStore.backfill */
|
|
@@ -52,6 +52,7 @@ var constants_1 = require("./constants");
|
|
|
52
52
|
var storage_1 = require("./storage");
|
|
53
53
|
var cookiePolicy_1 = require("./storage/cookiePolicy");
|
|
54
54
|
var event_1 = require("./event");
|
|
55
|
+
var utils_1 = require("./event/utils");
|
|
55
56
|
var queue_1 = require("./queue");
|
|
56
57
|
var logger_1 = require("./logger");
|
|
57
58
|
var consent_1 = require("./consent");
|
|
@@ -68,7 +69,7 @@ var wagmi_1 = require("./wagmi");
|
|
|
68
69
|
var solana_1 = require("./solana");
|
|
69
70
|
var SolanaManager_1 = require("./solana/SolanaManager");
|
|
70
71
|
// Internal: the Privy identify is reached through identify(user), not exported.
|
|
71
|
-
var
|
|
72
|
+
var utils_2 = require("./privy/utils");
|
|
72
73
|
/**
|
|
73
74
|
* Constants for provider switching reasons
|
|
74
75
|
*/
|
|
@@ -93,6 +94,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
93
94
|
/** In-memory URL used to deduplicate SPA pageview events. */
|
|
94
95
|
this._currentUrl = "";
|
|
95
96
|
this._pageHooksDisposed = false;
|
|
97
|
+
this._pageGeneration = 0;
|
|
96
98
|
this.currentUserId = "";
|
|
97
99
|
/**
|
|
98
100
|
* Clean up resources and event listeners
|
|
@@ -190,6 +192,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
190
192
|
isAutocaptureEnabled: function (t) { return _this.isAutocaptureEnabled(t); },
|
|
191
193
|
isTrackingSuppressed: function () { return _this.isTrackingSuppressed(); },
|
|
192
194
|
willTrackEvent: function (chainId) { return _this.willTrackEvent(chainId); },
|
|
195
|
+
retryDetection: function () { return _this.retryWalletDetection(); },
|
|
193
196
|
isWagmiMode: function () { return _this.isWagmiMode; },
|
|
194
197
|
connect: function (params, properties) { return _this.connect(params, properties); },
|
|
195
198
|
disconnect: function (params) { return _this.disconnect(params); },
|
|
@@ -218,7 +221,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
218
221
|
// ever sent once the user has opted out, even via a timer or
|
|
219
222
|
// pagehide flush scheduled before opt-out.
|
|
220
223
|
canSend: function () { return !_this.hasOptedOutTracking(); },
|
|
221
|
-
}), options);
|
|
224
|
+
}), options, function () { return !_this.hasOptedOutTracking(); });
|
|
222
225
|
// Check consent status on initialization
|
|
223
226
|
if (this.hasOptedOutTracking()) {
|
|
224
227
|
logger_1.logger.info("User has previously opted out of tracking");
|
|
@@ -245,9 +248,12 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
245
248
|
this.evmEvents.trackEIP1193Provider(provider);
|
|
246
249
|
}
|
|
247
250
|
}
|
|
248
|
-
//
|
|
249
|
-
|
|
250
|
-
|
|
251
|
+
// Solana wallets are discovered through the Wallet Standard
|
|
252
|
+
// unconditionally, the way EVM wallets are through EIP-6963: an app
|
|
253
|
+
// that never configures Solana still gets its connects. `solana: false`
|
|
254
|
+
// is the opt-out; an object adds framework-kit's store or a cluster.
|
|
255
|
+
if (options.solana !== false) {
|
|
256
|
+
this.solanaManager = new SolanaManager_1.SolanaManager(this, typeof options.solana === "object" ? options.solana : undefined);
|
|
251
257
|
}
|
|
252
258
|
this._currentUrl = window.location.href;
|
|
253
259
|
// Seed currentAddress/currentChainId from the persisted snapshot before
|
|
@@ -317,7 +323,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
317
323
|
});
|
|
318
324
|
FormoAnalytics.init = function (writeKey, options) {
|
|
319
325
|
return __awaiter(this, void 0, void 0, function () {
|
|
320
|
-
var analytics, discovered;
|
|
326
|
+
var analytics, discovered, error_1;
|
|
321
327
|
return __generator(this, function (_a) {
|
|
322
328
|
switch (_a.label) {
|
|
323
329
|
case 0:
|
|
@@ -327,18 +333,21 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
327
333
|
logger_1.logger.info("FormoAnalytics: Skipping provider detection (EVM disabled)");
|
|
328
334
|
return [3 /*break*/, 5];
|
|
329
335
|
case 1:
|
|
330
|
-
|
|
336
|
+
_a.trys.push([1, 4, , 5]);
|
|
331
337
|
return [4 /*yield*/, analytics.evmEvents.getProviders()];
|
|
332
338
|
case 2:
|
|
333
339
|
discovered = _a.sent();
|
|
334
340
|
return [4 /*yield*/, analytics.evmEvents.detectWallets(discovered)];
|
|
335
341
|
case 3:
|
|
336
342
|
_a.sent();
|
|
337
|
-
analytics.
|
|
343
|
+
if (!analytics.isWagmiMode) {
|
|
344
|
+
analytics.evmEvents.trackProviders(discovered);
|
|
345
|
+
}
|
|
338
346
|
return [3 /*break*/, 5];
|
|
339
347
|
case 4:
|
|
340
|
-
|
|
341
|
-
|
|
348
|
+
error_1 = _a.sent();
|
|
349
|
+
logger_1.logger.warn("FormoAnalytics: Provider discovery failed", error_1);
|
|
350
|
+
return [3 /*break*/, 5];
|
|
342
351
|
case 5: return [2 /*return*/, analytics];
|
|
343
352
|
}
|
|
344
353
|
});
|
|
@@ -369,7 +378,8 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
369
378
|
});
|
|
370
379
|
};
|
|
371
380
|
/**
|
|
372
|
-
* Reset the
|
|
381
|
+
* Reset user and wallet state while preserving the browser's anonymous id.
|
|
382
|
+
* Use `optOutTracking()` to clear the anonymous id and the attribution.
|
|
373
383
|
* @returns {void}
|
|
374
384
|
*/
|
|
375
385
|
FormoAnalytics.prototype.reset = function () {
|
|
@@ -380,15 +390,11 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
380
390
|
// page lifetime, because they fall back to currentAddress. Keep the
|
|
381
391
|
// EVM provider reference so tracking can resume on the next connect.
|
|
382
392
|
this.wallet.reset();
|
|
383
|
-
(0, storage_1.cookie)().remove(constants_1.LOCAL_ANONYMOUS_ID_KEY);
|
|
384
393
|
(0, storage_1.cookie)().remove(constants_1.SESSION_USER_ID_KEY);
|
|
385
394
|
(0, storage_1.cookie)().remove(session_1.SESSION_WALLET_DETECTED_KEY);
|
|
386
395
|
(0, storage_1.cookie)().remove(session_1.SESSION_WALLET_IDENTIFIED_KEY);
|
|
387
396
|
(0, storage_1.cookie)().remove(constants_1.ACTIVE_WALLET_KEY);
|
|
388
|
-
//
|
|
389
|
-
// clear it too so reset()/optOutTracking() don't leave it to be
|
|
390
|
-
// re-attached to the next session's events.
|
|
391
|
-
(0, storage_1.session)().remove(constants_1.SESSION_TRAFFIC_SOURCE_KEY);
|
|
397
|
+
// Attribution belongs to the visit, so reset preserves it.
|
|
392
398
|
};
|
|
393
399
|
FormoAnalytics.prototype.cleanup = function () {
|
|
394
400
|
this.isCleanedUp = true;
|
|
@@ -572,6 +578,12 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
572
578
|
*/
|
|
573
579
|
FormoAnalytics.prototype.syncWalletState = function (params) {
|
|
574
580
|
this.wallet.syncWalletState(params);
|
|
581
|
+
this.retryWalletDetection();
|
|
582
|
+
};
|
|
583
|
+
FormoAnalytics.prototype.retryWalletDetection = function () {
|
|
584
|
+
if (this.isCleanedUp)
|
|
585
|
+
return;
|
|
586
|
+
void this.evmEvents.detectWallets(this.evmEvents.detectableProviders());
|
|
575
587
|
};
|
|
576
588
|
/** @see WalletStateStore.clearProvider */
|
|
577
589
|
FormoAnalytics.prototype.clearActiveProvider = function () {
|
|
@@ -739,7 +751,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
739
751
|
// namespace before emitting. It reads this.currentAddress itself to
|
|
740
752
|
// preserve an already-connected wallet, so this dispatch is a thin
|
|
741
753
|
// pass-through and both entry points behave identically.
|
|
742
|
-
return [4 /*yield*/, (0,
|
|
754
|
+
return [4 /*yield*/, (0, utils_2.identifyPrivyUser)(this, maybeUser, {
|
|
743
755
|
activeAddress: opts.activeAddress,
|
|
744
756
|
properties: opts.properties,
|
|
745
757
|
})];
|
|
@@ -771,6 +783,11 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
771
783
|
return [2 /*return*/];
|
|
772
784
|
}
|
|
773
785
|
if (!!params) return [3 /*break*/, 12];
|
|
786
|
+
// Wagmi owns wallet identification.
|
|
787
|
+
if (this.isWagmiMode) {
|
|
788
|
+
logger_1.logger.info("identify() without params is a no-op in Wagmi mode");
|
|
789
|
+
return [2 /*return*/];
|
|
790
|
+
}
|
|
774
791
|
// If no params provided, auto-identify
|
|
775
792
|
logger_1.logger.info("Auto-identifying with providers:", this.evm.all.map(function (p) { return p.info.name; }));
|
|
776
793
|
_i = 0, _a = this.evm.all;
|
|
@@ -950,10 +967,8 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
950
967
|
return __generator(this, function (_c) {
|
|
951
968
|
switch (_c.label) {
|
|
952
969
|
case 0:
|
|
953
|
-
//
|
|
954
|
-
|
|
955
|
-
// excluded environment (opt-out / timezone / host / path).
|
|
956
|
-
if (this.isTrackingSuppressed()) {
|
|
970
|
+
// Apply all policy checks before persisting the detection marker.
|
|
971
|
+
if (!this.shouldTrack()) {
|
|
957
972
|
logger_1.logger.info("detect() skipped: tracking is suppressed for this visitor or environment");
|
|
958
973
|
return [2 /*return*/];
|
|
959
974
|
}
|
|
@@ -1015,6 +1030,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1015
1030
|
// Set opt-out flag in persistent storage using direct cookie access
|
|
1016
1031
|
// This must be done before switching storage to ensure persistence
|
|
1017
1032
|
(0, consent_1.setConsentFlag)(this.writeKey, constants_1.CONSENT_OPT_OUT_KEY, "true");
|
|
1033
|
+
this._pageGeneration++;
|
|
1018
1034
|
// Drop anything already buffered so a pending timer/pagehide flush
|
|
1019
1035
|
// cannot ship events after consent withdrawal.
|
|
1020
1036
|
this.eventManager.clear();
|
|
@@ -1022,6 +1038,9 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1022
1038
|
// on opt-in, and nothing else would retry an already-adopted one.
|
|
1023
1039
|
this.evmEvents.markRegisteredAdoptionsPending();
|
|
1024
1040
|
this.reset();
|
|
1041
|
+
// Consent withdrawal also clears the browser id and the attribution.
|
|
1042
|
+
(0, utils_1.clearAnonymousId)(constants_1.LOCAL_ANONYMOUS_ID_KEY);
|
|
1043
|
+
(0, storage_1.session)().remove(constants_1.SESSION_TRAFFIC_SOURCE_KEY);
|
|
1025
1044
|
logger_1.logger.info("Successfully opted out of tracking");
|
|
1026
1045
|
};
|
|
1027
1046
|
/**
|
|
@@ -1048,11 +1067,9 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1048
1067
|
logger_1.logger.info("Opting back into tracking");
|
|
1049
1068
|
// Remove opt-out flag
|
|
1050
1069
|
(0, consent_1.removeConsentFlag)(this.writeKey, constants_1.CONSENT_OPT_OUT_KEY);
|
|
1051
|
-
//
|
|
1052
|
-
// an unchanged wagmi connection produces no status or chain update to
|
|
1053
|
-
// retry on. Without this, opting back in leaves that wallet invisible for
|
|
1054
|
-
// the rest of the page load.
|
|
1070
|
+
// Retry wallet adoption skipped while opted out.
|
|
1055
1071
|
(_a = this.wagmiHandler) === null || _a === void 0 ? void 0 : _a.retryAdoption();
|
|
1072
|
+
this.retryWalletDetection();
|
|
1056
1073
|
logger_1.logger.info("Successfully opted back into tracking");
|
|
1057
1074
|
};
|
|
1058
1075
|
/**
|
|
@@ -1133,29 +1150,30 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1133
1150
|
};
|
|
1134
1151
|
FormoAnalytics.prototype.trackPageHit = function (category, name, properties, context, callback) {
|
|
1135
1152
|
return __awaiter(this, void 0, void 0, function () {
|
|
1153
|
+
var canTrack, generation;
|
|
1136
1154
|
var _this = this;
|
|
1137
1155
|
return __generator(this, function (_a) {
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
// Idempotent and cheap when nothing is pending.
|
|
1141
|
-
if (!this.isCleanedUp) {
|
|
1156
|
+
canTrack = this.shouldTrack();
|
|
1157
|
+
if (!this.isCleanedUp && canTrack) {
|
|
1142
1158
|
try {
|
|
1143
1159
|
this.evmEvents.retryExternalAdoptions();
|
|
1160
|
+
this.retryWalletDetection();
|
|
1144
1161
|
}
|
|
1145
1162
|
catch (_b) {
|
|
1146
|
-
|
|
1163
|
+
// Detection retries must not break page tracking.
|
|
1147
1164
|
}
|
|
1148
1165
|
}
|
|
1149
|
-
if (!
|
|
1166
|
+
if (!canTrack) {
|
|
1150
1167
|
logger_1.logger.info("Track page hit: Skipping event due to tracking configuration");
|
|
1151
1168
|
return [2 /*return*/];
|
|
1152
1169
|
}
|
|
1170
|
+
generation = this._pageGeneration;
|
|
1153
1171
|
setTimeout(function () {
|
|
1154
1172
|
// Drop in-flight page hits from an SDK instance that was torn down
|
|
1155
1173
|
// between scheduling and firing (e.g. provider remount in React Strict
|
|
1156
1174
|
// Mode / HMR). Otherwise the orphan instance would queue a page event
|
|
1157
1175
|
// here with its stale, never-populated `currentAddress`.
|
|
1158
|
-
if (_this._pageHooksDisposed)
|
|
1176
|
+
if (_this._pageHooksDisposed || generation !== _this._pageGeneration)
|
|
1159
1177
|
return;
|
|
1160
1178
|
(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
1161
1179
|
var e_2;
|
|
@@ -1185,7 +1203,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1185
1203
|
};
|
|
1186
1204
|
FormoAnalytics.prototype.trackEvent = function (type, payload, properties, context, callback) {
|
|
1187
1205
|
return __awaiter(this, void 0, void 0, function () {
|
|
1188
|
-
var
|
|
1206
|
+
var error_2;
|
|
1189
1207
|
return __generator(this, function (_a) {
|
|
1190
1208
|
switch (_a.label) {
|
|
1191
1209
|
case 0:
|
|
@@ -1204,8 +1222,8 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1204
1222
|
_a.sent();
|
|
1205
1223
|
return [3 /*break*/, 3];
|
|
1206
1224
|
case 2:
|
|
1207
|
-
|
|
1208
|
-
logger_1.logger.error("Error tracking event:",
|
|
1225
|
+
error_2 = _a.sent();
|
|
1226
|
+
logger_1.logger.error("Error tracking event:", error_2);
|
|
1209
1227
|
return [3 /*break*/, 3];
|
|
1210
1228
|
case 3: return [2 /*return*/];
|
|
1211
1229
|
}
|
|
@@ -1271,7 +1289,9 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1271
1289
|
*/
|
|
1272
1290
|
get: function () {
|
|
1273
1291
|
if (!this.solanaManager) {
|
|
1274
|
-
|
|
1292
|
+
// Only reachable after `solana: false` (or after cleanup). The host
|
|
1293
|
+
// opted out of discovery, so this manager serves the store path only.
|
|
1294
|
+
this.solanaManager = new SolanaManager_1.SolanaManager(this, undefined, false);
|
|
1275
1295
|
}
|
|
1276
1296
|
return this.solanaManager;
|
|
1277
1297
|
},
|
|
@@ -72,6 +72,18 @@ var defaultContext = {
|
|
|
72
72
|
hasOptedOutTracking: function () { return false; },
|
|
73
73
|
};
|
|
74
74
|
exports.FormoAnalyticsContext = (0, react_1.createContext)(defaultContext);
|
|
75
|
+
var optionObjectIds = new WeakMap();
|
|
76
|
+
var nextOptionObjectId = 1;
|
|
77
|
+
var optionObjectId = function (value) {
|
|
78
|
+
if (!value)
|
|
79
|
+
return undefined;
|
|
80
|
+
var id = optionObjectIds.get(value);
|
|
81
|
+
if (id === undefined) {
|
|
82
|
+
id = nextOptionObjectId++;
|
|
83
|
+
optionObjectIds.set(value, id);
|
|
84
|
+
}
|
|
85
|
+
return id;
|
|
86
|
+
};
|
|
75
87
|
/**
|
|
76
88
|
* A stable key over the serializable parts of Options. The provider effect
|
|
77
89
|
* re-initialises the SDK when this key changes; anything that alters SDK
|
|
@@ -95,6 +107,14 @@ var computeOptionsKey = function (options) {
|
|
|
95
107
|
logger: options.logger,
|
|
96
108
|
referral: options.referral,
|
|
97
109
|
evm: options.evm,
|
|
110
|
+
// `solana` is a boolean or an options object. A store is a live event
|
|
111
|
+
// source, so replacing it must reinitialize the SDK even when both the
|
|
112
|
+
// old and new options contain a store.
|
|
113
|
+
solana: typeof options.solana === "object" ? undefined : options.solana,
|
|
114
|
+
solanaStoreId: typeof options.solana === "object"
|
|
115
|
+
? optionObjectId(options.solana.store)
|
|
116
|
+
: undefined,
|
|
117
|
+
solanaCluster: typeof options.solana === "object" ? options.solana.cluster : undefined,
|
|
98
118
|
// For complex objects, just track their presence, not their content
|
|
99
119
|
hasProvider: !!options.provider,
|
|
100
120
|
hasWagmi: !!options.wagmi,
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { Address, APIEvent, ChainID, IFormoEvent, IFormoEventContext, IFormoEventProperties, Nullable, Options, SignatureStatus, TransactionStatus } from "../types";
|
|
2
2
|
import { IEventFactory } from "./type";
|
|
3
3
|
declare class EventFactory implements IEventFactory {
|
|
4
|
+
/** Consulted once per event, after the only await in creation. */
|
|
5
|
+
private readonly canCreate;
|
|
4
6
|
private options?;
|
|
5
7
|
private compiledPathPattern?;
|
|
6
8
|
private excludedQueryParams;
|
|
7
|
-
|
|
9
|
+
/** Bumped by invalidate(); an event created across a bump is dropped. */
|
|
10
|
+
private generation;
|
|
11
|
+
constructor(options?: Options,
|
|
12
|
+
/** Consulted once per event, after the only await in creation. */
|
|
13
|
+
canCreate?: () => boolean);
|
|
8
14
|
/**
|
|
9
15
|
* Validate an address for both EVM and Solana chains.
|
|
10
16
|
* Uses chainId for strict validation when available.
|
|
@@ -56,6 +62,8 @@ declare class EventFactory implements IEventFactory {
|
|
|
56
62
|
private redactStoredTrafficSources;
|
|
57
63
|
private getTrafficSources;
|
|
58
64
|
private getScreen;
|
|
65
|
+
/** Drop every event still being created. Called on clear() and close(). */
|
|
66
|
+
invalidate(): void;
|
|
59
67
|
private generateContext;
|
|
60
68
|
/**
|
|
61
69
|
* Add any missing default page properties using values from options and defaults
|
|
@@ -73,11 +73,18 @@ var constants_2 = require("./constants");
|
|
|
73
73
|
var sanitize_1 = require("./sanitize");
|
|
74
74
|
var utils_2 = require("./utils");
|
|
75
75
|
var browsers_1 = require("../browser/browsers");
|
|
76
|
+
var cancellation_1 = require("./cancellation");
|
|
76
77
|
var ISO_3166_ALPHA_2_REGEX = /^[A-Z]{2}$/;
|
|
77
78
|
var EventFactory = /** @class */ (function () {
|
|
78
|
-
function EventFactory(options
|
|
79
|
+
function EventFactory(options,
|
|
80
|
+
/** Consulted once per event, after the only await in creation. */
|
|
81
|
+
canCreate) {
|
|
82
|
+
if (canCreate === void 0) { canCreate = function () { return true; }; }
|
|
79
83
|
var _this = this;
|
|
80
84
|
var _a, _b;
|
|
85
|
+
this.canCreate = canCreate;
|
|
86
|
+
/** Bumped by invalidate(); an event created across a bump is dropped. */
|
|
87
|
+
this.generation = 0;
|
|
81
88
|
this.extractUTMParameters = function (url) {
|
|
82
89
|
var result = {
|
|
83
90
|
utm_campaign: "",
|
|
@@ -410,6 +417,10 @@ var EventFactory = /** @class */ (function () {
|
|
|
410
417
|
return safeDefaults;
|
|
411
418
|
}
|
|
412
419
|
};
|
|
420
|
+
/** Drop every event still being created. Called on clear() and close(). */
|
|
421
|
+
EventFactory.prototype.invalidate = function () {
|
|
422
|
+
this.generation++;
|
|
423
|
+
};
|
|
413
424
|
// Contextual fields that are automatically collected and populated by the Formo SDK
|
|
414
425
|
EventFactory.prototype.generateContext = function (context) {
|
|
415
426
|
return __awaiter(this, void 0, void 0, function () {
|
|
@@ -433,24 +444,28 @@ var EventFactory = /** @class */ (function () {
|
|
|
433
444
|
};
|
|
434
445
|
EventFactory.prototype.getEnrichedEvent = function (formoEvent, context) {
|
|
435
446
|
return __awaiter(this, void 0, void 0, function () {
|
|
436
|
-
var commonEventData, eventChainId, validAddress, processedEvent;
|
|
437
|
-
var _a;
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
switch (_d.label) {
|
|
447
|
+
var generation, enrichedContext, commonEventData, eventChainId, validAddress, processedEvent;
|
|
448
|
+
var _a, _b;
|
|
449
|
+
return __generator(this, function (_c) {
|
|
450
|
+
switch (_c.label) {
|
|
441
451
|
case 0:
|
|
442
|
-
|
|
452
|
+
generation = this.generation;
|
|
443
453
|
return [4 /*yield*/, this.generateContext(context)];
|
|
444
454
|
case 1:
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
455
|
+
enrichedContext = _c.sent();
|
|
456
|
+
if (generation !== this.generation || !this.canCreate()) {
|
|
457
|
+
throw cancellation_1.EVENT_CREATION_CANCELLED;
|
|
458
|
+
}
|
|
459
|
+
commonEventData = {
|
|
460
|
+
context: enrichedContext,
|
|
461
|
+
original_timestamp: (0, timestamp_1.getCurrentTimeFormatted)(),
|
|
462
|
+
user_id: formoEvent.user_id,
|
|
463
|
+
type: formoEvent.type,
|
|
464
|
+
channel: constants_2.CHANNEL,
|
|
465
|
+
version: constants_2.VERSION,
|
|
466
|
+
};
|
|
467
|
+
commonEventData.anonymous_id = (0, utils_2.generateAnonymousId)(constants_1.LOCAL_ANONYMOUS_ID_KEY, (_a = this.options) === null || _a === void 0 ? void 0 : _a.crossSubdomainCookies);
|
|
468
|
+
eventChainId = (_b = formoEvent.properties) === null || _b === void 0 ? void 0 : _b.chainId;
|
|
454
469
|
validAddress = this.validateEventAddress(formoEvent.address, eventChainId);
|
|
455
470
|
commonEventData.address = validAddress;
|
|
456
471
|
processedEvent = (0, mergeDeepRight_1.default)(formoEvent, commonEventData);
|
|
@@ -5,14 +5,16 @@ import { IEventFactory, IEventManager } from "./type";
|
|
|
5
5
|
* A service to generate valid event payloads and queue them for processing
|
|
6
6
|
*/
|
|
7
7
|
declare class EventManager implements IEventManager {
|
|
8
|
+
private readonly canAcceptEvent;
|
|
8
9
|
eventQueue: IEventQueue;
|
|
9
10
|
eventFactory: IEventFactory;
|
|
11
|
+
private generation;
|
|
10
12
|
/**
|
|
11
13
|
*
|
|
12
14
|
* @param eventQueue Event queue instance
|
|
13
15
|
* @param options Optional configuration (referral parsing, etc.)
|
|
14
16
|
*/
|
|
15
|
-
constructor(eventQueue: IEventQueue, options?: Options);
|
|
17
|
+
constructor(eventQueue: IEventQueue, options?: Options, canAcceptEvent?: () => boolean);
|
|
16
18
|
/**
|
|
17
19
|
* Consumes a new incoming event
|
|
18
20
|
* @param event Incoming event data
|
|
@@ -50,6 +50,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
50
50
|
exports.EventManager = void 0;
|
|
51
51
|
var logger_1 = require("../logger");
|
|
52
52
|
var EventFactory_1 = require("./EventFactory");
|
|
53
|
+
var cancellation_1 = require("./cancellation");
|
|
53
54
|
var address_1 = require("../utils/address");
|
|
54
55
|
/**
|
|
55
56
|
* A service to generate valid event payloads and queue them for processing
|
|
@@ -60,9 +61,13 @@ var EventManager = /** @class */ (function () {
|
|
|
60
61
|
* @param eventQueue Event queue instance
|
|
61
62
|
* @param options Optional configuration (referral parsing, etc.)
|
|
62
63
|
*/
|
|
63
|
-
function EventManager(eventQueue, options) {
|
|
64
|
+
function EventManager(eventQueue, options, canAcceptEvent) {
|
|
65
|
+
if (canAcceptEvent === void 0) { canAcceptEvent = function () { return true; }; }
|
|
66
|
+
var _this = this;
|
|
67
|
+
this.canAcceptEvent = canAcceptEvent;
|
|
68
|
+
this.generation = 0;
|
|
64
69
|
this.eventQueue = eventQueue;
|
|
65
|
-
this.eventFactory = new EventFactory_1.EventFactory(options);
|
|
70
|
+
this.eventFactory = new EventFactory_1.EventFactory(options, function () { return _this.canAcceptEvent(); });
|
|
66
71
|
}
|
|
67
72
|
/**
|
|
68
73
|
* Consumes a new incoming event
|
|
@@ -70,14 +75,34 @@ var EventManager = /** @class */ (function () {
|
|
|
70
75
|
*/
|
|
71
76
|
EventManager.prototype.addEvent = function (event, address, userId) {
|
|
72
77
|
return __awaiter(this, void 0, void 0, function () {
|
|
73
|
-
var callback, _event, formoEvent;
|
|
78
|
+
var callback, _event, generation, shouldContinue, formoEvent, error_1;
|
|
79
|
+
var _this = this;
|
|
74
80
|
return __generator(this, function (_a) {
|
|
75
81
|
switch (_a.label) {
|
|
76
82
|
case 0:
|
|
77
83
|
callback = event.callback, _event = __rest(event, ["callback"]);
|
|
78
|
-
|
|
84
|
+
generation = this.generation;
|
|
85
|
+
shouldContinue = function () {
|
|
86
|
+
return generation === _this.generation && _this.canAcceptEvent();
|
|
87
|
+
};
|
|
88
|
+
if (!shouldContinue())
|
|
89
|
+
return [2 /*return*/];
|
|
90
|
+
_a.label = 1;
|
|
79
91
|
case 1:
|
|
92
|
+
_a.trys.push([1, 3, , 4]);
|
|
93
|
+
return [4 /*yield*/, this.eventFactory.create(_event, address, userId)];
|
|
94
|
+
case 2:
|
|
80
95
|
formoEvent = _a.sent();
|
|
96
|
+
return [3 /*break*/, 4];
|
|
97
|
+
case 3:
|
|
98
|
+
error_1 = _a.sent();
|
|
99
|
+
if (error_1 === cancellation_1.EVENT_CREATION_CANCELLED)
|
|
100
|
+
return [2 /*return*/];
|
|
101
|
+
throw error_1;
|
|
102
|
+
case 4:
|
|
103
|
+
// Reject work invalidated while enrichment was pending.
|
|
104
|
+
if (!shouldContinue())
|
|
105
|
+
return [2 /*return*/];
|
|
81
106
|
// Check if the final event has a blocked address - don't queue it
|
|
82
107
|
if (formoEvent.address && (0, address_1.isBlockedAddress)(formoEvent.address)) {
|
|
83
108
|
logger_1.logger.warn("Event blocked: Address ".concat(formoEvent.address, " is in the blocked list and cannot emit events"));
|
|
@@ -98,10 +123,14 @@ var EventManager = /** @class */ (function () {
|
|
|
98
123
|
};
|
|
99
124
|
/** Drop any buffered events (consent withdrawal). Recoverable. */
|
|
100
125
|
EventManager.prototype.clear = function () {
|
|
126
|
+
this.generation++;
|
|
127
|
+
this.eventFactory.invalidate();
|
|
101
128
|
this.eventQueue.clear();
|
|
102
129
|
};
|
|
103
130
|
/** Terminal shutdown on teardown: nothing can be sent after this. */
|
|
104
131
|
EventManager.prototype.close = function () {
|
|
132
|
+
this.generation++;
|
|
133
|
+
this.eventFactory.invalidate();
|
|
105
134
|
this.eventQueue.close();
|
|
106
135
|
};
|
|
107
136
|
return EventManager;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EVENT_CREATION_CANCELLED = void 0;
|
|
4
|
+
/** Internal control flow: an event invalidated by consent withdrawal. */
|
|
5
|
+
var EVENT_CREATION_CANCELLED = Symbol();
|
|
6
|
+
exports.EVENT_CREATION_CANCELLED = EVENT_CREATION_CANCELLED;
|
|
7
|
+
//# sourceMappingURL=cancellation.js.map
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { AnonymousID } from "../types";
|
|
2
2
|
declare const generateAnonymousId: (key: string, crossSubdomainCookies?: boolean) => AnonymousID;
|
|
3
|
-
|
|
3
|
+
/** Clear the anonymous id from every storage layer. */
|
|
4
|
+
declare const clearAnonymousId: (key: string) => void;
|
|
5
|
+
/** Test hook: forget the page-lifetime memory, as a new page load would. */
|
|
6
|
+
declare const __resetAnonymousIdMemory: () => void;
|
|
7
|
+
export { generateAnonymousId, clearAnonymousId, __resetAnonymousIdMemory };
|
|
4
8
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -11,15 +11,28 @@ var __assign = (this && this.__assign) || function () {
|
|
|
11
11
|
return __assign.apply(this, arguments);
|
|
12
12
|
};
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
-
exports.generateAnonymousId = void 0;
|
|
14
|
+
exports.__resetAnonymousIdMemory = exports.clearAnonymousId = exports.generateAnonymousId = void 0;
|
|
15
15
|
var utils_1 = require("../utils");
|
|
16
16
|
var storage_1 = require("../storage");
|
|
17
17
|
var cookiePolicy_1 = require("../storage/cookiePolicy");
|
|
18
|
+
/** Page-lifetime fallback when persistent storage is unavailable. */
|
|
19
|
+
var volatileAnonymousId;
|
|
20
|
+
/** Persistent fallback when cookies are unavailable. */
|
|
21
|
+
var readLocal = function (key) {
|
|
22
|
+
try {
|
|
23
|
+
var value = (0, storage_1.local)().get(key);
|
|
24
|
+
return typeof value === "string" && value ? value : undefined;
|
|
25
|
+
}
|
|
26
|
+
catch (_a) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
18
30
|
var generateAnonymousId = function (key, crossSubdomainCookies) {
|
|
31
|
+
var _a, _b;
|
|
19
32
|
var storedAnonymousId = (0, storage_1.cookie)().get(key);
|
|
20
33
|
var anonymousId = (storedAnonymousId && typeof storedAnonymousId === "string"
|
|
21
34
|
? storedAnonymousId
|
|
22
|
-
: (0, utils_1.generateNativeUUID)());
|
|
35
|
+
: (_b = (_a = readLocal(key)) !== null && _a !== void 0 ? _a : volatileAnonymousId) !== null && _b !== void 0 ? _b : (0, utils_1.generateNativeUUID)());
|
|
23
36
|
var domain = (0, cookiePolicy_1.getIdentityCookieDomain)(crossSubdomainCookies);
|
|
24
37
|
// Re-set the cookie with the configured scope. When crossSubdomainCookies
|
|
25
38
|
// is true, this migrates legacy host-only cookies on the current host to the apex
|
|
@@ -27,7 +40,49 @@ var generateAnonymousId = function (key, crossSubdomainCookies) {
|
|
|
27
40
|
// example.com is not visible from app.example.com) cannot be migrated
|
|
28
41
|
// until the user revisits that host.
|
|
29
42
|
(0, storage_1.cookie)().set(key, anonymousId, __assign(__assign({ expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365).toUTCString(), path: "/" }, (0, cookiePolicy_1.getIdentityCookieSecurity)()), (domain ? { domain: domain } : {})));
|
|
43
|
+
// Retry host-only when a domain-scoped write is rejected.
|
|
44
|
+
if (domain && (0, storage_1.cookie)().get(key) !== anonymousId) {
|
|
45
|
+
(0, storage_1.cookie)().set(key, anonymousId, __assign({ expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365).toUTCString(), path: "/" }, (0, cookiePolicy_1.getIdentityCookieSecurity)()));
|
|
46
|
+
}
|
|
47
|
+
if ((0, storage_1.cookie)().get(key) === anonymousId) {
|
|
48
|
+
// Prefer the cookie and discard the fallback copy.
|
|
49
|
+
volatileAnonymousId = undefined;
|
|
50
|
+
if ((0, storage_1.usesCookieStorage)() && readLocal(key)) {
|
|
51
|
+
try {
|
|
52
|
+
(0, storage_1.local)().remove(key);
|
|
53
|
+
}
|
|
54
|
+
catch (_c) {
|
|
55
|
+
// Storage is unavailable.
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return anonymousId;
|
|
59
|
+
}
|
|
60
|
+
// Persist across embedded page loads when cookies are blocked.
|
|
61
|
+
try {
|
|
62
|
+
(0, storage_1.local)().set(key, anonymousId);
|
|
63
|
+
}
|
|
64
|
+
catch (_d) {
|
|
65
|
+
// Memory remains the last fallback.
|
|
66
|
+
}
|
|
67
|
+
volatileAnonymousId = anonymousId;
|
|
30
68
|
return anonymousId;
|
|
31
69
|
};
|
|
32
70
|
exports.generateAnonymousId = generateAnonymousId;
|
|
71
|
+
/** Clear the anonymous id from every storage layer. */
|
|
72
|
+
var clearAnonymousId = function (key) {
|
|
73
|
+
(0, storage_1.cookie)().remove(key);
|
|
74
|
+
try {
|
|
75
|
+
(0, storage_1.local)().remove(key);
|
|
76
|
+
}
|
|
77
|
+
catch (_a) {
|
|
78
|
+
// Storage is unavailable.
|
|
79
|
+
}
|
|
80
|
+
volatileAnonymousId = undefined;
|
|
81
|
+
};
|
|
82
|
+
exports.clearAnonymousId = clearAnonymousId;
|
|
83
|
+
/** Test hook: forget the page-lifetime memory, as a new page load would. */
|
|
84
|
+
var __resetAnonymousIdMemory = function () {
|
|
85
|
+
volatileAnonymousId = undefined;
|
|
86
|
+
};
|
|
87
|
+
exports.__resetAnonymousIdMemory = __resetAnonymousIdMemory;
|
|
33
88
|
//# sourceMappingURL=utils.js.map
|