@abdokouta/react-config 1.0.1 → 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 (39) hide show
  1. package/dist/{index.d.mts → index.d.cts} +1 -1
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +46 -17
  6. package/.examples/01-basic-usage.ts +0 -291
  7. package/.examples/02-env-helper.ts +0 -282
  8. package/.examples/README.md +0 -297
  9. package/.prettierrc.js +0 -1
  10. package/__tests__/config.module.test.ts +0 -244
  11. package/__tests__/drivers/env.driver.test.ts +0 -259
  12. package/__tests__/services/config.service.test.ts +0 -335
  13. package/__tests__/setup.d.ts +0 -11
  14. package/__tests__/vitest.setup.ts +0 -30
  15. package/eslint.config.js +0 -13
  16. package/src/config.module.ts +0 -152
  17. package/src/constants/index.ts +0 -5
  18. package/src/constants/tokens.constant.ts +0 -38
  19. package/src/drivers/env.driver.ts +0 -208
  20. package/src/drivers/file.driver.ts +0 -82
  21. package/src/drivers/index.ts +0 -6
  22. package/src/index.ts +0 -93
  23. package/src/interfaces/config-driver.interface.ts +0 -30
  24. package/src/interfaces/config-module-options.interface.ts +0 -84
  25. package/src/interfaces/config-service.interface.ts +0 -71
  26. package/src/interfaces/index.ts +0 -8
  27. package/src/interfaces/vite-config-plugin-options.interface.ts +0 -56
  28. package/src/plugins/index.ts +0 -5
  29. package/src/plugins/vite.plugin.ts +0 -118
  30. package/src/services/config.service.ts +0 -172
  31. package/src/services/index.ts +0 -5
  32. package/src/utils/define-config.util.ts +0 -35
  33. package/src/utils/get-nested-value.util.ts +0 -53
  34. package/src/utils/index.ts +0 -9
  35. package/src/utils/load-config-file.util.ts +0 -32
  36. package/src/utils/scan-config-files.util.ts +0 -36
  37. package/tsconfig.json +0 -28
  38. package/tsup.config.ts +0 -105
  39. package/vitest.config.ts +0 -66
