@sleekio/sdk
Version:
Modern A/B Testing SDK for JavaScript Applications - Production-ready with graceful fallbacks
370 lines (319 loc) • 9.11 kB
JavaScript
/**
* Production Usage Examples for Sleek SDK
*
* This file demonstrates how to safely integrate Sleek SDK in production
* environments with proper error handling and fallback mechanisms.
*/
import { createAnonymousSleek, createAuthenticatedSleek } from "sleek-sdk";
// ===== PRODUCTION CONFIGURATION =====
const PRODUCTION_CONFIG = {
// Fast timeouts to prevent blocking user experience
requestTimeout: 2000, // 2 seconds max
retryAttempts: 1, // Single retry only
// Efficient batching
batchEvents: true,
batchSize: 25,
flushInterval: 15000, // 15 seconds
// Graceful fallbacks
gracefulFallback: true,
enableLogging: false, // No console logs in production
// Custom headers (if needed for authentication)
headers: {
"X-Client-Version": "1.0.0",
// 'Authorization': 'Bearer your-token' // If API requires auth
},
// User management settings
userManager: {
useLocalStorage: true,
useSessionStorage: true,
cookieExpireDays: 365,
useDeviceFingerprint: false, // Privacy-friendly
},
};
// ===== SAFE INITIALIZATION =====
/**
* Initialize SDK with error boundaries
*/
function initializeSleekSDK(apiUrl, userId = null, userProperties = {}) {
try {
if (userId) {
return createAuthenticatedSleek(
apiUrl,
userId,
userProperties,
PRODUCTION_CONFIG
);
} else {
return createAnonymousSleek(apiUrl, PRODUCTION_CONFIG);
}
} catch (error) {
console.error("Failed to initialize Sleek SDK:", error);
// Return a mock SDK that always returns 'control'
return createMockSleekSDK();
}
}
/**
* Mock SDK for when initialization fails
*/
function createMockSleekSDK() {
return {
getVariant: () => Promise.resolve("control"),
getVariantConfig: () => Promise.resolve({}),
track: () => {}, // No-op
trackConversion: () => {}, // No-op
isAnonymous: () => true,
getUserId: () => "mock_user",
cleanup: () => {},
};
}
// ===== PRODUCTION USAGE PATTERNS =====
/**
* Example 1: E-commerce Product Page
*/
class ProductPageExperiment {
constructor(sleekSDK) {
this.sleek = sleekSDK;
this.experimentId = "product_page_layout_v2";
}
async initialize() {
try {
// Get variant with timeout protection
const variant = await Promise.race([
this.sleek.getVariant(this.experimentId),
new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout")), 1000)
),
]);
this.applyVariant(variant);
this.trackPageView();
} catch (error) {
console.warn("Experiment failed, using default layout:", error.message);
this.applyVariant("control");
}
}
applyVariant(variant) {
switch (variant) {
case "new_layout":
this.enableNewLayout();
break;
case "minimal_layout":
this.enableMinimalLayout();
break;
default:
this.enableDefaultLayout();
}
}
enableNewLayout() {
document.body.classList.add("new-product-layout");
}
enableMinimalLayout() {
document.body.classList.add("minimal-product-layout");
}
enableDefaultLayout() {
document.body.classList.add("default-product-layout");
}
trackPageView() {
// Safe tracking with error handling
try {
this.sleek.track(this.experimentId, "page_view", {
page: "product",
timestamp: Date.now(),
url: window.location.href,
});
} catch (error) {
// Silently fail - don't break user experience
console.warn("Failed to track page view:", error.message);
}
}
trackPurchase(orderValue, currency = "USD") {
try {
this.sleek.trackConversion(this.experimentId, {
value: orderValue,
currency,
event_type: "purchase",
});
} catch (error) {
console.warn("Failed to track purchase:", error.message);
}
}
}
/**
* Example 2: Feature Flag with Gradual Rollout
*/
class FeatureFlagManager {
constructor(sleekSDK) {
this.sleek = sleekSDK;
this.features = new Map();
}
async isFeatureEnabled(featureName, defaultValue = false) {
try {
// Use experiment ID as feature flag
const variant = await this.sleek.getVariant(`feature_${featureName}`);
const isEnabled = variant === "enabled";
// Cache result
this.features.set(featureName, isEnabled);
return isEnabled;
} catch (error) {
console.warn(
`Feature flag check failed for ${featureName}:`,
error.message
);
return defaultValue;
}
}
async getFeatureConfig(featureName, defaultConfig = {}) {
try {
const config = await this.sleek.getVariantConfig(
`feature_${featureName}`
);
return { ...defaultConfig, ...config };
} catch (error) {
console.warn(`Feature config failed for ${featureName}:`, error.message);
return defaultConfig;
}
}
trackFeatureUsage(featureName, action, properties = {}) {
try {
this.sleek.track(`feature_${featureName}`, action, {
feature: featureName,
...properties,
});
} catch (error) {
console.warn(
`Failed to track feature usage for ${featureName}:`,
error.message
);
}
}
}
/**
* Example 3: React Component with Hooks
*/
function useExperiment(experimentId, defaultVariant = "control") {
const [variant, setVariant] = useState(defaultVariant);
const [config, setConfig] = useState({});
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let mounted = true;
async function loadExperiment() {
try {
setLoading(true);
setError(null);
// Initialize SDK (you'd pass this from context in real app)
const sleek = initializeSleekSDK("https://sleek-api.vercel.app");
const [variantResult, configResult] = await Promise.all([
sleek.getVariant(experimentId),
sleek.getVariantConfig(experimentId),
]);
if (mounted) {
setVariant(variantResult);
setConfig(configResult);
}
} catch (err) {
if (mounted) {
setError(err.message);
setVariant(defaultVariant); // Fallback to default
}
} finally {
if (mounted) {
setLoading(false);
}
}
}
loadExperiment();
return () => {
mounted = false;
};
}, [experimentId, defaultVariant]);
const track = useCallback(
(eventName, properties = {}) => {
try {
// Get SDK from context in real app
const sleek = initializeSleekSDK("https://sleek-api.vercel.app");
sleek.track(experimentId, eventName, properties);
} catch (error) {
console.warn("Failed to track event:", error.message);
}
},
[experimentId]
);
return { variant, config, loading, error, track };
}
// ===== INITIALIZATION EXAMPLES =====
// Example 1: Anonymous user (most common)
const sleekAnonymous = initializeSleekSDK("https://sleek-api.vercel.app");
// Example 2: Authenticated user
const sleekAuthenticated = initializeSleekSDK(
"https://sleek-api.vercel.app",
"user_12345",
{
email: "user@example.com",
plan: "premium",
signup_date: "2024-01-15",
}
);
// Example 3: With custom configuration
const sleekCustom = createAnonymousSleek("https://sleek-api.vercel.app", {
...PRODUCTION_CONFIG,
requestTimeout: 1500, // Even faster timeout
batchSize: 50, // Larger batches
});
// ===== CLEANUP AND MEMORY MANAGEMENT =====
/**
* Proper cleanup when user navigates away or component unmounts
*/
function cleanupSleekSDK(sleekInstance) {
try {
// Flush any pending events
sleekInstance.flushEvents?.();
// Clean up timers and listeners
sleekInstance.cleanup?.();
} catch (error) {
console.warn("Error during SDK cleanup:", error.message);
}
}
// Browser: Clean up on page unload
if (typeof window !== "undefined") {
window.addEventListener("beforeunload", () => {
cleanupSleekSDK(sleekAnonymous);
});
}
// React: Clean up in useEffect
// useEffect(() => {
// return () => cleanupSleekSDK(sleekInstance);
// }, []);
// ===== ERROR MONITORING INTEGRATION =====
/**
* Integrate with error monitoring services
*/
function setupErrorMonitoring(sleekInstance) {
// Example with Sentry
if (typeof Sentry !== "undefined") {
const originalTrack = sleekInstance.track;
sleekInstance.track = function (experimentId, eventName, properties) {
try {
return originalTrack.call(this, experimentId, eventName, properties);
} catch (error) {
Sentry.captureException(error, {
tags: {
component: "sleek-sdk",
experiment_id: experimentId,
event_name: eventName,
},
});
throw error;
}
};
}
}
export {
initializeSleekSDK,
createMockSleekSDK,
ProductPageExperiment,
FeatureFlagManager,
useExperiment,
cleanupSleekSDK,
setupErrorMonitoring,
PRODUCTION_CONFIG,
};