featurely-site-manager 1.1.10 → 1.1.11
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 +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +58 -38
- package/dist/index.mjs +58 -38
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -105,6 +105,7 @@ declare class SiteManager {
|
|
|
105
105
|
private consecutiveFetchFailures;
|
|
106
106
|
private pageTrackingSetup;
|
|
107
107
|
private currentPagePath;
|
|
108
|
+
private currentPageSearch;
|
|
108
109
|
private pageEntryTime;
|
|
109
110
|
private static readonly MAX_CONSECUTIVE_FAILURES;
|
|
110
111
|
private lastVersionCheck;
|
|
@@ -128,6 +129,7 @@ declare class SiteManager {
|
|
|
128
129
|
private flushAnalytics;
|
|
129
130
|
private setupPageTracking;
|
|
130
131
|
private trackPageView;
|
|
132
|
+
private trackPageExit;
|
|
131
133
|
private onNavigate;
|
|
132
134
|
private generateSessionId;
|
|
133
135
|
checkVersion(currentVersion?: string): Promise<VersionCheckResponse | null>;
|
package/dist/index.d.ts
CHANGED
|
@@ -105,6 +105,7 @@ declare class SiteManager {
|
|
|
105
105
|
private consecutiveFetchFailures;
|
|
106
106
|
private pageTrackingSetup;
|
|
107
107
|
private currentPagePath;
|
|
108
|
+
private currentPageSearch;
|
|
108
109
|
private pageEntryTime;
|
|
109
110
|
private static readonly MAX_CONSECUTIVE_FAILURES;
|
|
110
111
|
private lastVersionCheck;
|
|
@@ -128,6 +129,7 @@ declare class SiteManager {
|
|
|
128
129
|
private flushAnalytics;
|
|
129
130
|
private setupPageTracking;
|
|
130
131
|
private trackPageView;
|
|
132
|
+
private trackPageExit;
|
|
131
133
|
private onNavigate;
|
|
132
134
|
private generateSessionId;
|
|
133
135
|
checkVersion(currentVersion?: string): Promise<VersionCheckResponse | null>;
|
package/dist/index.js
CHANGED
|
@@ -50,6 +50,7 @@ var _SiteManager = class _SiteManager {
|
|
|
50
50
|
this.consecutiveFetchFailures = 0;
|
|
51
51
|
this.pageTrackingSetup = false;
|
|
52
52
|
this.currentPagePath = "";
|
|
53
|
+
this.currentPageSearch = "";
|
|
53
54
|
this.pageEntryTime = 0;
|
|
54
55
|
this.lastVersionCheck = null;
|
|
55
56
|
var _a, _b, _c, _d, _e;
|
|
@@ -379,7 +380,7 @@ var _SiteManager = class _SiteManager {
|
|
|
379
380
|
this.analyticsFlushIntervalId = null;
|
|
380
381
|
}
|
|
381
382
|
}
|
|
382
|
-
async flushAnalytics() {
|
|
383
|
+
async flushAnalytics(useBeacon = false) {
|
|
383
384
|
if (this.analyticsQueue.length === 0) {
|
|
384
385
|
return;
|
|
385
386
|
}
|
|
@@ -396,21 +397,27 @@ var _SiteManager = class _SiteManager {
|
|
|
396
397
|
platform: typeof navigator !== "undefined" ? navigator.platform : void 0,
|
|
397
398
|
appVersion: this.config.appVersion
|
|
398
399
|
});
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
headers: { "Content-Type": "application/json" },
|
|
403
|
-
body: payload,
|
|
404
|
-
keepalive: true
|
|
405
|
-
});
|
|
406
|
-
if (!res.ok) {
|
|
407
|
-
const body = await res.text().catch(() => "(unreadable)");
|
|
408
|
-
console.error(`[Featurely] Analytics event "${event.eventName}" rejected by server: ${res.status} ${res.statusText} \u2014 ${body}`);
|
|
400
|
+
if (useBeacon) {
|
|
401
|
+
if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
|
|
402
|
+
navigator.sendBeacon(url, payload);
|
|
409
403
|
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
404
|
+
} else {
|
|
405
|
+
try {
|
|
406
|
+
const res = await fetch(url, {
|
|
407
|
+
method: "POST",
|
|
408
|
+
headers: { "Content-Type": "application/json" },
|
|
409
|
+
body: payload,
|
|
410
|
+
keepalive: true
|
|
411
|
+
});
|
|
412
|
+
if (!res.ok) {
|
|
413
|
+
const body = await res.text().catch(() => "(unreadable)");
|
|
414
|
+
console.error(`[Featurely] Analytics event "${event.eventName}" rejected: ${res.status} ${res.statusText} \u2014 ${body}`);
|
|
415
|
+
}
|
|
416
|
+
} catch (fetchError) {
|
|
417
|
+
const sentViaBeacon = typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function" && navigator.sendBeacon(url, payload);
|
|
418
|
+
if (!sentViaBeacon) {
|
|
419
|
+
console.error(`[Featurely] Failed to send analytics event "${event.eventName}":`, fetchError);
|
|
420
|
+
}
|
|
414
421
|
}
|
|
415
422
|
}
|
|
416
423
|
}
|
|
@@ -426,8 +433,9 @@ var _SiteManager = class _SiteManager {
|
|
|
426
433
|
isLoggedIn: !!this.config.userId
|
|
427
434
|
});
|
|
428
435
|
this.currentPagePath = window.location.pathname;
|
|
436
|
+
this.currentPageSearch = window.location.search;
|
|
429
437
|
this.pageEntryTime = Date.now();
|
|
430
|
-
this.trackPageView();
|
|
438
|
+
this.trackPageView(this.currentPagePath);
|
|
431
439
|
setTimeout(() => this.flushAnalytics(), 2e3);
|
|
432
440
|
const originalPushState = history.pushState.bind(history);
|
|
433
441
|
const originalReplaceState = history.replaceState.bind(history);
|
|
@@ -438,29 +446,35 @@ var _SiteManager = class _SiteManager {
|
|
|
438
446
|
};
|
|
439
447
|
history.replaceState = function(...args) {
|
|
440
448
|
originalReplaceState(...args);
|
|
441
|
-
|
|
449
|
+
const nextFull = window.location.pathname + window.location.search;
|
|
450
|
+
const prevFull = self.currentPagePath + self.currentPageSearch;
|
|
451
|
+
if (nextFull !== prevFull) {
|
|
442
452
|
self.onNavigate();
|
|
443
453
|
}
|
|
444
454
|
};
|
|
445
455
|
window.addEventListener("popstate", () => this.onNavigate());
|
|
456
|
+
window.addEventListener("hashchange", () => {
|
|
457
|
+
if (window.location.pathname !== this.currentPagePath) {
|
|
458
|
+
this.onNavigate();
|
|
459
|
+
}
|
|
460
|
+
});
|
|
446
461
|
window.addEventListener("visibilitychange", () => {
|
|
447
462
|
if (document.visibilityState === "hidden") {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
this.trackEvent("page_exit", {
|
|
451
|
-
path: this.currentPagePath,
|
|
452
|
-
durationSeconds: duration
|
|
453
|
-
});
|
|
454
|
-
this.flushAnalytics();
|
|
455
|
-
}
|
|
463
|
+
this.trackPageExit();
|
|
464
|
+
this.flushAnalytics(true);
|
|
456
465
|
} else if (document.visibilityState === "visible") {
|
|
457
466
|
this.pageEntryTime = Date.now();
|
|
458
467
|
}
|
|
459
468
|
});
|
|
469
|
+
window.addEventListener("pagehide", () => {
|
|
470
|
+
this.trackPageExit();
|
|
471
|
+
this.flushAnalytics(true);
|
|
472
|
+
});
|
|
460
473
|
}
|
|
461
|
-
|
|
474
|
+
/** Track a page_view for a captured path (path passed in to avoid closure bugs on rapid nav). */
|
|
475
|
+
trackPageView(path) {
|
|
462
476
|
const props = {
|
|
463
|
-
path
|
|
477
|
+
path,
|
|
464
478
|
title: document.title,
|
|
465
479
|
referrer: document.referrer || "",
|
|
466
480
|
isLoggedIn: !!this.config.userId
|
|
@@ -469,23 +483,29 @@ var _SiteManager = class _SiteManager {
|
|
|
469
483
|
if (this.config.userEmail) props.userEmail = this.config.userEmail;
|
|
470
484
|
this.trackEvent("page_view", props);
|
|
471
485
|
}
|
|
486
|
+
/** Record how long the user spent on the current page (called before navigation or unload). */
|
|
487
|
+
trackPageExit() {
|
|
488
|
+
if (!this.currentPagePath || this.pageEntryTime <= 0) return;
|
|
489
|
+
const duration = Math.round((Date.now() - this.pageEntryTime) / 1e3);
|
|
490
|
+
this.trackEvent("page_exit", {
|
|
491
|
+
path: this.currentPagePath,
|
|
492
|
+
durationSeconds: duration
|
|
493
|
+
// 0 is valid — records bounces/instant navigation
|
|
494
|
+
});
|
|
495
|
+
this.pageEntryTime = 0;
|
|
496
|
+
}
|
|
472
497
|
onNavigate() {
|
|
473
498
|
const newPath = window.location.pathname;
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
if (duration > 0) {
|
|
477
|
-
this.trackEvent("page_exit", {
|
|
478
|
-
path: this.currentPagePath,
|
|
479
|
-
durationSeconds: duration
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
|
-
}
|
|
499
|
+
const newSearch = window.location.search;
|
|
500
|
+
this.trackPageExit();
|
|
483
501
|
this.currentPagePath = newPath;
|
|
502
|
+
this.currentPageSearch = newSearch;
|
|
484
503
|
this.pageEntryTime = Date.now();
|
|
504
|
+
const capturedPath = newPath;
|
|
485
505
|
setTimeout(() => {
|
|
486
|
-
this.trackPageView();
|
|
506
|
+
this.trackPageView(capturedPath);
|
|
487
507
|
this.flushAnalytics();
|
|
488
|
-
},
|
|
508
|
+
}, 150);
|
|
489
509
|
}
|
|
490
510
|
generateSessionId() {
|
|
491
511
|
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
package/dist/index.mjs
CHANGED
|
@@ -15,6 +15,7 @@ var _SiteManager = class _SiteManager {
|
|
|
15
15
|
this.consecutiveFetchFailures = 0;
|
|
16
16
|
this.pageTrackingSetup = false;
|
|
17
17
|
this.currentPagePath = "";
|
|
18
|
+
this.currentPageSearch = "";
|
|
18
19
|
this.pageEntryTime = 0;
|
|
19
20
|
this.lastVersionCheck = null;
|
|
20
21
|
var _a, _b, _c, _d, _e;
|
|
@@ -344,7 +345,7 @@ var _SiteManager = class _SiteManager {
|
|
|
344
345
|
this.analyticsFlushIntervalId = null;
|
|
345
346
|
}
|
|
346
347
|
}
|
|
347
|
-
async flushAnalytics() {
|
|
348
|
+
async flushAnalytics(useBeacon = false) {
|
|
348
349
|
if (this.analyticsQueue.length === 0) {
|
|
349
350
|
return;
|
|
350
351
|
}
|
|
@@ -361,21 +362,27 @@ var _SiteManager = class _SiteManager {
|
|
|
361
362
|
platform: typeof navigator !== "undefined" ? navigator.platform : void 0,
|
|
362
363
|
appVersion: this.config.appVersion
|
|
363
364
|
});
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
headers: { "Content-Type": "application/json" },
|
|
368
|
-
body: payload,
|
|
369
|
-
keepalive: true
|
|
370
|
-
});
|
|
371
|
-
if (!res.ok) {
|
|
372
|
-
const body = await res.text().catch(() => "(unreadable)");
|
|
373
|
-
console.error(`[Featurely] Analytics event "${event.eventName}" rejected by server: ${res.status} ${res.statusText} \u2014 ${body}`);
|
|
365
|
+
if (useBeacon) {
|
|
366
|
+
if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
|
|
367
|
+
navigator.sendBeacon(url, payload);
|
|
374
368
|
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
369
|
+
} else {
|
|
370
|
+
try {
|
|
371
|
+
const res = await fetch(url, {
|
|
372
|
+
method: "POST",
|
|
373
|
+
headers: { "Content-Type": "application/json" },
|
|
374
|
+
body: payload,
|
|
375
|
+
keepalive: true
|
|
376
|
+
});
|
|
377
|
+
if (!res.ok) {
|
|
378
|
+
const body = await res.text().catch(() => "(unreadable)");
|
|
379
|
+
console.error(`[Featurely] Analytics event "${event.eventName}" rejected: ${res.status} ${res.statusText} \u2014 ${body}`);
|
|
380
|
+
}
|
|
381
|
+
} catch (fetchError) {
|
|
382
|
+
const sentViaBeacon = typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function" && navigator.sendBeacon(url, payload);
|
|
383
|
+
if (!sentViaBeacon) {
|
|
384
|
+
console.error(`[Featurely] Failed to send analytics event "${event.eventName}":`, fetchError);
|
|
385
|
+
}
|
|
379
386
|
}
|
|
380
387
|
}
|
|
381
388
|
}
|
|
@@ -391,8 +398,9 @@ var _SiteManager = class _SiteManager {
|
|
|
391
398
|
isLoggedIn: !!this.config.userId
|
|
392
399
|
});
|
|
393
400
|
this.currentPagePath = window.location.pathname;
|
|
401
|
+
this.currentPageSearch = window.location.search;
|
|
394
402
|
this.pageEntryTime = Date.now();
|
|
395
|
-
this.trackPageView();
|
|
403
|
+
this.trackPageView(this.currentPagePath);
|
|
396
404
|
setTimeout(() => this.flushAnalytics(), 2e3);
|
|
397
405
|
const originalPushState = history.pushState.bind(history);
|
|
398
406
|
const originalReplaceState = history.replaceState.bind(history);
|
|
@@ -403,29 +411,35 @@ var _SiteManager = class _SiteManager {
|
|
|
403
411
|
};
|
|
404
412
|
history.replaceState = function(...args) {
|
|
405
413
|
originalReplaceState(...args);
|
|
406
|
-
|
|
414
|
+
const nextFull = window.location.pathname + window.location.search;
|
|
415
|
+
const prevFull = self.currentPagePath + self.currentPageSearch;
|
|
416
|
+
if (nextFull !== prevFull) {
|
|
407
417
|
self.onNavigate();
|
|
408
418
|
}
|
|
409
419
|
};
|
|
410
420
|
window.addEventListener("popstate", () => this.onNavigate());
|
|
421
|
+
window.addEventListener("hashchange", () => {
|
|
422
|
+
if (window.location.pathname !== this.currentPagePath) {
|
|
423
|
+
this.onNavigate();
|
|
424
|
+
}
|
|
425
|
+
});
|
|
411
426
|
window.addEventListener("visibilitychange", () => {
|
|
412
427
|
if (document.visibilityState === "hidden") {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
this.trackEvent("page_exit", {
|
|
416
|
-
path: this.currentPagePath,
|
|
417
|
-
durationSeconds: duration
|
|
418
|
-
});
|
|
419
|
-
this.flushAnalytics();
|
|
420
|
-
}
|
|
428
|
+
this.trackPageExit();
|
|
429
|
+
this.flushAnalytics(true);
|
|
421
430
|
} else if (document.visibilityState === "visible") {
|
|
422
431
|
this.pageEntryTime = Date.now();
|
|
423
432
|
}
|
|
424
433
|
});
|
|
434
|
+
window.addEventListener("pagehide", () => {
|
|
435
|
+
this.trackPageExit();
|
|
436
|
+
this.flushAnalytics(true);
|
|
437
|
+
});
|
|
425
438
|
}
|
|
426
|
-
|
|
439
|
+
/** Track a page_view for a captured path (path passed in to avoid closure bugs on rapid nav). */
|
|
440
|
+
trackPageView(path) {
|
|
427
441
|
const props = {
|
|
428
|
-
path
|
|
442
|
+
path,
|
|
429
443
|
title: document.title,
|
|
430
444
|
referrer: document.referrer || "",
|
|
431
445
|
isLoggedIn: !!this.config.userId
|
|
@@ -434,23 +448,29 @@ var _SiteManager = class _SiteManager {
|
|
|
434
448
|
if (this.config.userEmail) props.userEmail = this.config.userEmail;
|
|
435
449
|
this.trackEvent("page_view", props);
|
|
436
450
|
}
|
|
451
|
+
/** Record how long the user spent on the current page (called before navigation or unload). */
|
|
452
|
+
trackPageExit() {
|
|
453
|
+
if (!this.currentPagePath || this.pageEntryTime <= 0) return;
|
|
454
|
+
const duration = Math.round((Date.now() - this.pageEntryTime) / 1e3);
|
|
455
|
+
this.trackEvent("page_exit", {
|
|
456
|
+
path: this.currentPagePath,
|
|
457
|
+
durationSeconds: duration
|
|
458
|
+
// 0 is valid — records bounces/instant navigation
|
|
459
|
+
});
|
|
460
|
+
this.pageEntryTime = 0;
|
|
461
|
+
}
|
|
437
462
|
onNavigate() {
|
|
438
463
|
const newPath = window.location.pathname;
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
if (duration > 0) {
|
|
442
|
-
this.trackEvent("page_exit", {
|
|
443
|
-
path: this.currentPagePath,
|
|
444
|
-
durationSeconds: duration
|
|
445
|
-
});
|
|
446
|
-
}
|
|
447
|
-
}
|
|
464
|
+
const newSearch = window.location.search;
|
|
465
|
+
this.trackPageExit();
|
|
448
466
|
this.currentPagePath = newPath;
|
|
467
|
+
this.currentPageSearch = newSearch;
|
|
449
468
|
this.pageEntryTime = Date.now();
|
|
469
|
+
const capturedPath = newPath;
|
|
450
470
|
setTimeout(() => {
|
|
451
|
-
this.trackPageView();
|
|
471
|
+
this.trackPageView(capturedPath);
|
|
452
472
|
this.flushAnalytics();
|
|
453
|
-
},
|
|
473
|
+
}, 150);
|
|
454
474
|
}
|
|
455
475
|
generateSessionId() {
|
|
456
476
|
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "featurely-site-manager",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.11",
|
|
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",
|