@formo/analytics 1.26.0 → 1.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/dist/cjs/src/FormoAnalytics.d.ts +71 -12
- package/dist/cjs/src/FormoAnalytics.js +274 -147
- package/dist/cjs/src/event/EventFactory.d.ts +10 -2
- package/dist/cjs/src/event/EventFactory.js +32 -21
- package/dist/cjs/src/fetch/index.d.ts +10 -2
- package/dist/cjs/src/fetch/index.js +122 -4
- package/dist/cjs/src/index.d.ts +4 -0
- package/dist/cjs/src/index.js +6 -0
- package/dist/cjs/src/privy/index.d.ts +9 -0
- package/dist/cjs/src/privy/index.js +12 -0
- package/dist/cjs/src/privy/types.d.ts +175 -0
- package/dist/cjs/src/privy/types.js +12 -0
- package/dist/cjs/src/privy/utils.d.ts +32 -0
- package/dist/cjs/src/privy/utils.js +188 -0
- package/dist/cjs/src/queue/EventQueue.d.ts +24 -2
- package/dist/cjs/src/queue/EventQueue.js +158 -49
- package/dist/cjs/src/session/index.js +2 -1
- package/dist/cjs/src/solana/SolanaAdapter.d.ts +211 -0
- package/dist/cjs/src/solana/SolanaAdapter.js +975 -0
- package/dist/cjs/src/solana/SolanaManager.d.ts +24 -0
- package/dist/cjs/src/solana/SolanaManager.js +80 -0
- package/dist/cjs/src/solana/address.d.ts +72 -0
- package/dist/cjs/src/solana/address.js +176 -0
- package/dist/cjs/src/solana/index.d.ts +13 -0
- package/dist/cjs/src/solana/index.js +32 -0
- package/dist/cjs/src/solana/types.d.ts +206 -0
- package/dist/cjs/src/solana/types.js +80 -0
- package/dist/cjs/src/types/base.d.ts +25 -0
- package/dist/cjs/src/types/events.d.ts +9 -6
- package/dist/cjs/src/utils/address.d.ts +21 -0
- package/dist/cjs/src/utils/address.js +48 -1
- package/dist/cjs/src/utils/builderCode.d.ts +30 -0
- package/dist/cjs/src/utils/builderCode.js +143 -0
- package/dist/cjs/src/utils/index.d.ts +1 -0
- package/dist/cjs/src/utils/index.js +1 -0
- package/dist/cjs/src/version.d.ts +1 -1
- package/dist/cjs/src/version.js +1 -1
- package/dist/cjs/src/wagmi/WagmiEventHandler.d.ts +24 -0
- package/dist/cjs/src/wagmi/WagmiEventHandler.js +242 -24
- package/dist/cjs/src/wagmi/types.d.ts +31 -0
- package/dist/cjs/src/wagmi/utils.d.ts +79 -0
- package/dist/cjs/src/wagmi/utils.js +218 -0
- package/dist/esm/src/FormoAnalytics.d.ts +71 -12
- package/dist/esm/src/FormoAnalytics.js +275 -148
- package/dist/esm/src/event/EventFactory.d.ts +10 -2
- package/dist/esm/src/event/EventFactory.js +34 -23
- package/dist/esm/src/fetch/index.d.ts +10 -2
- package/dist/esm/src/fetch/index.js +123 -2
- package/dist/esm/src/index.d.ts +4 -0
- package/dist/esm/src/index.js +3 -0
- package/dist/esm/src/privy/index.d.ts +9 -0
- package/dist/esm/src/privy/index.js +8 -0
- package/dist/esm/src/privy/types.d.ts +175 -0
- package/dist/esm/src/privy/types.js +11 -0
- package/dist/esm/src/privy/utils.d.ts +32 -0
- package/dist/esm/src/privy/utils.js +185 -0
- package/dist/esm/src/queue/EventQueue.d.ts +24 -2
- package/dist/esm/src/queue/EventQueue.js +158 -49
- package/dist/esm/src/session/index.js +2 -1
- package/dist/esm/src/solana/SolanaAdapter.d.ts +211 -0
- package/dist/esm/src/solana/SolanaAdapter.js +972 -0
- package/dist/esm/src/solana/SolanaManager.d.ts +24 -0
- package/dist/esm/src/solana/SolanaManager.js +77 -0
- package/dist/esm/src/solana/address.d.ts +72 -0
- package/dist/esm/src/solana/address.js +167 -0
- package/dist/esm/src/solana/index.d.ts +13 -0
- package/dist/esm/src/solana/index.js +13 -0
- package/dist/esm/src/solana/types.d.ts +206 -0
- package/dist/esm/src/solana/types.js +74 -0
- package/dist/esm/src/types/base.d.ts +25 -0
- package/dist/esm/src/types/events.d.ts +9 -6
- package/dist/esm/src/utils/address.d.ts +21 -0
- package/dist/esm/src/utils/address.js +45 -0
- package/dist/esm/src/utils/builderCode.d.ts +30 -0
- package/dist/esm/src/utils/builderCode.js +140 -0
- package/dist/esm/src/utils/index.d.ts +1 -0
- package/dist/esm/src/utils/index.js +1 -0
- package/dist/esm/src/version.d.ts +1 -1
- package/dist/esm/src/version.js +1 -1
- package/dist/esm/src/wagmi/WagmiEventHandler.d.ts +24 -0
- package/dist/esm/src/wagmi/WagmiEventHandler.js +242 -24
- package/dist/esm/src/wagmi/types.d.ts +31 -0
- package/dist/esm/src/wagmi/utils.d.ts +79 -0
- package/dist/esm/src/wagmi/utils.js +211 -0
- package/dist/index.umd.min.js +1 -1
- package/package.json +20 -4
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Utility functions for extracting profile properties from Privy user objects.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parsePrivyProperties = parsePrivyProperties;
|
|
7
|
+
/**
|
|
8
|
+
* Extract profile properties and wallet addresses from a Privy user object.
|
|
9
|
+
*
|
|
10
|
+
* Parses the Privy user's linked accounts into a flat properties object
|
|
11
|
+
* (email, social accounts, etc.) and extracts all linked wallet addresses.
|
|
12
|
+
*
|
|
13
|
+
* @param user - The Privy user object from `usePrivy()`
|
|
14
|
+
* @returns An object with `properties` and `wallets`
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { parsePrivyProperties } from '@formo/analytics';
|
|
19
|
+
*
|
|
20
|
+
* const { user } = usePrivy();
|
|
21
|
+
* if (user) {
|
|
22
|
+
* const { properties, wallets } = parsePrivyProperties(user);
|
|
23
|
+
*
|
|
24
|
+
* for (const wallet of wallets) {
|
|
25
|
+
* formo.identify({ address: wallet.address, userId: user.id }, properties);
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
function parsePrivyProperties(user) {
|
|
31
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
32
|
+
var accounts = user.linkedAccounts || [];
|
|
33
|
+
// Extract profile properties
|
|
34
|
+
var properties = {
|
|
35
|
+
privyDid: user.id,
|
|
36
|
+
privyCreatedAt: user.createdAt,
|
|
37
|
+
};
|
|
38
|
+
// Email
|
|
39
|
+
if ((_a = user.email) === null || _a === void 0 ? void 0 : _a.address) {
|
|
40
|
+
properties.email = user.email.address;
|
|
41
|
+
}
|
|
42
|
+
// Social accounts - extract usernames/identifiers
|
|
43
|
+
if ((_b = user.apple) === null || _b === void 0 ? void 0 : _b.email) {
|
|
44
|
+
properties.apple = user.apple.email;
|
|
45
|
+
}
|
|
46
|
+
if ((_c = user.discord) === null || _c === void 0 ? void 0 : _c.username) {
|
|
47
|
+
properties.discord = user.discord.username;
|
|
48
|
+
}
|
|
49
|
+
if ((_d = user.farcaster) === null || _d === void 0 ? void 0 : _d.username) {
|
|
50
|
+
properties.farcaster = user.farcaster.username;
|
|
51
|
+
}
|
|
52
|
+
if ((_e = user.github) === null || _e === void 0 ? void 0 : _e.username) {
|
|
53
|
+
properties.github = user.github.username;
|
|
54
|
+
}
|
|
55
|
+
if ((_f = user.google) === null || _f === void 0 ? void 0 : _f.email) {
|
|
56
|
+
properties.google = user.google.email;
|
|
57
|
+
}
|
|
58
|
+
if ((_g = user.instagram) === null || _g === void 0 ? void 0 : _g.username) {
|
|
59
|
+
properties.instagram = user.instagram.username;
|
|
60
|
+
}
|
|
61
|
+
if ((_h = user.line) === null || _h === void 0 ? void 0 : _h.email) {
|
|
62
|
+
properties.line = user.line.email;
|
|
63
|
+
}
|
|
64
|
+
if ((_j = user.linkedin) === null || _j === void 0 ? void 0 : _j.email) {
|
|
65
|
+
properties.linkedin = user.linkedin.email;
|
|
66
|
+
}
|
|
67
|
+
if ((_k = user.spotify) === null || _k === void 0 ? void 0 : _k.email) {
|
|
68
|
+
properties.spotify = user.spotify.email;
|
|
69
|
+
}
|
|
70
|
+
if ((_l = user.telegram) === null || _l === void 0 ? void 0 : _l.username) {
|
|
71
|
+
properties.telegram = user.telegram.username;
|
|
72
|
+
}
|
|
73
|
+
if ((_m = user.tiktok) === null || _m === void 0 ? void 0 : _m.username) {
|
|
74
|
+
properties.tiktok = user.tiktok.username;
|
|
75
|
+
}
|
|
76
|
+
if ((_o = user.twitter) === null || _o === void 0 ? void 0 : _o.username) {
|
|
77
|
+
properties.twitter = user.twitter.username;
|
|
78
|
+
}
|
|
79
|
+
// Fallback to linkedAccounts if convenience accessors are not populated
|
|
80
|
+
if (!properties.email) {
|
|
81
|
+
var emailAccount = accounts.find(function (account) { return account.type === "email"; });
|
|
82
|
+
if (emailAccount === null || emailAccount === void 0 ? void 0 : emailAccount.address) {
|
|
83
|
+
properties.email = emailAccount.address;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (!properties.apple) {
|
|
87
|
+
var appleAccount = accounts.find(function (a) { return a.type === "apple_oauth"; });
|
|
88
|
+
if (appleAccount === null || appleAccount === void 0 ? void 0 : appleAccount.email) {
|
|
89
|
+
properties.apple = appleAccount.email;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (!properties.discord) {
|
|
93
|
+
var discordAccount = accounts.find(function (a) { return a.type === "discord_oauth"; });
|
|
94
|
+
if (discordAccount === null || discordAccount === void 0 ? void 0 : discordAccount.username) {
|
|
95
|
+
properties.discord = discordAccount.username;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (!properties.farcaster) {
|
|
99
|
+
var farcasterAccount = accounts.find(function (a) { return a.type === "farcaster"; });
|
|
100
|
+
if (farcasterAccount === null || farcasterAccount === void 0 ? void 0 : farcasterAccount.username) {
|
|
101
|
+
properties.farcaster = farcasterAccount.username;
|
|
102
|
+
}
|
|
103
|
+
else if (farcasterAccount === null || farcasterAccount === void 0 ? void 0 : farcasterAccount.displayName) {
|
|
104
|
+
properties.farcaster = farcasterAccount.displayName;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (!properties.github) {
|
|
108
|
+
var githubAccount = accounts.find(function (a) { return a.type === "github_oauth"; });
|
|
109
|
+
if (githubAccount === null || githubAccount === void 0 ? void 0 : githubAccount.username) {
|
|
110
|
+
properties.github = githubAccount.username;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (!properties.google) {
|
|
114
|
+
var googleAccount = accounts.find(function (a) { return a.type === "google_oauth"; });
|
|
115
|
+
if (googleAccount === null || googleAccount === void 0 ? void 0 : googleAccount.email) {
|
|
116
|
+
properties.google = googleAccount.email;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (!properties.instagram) {
|
|
120
|
+
var instagramAccount = accounts.find(function (a) { return a.type === "instagram_oauth"; });
|
|
121
|
+
if (instagramAccount === null || instagramAccount === void 0 ? void 0 : instagramAccount.username) {
|
|
122
|
+
properties.instagram = instagramAccount.username;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (!properties.line) {
|
|
126
|
+
var lineAccount = accounts.find(function (a) { return a.type === "line"; });
|
|
127
|
+
if (lineAccount === null || lineAccount === void 0 ? void 0 : lineAccount.email) {
|
|
128
|
+
properties.line = lineAccount.email;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (!properties.linkedin) {
|
|
132
|
+
var linkedinAccount = accounts.find(function (a) { return a.type === "linkedin_oauth"; });
|
|
133
|
+
if (linkedinAccount === null || linkedinAccount === void 0 ? void 0 : linkedinAccount.email) {
|
|
134
|
+
properties.linkedin = linkedinAccount.email;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (!properties.spotify) {
|
|
138
|
+
var spotifyAccount = accounts.find(function (a) { return a.type === "spotify_oauth"; });
|
|
139
|
+
if (spotifyAccount === null || spotifyAccount === void 0 ? void 0 : spotifyAccount.email) {
|
|
140
|
+
properties.spotify = spotifyAccount.email;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (!properties.telegram) {
|
|
144
|
+
var telegramAccount = accounts.find(function (a) { return a.type === "telegram"; });
|
|
145
|
+
if (telegramAccount === null || telegramAccount === void 0 ? void 0 : telegramAccount.username) {
|
|
146
|
+
properties.telegram = telegramAccount.username;
|
|
147
|
+
}
|
|
148
|
+
else if (telegramAccount === null || telegramAccount === void 0 ? void 0 : telegramAccount.telegramUserId) {
|
|
149
|
+
properties.telegram = telegramAccount.telegramUserId;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (!properties.tiktok) {
|
|
153
|
+
var tiktokAccount = accounts.find(function (a) { return a.type === "tiktok_oauth"; });
|
|
154
|
+
if (tiktokAccount === null || tiktokAccount === void 0 ? void 0 : tiktokAccount.username) {
|
|
155
|
+
properties.tiktok = tiktokAccount.username;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (!properties.twitter) {
|
|
159
|
+
var twitterAccount = accounts.find(function (a) { return a.type === "twitter_oauth"; });
|
|
160
|
+
if (twitterAccount === null || twitterAccount === void 0 ? void 0 : twitterAccount.username) {
|
|
161
|
+
properties.twitter = twitterAccount.username;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Use OAuth emails as fallback for email if still blank
|
|
165
|
+
// Priority: email -> google -> apple -> linkedin
|
|
166
|
+
if (!properties.email) {
|
|
167
|
+
if (properties.google) {
|
|
168
|
+
properties.email = properties.google;
|
|
169
|
+
}
|
|
170
|
+
else if (properties.apple) {
|
|
171
|
+
properties.email = properties.apple;
|
|
172
|
+
}
|
|
173
|
+
else if (properties.linkedin) {
|
|
174
|
+
properties.email = properties.linkedin;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Extract wallet addresses
|
|
178
|
+
var wallets = accounts
|
|
179
|
+
.filter(function (a) { return a.type === "wallet" && a.address; })
|
|
180
|
+
.map(function (a) { return ({
|
|
181
|
+
address: a.address,
|
|
182
|
+
walletClient: a.walletClientType || a.walletClient,
|
|
183
|
+
chainType: a.chainType,
|
|
184
|
+
isEmbedded: a.walletClientType === "privy" || a.walletClient === "privy",
|
|
185
|
+
}); });
|
|
186
|
+
return { properties: properties, wallets: wallets };
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import { IFormoEvent } from "../types";
|
|
1
|
+
import { IFormoEvent, IFormoEventPayload } from "../types";
|
|
2
2
|
import { IEventQueue } from "./type";
|
|
3
|
+
type IFormoEventFlushPayload = IFormoEventPayload & {
|
|
4
|
+
sent_at: string;
|
|
5
|
+
};
|
|
3
6
|
type Options = {
|
|
4
7
|
apiHost: string;
|
|
5
8
|
flushAt?: number;
|
|
@@ -25,7 +28,26 @@ export declare class EventQueue implements IEventQueue {
|
|
|
25
28
|
constructor(writeKey: string, options: Options);
|
|
26
29
|
private generateMessageId;
|
|
27
30
|
enqueue(event: IFormoEvent, callback?: (...args: any) => void): Promise<void>;
|
|
28
|
-
flush(callback?: (...args: any) => void): Promise<
|
|
31
|
+
flush(callback?: (...args: any) => void): Promise<void | IFormoEventFlushPayload[]>;
|
|
32
|
+
/**
|
|
33
|
+
* Returns the UTF-8 byte length of a string. The browser's keepalive limit
|
|
34
|
+
* is enforced on the wire (UTF-8 bytes), not on JS string length (UTF-16
|
|
35
|
+
* code units). Non-ASCII characters (CJK, emoji) can be 2–4x larger in
|
|
36
|
+
* UTF-8 than their string .length suggests.
|
|
37
|
+
*/
|
|
38
|
+
private static byteLength;
|
|
39
|
+
/**
|
|
40
|
+
* Splits events into batches that respect the browser's 64KB keepalive
|
|
41
|
+
* payload size limit. Each batch pairs its serialized data with the
|
|
42
|
+
* original queue items (for per-item callback reporting) and a flag
|
|
43
|
+
* indicating whether keepalive is safe to use.
|
|
44
|
+
*/
|
|
45
|
+
private splitIntoBatches;
|
|
46
|
+
/**
|
|
47
|
+
* Sends batches sequentially, notifying per-item callbacks on success/failure.
|
|
48
|
+
* Returns the first error encountered (if any) so the caller can report it.
|
|
49
|
+
*/
|
|
50
|
+
private sendBatches;
|
|
29
51
|
private isErrorRetryable;
|
|
30
52
|
private isDuplicate;
|
|
31
53
|
private onPageLeave;
|
|
@@ -57,6 +57,16 @@ var logger_1 = require("../logger");
|
|
|
57
57
|
var constants_1 = require("../constants");
|
|
58
58
|
var fetch_1 = __importDefault(require("../fetch"));
|
|
59
59
|
var noop = function () { };
|
|
60
|
+
var safeCall = function (fn) {
|
|
61
|
+
var args = [];
|
|
62
|
+
for (var _i = 1; _i < arguments.length; _i++) {
|
|
63
|
+
args[_i - 1] = arguments[_i];
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
fn.apply(void 0, args);
|
|
67
|
+
}
|
|
68
|
+
catch ( /* swallow */_a) { /* swallow */ }
|
|
69
|
+
};
|
|
60
70
|
var DEFAULT_RETRY = 3;
|
|
61
71
|
var MAX_RETRY = 5;
|
|
62
72
|
var MIN_RETRY = 1;
|
|
@@ -66,6 +76,10 @@ var MIN_FLUSH_AT = 1;
|
|
|
66
76
|
var DEFAULT_QUEUE_SIZE = 1024 * 500; // 500kB
|
|
67
77
|
var MAX_QUEUE_SIZE = 1024 * 500; // 500kB
|
|
68
78
|
var MIN_QUEUE_SIZE = 200; // 200 bytes
|
|
79
|
+
// Browsers enforce a 64KB limit on the total body size of in-flight
|
|
80
|
+
// keepalive fetch requests. Payloads exceeding this are silently cancelled,
|
|
81
|
+
// producing a TypeError: Failed to fetch that cannot be resolved by retrying.
|
|
82
|
+
var KEEPALIVE_PAYLOAD_LIMIT = 64 * 1024; // 64kB
|
|
69
83
|
var DEFAULT_FLUSH_INTERVAL = 1000 * 30; // 1 MINUTE
|
|
70
84
|
var MAX_FLUSH_INTERVAL = 1000 * 300; // 5 MINUTES
|
|
71
85
|
var MIN_FLUSH_INTERVAL = 1000 * 10; // 10 SECONDS
|
|
@@ -150,7 +164,6 @@ var EventQueue = /** @class */ (function () {
|
|
|
150
164
|
});
|
|
151
165
|
}); });
|
|
152
166
|
}
|
|
153
|
-
//#region Public functions
|
|
154
167
|
EventQueue.prototype.generateMessageId = function (event) {
|
|
155
168
|
return __awaiter(this, void 0, void 0, function () {
|
|
156
169
|
var formattedTimestamp, eventForHashing, eventString;
|
|
@@ -206,7 +219,7 @@ var EventQueue = /** @class */ (function () {
|
|
|
206
219
|
};
|
|
207
220
|
EventQueue.prototype.flush = function (callback) {
|
|
208
221
|
return __awaiter(this, void 0, void 0, function () {
|
|
209
|
-
var
|
|
222
|
+
var items, sentAt, data, batches;
|
|
210
223
|
var _this = this;
|
|
211
224
|
return __generator(this, function (_a) {
|
|
212
225
|
switch (_a.label) {
|
|
@@ -220,75 +233,171 @@ var EventQueue = /** @class */ (function () {
|
|
|
220
233
|
callback();
|
|
221
234
|
return [2 /*return*/, Promise.resolve()];
|
|
222
235
|
}
|
|
223
|
-
|
|
224
|
-
case 1:
|
|
225
|
-
_a.trys.push([1, 4, , 5]);
|
|
226
|
-
if (!this.pendingFlush) return [3 /*break*/, 3];
|
|
236
|
+
if (!this.pendingFlush) return [3 /*break*/, 2];
|
|
227
237
|
return [4 /*yield*/, this.pendingFlush];
|
|
228
|
-
case
|
|
238
|
+
case 1:
|
|
229
239
|
_a.sent();
|
|
230
|
-
_a.label =
|
|
231
|
-
case
|
|
232
|
-
case 4:
|
|
233
|
-
err_1 = _a.sent();
|
|
234
|
-
this.pendingFlush = null;
|
|
235
|
-
throw err_1;
|
|
236
|
-
case 5:
|
|
240
|
+
_a.label = 2;
|
|
241
|
+
case 2:
|
|
237
242
|
items = this.queue.splice(0, this.flushAt);
|
|
238
243
|
this.payloadHashes.clear();
|
|
239
244
|
sentAt = new Date().toISOString();
|
|
240
245
|
data = items.map(function (item) { return (__assign(__assign({}, item.message), { sent_at: sentAt })); });
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
retries: this.retryCount,
|
|
254
|
-
retryDelay: function (attempt) { return Math.pow(2, attempt) * 1000; }, // exponential backoff
|
|
255
|
-
retryOn: function (_, error) { return _this.isErrorRetryable(error); },
|
|
256
|
-
})
|
|
257
|
-
.then(function () {
|
|
258
|
-
done();
|
|
246
|
+
batches = this.splitIntoBatches(items, data);
|
|
247
|
+
return [2 /*return*/, (this.pendingFlush = this.sendBatches(batches, data)
|
|
248
|
+
.then(function (firstError) {
|
|
249
|
+
if (firstError) {
|
|
250
|
+
safeCall(callback, firstError, data);
|
|
251
|
+
if (typeof _this.errorHandler === "function") {
|
|
252
|
+
safeCall(_this.errorHandler, firstError);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
safeCall(callback, undefined, data);
|
|
257
|
+
}
|
|
259
258
|
return Promise.resolve(data);
|
|
260
259
|
})
|
|
261
260
|
.catch(function (err) {
|
|
261
|
+
// Defensive: should not be reachable since sendBatches catches
|
|
262
|
+
// all errors internally, but guard against unexpected failures.
|
|
263
|
+
safeCall(callback, err, data);
|
|
262
264
|
if (typeof _this.errorHandler === "function") {
|
|
263
|
-
|
|
264
|
-
return _this.errorHandler(err);
|
|
265
|
-
}
|
|
266
|
-
if (err.response) {
|
|
267
|
-
var error = new Error(err.response.statusText);
|
|
268
|
-
done(error);
|
|
269
|
-
throw error;
|
|
265
|
+
safeCall(_this.errorHandler, err);
|
|
270
266
|
}
|
|
271
|
-
|
|
272
|
-
|
|
267
|
+
// Do NOT re-throw — analytics errors should never
|
|
268
|
+
// propagate as unhandled rejections to the host app
|
|
273
269
|
}))];
|
|
274
270
|
}
|
|
275
271
|
});
|
|
276
272
|
});
|
|
277
273
|
};
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
274
|
+
/**
|
|
275
|
+
* Returns the UTF-8 byte length of a string. The browser's keepalive limit
|
|
276
|
+
* is enforced on the wire (UTF-8 bytes), not on JS string length (UTF-16
|
|
277
|
+
* code units). Non-ASCII characters (CJK, emoji) can be 2–4x larger in
|
|
278
|
+
* UTF-8 than their string .length suggests.
|
|
279
|
+
*/
|
|
280
|
+
EventQueue.byteLength = function (str) {
|
|
281
|
+
return new TextEncoder().encode(str).byteLength;
|
|
282
|
+
};
|
|
283
|
+
/**
|
|
284
|
+
* Splits events into batches that respect the browser's 64KB keepalive
|
|
285
|
+
* payload size limit. Each batch pairs its serialized data with the
|
|
286
|
+
* original queue items (for per-item callback reporting) and a flag
|
|
287
|
+
* indicating whether keepalive is safe to use.
|
|
288
|
+
*/
|
|
289
|
+
EventQueue.prototype.splitIntoBatches = function (items, data) {
|
|
290
|
+
var serialized = JSON.stringify(data);
|
|
291
|
+
if (EventQueue.byteLength(serialized) <= KEEPALIVE_PAYLOAD_LIMIT) {
|
|
292
|
+
return [{ data: data, items: items, keepalive: true }];
|
|
293
|
+
}
|
|
294
|
+
var batches = [];
|
|
295
|
+
var currentData = [];
|
|
296
|
+
var currentItems = [];
|
|
297
|
+
var currentSize = 2; // account for JSON array brackets "[]"
|
|
298
|
+
for (var i = 0; i < data.length; i++) {
|
|
299
|
+
var event_1 = data[i];
|
|
300
|
+
var eventSize = EventQueue.byteLength(JSON.stringify(event_1));
|
|
301
|
+
var sizeWithEvent = currentSize + (currentData.length > 0 ? 1 : 0) + eventSize;
|
|
302
|
+
if (sizeWithEvent > KEEPALIVE_PAYLOAD_LIMIT) {
|
|
303
|
+
if (currentData.length > 0) {
|
|
304
|
+
batches.push({ data: currentData, items: currentItems, keepalive: true });
|
|
305
|
+
}
|
|
306
|
+
// If a single event exceeds the limit, send it without keepalive
|
|
307
|
+
if (eventSize + 2 > KEEPALIVE_PAYLOAD_LIMIT) {
|
|
308
|
+
batches.push({ data: [event_1], items: [items[i]], keepalive: false });
|
|
309
|
+
currentData = [];
|
|
310
|
+
currentItems = [];
|
|
311
|
+
currentSize = 2;
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
currentData = [event_1];
|
|
315
|
+
currentItems = [items[i]];
|
|
316
|
+
currentSize = 2 + eventSize;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
currentData.push(event_1);
|
|
321
|
+
currentItems.push(items[i]);
|
|
322
|
+
currentSize = sizeWithEvent;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (currentData.length > 0) {
|
|
326
|
+
batches.push({ data: currentData, items: currentItems, keepalive: true });
|
|
327
|
+
}
|
|
328
|
+
return batches;
|
|
329
|
+
};
|
|
330
|
+
/**
|
|
331
|
+
* Sends batches sequentially, notifying per-item callbacks on success/failure.
|
|
332
|
+
* Returns the first error encountered (if any) so the caller can report it.
|
|
333
|
+
*/
|
|
334
|
+
EventQueue.prototype.sendBatches = function (batches, allData) {
|
|
335
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
336
|
+
var firstError, _i, batches_1, batch, body, response, error, err_1;
|
|
337
|
+
var _this = this;
|
|
338
|
+
return __generator(this, function (_a) {
|
|
339
|
+
switch (_a.label) {
|
|
340
|
+
case 0:
|
|
341
|
+
_i = 0, batches_1 = batches;
|
|
342
|
+
_a.label = 1;
|
|
343
|
+
case 1:
|
|
344
|
+
if (!(_i < batches_1.length)) return [3 /*break*/, 6];
|
|
345
|
+
batch = batches_1[_i];
|
|
346
|
+
_a.label = 2;
|
|
347
|
+
case 2:
|
|
348
|
+
_a.trys.push([2, 4, , 5]);
|
|
349
|
+
body = JSON.stringify(batch.data);
|
|
350
|
+
return [4 /*yield*/, (0, fetch_1.default)("".concat(this.apiHost), {
|
|
351
|
+
headers: (0, constants_1.EVENTS_API_REQUEST_HEADER)(this.writeKey),
|
|
352
|
+
method: "POST",
|
|
353
|
+
body: body,
|
|
354
|
+
keepalive: batch.keepalive,
|
|
355
|
+
retries: this.retryCount,
|
|
356
|
+
retryDelay: function (attempt) { return Math.pow(2, attempt) * 1000; },
|
|
357
|
+
retryOn: function (_, error, response) { return _this.isErrorRetryable(error, response); },
|
|
358
|
+
})];
|
|
359
|
+
case 3:
|
|
360
|
+
response = _a.sent();
|
|
361
|
+
if (!response.ok) {
|
|
362
|
+
error = new Error(response.statusText || "HTTP ".concat(response.status));
|
|
363
|
+
error.response = response;
|
|
364
|
+
throw error;
|
|
365
|
+
}
|
|
366
|
+
batch.items.forEach(function (_a) {
|
|
367
|
+
var message = _a.message, cb = _a.callback;
|
|
368
|
+
return safeCall(cb, undefined, message, allData);
|
|
369
|
+
});
|
|
370
|
+
return [3 /*break*/, 5];
|
|
371
|
+
case 4:
|
|
372
|
+
err_1 = _a.sent();
|
|
373
|
+
firstError = firstError || err_1;
|
|
374
|
+
batch.items.forEach(function (_a) {
|
|
375
|
+
var message = _a.message, cb = _a.callback;
|
|
376
|
+
return safeCall(cb, err_1, message, allData);
|
|
377
|
+
});
|
|
378
|
+
return [3 /*break*/, 5];
|
|
379
|
+
case 5:
|
|
380
|
+
_i++;
|
|
381
|
+
return [3 /*break*/, 1];
|
|
382
|
+
case 6: return [2 /*return*/, firstError];
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
};
|
|
387
|
+
EventQueue.prototype.isErrorRetryable = function (error, response) {
|
|
388
|
+
var _a, _b;
|
|
281
389
|
// Retry Network Errors.
|
|
282
|
-
if ((0, validators_1.isNetworkError)(error))
|
|
390
|
+
if (error && (0, validators_1.isNetworkError)(error))
|
|
283
391
|
return true;
|
|
284
|
-
//
|
|
285
|
-
|
|
392
|
+
// Check response status if available
|
|
393
|
+
var status = (_a = response === null || response === void 0 ? void 0 : response.status) !== null && _a !== void 0 ? _a : (_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.status;
|
|
394
|
+
if (!status)
|
|
286
395
|
return false;
|
|
287
396
|
// Retry Server Errors (5xx).
|
|
288
|
-
if (
|
|
397
|
+
if (status >= 500 && status <= 599)
|
|
289
398
|
return true;
|
|
290
399
|
// Retry if rate limited.
|
|
291
|
-
if (
|
|
400
|
+
if (status === 429)
|
|
292
401
|
return true;
|
|
293
402
|
return false;
|
|
294
403
|
};
|
|
@@ -99,7 +99,8 @@ var FormoAnalyticsSession = /** @class */ (function () {
|
|
|
99
99
|
var _a;
|
|
100
100
|
var identifiedKey = this.generateIdentificationKey(address, rdns);
|
|
101
101
|
var identifiedWallets = ((_a = (0, storage_1.cookie)().get(exports.SESSION_WALLET_IDENTIFIED_KEY)) === null || _a === void 0 ? void 0 : _a.split(",")) || [];
|
|
102
|
-
|
|
102
|
+
var alreadyExists = identifiedWallets.includes(identifiedKey);
|
|
103
|
+
if (!alreadyExists) {
|
|
103
104
|
identifiedWallets.push(identifiedKey);
|
|
104
105
|
var newValue = identifiedWallets.join(",");
|
|
105
106
|
(0, storage_1.cookie)().set(exports.SESSION_WALLET_IDENTIFIED_KEY, newValue, {
|