featurely-site-manager 1.2.0 → 1.3.0
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/CHANGELOG.md +17 -0
- package/README.md +46 -46
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +12 -3
- package/dist/index.mjs +12 -3
- package/package.json +5 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.3.0] - 2026-04-28
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Beta Program Support**: Feature flags now support beta-only targeting
|
|
13
|
+
- `betaUsersOnly` field on feature flags - restrict features to enrolled beta users
|
|
14
|
+
- `betaUserEmails` array in site config - list of users enrolled in beta program
|
|
15
|
+
- Beta enrollment status is automatically evaluated in `isFeatureEnabled()`
|
|
16
|
+
- Percentage rollouts apply within the beta user group when `betaUsersOnly` is true
|
|
17
|
+
- Users can enroll/unenroll via project settings in the Featurely dashboard
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Feature flag evaluation now checks beta enrollment before other targeting rules
|
|
22
|
+
- Non-beta users will never see features marked with `betaUsersOnly: true`
|
|
23
|
+
- SiteConfig interface now includes `betaUserEmails` field
|
|
24
|
+
|
|
8
25
|
## [1.2.0] - 2026-04-21
|
|
9
26
|
|
|
10
27
|
### Added
|
package/README.md
CHANGED
|
@@ -30,42 +30,42 @@ if (manager.isFeatureEnabled("new-checkout")) {
|
|
|
30
30
|
|
|
31
31
|
### `SiteManagerConfig`
|
|
32
32
|
|
|
33
|
-
| Option
|
|
34
|
-
|
|
|
35
|
-
| `apiKey`
|
|
36
|
-
| `projectId`
|
|
37
|
-
| `apiUrl`
|
|
38
|
-
| `environment`
|
|
39
|
-
| `pollInterval`
|
|
40
|
-
| `userEmail`
|
|
41
|
-
| `userId`
|
|
42
|
-
| `customAttributes`
|
|
43
|
-
| `bootstrapFlags`
|
|
44
|
-
| `bypassCheck`
|
|
45
|
-
| `enableAnalytics`
|
|
46
|
-
| `analyticsFlushInterval`
|
|
47
|
-
| `appVersion`
|
|
48
|
-
| `enableVersionCheck`
|
|
49
|
-
| `versionCheckInterval`
|
|
50
|
-
| `platform`
|
|
51
|
-
| `updateRules`
|
|
52
|
-
| `autoInjectBanners`
|
|
53
|
-
| `autoCaptureClicks`
|
|
54
|
-
| `enableHeatmaps`
|
|
55
|
-
| `enableRageClickDetection`
|
|
56
|
-
| `enableScrollTracking`
|
|
57
|
-
| `enablePerformanceTracking` | `boolean`
|
|
58
|
-
| `heatmapSampleRate`
|
|
59
|
-
| `debugMode`
|
|
60
|
-
| `debugSecret`
|
|
61
|
-
| `onMaintenanceEnabled`
|
|
62
|
-
| `onMaintenanceDisabled`
|
|
63
|
-
| `onMessageReceived`
|
|
64
|
-
| `onMessageDismissed`
|
|
65
|
-
| `onFeatureFlagsUpdated`
|
|
66
|
-
| `onUpdateAvailable`
|
|
67
|
-
| `onUpdateRequired`
|
|
68
|
-
| `onError`
|
|
33
|
+
| Option | Type | Default | Description |
|
|
34
|
+
| --------------------------- | --------------------------------------------- | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
35
|
+
| `apiKey` | `string` | required | API key with `public:read` permission |
|
|
36
|
+
| `projectId` | `string` | required | Your Featurely project ID |
|
|
37
|
+
| `apiUrl` | `string` | `"https://www.featurely.no"` | Custom API base URL |
|
|
38
|
+
| `environment` | `string` | — | Hostname override or explicit environment slug (e.g. `process.env.NEXT_PUBLIC_ENV`). Use server-side where `window` is unavailable |
|
|
39
|
+
| `pollInterval` | `number` | `60000` | Config refresh interval in ms |
|
|
40
|
+
| `userEmail` | `string` | — | User email for maintenance whitelist checks |
|
|
41
|
+
| `userId` | `string` | — | User ID for flag bucketing and analytics |
|
|
42
|
+
| `customAttributes` | `Record<string, string \| number \| boolean>` | — | Custom targeting attributes for feature flag rules (e.g. `{ plan: "pro" }`) |
|
|
43
|
+
| `bootstrapFlags` | `Record<string, boolean>` | — | Pre-seed flag values from the server (SSR) to avoid flash on initial render |
|
|
44
|
+
| `bypassCheck` | `() => boolean` | — | Custom maintenance bypass logic |
|
|
45
|
+
| `enableAnalytics` | `boolean` | `true` | Track page views, web vitals, and custom events |
|
|
46
|
+
| `analyticsFlushInterval` | `number` | `60000` | Analytics send interval in ms |
|
|
47
|
+
| `appVersion` | `string` | — | Current app version for version checking |
|
|
48
|
+
| `enableVersionCheck` | `boolean` | `false` | Poll for app version updates |
|
|
49
|
+
| `versionCheckInterval` | `number` | `3600000` | Version check interval in ms |
|
|
50
|
+
| `platform` | `string` | — | Platform identifier for version-check endpoint (`"web" \| "ios" \| "android" \| "electron" \| "tauri"`) |
|
|
51
|
+
| `updateRules` | `VersionUpdateRules` | — | Override server classification per semver change type |
|
|
52
|
+
| `autoInjectBanners` | `boolean` | `true` | Inject status message banners directly into the DOM. Set `false` for custom banner UI |
|
|
53
|
+
| `autoCaptureClicks` | `boolean` | `false` | Track clicks on elements with `data-featurely-click` attribute |
|
|
54
|
+
| `enableHeatmaps` | `boolean` | `false` | Track click coordinates for heatmap visualization |
|
|
55
|
+
| `enableRageClickDetection` | `boolean` | `false` | Detect frustration patterns (5+ clicks in same area) |
|
|
56
|
+
| `enableScrollTracking` | `boolean` | `false` | Track scroll depth at 25%, 50%, 75%, 90%, 100% milestones |
|
|
57
|
+
| `enablePerformanceTracking` | `boolean` | `false` | Monitor resource timing and long tasks |
|
|
58
|
+
| `heatmapSampleRate` | `number` | `10` | Percentage of clicks to sample for heatmaps (0-100) |
|
|
59
|
+
| `debugMode` | `boolean` | `false` | Show floating debug overlay |
|
|
60
|
+
| `debugSecret` | `string` | — | Enable overlay in production via `?ft_debug=<secret>` URL param |
|
|
61
|
+
| `onMaintenanceEnabled` | `(config: MaintenanceConfig) => void` | — | Called when maintenance mode activates |
|
|
62
|
+
| `onMaintenanceDisabled` | `() => void` | — | Called when maintenance mode deactivates |
|
|
63
|
+
| `onMessageReceived` | `(message: StatusMessage) => void` | — | Called when a banner is shown (fires even with auto-injection) |
|
|
64
|
+
| `onMessageDismissed` | `(id: string) => void` | — | Called when a banner is dismissed |
|
|
65
|
+
| `onFeatureFlagsUpdated` | `(flags: FeatureFlag[]) => void` | — | Called when flag config changes |
|
|
66
|
+
| `onUpdateAvailable` | `(info: VersionCheckResponse) => void` | — | Called when a newer app version is available |
|
|
67
|
+
| `onUpdateRequired` | `(info: VersionCheckResponse) => void` | — | Called when an update is mandatory |
|
|
68
|
+
| `onError` | `(error: Error) => void` | — | Called on SDK-internal errors |
|
|
69
69
|
|
|
70
70
|
### `VersionUpdateRules`
|
|
71
71
|
|
|
@@ -217,24 +217,24 @@ const manager = new SiteManager({
|
|
|
217
217
|
apiKey: process.env.NEXT_PUBLIC_FEATURELY_API_KEY!,
|
|
218
218
|
projectId: process.env.NEXT_PUBLIC_PROJECT_ID!,
|
|
219
219
|
enableAnalytics: true,
|
|
220
|
-
enableHeatmaps: true,
|
|
221
|
-
enableRageClickDetection: true,
|
|
222
|
-
enableScrollTracking: true,
|
|
223
|
-
enablePerformanceTracking: true,
|
|
224
|
-
heatmapSampleRate: 10,
|
|
220
|
+
enableHeatmaps: true, // Click heatmaps
|
|
221
|
+
enableRageClickDetection: true, // Frustration detection
|
|
222
|
+
enableScrollTracking: true, // Scroll depth events
|
|
223
|
+
enablePerformanceTracking: true, // Resource timing & long tasks
|
|
224
|
+
heatmapSampleRate: 10, // Sample 10% of clicks
|
|
225
225
|
});
|
|
226
226
|
await manager.init();
|
|
227
227
|
|
|
228
228
|
// Track revenue
|
|
229
|
-
manager.trackRevenue(
|
|
230
|
-
productId:
|
|
229
|
+
manager.trackRevenue("purchase", 4999, "USD", {
|
|
230
|
+
productId: "pro-plan",
|
|
231
231
|
quantity: 1,
|
|
232
232
|
});
|
|
233
233
|
|
|
234
234
|
// Track custom events
|
|
235
|
-
manager.trackEvent(
|
|
236
|
-
plan:
|
|
237
|
-
source:
|
|
235
|
+
manager.trackEvent("signup_completed", {
|
|
236
|
+
plan: "pro",
|
|
237
|
+
source: "landing-page",
|
|
238
238
|
});
|
|
239
239
|
```
|
|
240
240
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare const SDK_VERSION = "1.
|
|
1
|
+
declare const SDK_VERSION = "1.3.0";
|
|
2
2
|
type MessageType = "info" | "warning" | "error" | "success";
|
|
3
3
|
type MessagePosition = "top" | "bottom";
|
|
4
4
|
type MessageStyle = "banner" | "toast";
|
|
@@ -30,6 +30,7 @@ interface FeatureFlag {
|
|
|
30
30
|
rolloutPercentage?: number;
|
|
31
31
|
targetEmails?: string[];
|
|
32
32
|
excludeEmails?: string[];
|
|
33
|
+
betaUsersOnly?: boolean;
|
|
33
34
|
variants?: {
|
|
34
35
|
key: string;
|
|
35
36
|
name: string;
|
|
@@ -101,6 +102,7 @@ interface SiteConfig {
|
|
|
101
102
|
maintenance: MaintenanceConfig;
|
|
102
103
|
messages: StatusMessage[];
|
|
103
104
|
featureFlags: FeatureFlag[];
|
|
105
|
+
betaUserEmails?: string[];
|
|
104
106
|
lastUpdated: string;
|
|
105
107
|
analyticsConfig?: AnalyticsConfig | null;
|
|
106
108
|
sdkVersions?: SdkVersionRequirement[];
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare const SDK_VERSION = "1.
|
|
1
|
+
declare const SDK_VERSION = "1.3.0";
|
|
2
2
|
type MessageType = "info" | "warning" | "error" | "success";
|
|
3
3
|
type MessagePosition = "top" | "bottom";
|
|
4
4
|
type MessageStyle = "banner" | "toast";
|
|
@@ -30,6 +30,7 @@ interface FeatureFlag {
|
|
|
30
30
|
rolloutPercentage?: number;
|
|
31
31
|
targetEmails?: string[];
|
|
32
32
|
excludeEmails?: string[];
|
|
33
|
+
betaUsersOnly?: boolean;
|
|
33
34
|
variants?: {
|
|
34
35
|
key: string;
|
|
35
36
|
name: string;
|
|
@@ -101,6 +102,7 @@ interface SiteConfig {
|
|
|
101
102
|
maintenance: MaintenanceConfig;
|
|
102
103
|
messages: StatusMessage[];
|
|
103
104
|
featureFlags: FeatureFlag[];
|
|
105
|
+
betaUserEmails?: string[];
|
|
104
106
|
lastUpdated: string;
|
|
105
107
|
analyticsConfig?: AnalyticsConfig | null;
|
|
106
108
|
sdkVersions?: SdkVersionRequirement[];
|
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.
|
|
39
|
+
var SDK_VERSION = "1.3.0";
|
|
40
40
|
var _SiteManager = class _SiteManager {
|
|
41
41
|
constructor(config) {
|
|
42
42
|
this.siteConfig = null;
|
|
@@ -1898,7 +1898,7 @@ var _SiteManager = class _SiteManager {
|
|
|
1898
1898
|
* Evaluate if a feature flag should be enabled for the current user
|
|
1899
1899
|
*/
|
|
1900
1900
|
evaluateFeatureFlag(flag) {
|
|
1901
|
-
var _a;
|
|
1901
|
+
var _a, _b;
|
|
1902
1902
|
if (!flag.enabled) {
|
|
1903
1903
|
return false;
|
|
1904
1904
|
}
|
|
@@ -1906,8 +1906,17 @@ var _SiteManager = class _SiteManager {
|
|
|
1906
1906
|
if (flag.excludeEmails && userEmail && flag.excludeEmails.includes(userEmail)) {
|
|
1907
1907
|
return false;
|
|
1908
1908
|
}
|
|
1909
|
+
if (flag.betaUsersOnly) {
|
|
1910
|
+
if (!userEmail) {
|
|
1911
|
+
return false;
|
|
1912
|
+
}
|
|
1913
|
+
const betaUsers = ((_a = this.siteConfig) == null ? void 0 : _a.betaUserEmails) || [];
|
|
1914
|
+
if (!betaUsers.includes(userEmail)) {
|
|
1915
|
+
return false;
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1909
1918
|
if (flag.targetAttributes && flag.targetAttributes.length > 0) {
|
|
1910
|
-
const attrs = (
|
|
1919
|
+
const attrs = (_b = this.config.customAttributes) != null ? _b : {};
|
|
1911
1920
|
const allMatch = flag.targetAttributes.every((rule) => {
|
|
1912
1921
|
const actual = attrs[rule.attribute];
|
|
1913
1922
|
if (actual === void 0) return false;
|
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
|
+
var SDK_VERSION = "1.3.0";
|
|
4
4
|
var _SiteManager = class _SiteManager {
|
|
5
5
|
constructor(config) {
|
|
6
6
|
this.siteConfig = null;
|
|
@@ -1862,7 +1862,7 @@ var _SiteManager = class _SiteManager {
|
|
|
1862
1862
|
* Evaluate if a feature flag should be enabled for the current user
|
|
1863
1863
|
*/
|
|
1864
1864
|
evaluateFeatureFlag(flag) {
|
|
1865
|
-
var _a;
|
|
1865
|
+
var _a, _b;
|
|
1866
1866
|
if (!flag.enabled) {
|
|
1867
1867
|
return false;
|
|
1868
1868
|
}
|
|
@@ -1870,8 +1870,17 @@ var _SiteManager = class _SiteManager {
|
|
|
1870
1870
|
if (flag.excludeEmails && userEmail && flag.excludeEmails.includes(userEmail)) {
|
|
1871
1871
|
return false;
|
|
1872
1872
|
}
|
|
1873
|
+
if (flag.betaUsersOnly) {
|
|
1874
|
+
if (!userEmail) {
|
|
1875
|
+
return false;
|
|
1876
|
+
}
|
|
1877
|
+
const betaUsers = ((_a = this.siteConfig) == null ? void 0 : _a.betaUserEmails) || [];
|
|
1878
|
+
if (!betaUsers.includes(userEmail)) {
|
|
1879
|
+
return false;
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1873
1882
|
if (flag.targetAttributes && flag.targetAttributes.length > 0) {
|
|
1874
|
-
const attrs = (
|
|
1883
|
+
const attrs = (_b = this.config.customAttributes) != null ? _b : {};
|
|
1875
1884
|
const allMatch = flag.targetAttributes.every((rule) => {
|
|
1876
1885
|
const actual = attrs[rule.attribute];
|
|
1877
1886
|
if (actual === void 0) return false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "featurely-site-manager",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
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",
|
|
@@ -57,6 +57,9 @@
|
|
|
57
57
|
"typescript": "^5.0.0"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"dompurify": "^3.3.3"
|
|
60
|
+
"dompurify": "^3.3.3",
|
|
61
|
+
"featurely-error-tracker": "^1.0.23",
|
|
62
|
+
"featurely-mcp": "^1.0.6",
|
|
63
|
+
"featurely-site-manager": "^1.2.0"
|
|
61
64
|
}
|
|
62
65
|
}
|