featurely-site-manager 1.0.7 → 1.1.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/CHANGELOG.md CHANGED
@@ -5,6 +5,51 @@ 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.1.0] - 2026-03-15
9
+
10
+ ### Added
11
+
12
+ - **Version Management**: Complete app version checking and update notification system
13
+ - Automatic version checking with configurable intervals (default: 1 hour)
14
+ - Force update support for critical releases (minimum version)
15
+ - Recommended update notifications
16
+ - `checkVersion(currentVersion?)` - Manual version checking
17
+ - `getLastVersionCheck()` - Get cached version check result
18
+ - `onUpdateAvailable` callback for optional updates
19
+ - `onUpdateRequired` callback for forced updates
20
+ - Release notes and download URL support
21
+ - Beta version flagging
22
+ - **Feature Flags**: Complete feature flag support
23
+ - `isFeatureEnabled(flagKey)` - Check if flag is enabled
24
+ - `getFeatureVariant(flagKey)` - Get A/B test variant
25
+ - `getAllFeatureFlags()` - Get all flags
26
+ - `getEnabledFeatures()` - Get enabled flags for current user
27
+ - Email-based targeting
28
+ - Percentage-based rollouts with consistent bucketing
29
+ - A/B testing with weighted variants
30
+ - `onFeatureFlagsUpdated` callback
31
+ - **Analytics**: Custom event tracking
32
+ - `trackEvent(eventName, properties)` - Track custom events
33
+ - Automatic feature flag usage tracking
34
+ - Session management
35
+ - Configurable flush intervals
36
+ - User identification support
37
+
38
+ ### Changed
39
+
40
+ - Updated package description to reflect full feature set
41
+ - Added comprehensive README documentation for all features
42
+ - Enhanced TypeScript types for version management
43
+ - Improved configuration options with granular control
44
+
45
+ ## [1.0.8] - 2026-03-15
46
+
47
+ ### Fixed
48
+
49
+ - **CRITICAL FIX**: Updated default API URL from `featurely.no` to `www.featurely.no` to prevent 307 CORS redirect issues
50
+ - Fixes NetworkError when fetching site configuration and status messages
51
+ - Resolves "Access-Control-Allow-Origin header missing" errors
52
+
8
53
  ## [1.0.7] - 2026-03-15
9
54
 
10
55
  ### Added
package/README.md CHANGED
@@ -43,6 +43,31 @@ siteManager.init();
43
43
  - Call-to-action buttons
44
44
  - Auto-expire functionality
45
45
 
46
+ ### 🚀 Version Management
47
+
48
+ - Automatic version checking
49
+ - Force updates for critical releases (minimum version)
50
+ - Recommended version notifications
51
+ - Configurable check intervals (default: 1 hour)
52
+ - Release notes and download URLs
53
+ - Beta version support
54
+ - Update callbacks for custom UI
55
+
56
+ ### 🔐 Feature Flags
57
+
58
+ - Enable/disable features remotely
59
+ - Percentage-based rollouts
60
+ - User targeting by email
61
+ - A/B testing with variants
62
+ - Consistent user bucketing
63
+
64
+ ### 📊 Analytics
65
+
66
+ - Track custom events
67
+ - Automatic feature flag usage tracking
68
+ - Session management
69
+ - User identification
70
+
46
71
  ## 📖 Usage
47
72
 
48
73
  ### Basic Setup
