featurely-site-manager 1.1.9 → 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 +55 -30
- package/dist/index.mjs +55 -30
- 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,16 +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
|
-
|
|
400
|
+
if (useBeacon) {
|
|
401
|
+
if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
|
|
402
|
+
navigator.sendBeacon(url, payload);
|
|
403
|
+
}
|
|
404
|
+
} else {
|
|
401
405
|
try {
|
|
402
|
-
await fetch(url, {
|
|
406
|
+
const res = await fetch(url, {
|
|
403
407
|
method: "POST",
|
|
404
408
|
headers: { "Content-Type": "application/json" },
|
|
405
|
-
body: payload
|
|
409
|
+
body: payload,
|
|
410
|
+
keepalive: true
|
|
406
411
|
});
|
|
407
|
-
|
|
408
|
-
|
|
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
|
+
}
|
|
409
421
|
}
|
|
410
422
|
}
|
|
411
423
|
}
|
|
@@ -421,8 +433,9 @@ var _SiteManager = class _SiteManager {
|
|
|
421
433
|
isLoggedIn: !!this.config.userId
|
|
422
434
|
});
|
|
423
435
|
this.currentPagePath = window.location.pathname;
|
|
436
|
+
this.currentPageSearch = window.location.search;
|
|
424
437
|
this.pageEntryTime = Date.now();
|
|
425
|
-
this.trackPageView();
|
|
438
|
+
this.trackPageView(this.currentPagePath);
|
|
426
439
|
setTimeout(() => this.flushAnalytics(), 2e3);
|
|
427
440
|
const originalPushState = history.pushState.bind(history);
|
|
428
441
|
const originalReplaceState = history.replaceState.bind(history);
|
|
@@ -433,29 +446,35 @@ var _SiteManager = class _SiteManager {
|
|
|
433
446
|
};
|
|
434
447
|
history.replaceState = function(...args) {
|
|
435
448
|
originalReplaceState(...args);
|
|
436
|
-
|
|
449
|
+
const nextFull = window.location.pathname + window.location.search;
|
|
450
|
+
const prevFull = self.currentPagePath + self.currentPageSearch;
|
|
451
|
+
if (nextFull !== prevFull) {
|
|
437
452
|
self.onNavigate();
|
|
438
453
|
}
|
|
439
454
|
};
|
|
440
455
|
window.addEventListener("popstate", () => this.onNavigate());
|
|
456
|
+
window.addEventListener("hashchange", () => {
|
|
457
|
+
if (window.location.pathname !== this.currentPagePath) {
|
|
458
|
+
this.onNavigate();
|
|
459
|
+
}
|
|
460
|
+
});
|
|
441
461
|
window.addEventListener("visibilitychange", () => {
|
|
442
462
|
if (document.visibilityState === "hidden") {
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
this.trackEvent("page_exit", {
|
|
446
|
-
path: this.currentPagePath,
|
|
447
|
-
durationSeconds: duration
|
|
448
|
-
});
|
|
449
|
-
this.flushAnalytics();
|
|
450
|
-
}
|
|
463
|
+
this.trackPageExit();
|
|
464
|
+
this.flushAnalytics(true);
|
|
451
465
|
} else if (document.visibilityState === "visible") {
|
|
452
466
|
this.pageEntryTime = Date.now();
|
|
453
467
|
}
|
|
454
468
|
});
|
|
469
|
+
window.addEventListener("pagehide", () => {
|
|
470
|
+
this.trackPageExit();
|
|
471
|
+
this.flushAnalytics(true);
|
|
472
|
+
});
|
|
455
473
|
}
|
|
456
|
-
|
|
474
|
+
/** Track a page_view for a captured path (path passed in to avoid closure bugs on rapid nav). */
|
|
475
|
+
trackPageView(path) {
|
|
457
476
|
const props = {
|
|
458
|
-
path
|
|
477
|
+
path,
|
|
459
478
|
title: document.title,
|
|
460
479
|
referrer: document.referrer || "",
|
|
461
480
|
isLoggedIn: !!this.config.userId
|
|
@@ -464,23 +483,29 @@ var _SiteManager = class _SiteManager {
|
|
|
464
483
|
if (this.config.userEmail) props.userEmail = this.config.userEmail;
|
|
465
484
|
this.trackEvent("page_view", props);
|
|
466
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
|
+
}
|
|
467
497
|
onNavigate() {
|
|
468
498
|
const newPath = window.location.pathname;
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
if (duration > 0) {
|
|
472
|
-
this.trackEvent("page_exit", {
|
|
473
|
-
path: this.currentPagePath,
|
|
474
|
-
durationSeconds: duration
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
|
-
}
|
|
499
|
+
const newSearch = window.location.search;
|
|
500
|
+
this.trackPageExit();
|
|
478
501
|
this.currentPagePath = newPath;
|
|
502
|
+
this.currentPageSearch = newSearch;
|
|
479
503
|
this.pageEntryTime = Date.now();
|
|
504
|
+
const capturedPath = newPath;
|
|
480
505
|
setTimeout(() => {
|
|
481
|
-
this.trackPageView();
|
|
506
|
+
this.trackPageView(capturedPath);
|
|
482
507
|
this.flushAnalytics();
|
|
483
|
-
},
|
|
508
|
+
}, 150);
|
|
484
509
|
}
|
|
485
510
|
generateSessionId() {
|
|
486
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,16 +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
|
-
|
|
365
|
+
if (useBeacon) {
|
|
366
|
+
if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
|
|
367
|
+
navigator.sendBeacon(url, payload);
|
|
368
|
+
}
|
|
369
|
+
} else {
|
|
366
370
|
try {
|
|
367
|
-
await fetch(url, {
|
|
371
|
+
const res = await fetch(url, {
|
|
368
372
|
method: "POST",
|
|
369
373
|
headers: { "Content-Type": "application/json" },
|
|
370
|
-
body: payload
|
|
374
|
+
body: payload,
|
|
375
|
+
keepalive: true
|
|
371
376
|
});
|
|
372
|
-
|
|
373
|
-
|
|
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
|
+
}
|
|
374
386
|
}
|
|
375
387
|
}
|
|
376
388
|
}
|
|
@@ -386,8 +398,9 @@ var _SiteManager = class _SiteManager {
|
|
|
386
398
|
isLoggedIn: !!this.config.userId
|
|
387
399
|
});
|
|
388
400
|
this.currentPagePath = window.location.pathname;
|
|
401
|
+
this.currentPageSearch = window.location.search;
|
|
389
402
|
this.pageEntryTime = Date.now();
|
|
390
|
-
this.trackPageView();
|
|
403
|
+
this.trackPageView(this.currentPagePath);
|
|
391
404
|
setTimeout(() => this.flushAnalytics(), 2e3);
|
|
392
405
|
const originalPushState = history.pushState.bind(history);
|
|
393
406
|
const originalReplaceState = history.replaceState.bind(history);
|
|
@@ -398,29 +411,35 @@ var _SiteManager = class _SiteManager {
|
|
|
398
411
|
};
|
|
399
412
|
history.replaceState = function(...args) {
|
|
400
413
|
originalReplaceState(...args);
|
|
401
|
-
|
|
414
|
+
const nextFull = window.location.pathname + window.location.search;
|
|
415
|
+
const prevFull = self.currentPagePath + self.currentPageSearch;
|
|
416
|
+
if (nextFull !== prevFull) {
|
|
402
417
|
self.onNavigate();
|
|
403
418
|
}
|
|
404
419
|
};
|
|
405
420
|
window.addEventListener("popstate", () => this.onNavigate());
|
|
421
|
+
window.addEventListener("hashchange", () => {
|
|
422
|
+
if (window.location.pathname !== this.currentPagePath) {
|
|
423
|
+
this.onNavigate();
|
|
424
|
+
}
|
|
425
|
+
});
|
|
406
426
|
window.addEventListener("visibilitychange", () => {
|
|
407
427
|
if (document.visibilityState === "hidden") {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
this.trackEvent("page_exit", {
|
|
411
|
-
path: this.currentPagePath,
|
|
412
|
-
durationSeconds: duration
|
|
413
|
-
});
|
|
414
|
-
this.flushAnalytics();
|
|
415
|
-
}
|
|
428
|
+
this.trackPageExit();
|
|
429
|
+
this.flushAnalytics(true);
|
|
416
430
|
} else if (document.visibilityState === "visible") {
|
|
417
431
|
this.pageEntryTime = Date.now();
|
|
418
432
|
}
|
|
419
433
|
});
|
|
434
|
+
window.addEventListener("pagehide", () => {
|
|
435
|
+
this.trackPageExit();
|
|
436
|
+
this.flushAnalytics(true);
|
|
437
|
+
});
|
|
420
438
|
}
|
|
421
|
-
|
|
439
|
+
/** Track a page_view for a captured path (path passed in to avoid closure bugs on rapid nav). */
|
|
440
|
+
trackPageView(path) {
|
|
422
441
|
const props = {
|
|
423
|
-
path
|
|
442
|
+
path,
|
|
424
443
|
title: document.title,
|
|
425
444
|
referrer: document.referrer || "",
|
|
426
445
|
isLoggedIn: !!this.config.userId
|
|
@@ -429,23 +448,29 @@ var _SiteManager = class _SiteManager {
|
|
|
429
448
|
if (this.config.userEmail) props.userEmail = this.config.userEmail;
|
|
430
449
|
this.trackEvent("page_view", props);
|
|
431
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
|
+
}
|
|
432
462
|
onNavigate() {
|
|
433
463
|
const newPath = window.location.pathname;
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
if (duration > 0) {
|
|
437
|
-
this.trackEvent("page_exit", {
|
|
438
|
-
path: this.currentPagePath,
|
|
439
|
-
durationSeconds: duration
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
}
|
|
464
|
+
const newSearch = window.location.search;
|
|
465
|
+
this.trackPageExit();
|
|
443
466
|
this.currentPagePath = newPath;
|
|
467
|
+
this.currentPageSearch = newSearch;
|
|
444
468
|
this.pageEntryTime = Date.now();
|
|
469
|
+
const capturedPath = newPath;
|
|
445
470
|
setTimeout(() => {
|
|
446
|
-
this.trackPageView();
|
|
471
|
+
this.trackPageView(capturedPath);
|
|
447
472
|
this.flushAnalytics();
|
|
448
|
-
},
|
|
473
|
+
}, 150);
|
|
449
474
|
}
|
|
450
475
|
generateSessionId() {
|
|
451
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",
|