UNPKG

unleash-client

Version:
189 lines 8.39 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = require("events"); const variant_1 = require("./variant"); const events_2 = require("./events"); class UnleashClient extends events_1.EventEmitter { constructor(repository, strategies) { super(); this.repository = repository; this.strategies = strategies || []; this.warnedStrategies = {}; this.warnedDependencies = {}; this.strategies.forEach((strategy) => { if (!strategy || !strategy.name || !strategy.isEnabled || typeof strategy.isEnabled !== 'function') { throw new Error('Invalid strategy data / interface'); } }); } getStrategy(name) { return this.strategies.find((strategy) => strategy.name === name); } warnStrategyOnce(missingStrategy, name, strategies) { if (!this.warnedStrategies[missingStrategy + name]) { this.warnedStrategies[missingStrategy + name] = true; this.emit(events_2.UnleashEvents.Warn, `Missing strategy "${missingStrategy}" for toggle "${name}". Ensure that "${strategies .map(({ name: n }) => n) .join(', ')}" are supported before using this toggle`); } } warnDependencyOnce(missingDependency, name) { if (!this.warnedDependencies[missingDependency + name]) { this.warnedDependencies[missingDependency + name] = true; this.emit(events_2.UnleashEvents.Warn, `Missing dependency "${missingDependency}" for toggle "${name}"`); } } isParentDependencySatisfied(feature, context) { var _a; if (!((_a = feature === null || feature === void 0 ? void 0 : feature.dependencies) === null || _a === void 0 ? void 0 : _a.length)) { return true; } return feature.dependencies.every((parent) => { var _a, _b; const parentToggle = this.repository.getToggle(parent.feature); if (!parentToggle) { this.warnDependencyOnce(parent.feature, feature.name); return false; } if ((_a = parentToggle.dependencies) === null || _a === void 0 ? void 0 : _a.length) { return false; } if (parent.enabled !== false) { if ((_b = parent.variants) === null || _b === void 0 ? void 0 : _b.length) { const { name, feature_enabled: featureEnabled } = this.getVariant(parent.feature, context); return featureEnabled && parent.variants.includes(name); } return this.isEnabled(parent.feature, context, () => false); } return !this.isEnabled(parent.feature, context, () => false); }); } isEnabled(name, context, fallback) { const feature = this.repository.getToggle(name); const enabled = this.isFeatureEnabled(feature, context, fallback).enabled; if (feature === null || feature === void 0 ? void 0 : feature.impressionData) { this.emit(events_2.UnleashEvents.Impression, (0, events_2.createImpressionEvent)({ featureName: name, context, enabled, eventType: 'isEnabled', })); } return enabled; } isFeatureEnabled(feature, context, fallback) { var _a; if (!feature) { return { enabled: fallback() }; } if (!feature || !this.isParentDependencySatisfied(feature, context) || !feature.enabled) { return { enabled: false }; } if (!Array.isArray(feature.strategies)) { const msg = `Malformed feature, strategies not an array, is a ${typeof feature.strategies}`; this.emit(events_2.UnleashEvents.Error, new Error(msg)); return { enabled: false }; } if (feature.strategies.length === 0) { return { enabled: feature.enabled }; } let strategyResult = { enabled: false }; (_a = feature.strategies) === null || _a === void 0 ? void 0 : _a.some((strategySelector) => { const strategy = this.getStrategy(strategySelector.name); if (!strategy) { this.warnStrategyOnce(strategySelector.name, feature.name, feature.strategies || []); return false; } const constraints = this.yieldConstraintsFor(strategySelector); const result = strategy.getResult(strategySelector.parameters, context, constraints, strategySelector.variants); if (result.enabled) { strategyResult = result; return true; } return false; }); return strategyResult; } *yieldConstraintsFor(strategy) { var _a; if (strategy.constraints) { yield* strategy.constraints; } const segments = (_a = strategy.segments) === null || _a === void 0 ? void 0 : _a.map((segmentId) => this.repository.getSegment(segmentId)); if (!segments) { return; } yield* this.yieldSegmentConstraints(segments); } *yieldSegmentConstraints(segments) { // eslint-disable-next-line no-restricted-syntax for (const segment of segments) { if (segment) { // eslint-disable-next-line no-restricted-syntax for (const constraint of segment === null || segment === void 0 ? void 0 : segment.constraints) { yield constraint; } } else { yield undefined; } } } getVariant(name, context, fallbackVariant) { const feature = this.repository.getToggle(name); const variant = this.resolveVariant(feature, context, true, fallbackVariant); if (feature === null || feature === void 0 ? void 0 : feature.impressionData) { this.emit(events_2.UnleashEvents.Impression, (0, events_2.createImpressionEvent)({ featureName: name, context, enabled: variant.enabled, eventType: 'getVariant', variant: variant.name, })); } return variant; } // This function is intended to close an issue in the proxy where feature enabled // state gets checked twice when resolving a variant with random stickiness and // gradual rollout. This is not intended for general use, prefer getVariant instead forceGetVariant(name, context, fallbackVariant) { const feature = this.repository.getToggle(name); return this.resolveVariant(feature, context, true, fallbackVariant); } resolveVariant(feature, context, checkToggle, fallbackVariant) { const fallback = fallbackVariant || variant_1.defaultVariant; if (typeof feature === 'undefined') { return { ...fallback, feature_enabled: false, featureEnabled: false }; } let featureEnabled = !checkToggle; if (checkToggle) { const result = this.isFeatureEnabled(feature, context, () => !!(fallbackVariant === null || fallbackVariant === void 0 ? void 0 : fallbackVariant.enabled)); featureEnabled = result.enabled; if (result.enabled && result.variant) { return { ...result.variant, feature_enabled: featureEnabled, featureEnabled }; } if (!result.enabled) { return { ...fallback, feature_enabled: featureEnabled, featureEnabled }; } } if (!feature.variants || !Array.isArray(feature.variants) || feature.variants.length === 0) { return { ...fallback, feature_enabled: featureEnabled, featureEnabled }; } const variant = (0, variant_1.selectVariant)(feature, context); if (variant === null) { return { ...fallback, feature_enabled: featureEnabled, featureEnabled }; } return { name: variant.name, payload: variant.payload, enabled: true, feature_enabled: featureEnabled, featureEnabled, }; } } exports.default = UnleashClient; //# sourceMappingURL=client.js.map