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 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 | 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 |
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, // 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
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('purchase', 4999, 'USD', {
230
- productId: 'pro-plan',
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('signup_completed', {
236
- plan: 'pro',
237
- source: 'landing-page',
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.29";
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.29";
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.1.29";
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 = (_a = this.config.customAttributes) != null ? _a : {};
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.1.29";
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 = (_a = this.config.customAttributes) != null ? _a : {};
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.2.0",
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
  }