@formo/analytics 1.28.2 → 1.28.3
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 +2 -0
- package/dist/cjs/src/FormoAnalytics.js +10 -5
- package/dist/cjs/src/FormoAnalyticsProvider.js +1 -0
- package/dist/cjs/src/consent/index.js +12 -10
- package/dist/cjs/src/event/EventFactory.js +6 -6
- package/dist/cjs/src/event/utils.d.ts +2 -3
- package/dist/cjs/src/event/utils.js +24 -24
- package/dist/cjs/src/queue/EventQueue.d.ts +1 -1
- package/dist/cjs/src/queue/EventQueue.js +21 -20
- package/dist/cjs/src/session/index.d.ts +0 -9
- package/dist/cjs/src/session/index.js +7 -0
- package/dist/cjs/src/solana/SolanaAdapter.d.ts +3 -4
- package/dist/cjs/src/solana/SolanaAdapter.js +42 -29
- package/dist/cjs/src/storage/StorageManager.js +11 -3
- package/dist/cjs/src/storage/built-in/cookie.js +29 -3
- package/dist/cjs/src/storage/cookiePolicy.d.ts +10 -0
- package/dist/cjs/src/storage/cookiePolicy.js +27 -0
- package/dist/cjs/src/types/base.d.ts +12 -0
- package/dist/cjs/src/utils/address.js +4 -6
- package/dist/cjs/src/utils/domain.d.ts +27 -0
- package/dist/cjs/src/utils/domain.js +74 -0
- package/dist/cjs/src/utils/generate.js +23 -11
- package/dist/cjs/src/validators/network.js +7 -2
- package/dist/cjs/src/version.d.ts +1 -1
- package/dist/cjs/src/version.js +1 -1
- package/dist/esm/src/FormoAnalytics.d.ts +2 -0
- package/dist/esm/src/FormoAnalytics.js +10 -5
- package/dist/esm/src/FormoAnalyticsProvider.js +1 -0
- package/dist/esm/src/consent/index.js +12 -10
- package/dist/esm/src/event/EventFactory.js +6 -6
- package/dist/esm/src/event/utils.d.ts +2 -3
- package/dist/esm/src/event/utils.js +25 -24
- package/dist/esm/src/queue/EventQueue.d.ts +1 -1
- package/dist/esm/src/queue/EventQueue.js +21 -20
- package/dist/esm/src/session/index.d.ts +0 -9
- package/dist/esm/src/session/index.js +7 -0
- package/dist/esm/src/solana/SolanaAdapter.d.ts +3 -4
- package/dist/esm/src/solana/SolanaAdapter.js +42 -29
- package/dist/esm/src/storage/StorageManager.js +11 -3
- package/dist/esm/src/storage/built-in/cookie.js +29 -3
- package/dist/esm/src/storage/cookiePolicy.d.ts +10 -0
- package/dist/esm/src/storage/cookiePolicy.js +24 -0
- package/dist/esm/src/types/base.d.ts +12 -0
- package/dist/esm/src/utils/address.js +4 -6
- package/dist/esm/src/utils/domain.d.ts +27 -0
- package/dist/esm/src/utils/domain.js +70 -0
- package/dist/esm/src/utils/generate.js +23 -11
- package/dist/esm/src/validators/network.js +7 -2
- 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 +1 -1
|
@@ -45,6 +45,8 @@ export declare class FormoAnalytics implements IFormoAnalytics {
|
|
|
45
45
|
* When true, EIP-1193 provider wrapping is skipped
|
|
46
46
|
*/
|
|
47
47
|
private isWagmiMode;
|
|
48
|
+
/** Instance-level flag so multiple SDK instances don't interfere. */
|
|
49
|
+
private crossSubdomainCookies;
|
|
48
50
|
config: Config;
|
|
49
51
|
currentChainId?: ChainID;
|
|
50
52
|
currentAddress?: Address;
|
|
@@ -60,6 +60,7 @@ exports.FormoAnalytics = void 0;
|
|
|
60
60
|
var mipd_1 = require("mipd");
|
|
61
61
|
var constants_1 = require("./constants");
|
|
62
62
|
var storage_1 = require("./storage");
|
|
63
|
+
var cookiePolicy_1 = require("./storage/cookiePolicy");
|
|
63
64
|
var event_1 = require("./event");
|
|
64
65
|
var queue_1 = require("./queue");
|
|
65
66
|
var logger_1 = require("./logger");
|
|
@@ -84,7 +85,7 @@ var PROVIDER_SWITCH_REASONS = {
|
|
|
84
85
|
var FormoAnalytics = /** @class */ (function () {
|
|
85
86
|
function FormoAnalytics(writeKey, options) {
|
|
86
87
|
if (options === void 0) { options = {}; }
|
|
87
|
-
var _a, _b;
|
|
88
|
+
var _a, _b, _c;
|
|
88
89
|
this.writeKey = writeKey;
|
|
89
90
|
this.options = options;
|
|
90
91
|
// Per-chain namespace state — isolates EVM and Solana connection state
|
|
@@ -123,6 +124,9 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
123
124
|
this.options = options;
|
|
124
125
|
// Check if Wagmi mode is enabled
|
|
125
126
|
this.isWagmiMode = !!options.wagmi;
|
|
127
|
+
this.crossSubdomainCookies = (_a = options.crossSubdomainCookies) !== null && _a !== void 0 ? _a : true;
|
|
128
|
+
// Normalize so downstream consumers (EventFactory) read the resolved value.
|
|
129
|
+
options.crossSubdomainCookies = this.crossSubdomainCookies;
|
|
126
130
|
this.session = new session_1.FormoAnalyticsSession();
|
|
127
131
|
this.currentUserId =
|
|
128
132
|
(0, storage_1.cookie)().get(constants_1.SESSION_USER_ID_KEY) || undefined;
|
|
@@ -137,8 +141,8 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
137
141
|
this.isAutocaptureEnabled = this.isAutocaptureEnabled.bind(this);
|
|
138
142
|
// Initialize logger with configuration from options
|
|
139
143
|
logger_1.Logger.init({
|
|
140
|
-
enabled: ((
|
|
141
|
-
enabledLevels: ((
|
|
144
|
+
enabled: ((_b = options.logger) === null || _b === void 0 ? void 0 : _b.enabled) || false,
|
|
145
|
+
enabledLevels: ((_c = options.logger) === null || _c === void 0 ? void 0 : _c.levels) || [],
|
|
142
146
|
});
|
|
143
147
|
this.eventManager = new event_1.EventManager(new queue_1.EventQueue(this.config.writeKey, {
|
|
144
148
|
apiHost: options.apiHost || constants_1.EVENTS_API_HOST,
|
|
@@ -508,7 +512,7 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
508
512
|
*/
|
|
509
513
|
FormoAnalytics.prototype.identify = function (params, properties, context, callback) {
|
|
510
514
|
return __awaiter(this, void 0, void 0, function () {
|
|
511
|
-
var _i, _a, providerDetail, provider, address_2, validAddress_1, err_1, address, providerName, userId, rdns, validAddress, isAlreadyIdentified, e_1;
|
|
515
|
+
var _i, _a, providerDetail, provider, address_2, validAddress_1, err_1, address, providerName, userId, rdns, validAddress, domain, isAlreadyIdentified, e_1;
|
|
512
516
|
var _b, _c;
|
|
513
517
|
return __generator(this, function (_d) {
|
|
514
518
|
switch (_d.label) {
|
|
@@ -587,7 +591,8 @@ var FormoAnalytics = /** @class */ (function () {
|
|
|
587
591
|
}
|
|
588
592
|
if (userId) {
|
|
589
593
|
this.currentUserId = userId;
|
|
590
|
-
(0,
|
|
594
|
+
domain = (0, cookiePolicy_1.getIdentityCookieDomain)(this.crossSubdomainCookies);
|
|
595
|
+
(0, storage_1.cookie)().set(constants_1.SESSION_USER_ID_KEY, userId, __assign({ path: "/" }, (domain ? { domain: domain } : {})));
|
|
591
596
|
}
|
|
592
597
|
isAlreadyIdentified = this.session.isWalletIdentified(validAddress, rdns || "");
|
|
593
598
|
logger_1.logger.debug("Identify: Checking deduplication", {
|
|
@@ -99,6 +99,7 @@ var InitializedAnalytics = function (_a) {
|
|
|
99
99
|
var serializableOptions = {
|
|
100
100
|
tracking: options.tracking,
|
|
101
101
|
autocapture: options.autocapture,
|
|
102
|
+
crossSubdomainCookies: options.crossSubdomainCookies,
|
|
102
103
|
apiHost: options.apiHost,
|
|
103
104
|
flushAt: options.flushAt,
|
|
104
105
|
flushInterval: options.flushInterval,
|
|
@@ -12,6 +12,7 @@ exports.setConsentFlag = setConsentFlag;
|
|
|
12
12
|
exports.getConsentFlag = getConsentFlag;
|
|
13
13
|
exports.removeConsentFlag = removeConsentFlag;
|
|
14
14
|
var hash_1 = require("../utils/hash");
|
|
15
|
+
var domain_1 = require("../utils/domain");
|
|
15
16
|
/**
|
|
16
17
|
* Generate a project-specific cookie key to avoid conflicts between different Formo projects
|
|
17
18
|
* Uses hashed writeKey for privacy and security
|
|
@@ -41,7 +42,14 @@ function setConsentFlag(projectId, key, value) {
|
|
|
41
42
|
var expires = new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toUTCString(); // 1 year (GDPR compliant)
|
|
42
43
|
var isSecure = ((_a = window === null || window === void 0 ? void 0 : window.location) === null || _a === void 0 ? void 0 : _a.protocol) === 'https:';
|
|
43
44
|
// Enhanced privacy settings: Secure (HTTPS), SameSite=Strict for consent cookies
|
|
44
|
-
|
|
45
|
+
var domain = (0, domain_1.getApexDomain)();
|
|
46
|
+
var domainAttr = domain ? "; domain=.".concat(domain) : '';
|
|
47
|
+
// Expire any legacy host-only cookie (pre-domain-attribute versions) so it
|
|
48
|
+
// doesn't shadow the new domain-wide cookie in document.cookie reads.
|
|
49
|
+
if (domain) {
|
|
50
|
+
document.cookie = "".concat(projectSpecificKey, "=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;");
|
|
51
|
+
}
|
|
52
|
+
document.cookie = "".concat(projectSpecificKey, "=").concat(encodeURIComponent(value), "; expires=").concat(expires, "; path=/").concat(domainAttr, "; SameSite=Strict").concat(isSecure ? '; Secure' : '');
|
|
45
53
|
}
|
|
46
54
|
}
|
|
47
55
|
/**
|
|
@@ -88,15 +96,9 @@ function deleteCookieDirectly(cookieName) {
|
|
|
88
96
|
// Clear from current domain/path
|
|
89
97
|
document.cookie = "".concat(cookieName, "=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;");
|
|
90
98
|
// Try to clear from parent domain if it's a proper multi-level domain
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
// Only try parent domain deletion for proper domains with multiple parts
|
|
95
|
-
// Skip localhost and single-level domains
|
|
96
|
-
if (parts.length >= 2 && hostname !== 'localhost') {
|
|
97
|
-
var domain = parts.slice(-2).join('.');
|
|
98
|
-
document.cookie = "".concat(cookieName, "=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=.").concat(domain, ";");
|
|
99
|
-
}
|
|
99
|
+
var domain = (0, domain_1.getApexDomain)();
|
|
100
|
+
if (domain) {
|
|
101
|
+
document.cookie = "".concat(cookieName, "=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=.").concat(domain, ";");
|
|
100
102
|
}
|
|
101
103
|
}
|
|
102
104
|
//# sourceMappingURL=index.js.map
|
|
@@ -295,22 +295,22 @@ var EventFactory = /** @class */ (function () {
|
|
|
295
295
|
return __awaiter(this, void 0, void 0, function () {
|
|
296
296
|
var commonEventData, eventChainId, validAddress, processedEvent;
|
|
297
297
|
var _a;
|
|
298
|
-
var _b;
|
|
299
|
-
return __generator(this, function (
|
|
300
|
-
switch (
|
|
298
|
+
var _b, _c;
|
|
299
|
+
return __generator(this, function (_d) {
|
|
300
|
+
switch (_d.label) {
|
|
301
301
|
case 0:
|
|
302
302
|
_a = {};
|
|
303
303
|
return [4 /*yield*/, this.generateContext(context)];
|
|
304
304
|
case 1:
|
|
305
|
-
commonEventData = (_a.context =
|
|
305
|
+
commonEventData = (_a.context = _d.sent(),
|
|
306
306
|
_a.original_timestamp = (0, timestamp_1.getCurrentTimeFormatted)(),
|
|
307
307
|
_a.user_id = formoEvent.user_id,
|
|
308
308
|
_a.type = formoEvent.type,
|
|
309
309
|
_a.channel = constants_2.CHANNEL,
|
|
310
310
|
_a.version = constants_2.VERSION,
|
|
311
311
|
_a);
|
|
312
|
-
commonEventData.anonymous_id = (0, utils_2.generateAnonymousId)(constants_1.LOCAL_ANONYMOUS_ID_KEY);
|
|
313
|
-
eventChainId = (
|
|
312
|
+
commonEventData.anonymous_id = (0, utils_2.generateAnonymousId)(constants_1.LOCAL_ANONYMOUS_ID_KEY, (_b = this.options) === null || _b === void 0 ? void 0 : _b.crossSubdomainCookies);
|
|
313
|
+
eventChainId = (_c = formoEvent.properties) === null || _c === void 0 ? void 0 : _c.chainId;
|
|
314
314
|
validAddress = this.validateEventAddress(formoEvent.address, eventChainId);
|
|
315
315
|
commonEventData.address = validAddress;
|
|
316
316
|
processedEvent = (0, mergeDeepRight_1.default)(formoEvent, commonEventData);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { AnonymousID } from "../types";
|
|
2
|
-
declare const generateAnonymousId: (key: string) => AnonymousID;
|
|
3
|
-
|
|
4
|
-
export { generateAnonymousId, getCookieDomain };
|
|
2
|
+
declare const generateAnonymousId: (key: string, crossSubdomainCookies?: boolean) => AnonymousID;
|
|
3
|
+
export { generateAnonymousId };
|
|
5
4
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
2
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
14
|
exports.generateAnonymousId = void 0;
|
|
4
|
-
exports.getCookieDomain = getCookieDomain;
|
|
5
15
|
var utils_1 = require("../utils");
|
|
6
16
|
var storage_1 = require("../storage");
|
|
7
|
-
var
|
|
17
|
+
var cookiePolicy_1 = require("../storage/cookiePolicy");
|
|
18
|
+
var generateAnonymousId = function (key, crossSubdomainCookies) {
|
|
8
19
|
var storedAnonymousId = (0, storage_1.cookie)().get(key);
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
(0,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
var anonymousId = (storedAnonymousId && typeof storedAnonymousId === "string"
|
|
21
|
+
? storedAnonymousId
|
|
22
|
+
: (0, utils_1.generateNativeUUID)());
|
|
23
|
+
var domain = (0, cookiePolicy_1.getIdentityCookieDomain)(crossSubdomainCookies);
|
|
24
|
+
// Re-set the cookie with the configured scope. When crossSubdomainCookies
|
|
25
|
+
// is true, this migrates legacy host-only cookies on the current host to the apex
|
|
26
|
+
// domain. Note: host-only cookies on other hosts (e.g. a cookie set on
|
|
27
|
+
// example.com is not visible from app.example.com) cannot be migrated
|
|
28
|
+
// until the user revisits that host.
|
|
29
|
+
(0, storage_1.cookie)().set(key, anonymousId, __assign({ expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365).toUTCString(), path: "/" }, (domain ? { domain: domain } : {})));
|
|
30
|
+
return anonymousId;
|
|
18
31
|
};
|
|
19
32
|
exports.generateAnonymousId = generateAnonymousId;
|
|
20
|
-
function getCookieDomain(hostname) {
|
|
21
|
-
if (hostname === void 0) { hostname = window.location.hostname; }
|
|
22
|
-
// Special cases
|
|
23
|
-
if (hostname.includes("localhost") ||
|
|
24
|
-
/^\d{1,3}(\.\d{1,3}){3}$/.test(hostname)) {
|
|
25
|
-
// Localhost or IP address
|
|
26
|
-
return "";
|
|
27
|
-
}
|
|
28
|
-
var parts = hostname.split(".");
|
|
29
|
-
if (parts.includes("www"))
|
|
30
|
-
parts.splice(parts.indexOf("www"), 1);
|
|
31
|
-
return ".".concat(parts.join("."));
|
|
32
|
-
}
|
|
33
33
|
//# sourceMappingURL=utils.js.map
|
|
@@ -28,7 +28,7 @@ export declare class EventQueue implements IEventQueue {
|
|
|
28
28
|
constructor(writeKey: string, options: Options);
|
|
29
29
|
private generateMessageId;
|
|
30
30
|
enqueue(event: IFormoEvent, callback?: (...args: any) => void): Promise<void>;
|
|
31
|
-
flush(callback?: (...args: any) => void): Promise<void | IFormoEventFlushPayload[]>;
|
|
31
|
+
flush(callback?: (...args: any) => void, drainAll?: boolean): Promise<void | IFormoEventFlushPayload[]>;
|
|
32
32
|
/**
|
|
33
33
|
* Returns the UTF-8 byte length of a string. The browser's keepalive limit
|
|
34
34
|
* is enforced on the wire (UTF-8 bytes), not on JS string length (UTF-16
|
|
@@ -124,12 +124,12 @@ var EventQueue = /** @class */ (function () {
|
|
|
124
124
|
});
|
|
125
125
|
// Catches the page being hidden, including scenarios like closing the tab.
|
|
126
126
|
document.addEventListener("pagehide", function () {
|
|
127
|
-
isAccessible = document.visibilityState
|
|
127
|
+
isAccessible = document.visibilityState !== "hidden";
|
|
128
128
|
handleOnLeave();
|
|
129
129
|
});
|
|
130
130
|
// Catches visibility changes, such as switching tabs or minimizing the browser.
|
|
131
131
|
document.addEventListener("visibilitychange", function () {
|
|
132
|
-
isAccessible =
|
|
132
|
+
isAccessible = document.visibilityState !== "hidden";
|
|
133
133
|
if (document.visibilityState === "hidden") {
|
|
134
134
|
handleOnLeave();
|
|
135
135
|
}
|
|
@@ -155,7 +155,7 @@ var EventQueue = /** @class */ (function () {
|
|
|
155
155
|
switch (_a.label) {
|
|
156
156
|
case 0:
|
|
157
157
|
if (!(isAccessible === false)) return [3 /*break*/, 2];
|
|
158
|
-
return [4 /*yield*/, this.flush()];
|
|
158
|
+
return [4 /*yield*/, this.flush(undefined, true)];
|
|
159
159
|
case 1:
|
|
160
160
|
_a.sent();
|
|
161
161
|
_a.label = 2;
|
|
@@ -185,10 +185,8 @@ var EventQueue = /** @class */ (function () {
|
|
|
185
185
|
return [4 /*yield*/, this.generateMessageId(event)];
|
|
186
186
|
case 1:
|
|
187
187
|
message_id = _a.sent();
|
|
188
|
-
return [4 /*yield*/, this.isDuplicate(message_id)];
|
|
189
|
-
case 2:
|
|
190
188
|
// check if the message already exists
|
|
191
|
-
if (
|
|
189
|
+
if (this.isDuplicate(message_id)) {
|
|
192
190
|
logger_1.logger.warn("Event already enqueued, try again after ".concat((0, utils_1.millisecondsToSecond)(this.flushIntervalMs), " seconds."));
|
|
193
191
|
return [2 /*return*/];
|
|
194
192
|
}
|
|
@@ -217,10 +215,11 @@ var EventQueue = /** @class */ (function () {
|
|
|
217
215
|
});
|
|
218
216
|
});
|
|
219
217
|
};
|
|
220
|
-
EventQueue.prototype.flush = function (
|
|
221
|
-
return __awaiter(this,
|
|
222
|
-
var items, sentAt, data, batches;
|
|
218
|
+
EventQueue.prototype.flush = function (callback_1) {
|
|
219
|
+
return __awaiter(this, arguments, void 0, function (callback, drainAll) {
|
|
220
|
+
var items, _i, items_1, item, sentAt, data, batches;
|
|
223
221
|
var _this = this;
|
|
222
|
+
if (drainAll === void 0) { drainAll = false; }
|
|
224
223
|
return __generator(this, function (_a) {
|
|
225
224
|
switch (_a.label) {
|
|
226
225
|
case 0:
|
|
@@ -234,13 +233,19 @@ var EventQueue = /** @class */ (function () {
|
|
|
234
233
|
return [2 /*return*/, Promise.resolve()];
|
|
235
234
|
}
|
|
236
235
|
if (!this.pendingFlush) return [3 /*break*/, 2];
|
|
236
|
+
if (!!drainAll) return [3 /*break*/, 2];
|
|
237
237
|
return [4 /*yield*/, this.pendingFlush];
|
|
238
238
|
case 1:
|
|
239
239
|
_a.sent();
|
|
240
240
|
_a.label = 2;
|
|
241
241
|
case 2:
|
|
242
|
-
items = this.queue.splice(0, this.flushAt);
|
|
243
|
-
|
|
242
|
+
items = this.queue.splice(0, drainAll ? this.queue.length : this.flushAt);
|
|
243
|
+
// Only remove hashes for flushed items so duplicate detection remains
|
|
244
|
+
// active for events still in the queue.
|
|
245
|
+
for (_i = 0, items_1 = items; _i < items_1.length; _i++) {
|
|
246
|
+
item = items_1[_i];
|
|
247
|
+
this.payloadHashes.delete(item.message.message_id);
|
|
248
|
+
}
|
|
244
249
|
sentAt = new Date().toISOString();
|
|
245
250
|
data = items.map(function (item) { return (__assign(__assign({}, item.message), { sent_at: sentAt })); });
|
|
246
251
|
batches = this.splitIntoBatches(items, data);
|
|
@@ -402,15 +407,11 @@ var EventQueue = /** @class */ (function () {
|
|
|
402
407
|
return false;
|
|
403
408
|
};
|
|
404
409
|
EventQueue.prototype.isDuplicate = function (eventId) {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
this.payloadHashes.add(eventId);
|
|
411
|
-
return [2 /*return*/, false];
|
|
412
|
-
});
|
|
413
|
-
});
|
|
410
|
+
// check if exists a message with identical payload within 1 minute
|
|
411
|
+
if (this.payloadHashes.has(eventId))
|
|
412
|
+
return true;
|
|
413
|
+
this.payloadHashes.add(eventId);
|
|
414
|
+
return false;
|
|
414
415
|
};
|
|
415
416
|
return EventQueue;
|
|
416
417
|
}());
|
|
@@ -38,15 +38,6 @@ export interface IFormoAnalyticsSession {
|
|
|
38
38
|
*/
|
|
39
39
|
markWalletIdentified(address: string, rdns: string): void;
|
|
40
40
|
}
|
|
41
|
-
/**
|
|
42
|
-
* Implementation of session management using cookies
|
|
43
|
-
*
|
|
44
|
-
* Tracks:
|
|
45
|
-
* - Detected wallets (by RDNS) - to prevent duplicate detection events
|
|
46
|
-
* - Identified wallet-address pairs - to prevent duplicate identification events
|
|
47
|
-
*
|
|
48
|
-
* Session data expires at end of day (86400 seconds).
|
|
49
|
-
*/
|
|
50
41
|
export declare class FormoAnalyticsSession implements IFormoAnalyticsSession {
|
|
51
42
|
/**
|
|
52
43
|
* Generate a unique key for wallet identification tracking
|
|
@@ -25,6 +25,7 @@ exports.SESSION_WALLET_IDENTIFIED_KEY = "wallet-identified";
|
|
|
25
25
|
*
|
|
26
26
|
* Session data expires at end of day (86400 seconds).
|
|
27
27
|
*/
|
|
28
|
+
var MAX_SESSION_ENTRIES = 20;
|
|
28
29
|
var FormoAnalyticsSession = /** @class */ (function () {
|
|
29
30
|
function FormoAnalyticsSession() {
|
|
30
31
|
}
|
|
@@ -62,6 +63,9 @@ var FormoAnalyticsSession = /** @class */ (function () {
|
|
|
62
63
|
var rdnses = ((_a = (0, storage_1.cookie)().get(exports.SESSION_WALLET_DETECTED_KEY)) === null || _a === void 0 ? void 0 : _a.split(",")) || [];
|
|
63
64
|
if (!rdnses.includes(rdns)) {
|
|
64
65
|
rdnses.push(rdns);
|
|
66
|
+
if (rdnses.length > MAX_SESSION_ENTRIES) {
|
|
67
|
+
rdnses.splice(0, rdnses.length - MAX_SESSION_ENTRIES);
|
|
68
|
+
}
|
|
65
69
|
(0, storage_1.cookie)().set(exports.SESSION_WALLET_DETECTED_KEY, rdnses.join(","), {
|
|
66
70
|
// Expires by the end of the day
|
|
67
71
|
expires: new Date(Date.now() + 86400 * 1000).toUTCString(),
|
|
@@ -102,6 +106,9 @@ var FormoAnalyticsSession = /** @class */ (function () {
|
|
|
102
106
|
var alreadyExists = identifiedWallets.includes(identifiedKey);
|
|
103
107
|
if (!alreadyExists) {
|
|
104
108
|
identifiedWallets.push(identifiedKey);
|
|
109
|
+
if (identifiedWallets.length > MAX_SESSION_ENTRIES) {
|
|
110
|
+
identifiedWallets.splice(0, identifiedWallets.length - MAX_SESSION_ENTRIES);
|
|
111
|
+
}
|
|
105
112
|
var newValue = identifiedWallets.join(",");
|
|
106
113
|
(0, storage_1.cookie)().set(exports.SESSION_WALLET_IDENTIFIED_KEY, newValue, {
|
|
107
114
|
// Expires by the end of the day
|
|
@@ -26,11 +26,10 @@ export declare class SolanaAdapter {
|
|
|
26
26
|
*/
|
|
27
27
|
private pendingTransactions;
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
29
|
+
* Per-adapter original methods stored in a WeakMap to prevent routing
|
|
30
|
+
* to the wrong wallet after switching adapters.
|
|
30
31
|
*/
|
|
31
|
-
private
|
|
32
|
-
private originalAdapterSignMessage?;
|
|
33
|
-
private originalAdapterSignTransaction?;
|
|
32
|
+
private adapterOriginals;
|
|
34
33
|
/**
|
|
35
34
|
* Bound wrapper references — used to detect when external code (e.g. StandardWalletAdapter._reset())
|
|
36
35
|
* overwrites our wraps so we can re-apply them.
|
|
@@ -118,6 +118,11 @@ var SolanaAdapter = /** @class */ (function () {
|
|
|
118
118
|
* Key: transaction signature, Value: transaction details
|
|
119
119
|
*/
|
|
120
120
|
this.pendingTransactions = new Map();
|
|
121
|
+
/**
|
|
122
|
+
* Per-adapter original methods stored in a WeakMap to prevent routing
|
|
123
|
+
* to the wrong wallet after switching adapters.
|
|
124
|
+
*/
|
|
125
|
+
this.adapterOriginals = new WeakMap();
|
|
121
126
|
/**
|
|
122
127
|
* Track active polling timeout IDs for cleanup
|
|
123
128
|
*/
|
|
@@ -147,21 +152,22 @@ var SolanaAdapter = /** @class */ (function () {
|
|
|
147
152
|
SolanaAdapter.prototype.restoreOriginalMethods = function () {
|
|
148
153
|
// Restore adapter methods
|
|
149
154
|
if (this.wrappedAdapter) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
155
|
+
var originals = this.adapterOriginals.get(this.wrappedAdapter);
|
|
156
|
+
if (originals) {
|
|
157
|
+
if (originals.sendTransaction) {
|
|
158
|
+
this.wrappedAdapter.sendTransaction = originals.sendTransaction;
|
|
159
|
+
}
|
|
160
|
+
if (originals.signMessage) {
|
|
161
|
+
this.wrappedAdapter.signMessage = originals.signMessage;
|
|
162
|
+
}
|
|
163
|
+
if (originals.signTransaction) {
|
|
164
|
+
this.wrappedAdapter.signTransaction = originals.signTransaction;
|
|
165
|
+
}
|
|
166
|
+
this.adapterOriginals.delete(this.wrappedAdapter);
|
|
158
167
|
}
|
|
159
168
|
this.wrappedAdapter = undefined;
|
|
160
169
|
}
|
|
161
|
-
// Clear
|
|
162
|
-
this.originalAdapterSendTransaction = undefined;
|
|
163
|
-
this.originalAdapterSignMessage = undefined;
|
|
164
|
-
this.originalAdapterSignTransaction = undefined;
|
|
170
|
+
// Clear bound wrapper references
|
|
165
171
|
this.boundWrappedSendTransaction = undefined;
|
|
166
172
|
this.boundWrappedSignMessage = undefined;
|
|
167
173
|
this.boundWrappedSignTransaction = undefined;
|
|
@@ -390,24 +396,26 @@ var SolanaAdapter = /** @class */ (function () {
|
|
|
390
396
|
}
|
|
391
397
|
// Store reference to adapter for cleanup
|
|
392
398
|
this.wrappedAdapter = adapter;
|
|
399
|
+
var originals = {};
|
|
393
400
|
// Wrap sendTransaction
|
|
394
401
|
if (adapter.sendTransaction) {
|
|
395
|
-
|
|
402
|
+
originals.sendTransaction = adapter.sendTransaction.bind(adapter);
|
|
396
403
|
this.boundWrappedSendTransaction = this.wrappedSendTransaction.bind(this);
|
|
397
404
|
adapter.sendTransaction = this.boundWrappedSendTransaction;
|
|
398
405
|
}
|
|
399
406
|
// Wrap signMessage
|
|
400
407
|
if (adapter.signMessage) {
|
|
401
|
-
|
|
408
|
+
originals.signMessage = adapter.signMessage.bind(adapter);
|
|
402
409
|
this.boundWrappedSignMessage = this.wrappedSignMessage.bind(this);
|
|
403
410
|
adapter.signMessage = this.boundWrappedSignMessage;
|
|
404
411
|
}
|
|
405
412
|
// Wrap signTransaction
|
|
406
413
|
if (adapter.signTransaction) {
|
|
407
|
-
|
|
414
|
+
originals.signTransaction = adapter.signTransaction.bind(adapter);
|
|
408
415
|
this.boundWrappedSignTransaction = this.wrappedSignTransaction.bind(this);
|
|
409
416
|
adapter.signTransaction = this.boundWrappedSignTransaction;
|
|
410
417
|
}
|
|
418
|
+
this.adapterOriginals.set(adapter, originals);
|
|
411
419
|
};
|
|
412
420
|
/**
|
|
413
421
|
* Re-wrap methods that were overwritten by external code.
|
|
@@ -419,9 +427,10 @@ var SolanaAdapter = /** @class */ (function () {
|
|
|
419
427
|
*/
|
|
420
428
|
SolanaAdapter.prototype.rewrapOverwrittenMethods = function (adapter) {
|
|
421
429
|
var rewrapped = false;
|
|
430
|
+
var originals = this.adapterOriginals.get(adapter) || {};
|
|
422
431
|
// signMessage
|
|
423
432
|
if (adapter.signMessage && adapter.signMessage !== this.boundWrappedSignMessage) {
|
|
424
|
-
|
|
433
|
+
originals.signMessage = adapter.signMessage.bind(adapter);
|
|
425
434
|
if (!this.boundWrappedSignMessage) {
|
|
426
435
|
this.boundWrappedSignMessage = this.wrappedSignMessage.bind(this);
|
|
427
436
|
}
|
|
@@ -429,11 +438,11 @@ var SolanaAdapter = /** @class */ (function () {
|
|
|
429
438
|
rewrapped = true;
|
|
430
439
|
}
|
|
431
440
|
else if (!adapter.signMessage && this.boundWrappedSignMessage) {
|
|
432
|
-
|
|
441
|
+
originals.signMessage = undefined;
|
|
433
442
|
}
|
|
434
443
|
// signTransaction
|
|
435
444
|
if (adapter.signTransaction && adapter.signTransaction !== this.boundWrappedSignTransaction) {
|
|
436
|
-
|
|
445
|
+
originals.signTransaction = adapter.signTransaction.bind(adapter);
|
|
437
446
|
if (!this.boundWrappedSignTransaction) {
|
|
438
447
|
this.boundWrappedSignTransaction = this.wrappedSignTransaction.bind(this);
|
|
439
448
|
}
|
|
@@ -441,17 +450,18 @@ var SolanaAdapter = /** @class */ (function () {
|
|
|
441
450
|
rewrapped = true;
|
|
442
451
|
}
|
|
443
452
|
else if (!adapter.signTransaction && this.boundWrappedSignTransaction) {
|
|
444
|
-
|
|
453
|
+
originals.signTransaction = undefined;
|
|
445
454
|
}
|
|
446
455
|
// sendTransaction — unlikely to be overwritten but check for completeness
|
|
447
456
|
if (adapter.sendTransaction && adapter.sendTransaction !== this.boundWrappedSendTransaction) {
|
|
448
|
-
|
|
457
|
+
originals.sendTransaction = adapter.sendTransaction.bind(adapter);
|
|
449
458
|
if (!this.boundWrappedSendTransaction) {
|
|
450
459
|
this.boundWrappedSendTransaction = this.wrappedSendTransaction.bind(this);
|
|
451
460
|
}
|
|
452
461
|
adapter.sendTransaction = this.boundWrappedSendTransaction;
|
|
453
462
|
rewrapped = true;
|
|
454
463
|
}
|
|
464
|
+
this.adapterOriginals.set(adapter, originals);
|
|
455
465
|
if (rewrapped) {
|
|
456
466
|
logger_1.logger.debug("SolanaAdapter: Re-wrapped overwritten adapter methods");
|
|
457
467
|
}
|
|
@@ -461,12 +471,13 @@ var SolanaAdapter = /** @class */ (function () {
|
|
|
461
471
|
*/
|
|
462
472
|
SolanaAdapter.prototype.wrappedSendTransaction = function (transaction, connection, options) {
|
|
463
473
|
return __awaiter(this, void 0, void 0, function () {
|
|
464
|
-
var chainId, address, signature, error_1;
|
|
474
|
+
var originals, chainId, address, signature, error_1;
|
|
465
475
|
return __generator(this, function (_a) {
|
|
466
476
|
switch (_a.label) {
|
|
467
477
|
case 0:
|
|
468
478
|
this.checkAndRebindContextAdapter();
|
|
469
|
-
|
|
479
|
+
originals = this.wrappedAdapter ? this.adapterOriginals.get(this.wrappedAdapter) : undefined;
|
|
480
|
+
if (!(originals === null || originals === void 0 ? void 0 : originals.sendTransaction)) {
|
|
470
481
|
throw new Error("sendTransaction not available");
|
|
471
482
|
}
|
|
472
483
|
chainId = this.chainId;
|
|
@@ -475,7 +486,7 @@ var SolanaAdapter = /** @class */ (function () {
|
|
|
475
486
|
_a.label = 1;
|
|
476
487
|
case 1:
|
|
477
488
|
_a.trys.push([1, 3, , 4]);
|
|
478
|
-
return [4 /*yield*/,
|
|
489
|
+
return [4 /*yield*/, originals.sendTransaction(transaction, connection, options)];
|
|
479
490
|
case 2:
|
|
480
491
|
signature = _a.sent();
|
|
481
492
|
this.emitTransactionEvent(events_1.TransactionStatus.BROADCASTED, address, chainId, signature);
|
|
@@ -501,12 +512,13 @@ var SolanaAdapter = /** @class */ (function () {
|
|
|
501
512
|
*/
|
|
502
513
|
SolanaAdapter.prototype.wrappedSignMessage = function (message) {
|
|
503
514
|
return __awaiter(this, void 0, void 0, function () {
|
|
504
|
-
var chainId, address, messageString, signature, signatureHex, error_2;
|
|
515
|
+
var originals, chainId, address, messageString, signature, signatureHex, error_2;
|
|
505
516
|
return __generator(this, function (_a) {
|
|
506
517
|
switch (_a.label) {
|
|
507
518
|
case 0:
|
|
508
519
|
this.checkAndRebindContextAdapter();
|
|
509
|
-
|
|
520
|
+
originals = this.wrappedAdapter ? this.adapterOriginals.get(this.wrappedAdapter) : undefined;
|
|
521
|
+
if (!(originals === null || originals === void 0 ? void 0 : originals.signMessage)) {
|
|
510
522
|
throw new Error("signMessage not available");
|
|
511
523
|
}
|
|
512
524
|
chainId = this.chainId;
|
|
@@ -516,7 +528,7 @@ var SolanaAdapter = /** @class */ (function () {
|
|
|
516
528
|
_a.label = 1;
|
|
517
529
|
case 1:
|
|
518
530
|
_a.trys.push([1, 3, , 4]);
|
|
519
|
-
return [4 /*yield*/,
|
|
531
|
+
return [4 /*yield*/, originals.signMessage(message)];
|
|
520
532
|
case 2:
|
|
521
533
|
signature = _a.sent();
|
|
522
534
|
signatureHex = uint8ArrayToHex(signature);
|
|
@@ -536,12 +548,13 @@ var SolanaAdapter = /** @class */ (function () {
|
|
|
536
548
|
*/
|
|
537
549
|
SolanaAdapter.prototype.wrappedSignTransaction = function (transaction) {
|
|
538
550
|
return __awaiter(this, void 0, void 0, function () {
|
|
539
|
-
var chainId, address, message, signedTx, error_3;
|
|
551
|
+
var originals, chainId, address, message, signedTx, error_3;
|
|
540
552
|
return __generator(this, function (_a) {
|
|
541
553
|
switch (_a.label) {
|
|
542
554
|
case 0:
|
|
543
555
|
this.checkAndRebindContextAdapter();
|
|
544
|
-
|
|
556
|
+
originals = this.wrappedAdapter ? this.adapterOriginals.get(this.wrappedAdapter) : undefined;
|
|
557
|
+
if (!(originals === null || originals === void 0 ? void 0 : originals.signTransaction)) {
|
|
545
558
|
throw new Error("signTransaction not available");
|
|
546
559
|
}
|
|
547
560
|
chainId = this.chainId;
|
|
@@ -551,7 +564,7 @@ var SolanaAdapter = /** @class */ (function () {
|
|
|
551
564
|
_a.label = 1;
|
|
552
565
|
case 1:
|
|
553
566
|
_a.trys.push([1, 3, , 4]);
|
|
554
|
-
return [4 /*yield*/,
|
|
567
|
+
return [4 /*yield*/, originals.signTransaction(transaction)];
|
|
555
568
|
case 2:
|
|
556
569
|
signedTx = _a.sent();
|
|
557
570
|
this.emitSignatureEvent(events_1.SignatureStatus.CONFIRMED, address, chainId, message);
|
|
@@ -23,11 +23,19 @@ var StorageManager = /** @class */ (function () {
|
|
|
23
23
|
StorageManager.prototype.getStorage = function (type) {
|
|
24
24
|
if (!this.storages.has(type)) {
|
|
25
25
|
var storage = this.createStorage(type);
|
|
26
|
+
var currentType = type;
|
|
26
27
|
// If storage is not available, try next
|
|
27
28
|
while (!storage.isAvailable()) {
|
|
28
|
-
var index = TYPES.indexOf(
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
var index = TYPES.indexOf(currentType);
|
|
30
|
+
if (index === -1 || index + 1 >= TYPES.length) {
|
|
31
|
+
// No more fallbacks, use memory storage as last resort
|
|
32
|
+
storage = this.createStorage("memoryStorage");
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
var prevType = currentType;
|
|
36
|
+
currentType = TYPES[index + 1];
|
|
37
|
+
logger_1.logger.warn("Storage ".concat(prevType, " is not available, trying ").concat(currentType));
|
|
38
|
+
storage = this.createStorage(currentType);
|
|
31
39
|
}
|
|
32
40
|
// Add to cache
|
|
33
41
|
this.storages.set(type, storage);
|