@formo/analytics 1.23.0 → 1.25.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 CHANGED
@@ -17,7 +17,7 @@
17
17
 
18
18
  ## Installation
19
19
 
20
- The Formo Web SDK is a Javascript library that allows you to track and analyze user interactions on your dapp.
20
+ The Formo Web SDK is a Javascript library that allows you to track user event data from your website and app.
21
21
 
22
22
  You can install Formo on:
23
23
  - [Websites](https://docs.formo.so/install#website)
@@ -126,7 +126,7 @@ var FormoAnalytics = /** @class */ (function () {
126
126
  retryCount: options.retryCount,
127
127
  maxQueueSize: options.maxQueueSize,
128
128
  flushInterval: options.flushInterval,
129
- }));
129
+ }), options);
130
130
  // Check consent status on initialization
131
131
  if (this.hasOptedOutTracking()) {
132
132
  lib_1.logger.info("User has previously opted out of tracking");
@@ -1,6 +1,9 @@
1
- import { Address, APIEvent, ChainID, IFormoEvent, IFormoEventContext, IFormoEventProperties, Nullable, SignatureStatus, TransactionStatus } from "../../types";
1
+ import { Address, APIEvent, ChainID, IFormoEvent, IFormoEventContext, IFormoEventProperties, Nullable, Options, SignatureStatus, TransactionStatus } from "../../types";
2
2
  import { IEventFactory } from "./type";
3
3
  declare class EventFactory implements IEventFactory {
4
+ private options?;
5
+ private compiledPathPattern?;
6
+ constructor(options?: Options);
4
7
  private getTimezone;
5
8
  private getLocation;
6
9
  private getLanguage;
@@ -8,6 +11,7 @@ declare class EventFactory implements IEventFactory {
8
11
  private extractUTMParameters;
9
12
  private extractReferralParameter;
10
13
  private getTrafficSources;
14
+ private getScreen;
11
15
  private generateContext;
12
16
  /**
13
17
  * Add any missing default page properties using values from options and defaults
@@ -59,13 +59,14 @@ var validators_1 = require("../../validators");
59
59
  var logger_1 = require("../logger");
60
60
  var mergeDeepRight_1 = __importDefault(require("../ramda/mergeDeepRight"));
61
61
  var storage_1 = require("../storage");
62
- var version_1 = require("../version");
62
+ var version_1 = require("../../version");
63
63
  var constants_2 = require("./constants");
64
64
  var utils_2 = require("./utils");
65
65
  var browsers_1 = require("../browser/browsers");
66
66
  var EventFactory = /** @class */ (function () {
67
- function EventFactory() {
67
+ function EventFactory(options) {
68
68
  var _this = this;
69
+ var _a;
69
70
  this.extractUTMParameters = function (url) {
70
71
  var result = {
71
72
  utm_campaign: "",
@@ -87,14 +88,33 @@ var EventFactory = /** @class */ (function () {
87
88
  return result;
88
89
  };
89
90
  this.extractReferralParameter = function (urlObj) {
90
- var _a;
91
- var referralParams = ["ref", "referral", "refcode"];
91
+ var _a, _b, _c;
92
+ // Strategy: Check query params first, then check path pattern if configured
93
+ // Query params logic:
94
+ // - If no referral config exists → use defaults
95
+ // - If referral config exists but queryParams is undefined → use defaults
96
+ // - If referral config exists with queryParams → use those
97
+ var defaultParams = ["ref", "referral", "refcode"];
98
+ var referralParams = !((_a = _this.options) === null || _a === void 0 ? void 0 : _a.referral)
99
+ ? defaultParams // No referral config at all → use defaults
100
+ : ((_b = _this.options.referral.queryParams) !== null && _b !== void 0 ? _b : defaultParams); // Has config → use queryParams or defaults
101
+ // Check query parameters (if any configured)
92
102
  for (var _i = 0, referralParams_1 = referralParams; _i < referralParams_1.length; _i++) {
93
103
  var param = referralParams_1[_i];
94
- var value = (_a = urlObj.searchParams.get(param)) === null || _a === void 0 ? void 0 : _a.trim();
104
+ var value = (_c = urlObj.searchParams.get(param)) === null || _c === void 0 ? void 0 : _c.trim();
95
105
  if (value)
96
106
  return value;
97
107
  }
108
+ // Check URL path pattern if configured
109
+ if (_this.compiledPathPattern) {
110
+ var pathname = urlObj.pathname;
111
+ var match = pathname.match(_this.compiledPathPattern);
112
+ if (match && match[1]) {
113
+ var referralCode = match[1].trim();
114
+ if (referralCode)
115
+ return referralCode;
116
+ }
117
+ }
98
118
  return "";
99
119
  };
100
120
  this.getTrafficSources = function (url) {
@@ -136,7 +156,8 @@ var EventFactory = /** @class */ (function () {
136
156
  * @param options API options
137
157
  */
138
158
  this.getPageProperties = function (properties) {
139
- var pageProps = properties;
159
+ // Create a copy to avoid mutating the original properties object
160
+ var pageProps = __assign({}, properties);
140
161
  if ((0, validators_1.isUndefined)(pageProps.url)) {
141
162
  pageProps.url = new URL(globalThis.location.href).href;
142
163
  }
@@ -146,8 +167,36 @@ var EventFactory = /** @class */ (function () {
146
167
  if ((0, validators_1.isUndefined)(pageProps.hash)) {
147
168
  pageProps.hash = globalThis.location.hash;
148
169
  }
170
+ // Add query string without the '?' prefix
171
+ if ((0, validators_1.isUndefined)(pageProps.query)) {
172
+ pageProps.query = globalThis.location.search.slice(1);
173
+ }
174
+ // Parse query parameters and add as individual properties (don't overwrite existing)
175
+ // Skip fields that are already captured in context or are semantic event properties
176
+ try {
177
+ var urlObj = new URL(globalThis.location.href);
178
+ urlObj.searchParams.forEach(function (value, key) {
179
+ // Only add if the property doesn't already exist and is not excluded
180
+ if ((0, validators_1.isUndefined)(pageProps[key]) && !constants_2.PAGE_PROPERTIES_EXCLUDED_FIELDS.has(key)) {
181
+ pageProps[key] = value;
182
+ }
183
+ });
184
+ }
185
+ catch (error) {
186
+ logger_1.logger.error("Error parsing query parameters for page properties:", error);
187
+ }
149
188
  return pageProps;
150
189
  };
190
+ this.options = options;
191
+ // Compile regex pattern once for better performance
192
+ if ((_a = options === null || options === void 0 ? void 0 : options.referral) === null || _a === void 0 ? void 0 : _a.pathPattern) {
193
+ try {
194
+ this.compiledPathPattern = new RegExp(options.referral.pathPattern);
195
+ }
196
+ catch (error) {
197
+ logger_1.logger.warn("Invalid referral path pattern: ".concat(options.referral.pathPattern, ". Error: ").concat(error));
198
+ }
199
+ }
151
200
  }
152
201
  EventFactory.prototype.getTimezone = function () {
153
202
  try {
@@ -184,22 +233,45 @@ var EventFactory = /** @class */ (function () {
184
233
  EventFactory.prototype.getLibraryVersion = function () {
185
234
  return version_1.version;
186
235
  };
236
+ // Get screen dimensions and pixel density
237
+ // Returns safe defaults if any error occurs to ensure event creation continues
238
+ EventFactory.prototype.getScreen = function () {
239
+ var _a, _b;
240
+ var safeDefaults = {
241
+ screen_width: 0,
242
+ screen_height: 0,
243
+ screen_density: 1,
244
+ viewport_width: 0,
245
+ viewport_height: 0,
246
+ };
247
+ try {
248
+ return {
249
+ screen_width: ((_a = globalThis.screen) === null || _a === void 0 ? void 0 : _a.width) || 0,
250
+ screen_height: ((_b = globalThis.screen) === null || _b === void 0 ? void 0 : _b.height) || 0,
251
+ screen_density: globalThis.devicePixelRatio || 1,
252
+ viewport_width: globalThis.innerWidth || 0,
253
+ viewport_height: globalThis.innerHeight || 0,
254
+ };
255
+ }
256
+ catch (error) {
257
+ logger_1.logger.error("Error resolving screen properties:", error);
258
+ return safeDefaults;
259
+ }
260
+ };
187
261
  // Contextual fields that are automatically collected and populated by the Formo SDK
188
262
  EventFactory.prototype.generateContext = function (context) {
189
263
  return __awaiter(this, void 0, void 0, function () {
190
- var path, browserName, language, timezone, location, library_version, defaultContext, mergedContext;
264
+ var browserName, language, timezone, location, library_version, defaultContext, mergedContext;
191
265
  return __generator(this, function (_a) {
192
266
  switch (_a.label) {
193
- case 0:
194
- path = globalThis.location.pathname;
195
- return [4 /*yield*/, (0, browsers_1.detectBrowser)()];
267
+ case 0: return [4 /*yield*/, (0, browsers_1.detectBrowser)()];
196
268
  case 1:
197
269
  browserName = _a.sent();
198
270
  language = this.getLanguage();
199
271
  timezone = this.getTimezone();
200
272
  location = this.getLocation();
201
273
  library_version = this.getLibraryVersion();
202
- defaultContext = __assign(__assign({ user_agent: globalThis.navigator.userAgent, locale: language, timezone: timezone, location: location }, this.getTrafficSources(globalThis.location.href)), { page_path: path, page_title: document.title, page_url: globalThis.location.href, library_name: "Formo Web SDK", library_version: library_version, browser: browserName });
274
+ defaultContext = __assign(__assign(__assign({ user_agent: globalThis.navigator.userAgent, locale: language, timezone: timezone, location: location }, this.getTrafficSources(globalThis.location.href)), { page_title: document.title, page_url: globalThis.location.href, library_name: "Formo Web SDK", library_version: library_version, browser: browserName }), this.getScreen());
203
275
  mergedContext = (0, mergeDeepRight_1.default)(defaultContext, context || {});
204
276
  return [2 /*return*/, mergedContext];
205
277
  }
@@ -247,7 +319,7 @@ var EventFactory = /** @class */ (function () {
247
319
  return __awaiter(this, void 0, void 0, function () {
248
320
  var props, pageEvent;
249
321
  return __generator(this, function (_a) {
250
- props = properties !== null && properties !== void 0 ? properties : {};
322
+ props = __assign({}, (properties !== null && properties !== void 0 ? properties : {}));
251
323
  props.category = category;
252
324
  props.name = name;
253
325
  props = this.getPageProperties(props);
@@ -1,4 +1,4 @@
1
- import { Address, APIEvent } from "../../types";
1
+ import { Address, APIEvent, Options } from "../../types";
2
2
  import { IEventQueue } from "../queue";
3
3
  import { IEventFactory, IEventManager } from "./type";
4
4
  /**
@@ -10,8 +10,9 @@ declare class EventManager implements IEventManager {
10
10
  /**
11
11
  *
12
12
  * @param eventQueue Event queue instance
13
+ * @param options Optional configuration (referral parsing, etc.)
13
14
  */
14
- constructor(eventQueue: IEventQueue);
15
+ constructor(eventQueue: IEventQueue, options?: Options);
15
16
  /**
16
17
  * Consumes a new incoming event
17
18
  * @param event Incoming event data
@@ -58,10 +58,11 @@ var EventManager = /** @class */ (function () {
58
58
  /**
59
59
  *
60
60
  * @param eventQueue Event queue instance
61
+ * @param options Optional configuration (referral parsing, etc.)
61
62
  */
62
- function EventManager(eventQueue) {
63
+ function EventManager(eventQueue, options) {
63
64
  this.eventQueue = eventQueue;
64
- this.eventFactory = new EventFactory_1.EventFactory();
65
+ this.eventFactory = new EventFactory_1.EventFactory(options);
65
66
  }
66
67
  /**
67
68
  * Consumes a new incoming event
@@ -1,4 +1,11 @@
1
1
  declare const CHANNEL = "web";
2
2
  declare const VERSION = "0";
3
- export { CHANNEL, VERSION };
3
+ /**
4
+ * Fields that should be excluded from page event properties parsing
5
+ * These are either:
6
+ * - Already captured in event context (UTM params, referral params)
7
+ * - Semantic event properties that should not be overridden by URL params
8
+ */
9
+ declare const PAGE_PROPERTIES_EXCLUDED_FIELDS: Set<string>;
10
+ export { CHANNEL, VERSION, PAGE_PROPERTIES_EXCLUDED_FIELDS };
4
11
  //# sourceMappingURL=constants.d.ts.map
@@ -1,8 +1,34 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VERSION = exports.CHANNEL = void 0;
3
+ exports.PAGE_PROPERTIES_EXCLUDED_FIELDS = exports.VERSION = exports.CHANNEL = void 0;
4
4
  var CHANNEL = "web";
5
5
  exports.CHANNEL = CHANNEL;
6
6
  var VERSION = "0";
7
7
  exports.VERSION = VERSION;
8
+ /**
9
+ * Fields that should be excluded from page event properties parsing
10
+ * These are either:
11
+ * - Already captured in event context (UTM params, referral params)
12
+ * - Semantic event properties that should not be overridden by URL params
13
+ */
14
+ var PAGE_PROPERTIES_EXCLUDED_FIELDS = new Set([
15
+ // Context fields (already captured in event context)
16
+ 'utm_source',
17
+ 'utm_medium',
18
+ 'utm_campaign',
19
+ 'utm_term',
20
+ 'utm_content',
21
+ 'ref',
22
+ 'referral',
23
+ 'refcode',
24
+ 'referrer',
25
+ // Semantic event properties (should not be overridden by URL params)
26
+ 'category',
27
+ 'name',
28
+ 'url',
29
+ 'path',
30
+ 'hash',
31
+ 'query',
32
+ ]);
33
+ exports.PAGE_PROPERTIES_EXCLUDED_FIELDS = PAGE_PROPERTIES_EXCLUDED_FIELDS;
8
34
  //# sourceMappingURL=constants.js.map
@@ -94,6 +94,24 @@ export interface AutocaptureOptions {
94
94
  */
95
95
  chain?: boolean;
96
96
  }
97
+ /**
98
+ * Configuration options for referral parameter parsing
99
+ */
100
+ export interface ReferralOptions {
101
+ /**
102
+ * Custom query parameter names to check for referral codes
103
+ * @default ["ref", "referral", "refcode"]
104
+ * @example ["via", "referrer", "source"] - will check ?via=CODE, ?referrer=CODE, ?source=CODE
105
+ */
106
+ queryParams?: string[];
107
+ /**
108
+ * URL path pattern to extract referral code from
109
+ * Should be a regex string that matches the path segment containing the referral code
110
+ * The first capture group will be used as the referral code
111
+ * @example "/r/([^/]+)" - will extract "01K17FKB" from "https://glider.fi/r/01K17FKB"
112
+ */
113
+ pathPattern?: string;
114
+ }
97
115
  export interface Options {
98
116
  provider?: EIP1193Provider;
99
117
  tracking?: boolean | TrackingOptions;
@@ -122,6 +140,12 @@ export interface Options {
122
140
  enabled?: boolean;
123
141
  levels?: LogLevel[];
124
142
  };
143
+ /**
144
+ * Configuration for referral parameter parsing from URLs
145
+ * Allows customizing how referral codes are detected from query parameters and URL paths
146
+ * @example { queryParams: ["via"], pathPattern: "/r/([^/]+)" }
147
+ */
148
+ referral?: ReferralOptions;
125
149
  ready?: (formo: IFormoAnalytics) => void;
126
150
  }
127
151
  export interface FormoAnalyticsProviderProps {
@@ -0,0 +1,2 @@
1
+ export declare const version = "1.25.0";
2
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.version = void 0;
4
+ // This file is auto-generated by scripts/update-version.js during npm version
5
+ // Do not edit manually - it will be overwritten
6
+ exports.version = '1.25.0';
7
+ //# sourceMappingURL=version.js.map
@@ -123,7 +123,7 @@ var FormoAnalytics = /** @class */ (function () {
123
123
  retryCount: options.retryCount,
124
124
  maxQueueSize: options.maxQueueSize,
125
125
  flushInterval: options.flushInterval,
126
- }));
126
+ }), options);
127
127
  // Check consent status on initialization
128
128
  if (this.hasOptedOutTracking()) {
129
129
  logger.info("User has previously opted out of tracking");
@@ -1,6 +1,9 @@
1
- import { Address, APIEvent, ChainID, IFormoEvent, IFormoEventContext, IFormoEventProperties, Nullable, SignatureStatus, TransactionStatus } from "../../types";
1
+ import { Address, APIEvent, ChainID, IFormoEvent, IFormoEventContext, IFormoEventProperties, Nullable, Options, SignatureStatus, TransactionStatus } from "../../types";
2
2
  import { IEventFactory } from "./type";
3
3
  declare class EventFactory implements IEventFactory {
4
+ private options?;
5
+ private compiledPathPattern?;
6
+ constructor(options?: Options);
4
7
  private getTimezone;
5
8
  private getLocation;
6
9
  private getLanguage;
@@ -8,6 +11,7 @@ declare class EventFactory implements IEventFactory {
8
11
  private extractUTMParameters;
9
12
  private extractReferralParameter;
10
13
  private getTrafficSources;
14
+ private getScreen;
11
15
  private generateContext;
12
16
  /**
13
17
  * Add any missing default page properties using values from options and defaults
@@ -53,13 +53,14 @@ import { isUndefined } from "../../validators";
53
53
  import { logger } from "../logger";
54
54
  import mergeDeepRight from "../ramda/mergeDeepRight";
55
55
  import { session } from "../storage";
56
- import { version } from "../version";
57
- import { CHANNEL, VERSION } from "./constants";
56
+ import { version } from "../../version";
57
+ import { CHANNEL, VERSION, PAGE_PROPERTIES_EXCLUDED_FIELDS } from "./constants";
58
58
  import { generateAnonymousId } from "./utils";
59
59
  import { detectBrowser } from "../browser/browsers";
60
60
  var EventFactory = /** @class */ (function () {
61
- function EventFactory() {
61
+ function EventFactory(options) {
62
62
  var _this = this;
63
+ var _a;
63
64
  this.extractUTMParameters = function (url) {
64
65
  var result = {
65
66
  utm_campaign: "",
@@ -81,14 +82,33 @@ var EventFactory = /** @class */ (function () {
81
82
  return result;
82
83
  };
83
84
  this.extractReferralParameter = function (urlObj) {
84
- var _a;
85
- var referralParams = ["ref", "referral", "refcode"];
85
+ var _a, _b, _c;
86
+ // Strategy: Check query params first, then check path pattern if configured
87
+ // Query params logic:
88
+ // - If no referral config exists → use defaults
89
+ // - If referral config exists but queryParams is undefined → use defaults
90
+ // - If referral config exists with queryParams → use those
91
+ var defaultParams = ["ref", "referral", "refcode"];
92
+ var referralParams = !((_a = _this.options) === null || _a === void 0 ? void 0 : _a.referral)
93
+ ? defaultParams // No referral config at all → use defaults
94
+ : ((_b = _this.options.referral.queryParams) !== null && _b !== void 0 ? _b : defaultParams); // Has config → use queryParams or defaults
95
+ // Check query parameters (if any configured)
86
96
  for (var _i = 0, referralParams_1 = referralParams; _i < referralParams_1.length; _i++) {
87
97
  var param = referralParams_1[_i];
88
- var value = (_a = urlObj.searchParams.get(param)) === null || _a === void 0 ? void 0 : _a.trim();
98
+ var value = (_c = urlObj.searchParams.get(param)) === null || _c === void 0 ? void 0 : _c.trim();
89
99
  if (value)
90
100
  return value;
91
101
  }
102
+ // Check URL path pattern if configured
103
+ if (_this.compiledPathPattern) {
104
+ var pathname = urlObj.pathname;
105
+ var match = pathname.match(_this.compiledPathPattern);
106
+ if (match && match[1]) {
107
+ var referralCode = match[1].trim();
108
+ if (referralCode)
109
+ return referralCode;
110
+ }
111
+ }
92
112
  return "";
93
113
  };
94
114
  this.getTrafficSources = function (url) {
@@ -130,7 +150,8 @@ var EventFactory = /** @class */ (function () {
130
150
  * @param options API options
131
151
  */
132
152
  this.getPageProperties = function (properties) {
133
- var pageProps = properties;
153
+ // Create a copy to avoid mutating the original properties object
154
+ var pageProps = __assign({}, properties);
134
155
  if (isUndefined(pageProps.url)) {
135
156
  pageProps.url = new URL(globalThis.location.href).href;
136
157
  }
@@ -140,8 +161,36 @@ var EventFactory = /** @class */ (function () {
140
161
  if (isUndefined(pageProps.hash)) {
141
162
  pageProps.hash = globalThis.location.hash;
142
163
  }
164
+ // Add query string without the '?' prefix
165
+ if (isUndefined(pageProps.query)) {
166
+ pageProps.query = globalThis.location.search.slice(1);
167
+ }
168
+ // Parse query parameters and add as individual properties (don't overwrite existing)
169
+ // Skip fields that are already captured in context or are semantic event properties
170
+ try {
171
+ var urlObj = new URL(globalThis.location.href);
172
+ urlObj.searchParams.forEach(function (value, key) {
173
+ // Only add if the property doesn't already exist and is not excluded
174
+ if (isUndefined(pageProps[key]) && !PAGE_PROPERTIES_EXCLUDED_FIELDS.has(key)) {
175
+ pageProps[key] = value;
176
+ }
177
+ });
178
+ }
179
+ catch (error) {
180
+ logger.error("Error parsing query parameters for page properties:", error);
181
+ }
143
182
  return pageProps;
144
183
  };
184
+ this.options = options;
185
+ // Compile regex pattern once for better performance
186
+ if ((_a = options === null || options === void 0 ? void 0 : options.referral) === null || _a === void 0 ? void 0 : _a.pathPattern) {
187
+ try {
188
+ this.compiledPathPattern = new RegExp(options.referral.pathPattern);
189
+ }
190
+ catch (error) {
191
+ logger.warn("Invalid referral path pattern: ".concat(options.referral.pathPattern, ". Error: ").concat(error));
192
+ }
193
+ }
145
194
  }
146
195
  EventFactory.prototype.getTimezone = function () {
147
196
  try {
@@ -178,22 +227,45 @@ var EventFactory = /** @class */ (function () {
178
227
  EventFactory.prototype.getLibraryVersion = function () {
179
228
  return version;
180
229
  };
230
+ // Get screen dimensions and pixel density
231
+ // Returns safe defaults if any error occurs to ensure event creation continues
232
+ EventFactory.prototype.getScreen = function () {
233
+ var _a, _b;
234
+ var safeDefaults = {
235
+ screen_width: 0,
236
+ screen_height: 0,
237
+ screen_density: 1,
238
+ viewport_width: 0,
239
+ viewport_height: 0,
240
+ };
241
+ try {
242
+ return {
243
+ screen_width: ((_a = globalThis.screen) === null || _a === void 0 ? void 0 : _a.width) || 0,
244
+ screen_height: ((_b = globalThis.screen) === null || _b === void 0 ? void 0 : _b.height) || 0,
245
+ screen_density: globalThis.devicePixelRatio || 1,
246
+ viewport_width: globalThis.innerWidth || 0,
247
+ viewport_height: globalThis.innerHeight || 0,
248
+ };
249
+ }
250
+ catch (error) {
251
+ logger.error("Error resolving screen properties:", error);
252
+ return safeDefaults;
253
+ }
254
+ };
181
255
  // Contextual fields that are automatically collected and populated by the Formo SDK
182
256
  EventFactory.prototype.generateContext = function (context) {
183
257
  return __awaiter(this, void 0, void 0, function () {
184
- var path, browserName, language, timezone, location, library_version, defaultContext, mergedContext;
258
+ var browserName, language, timezone, location, library_version, defaultContext, mergedContext;
185
259
  return __generator(this, function (_a) {
186
260
  switch (_a.label) {
187
- case 0:
188
- path = globalThis.location.pathname;
189
- return [4 /*yield*/, detectBrowser()];
261
+ case 0: return [4 /*yield*/, detectBrowser()];
190
262
  case 1:
191
263
  browserName = _a.sent();
192
264
  language = this.getLanguage();
193
265
  timezone = this.getTimezone();
194
266
  location = this.getLocation();
195
267
  library_version = this.getLibraryVersion();
196
- defaultContext = __assign(__assign({ user_agent: globalThis.navigator.userAgent, locale: language, timezone: timezone, location: location }, this.getTrafficSources(globalThis.location.href)), { page_path: path, page_title: document.title, page_url: globalThis.location.href, library_name: "Formo Web SDK", library_version: library_version, browser: browserName });
268
+ defaultContext = __assign(__assign(__assign({ user_agent: globalThis.navigator.userAgent, locale: language, timezone: timezone, location: location }, this.getTrafficSources(globalThis.location.href)), { page_title: document.title, page_url: globalThis.location.href, library_name: "Formo Web SDK", library_version: library_version, browser: browserName }), this.getScreen());
197
269
  mergedContext = mergeDeepRight(defaultContext, context || {});
198
270
  return [2 /*return*/, mergedContext];
199
271
  }
@@ -241,7 +313,7 @@ var EventFactory = /** @class */ (function () {
241
313
  return __awaiter(this, void 0, void 0, function () {
242
314
  var props, pageEvent;
243
315
  return __generator(this, function (_a) {
244
- props = properties !== null && properties !== void 0 ? properties : {};
316
+ props = __assign({}, (properties !== null && properties !== void 0 ? properties : {}));
245
317
  props.category = category;
246
318
  props.name = name;
247
319
  props = this.getPageProperties(props);
@@ -1,4 +1,4 @@
1
- import { Address, APIEvent } from "../../types";
1
+ import { Address, APIEvent, Options } from "../../types";
2
2
  import { IEventQueue } from "../queue";
3
3
  import { IEventFactory, IEventManager } from "./type";
4
4
  /**
@@ -10,8 +10,9 @@ declare class EventManager implements IEventManager {
10
10
  /**
11
11
  *
12
12
  * @param eventQueue Event queue instance
13
+ * @param options Optional configuration (referral parsing, etc.)
13
14
  */
14
- constructor(eventQueue: IEventQueue);
15
+ constructor(eventQueue: IEventQueue, options?: Options);
15
16
  /**
16
17
  * Consumes a new incoming event
17
18
  * @param event Incoming event data
@@ -55,10 +55,11 @@ var EventManager = /** @class */ (function () {
55
55
  /**
56
56
  *
57
57
  * @param eventQueue Event queue instance
58
+ * @param options Optional configuration (referral parsing, etc.)
58
59
  */
59
- function EventManager(eventQueue) {
60
+ function EventManager(eventQueue, options) {
60
61
  this.eventQueue = eventQueue;
61
- this.eventFactory = new EventFactory();
62
+ this.eventFactory = new EventFactory(options);
62
63
  }
63
64
  /**
64
65
  * Consumes a new incoming event
@@ -1,4 +1,11 @@
1
1
  declare const CHANNEL = "web";
2
2
  declare const VERSION = "0";
3
- export { CHANNEL, VERSION };
3
+ /**
4
+ * Fields that should be excluded from page event properties parsing
5
+ * These are either:
6
+ * - Already captured in event context (UTM params, referral params)
7
+ * - Semantic event properties that should not be overridden by URL params
8
+ */
9
+ declare const PAGE_PROPERTIES_EXCLUDED_FIELDS: Set<string>;
10
+ export { CHANNEL, VERSION, PAGE_PROPERTIES_EXCLUDED_FIELDS };
4
11
  //# sourceMappingURL=constants.d.ts.map
@@ -1,4 +1,29 @@
1
1
  var CHANNEL = "web";
2
2
  var VERSION = "0";
3
- export { CHANNEL, VERSION };
3
+ /**
4
+ * Fields that should be excluded from page event properties parsing
5
+ * These are either:
6
+ * - Already captured in event context (UTM params, referral params)
7
+ * - Semantic event properties that should not be overridden by URL params
8
+ */
9
+ var PAGE_PROPERTIES_EXCLUDED_FIELDS = new Set([
10
+ // Context fields (already captured in event context)
11
+ 'utm_source',
12
+ 'utm_medium',
13
+ 'utm_campaign',
14
+ 'utm_term',
15
+ 'utm_content',
16
+ 'ref',
17
+ 'referral',
18
+ 'refcode',
19
+ 'referrer',
20
+ // Semantic event properties (should not be overridden by URL params)
21
+ 'category',
22
+ 'name',
23
+ 'url',
24
+ 'path',
25
+ 'hash',
26
+ 'query',
27
+ ]);
28
+ export { CHANNEL, VERSION, PAGE_PROPERTIES_EXCLUDED_FIELDS };
4
29
  //# sourceMappingURL=constants.js.map
@@ -94,6 +94,24 @@ export interface AutocaptureOptions {
94
94
  */
95
95
  chain?: boolean;
96
96
  }
97
+ /**
98
+ * Configuration options for referral parameter parsing
99
+ */
100
+ export interface ReferralOptions {
101
+ /**
102
+ * Custom query parameter names to check for referral codes
103
+ * @default ["ref", "referral", "refcode"]
104
+ * @example ["via", "referrer", "source"] - will check ?via=CODE, ?referrer=CODE, ?source=CODE
105
+ */
106
+ queryParams?: string[];
107
+ /**
108
+ * URL path pattern to extract referral code from
109
+ * Should be a regex string that matches the path segment containing the referral code
110
+ * The first capture group will be used as the referral code
111
+ * @example "/r/([^/]+)" - will extract "01K17FKB" from "https://glider.fi/r/01K17FKB"
112
+ */
113
+ pathPattern?: string;
114
+ }
97
115
  export interface Options {
98
116
  provider?: EIP1193Provider;
99
117
  tracking?: boolean | TrackingOptions;
@@ -122,6 +140,12 @@ export interface Options {
122
140
  enabled?: boolean;
123
141
  levels?: LogLevel[];
124
142
  };
143
+ /**
144
+ * Configuration for referral parameter parsing from URLs
145
+ * Allows customizing how referral codes are detected from query parameters and URL paths
146
+ * @example { queryParams: ["via"], pathPattern: "/r/([^/]+)" }
147
+ */
148
+ referral?: ReferralOptions;
125
149
  ready?: (formo: IFormoAnalytics) => void;
126
150
  }
127
151
  export interface FormoAnalyticsProviderProps {
@@ -0,0 +1,2 @@
1
+ export declare const version = "1.25.0";
2
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1,4 @@
1
+ // This file is auto-generated by scripts/update-version.js during npm version
2
+ // Do not edit manually - it will be overwritten
3
+ export var version = '1.25.0';
4
+ //# sourceMappingURL=version.js.map