featurely-site-manager 1.3.0 → 1.3.1
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 +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +107 -31
- package/dist/index.mjs +107 -31
- package/package.json +2 -3
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare const SDK_VERSION = "1.3.
|
|
1
|
+
declare const SDK_VERSION = "1.3.1";
|
|
2
2
|
type MessageType = "info" | "warning" | "error" | "success";
|
|
3
3
|
type MessagePosition = "top" | "bottom";
|
|
4
4
|
type MessageStyle = "banner" | "toast";
|
|
@@ -31,6 +31,7 @@ interface FeatureFlag {
|
|
|
31
31
|
targetEmails?: string[];
|
|
32
32
|
excludeEmails?: string[];
|
|
33
33
|
betaUsersOnly?: boolean;
|
|
34
|
+
availableOnLocalhost?: boolean;
|
|
34
35
|
variants?: {
|
|
35
36
|
key: string;
|
|
36
37
|
name: string;
|
|
@@ -247,6 +248,7 @@ declare class SiteManager {
|
|
|
247
248
|
private matchHostnamePattern;
|
|
248
249
|
private evaluateFeatureFlag;
|
|
249
250
|
private getUserBucket;
|
|
251
|
+
private isLocalhost;
|
|
250
252
|
private simpleHash;
|
|
251
253
|
private getAnonymousId;
|
|
252
254
|
private getOrCreateVisitorId;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare const SDK_VERSION = "1.3.
|
|
1
|
+
declare const SDK_VERSION = "1.3.1";
|
|
2
2
|
type MessageType = "info" | "warning" | "error" | "success";
|
|
3
3
|
type MessagePosition = "top" | "bottom";
|
|
4
4
|
type MessageStyle = "banner" | "toast";
|
|
@@ -31,6 +31,7 @@ interface FeatureFlag {
|
|
|
31
31
|
targetEmails?: string[];
|
|
32
32
|
excludeEmails?: string[];
|
|
33
33
|
betaUsersOnly?: boolean;
|
|
34
|
+
availableOnLocalhost?: boolean;
|
|
34
35
|
variants?: {
|
|
35
36
|
key: string;
|
|
36
37
|
name: string;
|
|
@@ -247,6 +248,7 @@ declare class SiteManager {
|
|
|
247
248
|
private matchHostnamePattern;
|
|
248
249
|
private evaluateFeatureFlag;
|
|
249
250
|
private getUserBucket;
|
|
251
|
+
private isLocalhost;
|
|
250
252
|
private simpleHash;
|
|
251
253
|
private getAnonymousId;
|
|
252
254
|
private getOrCreateVisitorId;
|
package/dist/index.js
CHANGED
|
@@ -36,7 +36,7 @@ __export(index_exports, {
|
|
|
36
36
|
});
|
|
37
37
|
module.exports = __toCommonJS(index_exports);
|
|
38
38
|
var import_dompurify = __toESM(require("dompurify"));
|
|
39
|
-
var SDK_VERSION = "1.3.
|
|
39
|
+
var SDK_VERSION = "1.3.1";
|
|
40
40
|
var _SiteManager = class _SiteManager {
|
|
41
41
|
constructor(config) {
|
|
42
42
|
this.siteConfig = null;
|
|
@@ -153,9 +153,6 @@ var _SiteManager = class _SiteManager {
|
|
|
153
153
|
*/
|
|
154
154
|
async init() {
|
|
155
155
|
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
156
|
-
console.warn(
|
|
157
|
-
"Featurely Site Manager: Can only be initialized in a browser environment"
|
|
158
|
-
);
|
|
159
156
|
return;
|
|
160
157
|
}
|
|
161
158
|
if (this.config.debugMode) {
|
|
@@ -259,6 +256,7 @@ var _SiteManager = class _SiteManager {
|
|
|
259
256
|
return defaultValue;
|
|
260
257
|
}
|
|
261
258
|
const flag = this.siteConfig.featureFlags.find((f) => f.key === flagKey);
|
|
259
|
+
this.debugLog("info", `[flag-eval] Checking "${flagKey}" - found: ${!!flag}, enabled: ${(flag == null ? void 0 : flag.enabled) || false}`);
|
|
262
260
|
if (!flag || !flag.enabled) {
|
|
263
261
|
return defaultValue;
|
|
264
262
|
}
|
|
@@ -538,15 +536,15 @@ var _SiteManager = class _SiteManager {
|
|
|
538
536
|
const errorData = await response.json().catch(() => ({}));
|
|
539
537
|
const message = errorData.error || response.statusText;
|
|
540
538
|
if (response.status === 401) {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
539
|
+
this.debugLog(
|
|
540
|
+
"error",
|
|
541
|
+
`[config] Invalid or missing API key. ${message}. Check your API key at https://www.featurely.no/dashboard/settings`
|
|
544
542
|
);
|
|
545
543
|
this.stopPolling();
|
|
546
544
|
} else if (response.status === 403) {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
545
|
+
this.debugLog(
|
|
546
|
+
"error",
|
|
547
|
+
`[config] Permission denied. ${message}. Ensure your API key has the required permissions at https://www.featurely.no/dashboard/settings`
|
|
550
548
|
);
|
|
551
549
|
this.stopPolling();
|
|
552
550
|
}
|
|
@@ -698,23 +696,17 @@ var _SiteManager = class _SiteManager {
|
|
|
698
696
|
if (this.consecutiveFetchFailures <= _SiteManager.MAX_CONSECUTIVE_FAILURES) {
|
|
699
697
|
const isNetworkError = error instanceof TypeError && (error.message.includes("NetworkError") || error.message.includes("Failed to fetch") || error.message.includes("fetch"));
|
|
700
698
|
if (isNetworkError) {
|
|
701
|
-
|
|
702
|
-
\u2192 If your site has a Content-Security-Policy, add 'https://www.featurely.no' to the connect-src directive.
|
|
703
|
-
Example: connect-src 'self' https://www.featurely.no ...`;
|
|
704
|
-
console.error(cspMsg);
|
|
705
|
-
this.debugLog("error", "[config] possible CSP block \u2014 check connect-src");
|
|
699
|
+
this.debugLog("error", "[config] Network error - request blocked. Check Content-Security-Policy connect-src directive includes https://www.featurely.no");
|
|
706
700
|
} else {
|
|
707
|
-
|
|
701
|
+
this.debugLog("error", `[config] Failed to fetch configuration: ${error instanceof Error ? error.message : String(error)}`);
|
|
708
702
|
}
|
|
709
703
|
if (this.config.onError && error instanceof Error) {
|
|
710
704
|
this.config.onError(error);
|
|
711
705
|
}
|
|
712
706
|
} else if (this.consecutiveFetchFailures === _SiteManager.MAX_CONSECUTIVE_FAILURES + 1) {
|
|
713
|
-
const silenceMsg = `Featurely Site Manager: Silencing repeated fetch errors after ${_SiteManager.MAX_CONSECUTIVE_FAILURES} failures. Check your apiUrl and Content-Security-Policy configuration.`;
|
|
714
|
-
console.error(silenceMsg);
|
|
715
707
|
this.debugLog(
|
|
716
708
|
"error",
|
|
717
|
-
`[config]
|
|
709
|
+
`[config] Silencing repeated fetch errors after ${_SiteManager.MAX_CONSECUTIVE_FAILURES} consecutive failures. Check your apiUrl and Content-Security-Policy configuration.`
|
|
718
710
|
);
|
|
719
711
|
}
|
|
720
712
|
}
|
|
@@ -797,7 +789,6 @@ var _SiteManager = class _SiteManager {
|
|
|
797
789
|
if (!res.ok) {
|
|
798
790
|
const body = await res.text().catch(() => "(unreadable)");
|
|
799
791
|
const errDetail = `[analytics] batch rejected: ${res.status} ${res.statusText} \u2014 ${body}`;
|
|
800
|
-
console.error(`[Featurely] ${errDetail}`);
|
|
801
792
|
this.debugLog("error", errDetail);
|
|
802
793
|
this.errorCount++;
|
|
803
794
|
}
|
|
@@ -805,7 +796,6 @@ var _SiteManager = class _SiteManager {
|
|
|
805
796
|
const sentViaBeacon = typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function" && navigator.sendBeacon(url, payload);
|
|
806
797
|
const errMsg = fetchError instanceof Error ? fetchError.message : String(fetchError);
|
|
807
798
|
if (!sentViaBeacon) {
|
|
808
|
-
console.error(`[Featurely] Failed to send analytics batch:`, fetchError);
|
|
809
799
|
this.debugLog("error", `[analytics] batch send failed: ${errMsg}`);
|
|
810
800
|
this.errorCount++;
|
|
811
801
|
} else {
|
|
@@ -922,12 +912,12 @@ var _SiteManager = class _SiteManager {
|
|
|
922
912
|
this.debugOverlayEl = el;
|
|
923
913
|
this.setupGlobalErrorCapture();
|
|
924
914
|
this.debugLog("info", `[site-manager] debug overlay initialized`);
|
|
925
|
-
this.debugLog("info", `[site-manager]
|
|
915
|
+
this.debugLog("info", `[site-manager] v${SDK_VERSION} | project: ${this.config.projectId}`);
|
|
926
916
|
this.renderDebugOverlay();
|
|
927
917
|
this.debugRefreshId = setInterval(() => this.renderDebugOverlay(), 1500);
|
|
928
918
|
}
|
|
929
919
|
renderDebugOverlay() {
|
|
930
|
-
var _a, _b, _c, _d, _e;
|
|
920
|
+
var _a, _b, _c, _d, _e, _f;
|
|
931
921
|
const el = this.debugOverlayEl;
|
|
932
922
|
if (!el) return;
|
|
933
923
|
const ACCENT = "#6366f1";
|
|
@@ -954,7 +944,7 @@ var _SiteManager = class _SiteManager {
|
|
|
954
944
|
const flagCount = (_c = (_b = sc == null ? void 0 : sc.featureFlags) == null ? void 0 : _b.length) != null ? _c : 0;
|
|
955
945
|
const msgCount = (_e = (_d = sc == null ? void 0 : sc.messages) == null ? void 0 : _d.length) != null ? _e : 0;
|
|
956
946
|
content = `
|
|
957
|
-
${row("SDK Version",
|
|
947
|
+
${row("SDK Version", SDK_VERSION)}
|
|
958
948
|
${row("Project", cfg.projectId)}
|
|
959
949
|
${row("Hostname", typeof window !== "undefined" ? window.location.hostname : "\u2014")}
|
|
960
950
|
${row("API URL", cfg.apiUrl)}
|
|
@@ -1012,6 +1002,64 @@ var _SiteManager = class _SiteManager {
|
|
|
1012
1002
|
(e) => `<div style="padding:2px 0;display:flex;justify-content:space-between;border-bottom:1px solid rgba(255,255,255,0.03)"><span style="color:#a5b4fc">${e.name}</span><span style="color:${MUTED}">${e.ts}</span></div>`
|
|
1013
1003
|
).join("");
|
|
1014
1004
|
}
|
|
1005
|
+
} else if (tab === "flags") {
|
|
1006
|
+
const flags = (sc == null ? void 0 : sc.featureFlags) || [];
|
|
1007
|
+
if (flags.length === 0) {
|
|
1008
|
+
content = `<div style="color:${MUTED};padding:16px;text-align:center">No feature flags configured</div>`;
|
|
1009
|
+
} else {
|
|
1010
|
+
const flagCards = flags.map((flag) => {
|
|
1011
|
+
var _a2, _b2;
|
|
1012
|
+
const isEnabled = this.isFeatureEnabled(flag.key);
|
|
1013
|
+
const statusColor = isEnabled ? GREEN : MUTED;
|
|
1014
|
+
const statusIcon = isEnabled ? "\u2713" : "\u2717";
|
|
1015
|
+
const details = [];
|
|
1016
|
+
if (flag.rolloutPercentage !== void 0 && flag.rolloutPercentage < 100) {
|
|
1017
|
+
const bucket = this.getUserBucket(flag.key);
|
|
1018
|
+
details.push(`Rollout: ${flag.rolloutPercentage}% (bucket: ${bucket.toFixed(0)})`);
|
|
1019
|
+
}
|
|
1020
|
+
if (flag.betaUsersOnly) {
|
|
1021
|
+
const isBeta = (_b2 = (_a2 = this.siteConfig) == null ? void 0 : _a2.betaUserEmails) == null ? void 0 : _b2.includes(this.config.userEmail || "");
|
|
1022
|
+
details.push(`Beta only: ${isBeta ? "\u2713 enrolled" : "\u2717 not enrolled"}`);
|
|
1023
|
+
}
|
|
1024
|
+
if (flag.availableOnLocalhost) {
|
|
1025
|
+
const isLocal = typeof window !== "undefined" && (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1");
|
|
1026
|
+
details.push(`Localhost: ${isLocal ? "\u2713" : "\u2717"}`);
|
|
1027
|
+
}
|
|
1028
|
+
if (flag.targetEmails && flag.targetEmails.length > 0) {
|
|
1029
|
+
const isTargeted = flag.targetEmails.includes(this.config.userEmail || "");
|
|
1030
|
+
details.push(`Email targeted: ${isTargeted ? "\u2713" : "\u2717"}`);
|
|
1031
|
+
}
|
|
1032
|
+
if (flag.excludeEmails && flag.excludeEmails.length > 0) {
|
|
1033
|
+
const isExcluded = flag.excludeEmails.includes(this.config.userEmail || "");
|
|
1034
|
+
details.push(`Excluded: ${isExcluded ? "\u2713" : "\u2717"}`);
|
|
1035
|
+
}
|
|
1036
|
+
if (flag.targetAttributes && flag.targetAttributes.length > 0) {
|
|
1037
|
+
details.push(`Custom attributes: ${flag.targetAttributes.length} rule(s)`);
|
|
1038
|
+
}
|
|
1039
|
+
if (flag.variants && flag.variants.length > 0) {
|
|
1040
|
+
const variant = this.getFeatureVariant(flag.key);
|
|
1041
|
+
details.push(`Variant: ${variant || flag.defaultVariant || "default"}`);
|
|
1042
|
+
}
|
|
1043
|
+
const detailsHtml = details.length > 0 ? `<div style="font-size:9px;color:${MUTED};margin-top:3px;line-height:1.4">${details.join(" \u2022 ")}</div>` : "";
|
|
1044
|
+
return `
|
|
1045
|
+
<div style="padding:6px 8px;margin:4px 0;background:rgba(255,255,255,0.03);border-radius:6px;border-left:3px solid ${statusColor}">
|
|
1046
|
+
<div style="display:flex;justify-content:space-between;align-items:center">
|
|
1047
|
+
<div style="flex:1">
|
|
1048
|
+
<div style="display:flex;align-items:center;gap:6px">
|
|
1049
|
+
<span style="color:${statusColor};font-weight:600;font-size:10px">${statusIcon}</span>
|
|
1050
|
+
<span style="color:${TEXT};font-weight:600;font-family:monospace;font-size:10px">${flag.key}</span>
|
|
1051
|
+
${!flag.enabled ? badge("DISABLED", MUTED) : ""}
|
|
1052
|
+
</div>
|
|
1053
|
+
${flag.name ? `<div style="color:${MUTED};font-size:9px;margin-top:2px">${flag.name}</div>` : ""}
|
|
1054
|
+
${detailsHtml}
|
|
1055
|
+
</div>
|
|
1056
|
+
</div>
|
|
1057
|
+
</div>
|
|
1058
|
+
`;
|
|
1059
|
+
}).join("");
|
|
1060
|
+
const summary = `<div style="display:flex;justify-content:space-between;margin-bottom:8px;padding:4px 0;border-bottom:1px solid ${BORDER}"><span style="color:${MUTED};font-size:9px">TOTAL: ${flags.length}</span><span style="color:${MUTED};font-size:9px">USER: ${this.config.userEmail || this.config.userId || "anonymous"}</span></div>`;
|
|
1061
|
+
content = summary + `<div style="max-height:250px;overflow-y:auto">${flagCards}</div>`;
|
|
1062
|
+
}
|
|
1015
1063
|
} else if (tab === "test") {
|
|
1016
1064
|
const btnStyle = (color) => `background:${color};border:none;color:#fff;cursor:pointer;font-size:10px;font-family:inherit;padding:4px 10px;border-radius:4px;transition:opacity 0.1s`;
|
|
1017
1065
|
const sectionLabel = (text) => `<div style="color:${MUTED};font-size:9px;text-transform:uppercase;letter-spacing:0.08em;margin-bottom:6px;margin-top:10px">${text}</div>`;
|
|
@@ -1053,7 +1101,7 @@ var _SiteManager = class _SiteManager {
|
|
|
1053
1101
|
<div style="background:rgba(255,255,255,0.04);border-bottom:1px solid ${BORDER};padding:7px 10px;display:flex;align-items:center;gap:6px;cursor:pointer" id="__ft_dbg_hdr__">
|
|
1054
1102
|
<span style="color:${ACCENT};font-weight:700;font-size:10px;letter-spacing:0.05em">⬡ FEATURELY DEBUG</span>
|
|
1055
1103
|
<span style="flex:1"></span>
|
|
1056
|
-
${minimized ? "" : `<div style="display:flex;gap:4px;flex-wrap:wrap">${tabBtn("sdk", "SDK")}${tabBtn("logs", "Logs", this.errorCount)}${tabBtn("network", "Network")}${tabBtn("events", "Events")}${tabBtn("test", "Test")}</div>`}
|
|
1104
|
+
${minimized ? "" : `<div style="display:flex;gap:4px;flex-wrap:wrap">${tabBtn("sdk", "SDK")}${tabBtn("flags", "Flags", ((_f = sc == null ? void 0 : sc.featureFlags) == null ? void 0 : _f.length) || 0)}${tabBtn("logs", "Logs", this.errorCount)}${tabBtn("network", "Network")}${tabBtn("events", "Events")}${tabBtn("test", "Test")}</div>`}
|
|
1057
1105
|
<button id="__ft_dbg_min__" style="background:none;border:none;color:${MUTED};cursor:pointer;font-size:14px;line-height:1;padding:0 2px" title="${minimized ? "Expand" : "Minimize"}">${minimized ? "⬆" : "⬇"}</button>
|
|
1058
1106
|
<button id="__ft_dbg_cls__" style="background:none;border:none;color:${MUTED};cursor:pointer;font-size:14px;line-height:1;padding:0 2px" title="Close">×</button>
|
|
1059
1107
|
</div>
|
|
@@ -1694,7 +1742,7 @@ var _SiteManager = class _SiteManager {
|
|
|
1694
1742
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1695
1743
|
const versionToCheck = currentVersion || this.config.appVersion;
|
|
1696
1744
|
if (!versionToCheck) {
|
|
1697
|
-
|
|
1745
|
+
this.debugLog("warn", "[version-check] appVersion not provided");
|
|
1698
1746
|
return null;
|
|
1699
1747
|
}
|
|
1700
1748
|
try {
|
|
@@ -1773,7 +1821,7 @@ var _SiteManager = class _SiteManager {
|
|
|
1773
1821
|
}
|
|
1774
1822
|
return versionInfo;
|
|
1775
1823
|
} catch (error) {
|
|
1776
|
-
|
|
1824
|
+
this.debugLog("error", `[version-check] ${error instanceof Error ? error.message : String(error)}`);
|
|
1777
1825
|
if (this.config.onError) {
|
|
1778
1826
|
this.config.onError(
|
|
1779
1827
|
error instanceof Error ? error : new Error("Failed to check version")
|
|
@@ -1899,21 +1947,30 @@ var _SiteManager = class _SiteManager {
|
|
|
1899
1947
|
*/
|
|
1900
1948
|
evaluateFeatureFlag(flag) {
|
|
1901
1949
|
var _a, _b;
|
|
1950
|
+
if (flag.availableOnLocalhost && this.isLocalhost()) {
|
|
1951
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": ENABLED (localhost bypass)`);
|
|
1952
|
+
return true;
|
|
1953
|
+
}
|
|
1902
1954
|
if (!flag.enabled) {
|
|
1955
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": DISABLED (flag.enabled=false)`);
|
|
1903
1956
|
return false;
|
|
1904
1957
|
}
|
|
1905
1958
|
const userEmail = this.config.userEmail;
|
|
1906
1959
|
if (flag.excludeEmails && userEmail && flag.excludeEmails.includes(userEmail)) {
|
|
1960
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": DISABLED (user ${userEmail} excluded)`);
|
|
1907
1961
|
return false;
|
|
1908
1962
|
}
|
|
1909
1963
|
if (flag.betaUsersOnly) {
|
|
1910
1964
|
if (!userEmail) {
|
|
1965
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": DISABLED (beta-only, no email provided)`);
|
|
1911
1966
|
return false;
|
|
1912
1967
|
}
|
|
1913
1968
|
const betaUsers = ((_a = this.siteConfig) == null ? void 0 : _a.betaUserEmails) || [];
|
|
1914
1969
|
if (!betaUsers.includes(userEmail)) {
|
|
1970
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": DISABLED (beta-only, ${userEmail} not in beta list)`);
|
|
1915
1971
|
return false;
|
|
1916
1972
|
}
|
|
1973
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": beta check passed for ${userEmail}`);
|
|
1917
1974
|
}
|
|
1918
1975
|
if (flag.targetAttributes && flag.targetAttributes.length > 0) {
|
|
1919
1976
|
const attrs = (_b = this.config.customAttributes) != null ? _b : {};
|
|
@@ -1935,18 +1992,27 @@ var _SiteManager = class _SiteManager {
|
|
|
1935
1992
|
return false;
|
|
1936
1993
|
}
|
|
1937
1994
|
});
|
|
1938
|
-
if (!allMatch)
|
|
1995
|
+
if (!allMatch) {
|
|
1996
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": DISABLED (custom attribute rules not matched)`);
|
|
1997
|
+
return false;
|
|
1998
|
+
}
|
|
1999
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": custom attribute rules matched`);
|
|
1939
2000
|
}
|
|
1940
2001
|
if (flag.targetEmails && flag.targetEmails.length > 0) {
|
|
1941
2002
|
if (!userEmail || !flag.targetEmails.includes(userEmail)) {
|
|
2003
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": DISABLED (not in targetEmails)`);
|
|
1942
2004
|
return false;
|
|
1943
2005
|
}
|
|
2006
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": ENABLED (user in targetEmails)`);
|
|
1944
2007
|
return true;
|
|
1945
2008
|
}
|
|
1946
2009
|
if (flag.rolloutPercentage !== void 0 && flag.rolloutPercentage < 100) {
|
|
1947
2010
|
const bucket = this.getUserBucket(flag.key);
|
|
1948
|
-
|
|
2011
|
+
const enabled = bucket < flag.rolloutPercentage;
|
|
2012
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": ${enabled ? "ENABLED" : "DISABLED"} (rollout ${flag.rolloutPercentage}%, bucket ${bucket})`);
|
|
2013
|
+
return enabled;
|
|
1949
2014
|
}
|
|
2015
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": ENABLED (no restrictions)`);
|
|
1950
2016
|
return true;
|
|
1951
2017
|
}
|
|
1952
2018
|
/**
|
|
@@ -1963,6 +2029,16 @@ var _SiteManager = class _SiteManager {
|
|
|
1963
2029
|
this.featureFlagBuckets.set(flagKey, bucket);
|
|
1964
2030
|
return bucket;
|
|
1965
2031
|
}
|
|
2032
|
+
/**
|
|
2033
|
+
* Check if the current environment is localhost
|
|
2034
|
+
*/
|
|
2035
|
+
isLocalhost() {
|
|
2036
|
+
if (typeof window === "undefined") {
|
|
2037
|
+
return false;
|
|
2038
|
+
}
|
|
2039
|
+
const hostname = window.location.hostname;
|
|
2040
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
2041
|
+
}
|
|
1966
2042
|
/**
|
|
1967
2043
|
* Simple hash function for consistent bucketing
|
|
1968
2044
|
*/
|
|
@@ -2173,7 +2249,7 @@ var _SiteManager = class _SiteManager {
|
|
|
2173
2249
|
const regex = new RegExp(escapedPattern);
|
|
2174
2250
|
return regex.test(currentPath);
|
|
2175
2251
|
} catch (e) {
|
|
2176
|
-
|
|
2252
|
+
this.debugLog("warn", `[message] Invalid pattern: ${pattern}`);
|
|
2177
2253
|
return false;
|
|
2178
2254
|
}
|
|
2179
2255
|
});
|
|
@@ -2277,7 +2353,7 @@ var _SiteManager = class _SiteManager {
|
|
|
2277
2353
|
}
|
|
2278
2354
|
handleMessageAction(action, message) {
|
|
2279
2355
|
var _a;
|
|
2280
|
-
|
|
2356
|
+
this.debugLog("info", `[message-action] ${action} on message ${message.id}`);
|
|
2281
2357
|
if ((_a = message.cta) == null ? void 0 : _a.url) {
|
|
2282
2358
|
window.location.href = message.cta.url;
|
|
2283
2359
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import DOMPurify from "dompurify";
|
|
3
|
-
var SDK_VERSION = "1.3.
|
|
3
|
+
var SDK_VERSION = "1.3.1";
|
|
4
4
|
var _SiteManager = class _SiteManager {
|
|
5
5
|
constructor(config) {
|
|
6
6
|
this.siteConfig = null;
|
|
@@ -117,9 +117,6 @@ var _SiteManager = class _SiteManager {
|
|
|
117
117
|
*/
|
|
118
118
|
async init() {
|
|
119
119
|
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
120
|
-
console.warn(
|
|
121
|
-
"Featurely Site Manager: Can only be initialized in a browser environment"
|
|
122
|
-
);
|
|
123
120
|
return;
|
|
124
121
|
}
|
|
125
122
|
if (this.config.debugMode) {
|
|
@@ -223,6 +220,7 @@ var _SiteManager = class _SiteManager {
|
|
|
223
220
|
return defaultValue;
|
|
224
221
|
}
|
|
225
222
|
const flag = this.siteConfig.featureFlags.find((f) => f.key === flagKey);
|
|
223
|
+
this.debugLog("info", `[flag-eval] Checking "${flagKey}" - found: ${!!flag}, enabled: ${(flag == null ? void 0 : flag.enabled) || false}`);
|
|
226
224
|
if (!flag || !flag.enabled) {
|
|
227
225
|
return defaultValue;
|
|
228
226
|
}
|
|
@@ -502,15 +500,15 @@ var _SiteManager = class _SiteManager {
|
|
|
502
500
|
const errorData = await response.json().catch(() => ({}));
|
|
503
501
|
const message = errorData.error || response.statusText;
|
|
504
502
|
if (response.status === 401) {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
503
|
+
this.debugLog(
|
|
504
|
+
"error",
|
|
505
|
+
`[config] Invalid or missing API key. ${message}. Check your API key at https://www.featurely.no/dashboard/settings`
|
|
508
506
|
);
|
|
509
507
|
this.stopPolling();
|
|
510
508
|
} else if (response.status === 403) {
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
509
|
+
this.debugLog(
|
|
510
|
+
"error",
|
|
511
|
+
`[config] Permission denied. ${message}. Ensure your API key has the required permissions at https://www.featurely.no/dashboard/settings`
|
|
514
512
|
);
|
|
515
513
|
this.stopPolling();
|
|
516
514
|
}
|
|
@@ -662,23 +660,17 @@ var _SiteManager = class _SiteManager {
|
|
|
662
660
|
if (this.consecutiveFetchFailures <= _SiteManager.MAX_CONSECUTIVE_FAILURES) {
|
|
663
661
|
const isNetworkError = error instanceof TypeError && (error.message.includes("NetworkError") || error.message.includes("Failed to fetch") || error.message.includes("fetch"));
|
|
664
662
|
if (isNetworkError) {
|
|
665
|
-
|
|
666
|
-
\u2192 If your site has a Content-Security-Policy, add 'https://www.featurely.no' to the connect-src directive.
|
|
667
|
-
Example: connect-src 'self' https://www.featurely.no ...`;
|
|
668
|
-
console.error(cspMsg);
|
|
669
|
-
this.debugLog("error", "[config] possible CSP block \u2014 check connect-src");
|
|
663
|
+
this.debugLog("error", "[config] Network error - request blocked. Check Content-Security-Policy connect-src directive includes https://www.featurely.no");
|
|
670
664
|
} else {
|
|
671
|
-
|
|
665
|
+
this.debugLog("error", `[config] Failed to fetch configuration: ${error instanceof Error ? error.message : String(error)}`);
|
|
672
666
|
}
|
|
673
667
|
if (this.config.onError && error instanceof Error) {
|
|
674
668
|
this.config.onError(error);
|
|
675
669
|
}
|
|
676
670
|
} else if (this.consecutiveFetchFailures === _SiteManager.MAX_CONSECUTIVE_FAILURES + 1) {
|
|
677
|
-
const silenceMsg = `Featurely Site Manager: Silencing repeated fetch errors after ${_SiteManager.MAX_CONSECUTIVE_FAILURES} failures. Check your apiUrl and Content-Security-Policy configuration.`;
|
|
678
|
-
console.error(silenceMsg);
|
|
679
671
|
this.debugLog(
|
|
680
672
|
"error",
|
|
681
|
-
`[config]
|
|
673
|
+
`[config] Silencing repeated fetch errors after ${_SiteManager.MAX_CONSECUTIVE_FAILURES} consecutive failures. Check your apiUrl and Content-Security-Policy configuration.`
|
|
682
674
|
);
|
|
683
675
|
}
|
|
684
676
|
}
|
|
@@ -761,7 +753,6 @@ var _SiteManager = class _SiteManager {
|
|
|
761
753
|
if (!res.ok) {
|
|
762
754
|
const body = await res.text().catch(() => "(unreadable)");
|
|
763
755
|
const errDetail = `[analytics] batch rejected: ${res.status} ${res.statusText} \u2014 ${body}`;
|
|
764
|
-
console.error(`[Featurely] ${errDetail}`);
|
|
765
756
|
this.debugLog("error", errDetail);
|
|
766
757
|
this.errorCount++;
|
|
767
758
|
}
|
|
@@ -769,7 +760,6 @@ var _SiteManager = class _SiteManager {
|
|
|
769
760
|
const sentViaBeacon = typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function" && navigator.sendBeacon(url, payload);
|
|
770
761
|
const errMsg = fetchError instanceof Error ? fetchError.message : String(fetchError);
|
|
771
762
|
if (!sentViaBeacon) {
|
|
772
|
-
console.error(`[Featurely] Failed to send analytics batch:`, fetchError);
|
|
773
763
|
this.debugLog("error", `[analytics] batch send failed: ${errMsg}`);
|
|
774
764
|
this.errorCount++;
|
|
775
765
|
} else {
|
|
@@ -886,12 +876,12 @@ var _SiteManager = class _SiteManager {
|
|
|
886
876
|
this.debugOverlayEl = el;
|
|
887
877
|
this.setupGlobalErrorCapture();
|
|
888
878
|
this.debugLog("info", `[site-manager] debug overlay initialized`);
|
|
889
|
-
this.debugLog("info", `[site-manager]
|
|
879
|
+
this.debugLog("info", `[site-manager] v${SDK_VERSION} | project: ${this.config.projectId}`);
|
|
890
880
|
this.renderDebugOverlay();
|
|
891
881
|
this.debugRefreshId = setInterval(() => this.renderDebugOverlay(), 1500);
|
|
892
882
|
}
|
|
893
883
|
renderDebugOverlay() {
|
|
894
|
-
var _a, _b, _c, _d, _e;
|
|
884
|
+
var _a, _b, _c, _d, _e, _f;
|
|
895
885
|
const el = this.debugOverlayEl;
|
|
896
886
|
if (!el) return;
|
|
897
887
|
const ACCENT = "#6366f1";
|
|
@@ -918,7 +908,7 @@ var _SiteManager = class _SiteManager {
|
|
|
918
908
|
const flagCount = (_c = (_b = sc == null ? void 0 : sc.featureFlags) == null ? void 0 : _b.length) != null ? _c : 0;
|
|
919
909
|
const msgCount = (_e = (_d = sc == null ? void 0 : sc.messages) == null ? void 0 : _d.length) != null ? _e : 0;
|
|
920
910
|
content = `
|
|
921
|
-
${row("SDK Version",
|
|
911
|
+
${row("SDK Version", SDK_VERSION)}
|
|
922
912
|
${row("Project", cfg.projectId)}
|
|
923
913
|
${row("Hostname", typeof window !== "undefined" ? window.location.hostname : "\u2014")}
|
|
924
914
|
${row("API URL", cfg.apiUrl)}
|
|
@@ -976,6 +966,64 @@ var _SiteManager = class _SiteManager {
|
|
|
976
966
|
(e) => `<div style="padding:2px 0;display:flex;justify-content:space-between;border-bottom:1px solid rgba(255,255,255,0.03)"><span style="color:#a5b4fc">${e.name}</span><span style="color:${MUTED}">${e.ts}</span></div>`
|
|
977
967
|
).join("");
|
|
978
968
|
}
|
|
969
|
+
} else if (tab === "flags") {
|
|
970
|
+
const flags = (sc == null ? void 0 : sc.featureFlags) || [];
|
|
971
|
+
if (flags.length === 0) {
|
|
972
|
+
content = `<div style="color:${MUTED};padding:16px;text-align:center">No feature flags configured</div>`;
|
|
973
|
+
} else {
|
|
974
|
+
const flagCards = flags.map((flag) => {
|
|
975
|
+
var _a2, _b2;
|
|
976
|
+
const isEnabled = this.isFeatureEnabled(flag.key);
|
|
977
|
+
const statusColor = isEnabled ? GREEN : MUTED;
|
|
978
|
+
const statusIcon = isEnabled ? "\u2713" : "\u2717";
|
|
979
|
+
const details = [];
|
|
980
|
+
if (flag.rolloutPercentage !== void 0 && flag.rolloutPercentage < 100) {
|
|
981
|
+
const bucket = this.getUserBucket(flag.key);
|
|
982
|
+
details.push(`Rollout: ${flag.rolloutPercentage}% (bucket: ${bucket.toFixed(0)})`);
|
|
983
|
+
}
|
|
984
|
+
if (flag.betaUsersOnly) {
|
|
985
|
+
const isBeta = (_b2 = (_a2 = this.siteConfig) == null ? void 0 : _a2.betaUserEmails) == null ? void 0 : _b2.includes(this.config.userEmail || "");
|
|
986
|
+
details.push(`Beta only: ${isBeta ? "\u2713 enrolled" : "\u2717 not enrolled"}`);
|
|
987
|
+
}
|
|
988
|
+
if (flag.availableOnLocalhost) {
|
|
989
|
+
const isLocal = typeof window !== "undefined" && (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1");
|
|
990
|
+
details.push(`Localhost: ${isLocal ? "\u2713" : "\u2717"}`);
|
|
991
|
+
}
|
|
992
|
+
if (flag.targetEmails && flag.targetEmails.length > 0) {
|
|
993
|
+
const isTargeted = flag.targetEmails.includes(this.config.userEmail || "");
|
|
994
|
+
details.push(`Email targeted: ${isTargeted ? "\u2713" : "\u2717"}`);
|
|
995
|
+
}
|
|
996
|
+
if (flag.excludeEmails && flag.excludeEmails.length > 0) {
|
|
997
|
+
const isExcluded = flag.excludeEmails.includes(this.config.userEmail || "");
|
|
998
|
+
details.push(`Excluded: ${isExcluded ? "\u2713" : "\u2717"}`);
|
|
999
|
+
}
|
|
1000
|
+
if (flag.targetAttributes && flag.targetAttributes.length > 0) {
|
|
1001
|
+
details.push(`Custom attributes: ${flag.targetAttributes.length} rule(s)`);
|
|
1002
|
+
}
|
|
1003
|
+
if (flag.variants && flag.variants.length > 0) {
|
|
1004
|
+
const variant = this.getFeatureVariant(flag.key);
|
|
1005
|
+
details.push(`Variant: ${variant || flag.defaultVariant || "default"}`);
|
|
1006
|
+
}
|
|
1007
|
+
const detailsHtml = details.length > 0 ? `<div style="font-size:9px;color:${MUTED};margin-top:3px;line-height:1.4">${details.join(" \u2022 ")}</div>` : "";
|
|
1008
|
+
return `
|
|
1009
|
+
<div style="padding:6px 8px;margin:4px 0;background:rgba(255,255,255,0.03);border-radius:6px;border-left:3px solid ${statusColor}">
|
|
1010
|
+
<div style="display:flex;justify-content:space-between;align-items:center">
|
|
1011
|
+
<div style="flex:1">
|
|
1012
|
+
<div style="display:flex;align-items:center;gap:6px">
|
|
1013
|
+
<span style="color:${statusColor};font-weight:600;font-size:10px">${statusIcon}</span>
|
|
1014
|
+
<span style="color:${TEXT};font-weight:600;font-family:monospace;font-size:10px">${flag.key}</span>
|
|
1015
|
+
${!flag.enabled ? badge("DISABLED", MUTED) : ""}
|
|
1016
|
+
</div>
|
|
1017
|
+
${flag.name ? `<div style="color:${MUTED};font-size:9px;margin-top:2px">${flag.name}</div>` : ""}
|
|
1018
|
+
${detailsHtml}
|
|
1019
|
+
</div>
|
|
1020
|
+
</div>
|
|
1021
|
+
</div>
|
|
1022
|
+
`;
|
|
1023
|
+
}).join("");
|
|
1024
|
+
const summary = `<div style="display:flex;justify-content:space-between;margin-bottom:8px;padding:4px 0;border-bottom:1px solid ${BORDER}"><span style="color:${MUTED};font-size:9px">TOTAL: ${flags.length}</span><span style="color:${MUTED};font-size:9px">USER: ${this.config.userEmail || this.config.userId || "anonymous"}</span></div>`;
|
|
1025
|
+
content = summary + `<div style="max-height:250px;overflow-y:auto">${flagCards}</div>`;
|
|
1026
|
+
}
|
|
979
1027
|
} else if (tab === "test") {
|
|
980
1028
|
const btnStyle = (color) => `background:${color};border:none;color:#fff;cursor:pointer;font-size:10px;font-family:inherit;padding:4px 10px;border-radius:4px;transition:opacity 0.1s`;
|
|
981
1029
|
const sectionLabel = (text) => `<div style="color:${MUTED};font-size:9px;text-transform:uppercase;letter-spacing:0.08em;margin-bottom:6px;margin-top:10px">${text}</div>`;
|
|
@@ -1017,7 +1065,7 @@ var _SiteManager = class _SiteManager {
|
|
|
1017
1065
|
<div style="background:rgba(255,255,255,0.04);border-bottom:1px solid ${BORDER};padding:7px 10px;display:flex;align-items:center;gap:6px;cursor:pointer" id="__ft_dbg_hdr__">
|
|
1018
1066
|
<span style="color:${ACCENT};font-weight:700;font-size:10px;letter-spacing:0.05em">⬡ FEATURELY DEBUG</span>
|
|
1019
1067
|
<span style="flex:1"></span>
|
|
1020
|
-
${minimized ? "" : `<div style="display:flex;gap:4px;flex-wrap:wrap">${tabBtn("sdk", "SDK")}${tabBtn("logs", "Logs", this.errorCount)}${tabBtn("network", "Network")}${tabBtn("events", "Events")}${tabBtn("test", "Test")}</div>`}
|
|
1068
|
+
${minimized ? "" : `<div style="display:flex;gap:4px;flex-wrap:wrap">${tabBtn("sdk", "SDK")}${tabBtn("flags", "Flags", ((_f = sc == null ? void 0 : sc.featureFlags) == null ? void 0 : _f.length) || 0)}${tabBtn("logs", "Logs", this.errorCount)}${tabBtn("network", "Network")}${tabBtn("events", "Events")}${tabBtn("test", "Test")}</div>`}
|
|
1021
1069
|
<button id="__ft_dbg_min__" style="background:none;border:none;color:${MUTED};cursor:pointer;font-size:14px;line-height:1;padding:0 2px" title="${minimized ? "Expand" : "Minimize"}">${minimized ? "⬆" : "⬇"}</button>
|
|
1022
1070
|
<button id="__ft_dbg_cls__" style="background:none;border:none;color:${MUTED};cursor:pointer;font-size:14px;line-height:1;padding:0 2px" title="Close">×</button>
|
|
1023
1071
|
</div>
|
|
@@ -1658,7 +1706,7 @@ var _SiteManager = class _SiteManager {
|
|
|
1658
1706
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1659
1707
|
const versionToCheck = currentVersion || this.config.appVersion;
|
|
1660
1708
|
if (!versionToCheck) {
|
|
1661
|
-
|
|
1709
|
+
this.debugLog("warn", "[version-check] appVersion not provided");
|
|
1662
1710
|
return null;
|
|
1663
1711
|
}
|
|
1664
1712
|
try {
|
|
@@ -1737,7 +1785,7 @@ var _SiteManager = class _SiteManager {
|
|
|
1737
1785
|
}
|
|
1738
1786
|
return versionInfo;
|
|
1739
1787
|
} catch (error) {
|
|
1740
|
-
|
|
1788
|
+
this.debugLog("error", `[version-check] ${error instanceof Error ? error.message : String(error)}`);
|
|
1741
1789
|
if (this.config.onError) {
|
|
1742
1790
|
this.config.onError(
|
|
1743
1791
|
error instanceof Error ? error : new Error("Failed to check version")
|
|
@@ -1863,21 +1911,30 @@ var _SiteManager = class _SiteManager {
|
|
|
1863
1911
|
*/
|
|
1864
1912
|
evaluateFeatureFlag(flag) {
|
|
1865
1913
|
var _a, _b;
|
|
1914
|
+
if (flag.availableOnLocalhost && this.isLocalhost()) {
|
|
1915
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": ENABLED (localhost bypass)`);
|
|
1916
|
+
return true;
|
|
1917
|
+
}
|
|
1866
1918
|
if (!flag.enabled) {
|
|
1919
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": DISABLED (flag.enabled=false)`);
|
|
1867
1920
|
return false;
|
|
1868
1921
|
}
|
|
1869
1922
|
const userEmail = this.config.userEmail;
|
|
1870
1923
|
if (flag.excludeEmails && userEmail && flag.excludeEmails.includes(userEmail)) {
|
|
1924
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": DISABLED (user ${userEmail} excluded)`);
|
|
1871
1925
|
return false;
|
|
1872
1926
|
}
|
|
1873
1927
|
if (flag.betaUsersOnly) {
|
|
1874
1928
|
if (!userEmail) {
|
|
1929
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": DISABLED (beta-only, no email provided)`);
|
|
1875
1930
|
return false;
|
|
1876
1931
|
}
|
|
1877
1932
|
const betaUsers = ((_a = this.siteConfig) == null ? void 0 : _a.betaUserEmails) || [];
|
|
1878
1933
|
if (!betaUsers.includes(userEmail)) {
|
|
1934
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": DISABLED (beta-only, ${userEmail} not in beta list)`);
|
|
1879
1935
|
return false;
|
|
1880
1936
|
}
|
|
1937
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": beta check passed for ${userEmail}`);
|
|
1881
1938
|
}
|
|
1882
1939
|
if (flag.targetAttributes && flag.targetAttributes.length > 0) {
|
|
1883
1940
|
const attrs = (_b = this.config.customAttributes) != null ? _b : {};
|
|
@@ -1899,18 +1956,27 @@ var _SiteManager = class _SiteManager {
|
|
|
1899
1956
|
return false;
|
|
1900
1957
|
}
|
|
1901
1958
|
});
|
|
1902
|
-
if (!allMatch)
|
|
1959
|
+
if (!allMatch) {
|
|
1960
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": DISABLED (custom attribute rules not matched)`);
|
|
1961
|
+
return false;
|
|
1962
|
+
}
|
|
1963
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": custom attribute rules matched`);
|
|
1903
1964
|
}
|
|
1904
1965
|
if (flag.targetEmails && flag.targetEmails.length > 0) {
|
|
1905
1966
|
if (!userEmail || !flag.targetEmails.includes(userEmail)) {
|
|
1967
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": DISABLED (not in targetEmails)`);
|
|
1906
1968
|
return false;
|
|
1907
1969
|
}
|
|
1970
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": ENABLED (user in targetEmails)`);
|
|
1908
1971
|
return true;
|
|
1909
1972
|
}
|
|
1910
1973
|
if (flag.rolloutPercentage !== void 0 && flag.rolloutPercentage < 100) {
|
|
1911
1974
|
const bucket = this.getUserBucket(flag.key);
|
|
1912
|
-
|
|
1975
|
+
const enabled = bucket < flag.rolloutPercentage;
|
|
1976
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": ${enabled ? "ENABLED" : "DISABLED"} (rollout ${flag.rolloutPercentage}%, bucket ${bucket})`);
|
|
1977
|
+
return enabled;
|
|
1913
1978
|
}
|
|
1979
|
+
this.debugLog("info", `[flag-eval] "${flag.key}": ENABLED (no restrictions)`);
|
|
1914
1980
|
return true;
|
|
1915
1981
|
}
|
|
1916
1982
|
/**
|
|
@@ -1927,6 +1993,16 @@ var _SiteManager = class _SiteManager {
|
|
|
1927
1993
|
this.featureFlagBuckets.set(flagKey, bucket);
|
|
1928
1994
|
return bucket;
|
|
1929
1995
|
}
|
|
1996
|
+
/**
|
|
1997
|
+
* Check if the current environment is localhost
|
|
1998
|
+
*/
|
|
1999
|
+
isLocalhost() {
|
|
2000
|
+
if (typeof window === "undefined") {
|
|
2001
|
+
return false;
|
|
2002
|
+
}
|
|
2003
|
+
const hostname = window.location.hostname;
|
|
2004
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
2005
|
+
}
|
|
1930
2006
|
/**
|
|
1931
2007
|
* Simple hash function for consistent bucketing
|
|
1932
2008
|
*/
|
|
@@ -2137,7 +2213,7 @@ var _SiteManager = class _SiteManager {
|
|
|
2137
2213
|
const regex = new RegExp(escapedPattern);
|
|
2138
2214
|
return regex.test(currentPath);
|
|
2139
2215
|
} catch (e) {
|
|
2140
|
-
|
|
2216
|
+
this.debugLog("warn", `[message] Invalid pattern: ${pattern}`);
|
|
2141
2217
|
return false;
|
|
2142
2218
|
}
|
|
2143
2219
|
});
|
|
@@ -2241,7 +2317,7 @@ var _SiteManager = class _SiteManager {
|
|
|
2241
2317
|
}
|
|
2242
2318
|
handleMessageAction(action, message) {
|
|
2243
2319
|
var _a;
|
|
2244
|
-
|
|
2320
|
+
this.debugLog("info", `[message-action] ${action} on message ${message.id}`);
|
|
2245
2321
|
if ((_a = message.cta) == null ? void 0 : _a.url) {
|
|
2246
2322
|
window.location.href = message.cta.url;
|
|
2247
2323
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "featurely-site-manager",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.1",
|
|
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",
|
|
@@ -59,7 +59,6 @@
|
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"dompurify": "^3.3.3",
|
|
61
61
|
"featurely-error-tracker": "^1.0.23",
|
|
62
|
-
"featurely-mcp": "^1.0.6"
|
|
63
|
-
"featurely-site-manager": "^1.2.0"
|
|
62
|
+
"featurely-mcp": "^1.0.6"
|
|
64
63
|
}
|
|
65
64
|
}
|