@formo/analytics 1.25.0 → 1.27.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.
Files changed (192) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +6 -4
  3. package/dist/cjs/src/FormoAnalytics.d.ts +27 -15
  4. package/dist/cjs/src/FormoAnalytics.js +198 -257
  5. package/dist/cjs/src/FormoAnalyticsProvider.js +71 -13
  6. package/dist/{esm/src/lib/consent.d.ts → cjs/src/consent/index.d.ts} +1 -1
  7. package/dist/cjs/src/{lib/consent.js → consent/index.js} +1 -1
  8. package/dist/cjs/src/constants/base.d.ts +0 -2
  9. package/dist/cjs/src/constants/base.js +3 -3
  10. package/dist/cjs/src/constants/config.js +1 -1
  11. package/dist/cjs/src/{lib/event → event}/EventFactory.d.ts +2 -2
  12. package/dist/cjs/src/{lib/event → event}/EventFactory.js +9 -9
  13. package/dist/cjs/src/{lib/event → event}/EventManager.d.ts +1 -1
  14. package/dist/cjs/src/{lib/event → event}/EventManager.js +1 -1
  15. package/dist/cjs/src/{lib/event → event}/type.d.ts +1 -1
  16. package/dist/cjs/src/{lib/event → event}/utils.d.ts +1 -1
  17. package/dist/cjs/src/{lib/event → event}/utils.js +1 -1
  18. package/dist/cjs/src/fetch/index.d.ts +11 -0
  19. package/dist/cjs/src/fetch/index.js +126 -0
  20. package/dist/cjs/src/provider/detection.d.ts +58 -0
  21. package/dist/cjs/src/provider/detection.js +103 -0
  22. package/dist/cjs/src/provider/index.d.ts +6 -0
  23. package/dist/cjs/src/provider/index.js +11 -0
  24. package/dist/cjs/src/queue/EventQueue.d.ts +56 -0
  25. package/dist/cjs/src/{lib/queue → queue}/EventQueue.js +161 -52
  26. package/dist/{esm/src/lib → cjs/src}/queue/type.d.ts +1 -1
  27. package/dist/cjs/src/session/index.d.ts +91 -0
  28. package/dist/cjs/src/session/index.js +126 -0
  29. package/dist/cjs/src/{lib/storage → storage}/built-in/blueprint.js +1 -1
  30. package/dist/cjs/src/types/base.d.ts +35 -1
  31. package/dist/cjs/src/types/events.d.ts +5 -3
  32. package/dist/cjs/src/validators/object.js +0 -2
  33. package/dist/cjs/src/version.d.ts +1 -1
  34. package/dist/cjs/src/version.js +1 -1
  35. package/dist/cjs/src/wagmi/WagmiEventHandler.d.ts +93 -0
  36. package/dist/cjs/src/wagmi/WagmiEventHandler.js +669 -0
  37. package/dist/cjs/src/wagmi/index.d.ts +9 -0
  38. package/dist/cjs/src/wagmi/index.js +12 -0
  39. package/dist/cjs/src/wagmi/types.d.ts +146 -0
  40. package/dist/cjs/src/wagmi/types.js +10 -0
  41. package/dist/cjs/src/wagmi/utils.d.ts +74 -0
  42. package/dist/cjs/src/wagmi/utils.js +198 -0
  43. package/dist/esm/src/FormoAnalytics.d.ts +27 -15
  44. package/dist/esm/src/FormoAnalytics.js +88 -147
  45. package/dist/esm/src/FormoAnalyticsProvider.js +68 -10
  46. package/dist/{cjs/src/lib/consent.d.ts → esm/src/consent/index.d.ts} +1 -1
  47. package/dist/esm/src/{lib/consent.js → consent/index.js} +1 -1
  48. package/dist/esm/src/constants/base.d.ts +0 -2
  49. package/dist/esm/src/constants/base.js +2 -2
  50. package/dist/esm/src/constants/config.js +1 -1
  51. package/dist/esm/src/{lib/event → event}/EventFactory.d.ts +2 -2
  52. package/dist/esm/src/{lib/event → event}/EventFactory.js +9 -9
  53. package/dist/esm/src/{lib/event → event}/EventManager.d.ts +1 -1
  54. package/dist/esm/src/{lib/event → event}/EventManager.js +1 -1
  55. package/dist/esm/src/{lib/event → event}/type.d.ts +1 -1
  56. package/dist/esm/src/{lib/event → event}/utils.d.ts +1 -1
  57. package/dist/esm/src/{lib/event → event}/utils.js +1 -1
  58. package/dist/esm/src/fetch/index.d.ts +11 -0
  59. package/dist/esm/src/fetch/index.js +124 -0
  60. package/dist/esm/src/provider/detection.d.ts +58 -0
  61. package/dist/esm/src/provider/detection.js +98 -0
  62. package/dist/esm/src/provider/index.d.ts +6 -0
  63. package/dist/esm/src/provider/index.js +5 -0
  64. package/dist/esm/src/queue/EventQueue.d.ts +56 -0
  65. package/dist/esm/src/{lib/queue → queue}/EventQueue.js +161 -52
  66. package/dist/{cjs/src/lib → esm/src}/queue/type.d.ts +1 -1
  67. package/dist/esm/src/session/index.d.ts +91 -0
  68. package/dist/esm/src/session/index.js +123 -0
  69. package/dist/esm/src/{lib/storage → storage}/built-in/blueprint.js +1 -1
  70. package/dist/esm/src/types/base.d.ts +35 -1
  71. package/dist/esm/src/types/events.d.ts +5 -3
  72. package/dist/esm/src/validators/object.js +0 -2
  73. package/dist/esm/src/version.d.ts +1 -1
  74. package/dist/esm/src/version.js +1 -1
  75. package/dist/esm/src/wagmi/WagmiEventHandler.d.ts +93 -0
  76. package/dist/esm/src/wagmi/WagmiEventHandler.js +666 -0
  77. package/dist/esm/src/wagmi/index.d.ts +9 -0
  78. package/dist/esm/src/wagmi/index.js +8 -0
  79. package/dist/esm/src/wagmi/types.d.ts +146 -0
  80. package/dist/esm/src/wagmi/types.js +9 -0
  81. package/dist/esm/src/wagmi/utils.d.ts +74 -0
  82. package/dist/esm/src/wagmi/utils.js +192 -0
  83. package/dist/index.umd.min.js +1 -1
  84. package/package.json +23 -4
  85. package/dist/cjs/src/lib/fetch.d.ts +0 -3
  86. package/dist/cjs/src/lib/fetch.js +0 -8
  87. package/dist/cjs/src/lib/index.d.ts +0 -7
  88. package/dist/cjs/src/lib/index.js +0 -28
  89. package/dist/cjs/src/lib/queue/EventQueue.d.ts +0 -34
  90. package/dist/esm/src/lib/fetch.d.ts +0 -3
  91. package/dist/esm/src/lib/fetch.js +0 -3
  92. package/dist/esm/src/lib/index.d.ts +0 -7
  93. package/dist/esm/src/lib/index.js +0 -7
  94. package/dist/esm/src/lib/queue/EventQueue.d.ts +0 -34
  95. /package/dist/cjs/src/{lib/browser → browser}/browsers.d.ts +0 -0
  96. /package/dist/cjs/src/{lib/browser → browser}/browsers.js +0 -0
  97. /package/dist/cjs/src/{lib/event → event}/constants.d.ts +0 -0
  98. /package/dist/cjs/src/{lib/event → event}/constants.js +0 -0
  99. /package/dist/cjs/src/{lib/event → event}/index.d.ts +0 -0
  100. /package/dist/cjs/src/{lib/event → event}/index.js +0 -0
  101. /package/dist/cjs/src/{lib/event → event}/type.js +0 -0
  102. /package/dist/cjs/src/{lib/logger → logger}/Logger.d.ts +0 -0
  103. /package/dist/cjs/src/{lib/logger → logger}/Logger.js +0 -0
  104. /package/dist/cjs/src/{lib/logger → logger}/index.d.ts +0 -0
  105. /package/dist/cjs/src/{lib/logger → logger}/index.js +0 -0
  106. /package/dist/cjs/src/{lib/logger → logger}/type.d.ts +0 -0
  107. /package/dist/cjs/src/{lib/logger → logger}/type.js +0 -0
  108. /package/dist/cjs/src/{lib/queue → queue}/index.d.ts +0 -0
  109. /package/dist/cjs/src/{lib/queue → queue}/index.js +0 -0
  110. /package/dist/cjs/src/{lib/queue → queue}/type.js +0 -0
  111. /package/dist/cjs/src/{lib/ramda → ramda}/internal/_curry1.d.ts +0 -0
  112. /package/dist/cjs/src/{lib/ramda → ramda}/internal/_curry1.js +0 -0
  113. /package/dist/cjs/src/{lib/ramda → ramda}/internal/_curry2.d.ts +0 -0
  114. /package/dist/cjs/src/{lib/ramda → ramda}/internal/_curry2.js +0 -0
  115. /package/dist/cjs/src/{lib/ramda → ramda}/internal/_curry3.d.ts +0 -0
  116. /package/dist/cjs/src/{lib/ramda → ramda}/internal/_curry3.js +0 -0
  117. /package/dist/cjs/src/{lib/ramda → ramda}/internal/_has.d.ts +0 -0
  118. /package/dist/cjs/src/{lib/ramda → ramda}/internal/_has.js +0 -0
  119. /package/dist/cjs/src/{lib/ramda → ramda}/internal/_isObject.d.ts +0 -0
  120. /package/dist/cjs/src/{lib/ramda → ramda}/internal/_isObject.js +0 -0
  121. /package/dist/cjs/src/{lib/ramda → ramda}/internal/_isPlaceholder.d.ts +0 -0
  122. /package/dist/cjs/src/{lib/ramda → ramda}/internal/_isPlaceholder.js +0 -0
  123. /package/dist/cjs/src/{lib/ramda → ramda}/mergeDeepRight.d.ts +0 -0
  124. /package/dist/cjs/src/{lib/ramda → ramda}/mergeDeepRight.js +0 -0
  125. /package/dist/cjs/src/{lib/ramda → ramda}/mergeDeepWithKey.d.ts +0 -0
  126. /package/dist/cjs/src/{lib/ramda → ramda}/mergeDeepWithKey.js +0 -0
  127. /package/dist/cjs/src/{lib/ramda → ramda}/mergeWithKey.d.ts +0 -0
  128. /package/dist/cjs/src/{lib/ramda → ramda}/mergeWithKey.js +0 -0
  129. /package/dist/cjs/src/{lib/storage → storage}/StorageManager.d.ts +0 -0
  130. /package/dist/cjs/src/{lib/storage → storage}/StorageManager.js +0 -0
  131. /package/dist/cjs/src/{lib/storage → storage}/built-in/blueprint.d.ts +0 -0
  132. /package/dist/cjs/src/{lib/storage → storage}/built-in/cookie.d.ts +0 -0
  133. /package/dist/cjs/src/{lib/storage → storage}/built-in/cookie.js +0 -0
  134. /package/dist/cjs/src/{lib/storage → storage}/built-in/memory.d.ts +0 -0
  135. /package/dist/cjs/src/{lib/storage → storage}/built-in/memory.js +0 -0
  136. /package/dist/cjs/src/{lib/storage → storage}/built-in/web.d.ts +0 -0
  137. /package/dist/cjs/src/{lib/storage → storage}/built-in/web.js +0 -0
  138. /package/dist/cjs/src/{lib/storage → storage}/constant.d.ts +0 -0
  139. /package/dist/cjs/src/{lib/storage → storage}/constant.js +0 -0
  140. /package/dist/cjs/src/{lib/storage → storage}/index.d.ts +0 -0
  141. /package/dist/cjs/src/{lib/storage → storage}/index.js +0 -0
  142. /package/dist/cjs/src/{lib/storage → storage}/type.d.ts +0 -0
  143. /package/dist/cjs/src/{lib/storage → storage}/type.js +0 -0
  144. /package/dist/esm/src/{lib/browser → browser}/browsers.d.ts +0 -0
  145. /package/dist/esm/src/{lib/browser → browser}/browsers.js +0 -0
  146. /package/dist/esm/src/{lib/event → event}/constants.d.ts +0 -0
  147. /package/dist/esm/src/{lib/event → event}/constants.js +0 -0
  148. /package/dist/esm/src/{lib/event → event}/index.d.ts +0 -0
  149. /package/dist/esm/src/{lib/event → event}/index.js +0 -0
  150. /package/dist/esm/src/{lib/event → event}/type.js +0 -0
  151. /package/dist/esm/src/{lib/logger → logger}/Logger.d.ts +0 -0
  152. /package/dist/esm/src/{lib/logger → logger}/Logger.js +0 -0
  153. /package/dist/esm/src/{lib/logger → logger}/index.d.ts +0 -0
  154. /package/dist/esm/src/{lib/logger → logger}/index.js +0 -0
  155. /package/dist/esm/src/{lib/logger → logger}/type.d.ts +0 -0
  156. /package/dist/esm/src/{lib/logger → logger}/type.js +0 -0
  157. /package/dist/esm/src/{lib/queue → queue}/index.d.ts +0 -0
  158. /package/dist/esm/src/{lib/queue → queue}/index.js +0 -0
  159. /package/dist/esm/src/{lib/queue → queue}/type.js +0 -0
  160. /package/dist/esm/src/{lib/ramda → ramda}/internal/_curry1.d.ts +0 -0
  161. /package/dist/esm/src/{lib/ramda → ramda}/internal/_curry1.js +0 -0
  162. /package/dist/esm/src/{lib/ramda → ramda}/internal/_curry2.d.ts +0 -0
  163. /package/dist/esm/src/{lib/ramda → ramda}/internal/_curry2.js +0 -0
  164. /package/dist/esm/src/{lib/ramda → ramda}/internal/_curry3.d.ts +0 -0
  165. /package/dist/esm/src/{lib/ramda → ramda}/internal/_curry3.js +0 -0
  166. /package/dist/esm/src/{lib/ramda → ramda}/internal/_has.d.ts +0 -0
  167. /package/dist/esm/src/{lib/ramda → ramda}/internal/_has.js +0 -0
  168. /package/dist/esm/src/{lib/ramda → ramda}/internal/_isObject.d.ts +0 -0
  169. /package/dist/esm/src/{lib/ramda → ramda}/internal/_isObject.js +0 -0
  170. /package/dist/esm/src/{lib/ramda → ramda}/internal/_isPlaceholder.d.ts +0 -0
  171. /package/dist/esm/src/{lib/ramda → ramda}/internal/_isPlaceholder.js +0 -0
  172. /package/dist/esm/src/{lib/ramda → ramda}/mergeDeepRight.d.ts +0 -0
  173. /package/dist/esm/src/{lib/ramda → ramda}/mergeDeepRight.js +0 -0
  174. /package/dist/esm/src/{lib/ramda → ramda}/mergeDeepWithKey.d.ts +0 -0
  175. /package/dist/esm/src/{lib/ramda → ramda}/mergeDeepWithKey.js +0 -0
  176. /package/dist/esm/src/{lib/ramda → ramda}/mergeWithKey.d.ts +0 -0
  177. /package/dist/esm/src/{lib/ramda → ramda}/mergeWithKey.js +0 -0
  178. /package/dist/esm/src/{lib/storage → storage}/StorageManager.d.ts +0 -0
  179. /package/dist/esm/src/{lib/storage → storage}/StorageManager.js +0 -0
  180. /package/dist/esm/src/{lib/storage → storage}/built-in/blueprint.d.ts +0 -0
  181. /package/dist/esm/src/{lib/storage → storage}/built-in/cookie.d.ts +0 -0
  182. /package/dist/esm/src/{lib/storage → storage}/built-in/cookie.js +0 -0
  183. /package/dist/esm/src/{lib/storage → storage}/built-in/memory.d.ts +0 -0
  184. /package/dist/esm/src/{lib/storage → storage}/built-in/memory.js +0 -0
  185. /package/dist/esm/src/{lib/storage → storage}/built-in/web.d.ts +0 -0
  186. /package/dist/esm/src/{lib/storage → storage}/built-in/web.js +0 -0
  187. /package/dist/esm/src/{lib/storage → storage}/constant.d.ts +0 -0
  188. /package/dist/esm/src/{lib/storage → storage}/constant.js +0 -0
  189. /package/dist/esm/src/{lib/storage → storage}/index.d.ts +0 -0
  190. /package/dist/esm/src/{lib/storage → storage}/index.js +0 -0
  191. /package/dist/esm/src/{lib/storage → storage}/type.d.ts +0 -0
  192. /package/dist/esm/src/{lib/storage → storage}/type.js +0 -0
