@tamyla/clodo-framework 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 +564 -0
- package/LICENSE +21 -0
- package/README.md +1393 -0
- package/bin/README.md +71 -0
- package/bin/clodo-service.js +416 -0
- package/bin/security/security-cli.js +96 -0
- package/bin/service-management/README.md +74 -0
- package/bin/service-management/create-service.js +129 -0
- package/bin/service-management/init-service.js +102 -0
- package/bin/service-management/init-service.js.backup +889 -0
- package/bin/shared/config/customer-cli.js +293 -0
- package/dist/config/ConfigurationManager.js +159 -0
- package/dist/config/CustomerConfigCLI.js +220 -0
- package/dist/config/FeatureManager.js +426 -0
- package/dist/config/customers.js +441 -0
- package/dist/config/domains.js +180 -0
- package/dist/config/features.js +225 -0
- package/dist/config/index.js +6 -0
- package/dist/database/database-orchestrator.js +730 -0
- package/dist/database/index.js +4 -0
- package/dist/deployment/auditor.js +971 -0
- package/dist/deployment/index.js +10 -0
- package/dist/deployment/rollback-manager.js +523 -0
- package/dist/deployment/testers/api-tester.js +80 -0
- package/dist/deployment/testers/auth-tester.js +129 -0
- package/dist/deployment/testers/core.js +217 -0
- package/dist/deployment/testers/database-tester.js +105 -0
- package/dist/deployment/testers/index.js +74 -0
- package/dist/deployment/testers/load-tester.js +120 -0
- package/dist/deployment/testers/performance-tester.js +105 -0
- package/dist/deployment/validator.js +558 -0
- package/dist/deployment/wrangler-deployer.js +574 -0
- package/dist/handlers/GenericRouteHandler.js +532 -0
- package/dist/index.js +39 -0
- package/dist/migration/MigrationAdapters.js +562 -0
- package/dist/modules/ModuleManager.js +668 -0
- package/dist/modules/security.js +98 -0
- package/dist/orchestration/cross-domain-coordinator.js +1083 -0
- package/dist/orchestration/index.js +5 -0
- package/dist/orchestration/modules/DeploymentCoordinator.js +258 -0
- package/dist/orchestration/modules/DomainResolver.js +196 -0
- package/dist/orchestration/modules/StateManager.js +332 -0
- package/dist/orchestration/multi-domain-orchestrator.js +255 -0
- package/dist/routing/EnhancedRouter.js +158 -0
- package/dist/schema/SchemaManager.js +778 -0
- package/dist/security/ConfigurationValidator.js +490 -0
- package/dist/security/DeploymentManager.js +208 -0
- package/dist/security/SecretGenerator.js +142 -0
- package/dist/security/SecurityCLI.js +228 -0
- package/dist/security/index.js +51 -0
- package/dist/security/patterns/environment-rules.js +66 -0
- package/dist/security/patterns/insecure-patterns.js +21 -0
- package/dist/service-management/ConfirmationEngine.js +411 -0
- package/dist/service-management/ErrorTracker.js +294 -0
- package/dist/service-management/GenerationEngine.js +3109 -0
- package/dist/service-management/InputCollector.js +237 -0
- package/dist/service-management/ServiceCreator.js +229 -0
- package/dist/service-management/ServiceInitializer.js +448 -0
- package/dist/service-management/ServiceOrchestrator.js +638 -0
- package/dist/service-management/handlers/ConfigMutator.js +130 -0
- package/dist/service-management/handlers/ConfirmationHandler.js +71 -0
- package/dist/service-management/handlers/GenerationHandler.js +80 -0
- package/dist/service-management/handlers/InputHandler.js +59 -0
- package/dist/service-management/handlers/ValidationHandler.js +203 -0
- package/dist/service-management/index.js +7 -0
- package/dist/services/GenericDataService.js +488 -0
- package/dist/shared/cloudflare/domain-discovery.js +562 -0
- package/dist/shared/cloudflare/domain-manager.js +912 -0
- package/dist/shared/cloudflare/index.js +8 -0
- package/dist/shared/cloudflare/ops.js +387 -0
- package/dist/shared/config/cache.js +1167 -0
- package/dist/shared/config/command-config-manager.js +174 -0
- package/dist/shared/config/customer-cli.js +258 -0
- package/dist/shared/config/index.js +9 -0
- package/dist/shared/config/manager.js +289 -0
- package/dist/shared/database/connection-manager.js +338 -0
- package/dist/shared/database/index.js +7 -0
- package/dist/shared/database/orchestrator.js +632 -0
- package/dist/shared/deployment/auditor.js +971 -0
- package/dist/shared/deployment/index.js +10 -0
- package/dist/shared/deployment/rollback-manager.js +523 -0
- package/dist/shared/deployment/validator.js +558 -0
- package/dist/shared/index.js +32 -0
- package/dist/shared/monitoring/health-checker.js +250 -0
- package/dist/shared/monitoring/index.js +8 -0
- package/dist/shared/monitoring/memory-manager.js +382 -0
- package/dist/shared/monitoring/production-monitor.js +390 -0
- package/dist/shared/production-tester/api-tester.js +80 -0
- package/dist/shared/production-tester/auth-tester.js +129 -0
- package/dist/shared/production-tester/core.js +217 -0
- package/dist/shared/production-tester/database-tester.js +105 -0
- package/dist/shared/production-tester/index.js +74 -0
- package/dist/shared/production-tester/load-tester.js +120 -0
- package/dist/shared/production-tester/performance-tester.js +105 -0
- package/dist/shared/security/api-token-manager.js +296 -0
- package/dist/shared/security/index.js +8 -0
- package/dist/shared/security/secret-generator.js +918 -0
- package/dist/shared/security/secure-token-manager.js +379 -0
- package/dist/shared/utils/error-recovery.js +240 -0
- package/dist/shared/utils/graceful-shutdown-manager.js +380 -0
- package/dist/shared/utils/index.js +9 -0
- package/dist/shared/utils/interactive-prompts.js +134 -0
- package/dist/shared/utils/rate-limiter.js +249 -0
- package/dist/utils/ErrorHandler.js +173 -0
- package/dist/utils/deployment/config-cache.js +1160 -0
- package/dist/utils/deployment/index.js +6 -0
- package/dist/utils/deployment/interactive-prompts.js +97 -0
- package/dist/utils/deployment/secret-generator.js +896 -0
- package/dist/utils/dirname-helper.js +35 -0
- package/dist/utils/domain-config.js +159 -0
- package/dist/utils/error-recovery.js +240 -0
- package/dist/utils/esm-helper.js +52 -0
- package/dist/utils/framework-config.js +481 -0
- package/dist/utils/graceful-shutdown-manager.js +379 -0
- package/dist/utils/health-checker.js +114 -0
- package/dist/utils/index.js +36 -0
- package/dist/utils/prompt-handler.js +98 -0
- package/dist/utils/usage-tracker.js +252 -0
- package/dist/utils/validation.js +112 -0
- package/dist/version/VersionDetector.js +723 -0
- package/dist/worker/index.js +4 -0
- package/dist/worker/integration.js +332 -0
- package/docs/FRAMEWORK-ARCHITECTURE-OVERVIEW.md +206 -0
- package/docs/INTEGRATION_GUIDE.md +2045 -0
- package/docs/README.md +82 -0
- package/docs/SECURITY.md +242 -0
- package/docs/deployment/deployment-guide.md +540 -0
- package/docs/overview.md +280 -0
- package/package.json +176 -0
- package/types/index.d.ts +575 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature Flag System for CLODO Framework
|
|
3
|
+
* Enables gradual migration and progressive enhancement
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Default feature flags configuration
|
|
8
|
+
*/
|
|
9
|
+
const DEFAULT_FEATURES = {
|
|
10
|
+
// Schema Manager Features
|
|
11
|
+
ENABLE_ENHANCED_SCHEMA: true,
|
|
12
|
+
ENABLE_SCHEMA_CACHING: true,
|
|
13
|
+
ENABLE_COMPREHENSIVE_VALIDATION: true,
|
|
14
|
+
ENABLE_SQL_CACHING: true,
|
|
15
|
+
// Generic Data Service Features
|
|
16
|
+
ENABLE_QUERY_CACHING: true,
|
|
17
|
+
ENABLE_SECURITY_CONTROLS: true,
|
|
18
|
+
ENABLE_ADVANCED_PAGINATION: true,
|
|
19
|
+
ENABLE_RELATIONSHIP_LOADING: true,
|
|
20
|
+
// Module Manager Features
|
|
21
|
+
ENABLE_ENHANCED_HOOKS: true,
|
|
22
|
+
ENABLE_HOOK_TIMEOUT: true,
|
|
23
|
+
ENABLE_HOOK_METRICS: true,
|
|
24
|
+
ENABLE_PARALLEL_EXECUTION: true,
|
|
25
|
+
// Performance Features
|
|
26
|
+
ENABLE_PERFORMANCE_MONITORING: true,
|
|
27
|
+
ENABLE_CACHE_METRICS: true,
|
|
28
|
+
ENABLE_QUERY_OPTIMIZATION: true,
|
|
29
|
+
// Development Features
|
|
30
|
+
ENABLE_DEBUG_LOGGING: false,
|
|
31
|
+
ENABLE_DEVELOPMENT_MODE: false,
|
|
32
|
+
ENABLE_STRICT_VALIDATION: false,
|
|
33
|
+
// Migration Features
|
|
34
|
+
ENABLE_LEGACY_COMPATIBILITY: true,
|
|
35
|
+
ENABLE_DEPRECATION_WARNINGS: true,
|
|
36
|
+
ENABLE_MIGRATION_HELPERS: true
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Feature flag manager for controlling framework capabilities
|
|
41
|
+
*/
|
|
42
|
+
export class FeatureManager {
|
|
43
|
+
constructor(config = {}) {
|
|
44
|
+
this.features = {
|
|
45
|
+
...DEFAULT_FEATURES,
|
|
46
|
+
...config
|
|
47
|
+
};
|
|
48
|
+
this.listeners = new Map();
|
|
49
|
+
this.context = {
|
|
50
|
+
environment: this._detectEnvironment(),
|
|
51
|
+
version: this._getFrameworkVersion(),
|
|
52
|
+
timestamp: Date.now()
|
|
53
|
+
};
|
|
54
|
+
this._logFeatureState();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Check if a feature is enabled
|
|
59
|
+
* @param {string} featureName - Name of the feature flag
|
|
60
|
+
* @returns {boolean} Whether the feature is enabled
|
|
61
|
+
*/
|
|
62
|
+
isEnabled(featureName) {
|
|
63
|
+
// Check for environment-specific overrides
|
|
64
|
+
const envOverride = this._getEnvironmentOverride(featureName);
|
|
65
|
+
if (envOverride !== null) {
|
|
66
|
+
return envOverride;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Check for runtime overrides
|
|
70
|
+
const runtimeOverride = this._getRuntimeOverride(featureName);
|
|
71
|
+
if (runtimeOverride !== null) {
|
|
72
|
+
return runtimeOverride;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Return default configuration
|
|
76
|
+
return this.features[featureName] ?? false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Enable a feature flag
|
|
81
|
+
* @param {string} featureName - Name of the feature flag
|
|
82
|
+
* @param {Object} options - Enable options
|
|
83
|
+
*/
|
|
84
|
+
enable(featureName, options = {}) {
|
|
85
|
+
const previousValue = this.features[featureName];
|
|
86
|
+
this.features[featureName] = true;
|
|
87
|
+
this._logFeatureChange(featureName, previousValue, true, options);
|
|
88
|
+
this._notifyListeners(featureName, true, previousValue);
|
|
89
|
+
|
|
90
|
+
// Auto-enable dependencies if specified
|
|
91
|
+
if (options.dependencies) {
|
|
92
|
+
options.dependencies.forEach(dep => {
|
|
93
|
+
if (!this.isEnabled(dep)) {
|
|
94
|
+
this.enable(dep, {
|
|
95
|
+
reason: `Required by ${featureName}`
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Disable a feature flag
|
|
104
|
+
* @param {string} featureName - Name of the feature flag
|
|
105
|
+
* @param {Object} options - Disable options
|
|
106
|
+
*/
|
|
107
|
+
disable(featureName, options = {}) {
|
|
108
|
+
const previousValue = this.features[featureName];
|
|
109
|
+
this.features[featureName] = false;
|
|
110
|
+
this._logFeatureChange(featureName, previousValue, false, options);
|
|
111
|
+
this._notifyListeners(featureName, false, previousValue);
|
|
112
|
+
|
|
113
|
+
// Auto-disable dependents if specified
|
|
114
|
+
if (options.disableDependents) {
|
|
115
|
+
this._disableDependents(featureName);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Toggle a feature flag
|
|
121
|
+
* @param {string} featureName - Name of the feature flag
|
|
122
|
+
* @returns {boolean} New state of the feature
|
|
123
|
+
*/
|
|
124
|
+
toggle(featureName) {
|
|
125
|
+
const currentState = this.isEnabled(featureName);
|
|
126
|
+
if (currentState) {
|
|
127
|
+
this.disable(featureName, {
|
|
128
|
+
reason: 'Toggled off'
|
|
129
|
+
});
|
|
130
|
+
} else {
|
|
131
|
+
this.enable(featureName, {
|
|
132
|
+
reason: 'Toggled on'
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return !currentState;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Get all feature flags and their states
|
|
140
|
+
* @returns {Object} All feature flags with their current states
|
|
141
|
+
*/
|
|
142
|
+
getAllFeatures() {
|
|
143
|
+
const features = {};
|
|
144
|
+
for (const [name] of Object.entries(DEFAULT_FEATURES)) {
|
|
145
|
+
features[name] = {
|
|
146
|
+
enabled: this.isEnabled(name),
|
|
147
|
+
default: DEFAULT_FEATURES[name],
|
|
148
|
+
configured: this.features[name],
|
|
149
|
+
overridden: this.isEnabled(name) !== this.features[name]
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
return features;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Get features by category
|
|
157
|
+
* @param {string} category - Feature category (e.g., 'SCHEMA', 'DATA_SERVICE')
|
|
158
|
+
* @returns {Object} Features in the specified category
|
|
159
|
+
*/
|
|
160
|
+
getFeaturesByCategory(category) {
|
|
161
|
+
const prefix = `ENABLE_${category}`;
|
|
162
|
+
const categoryFeatures = {};
|
|
163
|
+
for (const [name, value] of Object.entries(this.features)) {
|
|
164
|
+
if (name.startsWith(prefix)) {
|
|
165
|
+
categoryFeatures[name] = {
|
|
166
|
+
enabled: this.isEnabled(name),
|
|
167
|
+
configured: value
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return categoryFeatures;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Listen for feature flag changes
|
|
176
|
+
* @param {string} featureName - Name of the feature flag to listen for
|
|
177
|
+
* @param {Function} callback - Callback function
|
|
178
|
+
*/
|
|
179
|
+
onFeatureChange(featureName, callback) {
|
|
180
|
+
if (!this.listeners.has(featureName)) {
|
|
181
|
+
this.listeners.set(featureName, []);
|
|
182
|
+
}
|
|
183
|
+
this.listeners.get(featureName).push(callback);
|
|
184
|
+
|
|
185
|
+
// Return unsubscribe function
|
|
186
|
+
return () => {
|
|
187
|
+
const callbacks = this.listeners.get(featureName);
|
|
188
|
+
if (callbacks) {
|
|
189
|
+
const index = callbacks.indexOf(callback);
|
|
190
|
+
if (index > -1) {
|
|
191
|
+
callbacks.splice(index, 1);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Safely execute code with feature flag check
|
|
199
|
+
* @param {string} featureName - Name of the feature flag
|
|
200
|
+
* @param {Function} enabledCallback - Function to execute if feature is enabled
|
|
201
|
+
* @param {Function} disabledCallback - Function to execute if feature is disabled
|
|
202
|
+
* @returns {any} Result of the executed callback
|
|
203
|
+
*/
|
|
204
|
+
withFeature(featureName, enabledCallback, disabledCallback = null) {
|
|
205
|
+
if (this.isEnabled(featureName)) {
|
|
206
|
+
try {
|
|
207
|
+
return enabledCallback();
|
|
208
|
+
} catch (error) {
|
|
209
|
+
console.warn(`Feature '${featureName}' execution failed:`, error);
|
|
210
|
+
if (disabledCallback) {
|
|
211
|
+
return disabledCallback();
|
|
212
|
+
}
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
} else {
|
|
216
|
+
if (this.isEnabled('ENABLE_DEPRECATION_WARNINGS')) {
|
|
217
|
+
console.warn(`Feature '${featureName}' is disabled. Consider enabling it for enhanced functionality.`);
|
|
218
|
+
}
|
|
219
|
+
return disabledCallback ? disabledCallback() : null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Create a feature-gated wrapper function
|
|
225
|
+
* @param {string} featureName - Name of the feature flag
|
|
226
|
+
* @param {Function} enhancedFunction - Enhanced function to use when feature is enabled
|
|
227
|
+
* @param {Function} legacyFunction - Legacy function to use when feature is disabled
|
|
228
|
+
* @returns {Function} Wrapped function that chooses implementation based on feature flag
|
|
229
|
+
*/
|
|
230
|
+
createFeatureGate(featureName, enhancedFunction, legacyFunction) {
|
|
231
|
+
return (...args) => {
|
|
232
|
+
return this.withFeature(featureName, () => enhancedFunction(...args), legacyFunction ? () => legacyFunction(...args) : null);
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Validate feature flag configuration
|
|
238
|
+
* @returns {Array} Array of validation errors
|
|
239
|
+
*/
|
|
240
|
+
validateConfiguration() {
|
|
241
|
+
const errors = [];
|
|
242
|
+
|
|
243
|
+
// Check for unknown feature flags
|
|
244
|
+
for (const featureName of Object.keys(this.features)) {
|
|
245
|
+
if (!(featureName in DEFAULT_FEATURES)) {
|
|
246
|
+
errors.push(`Unknown feature flag: ${featureName}`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Check for conflicting features
|
|
251
|
+
const conflicts = [['ENABLE_LEGACY_COMPATIBILITY', 'ENABLE_STRICT_VALIDATION'], ['ENABLE_DEVELOPMENT_MODE', 'ENABLE_QUERY_OPTIMIZATION']];
|
|
252
|
+
for (const [feature1, feature2] of conflicts) {
|
|
253
|
+
if (this.isEnabled(feature1) && this.isEnabled(feature2)) {
|
|
254
|
+
errors.push(`Conflicting features enabled: ${feature1} and ${feature2}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return errors;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Private methods
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Detect current environment
|
|
264
|
+
* @private
|
|
265
|
+
*/
|
|
266
|
+
_detectEnvironment() {
|
|
267
|
+
if (typeof process !== 'undefined' && process.env) {
|
|
268
|
+
return process.env.NODE_ENV || 'development';
|
|
269
|
+
}
|
|
270
|
+
if (typeof globalThis !== 'undefined' && globalThis.navigator) {
|
|
271
|
+
return 'browser';
|
|
272
|
+
}
|
|
273
|
+
return 'worker';
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Get framework version
|
|
278
|
+
* @private
|
|
279
|
+
*/
|
|
280
|
+
_getFrameworkVersion() {
|
|
281
|
+
// Try to get version from package.json if available
|
|
282
|
+
return '2.0.0'; // Placeholder - should be dynamically determined
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Get environment-specific override
|
|
287
|
+
* @private
|
|
288
|
+
*/
|
|
289
|
+
_getEnvironmentOverride(featureName) {
|
|
290
|
+
if (typeof process !== 'undefined' && process.env) {
|
|
291
|
+
const envVar = `CLODO_${featureName}`;
|
|
292
|
+
if (process.env[envVar] !== undefined) {
|
|
293
|
+
return process.env[envVar] === 'true';
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Get runtime override
|
|
301
|
+
* @private
|
|
302
|
+
*/
|
|
303
|
+
_getRuntimeOverride(featureName) {
|
|
304
|
+
// Check for URL parameters in browser environment
|
|
305
|
+
if (typeof URLSearchParams !== 'undefined' && typeof globalThis !== 'undefined' && globalThis.location) {
|
|
306
|
+
const params = new URLSearchParams(globalThis.location.search);
|
|
307
|
+
const override = params.get(`clodo_${featureName.toLowerCase()}`);
|
|
308
|
+
if (override !== null) {
|
|
309
|
+
return override === 'true';
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Log feature flag state on initialization
|
|
317
|
+
* @private
|
|
318
|
+
*/
|
|
319
|
+
_logFeatureState() {
|
|
320
|
+
if (this.isEnabled('ENABLE_DEBUG_LOGGING')) {
|
|
321
|
+
console.log('CLODO Framework Feature Flags:', {
|
|
322
|
+
context: this.context,
|
|
323
|
+
features: this.getAllFeatures()
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Log feature flag changes
|
|
330
|
+
* @private
|
|
331
|
+
*/
|
|
332
|
+
_logFeatureChange(featureName, previousValue, newValue, options) {
|
|
333
|
+
if (this.isEnabled('ENABLE_DEBUG_LOGGING')) {
|
|
334
|
+
console.log(`Feature flag changed: ${featureName}`, {
|
|
335
|
+
from: previousValue,
|
|
336
|
+
to: newValue,
|
|
337
|
+
options,
|
|
338
|
+
timestamp: new Date().toISOString()
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Notify listeners of feature flag changes
|
|
345
|
+
* @private
|
|
346
|
+
*/
|
|
347
|
+
_notifyListeners(featureName, newValue, previousValue) {
|
|
348
|
+
const callbacks = this.listeners.get(featureName);
|
|
349
|
+
if (callbacks) {
|
|
350
|
+
callbacks.forEach(callback => {
|
|
351
|
+
try {
|
|
352
|
+
callback(newValue, previousValue, featureName);
|
|
353
|
+
} catch (error) {
|
|
354
|
+
console.error(`Feature listener error for '${featureName}':`, error);
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Disable features that depend on the given feature
|
|
362
|
+
* @private
|
|
363
|
+
*/
|
|
364
|
+
_disableDependents(featureName) {
|
|
365
|
+
const dependents = this._findDependents(featureName);
|
|
366
|
+
dependents.forEach(dependent => {
|
|
367
|
+
if (this.isEnabled(dependent)) {
|
|
368
|
+
this.disable(dependent, {
|
|
369
|
+
reason: `Dependency ${featureName} was disabled`,
|
|
370
|
+
cascade: true
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Find features that depend on the given feature
|
|
378
|
+
* @private
|
|
379
|
+
*/
|
|
380
|
+
_findDependents(featureName) {
|
|
381
|
+
// Simple dependency mapping - in a real implementation, this would be more sophisticated
|
|
382
|
+
const dependencies = {
|
|
383
|
+
'ENABLE_ENHANCED_SCHEMA': ['ENABLE_SCHEMA_CACHING', 'ENABLE_COMPREHENSIVE_VALIDATION'],
|
|
384
|
+
'ENABLE_QUERY_CACHING': ['ENABLE_CACHE_METRICS'],
|
|
385
|
+
'ENABLE_ENHANCED_HOOKS': ['ENABLE_HOOK_TIMEOUT', 'ENABLE_HOOK_METRICS']
|
|
386
|
+
};
|
|
387
|
+
const dependents = [];
|
|
388
|
+
for (const [feature, deps] of Object.entries(dependencies)) {
|
|
389
|
+
if (deps.includes(featureName)) {
|
|
390
|
+
dependents.push(feature);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return dependents;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Create global feature manager instance
|
|
398
|
+
export const featureManager = new FeatureManager();
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Convenience function to check if a feature is enabled
|
|
402
|
+
* @param {string} featureName - Name of the feature flag
|
|
403
|
+
* @returns {boolean} Whether the feature is enabled
|
|
404
|
+
*/
|
|
405
|
+
export function isFeatureEnabled(featureName) {
|
|
406
|
+
return featureManager.isEnabled(featureName);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Convenience function to execute code with feature flag check
|
|
411
|
+
* @param {string} featureName - Name of the feature flag
|
|
412
|
+
* @param {Function} enabledCallback - Function to execute if feature is enabled
|
|
413
|
+
* @param {Function} disabledCallback - Function to execute if feature is disabled
|
|
414
|
+
* @returns {any} Result of the executed callback
|
|
415
|
+
*/
|
|
416
|
+
export function withFeature(featureName, enabledCallback, disabledCallback = null) {
|
|
417
|
+
return featureManager.withFeature(featureName, enabledCallback, disabledCallback);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Export feature flag constants for easy use
|
|
422
|
+
*/
|
|
423
|
+
export const FEATURES = Object.keys(DEFAULT_FEATURES).reduce((acc, key) => {
|
|
424
|
+
acc[key] = key;
|
|
425
|
+
return acc;
|
|
426
|
+
}, {});
|