@arc-js/config-manager 0.0.1 → 0.0.3

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/README.md CHANGED
@@ -114,7 +114,7 @@ src/
114
114
  // main.tsx
115
115
  import React from 'react';
116
116
  import ReactDOM from 'react-dom/client';
117
- import { ConfigProvider } from '@arc-js/config-manager';
117
+ import { ConfigManagerProvider } from '@arc-js/config-manager';
118
118
 
119
119
  const App = () => {
120
120
  return (
@@ -126,9 +126,9 @@ const App = () => {
126
126
 
127
127
  ReactDOM.createRoot(document.getElementById('root')!).render(
128
128
  <React.StrictMode>
129
- <ConfigProvider preloadModules={['admin', 'dashboard']}>
129
+ <ConfigManagerProvider configs={{ base: {}, modules: {} }}>
130
130
  <App />
131
- </ConfigProvider>
131
+ </ConfigManagerProvider>
132
132
  </React.StrictMode>
133
133
  );
134
134
  ```
@@ -144,181 +144,105 @@ const MyComponent = () => {
144
144
  const config = useConfig('admin'); // Optionnel: nom du module
145
145
 
146
146
  // Récupérer une valeur avec chemin pointé
147
- const appName = config.get('app.name');
148
- const apiTimeout = config.get('api.timeout');
147
+ const appName = config.cf('app.name');
148
+ const apiTimeout = config.cf('api.timeout');
149
149
 
150
150
  // Récupérer avec valeur par défaut
151
- const debugMode = config.get('app.debug', { defaultValue: false });
151
+ const debugMode = config.cf('app.debug', { defaultValue: false });
152
152
 
153
- // Récupérer ou lancer une exception si non trouvé
154
- const baseUrl = config.getOrThrow('api.baseUrl');
153
+ // Récupérer toute la configuration du module
154
+ const adminConfig = config.getConfig('admin');
155
155
 
156
- // Vérifier l'existence d'un chemin
157
- const hasAnalytics = config.has('app.features.analytics');
156
+ // Vérifier si le module est chargé
157
+ const isModuleLoaded = config.isModuleLoaded;
158
158
 
159
- // Récupérer toutes les configurations
160
- const allConfigs = config.getAll();
161
-
162
- // Accès aux variables d'environnement
163
- const nodeEnv = config.getEnv('NODE_ENV');
164
- const apiUrl = config.getEnv('apiUrl'); // VITE_API_URL
159
+ // Recharger les configurations
160
+ const handleReload = () => config.reloadConfig();
165
161
 
166
162
  return (
167
163
  <div>
168
164
  <h1>{appName}</h1>
169
165
  <p>API Timeout: {apiTimeout}ms</p>
170
- <p>Environment: {nodeEnv}</p>
166
+ <p>Module chargé: {isModuleLoaded ? 'Oui' : 'Non'}</p>
167
+ <button onClick={handleReload}>Recharger la configuration</button>
171
168
  </div>
172
169
  );
173
170
  };
174
171
  ```
175
172
 
176
- ### Hook useConfigValue (Typé)
173
+ ## 🛠️ Configuration du Provider
177
174
 
178
175
  ```typescript
179
- import { useConfigValue } from '@arc-js/config-manager';
180
-
181
- const FeatureToggle = () => {
182
- const { value: isEnabled, isLoading, exists } =
183
- useConfigValue<boolean>('app.features.analytics', {
184
- moduleName: 'admin',
185
- defaultValue: false
186
- });
187
-
188
- if (isLoading) return <div>Chargement...</div>;
189
-
190
- return (
191
- <div>
192
- <p>Analytics: {isEnabled ? 'Activé' : 'Désactivé'}</p>
193
- <p>Configuration existante: {exists ? 'Oui' : 'Non'}</p>
194
- </div>
195
- );
176
+ // App.tsx
177
+ import { ConfigManagerProvider } from '@arc-js/config-manager';
178
+
179
+ // Configuration avec chargeurs dynamiques
180
+ const configs = {
181
+ base: {
182
+ main: async () => (await import('./config.json')).default,
183
+ env: async () => ({
184
+ apiUrl: import.meta.env.VITE_API_URL,
185
+ nodeEnv: import.meta.env.NODE_ENV
186
+ })
187
+ },
188
+ modules: {
189
+ admin: async () => (await import('./modules/admin/config.json')).default,
190
+ dashboard: async () => (await import('./modules/dashboard/config.json')).default
191
+ }
196
192
  };
197
- ```
198
-
199
- ### Modifier une configuration
200
193
 
201
- ```typescript
202
- const SettingsPanel = () => {
203
- const config = useConfig();
204
-
205
- const toggleDebug = () => {
206
- const currentDebug = config.get('app.debug', { defaultValue: false });
207
- config.set('app.debug', !currentDebug);
208
- };
209
-
210
- const updateApiConfig = () => {
211
- config.merge({
212
- api: {
213
- timeout: 60000,
214
- retryAttempts: 5
215
- }
216
- }, 'admin'); // Appliquer uniquement au module admin
217
- };
218
-
194
+ const App = () => {
219
195
  return (
220
- <div>
221
- <button onClick={toggleDebug}>
222
- Toggle Debug Mode
223
- </button>
224
- <button onClick={updateApiConfig}>
225
- Update API Settings
226
- </button>
227
- </div>
196
+ <ConfigManagerProvider configs={configs}>
197
+ {/* Votre application */}
198
+ </ConfigManagerProvider>
228
199
  );
229
200
  };
230
201
  ```
231
202
 
232
203
  ## 🔧 Utilisation Avancée
233
204
 
234
- ### Variables d'environnement
235
-
236
- ```bash
237
- # .env
238
- VITE_API_URL=https://api.example.com
239
- VITE_APP_NAME="My App"
240
- VITE_DEBUG=true
241
- NODE_ENV=development
242
- ```
205
+ ### Accès aux variables d'environnement
243
206
 
244
207
  ```typescript
245
208
  const EnvironmentInfo = () => {
246
209
  const config = useConfig();
247
210
 
248
- // Accès aux variables VITE_*
249
- const apiUrl = config.getEnv('apiUrl'); // VITE_API_URL
250
- const appName = config.getEnv('appName'); // VITE_APP_NAME
251
-
252
- // Ou via le module ENV
253
- const viteApiUrl = config.get('apiUrl', { moduleName: 'ENV' });
254
-
255
- // Vérifier l'environnement
256
- const isProd = config.isProduction();
257
- const isDev = config.isDevelopment();
258
- const isTest = config.isTest();
211
+ // Accès aux variables via le module 'env'
212
+ const apiUrl = config.cf('apiUrl', { moduleName: 'env' });
213
+ const nodeEnv = config.cf('nodeEnv', { moduleName: 'env' });
259
214
 
260
215
  return (
261
216
  <div>
262
217
  <h2>Environment Configuration</h2>
263
218
  <p>API URL: {apiUrl}</p>
264
- <p>App Name: {appName}</p>
265
- <p>Mode: {isProd ? 'Production' : isDev ? 'Development' : 'Test'}</p>
219
+ <p>Node Environment: {nodeEnv}</p>
266
220
  </div>
267
221
  );
268
222
  };
269
223
  ```
270
224
 
271
- ### Configuration hiérarchique complexe
272
-
273
- ```json
274
- // src/modules/complex/config.json
275
- {
276
- "database": {
277
- "connections": {
278
- "primary": {
279
- "host": "localhost",
280
- "port": 5432,
281
- "credentials": {
282
- "username": "admin",
283
- "password": "secret"
284
- },
285
- "options": {
286
- "ssl": true,
287
- "pool": {
288
- "max": 20,
289
- "min": 5,
290
- "idleTimeout": 30000
291
- }
292
- }
293
- },
294
- "replica": {
295
- "host": "replica.local",
296
- "port": 5432
297
- }
298
- }
299
- }
300
- }
301
- ```
225
+ ### Chargement asynchrone de module
302
226
 
303
227
  ```typescript
304
- const DatabaseConfig = () => {
305
- const config = useConfig('complex');
306
-
307
- // Accès aux chemins profonds
308
- const primaryHost = config.get('database.connections.primary.host');
309
- const poolMax = config.get('database.connections.primary.options.pool.max');
310
- const sslEnabled = config.get('database.connections.primary.options.ssl');
311
-
312
- // Utilisation avec tableau de chemin
313
- const credentials = config.get(['database', 'connections', 'primary', 'credentials']);
228
+ const LazyModule = () => {
229
+ const config = useConfig();
230
+ const [moduleConfig, setModuleConfig] = useState(null);
231
+
232
+ useEffect(() => {
233
+ // Charger un module spécifique à la demande
234
+ config.loadModuleConfig('analytics').then(() => {
235
+ const analyticsConfig = config.getConfig('analytics');
236
+ setModuleConfig(analyticsConfig);
237
+ });
238
+ }, []);
239
+
240
+ if (!moduleConfig) return <div>Chargement du module...</div>;
314
241
 
315
242
  return (
316
243
  <div>
317
- <h3>Database Configuration</h3>
318
- <p>Primary Host: {primaryHost}</p>
319
- <p>Max Pool Connections: {poolMax}</p>
320
- <p>SSL: {sslEnabled ? 'Enabled' : 'Disabled'}</p>
321
- <pre>{JSON.stringify(credentials, null, 2)}</pre>
244
+ <h3>Configuration Analytics</h3>
245
+ <pre>{JSON.stringify(moduleConfig, null, 2)}</pre>
322
246
  </div>
323
247
  );
324
248
  };
@@ -326,7 +250,7 @@ const DatabaseConfig = () => {
326
250
 
327
251
  ## 🎯 Exemples Complets
328
252
 
329
- ### Exemple 1 : Configuration d'API avec fallback
253
+ ### Exemple 1 : Configuration d'API
330
254
 
331
255
  ```typescript
332
256
  import { useConfig } from '@arc-js/config-manager';
@@ -335,14 +259,12 @@ const ApiClient = () => {
335
259
  const config = useConfig();
336
260
 
337
261
  const getApiConfig = () => {
338
- // Utiliser l'URL de l'ENV si disponible, sinon la config globale
339
- const baseUrl = config.getEnv('apiUrl') ||
340
- config.get('api.baseUrl', {
341
- defaultValue: 'http://localhost:3000'
342
- });
262
+ const baseUrl = config.cf('api.baseUrl', {
263
+ defaultValue: 'http://localhost:3000'
264
+ });
343
265
 
344
- const timeout = config.get('api.timeout', { defaultValue: 30000 });
345
- const retryAttempts = config.get('api.retryAttempts', { defaultValue: 3 });
266
+ const timeout = config.cf('api.timeout', { defaultValue: 30000 });
267
+ const retryAttempts = config.cf('api.retryAttempts', { defaultValue: 3 });
346
268
 
347
269
  return { baseUrl, timeout, retryAttempts };
348
270
  };
@@ -351,13 +273,17 @@ const ApiClient = () => {
351
273
  const { baseUrl, timeout } = getApiConfig();
352
274
 
353
275
  try {
276
+ const controller = new AbortController();
277
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
278
+
354
279
  const response = await fetch(`\${baseUrl}/data`, {
355
- timeout,
280
+ signal: controller.signal,
356
281
  headers: {
357
282
  'Content-Type': 'application/json'
358
283
  }
359
284
  });
360
285
 
286
+ clearTimeout(timeoutId);
361
287
  return await response.json();
362
288
  } catch (error) {
363
289
  console.error('API Error:', error);
@@ -378,21 +304,23 @@ const ApiClient = () => {
378
304
  ### Exemple 2 : Feature flags modulaires
379
305
 
380
306
  ```typescript
381
- import { useConfigValue } from '@arc-js/config-manager';
382
-
383
307
  const FeatureComponent = ({ featureName, moduleName }: {
384
308
  featureName: string,
385
309
  moduleName?: string
386
310
  }) => {
387
- const { value: isEnabled, isLoading } = useConfigValue<boolean>(
388
- `features.\${featureName}`,
389
- {
390
- moduleName,
391
- defaultValue: false
311
+ const config = useConfig(moduleName);
312
+ const [isEnabled, setIsEnabled] = useState(false);
313
+ const [isLoading, setIsLoading] = useState(true);
314
+
315
+ useEffect(() => {
316
+ if (config.isModuleLoaded) {
317
+ const enabled = config.cf(`features.\${featureName}`, { defaultValue: false });
318
+ setIsEnabled(enabled);
319
+ setIsLoading(false);
392
320
  }
393
- );
394
-
395
- if (isLoading) return <div>Loading feature...</div>;
321
+ }, [config.isModuleLoaded, featureName]);
322
+
323
+ if (isLoading) return <div>Chargement de la feature...</div>;
396
324
  if (!isEnabled) return null;
397
325
 
398
326
  return (
@@ -415,69 +343,58 @@ const AppFeatures = () => {
415
343
  };
416
344
  ```
417
345
 
418
- ### Exemple 3 : Configuration dynamique avec React Query
346
+ ## 📋 API Reference
347
+
348
+ ### ConfigManagerProvider
349
+ | Prop | Type | Description |
350
+ |------|------|-------------|
351
+ | `configs` | `ConfigManagerConfig` | Configuration des chargeurs de configuration |
352
+ | `children` | `React.ReactNode` | Composants enfants |
419
353
 
354
+ ### Hook useConfig
355
+ Retourne un objet avec:
356
+ - `cf(key: string, options?: ConfigOptions)`: Récupère une valeur de configuration
357
+ - `getConfig(moduleName?: string)`: Récupère toute la configuration d'un module
358
+ - `reloadConfig()`: Recharge toutes les configurations
359
+ - `isLoading`: État de chargement global
360
+ - `isModuleLoaded`: Indique si le module demandé est chargé
361
+ - `loadModuleConfig(moduleName: string)`: Charge un module spécifique
362
+
363
+ ### Options de Configuration
420
364
  ```typescript
421
- import { useQuery } from '@tanstack/react-query';
422
- import { useConfig } from '@arc-js/config-manager';
365
+ interface ConfigOptions {
366
+ moduleName?: string; // Nom du module (défaut: 'base')
367
+ defaultValue?: any; // Valeur par défaut si non trouvé
368
+ pathSeparator?: string; // Séparateur de chemin (défaut: '.')
369
+ }
370
+ ```
423
371
 
424
- const ConfigBasedQuery = () => {
425
- const config = useConfig('admin');
372
+ ## 🛡️ Gestion des Erreurs
373
+
374
+ ### Fallback sécurisé
375
+ ```typescript
376
+ const SafeComponent = () => {
377
+ const config = useConfig();
426
378
 
427
- const { data, isLoading, error } = useQuery({
428
- queryKey: ['users', 'list'],
429
- queryFn: async () => {
430
- const baseUrl = config.getOrThrow('api.baseUrl');
431
- const pageSize = config.get('pagination.pageSize', { defaultValue: 20 });
432
-
433
- const response = await fetch(
434
- `\${baseUrl}/users?limit=\${pageSize}`
435
- );
436
-
437
- if (!response.ok) throw new Error('Failed to fetch');
438
- return response.json();
439
- },
440
- // Utiliser la config pour les options de requête
441
- retry: config.get('api.retryAttempts', { defaultValue: 3 }),
442
- retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
443
- enabled: config.get('features.dataFetching', { defaultValue: true })
379
+ // Utilisation sécurisée avec valeur par défaut
380
+ const importantValue = config.cf('important.path', {
381
+ defaultValue: 'valeur-par-defaut',
382
+ moduleName: 'admin'
444
383
  });
445
384
 
446
- if (isLoading) return <div>Loading...</div>;
447
- if (error) return <div>Error: {error.message}</div>;
385
+ // Vérifier avant utilisation
386
+ const adminConfig = config.getConfig('admin');
387
+ const hasRequiredConfig = 'requiredKey' in adminConfig;
448
388
 
449
- return (
450
- <div>
451
- <h2>Users</h2>
452
- <ul>
453
- {data?.users?.map(user => (
454
- <li key={user.id}>{user.name}</li>
455
- ))}
456
- </ul>
457
- </div>
458
- );
459
- };
460
- ```
461
-
462
- ## 🔧 Configuration Avancée
463
-
464
- ### Configuration Vite
465
-
466
- ```typescript
467
- // vite.config.ts
468
- import { defineConfig } from 'vite';
469
- import react from '@vitejs/plugin-react';
470
-
471
- export default defineConfig({
472
- plugins: [react()],
473
- define: {
474
- // Exposer les variables d'environnement
475
- 'import.meta.env': process.env
389
+ if (!hasRequiredConfig) {
390
+ return <div>Configuration manquante</div>;
476
391
  }
477
- });
392
+
393
+ return <div>Valeur: {importantValue}</div>;
394
+ };
478
395
  ```
479
396
 
480
- ### Configuration TypeScript
397
+ ## 🔧 Configuration TypeScript
481
398
 
482
399
  ```json
483
400
  {
@@ -498,97 +415,6 @@ export default defineConfig({
498
415
  }
499
416
  ```
500
417
 
501
- ### Scripts de validation
502
-
503
- ```json
504
- {
505
- "scripts": {
506
- "validate-config": "node scripts/validate-config.js",
507
- "generate-config-schema": "node scripts/generate-schema.js",
508
- "check-env": "node scripts/check-environment.js"
509
- }
510
- }
511
- ```
512
-
513
- ## 📋 Table des Conventions
514
-
515
- ### Chemins de configuration
516
-
517
- | Type | Format | Exemple |
518
- |------|--------|---------|
519
- | Pointé | `parent.child.grandchild` | `app.features.analytics` |
520
- | Tableau | `['parent', 'child', 'grandchild']` | `['api', 'baseUrl']` |
521
- | Mixte | Supporté | `app.features[0].name` |
522
-
523
- ### Fichiers de configuration
524
-
525
- | Chemin | Portée | Priorité |
526
- |--------|--------|----------|
527
- | `src/config.json` | Globale | Base |
528
- | `src/modules/*/config.json` | Module | Écrase la globale |
529
- | `.env` / Variables | Environnement | Écrase tout |
530
-
531
- ### Variables d'environnement
532
-
533
- | Variable | Conversion | Accès |
534
- |----------|------------|-------|
535
- | `VITE_API_URL` | `apiUrl` | `config.getEnv('apiUrl')` |
536
- | `VITE_APP_NAME` | `appName` | `config.get('appName', {moduleName: 'ENV'})` |
537
- | `NODE_ENV` | `nodeEnv` | `config.getEnv('NODE_ENV')` |
538
-
539
- ## 🛡️ Gestion des Erreurs
540
-
541
- ### Fallback et validation
542
-
543
- ```typescript
544
- const SafeConfigAccess = () => {
545
- const config = useConfig();
546
-
547
- try {
548
- // Cette méthode lève une exception si le chemin n'existe pas
549
- const requiredValue = config.getOrThrow('required.path', 'admin');
550
-
551
- return (
552
- <div>
553
- <p>Value: {requiredValue}</p>
554
- </div>
555
- );
556
- } catch (error) {
557
- return (
558
- <div className="error">
559
- <p>Configuration error: {(error as Error).message}</p>
560
- <p>Using fallback configuration...</p>
561
- </div>
562
- );
563
- }
564
- };
565
- ```
566
-
567
- ### Logs en développement
568
-
569
- ```typescript
570
- // Le service loggue automatiquement en mode développement
571
- const DebugConfig = () => {
572
- const config = useConfig();
573
- const isDev = config.isDevelopment();
574
-
575
- if (isDev) {
576
- // Afficher toute la configuration en dev
577
- console.log('Current Configuration:', config.getAll());
578
-
579
- // Vérifier les chemins manquants
580
- const missingPaths = ['app.name', 'api.baseUrl', 'unknown.path'];
581
- missingPaths.forEach(path => {
582
- if (!config.has(path)) {
583
- console.warn(`Missing config path: \${path}`);
584
- }
585
- });
586
- }
587
-
588
- return null;
589
- };
590
- ```
591
-
592
418
  ## 📄 Licence
593
419
 
594
420
  MIT License - Voir le fichier [LICENSE](LICENSE) pour plus de détails.
package/config.d.ts CHANGED
@@ -1,13 +1,17 @@
1
- declare const DEFAULT_LOCALE = "en";
2
- declare const SUPPORTED_LOCALES: string[];
3
- declare const COOKIE_NAME = "app_locale";
4
- type Locale = typeof SUPPORTED_LOCALES[number];
5
- declare const getSavedLocale: (supportedLocales?: string[]) => string;
6
- declare const saveLocale: (locale: string) => void;
7
- declare const PATH_CONFIG: {
8
- SRC_DIR: string;
9
- };
10
- declare const SRC_DIR: string;
1
+ declare const DEFAULT_CONFIG = "base";
2
+ interface ConfigMap {
3
+ [key: string]: () => Promise<Record<string, any>>;
4
+ }
5
+ interface ModuleConfigs {
6
+ [moduleName: string]: () => Promise<Record<string, any>>;
7
+ }
8
+ interface ConfigData {
9
+ base: ConfigMap;
10
+ modules?: ModuleConfigs;
11
+ }
12
+ type ConfigManagerConfig = ConfigData;
13
+ declare let configManagerConfig: ConfigManagerConfig | null;
14
+ declare const setConfigManagerConfig: (config: ConfigManagerConfig) => void;
11
15
 
12
- export { COOKIE_NAME, DEFAULT_LOCALE, PATH_CONFIG, SRC_DIR, SUPPORTED_LOCALES, getSavedLocale, saveLocale };
13
- export type { Locale };
16
+ export { DEFAULT_CONFIG, configManagerConfig, setConfigManagerConfig };
17
+ export type { ConfigData, ConfigManagerConfig, ConfigMap, ModuleConfigs };