@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,115 +0,0 @@
1
- /**
2
- * Vite Plugin for Config Package
3
- *
4
- * Simple plugin that receives env from Vite's loadEnv and injects it into the browser.
5
- * Does NOT apply any business logic - that's handled by ConfigService.
6
- *
7
- * Responsibilities:
8
- * 1. Receive environment variables from Vite's loadEnv (passed via options)
9
- * 2. Inject them into window.__APP_CONFIG__ (or custom global name)
10
- * 3. Optionally scan and collect .config.ts files
11
- *
12
- * ConfigService will then query from window.__APP_CONFIG__ and apply:
13
- * - Prefix stripping
14
- * - Type conversion
15
- * - Validation
16
- * - Defaults
17
- *
18
- * Usage:
19
- * ```ts
20
- * import { defineConfig, loadEnv } from 'vite'
21
- * import { viteConfigPlugin } from '@abdokouta/config/vite-plugin'
22
- *
23
- * export default defineConfig(({ mode }) => {
24
- * const env = loadEnv(mode, 'environments', '')
25
- *
26
- * return {
27
- * plugins: [
28
- * viteConfigPlugin({ env })
29
- * ]
30
- * }
31
- * })
32
- * ```
33
- */
34
-
35
- import path from 'path';
36
- import type { Plugin } from 'vite';
37
- import type { ViteConfigPluginOptions } from '@/interfaces/vite-config-plugin-options.interface';
38
- import { scanConfigFiles } from '@/utils/scan-config-files.util';
39
- import { loadConfigFile } from '@/utils/load-config-file.util';
40
-
41
- /**
42
- * Vite plugin that injects environment variables into the browser
43
- *
44
- * This plugin is DUMB - it just receives env and injects it.
45
- * ConfigService is SMART - it queries and applies business logic.
46
- */
47
- export function viteConfigPlugin(options: ViteConfigPluginOptions = {}): Plugin {
48
- const {
49
- env = {},
50
- includeAll = true,
51
- include = [],
52
- scanConfigFiles: shouldScanConfigFiles = false,
53
- globalName = '__APP_CONFIG__',
54
- } = options;
55
-
56
- let collectedConfig: Record<string, any> = {};
57
-
58
- return {
59
- name: 'vite-plugin-config',
60
-
61
- async configResolved(config) {
62
- // Scan and collect config files (optional, disabled by default)
63
- if (shouldScanConfigFiles) {
64
- const configFiles = await scanConfigFiles({
65
- ...options,
66
- root: config.root,
67
- });
68
-
69
- console.log('[vite-plugin-config] Found config files:', configFiles.length);
70
-
71
- // Load all config files and merge them
72
- for (const file of configFiles) {
73
- const fileConfig = await loadConfigFile(file);
74
- collectedConfig = { ...collectedConfig, ...fileConfig };
75
- console.log('[vite-plugin-config] Loaded config from:', path.relative(config.root, file));
76
- }
77
- }
78
- },
79
-
80
- transformIndexHtml(html) {
81
- // Build the config object from provided env
82
- const processEnv: Record<string, any> = {};
83
-
84
- for (const [key, value] of Object.entries(env)) {
85
- // Filter by include list if not including all
86
- if (!includeAll && !include.includes(key)) {
87
- continue;
88
- }
89
-
90
- // Just copy the value as-is (NO prefix stripping here)
91
- processEnv[key] = value;
92
- }
93
-
94
- // Merge with collected config from .config.ts files
95
- const finalConfig = { ...processEnv, ...collectedConfig };
96
-
97
- console.log('[vite-plugin-config] Injecting environment variables into HTML');
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')));
100
- console.log('[vite-plugin-config] Global name:', globalName);
101
-
102
- // Inject script into HTML head
103
- const script = `
104
- <script>
105
- window.${globalName} = ${JSON.stringify(finalConfig)};
106
- // Also set process.env for backward compatibility
107
- window.process = window.process || {};
108
- window.process.env = window.${globalName};
109
- </script>
110
- `;
111
-
112
- return html.replace('<head>', `<head>${script}`);
113
- },
114
- };
115
- }
@@ -1,172 +0,0 @@
1
- import { Inject, Injectable } from "@abdokouta/react-di";
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";
6
-
7
- /**
8
- * Configuration Service
9
- *
10
- * Provides type-safe access to configuration values with various getter methods.
11
- * Similar to NestJS ConfigService.
12
- *
13
- * @example
14
- * ```typescript
15
- * class MyService {
16
- * constructor(private config: ConfigService) {}
17
- *
18
- * getDbConfig() {
19
- * return {
20
- * host: this.config.getString('DB_HOST', 'localhost'),
21
- * port: this.config.getNumber('DB_PORT', 5432),
22
- * ssl: this.config.getBool('DB_SSL', false),
23
- * };
24
- * }
25
- * }
26
- * ```
27
- */
28
- @Injectable()
29
- export class ConfigService implements ConfigServiceInterface {
30
- constructor(
31
- @Inject(CONFIG_DRIVER)
32
- private driver: ConfigDriver,
33
- ) {}
34
-
35
- /**
36
- * Get configuration value
37
- */
38
- get<T = any>(key: string, defaultValue?: T): T | undefined {
39
- return this.driver.get<T>(key, defaultValue);
40
- }
41
-
42
- /**
43
- * Get configuration value or throw if not found
44
- */
45
- getOrThrow<T = any>(key: string): T {
46
- const value = this.get<T>(key);
47
- if (value === undefined) {
48
- throw new Error(`Configuration key "${key}" is required but not set`);
49
- }
50
- return value;
51
- }
52
-
53
- /**
54
- * Get string value
55
- */
56
- getString(key: string, defaultValue?: string): string | undefined {
57
- const value = this.get(key, defaultValue);
58
- return value !== undefined ? String(value) : undefined;
59
- }
60
-
61
- /**
62
- * Get string value or throw
63
- */
64
- getStringOrThrow(key: string): string {
65
- return String(this.getOrThrow(key));
66
- }
67
-
68
- /**
69
- * Get number value
70
- */
71
- getNumber(key: string, defaultValue?: number): number | undefined {
72
- const value = this.get(key, defaultValue);
73
- if (value === undefined) {
74
- return undefined;
75
- }
76
- const parsed = Number(value);
77
- return isNaN(parsed) ? defaultValue : parsed;
78
- }
79
-
80
- /**
81
- * Get number value or throw
82
- */
83
- getNumberOrThrow(key: string): number {
84
- const value = this.getNumber(key);
85
- if (value === undefined) {
86
- throw new Error(`Configuration key "${key}" is required but not set`);
87
- }
88
- return value;
89
- }
90
-
91
- /**
92
- * Get boolean value
93
- * Treats 'true', '1', 'yes', 'on' as true
94
- */
95
- getBool(key: string, defaultValue?: boolean): boolean | undefined {
96
- const value = this.get(key, defaultValue);
97
- if (value === undefined) {
98
- return undefined;
99
- }
100
- if (typeof value === "boolean") {
101
- return value;
102
- }
103
- return ["true", "1", "yes", "on"].includes(String(value).toLowerCase());
104
- }
105
-
106
- /**
107
- * Get boolean value or throw
108
- */
109
- getBoolOrThrow(key: string): boolean {
110
- const value = this.getBool(key);
111
- if (value === undefined) {
112
- throw new Error(`Configuration key "${key}" is required but not set`);
113
- }
114
- return value;
115
- }
116
-
117
- /**
118
- * Get array value (comma-separated string or actual array)
119
- */
120
- getArray(key: string, defaultValue?: string[]): string[] | undefined {
121
- const value = this.get(key, defaultValue);
122
- if (value === undefined) {
123
- return undefined;
124
- }
125
- if (Array.isArray(value)) {
126
- return value.map(String);
127
- }
128
- return String(value)
129
- .split(",")
130
- .map((v) => v.trim())
131
- .filter(Boolean);
132
- }
133
-
134
- /**
135
- * Get JSON value
136
- */
137
- getJson<T = any>(key: string, defaultValue?: T): T | undefined {
138
- const value = this.get(key, defaultValue);
139
- if (value === undefined) {
140
- return undefined;
141
- }
142
- if (typeof value === "object") {
143
- return value as T;
144
- }
145
- try {
146
- return JSON.parse(String(value)) as T;
147
- } catch {
148
- return defaultValue;
149
- }
150
- }
151
-
152
- /**
153
- * Check if configuration key exists
154
- */
155
- has(key: string): boolean {
156
- return this.driver.has(key);
157
- }
158
-
159
- /**
160
- * Get all configuration values
161
- */
162
- all(): Record<string, any> {
163
- return this.driver.all();
164
- }
165
-
166
- /**
167
- * Clear cache (no-op since we don't cache in ConfigService)
168
- */
169
- clearCache(): void {
170
- // No-op - caching should be done at a higher level if needed
171
- }
172
- }
@@ -1,5 +0,0 @@
1
- /**
2
- * Services
3
- */
4
-
5
- export { ConfigService } from './config.service';
@@ -1,56 +0,0 @@
1
- /**
2
- * Get nested value from object using dot notation
3
- *
4
- * @param obj - Source object
5
- * @param path - Dot-notated path (e.g., 'database.host')
6
- * @param defaultValue - Default value if path not found
7
- * @returns Value at path or default value
8
- *
9
- * @example
10
- * ```typescript
11
- * const config = { database: { host: 'localhost' } };
12
- * getNestedValue(config, 'database.host'); // 'localhost'
13
- * getNestedValue(config, 'database.port', 5432); // 5432
14
- * ```
15
- */
16
- export function getNestedValue<T = any>(
17
- obj: Record<string, any>,
18
- path: string,
19
- defaultValue?: T
20
- ): T | undefined {
21
- const keys = path.split('.');
22
- let current: any = obj;
23
-
24
- for (const key of keys) {
25
- if (current === null || current === undefined) {
26
- return defaultValue;
27
- }
28
- current = current[key];
29
- }
30
-
31
- return current !== undefined ? current : defaultValue;
32
- }
33
-
34
- /**
35
- * Check if nested path exists in object
36
- *
37
- * @param obj - Source object
38
- * @param path - Dot-notated path
39
- * @returns True if path exists
40
- */
41
- export function hasNestedValue(
42
- obj: Record<string, any>,
43
- path: string
44
- ): boolean {
45
- const keys = path.split('.');
46
- let current: any = obj;
47
-
48
- for (const key of keys) {
49
- if (current === null || current === undefined || !(key in current)) {
50
- return false;
51
- }
52
- current = current[key];
53
- }
54
-
55
- return true;
56
- }
@@ -1,6 +0,0 @@
1
- /**
2
- * Utils
3
- */
4
-
5
- export { getNestedValue, hasNestedValue } from './get-nested-value.util';
6
- export { loadConfigFile } from './load-config-file.util';
@@ -1,37 +0,0 @@
1
- /**
2
- * Load Config File Utility
3
- *
4
- * Dynamically loads and parses a config file.
5
- */
6
-
7
- /**
8
- * Load and parse config file
9
- *
10
- * @param filePath - Absolute path to the config file
11
- * @returns Parsed config object
12
- */
13
- export async function loadConfigFile(
14
- filePath: string
15
- ): Promise<Record<string, any>> {
16
- try {
17
- // Dynamic import of the config file
18
- // @ts-ignore - Dynamic import path
19
- const module = await import(/* @vite-ignore */ filePath);
20
-
21
- // Extract config object (could be default export or named export)
22
- const config = module.default || module;
23
-
24
- // If it's a function, call it
25
- if (typeof config === 'function') {
26
- return await config();
27
- }
28
-
29
- return config;
30
- } catch (error) {
31
- console.warn(
32
- `[vite-plugin-config] Failed to load config file: ${filePath}`,
33
- error
34
- );
35
- return {};
36
- }
37
- }
@@ -1,40 +0,0 @@
1
- /**
2
- * Scan Config Files Utility
3
- *
4
- * Scans and collects all .config.ts files based on the provided options.
5
- */
6
-
7
- import { glob } from 'glob';
8
- import type { ViteConfigPluginOptions } from '@/interfaces/vite-config-plugin-options.interface';
9
-
10
- /**
11
- * Scan and collect all .config.ts files
12
- *
13
- * @param options - Plugin options containing scan configuration
14
- * @returns Array of absolute file paths to config files
15
- */
16
- export async function scanConfigFiles(
17
- options: ViteConfigPluginOptions
18
- ): Promise<string[]> {
19
- const {
20
- configFilePattern = 'src/**/*.config.ts',
21
- excludeDirs = ['node_modules', 'dist', 'build', '.git'],
22
- root = process.cwd(),
23
- } = options;
24
-
25
- const patterns = Array.isArray(configFilePattern)
26
- ? configFilePattern
27
- : [configFilePattern];
28
- const configFiles: string[] = [];
29
-
30
- for (const pattern of patterns) {
31
- const files = await glob(pattern, {
32
- cwd: root,
33
- absolute: true,
34
- ignore: excludeDirs.map((dir) => `**/${dir}/**`),
35
- });
36
- configFiles.push(...files);
37
- }
38
-
39
- return configFiles;
40
- }
package/tsconfig.json DELETED
@@ -1,28 +0,0 @@
1
- {
2
- "extends": "@nesvel/typescript-config/base.json",
3
- "compilerOptions": {
4
- "outDir": "./dist",
5
- "rootDir": ".",
6
- /* Silence TypeScript deprecation warnings */
7
- "ignoreDeprecations": "6.0",
8
- /* Module resolution - Override Nesvel's NodeNext for compatibility */
9
- "module": "ESNext",
10
- "moduleResolution": "bundler",
11
- /* Decorators - Required for DI container */
12
- "experimentalDecorators": true,
13
- "emitDecoratorMetadata": true,
14
- /* Build options */
15
- "declaration": true,
16
- "declarationMap": true,
17
- "sourceMap": true,
18
- "jsx": "react-jsx",
19
- /* Testing */
20
- "types": ["vitest/globals", "node"],
21
- "baseUrl": ".",
22
- "paths": {
23
- "@/*": ["./src/*"]
24
- }
25
- },
26
- "include": ["src/**/*", "__tests__/**/*"],
27
- "exclude": ["node_modules", "dist", "config"]
28
- }
package/tsup.config.ts DELETED
@@ -1,105 +0,0 @@
1
- import { defineConfig } from "tsup";
2
-
3
- /**
4
- * tsup Configuration for @abdokouta/logger Package
5
- *
6
- * Builds the logger package with dual format output (ESM + CJS).
7
- * Provides a flexible logging system with multiple transporters
8
- * (console, storage, silent) and formatters (pretty, json, simple).
9
- *
10
- * @see https://tsup.egoist.dev/
11
- */
12
- export default defineConfig({
13
- /**
14
- * Entry point for the build.
15
- *
16
- * Exports the logger service, transporters, formatters, and hooks.
17
- */
18
- entry: ["src/index.ts"],
19
-
20
- /**
21
- * Dual format output (ESM + CJS).
22
- *
23
- * - ESM: Modern module format for tree-shaking
24
- * - CJS: CommonJS for Node.js compatibility
25
- */
26
- format: ["esm", "cjs"],
27
-
28
- /**
29
- * Generate TypeScript declaration files.
30
- *
31
- * Provides full type safety for consumers.
32
- */
33
- dts: true,
34
-
35
- /**
36
- * Generate source maps for debugging.
37
- *
38
- * Helps developers debug issues in production.
39
- */
40
- sourcemap: true,
41
-
42
- /**
43
- * Clean output directory before each build.
44
- *
45
- * Prevents stale files from previous builds.
46
- */
47
- clean: true,
48
-
49
- /**
50
- * Consumers will minify during their own build.
51
- *
52
- * Library code should not be pre-minified to allow
53
- * better tree-shaking and debugging.
54
- */
55
- minify: false,
56
-
57
- /**
58
- * Target ES2020 for broad compatibility.
59
- *
60
- * Supports modern browsers and Node.js 14+.
61
- * Includes optional chaining, nullish coalescing, and other ES2020 features.
62
- */
63
- target: "es2020",
64
-
65
- /**
66
- * Platform-neutral build.
67
- *
68
- * Works in both browser and Node.js environments.
69
- */
70
- platform: "neutral",
71
-
72
- /**
73
- * External dependencies that should not be bundled.
74
- *
75
- * - @abdokouta/react-di: DI system (peer dependency)
76
- * - react: React library (optional peer dependency for hooks)
77
- */
78
- external: ["@abdokouta/react-di", "react"],
79
-
80
- /**
81
- * Disable code splitting for library builds.
82
- *
83
- * Libraries should be single-file outputs for easier consumption.
84
- */
85
- splitting: false,
86
-
87
- /**
88
- * Skip node_modules from being processed.
89
- *
90
- * Improves build performance by not processing dependencies.
91
- */
92
- skipNodeModulesBundle: true,
93
-
94
- /**
95
- * Ensure proper file extensions for each format.
96
- *
97
- * - .mjs for ESM (explicit module format)
98
- * - .js for CJS (CommonJS format)
99
- *
100
- * This ensures proper module resolution in all environments.
101
- */
102
- outExtension({ format }) {
103
- return { js: format === "esm" ? ".mjs" : ".js" };
104
- },
105
- });
package/vitest.config.ts DELETED
@@ -1,66 +0,0 @@
1
- /**
2
- * @fileoverview Vitest configuration for @abdokouta/react-di package
3
- *
4
- * This configuration sets up the testing environment for the container package,
5
- * including test globals, jsdom environment, coverage reporting, and path aliases.
6
- *
7
- * Configuration Features:
8
- * - Globals: Enables global test functions (describe, it, expect)
9
- * - Environment: Uses jsdom for React component testing
10
- * - Setup Files: Runs vitest.setup.ts before tests
11
- * - Coverage: Configures v8 coverage provider with HTML/JSON/text reports
12
- * - Path Aliases: Resolves @ to ./src for consistent imports
13
- *
14
- * @module @abdokouta/react-di
15
- * @category Configuration
16
- */
17
-
18
- import { fileURLToPath } from "url";
19
- import { resolve, dirname } from "path";
20
- import { defineConfig } from "vitest/config";
21
-
22
- const __filename = fileURLToPath(import.meta.url);
23
- const __dirname = dirname(__filename);
24
-
25
- export default defineConfig({
26
- test: {
27
- // Enable global test functions (describe, it, expect, etc.)
28
- globals: true,
29
-
30
- // Use jsdom environment for React testing
31
- environment: "jsdom",
32
-
33
- // Run setup file before tests
34
- setupFiles: ["./__tests__/vitest.setup.ts"],
35
-
36
- // Only include __tests__ directory
37
- include: ["__tests__/**/*.{test,spec}.{ts,tsx}"],
38
-
39
- // Coverage configuration
40
- coverage: {
41
- // Use v8 coverage provider (faster than istanbul)
42
- provider: "v8",
43
-
44
- // Generate multiple report formats
45
- reporter: ["text", "json", "html"],
46
-
47
- // Exclude files from coverage
48
- exclude: [
49
- "node_modules/",
50
- "dist/",
51
- "test/",
52
- "**/*.test.ts",
53
- "**/*.test.tsx",
54
- "**/*.config.ts",
55
- ],
56
- },
57
- },
58
-
59
- // Resolve path aliases
60
- resolve: {
61
- alias: {
62
- // Map @ to ./src for consistent imports
63
- "@": resolve(__dirname, "./src"),
64
- },
65
- },
66
- });