@@ -1,152 +0,0 @@
1
- import { Module, DynamicModule } from '@abdokouta/react-di';
2
-
3
- import { EnvDriver } from './drivers/env.driver';
4
- import { FileDriver } from './drivers/file.driver';
5
- import { ConfigService } from './services/config.service';
6
- import type { ConfigDriver } from './interfaces/config-driver.interface';
7
- import type { ConfigModuleOptions } from './interfaces/config-module-options.interface';
8
- import { CONFIG_DRIVER, CONFIG_OPTIONS, CONFIG_SERVICE } from './constants/tokens.constant';
9
-
10
- /**
11
- * Configuration Module
12
- *
13
- * Provides configuration management with multiple drivers.
14
- * Similar to NestJS ConfigModule.
15
- *
16
- * @example
17
- * ```typescript
18
- * // Using environment variables (default)
19
- * @Module({
20
- * imports: [
21
- * ConfigModule.forRoot({
22
- * envFilePath: '.env',
23
- * isGlobal: true,
24
- * }),
25
- * ],
26
- * })
27
- * export class AppModule {}
28
- * ```
29
- *
30
- * @example
31
- * ```typescript
32
- * // Using file-based configuration
33
- * @Module({
34
- * imports: [
35
- * ConfigModule.forRoot({
36
- * driver: 'file',
37
- * filePattern: 'config/**\/*.config.ts',
38
- * isGlobal: true,
39
- * }),
40
- * ],
41
- * })
42
- * export class AppModule {}
43
- * ```
44
- */
45
- @Module({})
46
- export class ConfigModule {
47
- /**
48
- * Register configuration module with options
49
- *
50
- * @param options - Configuration options
51
- * @returns Dynamic module
52
- */
53
- static forRoot(options: ConfigModuleOptions = {}): DynamicModule {
54
- const driver = this.createDriver(options);
55
-
56
- const isGlobal = options.isGlobal ?? false;
57
-
58
- const providers = [
59
- {
60
- provide: CONFIG_OPTIONS,
61
- useValue: options,
62
- isGlobal,
63
- },
64
- {
65
- provide: CONFIG_DRIVER,
66
- useValue: driver,
67
- isGlobal,
68
- },
69
- ConfigService,
70
- {
71
- provide: CONFIG_SERVICE,
72
- useExisting: ConfigService,
73
- isGlobal,
74
- },
75
- ];
76
-
77
- return {
78
- module: ConfigModule,
79
- providers: providers as any,
80
- exports: [ConfigService],
81
- };
82
- }
83
-
84
- /**
85
- * Register configuration module asynchronously
86
- *
87
- * @param options - Async configuration options
88
- * @returns Dynamic module
89
- */
90
- static async forRootAsync(
91
- options: ConfigModuleOptions & {
92
- useFactory?: () => Promise<ConfigModuleOptions> | ConfigModuleOptions;
93
- }
94
- ): Promise<DynamicModule> {
95
- const resolvedOptions = options.useFactory ? await options.useFactory() : options;
96
-
97
- return this.forRoot(resolvedOptions);
98
- }
99
-
100
- /**
101
- * Create configuration driver based on options
102
- */
103
- private static createDriver(options: ConfigModuleOptions): ConfigDriver {
104
- const driverType = options.driver || 'env';
105
-
106
- switch (driverType) {
107
- case 'env':
108
- const envDriver = new EnvDriver({
109
- envFilePath: options.envFilePath,
110
- ignoreEnvFile: options.ignoreEnvFile,
111
- expandVariables: options.expandVariables,
112
- envPrefix: options.envPrefix,
113
- globalName: options.globalName,
114
- });
115
- envDriver.load();
116
-
117
- // Merge custom load function if provided
118
- if (options.load) {
119
- this.mergeCustomConfig(envDriver, options.load);
120
- }
121
-
122
- return envDriver;
123
-
124
- case 'file':
125
- const fileDriver = new FileDriver({
126
- config: typeof options.load === 'object' ? options.load : undefined,
127
- });
128
- return fileDriver;
129
-
130
- default:
131
- throw new Error(`Unknown configuration driver: ${driverType}`);
132
- }
133
- }
134
-
135
- /**
136
- * Merge custom configuration into driver
137
- */
138
- private static mergeCustomConfig(
139
- driver: ConfigDriver,
140
- load: Record<string, any> | (() => Record<string, any> | Promise<Record<string, any>>)
141
- ): void {
142
- const customConfig = typeof load === 'function' ? load() : load;
143
-
144
- if (customConfig instanceof Promise) {
145
- customConfig.then((config) => {
146
- Object.assign(driver.all(), config);
147
- });
148
- } else {
149
- Object.assign(driver.all(), customConfig);
150
- }
151
- }
152
- }
@@ -1,5 +0,0 @@
1
- /**
2
- * Constants
3
- */
4
-
5
- export { CONFIG_TOKENS } from './tokens.constant';
@@ -1,38 +0,0 @@
1
- /**
2
- * Dependency Injection Tokens
3
- *
4
- * Used for injecting configuration dependencies.
5
- */
6
-
7
- /**
8
- * Configuration driver token
9
- *
10
- * @example
11
- * ```typescript
12
- * @Inject(CONFIG_DRIVER)
13
- * private driver: ConfigDriver
14
- * ```
15
- */
16
- export const CONFIG_DRIVER = Symbol('CONFIG_DRIVER');
17
-
18
- /**
19
- * Configuration options token
20
- *
21
- * @example
22
- * ```typescript
23
- * @Inject(CONFIG_OPTIONS)
24
- * private options: ConfigModuleOptions
25
- * ```
26
- */
27
- export const CONFIG_OPTIONS = Symbol('CONFIG_OPTIONS');
28
-
29
- /**
30
- * Configuration service token
31
- *
32
- * @example
33
- * ```typescript
34
- * @Inject(CONFIG_SERVICE)
35
- * private config: ConfigService
36
- * ```
37
- */
38
- export const CONFIG_SERVICE = Symbol('CONFIG_SERVICE');
@@ -1,208 +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(
48
- `[EnvDriver] Loaded config from window.${globalName}:`,
49
- Object.keys(this.config).length,
50
- 'keys'
51
- );
52
- }
53
- // Fallback to process.env (Node.js environment or backward compatibility)
54
- else if (typeof process !== 'undefined' && process.env) {
55
- this.config = { ...process.env };
56
- console.log(
57
- '[EnvDriver] Loaded config from process.env:',
58
- Object.keys(this.config).length,
59
- 'keys'
60
- );
61
- }
62
- // No config source available
63
- else {
64
- console.warn(
65
- '[EnvDriver] No config source available (neither window.' + globalName + ' nor process.env)'
66
- );
67
- this.config = {};
68
- }
69
-
70
- console.log('[EnvDriver] Initial config keys:', [
71
- ...Object.keys(this.config).filter((k) => k.includes('APP') || k.includes('VITE')),
72
- ]);
73
-
74
- // Strip prefix if configured
75
- if (this.options.envPrefix !== false) {
76
- console.log('[EnvDriver] Stripping prefix...');
77
- this.stripPrefix();
78
- console.log('[EnvDriver] After stripPrefix, config keys:', [
79
- ...Object.keys(this.config).filter((k) => k.includes('APP') || k.includes('VITE')),
80
- ]);
81
- }
82
-
83
- // Expand variables if enabled
84
- if (this.options.expandVariables) {
85
- this.expandEnvVariables();
86
- }
87
-
88
- this.loaded = true;
89
- console.log('[EnvDriver] Load complete. Sample values:', {
90
- APP_NAME: this.config.APP_NAME,
91
- VITE_APP_NAME: this.config.VITE_APP_NAME,
92
- });
93
- return this.config;
94
- }
95
-
96
- /**
97
- * Get configuration value
98
- */
99
- get<T = any>(key: string, defaultValue?: T): T | undefined {
100
- if (!this.loaded) {
101
- this.load();
102
- }
103
- const value = getNestedValue(this.config, key, defaultValue);
104
- console.log(`[EnvDriver] get("${key}", "${defaultValue}") = "${value}"`);
105
- return value;
106
- }
107
-
108
- /**
109
- * Check if key exists
110
- */
111
- has(key: string): boolean {
112
- if (!this.loaded) {
113
- this.load();
114
- }
115
- return hasNestedValue(this.config, key);
116
- }
117
-
118
- /**
119
- * Get all configuration
120
- */
121
- all(): Record<string, any> {
122
- if (!this.loaded) {
123
- this.load();
124
- }
125
- return { ...this.config };
126
- }
127
-
128
- /**
129
- * Load .env file using dotenv
130
- */
131
- private loadDotEnv(): void {
132
- try {
133
- // Try to load dotenv
134
- const dotenv = require('dotenv');
135
- const paths = Array.isArray(this.options.envFilePath)
136
- ? this.options.envFilePath
137
- : [this.options.envFilePath || '.env'];
138
-
139
- for (const path of paths) {
140
- dotenv.config({ path });
141
- }
142
- } catch (error) {
143
- // dotenv not installed, skip
144
- }
145
- }
146
-
147
- /**
148
- * Expand environment variables (e.g., ${VAR_NAME})
149
- */
150
- private expandEnvVariables(): void {
151
- const regex = /\$\{([^}]+)\}/g;
152
-
153
- const expand = (value: string): string => {
154
- return value.replace(regex, (_, key) => {
155
- return this.config[key] || '';
156
- });
157
- };
158
-
159
- for (const [key, value] of Object.entries(this.config)) {
160
- if (typeof value === 'string' && value.includes('${')) {
161
- this.config[key] = expand(value);
162
- }
163
- }
164
- }
165
-
166
- /**
167
- * Strip environment variable prefix
168
- * Auto-detects framework (Vite, Next.js) or uses custom prefix
169
- */
170
- private stripPrefix(): void {
171
- let prefix = this.options.envPrefix;
172
-
173
- // Auto-detect framework prefix
174
- if (prefix === 'auto' || prefix === undefined) {
175
- // Check for Vite (import.meta.env exists or VITE_ variables present)
176
- const hasViteVars = Object.keys(this.config).some((key) => key.startsWith('VITE_'));
177
- if (hasViteVars || typeof import.meta !== 'undefined') {
178
- prefix = 'VITE_';
179
- }
180
- // Check for Next.js (NEXT_PUBLIC_ variables present)
181
- else if (Object.keys(this.config).some((key) => key.startsWith('NEXT_PUBLIC_'))) {
182
- prefix = 'NEXT_PUBLIC_';
183
- }
184
- // No framework detected, don't strip
185
- else {
186
- return;
187
- }
188
- }
189
-
190
- // Strip the prefix from all matching keys
191
- if (typeof prefix === 'string' && prefix.length > 0) {
192
- const newConfig: Record<string, any> = {};
193
-
194
- for (const [key, value] of Object.entries(this.config)) {
195
- if (key.startsWith(prefix)) {
196
- // Add both prefixed and unprefixed versions
197
- const unprefixedKey = key.substring(prefix.length);
198
- newConfig[unprefixedKey] = value;
199
- newConfig[key] = value; // Keep original too
200
- } else {
201
- newConfig[key] = value;
202
- }
203
- }
204
-
205
- this.config = newConfig;
206
- }
207
- }
208
- }
@@ -1,82 +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 =
39
- typeof globalThis !== 'undefined' &&
40
- typeof (globalThis as typeof globalThis & { window?: any }).window === 'undefined';
41
-
42
- if (isServer) {
43
- throw new Error(
44
- 'FileDriver requires pre-loaded configuration. ' +
45
- 'Use Vite plugin for client-side or pass config in constructor for server-side.'
46
- );
47
- }
48
-
49
- this.loaded = true;
50
- return this.config;
51
- }
52
-
53
- /**
54
- * Get configuration value
55
- */
56
- get<T = any>(key: string, defaultValue?: T): T | undefined {
57
- if (!this.loaded) {
58
- throw new Error('Configuration not loaded. Call load() first or use async initialization.');
59
- }
60
- return getNestedValue(this.config, key, defaultValue);
61
- }
62
-
63
- /**
64
- * Check if key exists
65
- */
66
- has(key: string): boolean {
67
- if (!this.loaded) {
68
- throw new Error('Configuration not loaded. Call load() first or use async initialization.');
69
- }
70
- return hasNestedValue(this.config, key);
71
- }
72
-
73
- /**
74
- * Get all configuration
75
- */
76
- all(): Record<string, any> {
77
- if (!this.loaded) {
78
- throw new Error('Configuration not loaded. Call load() first or use async initialization.');
79
- }
80
- return { ...this.config };
81
- }
82
- }
@@ -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,93 +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 { defineConfig } from './utils/define-config.util';
92
- export { getNestedValue, hasNestedValue } from './utils/get-nested-value.util';
93
- 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
- }