@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 +1 -1
- package/dist/cjs/src/FormoAnalytics.js +1 -1
- package/dist/cjs/src/lib/event/EventFactory.d.ts +5 -1
- package/dist/cjs/src/lib/event/EventFactory.js +84 -12
- package/dist/cjs/src/lib/event/EventManager.d.ts +3 -2
- package/dist/cjs/src/lib/event/EventManager.js +3 -2
- package/dist/cjs/src/lib/event/constants.d.ts +8 -1
- package/dist/cjs/src/lib/event/constants.js +27 -1
- package/dist/cjs/src/types/base.d.ts +24 -0
- package/dist/cjs/src/version.d.ts +2 -0
- package/dist/cjs/src/version.js +7 -0
- package/dist/esm/src/FormoAnalytics.js +1 -1
- package/dist/esm/src/lib/event/EventFactory.d.ts +5 -1
- package/dist/esm/src/lib/event/EventFactory.js +85 -13
- package/dist/esm/src/lib/event/EventManager.d.ts +3 -2
- package/dist/esm/src/lib/event/EventManager.js +3 -2
- package/dist/esm/src/lib/event/constants.d.ts +8 -1
- package/dist/esm/src/lib/event/constants.js +26 -1
- package/dist/esm/src/types/base.d.ts +24 -0
- package/dist/esm/src/version.d.ts +2 -0
- package/dist/esm/src/version.js +4 -0
- package/dist/index.umd.min.js +1 -1
- package/package.json +15 -31
- package/dist/cjs/src/lib/version.d.ts +0 -2
- package/dist/cjs/src/lib/version.js +0 -6
- package/dist/esm/src/lib/version.d.ts +0 -2
- package/dist/esm/src/lib/version.js +0 -3
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
|
|
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("
|
|
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
|
-
|
|
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 = (
|
|
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
|
-
|
|
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
|
|
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)), {
|
|
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
|
-
|
|
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,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 "
|
|
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
|
-
|
|
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 = (
|
|
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
|
-
|
|
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
|
|
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)), {
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|