featurely-site-manager 1.0.8 → 1.1.2
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 +37 -0
- package/README.md +337 -0
- package/dist/index.d.mts +28 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +118 -20
- package/dist/index.mjs +118 -20
- package/package.json +11 -7
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,43 @@ 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
|
+
|
|
8
45
|
## [1.0.8] - 2026-03-15
|
|
9
46
|
|
|
10
47
|
### Fixed
|
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;
|
|
@@ -92,6 +113,8 @@ declare class SiteManager {
|
|
|
92
113
|
getFeatureVariant(flagKey: string): string | null;
|
|
93
114
|
getAllFeatureFlags(): FeatureFlag[];
|
|
94
115
|
getEnabledFeatures(): string[];
|
|
116
|
+
isInMaintenanceMode(): boolean;
|
|
117
|
+
getActiveMessages(): StatusMessage[];
|
|
95
118
|
refresh(): Promise<void>;
|
|
96
119
|
trackEvent(eventName: string, properties?: Record<string, string | number | boolean>): void;
|
|
97
120
|
private fetchConfig;
|
|
@@ -101,6 +124,10 @@ declare class SiteManager {
|
|
|
101
124
|
private stopAnalyticsFlushing;
|
|
102
125
|
private flushAnalytics;
|
|
103
126
|
private generateSessionId;
|
|
127
|
+
checkVersion(currentVersion?: string): Promise<VersionCheckResponse | null>;
|
|
128
|
+
getLastVersionCheck(): VersionCheckResponse | null;
|
|
129
|
+
private startVersionChecking;
|
|
130
|
+
private stopVersionChecking;
|
|
104
131
|
private evaluateFeatureFlag;
|
|
105
132
|
private getUserBucket;
|
|
106
133
|
private simpleHash;
|
|
@@ -123,4 +150,4 @@ declare class SiteManager {
|
|
|
123
150
|
private injectStyles;
|
|
124
151
|
}
|
|
125
152
|
|
|
126
|
-
export { type FeatureFlag, type MaintenanceConfig, type MessagePosition, type MessageStyle, type MessageType, type SiteConfig, SiteManager, type SiteManagerConfig, type StatusMessage, SiteManager as default };
|
|
153
|
+
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;
|
|
@@ -92,6 +113,8 @@ declare class SiteManager {
|
|
|
92
113
|
getFeatureVariant(flagKey: string): string | null;
|
|
93
114
|
getAllFeatureFlags(): FeatureFlag[];
|
|
94
115
|
getEnabledFeatures(): string[];
|
|
116
|
+
isInMaintenanceMode(): boolean;
|
|
117
|
+
getActiveMessages(): StatusMessage[];
|
|
95
118
|
refresh(): Promise<void>;
|
|
96
119
|
trackEvent(eventName: string, properties?: Record<string, string | number | boolean>): void;
|
|
97
120
|
private fetchConfig;
|
|
@@ -101,6 +124,10 @@ declare class SiteManager {
|
|
|
101
124
|
private stopAnalyticsFlushing;
|
|
102
125
|
private flushAnalytics;
|
|
103
126
|
private generateSessionId;
|
|
127
|
+
checkVersion(currentVersion?: string): Promise<VersionCheckResponse | null>;
|
|
128
|
+
getLastVersionCheck(): VersionCheckResponse | null;
|
|
129
|
+
private startVersionChecking;
|
|
130
|
+
private stopVersionChecking;
|
|
104
131
|
private evaluateFeatureFlag;
|
|
105
132
|
private getUserBucket;
|
|
106
133
|
private simpleHash;
|
|
@@ -123,4 +150,4 @@ declare class SiteManager {
|
|
|
123
150
|
private injectStyles;
|
|
124
151
|
}
|
|
125
152
|
|
|
126
|
-
export { type FeatureFlag, type MaintenanceConfig, type MessagePosition, type MessageStyle, type MessageType, type SiteConfig, SiteManager, type SiteManagerConfig, type StatusMessage, SiteManager as default };
|
|
153
|
+
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
|
@@ -35,28 +35,11 @@ __export(index_exports, {
|
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(index_exports);
|
|
37
37
|
var import_dompurify = __toESM(require("dompurify"));
|
|
38
|
-
var import_featurely_error_tracker = require("featurely-error-tracker");
|
|
39
|
-
var internalErrorTracker = (() => {
|
|
40
|
-
try {
|
|
41
|
-
const tracker = new import_featurely_error_tracker.ErrorTracker({
|
|
42
|
-
apiKey: "ft_live_R4nAn9dDWxk6X3oMzB-tcQh0NrYvA04IhSfwPMUmyaU",
|
|
43
|
-
apiUrl: "https://www.featurely.no",
|
|
44
|
-
environment: "production",
|
|
45
|
-
appVersion: "1.0.7",
|
|
46
|
-
maxBreadcrumbs: 30,
|
|
47
|
-
enabled: true
|
|
48
|
-
});
|
|
49
|
-
tracker.install();
|
|
50
|
-
return tracker;
|
|
51
|
-
} catch (e) {
|
|
52
|
-
console.warn("Failed to initialize SDK error tracking:", e);
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
})();
|
|
56
38
|
var _SiteManager = class _SiteManager {
|
|
57
39
|
constructor(config) {
|
|
58
40
|
this.siteConfig = null;
|
|
59
41
|
this.pollIntervalId = null;
|
|
42
|
+
this.versionCheckIntervalId = null;
|
|
60
43
|
this.messageContainers = /* @__PURE__ */ new Map();
|
|
61
44
|
this.dismissedMessages = /* @__PURE__ */ new Set();
|
|
62
45
|
this.featureFlagBuckets = /* @__PURE__ */ new Map();
|
|
@@ -65,7 +48,8 @@ var _SiteManager = class _SiteManager {
|
|
|
65
48
|
this.analyticsFlushIntervalId = null;
|
|
66
49
|
this.sessionId = this.generateSessionId();
|
|
67
50
|
this.consecutiveFetchFailures = 0;
|
|
68
|
-
|
|
51
|
+
this.lastVersionCheck = null;
|
|
52
|
+
var _a, _b, _c, _d, _e;
|
|
69
53
|
if (!config.apiKey) {
|
|
70
54
|
throw new Error("Featurely Site Manager: apiKey is required");
|
|
71
55
|
}
|
|
@@ -87,7 +71,13 @@ var _SiteManager = class _SiteManager {
|
|
|
87
71
|
onFeatureFlagsUpdated: config.onFeatureFlagsUpdated,
|
|
88
72
|
onError: config.onError,
|
|
89
73
|
enableAnalytics: (_b = config.enableAnalytics) != null ? _b : true,
|
|
90
|
-
analyticsFlushInterval: (_c = config.analyticsFlushInterval) != null ? _c : 6e4
|
|
74
|
+
analyticsFlushInterval: (_c = config.analyticsFlushInterval) != null ? _c : 6e4,
|
|
75
|
+
appVersion: config.appVersion,
|
|
76
|
+
enableVersionCheck: (_d = config.enableVersionCheck) != null ? _d : false,
|
|
77
|
+
versionCheckInterval: (_e = config.versionCheckInterval) != null ? _e : 36e5,
|
|
78
|
+
// 1 hour
|
|
79
|
+
onUpdateAvailable: config.onUpdateAvailable,
|
|
80
|
+
onUpdateRequired: config.onUpdateRequired
|
|
91
81
|
};
|
|
92
82
|
this.loadDismissedMessages();
|
|
93
83
|
}
|
|
@@ -106,6 +96,10 @@ var _SiteManager = class _SiteManager {
|
|
|
106
96
|
if (this.config.enableAnalytics) {
|
|
107
97
|
this.startAnalyticsFlushing();
|
|
108
98
|
}
|
|
99
|
+
if (this.config.enableVersionCheck && this.config.appVersion) {
|
|
100
|
+
await this.checkVersion();
|
|
101
|
+
this.startVersionChecking();
|
|
102
|
+
}
|
|
109
103
|
this.injectStyles();
|
|
110
104
|
}
|
|
111
105
|
/**
|
|
@@ -113,6 +107,7 @@ var _SiteManager = class _SiteManager {
|
|
|
113
107
|
*/
|
|
114
108
|
destroy() {
|
|
115
109
|
this.stopPolling();
|
|
110
|
+
this.stopVersionChecking();
|
|
116
111
|
this.stopAnalyticsFlushing();
|
|
117
112
|
this.flushAnalytics();
|
|
118
113
|
this.clearMessages();
|
|
@@ -213,6 +208,39 @@ var _SiteManager = class _SiteManager {
|
|
|
213
208
|
}
|
|
214
209
|
return this.siteConfig.featureFlags.filter((flag) => flag.enabled && this.evaluateFeatureFlag(flag)).map((flag) => flag.key);
|
|
215
210
|
}
|
|
211
|
+
/**
|
|
212
|
+
* Check if the site is currently in maintenance mode (accounting for bypass)
|
|
213
|
+
*/
|
|
214
|
+
isInMaintenanceMode() {
|
|
215
|
+
var _a;
|
|
216
|
+
if (!((_a = this.siteConfig) == null ? void 0 : _a.maintenance.enabled)) return false;
|
|
217
|
+
return !this.shouldBypassMaintenance();
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Get all currently active status messages for the current page and time
|
|
221
|
+
*/
|
|
222
|
+
getActiveMessages() {
|
|
223
|
+
if (!this.siteConfig) return [];
|
|
224
|
+
const currentTime = /* @__PURE__ */ new Date();
|
|
225
|
+
const currentPath = typeof window !== "undefined" ? window.location.pathname : "";
|
|
226
|
+
return this.siteConfig.messages.filter((message) => {
|
|
227
|
+
if (this.dismissedMessages.has(message.id)) return false;
|
|
228
|
+
if (message.startsAt && new Date(message.startsAt) > currentTime) return false;
|
|
229
|
+
if (message.expiresAt && new Date(message.expiresAt) < currentTime) return false;
|
|
230
|
+
if (message.targetPages && message.targetPages.length > 0) {
|
|
231
|
+
const matches = message.targetPages.some((pattern) => {
|
|
232
|
+
try {
|
|
233
|
+
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
234
|
+
return new RegExp(escaped).test(currentPath);
|
|
235
|
+
} catch {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
if (!matches) return false;
|
|
240
|
+
}
|
|
241
|
+
return true;
|
|
242
|
+
});
|
|
243
|
+
}
|
|
216
244
|
/**
|
|
217
245
|
* Manually refresh configuration
|
|
218
246
|
*/
|
|
@@ -355,6 +383,76 @@ var _SiteManager = class _SiteManager {
|
|
|
355
383
|
return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
|
|
356
384
|
}
|
|
357
385
|
// ============================================================================
|
|
386
|
+
// Version Checking
|
|
387
|
+
// ============================================================================
|
|
388
|
+
/**
|
|
389
|
+
* Check if an app update is available
|
|
390
|
+
* @param currentVersion - Optional version to check (defaults to config.appVersion)
|
|
391
|
+
* @returns Version check response with update status
|
|
392
|
+
*/
|
|
393
|
+
async checkVersion(currentVersion) {
|
|
394
|
+
const versionToCheck = currentVersion || this.config.appVersion;
|
|
395
|
+
if (!versionToCheck) {
|
|
396
|
+
console.warn(
|
|
397
|
+
"Featurely Site Manager: appVersion not provided for version check"
|
|
398
|
+
);
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
try {
|
|
402
|
+
const response = await fetch(
|
|
403
|
+
`${this.config.apiUrl}/api/public/v1/version-check?projectId=${this.config.projectId}¤tVersion=${versionToCheck}`,
|
|
404
|
+
{
|
|
405
|
+
headers: {
|
|
406
|
+
"X-API-Key": this.config.apiKey
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
);
|
|
410
|
+
if (!response.ok) {
|
|
411
|
+
throw new Error(`Version check failed: ${response.statusText}`);
|
|
412
|
+
}
|
|
413
|
+
const versionInfo = await response.json();
|
|
414
|
+
this.lastVersionCheck = versionInfo;
|
|
415
|
+
this.trackEvent("version_checked", {
|
|
416
|
+
currentVersion: versionToCheck,
|
|
417
|
+
updateAvailable: versionInfo.updateAvailable,
|
|
418
|
+
updateRequired: versionInfo.updateRequired,
|
|
419
|
+
updateRecommended: versionInfo.updateRecommended
|
|
420
|
+
});
|
|
421
|
+
if (versionInfo.updateRequired && this.config.onUpdateRequired) {
|
|
422
|
+
this.config.onUpdateRequired(versionInfo);
|
|
423
|
+
} else if (versionInfo.updateAvailable && this.config.onUpdateAvailable) {
|
|
424
|
+
this.config.onUpdateAvailable(versionInfo);
|
|
425
|
+
}
|
|
426
|
+
return versionInfo;
|
|
427
|
+
} catch (error) {
|
|
428
|
+
console.error("Error checking version:", error);
|
|
429
|
+
if (this.config.onError) {
|
|
430
|
+
this.config.onError(
|
|
431
|
+
error instanceof Error ? error : new Error("Failed to check version")
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Get the last version check result (cached)
|
|
439
|
+
*/
|
|
440
|
+
getLastVersionCheck() {
|
|
441
|
+
return this.lastVersionCheck;
|
|
442
|
+
}
|
|
443
|
+
startVersionChecking() {
|
|
444
|
+
if (this.versionCheckIntervalId) return;
|
|
445
|
+
this.versionCheckIntervalId = setInterval(() => {
|
|
446
|
+
this.checkVersion();
|
|
447
|
+
}, this.config.versionCheckInterval);
|
|
448
|
+
}
|
|
449
|
+
stopVersionChecking() {
|
|
450
|
+
if (this.versionCheckIntervalId) {
|
|
451
|
+
clearInterval(this.versionCheckIntervalId);
|
|
452
|
+
this.versionCheckIntervalId = null;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
// ============================================================================
|
|
358
456
|
// Feature Flags
|
|
359
457
|
// ============================================================================
|
|
360
458
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -1,27 +1,10 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import DOMPurify from "dompurify";
|
|
3
|
-
import { ErrorTracker } from "featurely-error-tracker";
|
|
4
|
-
var internalErrorTracker = (() => {
|
|
5
|
-
try {
|
|
6
|
-
const tracker = new ErrorTracker({
|
|
7
|
-
apiKey: "ft_live_R4nAn9dDWxk6X3oMzB-tcQh0NrYvA04IhSfwPMUmyaU",
|
|
8
|
-
apiUrl: "https://www.featurely.no",
|
|
9
|
-
environment: "production",
|
|
10
|
-
appVersion: "1.0.7",
|
|
11
|
-
maxBreadcrumbs: 30,
|
|
12
|
-
enabled: true
|
|
13
|
-
});
|
|
14
|
-
tracker.install();
|
|
15
|
-
return tracker;
|
|
16
|
-
} catch (e) {
|
|
17
|
-
console.warn("Failed to initialize SDK error tracking:", e);
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
})();
|
|
21
3
|
var _SiteManager = class _SiteManager {
|
|
22
4
|
constructor(config) {
|
|
23
5
|
this.siteConfig = null;
|
|
24
6
|
this.pollIntervalId = null;
|
|
7
|
+
this.versionCheckIntervalId = null;
|
|
25
8
|
this.messageContainers = /* @__PURE__ */ new Map();
|
|
26
9
|
this.dismissedMessages = /* @__PURE__ */ new Set();
|
|
27
10
|
this.featureFlagBuckets = /* @__PURE__ */ new Map();
|
|
@@ -30,7 +13,8 @@ var _SiteManager = class _SiteManager {
|
|
|
30
13
|
this.analyticsFlushIntervalId = null;
|
|
31
14
|
this.sessionId = this.generateSessionId();
|
|
32
15
|
this.consecutiveFetchFailures = 0;
|
|
33
|
-
|
|
16
|
+
this.lastVersionCheck = null;
|
|
17
|
+
var _a, _b, _c, _d, _e;
|
|
34
18
|
if (!config.apiKey) {
|
|
35
19
|
throw new Error("Featurely Site Manager: apiKey is required");
|
|
36
20
|
}
|
|
@@ -52,7 +36,13 @@ var _SiteManager = class _SiteManager {
|
|
|
52
36
|
onFeatureFlagsUpdated: config.onFeatureFlagsUpdated,
|
|
53
37
|
onError: config.onError,
|
|
54
38
|
enableAnalytics: (_b = config.enableAnalytics) != null ? _b : true,
|
|
55
|
-
analyticsFlushInterval: (_c = config.analyticsFlushInterval) != null ? _c : 6e4
|
|
39
|
+
analyticsFlushInterval: (_c = config.analyticsFlushInterval) != null ? _c : 6e4,
|
|
40
|
+
appVersion: config.appVersion,
|
|
41
|
+
enableVersionCheck: (_d = config.enableVersionCheck) != null ? _d : false,
|
|
42
|
+
versionCheckInterval: (_e = config.versionCheckInterval) != null ? _e : 36e5,
|
|
43
|
+
// 1 hour
|
|
44
|
+
onUpdateAvailable: config.onUpdateAvailable,
|
|
45
|
+
onUpdateRequired: config.onUpdateRequired
|
|
56
46
|
};
|
|
57
47
|
this.loadDismissedMessages();
|
|
58
48
|
}
|
|
@@ -71,6 +61,10 @@ var _SiteManager = class _SiteManager {
|
|
|
71
61
|
if (this.config.enableAnalytics) {
|
|
72
62
|
this.startAnalyticsFlushing();
|
|
73
63
|
}
|
|
64
|
+
if (this.config.enableVersionCheck && this.config.appVersion) {
|
|
65
|
+
await this.checkVersion();
|
|
66
|
+
this.startVersionChecking();
|
|
67
|
+
}
|
|
74
68
|
this.injectStyles();
|
|
75
69
|
}
|
|
76
70
|
/**
|
|
@@ -78,6 +72,7 @@ var _SiteManager = class _SiteManager {
|
|
|
78
72
|
*/
|
|
79
73
|
destroy() {
|
|
80
74
|
this.stopPolling();
|
|
75
|
+
this.stopVersionChecking();
|
|
81
76
|
this.stopAnalyticsFlushing();
|
|
82
77
|
this.flushAnalytics();
|
|
83
78
|
this.clearMessages();
|
|
@@ -178,6 +173,39 @@ var _SiteManager = class _SiteManager {
|
|
|
178
173
|
}
|
|
179
174
|
return this.siteConfig.featureFlags.filter((flag) => flag.enabled && this.evaluateFeatureFlag(flag)).map((flag) => flag.key);
|
|
180
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Check if the site is currently in maintenance mode (accounting for bypass)
|
|
178
|
+
*/
|
|
179
|
+
isInMaintenanceMode() {
|
|
180
|
+
var _a;
|
|
181
|
+
if (!((_a = this.siteConfig) == null ? void 0 : _a.maintenance.enabled)) return false;
|
|
182
|
+
return !this.shouldBypassMaintenance();
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Get all currently active status messages for the current page and time
|
|
186
|
+
*/
|
|
187
|
+
getActiveMessages() {
|
|
188
|
+
if (!this.siteConfig) return [];
|
|
189
|
+
const currentTime = /* @__PURE__ */ new Date();
|
|
190
|
+
const currentPath = typeof window !== "undefined" ? window.location.pathname : "";
|
|
191
|
+
return this.siteConfig.messages.filter((message) => {
|
|
192
|
+
if (this.dismissedMessages.has(message.id)) return false;
|
|
193
|
+
if (message.startsAt && new Date(message.startsAt) > currentTime) return false;
|
|
194
|
+
if (message.expiresAt && new Date(message.expiresAt) < currentTime) return false;
|
|
195
|
+
if (message.targetPages && message.targetPages.length > 0) {
|
|
196
|
+
const matches = message.targetPages.some((pattern) => {
|
|
197
|
+
try {
|
|
198
|
+
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
199
|
+
return new RegExp(escaped).test(currentPath);
|
|
200
|
+
} catch {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
if (!matches) return false;
|
|
205
|
+
}
|
|
206
|
+
return true;
|
|
207
|
+
});
|
|
208
|
+
}
|
|
181
209
|
/**
|
|
182
210
|
* Manually refresh configuration
|
|
183
211
|
*/
|
|
@@ -320,6 +348,76 @@ var _SiteManager = class _SiteManager {
|
|
|
320
348
|
return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
|
|
321
349
|
}
|
|
322
350
|
// ============================================================================
|
|
351
|
+
// Version Checking
|
|
352
|
+
// ============================================================================
|
|
353
|
+
/**
|
|
354
|
+
* Check if an app update is available
|
|
355
|
+
* @param currentVersion - Optional version to check (defaults to config.appVersion)
|
|
356
|
+
* @returns Version check response with update status
|
|
357
|
+
*/
|
|
358
|
+
async checkVersion(currentVersion) {
|
|
359
|
+
const versionToCheck = currentVersion || this.config.appVersion;
|
|
360
|
+
if (!versionToCheck) {
|
|
361
|
+
console.warn(
|
|
362
|
+
"Featurely Site Manager: appVersion not provided for version check"
|
|
363
|
+
);
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
try {
|
|
367
|
+
const response = await fetch(
|
|
368
|
+
`${this.config.apiUrl}/api/public/v1/version-check?projectId=${this.config.projectId}¤tVersion=${versionToCheck}`,
|
|
369
|
+
{
|
|
370
|
+
headers: {
|
|
371
|
+
"X-API-Key": this.config.apiKey
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
);
|
|
375
|
+
if (!response.ok) {
|
|
376
|
+
throw new Error(`Version check failed: ${response.statusText}`);
|
|
377
|
+
}
|
|
378
|
+
const versionInfo = await response.json();
|
|
379
|
+
this.lastVersionCheck = versionInfo;
|
|
380
|
+
this.trackEvent("version_checked", {
|
|
381
|
+
currentVersion: versionToCheck,
|
|
382
|
+
updateAvailable: versionInfo.updateAvailable,
|
|
383
|
+
updateRequired: versionInfo.updateRequired,
|
|
384
|
+
updateRecommended: versionInfo.updateRecommended
|
|
385
|
+
});
|
|
386
|
+
if (versionInfo.updateRequired && this.config.onUpdateRequired) {
|
|
387
|
+
this.config.onUpdateRequired(versionInfo);
|
|
388
|
+
} else if (versionInfo.updateAvailable && this.config.onUpdateAvailable) {
|
|
389
|
+
this.config.onUpdateAvailable(versionInfo);
|
|
390
|
+
}
|
|
391
|
+
return versionInfo;
|
|
392
|
+
} catch (error) {
|
|
393
|
+
console.error("Error checking version:", error);
|
|
394
|
+
if (this.config.onError) {
|
|
395
|
+
this.config.onError(
|
|
396
|
+
error instanceof Error ? error : new Error("Failed to check version")
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Get the last version check result (cached)
|
|
404
|
+
*/
|
|
405
|
+
getLastVersionCheck() {
|
|
406
|
+
return this.lastVersionCheck;
|
|
407
|
+
}
|
|
408
|
+
startVersionChecking() {
|
|
409
|
+
if (this.versionCheckIntervalId) return;
|
|
410
|
+
this.versionCheckIntervalId = setInterval(() => {
|
|
411
|
+
this.checkVersion();
|
|
412
|
+
}, this.config.versionCheckInterval);
|
|
413
|
+
}
|
|
414
|
+
stopVersionChecking() {
|
|
415
|
+
if (this.versionCheckIntervalId) {
|
|
416
|
+
clearInterval(this.versionCheckIntervalId);
|
|
417
|
+
this.versionCheckIntervalId = null;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
// ============================================================================
|
|
323
421
|
// Feature Flags
|
|
324
422
|
// ============================================================================
|
|
325
423
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "featurely-site-manager",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.1.2",
|
|
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,18 +29,23 @@
|
|
|
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",
|
|
37
42
|
"repository": {
|
|
38
43
|
"type": "git",
|
|
39
|
-
"url": "https://github.com/
|
|
44
|
+
"url": "https://github.com/featurely/site-manager",
|
|
40
45
|
"directory": "packages/featurely-site-manager"
|
|
41
46
|
},
|
|
42
47
|
"bugs": {
|
|
43
|
-
"url": "https://github.com/
|
|
48
|
+
"url": "https://github.com/featurely/site-manager/issues"
|
|
44
49
|
},
|
|
45
50
|
"homepage": "https://featurely.no",
|
|
46
51
|
"devDependencies": {
|
|
@@ -49,7 +54,6 @@
|
|
|
49
54
|
"typescript": "^5.0.0"
|
|
50
55
|
},
|
|
51
56
|
"dependencies": {
|
|
52
|
-
"dompurify": "^3.3.3"
|
|
53
|
-
"featurely-error-tracker": "^1.0.7"
|
|
57
|
+
"dompurify": "^3.3.3"
|
|
54
58
|
}
|
|
55
59
|
}
|