featurely-site-manager 1.1.3 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +90 -2
- package/dist/index.mjs +90 -2
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -103,6 +103,9 @@ declare class SiteManager {
|
|
|
103
103
|
private analyticsFlushIntervalId;
|
|
104
104
|
private sessionId;
|
|
105
105
|
private consecutiveFetchFailures;
|
|
106
|
+
private pageTrackingSetup;
|
|
107
|
+
private currentPagePath;
|
|
108
|
+
private pageEntryTime;
|
|
106
109
|
private static readonly MAX_CONSECUTIVE_FAILURES;
|
|
107
110
|
private lastVersionCheck;
|
|
108
111
|
constructor(config: SiteManagerConfig);
|
|
@@ -123,6 +126,9 @@ declare class SiteManager {
|
|
|
123
126
|
private startAnalyticsFlushing;
|
|
124
127
|
private stopAnalyticsFlushing;
|
|
125
128
|
private flushAnalytics;
|
|
129
|
+
private setupPageTracking;
|
|
130
|
+
private trackPageView;
|
|
131
|
+
private onNavigate;
|
|
126
132
|
private generateSessionId;
|
|
127
133
|
checkVersion(currentVersion?: string): Promise<VersionCheckResponse | null>;
|
|
128
134
|
getLastVersionCheck(): VersionCheckResponse | null;
|
package/dist/index.d.ts
CHANGED
|
@@ -103,6 +103,9 @@ declare class SiteManager {
|
|
|
103
103
|
private analyticsFlushIntervalId;
|
|
104
104
|
private sessionId;
|
|
105
105
|
private consecutiveFetchFailures;
|
|
106
|
+
private pageTrackingSetup;
|
|
107
|
+
private currentPagePath;
|
|
108
|
+
private pageEntryTime;
|
|
106
109
|
private static readonly MAX_CONSECUTIVE_FAILURES;
|
|
107
110
|
private lastVersionCheck;
|
|
108
111
|
constructor(config: SiteManagerConfig);
|
|
@@ -123,6 +126,9 @@ declare class SiteManager {
|
|
|
123
126
|
private startAnalyticsFlushing;
|
|
124
127
|
private stopAnalyticsFlushing;
|
|
125
128
|
private flushAnalytics;
|
|
129
|
+
private setupPageTracking;
|
|
130
|
+
private trackPageView;
|
|
131
|
+
private onNavigate;
|
|
126
132
|
private generateSessionId;
|
|
127
133
|
checkVersion(currentVersion?: string): Promise<VersionCheckResponse | null>;
|
|
128
134
|
getLastVersionCheck(): VersionCheckResponse | null;
|
package/dist/index.js
CHANGED
|
@@ -48,6 +48,9 @@ var _SiteManager = class _SiteManager {
|
|
|
48
48
|
this.analyticsFlushIntervalId = null;
|
|
49
49
|
this.sessionId = this.generateSessionId();
|
|
50
50
|
this.consecutiveFetchFailures = 0;
|
|
51
|
+
this.pageTrackingSetup = false;
|
|
52
|
+
this.currentPagePath = "";
|
|
53
|
+
this.pageEntryTime = 0;
|
|
51
54
|
this.lastVersionCheck = null;
|
|
52
55
|
var _a, _b, _c, _d, _e;
|
|
53
56
|
if (!config.apiKey) {
|
|
@@ -95,6 +98,7 @@ var _SiteManager = class _SiteManager {
|
|
|
95
98
|
this.startPolling();
|
|
96
99
|
if (this.config.enableAnalytics) {
|
|
97
100
|
this.startAnalyticsFlushing();
|
|
101
|
+
this.setupPageTracking();
|
|
98
102
|
}
|
|
99
103
|
if (this.config.enableVersionCheck && this.config.appVersion) {
|
|
100
104
|
await this.checkVersion();
|
|
@@ -117,10 +121,17 @@ var _SiteManager = class _SiteManager {
|
|
|
117
121
|
*/
|
|
118
122
|
setUser(email, userId) {
|
|
119
123
|
var _a;
|
|
124
|
+
const wasLoggedIn = !!this.config.userId;
|
|
120
125
|
this.config.userEmail = email;
|
|
121
126
|
if (userId) {
|
|
122
127
|
this.config.userId = userId;
|
|
123
128
|
}
|
|
129
|
+
if (!wasLoggedIn && this.config.userId && this.config.enableAnalytics) {
|
|
130
|
+
this.trackEvent("user_login", {
|
|
131
|
+
userId: this.config.userId,
|
|
132
|
+
...this.config.userEmail ? { userEmail: this.config.userEmail } : {}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
124
135
|
if ((_a = this.siteConfig) == null ? void 0 : _a.maintenance.enabled) {
|
|
125
136
|
this.checkMaintenanceMode();
|
|
126
137
|
}
|
|
@@ -321,13 +332,22 @@ var _SiteManager = class _SiteManager {
|
|
|
321
332
|
} catch (error) {
|
|
322
333
|
this.consecutiveFetchFailures++;
|
|
323
334
|
if (this.consecutiveFetchFailures <= _SiteManager.MAX_CONSECUTIVE_FAILURES) {
|
|
324
|
-
|
|
335
|
+
const isNetworkError = error instanceof TypeError && (error.message.includes("NetworkError") || error.message.includes("Failed to fetch") || error.message.includes("fetch"));
|
|
336
|
+
if (isNetworkError) {
|
|
337
|
+
console.error(
|
|
338
|
+
`[Featurely] Network error \u2014 request to Featurely was blocked.
|
|
339
|
+
\u2192 If your site has a Content-Security-Policy, add 'https://www.featurely.no' to the connect-src directive.
|
|
340
|
+
Example: connect-src 'self' https://www.featurely.no ...`
|
|
341
|
+
);
|
|
342
|
+
} else {
|
|
343
|
+
console.error("Featurely Site Manager: Failed to fetch configuration", error);
|
|
344
|
+
}
|
|
325
345
|
if (this.config.onError && error instanceof Error) {
|
|
326
346
|
this.config.onError(error);
|
|
327
347
|
}
|
|
328
348
|
} else if (this.consecutiveFetchFailures === _SiteManager.MAX_CONSECUTIVE_FAILURES + 1) {
|
|
329
349
|
console.error(
|
|
330
|
-
`Featurely Site Manager: Silencing repeated fetch errors after ${_SiteManager.MAX_CONSECUTIVE_FAILURES} failures. Check your apiUrl configuration.`
|
|
350
|
+
`Featurely Site Manager: Silencing repeated fetch errors after ${_SiteManager.MAX_CONSECUTIVE_FAILURES} failures. Check your apiUrl and Content-Security-Policy configuration.`
|
|
331
351
|
);
|
|
332
352
|
}
|
|
333
353
|
}
|
|
@@ -390,6 +410,74 @@ var _SiteManager = class _SiteManager {
|
|
|
390
410
|
}
|
|
391
411
|
}
|
|
392
412
|
}
|
|
413
|
+
setupPageTracking() {
|
|
414
|
+
if (this.pageTrackingSetup) return;
|
|
415
|
+
this.pageTrackingSetup = true;
|
|
416
|
+
this.trackEvent("session_start", {
|
|
417
|
+
referrer: document.referrer || "",
|
|
418
|
+
language: navigator.language,
|
|
419
|
+
screenWidth: window.screen.width,
|
|
420
|
+
screenHeight: window.screen.height,
|
|
421
|
+
isLoggedIn: !!this.config.userId
|
|
422
|
+
});
|
|
423
|
+
this.currentPagePath = window.location.pathname;
|
|
424
|
+
this.pageEntryTime = Date.now();
|
|
425
|
+
this.trackPageView();
|
|
426
|
+
const originalPushState = history.pushState.bind(history);
|
|
427
|
+
const originalReplaceState = history.replaceState.bind(history);
|
|
428
|
+
const self = this;
|
|
429
|
+
history.pushState = function(...args) {
|
|
430
|
+
originalPushState(...args);
|
|
431
|
+
self.onNavigate();
|
|
432
|
+
};
|
|
433
|
+
history.replaceState = function(...args) {
|
|
434
|
+
originalReplaceState(...args);
|
|
435
|
+
if (window.location.pathname !== self.currentPagePath) {
|
|
436
|
+
self.onNavigate();
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
window.addEventListener("popstate", () => this.onNavigate());
|
|
440
|
+
window.addEventListener("visibilitychange", () => {
|
|
441
|
+
if (document.visibilityState === "hidden") {
|
|
442
|
+
const duration = Math.round((Date.now() - this.pageEntryTime) / 1e3);
|
|
443
|
+
if (duration > 0) {
|
|
444
|
+
this.trackEvent("page_exit", {
|
|
445
|
+
path: this.currentPagePath,
|
|
446
|
+
durationSeconds: duration
|
|
447
|
+
});
|
|
448
|
+
this.flushAnalytics();
|
|
449
|
+
}
|
|
450
|
+
} else if (document.visibilityState === "visible") {
|
|
451
|
+
this.pageEntryTime = Date.now();
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
trackPageView() {
|
|
456
|
+
const props = {
|
|
457
|
+
path: this.currentPagePath,
|
|
458
|
+
title: document.title,
|
|
459
|
+
referrer: document.referrer || "",
|
|
460
|
+
isLoggedIn: !!this.config.userId
|
|
461
|
+
};
|
|
462
|
+
if (this.config.userId) props.userId = this.config.userId;
|
|
463
|
+
if (this.config.userEmail) props.userEmail = this.config.userEmail;
|
|
464
|
+
this.trackEvent("page_view", props);
|
|
465
|
+
}
|
|
466
|
+
onNavigate() {
|
|
467
|
+
const newPath = window.location.pathname;
|
|
468
|
+
if (this.currentPagePath && this.pageEntryTime > 0) {
|
|
469
|
+
const duration = Math.round((Date.now() - this.pageEntryTime) / 1e3);
|
|
470
|
+
if (duration > 0) {
|
|
471
|
+
this.trackEvent("page_exit", {
|
|
472
|
+
path: this.currentPagePath,
|
|
473
|
+
durationSeconds: duration
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
this.currentPagePath = newPath;
|
|
478
|
+
this.pageEntryTime = Date.now();
|
|
479
|
+
setTimeout(() => this.trackPageView(), 100);
|
|
480
|
+
}
|
|
393
481
|
generateSessionId() {
|
|
394
482
|
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
395
483
|
const array = new Uint8Array(16);
|
package/dist/index.mjs
CHANGED
|
@@ -13,6 +13,9 @@ var _SiteManager = class _SiteManager {
|
|
|
13
13
|
this.analyticsFlushIntervalId = null;
|
|
14
14
|
this.sessionId = this.generateSessionId();
|
|
15
15
|
this.consecutiveFetchFailures = 0;
|
|
16
|
+
this.pageTrackingSetup = false;
|
|
17
|
+
this.currentPagePath = "";
|
|
18
|
+
this.pageEntryTime = 0;
|
|
16
19
|
this.lastVersionCheck = null;
|
|
17
20
|
var _a, _b, _c, _d, _e;
|
|
18
21
|
if (!config.apiKey) {
|
|
@@ -60,6 +63,7 @@ var _SiteManager = class _SiteManager {
|
|
|
60
63
|
this.startPolling();
|
|
61
64
|
if (this.config.enableAnalytics) {
|
|
62
65
|
this.startAnalyticsFlushing();
|
|
66
|
+
this.setupPageTracking();
|
|
63
67
|
}
|
|
64
68
|
if (this.config.enableVersionCheck && this.config.appVersion) {
|
|
65
69
|
await this.checkVersion();
|
|
@@ -82,10 +86,17 @@ var _SiteManager = class _SiteManager {
|
|
|
82
86
|
*/
|
|
83
87
|
setUser(email, userId) {
|
|
84
88
|
var _a;
|
|
89
|
+
const wasLoggedIn = !!this.config.userId;
|
|
85
90
|
this.config.userEmail = email;
|
|
86
91
|
if (userId) {
|
|
87
92
|
this.config.userId = userId;
|
|
88
93
|
}
|
|
94
|
+
if (!wasLoggedIn && this.config.userId && this.config.enableAnalytics) {
|
|
95
|
+
this.trackEvent("user_login", {
|
|
96
|
+
userId: this.config.userId,
|
|
97
|
+
...this.config.userEmail ? { userEmail: this.config.userEmail } : {}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
89
100
|
if ((_a = this.siteConfig) == null ? void 0 : _a.maintenance.enabled) {
|
|
90
101
|
this.checkMaintenanceMode();
|
|
91
102
|
}
|
|
@@ -286,13 +297,22 @@ var _SiteManager = class _SiteManager {
|
|
|
286
297
|
} catch (error) {
|
|
287
298
|
this.consecutiveFetchFailures++;
|
|
288
299
|
if (this.consecutiveFetchFailures <= _SiteManager.MAX_CONSECUTIVE_FAILURES) {
|
|
289
|
-
|
|
300
|
+
const isNetworkError = error instanceof TypeError && (error.message.includes("NetworkError") || error.message.includes("Failed to fetch") || error.message.includes("fetch"));
|
|
301
|
+
if (isNetworkError) {
|
|
302
|
+
console.error(
|
|
303
|
+
`[Featurely] Network error \u2014 request to Featurely was blocked.
|
|
304
|
+
\u2192 If your site has a Content-Security-Policy, add 'https://www.featurely.no' to the connect-src directive.
|
|
305
|
+
Example: connect-src 'self' https://www.featurely.no ...`
|
|
306
|
+
);
|
|
307
|
+
} else {
|
|
308
|
+
console.error("Featurely Site Manager: Failed to fetch configuration", error);
|
|
309
|
+
}
|
|
290
310
|
if (this.config.onError && error instanceof Error) {
|
|
291
311
|
this.config.onError(error);
|
|
292
312
|
}
|
|
293
313
|
} else if (this.consecutiveFetchFailures === _SiteManager.MAX_CONSECUTIVE_FAILURES + 1) {
|
|
294
314
|
console.error(
|
|
295
|
-
`Featurely Site Manager: Silencing repeated fetch errors after ${_SiteManager.MAX_CONSECUTIVE_FAILURES} failures. Check your apiUrl configuration.`
|
|
315
|
+
`Featurely Site Manager: Silencing repeated fetch errors after ${_SiteManager.MAX_CONSECUTIVE_FAILURES} failures. Check your apiUrl and Content-Security-Policy configuration.`
|
|
296
316
|
);
|
|
297
317
|
}
|
|
298
318
|
}
|
|
@@ -355,6 +375,74 @@ var _SiteManager = class _SiteManager {
|
|
|
355
375
|
}
|
|
356
376
|
}
|
|
357
377
|
}
|
|
378
|
+
setupPageTracking() {
|
|
379
|
+
if (this.pageTrackingSetup) return;
|
|
380
|
+
this.pageTrackingSetup = true;
|
|
381
|
+
this.trackEvent("session_start", {
|
|
382
|
+
referrer: document.referrer || "",
|
|
383
|
+
language: navigator.language,
|
|
384
|
+
screenWidth: window.screen.width,
|
|
385
|
+
screenHeight: window.screen.height,
|
|
386
|
+
isLoggedIn: !!this.config.userId
|
|
387
|
+
});
|
|
388
|
+
this.currentPagePath = window.location.pathname;
|
|
389
|
+
this.pageEntryTime = Date.now();
|
|
390
|
+
this.trackPageView();
|
|
391
|
+
const originalPushState = history.pushState.bind(history);
|
|
392
|
+
const originalReplaceState = history.replaceState.bind(history);
|
|
393
|
+
const self = this;
|
|
394
|
+
history.pushState = function(...args) {
|
|
395
|
+
originalPushState(...args);
|
|
396
|
+
self.onNavigate();
|
|
397
|
+
};
|
|
398
|
+
history.replaceState = function(...args) {
|
|
399
|
+
originalReplaceState(...args);
|
|
400
|
+
if (window.location.pathname !== self.currentPagePath) {
|
|
401
|
+
self.onNavigate();
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
window.addEventListener("popstate", () => this.onNavigate());
|
|
405
|
+
window.addEventListener("visibilitychange", () => {
|
|
406
|
+
if (document.visibilityState === "hidden") {
|
|
407
|
+
const duration = Math.round((Date.now() - this.pageEntryTime) / 1e3);
|
|
408
|
+
if (duration > 0) {
|
|
409
|
+
this.trackEvent("page_exit", {
|
|
410
|
+
path: this.currentPagePath,
|
|
411
|
+
durationSeconds: duration
|
|
412
|
+
});
|
|
413
|
+
this.flushAnalytics();
|
|
414
|
+
}
|
|
415
|
+
} else if (document.visibilityState === "visible") {
|
|
416
|
+
this.pageEntryTime = Date.now();
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
trackPageView() {
|
|
421
|
+
const props = {
|
|
422
|
+
path: this.currentPagePath,
|
|
423
|
+
title: document.title,
|
|
424
|
+
referrer: document.referrer || "",
|
|
425
|
+
isLoggedIn: !!this.config.userId
|
|
426
|
+
};
|
|
427
|
+
if (this.config.userId) props.userId = this.config.userId;
|
|
428
|
+
if (this.config.userEmail) props.userEmail = this.config.userEmail;
|
|
429
|
+
this.trackEvent("page_view", props);
|
|
430
|
+
}
|
|
431
|
+
onNavigate() {
|
|
432
|
+
const newPath = window.location.pathname;
|
|
433
|
+
if (this.currentPagePath && this.pageEntryTime > 0) {
|
|
434
|
+
const duration = Math.round((Date.now() - this.pageEntryTime) / 1e3);
|
|
435
|
+
if (duration > 0) {
|
|
436
|
+
this.trackEvent("page_exit", {
|
|
437
|
+
path: this.currentPagePath,
|
|
438
|
+
durationSeconds: duration
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
this.currentPagePath = newPath;
|
|
443
|
+
this.pageEntryTime = Date.now();
|
|
444
|
+
setTimeout(() => this.trackPageView(), 100);
|
|
445
|
+
}
|
|
358
446
|
generateSessionId() {
|
|
359
447
|
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
360
448
|
const array = new Uint8Array(16);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "featurely-site-manager",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"description": "Complete site management SDK for maintenance mode, status messages, feature flags, version checking, and analytics",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|