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 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
- console.error("Featurely Site Manager: Failed to fetch configuration", error);
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
- console.error("Featurely Site Manager: Failed to fetch configuration", error);
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",
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",