featurely-site-manager 1.0.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 ADDED
@@ -0,0 +1,29 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2026-03-14
9
+
10
+ ### Added
11
+
12
+ - Initial release of @featurely/site-manager
13
+ - Maintenance mode with default and custom pages
14
+ - Expected restoration time display
15
+ - Status page link support
16
+ - Whitelist system (localStorage keys, emails, custom functions)
17
+ - Status messages/banners
18
+ - Message types: info, warning, error, success
19
+ - Message positioning: top, bottom banners and toast notifications
20
+ - Message scheduling (start/end times)
21
+ - Page targeting (URL patterns)
22
+ - Dismissible messages with localStorage persistence
23
+ - Call-to-action buttons in messages
24
+ - Auto-polling configuration updates (default: 60s)
25
+ - Comprehensive TypeScript support
26
+ - Mobile-responsive designs
27
+ - Zero dependencies
28
+ - Callbacks for all major events
29
+ - React, Next.js, and Vue integration examples
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Featurely
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,447 @@
1
+ # @featurely/site-manager
2
+
3
+ Powerful site management SDK for controlling maintenance mode, displaying status messages, and managing site-wide features from your Featurely dashboard.
4
+
5
+ ## 📦 Installation
6
+
7
+ ```bash
8
+ npm install @featurely/site-manager
9
+ ```
10
+
11
+ ## 🚀 Quick Start
12
+
13
+ ```typescript
14
+ import { SiteManager } from "@featurely/site-manager";
15
+
16
+ const siteManager = new SiteManager({
17
+ apiKey: "your-featurely-api-key",
18
+ projectId: "your-project-id",
19
+ });
20
+
21
+ siteManager.init();
22
+ ```
23
+
24
+ ## ✨ Features
25
+
26
+ ### 🔧 Maintenance Mode
27
+
28
+ - **Default** or **custom** maintenance pages
29
+ - Expected restoration time countdown
30
+ - Status page links
31
+ - Smart whitelist system:
32
+ - localStorage keys
33
+ - Email addresses
34
+ - Custom bypass functions
35
+
36
+ ### 📢 Status Messages
37
+
38
+ - Multiple message types: info, warning, error, success
39
+ - Flexible positioning: top, bottom banners or toast notifications
40
+ - Dismissible messages with persistence
41
+ - Scheduled messages (start/end times)
42
+ - Page targeting (show on specific URLs)
43
+ - Call-to-action buttons
44
+ - Auto-expire functionality
45
+
46
+ ## 📖 Usage
47
+
48
+ ### Basic Setup
49
+
50
+ ```typescript
51
+ const manager = new SiteManager({
52
+ apiKey: "ft_live_your_api_key",
53
+ projectId: "proj_your_project_id",
54
+ });
55
+
56
+ manager.init();
57
+ ```
58
+
59
+ ### With User Context
60
+
61
+ ```typescript
62
+ const manager = new SiteManager({
63
+ apiKey: "ft_live_your_api_key",
64
+ projectId: "proj_your_project_id",
65
+ userEmail: "user@example.com", // For whitelist checks
66
+ });
67
+
68
+ manager.init();
69
+
70
+ // Update user later
71
+ manager.setUser("newuser@example.com");
72
+ ```
73
+
74
+ ### With Callbacks
75
+
76
+ ```typescript
77
+ const manager = new SiteManager({
78
+ apiKey: "ft_live_your_api_key",
79
+ projectId: "proj_your_project_id",
80
+
81
+ onMaintenanceEnabled: (config) => {
82
+ console.log("Maintenance mode enabled:", config);
83
+ // Track in analytics
84
+ },
85
+
86
+ onMaintenanceDisabled: () => {
87
+ console.log("Site is back online!");
88
+ },
89
+
90
+ onMessageReceived: (message) => {
91
+ console.log("New message:", message);
92
+ },
93
+
94
+ onError: (error) => {
95
+ console.error("Site Manager error:", error);
96
+ },
97
+ });
98
+
99
+ manager.init();
100
+ ```
101
+
102
+ ### Custom Maintenance Bypass
103
+
104
+ ```typescript
105
+ const manager = new SiteManager({
106
+ apiKey: "ft_live_your_api_key",
107
+ projectId: "proj_your_project_id",
108
+
109
+ bypassCheck: () => {
110
+ // Custom logic - e.g., check if user is admin
111
+ return window.location.search.includes("admin=true");
112
+ },
113
+ });
114
+
115
+ manager.init();
116
+ ```
117
+
118
+ ### Custom Poll Interval
119
+
120
+ ```typescript
121
+ const manager = new SiteManager({
122
+ apiKey: "ft_live_your_api_key",
123
+ projectId: "proj_your_project_id",
124
+ pollInterval: 30000, // Check every 30 seconds instead of default 60s
125
+ });
126
+
127
+ manager.init();
128
+ ```
129
+
130
+ ## 🔧 Configuration
131
+
132
+ ### SiteManagerConfig
133
+
134
+ | Option | Type | Required | Default | Description |
135
+ | ----------------------- | --------------- | -------- | ------------------------- | ----------------------------- |
136
+ | `apiKey` | `string` | ✅ | - | Your Featurely API key |
137
+ | `projectId` | `string` | ✅ | - | Your Featurely project ID |
138
+ | `apiUrl` | `string` | ❌ | `'https://featurely.com'` | Custom API endpoint |
139
+ | `pollInterval` | `number` | ❌ | `60000` | Polling interval in ms |
140
+ | `userEmail` | `string` | ❌ | - | User email for whitelist |
141
+ | `bypassCheck` | `() => boolean` | ❌ | - | Custom bypass function |
142
+ | `onMaintenanceEnabled` | `function` | ❌ | - | Maintenance enabled callback |
143
+ | `onMaintenanceDisabled` | `function` | ❌ | - | Maintenance disabled callback |
144
+ | `onMessageReceived` | `function` | ❌ | - | Message received callback |
145
+ | `onMessageDismissed` | `function` | ❌ | - | Message dismissed callback |
146
+ | `onError` | `function` | ❌ | - | Error callback |
147
+
148
+ ## 🎯 API Methods
149
+
150
+ ### `init()`
151
+
152
+ Initialize and start the site manager.
153
+
154
+ ```typescript
155
+ await manager.init();
156
+ ```
157
+
158
+ ### `setUser(email: string)`
159
+
160
+ Update user email for whitelist checks.
161
+
162
+ ```typescript
163
+ manager.setUser("user@example.com");
164
+ ```
165
+
166
+ ### `refresh()`
167
+
168
+ Manually refresh configuration from server.
169
+
170
+ ```typescript
171
+ await manager.refresh();
172
+ ```
173
+
174
+ ### `destroy()`
175
+
176
+ Stop the manager and clean up.
177
+
178
+ ```typescript
179
+ manager.destroy();
180
+ ```
181
+
182
+ ## 🎨 Maintenance Mode
183
+
184
+ ### Default Page
185
+
186
+ The SDK includes a beautiful default maintenance page with:
187
+
188
+ - Professional gradient background
189
+ - Maintenance icon
190
+ - Customizable messages
191
+ - Expected restoration time
192
+ - Optional status page link
193
+
194
+ ### Custom Page
195
+
196
+ Provide your own HTML:
197
+
198
+ ```typescript
199
+ // Set in Featurely dashboard:
200
+ {
201
+ "type": "custom",
202
+ "customHtml": "<div>Your custom HTML here</div>"
203
+ }
204
+ ```
205
+
206
+ ### Whitelist System
207
+
208
+ **localStorage Keys:**
209
+ Users with specific localStorage keys bypass maintenance.
210
+
211
+ ```typescript
212
+ // In Featurely dashboard, set whitelist keys: ['featurely_admin', 'bypass_key']
213
+ // Users can set: localStorage.setItem('featurely_admin', 'true');
214
+ ```
215
+
216
+ **Email Addresses:**
217
+ Specific users bypass maintenance.
218
+
219
+ ```typescript
220
+ // In Featurely dashboard: ['admin@company.com', 'support@company.com']
221
+ const manager = new SiteManager({
222
+ apiKey: "ft_live_...",
223
+ projectId: "proj_...",
224
+ userEmail: "admin@company.com", // This user bypasses
225
+ });
226
+ ```
227
+
228
+ **Custom Function:**
229
+ Complex bypass logic.
230
+
231
+ ```typescript
232
+ const manager = new SiteManager({
233
+ apiKey: "ft_live_...",
234
+ projectId: "proj_...",
235
+ bypassCheck: () => {
236
+ // E.g., check if admin query param is present
237
+ return new URLSearchParams(window.location.search).get("admin") === "true";
238
+ },
239
+ });
240
+ ```
241
+
242
+ ## 📢 Status Messages
243
+
244
+ Messages are managed from your Featurely dashboard and automatically displayed based on:
245
+
246
+ - Start/end times
247
+ - Target pages (URL patterns)
248
+ - Message type and priority
249
+
250
+ ### Message Types
251
+
252
+ - **info** (blue) - Informational messages
253
+ - **warning** (orange) - Warnings and alerts
254
+ - **error** (red) - Critical errors
255
+ - **success** (green) - Success notifications
256
+
257
+ ### Message Styles
258
+
259
+ - **banner** - Full-width top or bottom banner
260
+ - **toast** - Small notification in corner
261
+
262
+ ### Example Message Configuration
263
+
264
+ ```json
265
+ {
266
+ "id": "msg_123",
267
+ "type": "warning",
268
+ "title": "Scheduled Maintenance",
269
+ "message": "We'll be performing maintenance tonight at 3 AM EST.",
270
+ "position": "top",
271
+ "style": "banner",
272
+ "dismissible": true,
273
+ "startsAt": "2026-03-14T10:00:00Z",
274
+ "expiresAt": "2026-03-15T03:00:00Z",
275
+ "cta": {
276
+ "text": "Learn More",
277
+ "url": "/maintenance-info"
278
+ }
279
+ }
280
+ ```
281
+
282
+ ## 🌐 Framework Integration
283
+
284
+ ### React
285
+
286
+ ```tsx
287
+ import { useEffect } from "react";
288
+ import { SiteManager } from "@featurely/site-manager";
289
+
290
+ function App() {
291
+ useEffect(() => {
292
+ const manager = new SiteManager({
293
+ apiKey: process.env.REACT_APP_FEATURELY_API_KEY!,
294
+ projectId: process.env.REACT_APP_FEATURELY_PROJECT_ID!,
295
+ });
296
+
297
+ manager.init();
298
+
299
+ return () => manager.destroy();
300
+ }, []);
301
+
302
+ return <div>Your app</div>;
303
+ }
304
+ ```
305
+
306
+ ### Next.js
307
+
308
+ ```tsx
309
+ // app/layout.tsx
310
+ "use client";
311
+
312
+ import { useEffect } from "react";
313
+ import { SiteManager } from "@featurely/site-manager";
314
+
315
+ export default function RootLayout({ children }) {
316
+ useEffect(() => {
317
+ if (typeof window === "undefined") return;
318
+
319
+ const manager = new SiteManager({
320
+ apiKey: process.env.NEXT_PUBLIC_FEATURELY_API_KEY!,
321
+ projectId: process.env.NEXT_PUBLIC_FEATURELY_PROJECT_ID!,
322
+ });
323
+
324
+ manager.init();
325
+
326
+ return () => manager.destroy();
327
+ }, []);
328
+
329
+ return (
330
+ <html>
331
+ <body>{children}</body>
332
+ </html>
333
+ );
334
+ }
335
+ ```
336
+
337
+ ### Vue
338
+
339
+ ```vue
340
+ <script setup>
341
+ import { onMounted, onUnmounted } from "vue";
342
+ import { SiteManager } from "@featurely/site-manager";
343
+
344
+ let manager = null;
345
+
346
+ onMounted(() => {
347
+ manager = new SiteManager({
348
+ apiKey: import.meta.env.VITE_FEATURELY_API_KEY,
349
+ projectId: import.meta.env.VITE_FEATURELY_PROJECT_ID,
350
+ });
351
+
352
+ manager.init();
353
+ });
354
+
355
+ onUnmounted(() => {
356
+ manager?.destroy();
357
+ });
358
+ </script>
359
+ ```
360
+
361
+ ## 🌐 Environment Variables
362
+
363
+ **React/Vite:**
364
+
365
+ ```env
366
+ VITE_FEATURELY_API_KEY=ft_live_your_key
367
+ VITE_FEATURELY_PROJECT_ID=proj_your_id
368
+ ```
369
+
370
+ **Next.js:**
371
+
372
+ ```env
373
+ NEXT_PUBLIC_FEATURELY_API_KEY=ft_live_your_key
374
+ NEXT_PUBLIC_FEATURELY_PROJECT_ID=proj_your_id
375
+ ```
376
+
377
+ ## 🎨 Styling
378
+
379
+ The SDK injects minimal, scoped styles. All classes are prefixed with `featurely-` to avoid conflicts.
380
+
381
+ ### Custom Styling
382
+
383
+ Override default styles:
384
+
385
+ ```css
386
+ /* Customize maintenance page */
387
+ .featurely-maintenance-overlay {
388
+ background: linear-gradient(to right, #your #colors) !important;
389
+ }
390
+
391
+ /* Customize messages */
392
+ .featurely-message-info {
393
+ background: #your-color !important;
394
+ }
395
+ ```
396
+
397
+ ## 📊 Dashboard Management
398
+
399
+ Manage all settings from your Featurely dashboard at:
400
+ `https://featurely.com/dashboard/settings` → Site Management tab
401
+
402
+ ### Maintenance Mode Panel
403
+
404
+ - Toggle on/off
405
+ - Set expected restoration time
406
+ - Configure whitelist (emails, localStorage keys)
407
+ - Choose default or custom page
408
+ - Add status page link
409
+
410
+ ### Status Messages Panel
411
+
412
+ - Create new messages
413
+ - Set type, title, and content
414
+ - Schedule start/end times
415
+ - Target specific pages
416
+ - Add CTA buttons
417
+ - Set priority
418
+
419
+ ## 🔒 Security
420
+
421
+ - API keys should be environment variables
422
+ - All requests use HTTPS
423
+ - Whitelist checks happen client-side (easily bypassable - use for convenience, not security)
424
+ - For true access control, implement server-side authentication
425
+
426
+ ## 📱 Browser Support
427
+
428
+ - Chrome/Edge (latest)
429
+ - Firefox (latest)
430
+ - Safari (latest)
431
+ - Mobile browsers
432
+
433
+ ## 📄 License
434
+
435
+ MIT License - see LICENSE file for details.
436
+
437
+ ## 🔗 Links
438
+
439
+ - [Featurely](https://featurely.com)
440
+ - [Documentation](https://docs.featurely.com)
441
+ - [npm](https://www.npmjs.com/package/@featurely/site-manager)
442
+
443
+ ## 💬 Support
444
+
445
+ - Open a GitHub issue
446
+ - Contact support@featurely.com
447
+ - Check the [documentation](https://docs.featurely.com)
@@ -0,0 +1,124 @@
1
+ type MessageType = "info" | "warning" | "error" | "success";
2
+ type MessagePosition = "top" | "bottom";
3
+ type MessageStyle = "banner" | "toast";
4
+ interface StatusMessage {
5
+ id: string;
6
+ type: MessageType;
7
+ title: string;
8
+ message: string;
9
+ icon?: string;
10
+ dismissible: boolean;
11
+ position: MessagePosition;
12
+ style: MessageStyle;
13
+ expiresAt?: string;
14
+ startsAt?: string;
15
+ targetPages?: string[];
16
+ cta?: {
17
+ text: string;
18
+ url: string;
19
+ action?: string;
20
+ };
21
+ priority?: number;
22
+ }
23
+ interface FeatureFlag {
24
+ id: string;
25
+ key: string;
26
+ name: string;
27
+ description?: string;
28
+ enabled: boolean;
29
+ rolloutPercentage?: number;
30
+ targetEmails?: string[];
31
+ excludeEmails?: string[];
32
+ variants?: {
33
+ key: string;
34
+ name: string;
35
+ weight: number;
36
+ }[];
37
+ defaultVariant?: string;
38
+ }
39
+ interface MaintenanceConfig {
40
+ enabled: boolean;
41
+ type: "default" | "custom";
42
+ customHtml?: string;
43
+ expectedRestoration?: string;
44
+ showStatusLink: boolean;
45
+ statusPageUrl?: string;
46
+ whitelist: {
47
+ localStorageKeys?: string[];
48
+ emails?: string[];
49
+ ips?: string[];
50
+ };
51
+ }
52
+ interface SiteConfig {
53
+ maintenance: MaintenanceConfig;
54
+ messages: StatusMessage[];
55
+ featureFlags: FeatureFlag[];
56
+ lastUpdated: string;
57
+ }
58
+ interface SiteManagerConfig {
59
+ apiKey: string;
60
+ projectId: string;
61
+ apiUrl?: string;
62
+ pollInterval?: number;
63
+ userEmail?: string;
64
+ bypassCheck?: () => boolean;
65
+ onMaintenanceEnabled?: (config: MaintenanceConfig) => void;
66
+ onMaintenanceDisabled?: () => void;
67
+ onMessageReceived?: (message: StatusMessage) => void;
68
+ onMessageDismissed?: (messageId: string) => void;
69
+ onFeatureFlagsUpdated?: (flags: FeatureFlag[]) => void;
70
+ userId?: string;
71
+ enableAnalytics?: boolean;
72
+ analyticsFlushInterval?: number;
73
+ onError?: (error: Error) => void;
74
+ }
75
+ declare class SiteManager {
76
+ private config;
77
+ private siteConfig;
78
+ private pollIntervalId;
79
+ private messageContainers;
80
+ private dismissedMessages;
81
+ private featureFlagBuckets;
82
+ private analyticsQueue;
83
+ private analyticsFlushIntervalId;
84
+ private sessionId;
85
+ constructor(config: SiteManagerConfig);
86
+ init(): Promise<void>;
87
+ destroy(): void;
88
+ setUser(email: string, userId?: string): void;
89
+ isFeatureEnabled(flagKey: string): boolean;
90
+ getFeatureVariant(flagKey: string): string | null;
91
+ getAllFeatureFlags(): FeatureFlag[];
92
+ getEnabledFeatures(): string[];
93
+ refresh(): Promise<void>;
94
+ trackEvent(eventName: string, properties?: Record<string, string | number | boolean>): void;
95
+ private fetchConfig;
96
+ private startPolling;
97
+ private stopPolling;
98
+ private startAnalyticsFlushing;
99
+ private stopAnalyticsFlushing;
100
+ private flushAnalytics;
101
+ private generateSessionId;
102
+ private evaluateFeatureFlag;
103
+ private getUserBucket;
104
+ private simpleHash;
105
+ private getAnonymousId;
106
+ private checkMaintenanceMode;
107
+ private enableMaintenanceMode;
108
+ private disableMaintenanceMode;
109
+ private shouldBypassMaintenance;
110
+ private showMaintenancePage;
111
+ private hideMaintenancePage;
112
+ private getDefaultMaintenanceHtml;
113
+ private updateMessages;
114
+ private showMessage;
115
+ private dismissMessage;
116
+ private clearMessages;
117
+ private handleMessageAction;
118
+ private getDefaultIcon;
119
+ private loadDismissedMessages;
120
+ private saveDismissedMessages;
121
+ private injectStyles;
122
+ }
123
+
124
+ export { type FeatureFlag, type MaintenanceConfig, type MessagePosition, type MessageStyle, type MessageType, type SiteConfig, SiteManager, type SiteManagerConfig, type StatusMessage, SiteManager as default };