@abdokouta/react-config 1.0.0 → 1.0.1

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.
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * Dependency Injection Tokens
3
- *
3
+ *
4
4
  * Used for injecting configuration dependencies.
5
5
  */
6
6
 
7
7
  /**
8
8
  * Configuration driver token
9
- *
9
+ *
10
10
  * @example
11
11
  * ```typescript
12
12
  * @Inject(CONFIG_DRIVER)
@@ -17,7 +17,7 @@ export const CONFIG_DRIVER = Symbol('CONFIG_DRIVER');
17
17
 
18
18
  /**
19
19
  * Configuration options token
20
- *
20
+ *
21
21
  * @example
22
22
  * ```typescript
23
23
  * @Inject(CONFIG_OPTIONS)
@@ -28,7 +28,7 @@ export const CONFIG_OPTIONS = Symbol('CONFIG_OPTIONS');
28
28
 
29
29
  /**
30
30
  * Configuration service token
31
- *
31
+ *
32
32
  * @example
33
33
  * ```typescript
34
34
  * @Inject(CONFIG_SERVICE)
@@ -3,7 +3,7 @@ import { getNestedValue, hasNestedValue } from '../utils/get-nested-value.util';
3
3
 
4
4
  /**
5
5
  * Environment Variable Configuration Driver
6
- *
6
+ *
7
7
  * Loads configuration from environment variables (process.env).
8
8
  * Supports .env files via dotenv.
9
9
  */
