UNPKG

eehitus-feature-flag-client

Version:

A feature flag client for Node.js and React

176 lines (175 loc) 6.8 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import axios from "axios"; // Use consistent import pattern for the axiosInstance let axiosInstance; function loadAxios() { try { if (typeof window === "undefined" || typeof require !== "undefined") { // Server-side or environments where require is available const axiosModule = axios; return axiosModule.create(); } else { // Client-side with ES modules return axios.create(); } } catch (error) { throw new Error("Axios is a peer dependency and is missing. Please install it by running:\n\n" + " yarn add axios\n\n" + "or\n\n" + " npm install axios\n\n"); } } // Initialize the axiosInstance axiosInstance = loadAxios(); export class FeatureFlagClient { constructor(url, appId, environment, options = {}) { var _a, _b; this.featureFlags = {}; this.intervalId = null; this.isBrowser = typeof window !== "undefined"; this.isLoading = false; this.hasLoaded = false; this.url = url; this.appId = appId; this.environment = environment; // Set options with defaults this.enableBrowserPolling = (_a = options.enableBrowserPolling) !== null && _a !== void 0 ? _a : false; this.pollingInterval = (_b = options.pollingInterval) !== null && _b !== void 0 ? _b : 60000; // Default to 1 minute if (!this.isBrowser) { // For server, immediately fetch and set up polling this.fetchFeatureFlags(); this.intervalId = setInterval(() => this.fetchFeatureFlags(), this.pollingInterval); } else { // For browser, fetch once at initialization this.fetchOnce(); // Set up polling for browser if enabled if (this.enableBrowserPolling) { this.startPolling(); } } } fetchFeatureFlags() { return __awaiter(this, void 0, void 0, function* () { // Set loading state this.isLoading = true; try { // Use the new /flags/active endpoint const activeEndpoint = `${this.url}/flags/active`; console.log("Fetching flags from:", activeEndpoint); const response = yield axiosInstance.get(activeEndpoint, { params: { app: this.appId, environment: this.environment, }, }); const newFlags = {}; response.data.forEach((flag) => { newFlags[flag.name] = true; // All returned flags are active }); // Update the flags this.featureFlags = newFlags; // Log the fetched flags for debugging console.log("Fetched flags:", this.featureFlags); // Update loading state this.isLoading = false; this.hasLoaded = true; return this.featureFlags; } catch (error) { console.error("Error fetching feature flags:", error); this.isLoading = false; return this.featureFlags; // Return current flags on error } }); } fetchOnce() { return __awaiter(this, void 0, void 0, function* () { // Only fetch if we haven't loaded before if (!this.hasLoaded && !this.isLoading) { return this.fetchFeatureFlags(); } else if (this.isLoading) { // Wait for current fetch to finish yield new Promise((resolve) => { const checkLoaded = () => { if (!this.isLoading) { resolve(true); } else { setTimeout(checkLoaded, 50); } }; checkLoaded(); }); } return this.featureFlags; }); } fetchFresh() { return __awaiter(this, void 0, void 0, function* () { return this.fetchFeatureFlags(); }); } ensureFlagsLoaded() { return __awaiter(this, void 0, void 0, function* () { if (!this.hasLoaded) { yield this.fetchOnce(); } }); } flagEnabled(flagName) { var _a; // Debug logging console.log("Checking flag:", flagName, "Current flags:", this.featureFlags); // Return flag value or false if not found return Boolean((_a = this.featureFlags[flagName]) !== null && _a !== void 0 ? _a : false); } getFlagEnabledAsync(flagName) { return __awaiter(this, void 0, void 0, function* () { // Ensure flags are loaded before checking yield this.ensureFlagsLoaded(); return this.flagEnabled(flagName); }); } stopFetching() { if (this.intervalId) { clearInterval(this.intervalId); this.intervalId = null; console.log('Feature flag polling stopped'); } } startPolling(interval) { // Stop any existing interval this.stopFetching(); // Update interval if provided if (interval) { this.pollingInterval = interval; } // Start new interval this.intervalId = setInterval(() => this.fetchFeatureFlags(), this.pollingInterval); console.log(`Feature flag polling started with interval: ${this.pollingInterval}ms`); } // Add method to check if polling is active isPollingActive() { return this.intervalId !== null; } // Add method to update polling interval setPollingInterval(interval) { this.pollingInterval = interval; // If polling is active, restart with new interval if (this.isPollingActive()) { this.startPolling(); } } }