@formo/analytics 1.39.0 → 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 +47 -32
- 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/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/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 +45 -30
- 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/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/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");
|
|
@@ -320,7 +323,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
320
323
|
});
|
|
321
324
|
FormoAnalytics.init = function (writeKey, options) {
|
|
322
325
|
return __awaiter(this, void 0, void 0, function () {
|
|
323
|
-
var analytics, discovered;
|
|
326
|
+
var analytics, discovered, error_1;
|
|
324
327
|
return __generator(this, function (_a) {
|
|
325
328
|
switch (_a.label) {
|
|
326
329
|
case 0:
|
|
@@ -330,18 +333,21 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
330
333
|
logger_1.logger.info("FormoAnalytics: Skipping provider detection (EVM disabled)");
|
|
331
334
|
return [3 /*break*/, 5];
|
|
332
335
|
case 1:
|
|
333
|
-
|
|
336
|
+
_a.trys.push([1, 4, , 5]);
|
|
334
337
|
return [4 /*yield*/, analytics.evmEvents.getProviders()];
|
|
335
338
|
case 2:
|
|
336
339
|
discovered = _a.sent();
|
|
337
340
|
return [4 /*yield*/, analytics.evmEvents.detectWallets(discovered)];
|
|
338
341
|
case 3:
|
|
339
342
|
_a.sent();
|
|
340
|
-
analytics.
|
|
343
|
+
if (!analytics.isWagmiMode) {
|
|
344
|
+
analytics.evmEvents.trackProviders(discovered);
|
|
345
|
+
}
|
|
341
346
|
return [3 /*break*/, 5];
|
|
342
347
|
case 4:
|
|
343
|
-
|
|
344
|
-
|
|
348
|
+
error_1 = _a.sent();
|
|
349
|
+
logger_1.logger.warn("FormoAnalytics: Provider discovery failed", error_1);
|
|
350
|
+
return [3 /*break*/, 5];
|
|
345
351
|
case 5: return [2 /*return*/, analytics];
|
|
346
352
|
}
|
|
347
353
|
});
|
|
@@ -372,7 +378,8 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
372
378
|
});
|
|
373
379
|
};
|
|
374
380
|
/**
|
|
375
|
-
* 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.
|
|
376
383
|
* @returns {void}
|
|
377
384
|
*/
|
|
378
385
|
FormoAnalytics.prototype.reset = function () {
|
|
@@ -383,15 +390,11 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
383
390
|
// page lifetime, because they fall back to currentAddress. Keep the
|
|
384
391
|
// EVM provider reference so tracking can resume on the next connect.
|
|
385
392
|
this.wallet.reset();
|
|
386
|
-
(0, storage_1.cookie)().remove(constants_1.LOCAL_ANONYMOUS_ID_KEY);
|
|
387
393
|
(0, storage_1.cookie)().remove(constants_1.SESSION_USER_ID_KEY);
|
|
388
394
|
(0, storage_1.cookie)().remove(session_1.SESSION_WALLET_DETECTED_KEY);
|
|
389
395
|
(0, storage_1.cookie)().remove(session_1.SESSION_WALLET_IDENTIFIED_KEY);
|
|
390
396
|
(0, storage_1.cookie)().remove(constants_1.ACTIVE_WALLET_KEY);
|
|
391
|
-
//
|
|
392
|
-
// clear it too so reset()/optOutTracking() don't leave it to be
|
|
393
|
-
// re-attached to the next session's events.
|
|
394
|
-
(0, storage_1.session)().remove(constants_1.SESSION_TRAFFIC_SOURCE_KEY);
|
|
397
|
+
// Attribution belongs to the visit, so reset preserves it.
|
|
395
398
|
};
|
|
396
399
|
FormoAnalytics.prototype.cleanup = function () {
|
|
397
400
|
this.isCleanedUp = true;
|
|
@@ -575,6 +578,12 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
575
578
|
*/
|
|
576
579
|
FormoAnalytics.prototype.syncWalletState = function (params) {
|
|
577
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());
|
|
578
587
|
};
|
|
579
588
|
/** @see WalletStateStore.clearProvider */
|
|
580
589
|
FormoAnalytics.prototype.clearActiveProvider = function () {
|
|
@@ -742,7 +751,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
742
751
|
// namespace before emitting. It reads this.currentAddress itself to
|
|
743
752
|
// preserve an already-connected wallet, so this dispatch is a thin
|
|
744
753
|
// pass-through and both entry points behave identically.
|
|
745
|
-
return [4 /*yield*/, (0,
|
|
754
|
+
return [4 /*yield*/, (0, utils_2.identifyPrivyUser)(this, maybeUser, {
|
|
746
755
|
activeAddress: opts.activeAddress,
|
|
747
756
|
properties: opts.properties,
|
|
748
757
|
})];
|
|
@@ -774,6 +783,11 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
774
783
|
return [2 /*return*/];
|
|
775
784
|
}
|
|
776
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
|
+
}
|
|
777
791
|
// If no params provided, auto-identify
|
|
778
792
|
logger_1.logger.info("Auto-identifying with providers:", this.evm.all.map(function (p) { return p.info.name; }));
|
|
779
793
|
_i = 0, _a = this.evm.all;
|
|
@@ -953,10 +967,8 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
953
967
|
return __generator(this, function (_c) {
|
|
954
968
|
switch (_c.label) {
|
|
955
969
|
case 0:
|
|
956
|
-
//
|
|
957
|
-
|
|
958
|
-
// excluded environment (opt-out / timezone / host / path).
|
|
959
|
-
if (this.isTrackingSuppressed()) {
|
|
970
|
+
// Apply all policy checks before persisting the detection marker.
|
|
971
|
+
if (!this.shouldTrack()) {
|
|
960
972
|
logger_1.logger.info("detect() skipped: tracking is suppressed for this visitor or environment");
|
|
961
973
|
return [2 /*return*/];
|
|
962
974
|
}
|
|
@@ -1018,6 +1030,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1018
1030
|
// Set opt-out flag in persistent storage using direct cookie access
|
|
1019
1031
|
// This must be done before switching storage to ensure persistence
|
|
1020
1032
|
(0, consent_1.setConsentFlag)(this.writeKey, constants_1.CONSENT_OPT_OUT_KEY, "true");
|
|
1033
|
+
this._pageGeneration++;
|
|
1021
1034
|
// Drop anything already buffered so a pending timer/pagehide flush
|
|
1022
1035
|
// cannot ship events after consent withdrawal.
|
|
1023
1036
|
this.eventManager.clear();
|
|
@@ -1025,6 +1038,9 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1025
1038
|
// on opt-in, and nothing else would retry an already-adopted one.
|
|
1026
1039
|
this.evmEvents.markRegisteredAdoptionsPending();
|
|
1027
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);
|
|
1028
1044
|
logger_1.logger.info("Successfully opted out of tracking");
|
|
1029
1045
|
};
|
|
1030
1046
|
/**
|
|
@@ -1051,11 +1067,9 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1051
1067
|
logger_1.logger.info("Opting back into tracking");
|
|
1052
1068
|
// Remove opt-out flag
|
|
1053
1069
|
(0, consent_1.removeConsentFlag)(this.writeKey, constants_1.CONSENT_OPT_OUT_KEY);
|
|
1054
|
-
//
|
|
1055
|
-
// an unchanged wagmi connection produces no status or chain update to
|
|
1056
|
-
// retry on. Without this, opting back in leaves that wallet invisible for
|
|
1057
|
-
// the rest of the page load.
|
|
1070
|
+
// Retry wallet adoption skipped while opted out.
|
|
1058
1071
|
(_a = this.wagmiHandler) === null || _a === void 0 ? void 0 : _a.retryAdoption();
|
|
1072
|
+
this.retryWalletDetection();
|
|
1059
1073
|
logger_1.logger.info("Successfully opted back into tracking");
|
|
1060
1074
|
};
|
|
1061
1075
|
/**
|
|
@@ -1136,29 +1150,30 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1136
1150
|
};
|
|
1137
1151
|
FormoAnalytics.prototype.trackPageHit = function (category, name, properties, context, callback) {
|
|
1138
1152
|
return __awaiter(this, void 0, void 0, function () {
|
|
1153
|
+
var canTrack, generation;
|
|
1139
1154
|
var _this = this;
|
|
1140
1155
|
return __generator(this, function (_a) {
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
// Idempotent and cheap when nothing is pending.
|
|
1144
|
-
if (!this.isCleanedUp) {
|
|
1156
|
+
canTrack = this.shouldTrack();
|
|
1157
|
+
if (!this.isCleanedUp && canTrack) {
|
|
1145
1158
|
try {
|
|
1146
1159
|
this.evmEvents.retryExternalAdoptions();
|
|
1160
|
+
this.retryWalletDetection();
|
|
1147
1161
|
}
|
|
1148
1162
|
catch (_b) {
|
|
1149
|
-
|
|
1163
|
+
// Detection retries must not break page tracking.
|
|
1150
1164
|
}
|
|
1151
1165
|
}
|
|
1152
|
-
if (!
|
|
1166
|
+
if (!canTrack) {
|
|
1153
1167
|
logger_1.logger.info("Track page hit: Skipping event due to tracking configuration");
|
|
1154
1168
|
return [2 /*return*/];
|
|
1155
1169
|
}
|
|
1170
|
+
generation = this._pageGeneration;
|
|
1156
1171
|
setTimeout(function () {
|
|
1157
1172
|
// Drop in-flight page hits from an SDK instance that was torn down
|
|
1158
1173
|
// between scheduling and firing (e.g. provider remount in React Strict
|
|
1159
1174
|
// Mode / HMR). Otherwise the orphan instance would queue a page event
|
|
1160
1175
|
// here with its stale, never-populated `currentAddress`.
|
|
1161
|
-
if (_this._pageHooksDisposed)
|
|
1176
|
+
if (_this._pageHooksDisposed || generation !== _this._pageGeneration)
|
|
1162
1177
|
return;
|
|
1163
1178
|
(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
1164
1179
|
var e_2;
|
|
@@ -1188,7 +1203,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1188
1203
|
};
|
|
1189
1204
|
FormoAnalytics.prototype.trackEvent = function (type, payload, properties, context, callback) {
|
|
1190
1205
|
return __awaiter(this, void 0, void 0, function () {
|
|
1191
|
-
var
|
|
1206
|
+
var error_2;
|
|
1192
1207
|
return __generator(this, function (_a) {
|
|
1193
1208
|
switch (_a.label) {
|
|
1194
1209
|
case 0:
|
|
@@ -1207,8 +1222,8 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
1207
1222
|
_a.sent();
|
|
1208
1223
|
return [3 /*break*/, 3];
|
|
1209
1224
|
case 2:
|
|
1210
|
-
|
|
1211
|
-
logger_1.logger.error("Error tracking event:",
|
|
1225
|
+
error_2 = _a.sent();
|
|
1226
|
+
logger_1.logger.error("Error tracking event:", error_2);
|
|
1212
1227
|
return [3 /*break*/, 3];
|
|
1213
1228
|
case 3: return [2 /*return*/];
|
|
1214
1229
|
}
|
|
@@ -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
|
|
@@ -11,6 +11,8 @@ export interface EvmEventTrackerDeps {
|
|
|
11
11
|
isTrackingSuppressed(): boolean;
|
|
12
12
|
/** Whether an event on this chain would actually be sent. */
|
|
13
13
|
willTrackEvent(chainId?: ChainID): boolean;
|
|
14
|
+
/** Retry detect events after the active chain changes. */
|
|
15
|
+
retryDetection(): void;
|
|
14
16
|
/** In wagmi mode the SDK does not wrap providers itself. */
|
|
15
17
|
isWagmiMode(): boolean;
|
|
16
18
|
/** Emission. The tracker decides WHEN; the SDK owns the event API. */
|
|
@@ -152,6 +154,8 @@ export declare class EvmEventTracker {
|
|
|
152
154
|
* had already replaced.
|
|
153
155
|
*/
|
|
154
156
|
private unsubscribeDiscovery?;
|
|
157
|
+
/** Discovery store retained for complete listener cleanup. */
|
|
158
|
+
private discoveryStore?;
|
|
155
159
|
constructor(wallet: WalletStateStore, registry: EvmProviderRegistry, deps: EvmEventTrackerDeps);
|
|
156
160
|
/** Stop listening for wallet announcements. Called from SDK teardown. */
|
|
157
161
|
cleanup(): void;
|
|
@@ -259,8 +263,11 @@ export declare class EvmEventTracker {
|
|
|
259
263
|
private registerConnectListener;
|
|
260
264
|
private registerDisconnectListener;
|
|
261
265
|
private onConnected;
|
|
266
|
+
/** Whether Formo owns provider lifecycle tracking. */
|
|
267
|
+
private tracksDiscovered;
|
|
262
268
|
getProviders(): Promise<readonly EIP6963ProviderDetail[]>;
|
|
263
269
|
detectWallets(providers: readonly EIP6963ProviderDetail[]): Promise<void>;
|
|
270
|
+
detectableProviders(): readonly EIP6963ProviderDetail[];
|
|
264
271
|
/**
|
|
265
272
|
* Seed a provider's chain from whatever it already exposes synchronously.
|
|
266
273
|
*
|