@abdokouta/react-config 1.0.0 → 1.0.2

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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2 -1
  3. package/config/config.config.ts +9 -9
  4. package/dist/{index.d.mts → index.d.cts} +34 -2
  5. package/dist/index.d.ts +34 -2
  6. package/dist/index.js +28 -11
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +28 -11
  9. package/dist/index.mjs.map +1 -1
  10. package/package.json +55 -25
  11. package/.examples/01-basic-usage.ts +0 -289
  12. package/.examples/02-env-helper.ts +0 -282
  13. package/.examples/README.md +0 -285
  14. package/.prettierrc.js +0 -1
  15. package/__tests__/config.module.test.ts +0 -244
  16. package/__tests__/drivers/env.driver.test.ts +0 -259
  17. package/__tests__/services/config.service.test.ts +0 -328
  18. package/__tests__/setup.d.ts +0 -11
  19. package/__tests__/vitest.setup.ts +0 -30
  20. package/eslint.config.js +0 -13
  21. package/src/config.module.ts +0 -154
  22. package/src/constants/index.ts +0 -5
  23. package/src/constants/tokens.constant.ts +0 -38
  24. package/src/drivers/env.driver.ts +0 -194
  25. package/src/drivers/file.driver.ts +0 -81
  26. package/src/drivers/index.ts +0 -6
  27. package/src/index.ts +0 -92
  28. package/src/interfaces/config-driver.interface.ts +0 -30
  29. package/src/interfaces/config-module-options.interface.ts +0 -84
  30. package/src/interfaces/config-service.interface.ts +0 -71
  31. package/src/interfaces/index.ts +0 -8
  32. package/src/interfaces/vite-config-plugin-options.interface.ts +0 -56
  33. package/src/plugins/index.ts +0 -5
  34. package/src/plugins/vite.plugin.ts +0 -115
  35. package/src/services/config.service.ts +0 -172
  36. package/src/services/index.ts +0 -5
  37. package/src/utils/get-nested-value.util.ts +0 -56
  38. package/src/utils/index.ts +0 -6
  39. package/src/utils/load-config-file.util.ts +0 -37
  40. package/src/utils/scan-config-files.util.ts +0 -40
  41. package/tsconfig.json +0 -28
  42. package/tsup.config.ts +0 -105
  43. package/vitest.config.ts +0 -66
