featurely-site-manager 1.1.21 → 1.1.23
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.js +133 -46
- package/dist/index.mjs +133 -46
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -123,7 +123,8 @@ var _SiteManager = class _SiteManager {
|
|
|
123
123
|
} else if (key.startsWith("ft_")) {
|
|
124
124
|
const flagKey = key.slice(3);
|
|
125
125
|
if (value === "true" || value === "1") this.flagOverrides.set(flagKey, true);
|
|
126
|
-
else if (value === "false" || value === "0")
|
|
126
|
+
else if (value === "false" || value === "0")
|
|
127
|
+
this.flagOverrides.set(flagKey, false);
|
|
127
128
|
}
|
|
128
129
|
});
|
|
129
130
|
} catch {
|
|
@@ -195,7 +196,10 @@ var _SiteManager = class _SiteManager {
|
|
|
195
196
|
this.config.userId = userId;
|
|
196
197
|
}
|
|
197
198
|
if (customAttributes) {
|
|
198
|
-
this.config.customAttributes = {
|
|
199
|
+
this.config.customAttributes = {
|
|
200
|
+
...(_a = this.config.customAttributes) != null ? _a : {},
|
|
201
|
+
...customAttributes
|
|
202
|
+
};
|
|
199
203
|
this.featureFlagBuckets.clear();
|
|
200
204
|
}
|
|
201
205
|
if (!wasLoggedIn && this.config.userId && this.config.enableAnalytics) {
|
|
@@ -217,16 +221,25 @@ var _SiteManager = class _SiteManager {
|
|
|
217
221
|
var _a, _b;
|
|
218
222
|
if (this.flagOverrides.has(flagKey)) {
|
|
219
223
|
const overrideValue = this.flagOverrides.get(flagKey);
|
|
220
|
-
this.debugLog(
|
|
224
|
+
this.debugLog(
|
|
225
|
+
"info",
|
|
226
|
+
`[flag] "${flagKey}" \u2192 ${overrideValue ? "ENABLED" : "DISABLED"} (override)`
|
|
227
|
+
);
|
|
221
228
|
return overrideValue;
|
|
222
229
|
}
|
|
223
230
|
if (!((_a = this.siteConfig) == null ? void 0 : _a.featureFlags)) {
|
|
224
231
|
if (this.config.bootstrapFlags && Object.prototype.hasOwnProperty.call(this.config.bootstrapFlags, flagKey)) {
|
|
225
232
|
const bootstrapValue = this.config.bootstrapFlags[flagKey];
|
|
226
|
-
this.debugLog(
|
|
233
|
+
this.debugLog(
|
|
234
|
+
"info",
|
|
235
|
+
`[flag] "${flagKey}" \u2192 ${bootstrapValue ? "ENABLED" : "DISABLED"} (bootstrap)`
|
|
236
|
+
);
|
|
227
237
|
return bootstrapValue;
|
|
228
238
|
}
|
|
229
|
-
this.debugLog(
|
|
239
|
+
this.debugLog(
|
|
240
|
+
"info",
|
|
241
|
+
`[flag] "${flagKey}" \u2192 default (${defaultValue}) \u2014 config not yet loaded`
|
|
242
|
+
);
|
|
230
243
|
return defaultValue;
|
|
231
244
|
}
|
|
232
245
|
const flag = this.siteConfig.featureFlags.find((f) => f.key === flagKey);
|
|
@@ -346,7 +359,10 @@ var _SiteManager = class _SiteManager {
|
|
|
346
359
|
this.debugLog("info", `[flag] override removed for "${flagKey}"`);
|
|
347
360
|
} else {
|
|
348
361
|
this.flagOverrides.set(flagKey, value);
|
|
349
|
-
this.debugLog(
|
|
362
|
+
this.debugLog(
|
|
363
|
+
"info",
|
|
364
|
+
`[flag] override set "${flagKey}" \u2192 ${value ? "ENABLED" : "DISABLED"}`
|
|
365
|
+
);
|
|
350
366
|
}
|
|
351
367
|
}
|
|
352
368
|
/**
|
|
@@ -504,7 +520,10 @@ var _SiteManager = class _SiteManager {
|
|
|
504
520
|
return flag;
|
|
505
521
|
})
|
|
506
522
|
};
|
|
507
|
-
this.debugLog(
|
|
523
|
+
this.debugLog(
|
|
524
|
+
"info",
|
|
525
|
+
`[env] applied ${Object.keys(envOverrides).length} flag override(s) from environment "${matchedEnv.name}"`
|
|
526
|
+
);
|
|
508
527
|
}
|
|
509
528
|
}
|
|
510
529
|
this.siteConfig = newConfig;
|
|
@@ -546,7 +565,10 @@ var _SiteManager = class _SiteManager {
|
|
|
546
565
|
}
|
|
547
566
|
}
|
|
548
567
|
if (matchedEnv && matchedEnv.errorLoggingEnabled === false) {
|
|
549
|
-
this.debugLog(
|
|
568
|
+
this.debugLog(
|
|
569
|
+
"info",
|
|
570
|
+
`[env] error logging disabled for environment${envLabel}`
|
|
571
|
+
);
|
|
550
572
|
}
|
|
551
573
|
if (newConfig.maintenance.enabled && !wasMaintenanceEnabled) {
|
|
552
574
|
this.enableMaintenanceMode();
|
|
@@ -1048,7 +1070,14 @@ var _SiteManager = class _SiteManager {
|
|
|
1048
1070
|
const params = {};
|
|
1049
1071
|
try {
|
|
1050
1072
|
const sp = new URLSearchParams(search);
|
|
1051
|
-
for (const key of [
|
|
1073
|
+
for (const key of [
|
|
1074
|
+
"utm_source",
|
|
1075
|
+
"utm_medium",
|
|
1076
|
+
"utm_campaign",
|
|
1077
|
+
"utm_content",
|
|
1078
|
+
"utm_term",
|
|
1079
|
+
"ref"
|
|
1080
|
+
]) {
|
|
1052
1081
|
const val = sp.get(key);
|
|
1053
1082
|
if (val) params[key] = val;
|
|
1054
1083
|
}
|
|
@@ -1060,18 +1089,49 @@ var _SiteManager = class _SiteManager {
|
|
|
1060
1089
|
setupWebVitals() {
|
|
1061
1090
|
if (typeof PerformanceObserver === "undefined") return;
|
|
1062
1091
|
try {
|
|
1092
|
+
let lcpValue = 0;
|
|
1093
|
+
let lcpReported = false;
|
|
1063
1094
|
const lcpObs = new PerformanceObserver((list) => {
|
|
1064
1095
|
const entries = list.getEntries();
|
|
1065
1096
|
const last = entries[entries.length - 1];
|
|
1066
1097
|
if (last) {
|
|
1098
|
+
lcpValue = Math.round(last.startTime);
|
|
1099
|
+
}
|
|
1100
|
+
});
|
|
1101
|
+
lcpObs.observe({ type: "largest-contentful-paint", buffered: true });
|
|
1102
|
+
const reportLcp = () => {
|
|
1103
|
+
if (lcpValue > 0 && !lcpReported) {
|
|
1104
|
+
lcpReported = true;
|
|
1067
1105
|
this.trackEvent("web_vital", {
|
|
1068
1106
|
metric: "LCP",
|
|
1069
|
-
valueMs:
|
|
1107
|
+
valueMs: lcpValue,
|
|
1070
1108
|
path: window.location.pathname
|
|
1071
1109
|
});
|
|
1072
1110
|
}
|
|
1111
|
+
};
|
|
1112
|
+
const interactionTypes = [
|
|
1113
|
+
"click",
|
|
1114
|
+
"keydown",
|
|
1115
|
+
"scroll",
|
|
1116
|
+
"pointerdown",
|
|
1117
|
+
"touchstart"
|
|
1118
|
+
];
|
|
1119
|
+
const onFirstInteraction = () => {
|
|
1120
|
+
reportLcp();
|
|
1121
|
+
interactionTypes.forEach(
|
|
1122
|
+
(t) => window.removeEventListener(t, onFirstInteraction)
|
|
1123
|
+
);
|
|
1124
|
+
};
|
|
1125
|
+
interactionTypes.forEach(
|
|
1126
|
+
(t) => window.addEventListener(t, onFirstInteraction, {
|
|
1127
|
+
once: true,
|
|
1128
|
+
passive: true
|
|
1129
|
+
})
|
|
1130
|
+
);
|
|
1131
|
+
window.addEventListener("visibilitychange", () => {
|
|
1132
|
+
if (document.visibilityState === "hidden") reportLcp();
|
|
1073
1133
|
});
|
|
1074
|
-
|
|
1134
|
+
window.addEventListener("pagehide", reportLcp);
|
|
1075
1135
|
} catch {
|
|
1076
1136
|
}
|
|
1077
1137
|
try {
|
|
@@ -1116,46 +1176,59 @@ var _SiteManager = class _SiteManager {
|
|
|
1116
1176
|
}
|
|
1117
1177
|
}
|
|
1118
1178
|
});
|
|
1119
|
-
inpObs.observe({
|
|
1179
|
+
inpObs.observe({
|
|
1180
|
+
type: "event",
|
|
1181
|
+
buffered: true,
|
|
1182
|
+
durationThreshold: 40
|
|
1183
|
+
});
|
|
1120
1184
|
} catch {
|
|
1121
1185
|
}
|
|
1122
1186
|
}
|
|
1123
1187
|
/** Set up outbound link click tracking. */
|
|
1124
1188
|
setupOutboundLinkTracking() {
|
|
1125
1189
|
const currentHost = window.location.hostname;
|
|
1126
|
-
document.addEventListener(
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1190
|
+
document.addEventListener(
|
|
1191
|
+
"click",
|
|
1192
|
+
(e) => {
|
|
1193
|
+
var _a;
|
|
1194
|
+
const target = (_a = e.target) == null ? void 0 : _a.closest("a");
|
|
1195
|
+
if (!target) return;
|
|
1196
|
+
const href = target.getAttribute("href") || "";
|
|
1197
|
+
if (!href || href.startsWith("#") || href.startsWith("mailto:") || href.startsWith("tel:"))
|
|
1198
|
+
return;
|
|
1199
|
+
try {
|
|
1200
|
+
const url = new URL(href, window.location.href);
|
|
1201
|
+
if (url.hostname && url.hostname !== currentHost) {
|
|
1202
|
+
this.trackEvent("outbound_link_click", {
|
|
1203
|
+
href: url.href,
|
|
1204
|
+
destination: url.hostname,
|
|
1205
|
+
path: window.location.pathname
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
} catch {
|
|
1140
1209
|
}
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1210
|
+
},
|
|
1211
|
+
{ capture: true, passive: true }
|
|
1212
|
+
);
|
|
1144
1213
|
}
|
|
1145
1214
|
/** Set up auto-capture for elements with data-featurely-click attribute. */
|
|
1146
1215
|
setupAutoClickCapture() {
|
|
1147
|
-
document.addEventListener(
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
label,
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1216
|
+
document.addEventListener(
|
|
1217
|
+
"click",
|
|
1218
|
+
(e) => {
|
|
1219
|
+
var _a, _b;
|
|
1220
|
+
const target = (_a = e.target) == null ? void 0 : _a.closest("[data-featurely-click]");
|
|
1221
|
+
if (!target) return;
|
|
1222
|
+
const eventName = target.getAttribute("data-featurely-click") || "element_clicked";
|
|
1223
|
+
const label = target.getAttribute("data-featurely-label") || ((_b = target.textContent) == null ? void 0 : _b.trim().slice(0, 100)) || "";
|
|
1224
|
+
this.trackEvent(eventName, {
|
|
1225
|
+
label,
|
|
1226
|
+
path: window.location.pathname,
|
|
1227
|
+
tag: target.tagName.toLowerCase()
|
|
1228
|
+
});
|
|
1229
|
+
},
|
|
1230
|
+
{ capture: true, passive: true }
|
|
1231
|
+
);
|
|
1159
1232
|
}
|
|
1160
1233
|
setupPageTracking() {
|
|
1161
1234
|
if (this.pageTrackingSetup) return;
|
|
@@ -1293,7 +1366,9 @@ var _SiteManager = class _SiteManager {
|
|
|
1293
1366
|
let versionInfo = await response.json();
|
|
1294
1367
|
const rollout = (_b = (_a = versionInfo.latestVersion) == null ? void 0 : _a.rolloutPercentage) != null ? _b : 100;
|
|
1295
1368
|
if (rollout < 100 && versionInfo.updateAvailable) {
|
|
1296
|
-
const bucket = this.getUserBucket(
|
|
1369
|
+
const bucket = this.getUserBucket(
|
|
1370
|
+
`version:${(_d = (_c = versionInfo.latestVersion) == null ? void 0 : _c.version) != null ? _d : ""}`
|
|
1371
|
+
);
|
|
1297
1372
|
if (bucket >= rollout) {
|
|
1298
1373
|
versionInfo = {
|
|
1299
1374
|
...versionInfo,
|
|
@@ -1301,7 +1376,10 @@ var _SiteManager = class _SiteManager {
|
|
|
1301
1376
|
updateRequired: false,
|
|
1302
1377
|
updateRecommended: false
|
|
1303
1378
|
};
|
|
1304
|
-
this.debugLog(
|
|
1379
|
+
this.debugLog(
|
|
1380
|
+
"info",
|
|
1381
|
+
`[version] outside phased rollout (${rollout}%) \u2014 skipping update`
|
|
1382
|
+
);
|
|
1305
1383
|
}
|
|
1306
1384
|
}
|
|
1307
1385
|
if (this.config.updateRules && versionInfo.updateType && versionInfo.updateType !== "hash") {
|
|
@@ -1314,7 +1392,10 @@ var _SiteManager = class _SiteManager {
|
|
|
1314
1392
|
updateRecommended: classification === "recommended",
|
|
1315
1393
|
updateAvailable: classification === "available" || versionInfo.updateAvailable
|
|
1316
1394
|
};
|
|
1317
|
-
this.debugLog(
|
|
1395
|
+
this.debugLog(
|
|
1396
|
+
"info",
|
|
1397
|
+
`[version] update rules applied: ${type} \u2192 ${classification}`
|
|
1398
|
+
);
|
|
1318
1399
|
}
|
|
1319
1400
|
if (this.config.platform && ((_h = versionInfo.latestVersion) == null ? void 0 : _h.platformUrls)) {
|
|
1320
1401
|
const platformUrl = versionInfo.latestVersion.platformUrls[this.config.platform];
|
|
@@ -1358,12 +1439,18 @@ var _SiteManager = class _SiteManager {
|
|
|
1358
1439
|
*/
|
|
1359
1440
|
async forceUpdateWeb() {
|
|
1360
1441
|
if (typeof window === "undefined") return;
|
|
1361
|
-
this.debugLog(
|
|
1442
|
+
this.debugLog(
|
|
1443
|
+
"info",
|
|
1444
|
+
"[version] forceUpdateWeb: clearing SW registrations and caches\u2026"
|
|
1445
|
+
);
|
|
1362
1446
|
try {
|
|
1363
1447
|
if ("serviceWorker" in navigator) {
|
|
1364
1448
|
const registrations = await navigator.serviceWorker.getRegistrations();
|
|
1365
1449
|
await Promise.all(registrations.map((r) => r.unregister()));
|
|
1366
|
-
this.debugLog(
|
|
1450
|
+
this.debugLog(
|
|
1451
|
+
"info",
|
|
1452
|
+
`[version] unregistered ${registrations.length} service worker(s)`
|
|
1453
|
+
);
|
|
1367
1454
|
}
|
|
1368
1455
|
if ("caches" in window) {
|
|
1369
1456
|
const cacheKeys = await caches.keys();
|
package/dist/index.mjs
CHANGED
|
@@ -88,7 +88,8 @@ var _SiteManager = class _SiteManager {
|
|
|
88
88
|
} else if (key.startsWith("ft_")) {
|
|
89
89
|
const flagKey = key.slice(3);
|
|
90
90
|
if (value === "true" || value === "1") this.flagOverrides.set(flagKey, true);
|
|
91
|
-
else if (value === "false" || value === "0")
|
|
91
|
+
else if (value === "false" || value === "0")
|
|
92
|
+
this.flagOverrides.set(flagKey, false);
|
|
92
93
|
}
|
|
93
94
|
});
|
|
94
95
|
} catch {
|
|
@@ -160,7 +161,10 @@ var _SiteManager = class _SiteManager {
|
|
|
160
161
|
this.config.userId = userId;
|
|
161
162
|
}
|
|
162
163
|
if (customAttributes) {
|
|
163
|
-
this.config.customAttributes = {
|
|
164
|
+
this.config.customAttributes = {
|
|
165
|
+
...(_a = this.config.customAttributes) != null ? _a : {},
|
|
166
|
+
...customAttributes
|
|
167
|
+
};
|
|
164
168
|
this.featureFlagBuckets.clear();
|
|
165
169
|
}
|
|
166
170
|
if (!wasLoggedIn && this.config.userId && this.config.enableAnalytics) {
|
|
@@ -182,16 +186,25 @@ var _SiteManager = class _SiteManager {
|
|
|
182
186
|
var _a, _b;
|
|
183
187
|
if (this.flagOverrides.has(flagKey)) {
|
|
184
188
|
const overrideValue = this.flagOverrides.get(flagKey);
|
|
185
|
-
this.debugLog(
|
|
189
|
+
this.debugLog(
|
|
190
|
+
"info",
|
|
191
|
+
`[flag] "${flagKey}" \u2192 ${overrideValue ? "ENABLED" : "DISABLED"} (override)`
|
|
192
|
+
);
|
|
186
193
|
return overrideValue;
|
|
187
194
|
}
|
|
188
195
|
if (!((_a = this.siteConfig) == null ? void 0 : _a.featureFlags)) {
|
|
189
196
|
if (this.config.bootstrapFlags && Object.prototype.hasOwnProperty.call(this.config.bootstrapFlags, flagKey)) {
|
|
190
197
|
const bootstrapValue = this.config.bootstrapFlags[flagKey];
|
|
191
|
-
this.debugLog(
|
|
198
|
+
this.debugLog(
|
|
199
|
+
"info",
|
|
200
|
+
`[flag] "${flagKey}" \u2192 ${bootstrapValue ? "ENABLED" : "DISABLED"} (bootstrap)`
|
|
201
|
+
);
|
|
192
202
|
return bootstrapValue;
|
|
193
203
|
}
|
|
194
|
-
this.debugLog(
|
|
204
|
+
this.debugLog(
|
|
205
|
+
"info",
|
|
206
|
+
`[flag] "${flagKey}" \u2192 default (${defaultValue}) \u2014 config not yet loaded`
|
|
207
|
+
);
|
|
195
208
|
return defaultValue;
|
|
196
209
|
}
|
|
197
210
|
const flag = this.siteConfig.featureFlags.find((f) => f.key === flagKey);
|
|
@@ -311,7 +324,10 @@ var _SiteManager = class _SiteManager {
|
|
|
311
324
|
this.debugLog("info", `[flag] override removed for "${flagKey}"`);
|
|
312
325
|
} else {
|
|
313
326
|
this.flagOverrides.set(flagKey, value);
|
|
314
|
-
this.debugLog(
|
|
327
|
+
this.debugLog(
|
|
328
|
+
"info",
|
|
329
|
+
`[flag] override set "${flagKey}" \u2192 ${value ? "ENABLED" : "DISABLED"}`
|
|
330
|
+
);
|
|
315
331
|
}
|
|
316
332
|
}
|
|
317
333
|
/**
|
|
@@ -469,7 +485,10 @@ var _SiteManager = class _SiteManager {
|
|
|
469
485
|
return flag;
|
|
470
486
|
})
|
|
471
487
|
};
|
|
472
|
-
this.debugLog(
|
|
488
|
+
this.debugLog(
|
|
489
|
+
"info",
|
|
490
|
+
`[env] applied ${Object.keys(envOverrides).length} flag override(s) from environment "${matchedEnv.name}"`
|
|
491
|
+
);
|
|
473
492
|
}
|
|
474
493
|
}
|
|
475
494
|
this.siteConfig = newConfig;
|
|
@@ -511,7 +530,10 @@ var _SiteManager = class _SiteManager {
|
|
|
511
530
|
}
|
|
512
531
|
}
|
|
513
532
|
if (matchedEnv && matchedEnv.errorLoggingEnabled === false) {
|
|
514
|
-
this.debugLog(
|
|
533
|
+
this.debugLog(
|
|
534
|
+
"info",
|
|
535
|
+
`[env] error logging disabled for environment${envLabel}`
|
|
536
|
+
);
|
|
515
537
|
}
|
|
516
538
|
if (newConfig.maintenance.enabled && !wasMaintenanceEnabled) {
|
|
517
539
|
this.enableMaintenanceMode();
|
|
@@ -1013,7 +1035,14 @@ var _SiteManager = class _SiteManager {
|
|
|
1013
1035
|
const params = {};
|
|
1014
1036
|
try {
|
|
1015
1037
|
const sp = new URLSearchParams(search);
|
|
1016
|
-
for (const key of [
|
|
1038
|
+
for (const key of [
|
|
1039
|
+
"utm_source",
|
|
1040
|
+
"utm_medium",
|
|
1041
|
+
"utm_campaign",
|
|
1042
|
+
"utm_content",
|
|
1043
|
+
"utm_term",
|
|
1044
|
+
"ref"
|
|
1045
|
+
]) {
|
|
1017
1046
|
const val = sp.get(key);
|
|
1018
1047
|
if (val) params[key] = val;
|
|
1019
1048
|
}
|
|
@@ -1025,18 +1054,49 @@ var _SiteManager = class _SiteManager {
|
|
|
1025
1054
|
setupWebVitals() {
|
|
1026
1055
|
if (typeof PerformanceObserver === "undefined") return;
|
|
1027
1056
|
try {
|
|
1057
|
+
let lcpValue = 0;
|
|
1058
|
+
let lcpReported = false;
|
|
1028
1059
|
const lcpObs = new PerformanceObserver((list) => {
|
|
1029
1060
|
const entries = list.getEntries();
|
|
1030
1061
|
const last = entries[entries.length - 1];
|
|
1031
1062
|
if (last) {
|
|
1063
|
+
lcpValue = Math.round(last.startTime);
|
|
1064
|
+
}
|
|
1065
|
+
});
|
|
1066
|
+
lcpObs.observe({ type: "largest-contentful-paint", buffered: true });
|
|
1067
|
+
const reportLcp = () => {
|
|
1068
|
+
if (lcpValue > 0 && !lcpReported) {
|
|
1069
|
+
lcpReported = true;
|
|
1032
1070
|
this.trackEvent("web_vital", {
|
|
1033
1071
|
metric: "LCP",
|
|
1034
|
-
valueMs:
|
|
1072
|
+
valueMs: lcpValue,
|
|
1035
1073
|
path: window.location.pathname
|
|
1036
1074
|
});
|
|
1037
1075
|
}
|
|
1076
|
+
};
|
|
1077
|
+
const interactionTypes = [
|
|
1078
|
+
"click",
|
|
1079
|
+
"keydown",
|
|
1080
|
+
"scroll",
|
|
1081
|
+
"pointerdown",
|
|
1082
|
+
"touchstart"
|
|
1083
|
+
];
|
|
1084
|
+
const onFirstInteraction = () => {
|
|
1085
|
+
reportLcp();
|
|
1086
|
+
interactionTypes.forEach(
|
|
1087
|
+
(t) => window.removeEventListener(t, onFirstInteraction)
|
|
1088
|
+
);
|
|
1089
|
+
};
|
|
1090
|
+
interactionTypes.forEach(
|
|
1091
|
+
(t) => window.addEventListener(t, onFirstInteraction, {
|
|
1092
|
+
once: true,
|
|
1093
|
+
passive: true
|
|
1094
|
+
})
|
|
1095
|
+
);
|
|
1096
|
+
window.addEventListener("visibilitychange", () => {
|
|
1097
|
+
if (document.visibilityState === "hidden") reportLcp();
|
|
1038
1098
|
});
|
|
1039
|
-
|
|
1099
|
+
window.addEventListener("pagehide", reportLcp);
|
|
1040
1100
|
} catch {
|
|
1041
1101
|
}
|
|
1042
1102
|
try {
|
|
@@ -1081,46 +1141,59 @@ var _SiteManager = class _SiteManager {
|
|
|
1081
1141
|
}
|
|
1082
1142
|
}
|
|
1083
1143
|
});
|
|
1084
|
-
inpObs.observe({
|
|
1144
|
+
inpObs.observe({
|
|
1145
|
+
type: "event",
|
|
1146
|
+
buffered: true,
|
|
1147
|
+
durationThreshold: 40
|
|
1148
|
+
});
|
|
1085
1149
|
} catch {
|
|
1086
1150
|
}
|
|
1087
1151
|
}
|
|
1088
1152
|
/** Set up outbound link click tracking. */
|
|
1089
1153
|
setupOutboundLinkTracking() {
|
|
1090
1154
|
const currentHost = window.location.hostname;
|
|
1091
|
-
document.addEventListener(
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1155
|
+
document.addEventListener(
|
|
1156
|
+
"click",
|
|
1157
|
+
(e) => {
|
|
1158
|
+
var _a;
|
|
1159
|
+
const target = (_a = e.target) == null ? void 0 : _a.closest("a");
|
|
1160
|
+
if (!target) return;
|
|
1161
|
+
const href = target.getAttribute("href") || "";
|
|
1162
|
+
if (!href || href.startsWith("#") || href.startsWith("mailto:") || href.startsWith("tel:"))
|
|
1163
|
+
return;
|
|
1164
|
+
try {
|
|
1165
|
+
const url = new URL(href, window.location.href);
|
|
1166
|
+
if (url.hostname && url.hostname !== currentHost) {
|
|
1167
|
+
this.trackEvent("outbound_link_click", {
|
|
1168
|
+
href: url.href,
|
|
1169
|
+
destination: url.hostname,
|
|
1170
|
+
path: window.location.pathname
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
} catch {
|
|
1105
1174
|
}
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1175
|
+
},
|
|
1176
|
+
{ capture: true, passive: true }
|
|
1177
|
+
);
|
|
1109
1178
|
}
|
|
1110
1179
|
/** Set up auto-capture for elements with data-featurely-click attribute. */
|
|
1111
1180
|
setupAutoClickCapture() {
|
|
1112
|
-
document.addEventListener(
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
label,
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1181
|
+
document.addEventListener(
|
|
1182
|
+
"click",
|
|
1183
|
+
(e) => {
|
|
1184
|
+
var _a, _b;
|
|
1185
|
+
const target = (_a = e.target) == null ? void 0 : _a.closest("[data-featurely-click]");
|
|
1186
|
+
if (!target) return;
|
|
1187
|
+
const eventName = target.getAttribute("data-featurely-click") || "element_clicked";
|
|
1188
|
+
const label = target.getAttribute("data-featurely-label") || ((_b = target.textContent) == null ? void 0 : _b.trim().slice(0, 100)) || "";
|
|
1189
|
+
this.trackEvent(eventName, {
|
|
1190
|
+
label,
|
|
1191
|
+
path: window.location.pathname,
|
|
1192
|
+
tag: target.tagName.toLowerCase()
|
|
1193
|
+
});
|
|
1194
|
+
},
|
|
1195
|
+
{ capture: true, passive: true }
|
|
1196
|
+
);
|
|
1124
1197
|
}
|
|
1125
1198
|
setupPageTracking() {
|
|
1126
1199
|
if (this.pageTrackingSetup) return;
|
|
@@ -1258,7 +1331,9 @@ var _SiteManager = class _SiteManager {
|
|
|
1258
1331
|
let versionInfo = await response.json();
|
|
1259
1332
|
const rollout = (_b = (_a = versionInfo.latestVersion) == null ? void 0 : _a.rolloutPercentage) != null ? _b : 100;
|
|
1260
1333
|
if (rollout < 100 && versionInfo.updateAvailable) {
|
|
1261
|
-
const bucket = this.getUserBucket(
|
|
1334
|
+
const bucket = this.getUserBucket(
|
|
1335
|
+
`version:${(_d = (_c = versionInfo.latestVersion) == null ? void 0 : _c.version) != null ? _d : ""}`
|
|
1336
|
+
);
|
|
1262
1337
|
if (bucket >= rollout) {
|
|
1263
1338
|
versionInfo = {
|
|
1264
1339
|
...versionInfo,
|
|
@@ -1266,7 +1341,10 @@ var _SiteManager = class _SiteManager {
|
|
|
1266
1341
|
updateRequired: false,
|
|
1267
1342
|
updateRecommended: false
|
|
1268
1343
|
};
|
|
1269
|
-
this.debugLog(
|
|
1344
|
+
this.debugLog(
|
|
1345
|
+
"info",
|
|
1346
|
+
`[version] outside phased rollout (${rollout}%) \u2014 skipping update`
|
|
1347
|
+
);
|
|
1270
1348
|
}
|
|
1271
1349
|
}
|
|
1272
1350
|
if (this.config.updateRules && versionInfo.updateType && versionInfo.updateType !== "hash") {
|
|
@@ -1279,7 +1357,10 @@ var _SiteManager = class _SiteManager {
|
|
|
1279
1357
|
updateRecommended: classification === "recommended",
|
|
1280
1358
|
updateAvailable: classification === "available" || versionInfo.updateAvailable
|
|
1281
1359
|
};
|
|
1282
|
-
this.debugLog(
|
|
1360
|
+
this.debugLog(
|
|
1361
|
+
"info",
|
|
1362
|
+
`[version] update rules applied: ${type} \u2192 ${classification}`
|
|
1363
|
+
);
|
|
1283
1364
|
}
|
|
1284
1365
|
if (this.config.platform && ((_h = versionInfo.latestVersion) == null ? void 0 : _h.platformUrls)) {
|
|
1285
1366
|
const platformUrl = versionInfo.latestVersion.platformUrls[this.config.platform];
|
|
@@ -1323,12 +1404,18 @@ var _SiteManager = class _SiteManager {
|
|
|
1323
1404
|
*/
|
|
1324
1405
|
async forceUpdateWeb() {
|
|
1325
1406
|
if (typeof window === "undefined") return;
|
|
1326
|
-
this.debugLog(
|
|
1407
|
+
this.debugLog(
|
|
1408
|
+
"info",
|
|
1409
|
+
"[version] forceUpdateWeb: clearing SW registrations and caches\u2026"
|
|
1410
|
+
);
|
|
1327
1411
|
try {
|
|
1328
1412
|
if ("serviceWorker" in navigator) {
|
|
1329
1413
|
const registrations = await navigator.serviceWorker.getRegistrations();
|
|
1330
1414
|
await Promise.all(registrations.map((r) => r.unregister()));
|
|
1331
|
-
this.debugLog(
|
|
1415
|
+
this.debugLog(
|
|
1416
|
+
"info",
|
|
1417
|
+
`[version] unregistered ${registrations.length} service worker(s)`
|
|
1418
|
+
);
|
|
1332
1419
|
}
|
|
1333
1420
|
if ("caches" in window) {
|
|
1334
1421
|
const cacheKeys = await caches.keys();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "featurely-site-manager",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.23",
|
|
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",
|