@@ -0,0 +1,56 @@
1
+ import { IFormoEvent, IFormoEventPayload } from "../types";
2
+ import { IEventQueue } from "./type";
3
+ type IFormoEventFlushPayload = IFormoEventPayload & {
4
+ sent_at: string;
5
+ };
6
+ type Options = {
7
+ apiHost: string;
8
+ flushAt?: number;
9
+ flushInterval?: number;
10
+ host?: string;
11
+ retryCount?: number;
12
+ errorHandler?: any;
13
+ maxQueueSize?: number;
14
+ };
15
+ export declare class EventQueue implements IEventQueue {
16
+ private writeKey;
17
+ private apiHost;
18
+ private queue;
19
+ private timer;
20
+ private flushAt;
21
+ private flushIntervalMs;
22
+ private flushed;
23
+ private maxQueueSize;
24
+ private errorHandler;
25
+ private retryCount;
26
+ private pendingFlush;
27
+ private payloadHashes;
28
+ constructor(writeKey: string, options: Options);
29
+ private generateMessageId;
30
+ enqueue(event: IFormoEvent, callback?: (...args: any) => void): Promise<void>;
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;
51
+ private isErrorRetryable;
52
+ private isDuplicate;
53
+ private onPageLeave;
54
+ }
55
+ export {};
56
+ //# sourceMappingURL=EventQueue.d.ts.map
@@ -51,12 +51,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
51
51
  };