@@ -115,6 +140,318 @@ const manager = new SiteManager({
115
140
  manager.init();
116
141
  ```
117
142
 
143
+ ### Version Checking
144
+
145
+ ```typescript
146
+ const manager = new SiteManager({
147
+ apiKey: "ft_live_your_api_key",
148
+ projectId: "proj_your_project_id",
149
+ appVersion: "1.2.3", // Your current app version
150
+ enableVersionCheck: true,
151
+ versionCheckInterval: 3600000, // Check every hour (default)
152
+
153
+ onUpdateAvailable: (versionInfo) => {
154
+ console.log("Update available:", versionInfo.latestVersion);
155
+ // Show optional update notification to user
156
+ },
157
+
158
+ onUpdateRequired: (versionInfo) => {
159
+ console.log("Update required:", versionInfo.latestVersion);
160
+ // Force user to update (breaking changes)
161
+ if (confirm(`Update required: ${versionInfo.latestVersion.title}\n\n${versionInfo.latestVersion.releaseNotes}`)) {
162
+ window.location.href = versionInfo.latestVersion.downloadUrl || '/update';
163
+ }
164
+ },
165
+ });
166
+
167
+ manager.init();
168
+
169
+ // Manual version check
170
+ const versionStatus = await manager.checkVersion();
171
+ if (versionStatus?.updateAvailable) {
172
+ console.log("New version:", versionStatus.latestVersion);
173
+ }
174
+ ```
175
+
176
+ ### Feature Flags
177
+
178
+ ```typescript
179
+ const manager = new SiteManager({
180
+ apiKey: "ft_live_your_api_key",
181
+ projectId: "proj_your_project_id",
182
+ userEmail: "user@example.com",
183
+
184
+ onFeatureFlagsUpdated: (flags) => {
185
+ console.log("Feature flags updated:", flags);
186
+ },
187
+ });
188
+
189
+ manager.init();
190
+
191
+ // Check if a feature is enabled
192
+ if (manager.isFeatureEnabled("new-checkout")) {
193
+ // Show new checkout UI
194
+ }
195
+
196
+ // Get A/B test variant
197
+ const variant = manager.getFeatureVariant("homepage-design");
198
+ if (variant === "variant-a") {
199
+ // Show design A
200
+ }
201
+
202
+ // Get all enabled features
203
+ const enabledFeatures = manager.getEnabledFeatures();
204
+ console.log("Enabled features:", enabledFeatures);
205
+ ```
206
+
207
+ ### Analytics
208
+
209
+ ```typescript
210
+ const manager = new SiteManager({
211
+ apiKey: "ft_live_your_api_key",
212
+ projectId: "proj_your_project_id",
213
+ userId: "user_123",
214
+ enableAnalytics: true,
215
+ analyticsFlushInterval: 60000, // Flush every minute
216
+ });
217
+
218
+ manager.init();
219
+
220
+ // Track custom events
221
+ manager.trackEvent("button_clicked", {
222
+ button: "signup",
223
+ page: "/home",
224
+ });
225
+
226
+ manager.trackEvent("feature_used", {
227
+ feature: "dark_mode",
228
+ enabled: true,
229
+ });
230
+ ```
231
+
232
+ ### Custom Poll Interval
233
+
234
+ ```typescript
235
+ const manager = new SiteManager({
236
+ apiKey: "ft_live_your_api_key",
237
+ projectId: "proj_your_project_id",
238
+ pollInterval: 30000, // Check every 30 seconds instead of default 60s
239
+ });
240
+
241
+ manager.init();
242
+ ```
243
+
244
+ ## 🔧 Configuration
245
+
246
+ ### SiteManagerConfig
247
+
248
+ | Option | Type | Required | Default | Description |
249
+ | ------------------------ | --------------- | -------- | ------------------------- | ----------------------------------------- |
250
+ | `apiKey` | `string` | ✅ | - | Your Featurely API key |
251
+ | `projectId` | `string` | ✅ | - | Your Featurely project ID |
252
+ | `apiUrl` | `string` | ❌ | `'https://featurely.no'` | Custom API endpoint |
253
+ | `pollInterval` | `number` | ❌ | `60000` | Config polling interval in ms |
254
+ | `userEmail` | `string` | ❌ | - | User email for whitelist & targeting |
255
+ | `userId` | `string` | ❌ | - | User ID for analytics & feature flags |
256
+ | `bypassCheck` | `() => boolean` | ❌ | - | Custom maintenance bypass function |
257
+ | `onMaintenanceEnabled` | `function` | ❌ | - | Maintenance enabled callback |
258
+ | `onMaintenanceDisabled` | `function` | ❌ | - | Maintenance disabled callback |
259
+ | `onMessageReceived` | `function` | ❌ | - | Message received callback |
260
+ | `onMessageDismissed` | `function` | ❌ | - | Message dismissed callback |
261
+ | `onFeatureFlagsUpdated` | `function` | ❌ | - | Feature flags updated callback |
262
+ | `enableAnalytics` | `boolean` | ❌ | `true` | Enable analytics tracking |
263
+ | `analyticsFlushInterval` | `number` | ❌ | `60000` | Analytics flush interval in ms |
264
+ | `appVersion` | `string` | ❌ | - | Current app version for version checking |
265
+ | `enableVersionCheck` | `boolean` | ❌ | `false` | Enable automatic version checking |
266
+ | `versionCheckInterval` | `number` | ❌ | `3600000` | Version check interval in ms (1 hour) |
267
+ | `onUpdateAvailable` | `function` | ❌ | - | Callback when update is available |
268
+ | `onUpdateRequired` | `function` | ❌ | - | Callback when update is required (forced) |
269
+ | `onError` | `function` | ❌ | - | Error callback |
270
+
271
+ ## 🎯 API Methods
272
+
273
+ ### Core Methods
274
+
275
+ #### `init()`
276
+
277
+ Initialize and start the site manager.
278
+
279
+ ```typescript
280
+ await manager.init();
281
+ ```
282
+
283
+ #### `setUser(email: string, userId?: string)`
284
+
285
+ Update user email and ID for whitelist and targeting.
286
+
287
+ ```typescript
288
+ manager.setUser("user@example.com", "user_123");
289
+ ```
290
+
291
+ #### `refresh()`
292
+
293
+ Manually refresh configuration from server.
294
+
295
+ ```typescript
296
+ await manager.refresh();
297
+ ```
298
+
299
+ #### `destroy()`
300
+
301
+ Stop the manager and clean up.
302
+
303
+ ```typescript
304
+ manager.destroy();
305
+ ```
306
+
307
+ ### Version Management Methods
308
+
309
+ #### `checkVersion(currentVersion?: string)`
310
+
311
+ Manually check if an update is available.
312
+
313
+ ```typescript
314
+ const versionInfo = await manager.checkVersion();
315
+ if (versionInfo?.updateAvailable) {
316
+ console.log("Update available:", versionInfo.latestVersion);
317
+ console.log("Is required?", versionInfo.updateRequired);
318
+ console.log("Is recommended?", versionInfo.updateRecommended);
319
+ }
320
+
321
+ // Check a specific version
322
+ const customCheck = await manager.checkVersion("1.0.0");
323
+ ```
324
+
325
+ #### `getLastVersionCheck()`
326
+
327
+ Get the last cached version check result.
328
+
329
+ ```typescript
330
+ const lastCheck = manager.getLastVersionCheck();
331
+ if (lastCheck?.updateAvailable) {
332
+ // Show update notification
333
+ }
334
+ ```
335
+
336
+ ### Feature Flag Methods
337
+
338
+ #### `isFeatureEnabled(flagKey: string)`
339
+
340
+ Check if a feature flag is enabled for the current user.
341
+
342
+ ```typescript
343
+ if (manager.isFeatureEnabled("new-dashboard")) {
344
+ // Show new dashboard
345
+ }
346
+ ```
347
+
348
+ #### `getFeatureVariant(flagKey: string)`
349
+
350
+ Get the assigned variant for A/B testing.
351
+
352
+ ```typescript
353
+ const variant = manager.getFeatureVariant("homepage-redesign");
354
+ // Returns: 'control', 'variant-a', 'variant-b', etc.
355
+ ```
356
+
357
+ #### `getAllFeatureFlags()`
358
+
359
+ Get all feature flags.
360
+
361
+ ```typescript
362
+ const flags = manager.getAllFeatureFlags();
363
+ console.log("All flags:", flags);
364
+ ```
365
+
366
+ #### `getEnabledFeatures()`
367
+
368
+ Get all enabled feature flag keys for the current user.
369
+
370
+ ```typescript
371
+ const enabledFeatures = manager.getEnabledFeatures();
372
+ // Returns: ['feature-1', 'feature-2', ...]
373
+ ```
374
+
375
+ ### Analytics Methods
376
+
377
+ #### `trackEvent(eventName: string, properties?: object)`
378
+
379
+ Track a custom analytics event.
380
+
381
+ ```typescript
382
+ manager.trackEvent("button_clicked", {
383
+ button: "signup",
384
+ page: "/pricing",
385
+ });
386
+
387
+ manager.trackEvent("purchase_completed", {
388
+ amount: 99.99,
389
+ currency: "USD",
390
+ plan: "pro",
391
+ });
392
+ ```
393
+
394
+ ## 💼 Use Cases
395
+
396
+ ### Update Notifications
397
+
398
+ ```typescript
399
+ const manager = new SiteManager({
400
+ apiKey: "ft_live_...",
401
+ projectId: "proj_...",
402
+ appVersion: "1.2.3",
403
+ enableVersionCheck: true,
404
+
405
+ onUpdateRequired: (versionInfo) => {
406
+ // Block app usage until updated
407
+ document.body.innerHTML = `
408
+ <div style="text-align: center; padding: 60px;">
409
+ <h1>Critical Update Required</h1>
410
+ <p>${versionInfo.latestVersion.title}</p>
411
+ <p>${versionInfo.latestVersion.releaseNotes}</p>
412
+ <a href="${versionInfo.latestVersion.downloadUrl}">
413
+ <button>Download Update</button>
414
+ </a>
415
+ </div>
416
+ `;
417
+ },
418
+
419
+ onUpdateAvailable: (versionInfo) => {
420
+ // Show non-blocking notification
421
+ if (!versionInfo.latestVersion.isBeta) {
422
+ showToast(`Update available: ${versionInfo.latestVersion.version}`);
423
+ }
424
+ },
425
+ });
426
+ ```
427
+
428
+ ### Feature Rollouts
429
+
430
+ ```typescript
431
+ // Dashboard configuration
432
+ manager.init();
433
+
434
+ // Gradually enable new checkout for 20% of users
435
+ if (manager.isFeatureEnabled("new-checkout-v2")) {
436
+ loadNewCheckout();
437
+ } else {
438
+ loadLegacyCheckout();
439
+ }
440
+
441
+ // A/B test with variants
442
+ const buttonColor = manager.getFeatureVariant("cta-button-color");
443
+ switch (buttonColor) {
444
+ case "green":
445
+ setButtonColor("#00CC66");
446
+ break;
447
+ case "blue":
448
+ setButtonColor("#0066CC");
449
+ break;
450
+ default:
451
+ setButtonColor("#FF6600"); // control
452
+ }
453
+ ```
454
+
118
455
  ### Custom Poll Interval
119
456
 
120
457
  ```typescript
package/dist/index.d.mts CHANGED
@@ -49,6 +49,20 @@ interface MaintenanceConfig {
49
49
  ips?: string[];
50
50
  };
51
51
  }
52
+ interface AppVersion {
53
+ version: string;
54
+ title: string;
55
+ releaseNotes: string;
56
+ downloadUrl?: string;
57
+ releaseDate: string;
58
+ isBeta?: boolean;
59
+ }
60
+ interface VersionCheckResponse {
61
+ updateAvailable: boolean;
62
+ updateRequired: boolean;
63
+ updateRecommended: boolean;
64
+ latestVersion?: AppVersion;
65
+ }
52
66
  interface SiteConfig {
53
67
  maintenance: MaintenanceConfig;
54
68
  messages: StatusMessage[];
@@ -70,12 +84,18 @@ interface SiteManagerConfig {
70
84
  userId?: string;
71
85
  enableAnalytics?: boolean;
72
86
  analyticsFlushInterval?: number;
87
+ appVersion?: string;
88
+ enableVersionCheck?: boolean;
89
+ versionCheckInterval?: number;
90
+ onUpdateAvailable?: (versionInfo: VersionCheckResponse) => void;
91
+ onUpdateRequired?: (versionInfo: VersionCheckResponse) => void;
73
92
  onError?: (error: Error) => void;
74
93
  }
75
94
  declare class SiteManager {
76
95
  private config;
77
96
  private siteConfig;
78
97
  private pollIntervalId;
98
+ private versionCheckIntervalId;
79
99
  private messageContainers;
80
100
  private dismissedMessages;
81
101
  private featureFlagBuckets;
@@ -84,6 +104,7 @@ declare class SiteManager {
84
104
  private sessionId;
85
105
  private consecutiveFetchFailures;
86
106
  private static readonly MAX_CONSECUTIVE_FAILURES;
107
+ private lastVersionCheck;
87
108
  constructor(config: SiteManagerConfig);
88
109
  init(): Promise<void>;
89
110
  destroy(): void;
@@ -101,6 +122,10 @@ declare class SiteManager {
101
122
  private stopAnalyticsFlushing;
102
123
  private flushAnalytics;
103
124
  private generateSessionId;
125
+ checkVersion(currentVersion?: string): Promise<VersionCheckResponse | null>;
126
+ getLastVersionCheck(): VersionCheckResponse | null;
127
+ private startVersionChecking;
128
+ private stopVersionChecking;
104
129
  private evaluateFeatureFlag;
105
130
  private getUserBucket;
106
131
  private simpleHash;
@@ -123,4 +148,4 @@ declare class SiteManager {
123
148
  private injectStyles;
124
149
  }
125
150
 
126
- export { type FeatureFlag, type MaintenanceConfig, type MessagePosition, type MessageStyle, type MessageType, type SiteConfig, SiteManager, type SiteManagerConfig, type StatusMessage, SiteManager as default };
151
+ export { type AppVersion, type FeatureFlag, type MaintenanceConfig, type MessagePosition, type MessageStyle, type MessageType, type SiteConfig, SiteManager, type SiteManagerConfig, type StatusMessage, type VersionCheckResponse, SiteManager as default };
package/dist/index.d.ts CHANGED
@@ -49,6 +49,20 @@ interface MaintenanceConfig {
49
49
  ips?: string[];
50
50
  };
51
51
  }
52
+ interface AppVersion {
53
+ version: string;
54
+ title: string;
55
+ releaseNotes: string;
56
+ downloadUrl?: string;
57
+ releaseDate: string;
58
+ isBeta?: boolean;
59
+ }
60
+ interface VersionCheckResponse {
61
+ updateAvailable: boolean;
62
+ updateRequired: boolean;
63
+ updateRecommended: boolean;
64
+ latestVersion?: AppVersion;
65
+ }
52
66
  interface SiteConfig {
53
67
  maintenance: MaintenanceConfig;
54
68
  messages: StatusMessage[];
@@ -70,12 +84,18 @@ interface SiteManagerConfig {
70
84
  userId?: string;
71
85
  enableAnalytics?: boolean;
72
86
  analyticsFlushInterval?: number;
87
+ appVersion?: string;
88
+ enableVersionCheck?: boolean;
89
+ versionCheckInterval?: number;
90
+ onUpdateAvailable?: (versionInfo: VersionCheckResponse) => void;
91
+ onUpdateRequired?: (versionInfo: VersionCheckResponse) => void;
73
92
  onError?: (error: Error) => void;
74
93
  }
75
94
  declare class SiteManager {
76
95
  private config;
77
96
  private siteConfig;
78
97
  private pollIntervalId;
98
+ private versionCheckIntervalId;
79
99
  private messageContainers;
80
100
  private dismissedMessages;
81
101
  private featureFlagBuckets;
@@ -84,6 +104,7 @@ declare class SiteManager {
84
104
  private sessionId;
85
105
  private consecutiveFetchFailures;
86
106
  private static readonly MAX_CONSECUTIVE_FAILURES;
107
+ private lastVersionCheck;
87
108
  constructor(config: SiteManagerConfig);
88
109
  init(): Promise<void>;
89
110
  destroy(): void;
@@ -101,6 +122,10 @@ declare class SiteManager {
101
122
  private stopAnalyticsFlushing;
102
123
  private flushAnalytics;
103
124
  private generateSessionId;
125
+ checkVersion(currentVersion?: string): Promise<VersionCheckResponse | null>;
126
+ getLastVersionCheck(): VersionCheckResponse | null;
127
+ private startVersionChecking;
128
+ private stopVersionChecking;
104
129
  private evaluateFeatureFlag;
105
130
  private getUserBucket;
106
131
  private simpleHash;
@@ -123,4 +148,4 @@ declare class SiteManager {
123
148
  private injectStyles;
124
149
  }
125
150
 
126
- export { type FeatureFlag, type MaintenanceConfig, type MessagePosition, type MessageStyle, type MessageType, type SiteConfig, SiteManager, type SiteManagerConfig, type StatusMessage, SiteManager as default };
151
+ export { type AppVersion, type FeatureFlag, type MaintenanceConfig, type MessagePosition, type MessageStyle, type MessageType, type SiteConfig, SiteManager, type SiteManagerConfig, type StatusMessage, type VersionCheckResponse, SiteManager as default };
package/dist/index.js CHANGED
@@ -40,9 +40,9 @@ var internalErrorTracker = (() => {
40
40
  try {
41
41
  const tracker = new import_featurely_error_tracker.ErrorTracker({
42
42
  apiKey: "ft_live_R4nAn9dDWxk6X3oMzB-tcQh0NrYvA04IhSfwPMUmyaU",
43
- apiUrl: "https://featurely.no",
43
+ apiUrl: "https://www.featurely.no",
44
44
  environment: "production",
45
- appVersion: "1.0.7",
45
+ appVersion: "1.1.1",
46
46
  maxBreadcrumbs: 30,
47
47
  enabled: true
48
48
  });
@@ -57,6 +57,7 @@ var _SiteManager = class _SiteManager {
57
57
  constructor(config) {
58
58
  this.siteConfig = null;
59
59
  this.pollIntervalId = null;
60
+ this.versionCheckIntervalId = null;
60
61
  this.messageContainers = /* @__PURE__ */ new Map();
61
62
  this.dismissedMessages = /* @__PURE__ */ new Set();
62
63
  this.featureFlagBuckets = /* @__PURE__ */ new Map();
@@ -65,7 +66,8 @@ var _SiteManager = class _SiteManager {
65
66
  this.analyticsFlushIntervalId = null;
66
67
  this.sessionId = this.generateSessionId();
67
68
  this.consecutiveFetchFailures = 0;
68
- var _a, _b, _c;
69
+ this.lastVersionCheck = null;
70
+ var _a, _b, _c, _d, _e;
69
71
  if (!config.apiKey) {
70
72
  throw new Error("Featurely Site Manager: apiKey is required");
71
73
  }
@@ -75,7 +77,7 @@ var _SiteManager = class _SiteManager {
75
77
  this.config = {
76
78
  apiKey: config.apiKey,
77
79
  projectId: config.projectId,
78
- apiUrl: config.apiUrl || "https://featurely.no",
80
+ apiUrl: config.apiUrl || "https://www.featurely.no",
79
81
  pollInterval: (_a = config.pollInterval) != null ? _a : 6e4,
80
82
  userEmail: config.userEmail,
81
83
  userId: config.userId,
@@ -87,7 +89,13 @@ var _SiteManager = class _SiteManager {
87
89
  onFeatureFlagsUpdated: config.onFeatureFlagsUpdated,
88
90
  onError: config.onError,
89
91
  enableAnalytics: (_b = config.enableAnalytics) != null ? _b : true,
90
- analyticsFlushInterval: (_c = config.analyticsFlushInterval) != null ? _c : 6e4
92
+ analyticsFlushInterval: (_c = config.analyticsFlushInterval) != null ? _c : 6e4,
93
+ appVersion: config.appVersion,
94
+ enableVersionCheck: (_d = config.enableVersionCheck) != null ? _d : false,
95
+ versionCheckInterval: (_e = config.versionCheckInterval) != null ? _e : 36e5,
96
+ // 1 hour
97
+ onUpdateAvailable: config.onUpdateAvailable,
98
+ onUpdateRequired: config.onUpdateRequired
91
99
  };
92
100
  this.loadDismissedMessages();
93
101
  }
@@ -106,6 +114,10 @@ var _SiteManager = class _SiteManager {
106
114
  if (this.config.enableAnalytics) {
107
115
  this.startAnalyticsFlushing();
108
116
  }
117
+ if (this.config.enableVersionCheck && this.config.appVersion) {
118
+ await this.checkVersion();
119
+ this.startVersionChecking();
120
+ }
109
121
  this.injectStyles();
110
122
  }
111
123
  /**
@@ -113,6 +125,7 @@ var _SiteManager = class _SiteManager {
113
125
  */
114
126
  destroy() {
115
127
  this.stopPolling();
128
+ this.stopVersionChecking();
116
129
  this.stopAnalyticsFlushing();
117
130
  this.flushAnalytics();
118
131
  this.clearMessages();
@@ -355,6 +368,76 @@ var _SiteManager = class _SiteManager {
355
368
  return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
356
369
  }
357
370
  // ============================================================================
371
+ // Version Checking
372
+ // ============================================================================
373
+ /**
374
+ * Check if an app update is available
375
+ * @param currentVersion - Optional version to check (defaults to config.appVersion)
376
+ * @returns Version check response with update status
377
+ */
378
+ async checkVersion(currentVersion) {
379
+ const versionToCheck = currentVersion || this.config.appVersion;
380
+ if (!versionToCheck) {
381
+ console.warn(
382
+ "Featurely Site Manager: appVersion not provided for version check"
383
+ );
384
+ return null;
385
+ }
386
+ try {
387
+ const response = await fetch(
388
+ `${this.config.apiUrl}/api/public/v1/version-check?projectId=${this.config.projectId}&currentVersion=${versionToCheck}`,
389
+ {
390
+ headers: {
391
+ "X-API-Key": this.config.apiKey
392
+ }
393
+ }
394
+ );
395
+ if (!response.ok) {
396
+ throw new Error(`Version check failed: ${response.statusText}`);
397
+ }
398
+ const versionInfo = await response.json();
399
+ this.lastVersionCheck = versionInfo;
400
+ this.trackEvent("version_checked", {
401
+ currentVersion: versionToCheck,
402
+ updateAvailable: versionInfo.updateAvailable,
403
+ updateRequired: versionInfo.updateRequired,
404
+ updateRecommended: versionInfo.updateRecommended
405
+ });
406
+ if (versionInfo.updateRequired && this.config.onUpdateRequired) {
407
+ this.config.onUpdateRequired(versionInfo);
408
+ } else if (versionInfo.updateAvailable && this.config.onUpdateAvailable) {
409
+ this.config.onUpdateAvailable(versionInfo);
410
+ }
411
+ return versionInfo;
412
+ } catch (error) {
413
+ console.error("Error checking version:", error);
414
+ if (this.config.onError) {
415
+ this.config.onError(
416
+ error instanceof Error ? error : new Error("Failed to check version")
417
+ );
418
+ }
419
+ return null;
420
+ }
421
+ }
422
+ /**
423
+ * Get the last version check result (cached)
424
+ */
425
+ getLastVersionCheck() {
426
+ return this.lastVersionCheck;
427
+ }
428
+ startVersionChecking() {
429
+ if (this.versionCheckIntervalId) return;
430
+ this.versionCheckIntervalId = setInterval(() => {
431
+ this.checkVersion();
432
+ }, this.config.versionCheckInterval);
433
+ }
434
+ stopVersionChecking() {
435
+ if (this.versionCheckIntervalId) {
436
+ clearInterval(this.versionCheckIntervalId);
437
+ this.versionCheckIntervalId = null;
438
+ }
439
+ }
440
+ // ============================================================================
358
441
  // Feature Flags
359
442
  // ============================================================================
360
443
  /**
package/dist/index.mjs CHANGED
@@ -5,9 +5,9 @@ var internalErrorTracker = (() => {
5
5
  try {
6
6
  const tracker = new ErrorTracker({
7
7
  apiKey: "ft_live_R4nAn9dDWxk6X3oMzB-tcQh0NrYvA04IhSfwPMUmyaU",
8
- apiUrl: "https://featurely.no",
8
+ apiUrl: "https://www.featurely.no",
9
9
  environment: "production",
10
- appVersion: "1.0.7",
10
+ appVersion: "1.1.1",
11
11
  maxBreadcrumbs: 30,
12
12
  enabled: true
13
13
  });
@@ -22,6 +22,7 @@ var _SiteManager = class _SiteManager {
22
22
  constructor(config) {
23
23
  this.siteConfig = null;
24
24
  this.pollIntervalId = null;
25
+ this.versionCheckIntervalId = null;
25
26
  this.messageContainers = /* @__PURE__ */ new Map();
26
27
  this.dismissedMessages = /* @__PURE__ */ new Set();
27
28
  this.featureFlagBuckets = /* @__PURE__ */ new Map();
@@ -30,7 +31,8 @@ var _SiteManager = class _SiteManager {
30
31
  this.analyticsFlushIntervalId = null;
31
32
  this.sessionId = this.generateSessionId();
32
33
  this.consecutiveFetchFailures = 0;
33
- var _a, _b, _c;
34
+ this.lastVersionCheck = null;
35
+ var _a, _b, _c, _d, _e;
34
36
  if (!config.apiKey) {
35
37
  throw new Error("Featurely Site Manager: apiKey is required");
36
38
  }
@@ -40,7 +42,7 @@ var _SiteManager = class _SiteManager {
40
42
  this.config = {
41
43
  apiKey: config.apiKey,
42
44
  projectId: config.projectId,
43
- apiUrl: config.apiUrl || "https://featurely.no",
45
+ apiUrl: config.apiUrl || "https://www.featurely.no",
44
46
  pollInterval: (_a = config.pollInterval) != null ? _a : 6e4,
45
47
  userEmail: config.userEmail,
46
48
  userId: config.userId,
@@ -52,7 +54,13 @@ var _SiteManager = class _SiteManager {
52
54
  onFeatureFlagsUpdated: config.onFeatureFlagsUpdated,
53
55
  onError: config.onError,
54
56
  enableAnalytics: (_b = config.enableAnalytics) != null ? _b : true,
55
- analyticsFlushInterval: (_c = config.analyticsFlushInterval) != null ? _c : 6e4
57
+ analyticsFlushInterval: (_c = config.analyticsFlushInterval) != null ? _c : 6e4,
58
+ appVersion: config.appVersion,
59
+ enableVersionCheck: (_d = config.enableVersionCheck) != null ? _d : false,
60
+ versionCheckInterval: (_e = config.versionCheckInterval) != null ? _e : 36e5,
61
+ // 1 hour
62
+ onUpdateAvailable: config.onUpdateAvailable,
63
+ onUpdateRequired: config.onUpdateRequired
56
64
  };
57
65
  this.loadDismissedMessages();
58
66
  }
@@ -71,6 +79,10 @@ var _SiteManager = class _SiteManager {
71
79
  if (this.config.enableAnalytics) {
72
80
  this.startAnalyticsFlushing();
73
81
  }
82
+ if (this.config.enableVersionCheck && this.config.appVersion) {
83
+ await this.checkVersion();
84
+ this.startVersionChecking();
85
+ }
74
86
  this.injectStyles();
75
87
  }
76
88
  /**
@@ -78,6 +90,7 @@ var _SiteManager = class _SiteManager {
78
90
  */
79
91
  destroy() {
80
92
  this.stopPolling();
93
+ this.stopVersionChecking();
81
94
  this.stopAnalyticsFlushing();
82
95
  this.flushAnalytics();
83
96
  this.clearMessages();
@@ -320,6 +333,76 @@ var _SiteManager = class _SiteManager {
320
333
  return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
321
334
  }
322
335
  // ============================================================================
336
+ // Version Checking
337
+ // ============================================================================
338
+ /**
339
+ * Check if an app update is available
340
+ * @param currentVersion - Optional version to check (defaults to config.appVersion)
341
+ * @returns Version check response with update status
342
+ */
343
+ async checkVersion(currentVersion) {
344
+ const versionToCheck = currentVersion || this.config.appVersion;
345
+ if (!versionToCheck) {
346
+ console.warn(
347
+ "Featurely Site Manager: appVersion not provided for version check"
348
+ );
349
+ return null;
350
+ }
351
+ try {
352
+ const response = await fetch(
353
+ `${this.config.apiUrl}/api/public/v1/version-check?projectId=${this.config.projectId}&currentVersion=${versionToCheck}`,
354
+ {
355
+ headers: {
356
+ "X-API-Key": this.config.apiKey
357
+ }
358
+ }
359
+ );
360
+ if (!response.ok) {
361
+ throw new Error(`Version check failed: ${response.statusText}`);
362
+ }
363
+ const versionInfo = await response.json();
364
+ this.lastVersionCheck = versionInfo;
365
+ this.trackEvent("version_checked", {
366
+ currentVersion: versionToCheck,
367
+ updateAvailable: versionInfo.updateAvailable,
368
+ updateRequired: versionInfo.updateRequired,
369
+ updateRecommended: versionInfo.updateRecommended
370
+ });
371
+ if (versionInfo.updateRequired && this.config.onUpdateRequired) {
372
+ this.config.onUpdateRequired(versionInfo);
373
+ } else if (versionInfo.updateAvailable && this.config.onUpdateAvailable) {
374
+ this.config.onUpdateAvailable(versionInfo);
375
+ }
376
+ return versionInfo;
377
+ } catch (error) {
378
+ console.error("Error checking version:", error);
379
+ if (this.config.onError) {
380
+ this.config.onError(
381
+ error instanceof Error ? error : new Error("Failed to check version")
382
+ );
383
+ }
384
+ return null;
385
+ }
386
+ }
387
+ /**
388
+ * Get the last version check result (cached)
389
+ */
390
+ getLastVersionCheck() {
391
+ return this.lastVersionCheck;
392
+ }
393
+ startVersionChecking() {
394
+ if (this.versionCheckIntervalId) return;
395
+ this.versionCheckIntervalId = setInterval(() => {
396
+ this.checkVersion();
397
+ }, this.config.versionCheckInterval);
398
+ }
399
+ stopVersionChecking() {
400
+ if (this.versionCheckIntervalId) {
401
+ clearInterval(this.versionCheckIntervalId);
402
+ this.versionCheckIntervalId = null;
403
+ }
404
+ }
405
+ // ============================================================================
323
406
  // Feature Flags
324
407
  // ============================================================================
325
408
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "featurely-site-manager",
3
- "version": "1.0.7",
4
- "description": "Site management SDK for maintenance mode, status messages, and feature flags",
3
+ "version": "1.1.1",
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",
7
7
  "types": "dist/index.d.ts",
@@ -29,8 +29,13 @@
29
29
  "maintenance-mode",
30
30
  "status-messages",
31
31
  "feature-flags",
32
+ "version-checking",
33
+ "update-notifications",
34
+ "analytics",
32
35
  "downtime",
33
- "banners"
36
+ "banners",
37
+ "a/b-testing",
38
+ "feature-toggles"
34
39
  ],
35
40
  "author": "Featurely",
36
41
  "license": "MIT",