@@ -1,194 +0,0 @@
1
- import type { ConfigDriver } from '../interfaces/config-driver.interface';
2
- import { getNestedValue, hasNestedValue } from '../utils/get-nested-value.util';
3
-
4
- /**
5
- * Environment Variable Configuration Driver
6
- *
7
- * Loads configuration from environment variables (process.env).
8
- * Supports .env files via dotenv.
9
- */
10
- export class EnvDriver implements ConfigDriver {
11
- private config: Record<string, any> = {};
12
- private loaded = false;
13
-
14
- constructor(
15
- private options: {
16
- envFilePath?: string | string[];
17
- ignoreEnvFile?: boolean;
18
- expandVariables?: boolean;
19
- envPrefix?: string | false;
20
- globalName?: string; // Custom global variable name
21
- } = {}
22
- ) {}
23
-
24
- /**
25
- * Load environment variables
26
- */
27
- load(): Record<string, any> {
28
- if (this.loaded) {
29
- console.log('[EnvDriver] Already loaded, returning cached config');
30
- return this.config;
31
- }
32
-
33
- console.log('[EnvDriver] Loading environment variables...');
34
- console.log('[EnvDriver] Options:', { ...this.options });
35
-
36
- // Load .env file if not ignored
37
- if (!this.options.ignoreEnvFile) {
38
- this.loadDotEnv();
39
- }
40
-
41
- // Get global config name (default: __APP_CONFIG__)
42
- const globalName = this.options.globalName || '__APP_CONFIG__';
43
-
44
- // Try to load from custom global variable first (browser environment)
45
- if (typeof window !== 'undefined' && (window as any)[globalName]) {
46
- this.config = { ...(window as any)[globalName] };
47
- console.log(`[EnvDriver] Loaded config from window.${globalName}:`, Object.keys(this.config).length, 'keys');
48
- }
49
- // Fallback to process.env (Node.js environment or backward compatibility)
50
- else if (typeof process !== 'undefined' && process.env) {
51
- this.config = { ...process.env };
52
- console.log('[EnvDriver] Loaded config from process.env:', Object.keys(this.config).length, 'keys');
53
- }
54
- // No config source available
55
- else {
56
- console.warn('[EnvDriver] No config source available (neither window.' + globalName + ' nor process.env)');
57
- this.config = {};
58
- }
59
-
60
- console.log('[EnvDriver] Initial config keys:', [...Object.keys(this.config).filter(k => k.includes('APP') || k.includes('VITE'))]);
61
-
62
- // Strip prefix if configured
63
- if (this.options.envPrefix !== false) {
64
- console.log('[EnvDriver] Stripping prefix...');
65
- this.stripPrefix();
66
- console.log('[EnvDriver] After stripPrefix, config keys:', [...Object.keys(this.config).filter(k => k.includes('APP') || k.includes('VITE'))]);
67
- }
68
-
69
- // Expand variables if enabled
70
- if (this.options.expandVariables) {
71
- this.expandEnvVariables();
72
- }
73
-
74
- this.loaded = true;
75
- console.log('[EnvDriver] Load complete. Sample values:', {
76
- APP_NAME: this.config.APP_NAME,
77
- VITE_APP_NAME: this.config.VITE_APP_NAME,
78
- });
79
- return this.config;
80
- }
81
-
82
- /**
83
- * Get configuration value
84
- */
85
- get<T = any>(key: string, defaultValue?: T): T | undefined {
86
- if (!this.loaded) {
87
- this.load();
88
- }
89
- const value = getNestedValue(this.config, key, defaultValue);
90
- console.log(`[EnvDriver] get("${key}", "${defaultValue}") = "${value}"`);
91
- return value;
92
- }
93
-
94
- /**
95
- * Check if key exists
96
- */
97
- has(key: string): boolean {
98
- if (!this.loaded) {
99
- this.load();
100
- }
101
- return hasNestedValue(this.config, key);
102
- }
103
-
104
- /**
105
- * Get all configuration
106
- */
107
- all(): Record<string, any> {
108
- if (!this.loaded) {
109
- this.load();
110
- }
111
- return { ...this.config };
112
- }
113
-
114
- /**
115
- * Load .env file using dotenv
116
- */
117
- private loadDotEnv(): void {
118
- try {
119
- // Try to load dotenv
120
- const dotenv = require('dotenv');
121
- const paths = Array.isArray(this.options.envFilePath)
122
- ? this.options.envFilePath
123
- : [this.options.envFilePath || '.env'];
124
-
125
- for (const path of paths) {
126
- dotenv.config({ path });
127
- }
128
- } catch (error) {
129
- // dotenv not installed, skip
130
- }
131
- }
132
-
133
- /**
134
- * Expand environment variables (e.g., ${VAR_NAME})
135
- */
136
- private expandEnvVariables(): void {
137
- const regex = /\$\{([^}]+)\}/g;
138
-
139
- const expand = (value: string): string => {
140
- return value.replace(regex, (_, key) => {
141
- return this.config[key] || '';
142
- });
143
- };
144
-
145
- for (const [key, value] of Object.entries(this.config)) {
146
- if (typeof value === 'string' && value.includes('${')) {
147
- this.config[key] = expand(value);
148
- }
149
- }
150
- }
151
-
152
- /**
153
- * Strip environment variable prefix
154
- * Auto-detects framework (Vite, Next.js) or uses custom prefix
155
- */
156
- private stripPrefix(): void {
157
- let prefix = this.options.envPrefix;
158
-
159
- // Auto-detect framework prefix
160
- if (prefix === 'auto' || prefix === undefined) {
161
- // Check for Vite (import.meta.env exists or VITE_ variables present)
162
- const hasViteVars = Object.keys(this.config).some(key => key.startsWith('VITE_'));
163
- if (hasViteVars || typeof import.meta !== 'undefined') {
164
- prefix = 'VITE_';
165
- }
166
- // Check for Next.js (NEXT_PUBLIC_ variables present)
167
- else if (Object.keys(this.config).some(key => key.startsWith('NEXT_PUBLIC_'))) {
168
- prefix = 'NEXT_PUBLIC_';
169
- }
170
- // No framework detected, don't strip
171
- else {
172
- return;
173
- }
174
- }
175
-
176
- // Strip the prefix from all matching keys
177
- if (typeof prefix === 'string' && prefix.length > 0) {
178
- const newConfig: Record<string, any> = {};
179
-
180
- for (const [key, value] of Object.entries(this.config)) {
181
- if (key.startsWith(prefix)) {
182
- // Add both prefixed and unprefixed versions
183
- const unprefixedKey = key.substring(prefix.length);
184
- newConfig[unprefixedKey] = value;
185
- newConfig[key] = value; // Keep original too
186
- } else {
187
- newConfig[key] = value;
188
- }
189
- }
190
-
191
- this.config = newConfig;
192
- }
193
- }
194
- }
@@ -1,81 +0,0 @@
1
- import type { ConfigDriver } from '@/interfaces/config-driver.interface';
2
- import { getNestedValue, hasNestedValue } from '@/utils/get-nested-value.util';
3
-
4
- /**
5
- * File-based Configuration Driver
6
- *
7
- * Loads configuration from TypeScript/JavaScript files.
8
- * This is a server-side only driver. For client-side, use the Vite plugin.
9
- *
10
- * @see packages/pixielity/config/src/plugins/vite-config.plugin.ts
11
- */
12
- export class FileDriver implements ConfigDriver {
13
- private config: Record<string, any> = {};
14
- private loaded = false;
15
-
16
- constructor(
17
- options: {
18
- config?: Record<string, any>;
19
- } = {}
20
- ) {
21
- // Pre-loaded config from Vite plugin or server
22
- if (options.config) {
23
- this.config = options.config;
24
- this.loaded = true;
25
- }
26
- }
27
-
28
- /**
29
- * Load configuration
30
- * Config should be pre-loaded via Vite plugin or passed in constructor
31
- */
32
- async load(): Promise<Record<string, any>> {
33
- if (this.loaded) {
34
- return this.config;
35
- }
36
-
37
- // If running on server (Node.js), throw error
38
- const isServer = typeof globalThis !== 'undefined' &&
39
- typeof (globalThis as typeof globalThis & { window?: any }).window === 'undefined';
40
-
41
- if (isServer) {
42
- throw new Error(
43
- 'FileDriver requires pre-loaded configuration. ' +
44
- 'Use Vite plugin for client-side or pass config in constructor for server-side.'
45
- );
46
- }
47
-
48
- this.loaded = true;
49
- return this.config;
50
- }
51
-
52
- /**
53
- * Get configuration value
54
- */
55
- get<T = any>(key: string, defaultValue?: T): T | undefined {
56
- if (!this.loaded) {
57
- throw new Error('Configuration not loaded. Call load() first or use async initialization.');
58
- }
59
- return getNestedValue(this.config, key, defaultValue);
60
- }
61
-
62
- /**
63
- * Check if key exists
64
- */
65
- has(key: string): boolean {
66
- if (!this.loaded) {
67
- throw new Error('Configuration not loaded. Call load() first or use async initialization.');
68
- }
69
- return hasNestedValue(this.config, key);
70
- }
71
-
72
- /**
73
- * Get all configuration
74
- */
75
- all(): Record<string, any> {
76
- if (!this.loaded) {
77
- throw new Error('Configuration not loaded. Call load() first or use async initialization.');
78
- }
79
- return { ...this.config };
80
- }
81
- }
@@ -1,6 +0,0 @@
1
- /**
2
- * Drivers
3
- */
4
-
5
- export { EnvDriver } from './env.driver';
6
- export { FileDriver } from './file.driver';
package/src/index.ts DELETED
@@ -1,92 +0,0 @@
1
- /**
2
- * @abdokouta/config
3
- *
4
- * NestJS-inspired configuration management with multiple drivers for loading
5
- * configuration from various sources (environment variables, files, etc.).
6
- * Provides type-safe access to configuration values with support for nested
7
- * properties and default values.
8
- *
9
- * @example
10
- * Basic usage with environment variables:
11
- * ```typescript
12
- * import { ConfigModule, ConfigService, EnvDriver } from '@abdokouta/config';
13
- * import { Module, Injectable, Inject } from '@abdokouta/react-di';
14
- *
15
- * @Module({
16
- * imports: [
17
- * ConfigModule.forRoot({
18
- * driver: new EnvDriver(),
19
- * }),
20
- * ],
21
- * })
22
- * export class AppModule {}
23
- *
24
- * @Injectable()
25
- * class DatabaseService {
26
- * constructor(@Inject(ConfigService) private config: ConfigService) {}
27
- *
28
- * connect() {
29
- * const host = this.config.get('DATABASE_HOST', 'localhost');
30
- * const port = this.config.get('DATABASE_PORT', 5432);
31
- * // Connect to database...
32
- * }
33
- * }
34
- * ```
35
- *
36
- * @example
37
- * Using file-based configuration:
38
- * ```typescript
39
- * import { ConfigModule, FileDriver } from '@abdokouta/config';
40
- *
41
- * @Module({
42
- * imports: [
43
- * ConfigModule.forRoot({
44
- * driver: new FileDriver({
45
- * path: './config/app.json',
46
- * }),
47
- * }),
48
- * ],
49
- * })
50
- * export class AppModule {}
51
- * ```
52
- *
53
- * @example
54
- * Accessing nested configuration:
55
- * ```typescript
56
- * // config.json: { "database": { "host": "localhost", "port": 5432 } }
57
- * const host = config.get('database.host');
58
- * const port = config.get('database.port', 3306);
59
- * ```
60
- *
61
- * @module @abdokouta/config
62
- */
63
-
64
- // ============================================================================
65
- // Module (DI Configuration)
66
- // ============================================================================
67
- export { ConfigModule } from './config.module';
68
-
69
- // ============================================================================
70
- // Core Service
71
- // ============================================================================
72
- export { ConfigService } from './services/config.service';
73
-
74
- // ============================================================================
75
- // Drivers
76
- // ============================================================================
77
- export { EnvDriver } from './drivers/env.driver';
78
- export { FileDriver } from './drivers/file.driver';
79
-
80
- // ============================================================================
81
- // Interfaces
82
- // ============================================================================
83
- export type { ConfigDriver } from './interfaces/config-driver.interface';
84
- export type { ConfigModuleOptions } from './interfaces/config-module-options.interface';
85
- export type { ConfigServiceInterface } from './interfaces/config-service.interface';
86
- export type { ViteConfigPluginOptions } from './interfaces/vite-config-plugin-options.interface';
87
-
88
- // ============================================================================
89
- // Utilities
90
- // ============================================================================
91
- export { getNestedValue, hasNestedValue } from './utils/get-nested-value.util';
92
- export { loadConfigFile } from './utils/load-config-file.util';
@@ -1,30 +0,0 @@
1
- /**
2
- * Configuration Driver Interface
3
- *
4
- * Defines the contract for configuration drivers (env, file, firebase, etc.)
5
- */
6
- export interface ConfigDriver {
7
- /**
8
- * Load configuration from the driver source
9
- * @returns Configuration object
10
- */
11
- load(): Promise<Record<string, any>> | Record<string, any>;
12
-
13
- /**
14
- * Get a configuration value by key
15
- * @param key - Configuration key (supports dot notation)
16
- * @param defaultValue - Default value if key not found
17
- */
18
- get<T = any>(key: string, defaultValue?: T): T | undefined;
19
-
20
- /**
21
- * Check if a configuration key exists
22
- * @param key - Configuration key
23
- */
24
- has(key: string): boolean;
25
-
26
- /**
27
- * Get all configuration values
28
- */
29
- all(): Record<string, any>;
30
- }
@@ -1,84 +0,0 @@
1
- /**
2
- * Configuration Module Options
3
- */
4
- export interface ConfigModuleOptions {
5
- /**
6
- * Configuration driver to use
7
- * @default 'env'
8
- */
9
- driver?: 'env' | 'file' | 'firebase' | string;
10
-
11
- /**
12
- * Path to .env file (for env driver)
13
- * @default '.env'
14
- */
15
- envFilePath?: string | string[];
16
-
17
- /**
18
- * Path pattern to scan for config files (for file driver)
19
- * @example 'config/**\/*.config.ts'
20
- */
21
- filePattern?: string | string[];
22
-
23
- /**
24
- * Whether to ignore .env file
25
- * @default false
26
- */
27
- ignoreEnvFile?: boolean;
28
-
29
- /**
30
- * Whether to expand environment variables
31
- * @default false
32
- */
33
- expandVariables?: boolean;
34
-
35
- /**
36
- * Custom configuration object to merge
37
- */
38
- load?: Record<string, any> | (() => Record<string, any> | Promise<Record<string, any>>);
39
-
40
- /**
41
- * Whether configuration is global
42
- * @default false
43
- */
44
- isGlobal?: boolean;
45
-
46
- /**
47
- * Cache configuration values
48
- * @default true
49
- */
50
- cache?: boolean;
51
-
52
- /**
53
- * Validate configuration on load
54
- */
55
- validate?: (config: Record<string, any>) => void | Promise<void>;
56
-
57
- /**
58
- * Firebase configuration (for firebase driver)
59
- */
60
- firebase?: {
61
- projectId: string;
62
- configPath?: string;
63
- };
64
-
65
- /**
66
- * Environment variable prefix to strip
67
- * Set to 'auto' to auto-detect (VITE_ for Vite, NEXT_PUBLIC_ for Next.js)
68
- * Set to 'VITE_' or 'NEXT_PUBLIC_' for specific framework
69
- * Set to a custom string to strip that prefix
70
- * Set to false to disable prefix stripping
71
- * @default 'auto'
72
- * @example 'VITE_' - strips VITE_ prefix, so VITE_APP_NAME becomes APP_NAME
73
- * @example 'NEXT_PUBLIC_' - strips NEXT_PUBLIC_ prefix, so NEXT_PUBLIC_API_URL becomes API_URL
74
- * @example 'auto' - auto-detects framework and strips appropriate prefix
75
- */
76
- envPrefix?: 'auto' | 'VITE_' | 'NEXT_PUBLIC_' | string | false;
77
-
78
- /**
79
- * Global variable name to read config from in browser
80
- * @default '__APP_CONFIG__'
81
- * @example '__APP_CONFIG__' - reads from window.__APP_CONFIG__
82
- */
83
- globalName?: string;
84
- }
@@ -1,71 +0,0 @@
1
- /**
2
- * Configuration Service Interface
3
- *
4
- * Defines the contract for configuration service implementations.
5
- */
6
- export interface ConfigServiceInterface {
7
- /**
8
- * Get configuration value
9
- */
10
- get<T = any>(key: string, defaultValue?: T): T | undefined;
11
-
12
- /**
13
- * Get configuration value or throw if not found
14
- */
15
- getOrThrow<T = any>(key: string): T;
16
-
17
- /**
18
- * Get string value
19
- */
20
- getString(key: string, defaultValue?: string): string | undefined;
21
-
22
- /**
23
- * Get string value or throw
24
- */
25
- getStringOrThrow(key: string): string;
26
-
27
- /**
28
- * Get number value
29
- */
30
- getNumber(key: string, defaultValue?: number): number | undefined;
31
-
32
- /**
33
- * Get number value or throw
34
- */
35
- getNumberOrThrow(key: string): number;
36
-
37
- /**
38
- * Get boolean value
39
- */
40
- getBool(key: string, defaultValue?: boolean): boolean | undefined;
41
-
42
- /**
43
- * Get boolean value or throw
44
- */
45
- getBoolOrThrow(key: string): boolean;
46
-
47
- /**
48
- * Get array value
49
- */
50
- getArray(key: string, defaultValue?: string[]): string[] | undefined;
51
-
52
- /**
53
- * Get JSON value
54
- */
55
- getJson<T = any>(key: string, defaultValue?: T): T | undefined;
56
-
57
- /**
58
- * Check if configuration key exists
59
- */
60
- has(key: string): boolean;
61
-
62
- /**
63
- * Get all configuration values
64
- */
65
- all(): Record<string, any>;
66
-
67
- /**
68
- * Clear cache
69
- */
70
- clearCache(): void;
71
- }
@@ -1,8 +0,0 @@
1
- /**
2
- * Interfaces
3
- */
4
-
5
- export type { ConfigDriver } from './config-driver.interface';
6
- export type { ConfigModuleOptions } from './config-module-options.interface';
7
- export type { ConfigServiceInterface } from './config-service.interface';
8
- export type { ViteConfigPluginOptions } from './vite-config-plugin-options.interface';
@@ -1,56 +0,0 @@
1
- /**
2
- * Vite Config Plugin Options Interface
3
- *
4
- * Configuration options for the Vite config plugin that injects
5
- * environment variables into the browser.
6
- */
7
-
8
- export interface ViteConfigPluginOptions {
9
- /**
10
- * Environment variables loaded by Vite's loadEnv
11
- * Pass the result of loadEnv(mode, envDir, '') here
12
- */
13
- env?: Record<string, string>;
14
-
15
- /**
16
- * Scan and collect .config.ts files
17
- * @default false (disabled by default to avoid Node.js module issues)
18
- */
19
- scanConfigFiles?: boolean;
20
-
21
- /**
22
- * Pattern to match config files
23
- * @default 'src/**\/*.config.ts'
24
- */
25
- configFilePattern?: string | string[];
26
-
27
- /**
28
- * Directories to exclude from config file scanning
29
- * @default ['node_modules', 'dist', 'build', '.git']
30
- */
31
- excludeDirs?: string[];
32
-
33
- /**
34
- * Root directory for scanning config files
35
- * @default process.cwd()
36
- */
37
- root?: string;
38
-
39
- /**
40
- * Include all environment variables
41
- * @default true
42
- */
43
- includeAll?: boolean;
44
-
45
- /**
46
- * Specific environment variables to include
47
- * Only used if includeAll is false
48
- */
49
- include?: string[];
50
-
51
- /**
52
- * Global variable name to inject config into
53
- * @default '__APP_CONFIG__'
54
- */
55
- globalName?: string;
56
- }
@@ -1,5 +0,0 @@
1
- /**
2
- * Plugins
3
- */
4
-
5
- export { viteConfigPlugin } from './vite.plugin';