티스토리 수익 글 보기

티스토리 수익 글 보기

/** * Feature Evaluator JavaScript Client * Provides functions to retrieve and lookup feature flag variants */ (function(window) { ‘use strict’; // Default API endpoint – can be overridden const DEFAULT_API_ENDPOINT = ‘/fe/api/features/variants’; const OVERRIDES_PARAM_NAME = ‘fe-overrides’; // Cache for storing feature variants window.featureVariants = window.featureVariants || {}; /** * Extracts the sessionId from the MUID cookie * @returns {string|null} The sessionId value or null if not found */ function getSessionIdFromCookie() { const cookies = document.cookie.split(‘;’); for (let cookie of cookies) { const [name, value] = cookie.trim().split(‘=’); if (name === ‘MUID’) { return value; } } return null; } /** * Extracts the locale from the current URL using regex * Supports formats like: * – /en-US/ * – /fr-FR/ * – /en-Latn-US/ * @returns {string|null} The locale or null if not found */ function getLocaleFromUrl() { try { const path = window.location.pathname; const regex = /\/([a-zA-Z]{2}(?:-[a-zA-Z]{4})?-[a-zA-Z]{2})(?=\/|$)/; const match = path.match(regex); return match ? match[1].toLowerCase() : null; } catch (error) { console.warn(‘Error parsing URL for locale:’, error); return null; } } /** * Gets feature variations from the API * @param {string} [apiEndpoint] – Custom API endpoint with optional overrides query parameter * @param {string[]} [featureNames] – Specific feature names to request (optional) * @returns {Promise} Promise that resolves to the feature variants object */ async function getVariations(apiEndpoint = DEFAULT_API_ENDPOINT, featureNames = null) { try { const sessionId = getSessionIdFromCookie(); const locale = getLocaleFromUrl(); const requestBody = { sessionId: sessionId, locale: locale }; // Add feature names if provided if (featureNames && Array.isArray(featureNames) && featureNames.length > 0) { requestBody.featureNames = featureNames; } // If no custom endpoint provided, check for overrides in current URL let finalEndpoint = apiEndpoint; const urlParams = new URLSearchParams(window.location.search); const overrides = urlParams.get(OVERRIDES_PARAM_NAME); if (overrides) { const separator = finalEndpoint.includes(‘?’) ? ‘&’ : ‘?’; finalEndpoint += `${separator}${OVERRIDES_PARAM_NAME}=${encodeURIComponent(overrides)}`; } const response = await fetch(finalEndpoint, { method: ‘POST’, headers: { ‘Content-Type’: ‘application/json’, }, body: JSON.stringify(requestBody) }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); // Store the response in the global window object for caching window.featureVariants = data; console.log(‘Feature variants loaded:’, data); return data; } catch (error) { console.error(‘Error fetching feature variations:’, error); throw error; } } /** * Gets a specific feature variant by feature name from the cached results * @param {string} featureName – The name of the feature to lookup * @returns {Object|null} The feature object or null if not found */ function getVariantByFeatureName(featureName) { if (!featureName) { console.warn(‘Feature name is required’); return null; } if (!window.featureVariants || !window.featureVariants.features) { console.warn(‘No feature variants loaded. Call getVariations() first.’); return null; } const variants = window.featureVariants.features; const feature = variants.find(variant => variant.featureName === featureName); if (!feature) { console.warn(`Feature ‘${featureName}’ not found in loaded variants`); return null; } return feature; } /** * Gets the enabled status of a feature by name * @param {string} featureName – The name of the feature to check * @returns {boolean|null} True if enabled, false if disabled, null if not found */ function isFeatureEnabled(featureName) { const feature = getVariantByFeatureName(featureName); return feature ? feature.isEnabled : false; } /** * Gets the variant configuration of a feature by name * @param {string} featureName – The name of the feature * @returns {Object|null} The variant configuration or null if not found */ function getFeatureVariant(featureName) { const feature = getVariantByFeatureName(featureName); return feature ? feature.variant : null; } /** * Parses override query parameters from current URL * @returns {Object|null} Parsed overrides object or null if none found */ function getOverridesFromUrl() { const urlParams = new URLSearchParams(window.location.search); const overrides = urlParams.get(OVERRIDES_PARAM_NAME); if (!overrides) { return null; } const parsed = {}; const overridePairs = overrides.split(‘,’); for (const pair of overridePairs) { const [featureProperty, value] = pair.split(‘:’); if (featureProperty && value) { const parts = featureProperty.split(‘.’); const featureName = parts.slice(0, -1).join(‘.’); const property = parts[parts.length – 1]; if (!parsed[featureName]) { parsed[featureName] = {}; } parsed[featureName][property] = value; } } return Object.keys(parsed).length > 0 ? parsed : null; } /** * Creates an override string from an object * @param {Object} overrides – Override object * @returns {string} Override string in API format */ function createOverrideString(overrides) { const parts = []; for (const [featureName, properties] of Object.entries(overrides)) { for (const [property, value] of Object.entries(properties)) { parts.push(`${featureName}.${property}:${value}`); } } return parts.join(‘,’); } // Expose functions to the global scope window.FeatureEvaluator = { getVariations: getVariations, getVariantByFeatureName: getVariantByFeatureName, isFeatureEnabled: isFeatureEnabled, getFeatureVariant: getFeatureVariant, // Utility functions getSessionIdFromCookie: getSessionIdFromCookie, getLocaleFromUrl: getLocaleFromUrl, getOverridesFromUrl: getOverridesFromUrl, createOverrideString: createOverrideString }; })(window);