52
52
  Object.defineProperty(exports, "__esModule", { value: true });
53
53
  exports.EventQueue = void 0;
54
- var validators_1 = require("../../validators");
55
- var utils_1 = require("../../utils");
54
+ var validators_1 = require("../validators");
55
+ var utils_1 = require("../utils");
56
56
  var logger_1 = require("../logger");
57
- var constants_1 = require("../../constants");
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 err_1, items, sentAt, data, done;
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
- _a.label = 1;
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 2:
238
+ case 1:
229
239
  _a.sent();
230
- _a.label = 3;
231
- case 3: return [3 /*break*/, 5];
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
- done = function (err) {
242
- items.forEach(function (_a) {
243
- var message = _a.message, callback = _a.callback;
244
- return callback(err, message, data);
245
- });
246
- callback(err, data);
247
- };
248
- return [2 /*return*/, (this.pendingFlush = (0, fetch_1.default)("".concat(this.apiHost), {
249
- headers: (0, constants_1.EVENTS_API_REQUEST_HEADER)(this.writeKey),
250
- method: "POST",
251
- body: JSON.stringify(data),
252
- keepalive: true,
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
- done(err);
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
- done(err);
272
- throw err;
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
- //#region Utility functions
279
- EventQueue.prototype.isErrorRetryable = function (error) {
280
- var _a, _b, _c;
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
- // Cannot determine if the request can be retried
285
- if (!(error === null || error === void 0 ? void 0 : error.response))
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 (((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status) >= 500 && ((_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.status) <= 599)
397
+ if (status >= 500 && status <= 599)
289
398
  return true;
290
399
  // Retry if rate limited.
291
- if (((_c = error === null || error === void 0 ? void 0 : error.response) === null || _c === void 0 ? void 0 : _c.status) === 429)
400
+ if (status === 429)
292
401
  return true;
293
402
  return false;
294
403
  };
@@ -1,4 +1,4 @@
1
- import { IFormoEvent } from "../../types";
1
+ import { IFormoEvent } from "../types";
2
2
  export interface IEventQueue {
3
3
  enqueue(event: IFormoEvent, callback?: (...args: any) => void): Promise<void>;
4
4
  flush(callback?: (...args: any) => void): Promise<any>;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Session management for Formo Analytics
3
+ *
4
+ * Handles tracking of detected wallets and identified wallet-address pairs
5
+ * using cookies to maintain state across page loads within a session.
6
+ */
7
+ /**
8
+ * Cookie keys for session tracking
9
+ * NOTE: These values must match the original constants in constants/base.ts
10
+ * to maintain backward compatibility with existing user sessions
11
+ */
12
+ export declare const SESSION_WALLET_DETECTED_KEY = "wallet-detected";
13
+ export declare const SESSION_WALLET_IDENTIFIED_KEY = "wallet-identified";
14
+ /**
15
+ * Interface for session management operations
16
+ */
17
+ export interface IFormoAnalyticsSession {
18
+ /**
19
+ * Check if a wallet has been detected in this session
20
+ * @param rdns The reverse domain name (RDNS) of the wallet provider
21
+ */
22
+ isWalletDetected(rdns: string): boolean;
23
+ /**
24
+ * Mark a wallet as detected in this session
25
+ * @param rdns The reverse domain name (RDNS) of the wallet provider
26
+ */
27
+ markWalletDetected(rdns: string): void;
28
+ /**
29
+ * Check if a wallet-address pair has been identified in this session
30
+ * @param address The wallet address
31
+ * @param rdns The reverse domain name (RDNS) of the wallet provider
32
+ */
33
+ isWalletIdentified(address: string, rdns: string): boolean;
34
+ /**
35
+ * Mark a wallet-address pair as identified in this session
36
+ * @param address The wallet address
37
+ * @param rdns The reverse domain name (RDNS) of the wallet provider
38
+ */
39
+ markWalletIdentified(address: string, rdns: string): void;
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
+ export declare class FormoAnalyticsSession implements IFormoAnalyticsSession {
51
+ /**
52
+ * Generate a unique key for wallet identification tracking
53
+ * Combines address and RDNS to track specific wallet-address combinations
54
+ *
55
+ * @param address The wallet address
56
+ * @param rdns The reverse domain name of the wallet provider
57
+ * @returns A unique identification key
58
+ */
59
+ private generateIdentificationKey;
60
+ /**
61
+ * Check if a wallet provider has been detected in this session
62
+ *
63
+ * @param rdns The reverse domain name of the wallet provider
64
+ * @returns true if the wallet has been detected
65
+ */
66
+ isWalletDetected(rdns: string): boolean;
67
+ /**
68
+ * Mark a wallet provider as detected in this session
69
+ * Prevents duplicate detection events from being emitted
70
+ *
71
+ * @param rdns The reverse domain name of the wallet provider
72
+ */
73
+ markWalletDetected(rdns: string): void;
74
+ /**
75
+ * Check if a specific wallet-address combination has been identified
76
+ *
77
+ * @param address The wallet address
78
+ * @param rdns The reverse domain name of the wallet provider
79
+ * @returns true if this wallet-address pair has been identified
80
+ */
81
+ isWalletIdentified(address: string, rdns: string): boolean;
82
+ /**
83
+ * Mark a wallet-address combination as identified in this session
84
+ * Prevents duplicate identification events from being emitted
85
+ *
86
+ * @param address The wallet address
87
+ * @param rdns The reverse domain name of the wallet provider
88
+ */
89
+ markWalletIdentified(address: string, rdns: string): void;
90
+ }
91
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ /**
3
+ * Session management for Formo Analytics
4
+ *
5
+ * Handles tracking of detected wallets and identified wallet-address pairs
6
+ * using cookies to maintain state across page loads within a session.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.FormoAnalyticsSession = exports.SESSION_WALLET_IDENTIFIED_KEY = exports.SESSION_WALLET_DETECTED_KEY = void 0;
10
+ var storage_1 = require("../storage");
11
+ var logger_1 = require("../logger");
12
+ /**
13
+ * Cookie keys for session tracking
14
+ * NOTE: These values must match the original constants in constants/base.ts
15
+ * to maintain backward compatibility with existing user sessions
16
+ */
17
+ exports.SESSION_WALLET_DETECTED_KEY = "wallet-detected";
18
+ exports.SESSION_WALLET_IDENTIFIED_KEY = "wallet-identified";
19
+ /**
20
+ * Implementation of session management using cookies
21
+ *
22
+ * Tracks:
23
+ * - Detected wallets (by RDNS) - to prevent duplicate detection events
24
+ * - Identified wallet-address pairs - to prevent duplicate identification events
25
+ *
26
+ * Session data expires at end of day (86400 seconds).
27
+ */
28
+ var FormoAnalyticsSession = /** @class */ (function () {
29
+ function FormoAnalyticsSession() {
30
+ }
31
+ /**
32
+ * Generate a unique key for wallet identification tracking
33
+ * Combines address and RDNS to track specific wallet-address combinations
34
+ *
35
+ * @param address The wallet address
36
+ * @param rdns The reverse domain name of the wallet provider
37
+ * @returns A unique identification key
38
+ */
39
+ FormoAnalyticsSession.prototype.generateIdentificationKey = function (address, rdns) {
40
+ // If rdns is missing, use address-only key as fallback for empty identifies
41
+ return rdns ? "".concat(address, ":").concat(rdns) : address;
42
+ };
43
+ /**
44
+ * Check if a wallet provider has been detected in this session
45
+ *
46
+ * @param rdns The reverse domain name of the wallet provider
47
+ * @returns true if the wallet has been detected
48
+ */
49
+ FormoAnalyticsSession.prototype.isWalletDetected = function (rdns) {
50
+ var _a;
51
+ var rdnses = ((_a = (0, storage_1.cookie)().get(exports.SESSION_WALLET_DETECTED_KEY)) === null || _a === void 0 ? void 0 : _a.split(",")) || [];
52
+ return rdnses.includes(rdns);
53
+ };
54
+ /**
55
+ * Mark a wallet provider as detected in this session
56
+ * Prevents duplicate detection events from being emitted
57
+ *
58
+ * @param rdns The reverse domain name of the wallet provider
59
+ */
60
+ FormoAnalyticsSession.prototype.markWalletDetected = function (rdns) {
61
+ var _a;
62
+ var rdnses = ((_a = (0, storage_1.cookie)().get(exports.SESSION_WALLET_DETECTED_KEY)) === null || _a === void 0 ? void 0 : _a.split(",")) || [];
63
+ if (!rdnses.includes(rdns)) {
64
+ rdnses.push(rdns);
65
+ (0, storage_1.cookie)().set(exports.SESSION_WALLET_DETECTED_KEY, rdnses.join(","), {
66
+ // Expires by the end of the day
67
+ expires: new Date(Date.now() + 86400 * 1000).toUTCString(),
68
+ path: "/",
69
+ });
70
+ }
71
+ };
72
+ /**
73
+ * Check if a specific wallet-address combination has been identified
74
+ *
75
+ * @param address The wallet address
76
+ * @param rdns The reverse domain name of the wallet provider
77
+ * @returns true if this wallet-address pair has been identified
78
+ */
79
+ FormoAnalyticsSession.prototype.isWalletIdentified = function (address, rdns) {
80
+ var identifiedKey = this.generateIdentificationKey(address, rdns);
81
+ var cookieValue = (0, storage_1.cookie)().get(exports.SESSION_WALLET_IDENTIFIED_KEY);
82
+ var identifiedWallets = (cookieValue === null || cookieValue === void 0 ? void 0 : cookieValue.split(",")) || [];
83
+ var isIdentified = identifiedWallets.includes(identifiedKey);
84
+ logger_1.logger.debug("Session: Checking wallet identification", {
85
+ identifiedKey: identifiedKey,
86
+ isIdentified: isIdentified,
87
+ hasRdns: !!rdns,
88
+ });
89
+ return isIdentified;
90
+ };
91
+ /**
92
+ * Mark a wallet-address combination as identified in this session
93
+ * Prevents duplicate identification events from being emitted
94
+ *
95
+ * @param address The wallet address
96
+ * @param rdns The reverse domain name of the wallet provider
97
+ */
98
+ FormoAnalyticsSession.prototype.markWalletIdentified = function (address, rdns) {
99
+ var _a;
100
+ var identifiedKey = this.generateIdentificationKey(address, rdns);
101
+ var identifiedWallets = ((_a = (0, storage_1.cookie)().get(exports.SESSION_WALLET_IDENTIFIED_KEY)) === null || _a === void 0 ? void 0 : _a.split(",")) || [];
102
+ if (!identifiedWallets.includes(identifiedKey)) {
103
+ identifiedWallets.push(identifiedKey);
104
+ var newValue = identifiedWallets.join(",");
105
+ (0, storage_1.cookie)().set(exports.SESSION_WALLET_IDENTIFIED_KEY, newValue, {
106
+ // Expires by the end of the day
107
+ expires: new Date(Date.now() + 86400 * 1000).toUTCString(),
108
+ path: "/",
109
+ });
110
+ logger_1.logger.debug("Session: Marked wallet as identified", {
111
+ identifiedKey: identifiedKey,
112
+ hasRdns: !!rdns,
113
+ });
114
+ }
115
+ else {
116
+ logger_1.logger.info("Session: Wallet already marked as identified", {
117
+ identifiedKey: identifiedKey,
118
+ existingWallets: identifiedWallets,
119
+ hasRdns: !!rdns,
120
+ });
121
+ }
122
+ };
123
+ return FormoAnalyticsSession;
124
+ }());
125
+ exports.FormoAnalyticsSession = FormoAnalyticsSession;
126
+ //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  var constant_1 = require("../constant");
4
- var hash_1 = require("../../../utils/hash");
4
+ var hash_1 = require("../../utils/hash");
5
5
  var StorageBlueprint = /** @class */ (function () {
6
6
  function StorageBlueprint(writeKey) {
7
7
  this.writeKey = writeKey;
@@ -1,4 +1,4 @@
1
- import { LogLevel } from "../lib";
1
+ import { LogLevel } from "../logger";
2
2
  import { IFormoEventContext, IFormoEventProperties, SignatureStatus, TransactionStatus } from "./events";
3
3
  import { EIP1193Provider } from "./provider";
4
4
  import { ReactNode } from "react";
@@ -9,6 +9,7 @@ export type ValidInputTypes = Uint8Array | bigint | string | number | boolean;
9
9
  export interface IFormoAnalytics {
10
10
  page(category?: string, name?: string, properties?: IFormoEventProperties, context?: IFormoEventContext, callback?: (...args: unknown[]) => void): Promise<void>;
11
11
  reset(): void;
12
+ cleanup(): void;
12
13
  detect(params: {
13
14
  rdns: string;
14
15
  providerName: string;
@@ -40,6 +41,8 @@ export interface IFormoAnalytics {
40
41
  to?: string;
41
42
  value?: string;
42
43
  transactionHash?: string;
44
+ function_name?: string;
45
+ function_args?: Record<string, unknown>;
43
46
  }, properties?: IFormoEventProperties, context?: IFormoEventContext, callback?: (...args: unknown[]) => void): Promise<void>;
44
47
  identify(params: {
45
48
  address: Address;
@@ -112,6 +115,23 @@ export interface ReferralOptions {
112
115
  */
113
116
  pathPattern?: string;
114
117
  }
118
+ /**
119
+ * Configuration options for Wagmi integration
120
+ * Allows the SDK to hook into Wagmi v2 wallet events instead of wrapping EIP-1193 providers
121
+ */
122
+ export interface WagmiOptions {
123
+ /**
124
+ * Wagmi config instance from createConfig()
125
+ * The SDK will subscribe to this config's state changes to track wallet events
126
+ */
127
+ config: any;
128
+ /**
129
+ * Optional QueryClient instance from @tanstack/react-query
130
+ * Required for tracking signature and transaction events via mutation cache
131
+ * If not provided, only connection/disconnection/chain events will be tracked
132
+ */
133
+ queryClient?: any;
134
+ }
115
135
  export interface Options {
116
136
  provider?: EIP1193Provider;
117
137
  tracking?: boolean | TrackingOptions;
@@ -123,6 +143,14 @@ export interface Options {
123
143
  * @default true
124
144
  */
125
145
  autocapture?: boolean | AutocaptureOptions;
146
+ /**
147
+ * Wagmi integration configuration
148
+ * When provided, the SDK will hook into Wagmi's event system instead of wrapping EIP-1193 providers
149
+ * This replaces the default provider tracking with Wagmi's config.subscribe() and MutationCache
150
+ * @requires wagmi@>=2.0.0
151
+ * @requires @tanstack/react-query@>=5.0.0 (for mutation tracking)
152
+ */
153
+ wagmi?: WagmiOptions;
126
154
  /**
127
155
  * Custom API host for sending events through your own domain to bypass ad blockers
128
156
  * - If not provided, events are sent directly to events.formo.so
@@ -146,6 +174,12 @@ export interface Options {
146
174
  * @example { queryParams: ["via"], pathPattern: "/r/([^/]+)" }
147
175
  */
148
176
  referral?: ReferralOptions;
177
+ /**
178
+ * Optional error handler for analytics network failures.
179
+ * Called when event flush fails after all retries.
180
+ * If not provided, errors are silently swallowed.
181
+ */
182
+ errorHandler?: (err: Error) => void;
149
183
  ready?: (formo: IFormoAnalytics) => void;
150
184
  }
151
185
  export interface FormoAnalyticsProviderProps {
@@ -59,10 +59,12 @@ export interface TransactionAPIEvent {
59
59
  status: TransactionStatus;
60
60
  chainId: ChainID;
61
61
  address: Address;
62
- data: string;
63
- to: string;
64
- value: string;
62
+ data?: string;
63
+ to?: string;
64
+ value?: string;
65
65
  transactionHash?: string;
66
+ function_name?: string;
67
+ function_args?: Record<string, unknown>;
66
68
  }
67
69
  export interface SignatureAPIEvent {
68
70
  type: "signature";
@@ -2,8 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isNullish = void 0;
4
4
  var isNullish = function (item) {
5
- // Using "null" value intentionally for validation
6
- // eslint-disable-next-line no-null/no-null
7
5
  return item === undefined || item === null;
8
6
  };
9
7
  exports.isNullish = isNullish;
@@ -1,2 +1,2 @@
1
- export declare const version = "1.25.0";
1
+ export declare const version = "1.27.0";
2
2
  //# sourceMappingURL=version.d.ts.map