@@ -44,26 +44,40 @@ export class EnvDriver implements ConfigDriver {
44
44
  // Try to load from custom global variable first (browser environment)
45
45
  if (typeof window !== 'undefined' && (window as any)[globalName]) {
46
46
  this.config = { ...(window as any)[globalName] };
47
- console.log(`[EnvDriver] Loaded config from window.${globalName}:`, Object.keys(this.config).length, 'keys');
47
+ console.log(
48
+ `[EnvDriver] Loaded config from window.${globalName}:`,
49
+ Object.keys(this.config).length,
50
+ 'keys'
51
+ );
48
52
  }
49
53
  // Fallback to process.env (Node.js environment or backward compatibility)
50
54
  else if (typeof process !== 'undefined' && process.env) {
51
55
  this.config = { ...process.env };
52
- console.log('[EnvDriver] Loaded config from process.env:', Object.keys(this.config).length, 'keys');
56
+ console.log(
57
+ '[EnvDriver] Loaded config from process.env:',
58
+ Object.keys(this.config).length,
59
+ 'keys'
60
+ );
53
61
  }
54
62
  // No config source available
55
63
  else {
56
- console.warn('[EnvDriver] No config source available (neither window.' + globalName + ' nor process.env)');
64
+ console.warn(
65
+ '[EnvDriver] No config source available (neither window.' + globalName + ' nor process.env)'
66
+ );
57
67
  this.config = {};
58
68
  }
59
69
 
60
- console.log('[EnvDriver] Initial config keys:', [...Object.keys(this.config).filter(k => k.includes('APP') || k.includes('VITE'))]);
70
+ console.log('[EnvDriver] Initial config keys:', [
71
+ ...Object.keys(this.config).filter((k) => k.includes('APP') || k.includes('VITE')),
72
+ ]);
61
73
 
62
74
  // Strip prefix if configured
63
75
  if (this.options.envPrefix !== false) {
64
76
  console.log('[EnvDriver] Stripping prefix...');
65
77
  this.stripPrefix();
66
- console.log('[EnvDriver] After stripPrefix, config keys:', [...Object.keys(this.config).filter(k => k.includes('APP') || k.includes('VITE'))]);
78
+ console.log('[EnvDriver] After stripPrefix, config keys:', [
79
+ ...Object.keys(this.config).filter((k) => k.includes('APP') || k.includes('VITE')),
80
+ ]);
67
81
  }
68
82
 
69
83
  // Expand variables if enabled
@@ -159,12 +173,12 @@ export class EnvDriver implements ConfigDriver {
159
173
  // Auto-detect framework prefix
160
174
  if (prefix === 'auto' || prefix === undefined) {
161
175
  // Check for Vite (import.meta.env exists or VITE_ variables present)
162
- const hasViteVars = Object.keys(this.config).some(key => key.startsWith('VITE_'));
176
+ const hasViteVars = Object.keys(this.config).some((key) => key.startsWith('VITE_'));
163
177
  if (hasViteVars || typeof import.meta !== 'undefined') {
164
178
  prefix = 'VITE_';
165
179
  }
166
180
  // Check for Next.js (NEXT_PUBLIC_ variables present)
167
- else if (Object.keys(this.config).some(key => key.startsWith('NEXT_PUBLIC_'))) {
181
+ else if (Object.keys(this.config).some((key) => key.startsWith('NEXT_PUBLIC_'))) {
168
182
  prefix = 'NEXT_PUBLIC_';
169
183
  }
170
184
  // No framework detected, don't strip
@@ -176,7 +190,7 @@ export class EnvDriver implements ConfigDriver {
176
190
  // Strip the prefix from all matching keys
177
191
  if (typeof prefix === 'string' && prefix.length > 0) {
178
192
  const newConfig: Record<string, any> = {};
179
-
193
+
180
194
  for (const [key, value] of Object.entries(this.config)) {
181
195
  if (key.startsWith(prefix)) {
182
196
  // Add both prefixed and unprefixed versions
@@ -187,7 +201,7 @@ export class EnvDriver implements ConfigDriver {
187
201
  newConfig[key] = value;
188
202
  }
189
203
  }
190
-
204
+
191
205
  this.config = newConfig;
192
206
  }
193
207
  }
@@ -3,10 +3,10 @@ import { getNestedValue, hasNestedValue } from '@/utils/get-nested-value.util';
3
3
 
4
4
  /**
5
5
  * File-based Configuration Driver
6
- *
6
+ *
7
7
  * Loads configuration from TypeScript/JavaScript files.
8
8
  * This is a server-side only driver. For client-side, use the Vite plugin.
9
- *
9
+ *
10
10
  * @see packages/pixielity/config/src/plugins/vite-config.plugin.ts
11
11
  */
12
12
  export class FileDriver implements ConfigDriver {
@@ -35,13 +35,14 @@ export class FileDriver implements ConfigDriver {
35
35
  }
36
36
 
37
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
-
38
+ const isServer =
39
+ typeof globalThis !== 'undefined' &&
40
+ typeof (globalThis as typeof globalThis & { window?: any }).window === 'undefined';
41
+
41
42
  if (isServer) {
42
43
  throw new Error(
43
44
  'FileDriver requires pre-loaded configuration. ' +
44
- 'Use Vite plugin for client-side or pass config in constructor for server-side.'
45
+ 'Use Vite plugin for client-side or pass config in constructor for server-side.'
45
46
  );
46
47
  }
47
48
 
package/src/index.ts CHANGED
@@ -88,5 +88,6 @@ export type { ViteConfigPluginOptions } from './interfaces/vite-config-plugin-op
88
88
  // ============================================================================
89
89
  // Utilities
90
90
  // ============================================================================
91
+ export { defineConfig } from './utils/define-config.util';
91
92
  export { getNestedValue, hasNestedValue } from './utils/get-nested-value.util';
92
93
  export { loadConfigFile } from './utils/load-config-file.util';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Configuration Driver Interface
3
- *
3
+ *
4
4
  * Defines the contract for configuration drivers (env, file, firebase, etc.)
5
5
  */
6
6
  export interface ConfigDriver {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Configuration Service Interface
3
- *
3
+ *
4
4
  * Defines the contract for configuration service implementations.
5
5
  */
6
6
  export interface ConfigServiceInterface {
@@ -3,26 +3,26 @@
3
3
  *
4
4
  * Simple plugin that receives env from Vite's loadEnv and injects it into the browser.
5
5
  * Does NOT apply any business logic - that's handled by ConfigService.
6
- *
6
+ *
7
7
  * Responsibilities:
8
8
  * 1. Receive environment variables from Vite's loadEnv (passed via options)
9
9
  * 2. Inject them into window.__APP_CONFIG__ (or custom global name)
10
10
  * 3. Optionally scan and collect .config.ts files
11
- *
11
+ *
12
12
  * ConfigService will then query from window.__APP_CONFIG__ and apply:
13
13
  * - Prefix stripping
14
14
  * - Type conversion
15
15
  * - Validation
16
16
  * - Defaults
17
- *
17
+ *
18
18
  * Usage:
19
19
  * ```ts
20
20
  * import { defineConfig, loadEnv } from 'vite'
21
21
  * import { viteConfigPlugin } from '@abdokouta/config/vite-plugin'
22
- *
22
+ *
23
23
  * export default defineConfig(({ mode }) => {
24
24
  * const env = loadEnv(mode, 'environments', '')
25
- *
25
+ *
26
26
  * return {
27
27
  * plugins: [
28
28
  * viteConfigPlugin({ env })
@@ -40,7 +40,7 @@ import { loadConfigFile } from '@/utils/load-config-file.util';
40
40
 
41
41
  /**
42
42
  * Vite plugin that injects environment variables into the browser
43
- *
43
+ *
44
44
  * This plugin is DUMB - it just receives env and injects it.
45
45
  * ConfigService is SMART - it queries and applies business logic.
46
46
  */
@@ -67,7 +67,7 @@ export function viteConfigPlugin(options: ViteConfigPluginOptions = {}): Plugin
67
67
  });
68
68
 
69
69
  console.log('[vite-plugin-config] Found config files:', configFiles.length);
70
-
70
+
71
71
  // Load all config files and merge them
72
72
  for (const file of configFiles) {
73
73
  const fileConfig = await loadConfigFile(file);
@@ -96,7 +96,10 @@ export function viteConfigPlugin(options: ViteConfigPluginOptions = {}): Plugin
96
96
 
97
97
  console.log('[vite-plugin-config] Injecting environment variables into HTML');
98
98
  console.log('[vite-plugin-config] Total variables:', Object.keys(finalConfig).length);
99
- console.log('[vite-plugin-config] Sample keys:', Object.keys(finalConfig).filter(k => k.includes('APP') || k.includes('VITE')));
99
+ console.log(
100
+ '[vite-plugin-config] Sample keys:',
101
+ Object.keys(finalConfig).filter((k) => k.includes('APP') || k.includes('VITE'))
102
+ );
100
103
  console.log('[vite-plugin-config] Global name:', globalName);
101
104
 
102
105
  // Inject script into HTML head
@@ -1,8 +1,8 @@
1
- import { Inject, Injectable } from "@abdokouta/react-di";
1
+ import { Inject, Injectable } from '@abdokouta/react-di';
2
2
 
3
- import { CONFIG_DRIVER } from "@/constants/tokens.constant";
4
- import type { ConfigDriver } from "@/interfaces/config-driver.interface";
5
- import type { ConfigServiceInterface } from "@/interfaces/config-service.interface";
3
+ import { CONFIG_DRIVER } from '@/constants/tokens.constant';
4
+ import type { ConfigDriver } from '@/interfaces/config-driver.interface';
5
+ import type { ConfigServiceInterface } from '@/interfaces/config-service.interface';
6
6
 
7
7
  /**
8
8
  * Configuration Service
@@ -29,7 +29,7 @@ import type { ConfigServiceInterface } from "@/interfaces/config-service.interfa
29
29
  export class ConfigService implements ConfigServiceInterface {
30
30
  constructor(
31
31
  @Inject(CONFIG_DRIVER)
32
- private driver: ConfigDriver,
32
+ private driver: ConfigDriver
33
33
  ) {}
34
34
 
35
35
  /**
@@ -97,10 +97,10 @@ export class ConfigService implements ConfigServiceInterface {
97
97
  if (value === undefined) {
98
98
  return undefined;
99
99
  }
100
- if (typeof value === "boolean") {
100
+ if (typeof value === 'boolean') {
101
101
  return value;
102
102
  }
103
- return ["true", "1", "yes", "on"].includes(String(value).toLowerCase());
103
+ return ['true', '1', 'yes', 'on'].includes(String(value).toLowerCase());
104
104
  }
105
105
 
106
106
  /**
@@ -126,7 +126,7 @@ export class ConfigService implements ConfigServiceInterface {
126
126
  return value.map(String);
127
127
  }
128
128
  return String(value)
129
- .split(",")
129
+ .split(',')
130
130
  .map((v) => v.trim())
131
131
  .filter(Boolean);
132
132
  }
@@ -139,7 +139,7 @@ export class ConfigService implements ConfigServiceInterface {
139
139
  if (value === undefined) {
140
140
  return undefined;
141
141
  }
142
- if (typeof value === "object") {
142
+ if (typeof value === 'object') {
143
143
  return value as T;
144
144
  }
145
145
  try {
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Define Config Utility
3
+ *
4
+ * Helper function to define config module options with type safety.
5
+ *
6
+ * @module @abdokouta/config
7
+ */
8
+
9
+ import type { ConfigModuleOptions } from '../interfaces/config-module-options.interface';
10
+
11
+ /**
12
+ * Helper function to define config module options with type safety
13
+ *
14
+ * Provides IDE autocomplete and type checking for configuration objects.
15
+ * This pattern is consistent with modern tooling (Vite, Vitest, etc.).
16
+ *
17
+ * @param config - The config module options object
18
+ * @returns The same configuration object with proper typing
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * // config.config.ts
23
+ * import { defineConfig } from '@abdokouta/config';
24
+ *
25
+ * export default defineConfig({
26
+ * driver: 'env',
27
+ * ignoreEnvFile: true,
28
+ * isGlobal: true,
29
+ * envPrefix: 'auto',
30
+ * });
31
+ * ```
32
+ */
33
+ export function defineConfig(config: ConfigModuleOptions): ConfigModuleOptions {
34
+ return config;
35
+ }
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * Get nested value from object using dot notation
3
- *
3
+ *
4
4
  * @param obj - Source object
5
5
  * @param path - Dot-notated path (e.g., 'database.host')
6
6
  * @param defaultValue - Default value if path not found
7
7
  * @returns Value at path or default value
8
- *
8
+ *
9
9
  * @example
10
10
  * ```typescript
11
11
  * const config = { database: { host: 'localhost' } };
@@ -33,15 +33,12 @@ export function getNestedValue<T = any>(
33
33
 
34
34
  /**
35
35
  * Check if nested path exists in object
36
- *
36
+ *
37
37
  * @param obj - Source object
38
38
  * @param path - Dot-notated path
39
39
  * @returns True if path exists
40
40
  */
41
- export function hasNestedValue(
42
- obj: Record<string, any>,
43
- path: string
44
- ): boolean {
41
+ export function hasNestedValue(obj: Record<string, any>, path: string): boolean {
45
42
  const keys = path.split('.');
46
43
  let current: any = obj;
47
44
 
@@ -1,6 +1,9 @@
1
1
  /**
2
2
  * Utils
3
+ *
4
+ * @module @abdokouta/config
3
5
  */
4
6
 
7
+ export { defineConfig } from './define-config.util';
5
8
  export { getNestedValue, hasNestedValue } from './get-nested-value.util';
6
9
  export { loadConfigFile } from './load-config-file.util';
@@ -10,9 +10,7 @@
10
10
  * @param filePath - Absolute path to the config file
11
11
  * @returns Parsed config object
12
12
  */
13
- export async function loadConfigFile(
14
- filePath: string
15
- ): Promise<Record<string, any>> {
13
+ export async function loadConfigFile(filePath: string): Promise<Record<string, any>> {
16
14
  try {
17
15
  // Dynamic import of the config file
18
16
  // @ts-ignore - Dynamic import path
@@ -28,10 +26,7 @@ export async function loadConfigFile(
28
26
 
29
27
  return config;
30
28
  } catch (error) {
31
- console.warn(
32
- `[vite-plugin-config] Failed to load config file: ${filePath}`,
33
- error
34
- );
29
+ console.warn(`[vite-plugin-config] Failed to load config file: ${filePath}`, error);
35
30
  return {};
36
31
  }
37
32
  }
@@ -13,18 +13,14 @@ import type { ViteConfigPluginOptions } from '@/interfaces/vite-config-plugin-op
13
13
  * @param options - Plugin options containing scan configuration
14
14
  * @returns Array of absolute file paths to config files
15
15
  */
16
- export async function scanConfigFiles(
17
- options: ViteConfigPluginOptions
18
- ): Promise<string[]> {
16
+ export async function scanConfigFiles(options: ViteConfigPluginOptions): Promise<string[]> {
19
17
  const {
20
18
  configFilePattern = 'src/**/*.config.ts',
21
19
  excludeDirs = ['node_modules', 'dist', 'build', '.git'],
22
20
  root = process.cwd(),
23
21
  } = options;
24
22
 
25
- const patterns = Array.isArray(configFilePattern)
26
- ? configFilePattern
27
- : [configFilePattern];
23
+ const patterns = Array.isArray(configFilePattern) ? configFilePattern : [configFilePattern];
28
24
  const configFiles: string[] = [];
29
25
 
30
26
  for (const pattern of patterns) {
package/tsup.config.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { defineConfig } from "tsup";
1
+ import { defineConfig } from 'tsup';
2
2
 
3
3
  /**
4
4
  * tsup Configuration for @abdokouta/logger Package
@@ -15,7 +15,7 @@ export default defineConfig({
15
15
  *
16
16
  * Exports the logger service, transporters, formatters, and hooks.
17
17
  */
18
- entry: ["src/index.ts"],
18
+ entry: ['src/index.ts'],
19
19
 
20
20
  /**
21
21
  * Dual format output (ESM + CJS).
@@ -23,7 +23,7 @@ export default defineConfig({
23
23
  * - ESM: Modern module format for tree-shaking
24
24
  * - CJS: CommonJS for Node.js compatibility
25
25
  */
26
- format: ["esm", "cjs"],
26
+ format: ['esm', 'cjs'],
27
27
 
28
28
  /**
29
29
  * Generate TypeScript declaration files.
@@ -60,14 +60,14 @@ export default defineConfig({
60
60
  * Supports modern browsers and Node.js 14+.
61
61
  * Includes optional chaining, nullish coalescing, and other ES2020 features.
62
62
  */
63
- target: "es2020",
63
+ target: 'es2020',
64
64
 
65
65
  /**
66
66
  * Platform-neutral build.
67
67
  *
68
68
  * Works in both browser and Node.js environments.
69
69
  */
70
- platform: "neutral",
70
+ platform: 'neutral',
71
71
 
72
72
  /**
73
73
  * External dependencies that should not be bundled.
@@ -75,7 +75,7 @@ export default defineConfig({
75
75
  * - @abdokouta/react-di: DI system (peer dependency)
76
76
  * - react: React library (optional peer dependency for hooks)
77
77
  */
78
- external: ["@abdokouta/react-di", "react"],
78
+ external: ['@abdokouta/react-di', 'react'],
79
79
 
80
80
  /**
81
81
  * Disable code splitting for library builds.
@@ -100,6 +100,6 @@ export default defineConfig({
100
100
  * This ensures proper module resolution in all environments.
101
101
  */
102
102
  outExtension({ format }) {
103
- return { js: format === "esm" ? ".mjs" : ".js" };
103
+ return { js: format === 'esm' ? '.mjs' : '.js' };
104
104
  },
105
105
  });
package/vitest.config.ts CHANGED
@@ -15,9 +15,9 @@
15
15
  * @category Configuration
16
16
  */
17
17
 
18
- import { fileURLToPath } from "url";
19
- import { resolve, dirname } from "path";
20
- import { defineConfig } from "vitest/config";
18
+ import { fileURLToPath } from 'url';
19
+ import { resolve, dirname } from 'path';
20
+ import { defineConfig } from 'vitest/config';
21
21
 
22
22
  const __filename = fileURLToPath(import.meta.url);
23
23
  const __dirname = dirname(__filename);
@@ -28,30 +28,30 @@ export default defineConfig({
28
28
  globals: true,
29
29
 
30
30
  // Use jsdom environment for React testing
31
- environment: "jsdom",
31
+ environment: 'jsdom',
32
32
 
33
33
  // Run setup file before tests
34
- setupFiles: ["./__tests__/vitest.setup.ts"],
34
+ setupFiles: ['./__tests__/vitest.setup.ts'],
35
35
 
36
36
  // Only include __tests__ directory
37
- include: ["__tests__/**/*.{test,spec}.{ts,tsx}"],
37
+ include: ['__tests__/**/*.{test,spec}.{ts,tsx}'],
38
38
 
39
39
  // Coverage configuration
40
40
  coverage: {
41
41
  // Use v8 coverage provider (faster than istanbul)
42
- provider: "v8",
42
+ provider: 'v8',
43
43
 
44
44
  // Generate multiple report formats
45
- reporter: ["text", "json", "html"],
45
+ reporter: ['text', 'json', 'html'],
46
46
 
47
47
  // Exclude files from coverage
48
48
  exclude: [
49
- "node_modules/",
50
- "dist/",
51
- "test/",
52
- "**/*.test.ts",
53
- "**/*.test.tsx",
54
- "**/*.config.ts",
49
+ 'node_modules/',
50
+ 'dist/',
51
+ 'test/',
52
+ '**/*.test.ts',
53
+ '**/*.test.tsx',
54
+ '**/*.config.ts',
55
55
  ],
56
56
  },
57
57
  },
@@ -60,7 +60,7 @@ export default defineConfig({
60
60
  resolve: {
61
61
  alias: {
62
62
  // Map @ to ./src for consistent imports
63
- "@": resolve(__dirname, "./src"),
63
+ '@': resolve(__dirname, './src'),
64
64
  },
65
65
  },
66
66
  });