@tamyla/clodo-framework 3.1.5 → 3.1.8
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 +22 -0
- package/bin/clodo-service.js +29 -947
- package/bin/database/enterprise-db-manager.js +7 -5
- package/bin/security/security-cli.js +0 -0
- package/bin/service-management/create-service.js +0 -0
- package/bin/service-management/init-service.js +0 -0
- package/bin/shared/cloudflare/domain-discovery.js +11 -10
- package/bin/shared/cloudflare/ops.js +1 -1
- package/bin/shared/config/ConfigurationManager.js +539 -0
- package/bin/shared/config/index.js +13 -1
- package/bin/shared/database/connection-manager.js +2 -2
- package/bin/shared/database/orchestrator.js +5 -4
- package/bin/shared/deployment/auditor.js +9 -8
- package/bin/shared/logging/Logger.js +214 -0
- package/bin/shared/monitoring/production-monitor.js +21 -9
- package/bin/shared/utils/ErrorHandler.js +675 -0
- package/bin/shared/utils/error-recovery.js +33 -13
- package/bin/shared/utils/file-manager.js +162 -0
- package/bin/shared/utils/formatters.js +247 -0
- package/bin/shared/utils/index.js +14 -4
- package/bin/shared/validation/ValidationRegistry.js +143 -0
- package/dist/deployment/auditor.js +23 -8
- package/dist/deployment/orchestration/BaseDeploymentOrchestrator.js +426 -0
- package/dist/deployment/orchestration/EnterpriseOrchestrator.js +401 -0
- package/dist/deployment/orchestration/PortfolioOrchestrator.js +273 -0
- package/dist/deployment/orchestration/SingleServiceOrchestrator.js +231 -0
- package/dist/deployment/orchestration/UnifiedDeploymentOrchestrator.js +662 -0
- package/dist/deployment/orchestration/index.js +17 -0
- package/dist/index.js +12 -0
- package/dist/orchestration/modules/DomainResolver.js +8 -6
- package/dist/orchestration/multi-domain-orchestrator.js +13 -1
- package/dist/security/index.js +2 -2
- package/dist/service-management/ConfirmationEngine.js +8 -7
- package/dist/service-management/ErrorTracker.js +7 -2
- package/dist/service-management/InputCollector.js +18 -12
- package/dist/service-management/ServiceCreator.js +22 -7
- package/dist/service-management/ServiceInitializer.js +12 -18
- package/dist/shared/cloudflare/domain-discovery.js +11 -10
- package/dist/shared/cloudflare/ops.js +1 -1
- package/dist/shared/config/ConfigurationManager.js +519 -0
- package/dist/shared/config/index.js +5 -1
- package/dist/shared/database/connection-manager.js +2 -2
- package/dist/shared/database/orchestrator.js +13 -4
- package/dist/shared/deployment/auditor.js +23 -8
- package/dist/shared/logging/Logger.js +209 -0
- package/dist/shared/monitoring/production-monitor.js +24 -8
- package/dist/{utils → shared/utils}/ErrorHandler.js +306 -28
- package/dist/shared/utils/error-recovery.js +33 -13
- package/dist/shared/utils/file-manager.js +155 -0
- package/dist/shared/utils/formatters.js +215 -0
- package/dist/shared/utils/index.js +14 -4
- package/dist/shared/validation/ValidationRegistry.js +126 -0
- package/dist/utils/config/unified-config-manager.js +14 -12
- package/dist/utils/deployment/config-cache.js +3 -1
- package/dist/utils/deployment/secret-generator.js +32 -29
- package/dist/utils/framework-config.js +6 -3
- package/dist/utils/ui-structures-loader.js +3 -0
- package/dist/worker/integration.js +11 -1
- package/package.json +31 -3
- package/dist/config/FeatureManager.js +0 -426
- package/dist/config/features.js +0 -230
- package/dist/utils/error-recovery.js +0 -240
|
@@ -1,426 +0,0 @@
|
|
|
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
|
-
}, {});
|
package/dist/config/features.js
DELETED
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
// Simple inline logger to avoid circular dependency with index.js
|
|
2
|
-
const logger = {
|
|
3
|
-
info: (message, ...args) => console.log(`[FeatureFlagManager] ${message}`, ...args),
|
|
4
|
-
error: (message, ...args) => console.error(`[FeatureFlagManager] ${message}`, ...args),
|
|
5
|
-
warn: (message, ...args) => console.warn(`[FeatureFlagManager] ${message}`, ...args),
|
|
6
|
-
debug: (message, ...args) => console.debug(`[FeatureFlagManager] ${message}`, ...args)
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Feature Flag Manager Class
|
|
11
|
-
* Manages feature flags for domain-specific functionality
|
|
12
|
-
*/
|
|
13
|
-
export class FeatureFlagManager {
|
|
14
|
-
constructor() {
|
|
15
|
-
this.currentDomain = null;
|
|
16
|
-
this.globalOverrides = new Map();
|
|
17
|
-
this.listeners = new Set();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Sets the current domain configuration
|
|
22
|
-
* @param {Object} domainConfig - Domain configuration object
|
|
23
|
-
*/
|
|
24
|
-
setDomain(domainConfig) {
|
|
25
|
-
if (!domainConfig) {
|
|
26
|
-
throw new Error('Domain configuration is required');
|
|
27
|
-
}
|
|
28
|
-
this.currentDomain = domainConfig;
|
|
29
|
-
logger.info(`Domain set for feature flags: ${domainConfig.name}`);
|
|
30
|
-
|
|
31
|
-
// Notify listeners of domain change
|
|
32
|
-
this._notifyListeners('domainChanged', {
|
|
33
|
-
domain: domainConfig.name
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Checks if a feature is enabled
|
|
39
|
-
* @param {string} featureName - Name of the feature to check
|
|
40
|
-
* @param {boolean} defaultValue - Default value if feature not configured
|
|
41
|
-
* @returns {boolean} Whether the feature is enabled
|
|
42
|
-
*/
|
|
43
|
-
isEnabled(featureName, defaultValue = false) {
|
|
44
|
-
if (!this.currentDomain) {
|
|
45
|
-
logger.warn('No domain set, using default value for feature check');
|
|
46
|
-
return defaultValue;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Check global overrides first
|
|
50
|
-
if (this.globalOverrides.has(featureName)) {
|
|
51
|
-
return this.globalOverrides.get(featureName);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Check domain-specific features
|
|
55
|
-
const features = this.currentDomain.features || {};
|
|
56
|
-
const enabled = features[featureName] ?? defaultValue;
|
|
57
|
-
logger.debug(`Feature ${featureName}: ${enabled ? 'enabled' : 'disabled'}`);
|
|
58
|
-
return enabled;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Gets all enabled features for current domain
|
|
63
|
-
* @returns {string[]} Array of enabled feature names
|
|
64
|
-
*/
|
|
65
|
-
getEnabledFeatures() {
|
|
66
|
-
if (!this.currentDomain?.features) {
|
|
67
|
-
return [];
|
|
68
|
-
}
|
|
69
|
-
return Object.entries(this.currentDomain.features).filter(([, enabled]) => enabled === true).map(([feature]) => feature);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Gets all disabled features for current domain
|
|
74
|
-
* @returns {string[]} Array of disabled feature names
|
|
75
|
-
*/
|
|
76
|
-
getDisabledFeatures() {
|
|
77
|
-
if (!this.currentDomain?.features) {
|
|
78
|
-
return [];
|
|
79
|
-
}
|
|
80
|
-
return Object.entries(this.currentDomain.features).filter(([, enabled]) => enabled === false).map(([feature]) => feature);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Gets all configured features with their status
|
|
85
|
-
* @returns {Object} Object mapping feature names to enabled status
|
|
86
|
-
*/
|
|
87
|
-
getAllFeatures() {
|
|
88
|
-
return this.currentDomain?.features || {};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Sets a global feature override
|
|
93
|
-
* @param {string} featureName - Name of the feature
|
|
94
|
-
* @param {boolean} enabled - Whether to enable the feature
|
|
95
|
-
*/
|
|
96
|
-
setGlobalOverride(featureName, enabled) {
|
|
97
|
-
this.globalOverrides.set(featureName, enabled);
|
|
98
|
-
logger.info(`Global override set: ${featureName} = ${enabled}`);
|
|
99
|
-
this._notifyListeners('overrideChanged', {
|
|
100
|
-
feature: featureName,
|
|
101
|
-
enabled
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Removes a global feature override
|
|
107
|
-
* @param {string} featureName - Name of the feature
|
|
108
|
-
*/
|
|
109
|
-
removeGlobalOverride(featureName) {
|
|
110
|
-
const hadOverride = this.globalOverrides.has(featureName);
|
|
111
|
-
this.globalOverrides.delete(featureName);
|
|
112
|
-
if (hadOverride) {
|
|
113
|
-
logger.info(`Global override removed: ${featureName}`);
|
|
114
|
-
this._notifyListeners('overrideChanged', {
|
|
115
|
-
feature: featureName,
|
|
116
|
-
removed: true
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Clears all global overrides
|
|
123
|
-
*/
|
|
124
|
-
clearGlobalOverrides() {
|
|
125
|
-
const count = this.globalOverrides.size;
|
|
126
|
-
this.globalOverrides.clear();
|
|
127
|
-
if (count > 0) {
|
|
128
|
-
logger.info(`Cleared ${count} global overrides`);
|
|
129
|
-
this._notifyListeners('overridesCleared', {
|
|
130
|
-
count
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Adds a listener for feature flag changes
|
|
137
|
-
* @param {Function} listener - Callback function
|
|
138
|
-
*/
|
|
139
|
-
addListener(listener) {
|
|
140
|
-
this.listeners.add(listener);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Removes a listener
|
|
145
|
-
* @param {Function} listener - Callback function to remove
|
|
146
|
-
*/
|
|
147
|
-
removeListener(listener) {
|
|
148
|
-
this.listeners.delete(listener);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Gets feature flag status with metadata
|
|
153
|
-
* @param {string} featureName - Name of the feature
|
|
154
|
-
* @returns {Object} Feature status information
|
|
155
|
-
*/
|
|
156
|
-
getFeatureInfo(featureName) {
|
|
157
|
-
const domainEnabled = this.currentDomain?.features?.[featureName] ?? null;
|
|
158
|
-
const globalOverride = this.globalOverrides.get(featureName);
|
|
159
|
-
const effectiveEnabled = this.isEnabled(featureName);
|
|
160
|
-
return {
|
|
161
|
-
name: featureName,
|
|
162
|
-
domainEnabled,
|
|
163
|
-
globalOverride,
|
|
164
|
-
effectiveEnabled,
|
|
165
|
-
hasOverride: globalOverride !== undefined,
|
|
166
|
-
domain: this.currentDomain?.name
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Creates a feature toggle function
|
|
172
|
-
* @param {string} featureName - Name of the feature
|
|
173
|
-
* @param {Function} enabledHandler - Function to call when enabled
|
|
174
|
-
* @param {Function} disabledHandler - Function to call when disabled
|
|
175
|
-
* @returns {Function} Toggle function
|
|
176
|
-
*/
|
|
177
|
-
createToggle(featureName, enabledHandler, disabledHandler) {
|
|
178
|
-
return (...args) => {
|
|
179
|
-
if (this.isEnabled(featureName)) {
|
|
180
|
-
return enabledHandler?.(...args);
|
|
181
|
-
} else {
|
|
182
|
-
return disabledHandler?.(...args);
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Notifies all listeners of changes
|
|
189
|
-
* @private
|
|
190
|
-
*/
|
|
191
|
-
_notifyListeners(event, data) {
|
|
192
|
-
this.listeners.forEach(listener => {
|
|
193
|
-
try {
|
|
194
|
-
listener(event, data);
|
|
195
|
-
} catch (error) {
|
|
196
|
-
logger.error(`Error in feature flag listener: ${error.message}`);
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Default singleton instance
|
|
203
|
-
export const featureManager = new FeatureFlagManager();
|
|
204
|
-
|
|
205
|
-
// Convenience functions for common operations
|
|
206
|
-
export const isFeatureEnabled = (featureName, defaultValue = false) => featureManager.isEnabled(featureName, defaultValue);
|
|
207
|
-
export const getEnabledFeatures = () => featureManager.getEnabledFeatures();
|
|
208
|
-
export const setFeatureOverride = (featureName, enabled) => featureManager.setGlobalOverride(featureName, enabled);
|
|
209
|
-
|
|
210
|
-
// Feature flag constants for common features
|
|
211
|
-
export const COMMON_FEATURES = {
|
|
212
|
-
AUTHENTICATION: 'authentication',
|
|
213
|
-
AUTHORIZATION: 'authorization',
|
|
214
|
-
LOGGING: 'logging',
|
|
215
|
-
MONITORING: 'monitoring',
|
|
216
|
-
ANALYTICS: 'analytics',
|
|
217
|
-
CACHING: 'caching',
|
|
218
|
-
RATE_LIMITING: 'rateLimiting',
|
|
219
|
-
FILE_STORAGE: 'fileStorage',
|
|
220
|
-
EMAIL_NOTIFICATIONS: 'emailNotifications',
|
|
221
|
-
PUSH_NOTIFICATIONS: 'pushNotifications',
|
|
222
|
-
SEARCH: 'search',
|
|
223
|
-
FILTERING: 'filtering',
|
|
224
|
-
SORTING: 'sorting',
|
|
225
|
-
PAGINATION: 'pagination',
|
|
226
|
-
EXPORT: 'export',
|
|
227
|
-
IMPORT: 'import',
|
|
228
|
-
BACKUP: 'backup',
|
|
229
|
-
RESTORE: 'restore'
|
|
230
|
-
};
|