npm package: @jamx-framework/config
Description: JAMX Framework — centralized configuration
Author: Stiven-21
Latest version: 2.0.0

# @jamx-framework/config

## Descripción
Módulo de configuración centralizada para JAMX Framework. Proporciona un sistema tipado y con valores por defecto para configurar todos los aspectos de una aplicación JAMX: servidor, base de datos, autenticación, seguridad, i18n, UI y plugins. Incluye carga desde `jamx.config.ts`, validación de tipos, y un gestor de configuración para acceso global.

## Cómo funciona
El módulo maneja la configuración en tres capas:

1. **Tipos**: Definiciones TypeScript completas para todas las opciones de configuración
2. **defineConfig**: Función que aplica valores por defecto y valida la estructura
3. **ConfigManager**: Clase para cargar, almacenar y acceder a la configuración en runtime
4. **loadConfig**: Función asíncrona que carga `jamx.config.ts` desde el sistema de archivos

## Componentes principales

### Tipos (`src/index.ts`)
- `Target`: Plataformas de compilación (`"web"`, `"ios"`, `"android"`, `"desktop"`)
- `StylingMode`: Modo de estilos (`"jamx-native"`, `"tailwind"`, `"external"`, `"none"`)
- `DatabaseDriver`: Drivers de BD (`"postgresql"`, `"mysql"`, `"sqlite"`)
- `SessionEngine`: Motor de sesiones (`"jwt"`, `"database"`, `"redis"`)
- `AuthStrategy`: Estrategias de autenticación
- `JamxConfig`: Interfaz principal de configuración
- `DatabaseConnection`: Configuración de conexión a BD

### Funciones

#### defineConfig
```typescript
function defineConfig(config: JamxConfig): JamxConfig
```
Aplica defaults y retorna una configuración validada.

#### loadConfig
```typescript
async function loadConfig(projectRoot: string): Promise<JamxConfig>
```
Carga `jamx.config.ts` desde el sistema de archivos.

### Clase ConfigManager
```typescript
class ConfigManager {
  async load(source?: JamxConfig | string): Promise<JamxConfig>;
  get(): JamxConfig;
  section<K extends keyof JamxConfig>(key: K): JamxConfig[K];
  readonly env: string;
  readonly isDev: boolean;
  readonly isProd: boolean;
}
```
Gestor de configuración con acceso por secciones y helpers de entorno.

## Uso básico

### Definir configuración en `jamx.config.ts`
```typescript
import { defineConfig } from '@jamx-framework/config';

export default defineConfig({
  targets: ['web', 'ios'],
  server: {
    port: 3000,
    host: 'localhost',
    cors: {
      enabled: true,
      origins: ['https://example.com'],
    },
  },
  database: {
    primary: {
      driver: 'postgresql',
      url: 'postgresql://user:pass@localhost:5432/db',
    },
  },
  auth: {
    strategies: ['credentials', 'oauth2'],
    session: {
      engine: 'jwt',
      duration: '7d',
      secret: process.env.JWT_SECRET,
    },
    pages: {
      login: '/login',
      logout: '/logout',
      register: '/register',
    },
  },
  security: {
    csrf: true,
    rateLimit: {
      enabled: true,
      max: 100,
      window: '1m',
    },
    headers: {
      hsts: true,
      frameOptions: 'DENY',
      contentTypeSniff: true,
    },
  },
  i18n: {
    defaultLocale: 'es',
    locales: ['es', 'en', 'fr'],
    strategy: 'prefix',
  },
  ui: {
    styling: {
      mode: 'tailwind',
    },
  },
});
```

### Usar ConfigManager en la aplicación
```typescript
import { ConfigManager } from '@jamx-framework/config';

const configManager = new ConfigManager();

// Cargar desde archivo
await configManager.load('./'); // busca jamx.config.ts

// O cargar desde objeto
await configManager.load({
  targets: ['web'],
  server: { port: 8080 },
});

// Acceder a la configuración completa
const config = configManager.get();
console.log(config.server?.port); // 3000 (default) o 8080

// Acceder a una sección
const serverConfig = configManager.section('server');
console.log(serverConfig.port);

// Verificar entorno
if (configManager.isDev) {
  console.log('Modo desarrollo');
}

if (configManager.isProd) {
  console.log('Modo producción');
}
```

### Usar defineConfig directamente
```typescript
import { defineConfig } from '@jamx-framework/config';

const config = defineConfig({
  targets: ['web'],
  // solo especificamos lo que necesitamos
});

console.log(config.server?.port); // 3000 (default aplicado)
console.log(config.ui?.styling?.mode); // "jamx-native" (default)
console.log(config.security?.csrf); // true (default)
```

### Acceder a configuración en módulos
```typescript
// En un módulo de base de datos
import { ConfigManager } from '@jamx-framework/config';

const configManager = new ConfigManager();
const dbConfig = configManager.section('database');

const connection = dbConfig.primary;
if (connection?.driver === 'postgresql') {
  // conectar a PostgreSQL
}
```

## API Reference

### Tipos

#### Target
```typescript
type Target = "web" | "ios" | "android" | "desktop";
```
Plataformas de compilación objetivo.

#### StylingMode
```typescript
type StylingMode = "jamx-native" | "tailwind" | "external" | "none";
```
Modo de estilos de la aplicación.

#### DatabaseDriver
```typescript
type DatabaseDriver = "postgresql" | "mysql" | "sqlite";
```
Drivers de base de datos soportados.

#### SessionEngine
```typescript
type SessionEngine = "jwt" | "database" | "redis";
```
Motor de gestión de sesiones.

#### AuthStrategy
```typescript
type AuthStrategy =
  | "credentials"
  | "oauth2"
  | "magic-link"
  | "passkey"
  | "saml";
```
Estrategias de autenticación disponibles.

#### JamxConfig
```typescript
interface JamxConfig {
  targets: Target[];
  server?: {
    port?: number;
    host?: string;
    cors?: { enabled?: boolean; origins?: string[] };
  };
  ui?: {
    styling?: { mode?: StylingMode };
  };
  database?: {
    primary?: DatabaseConnection;
    [key: string]: DatabaseConnection | undefined;
  };
  auth?: {
    strategies?: AuthStrategy[];
    session?: { engine?: SessionEngine; duration?: string; secret?: string };
    pages?: { login?: string; logout?: string; register?: string };
  };
  security?: {
    csrf?: boolean;
    rateLimit?: { enabled?: boolean; max?: number; window?: string };
    headers?: {
      hsts?: boolean;
      frameOptions?: "DENY" | "SAMEORIGIN";
      contentTypeSniff?: boolean;
    };
  };
  i18n?: {
    defaultLocale?: string;
    locales?: string[];
    strategy?: "prefix" | "prefix-except-default" | "domain";
  };
  plugins?: unknown[];
}
```

#### DatabaseConnection
```typescript
interface DatabaseConnection {
  driver: DatabaseDriver;
  url: string;
}
```

### Funciones

#### defineConfig
```typescript
function defineConfig(config: JamxConfig): JamxConfig
```
Aplica valores por defecto a la configuración y la retorna validada.

**Defaults aplicados:**
- `targets`: Requerido, sin default
- `server.port`: 3000
- `server.host`: "localhost"
- `server.cors.enabled`: false
- `server.cors.origins`: []
- `ui.styling.mode`: "jamx-native"
- `security.csrf`: true
- `security.rateLimit.enabled`: true
- `security.rateLimit.max`: 100
- `security.rateLimit.window`: "1m"

#### loadConfig
```typescript
async function loadConfig(projectRoot: string): Promise<JamxConfig>
```
Carga la configuración desde `jamx.config.ts` en `projectRoot`.

**Comportamiento:**
- Busca `jamx.config.ts` en `projectRoot`
- Importa el módulo ES
- Espera un default export o named export `config`
- Lanza error si no encuentra o no es un objeto
- Internamente llama a `defineConfig` para aplicar defaults

**Ejemplo:**
```typescript
// jamx.config.ts
export default {
  targets: ['web'],
  server: { port: 8080 },
};

// En código
const config = await loadConfig('/path/to/project');
// config.server.port === 8080
```

### Clase ConfigManager

#### Constructor
```typescript
new ConfigManager()
```
Crea un gestor vacío.

#### load
```typescript
async load(source?: JamxConfig | string): Promise<JamxConfig>
```
Carga la configuración desde:
- Objeto `JamxConfig`: aplica `defineConfig`
- Ruta de proyecto (string): llama a `loadConfig(source)`
- Sin argumentos: crea config mínima con `{ targets: ['web'] }`

#### get
```typescript
get(): JamxConfig
```
Retorna la configuración cargada. Lanza error si no se ha llamado `load()` antes.

#### section
```typescript
section<K extends keyof JamxConfig>(key: K): JamxConfig[K]
```
Accede a una sección específica de la configuración.

**Ejemplo:**
```typescript
const server = configManager.section('server');
const port = server?.port;
```

#### Getters de conveniencia
```typescript
readonly env: string;      // NODE_ENV o "development"
readonly isDev: boolean;   // true si env === "development"
readonly isProd: boolean;  // true si env === "production"
```

## Estructura de JamxConfig

### targets
Array de plataformas a compilar:
```typescript
targets: ['web'];                    // solo web
targets: ['web', 'ios', 'android']; // multiplataforma
```

### server
Configuración del servidor de desarrollo/producción:
```typescript
server: {
  port: 3000,           // puerto HTTP
  host: 'localhost',    // host para binding
  cors: {
    enabled: true,      // habilitar CORS
    origins: ['*'],     // orígenes permitidos
  },
}
```

### ui
Configuración de interfaz de usuario:
```typescript
ui: {
  styling: {
    mode: 'tailwind', // 'jamx-native' | 'tailwind' | 'external' | 'none'
  },
}
```

### database
Configuración de bases de datos:
```typescript
database: {
  primary: {
    driver: 'postgresql', // 'postgresql' | 'mysql' | 'sqlite'
    url: 'postgresql://...',
  },
  // conexiones adicionales
  analytics: {
    driver: 'mysql',
    url: 'mysql://...',
  },
}
```

### auth
Configuración de autenticación:
```typescript
auth: {
  strategies: ['credentials', 'oauth2'],
  session: {
    engine: 'jwt',      // 'jwt' | 'database' | 'redis'
    duration: '7d',     // TTL de sesión
    secret: 'secret',   // secreto para firmar tokens
  },
  pages: {
    login: '/login',
    logout: '/logout',
    register: '/register',
  },
}
```

### security
Configuración de seguridad:
```typescript
security: {
  csrf: true, // habilitar protección CSRF
  rateLimit: {
    enabled: true,
    max: 100,      // requests por ventana
    window: '1m',  // '1s', '1m', '1h', etc.
  },
  headers: {
    hsts: true,               // HTTP Strict Transport Security
    frameOptions: 'DENY',     // X-Frame-Options
    contentTypeSniff: true,   // X-Content-Type-Options
  },
}
```

### i18n
Configuración de internacionalización:
```typescript
i18n: {
  defaultLocale: 'es',
  locales: ['es', 'en', 'fr'],
  strategy: 'prefix', // 'prefix' | 'prefix-except-default' | 'domain'
}
```

### plugins
Array de configuraciones de plugins:
```typescript
plugins: [
  { name: 'my-plugin', options: { ... } },
  { name: 'another-plugin', options: { ... } },
]
```

## Flujo de carga de configuración

### 1. Desde archivo (recomendado)
```typescript
// jamx.config.ts
import { defineConfig } from '@jamx-framework/config';

export default defineConfig({
  targets: ['web'],
  server: { port: 3000 },
});

// En la aplicación
const manager = new ConfigManager();
await manager.load('./'); // carga desde directorio actual
const config = manager.get();
```

### 2. Desde objeto
```typescript
const manager = new ConfigManager();
await manager.load({
  targets: ['web', 'ios'],
  server: { port: 8080 },
});
const config = manager.get();
```

### 3. Directo con defineConfig
```typescript
import { defineConfig } from '@jamx-framework/config';

const config = defineConfig({
  targets: ['web'],
});
// config ya tiene defaults aplicados
```

## Validación y defaults

### Validación
- `defineConfig` valida que `targets` esté presente
- Verifica que los objetos anidados tengan la estructura correcta
- No valida valores específicos (ej: puerto válido), solo tipos

### Defaults
Los defaults se aplican en capas:

1. **Nivel raíz**: `targets` es requerido, no tiene default
2. **Nivel objeto**: Si `server` existe, se aplican defaults a sus propiedades
3. **Nivel propiedad**: Cada propiedad tiene su propio default

**Ejemplo:**
```typescript
const config = defineConfig({ targets: ['web'] });

// Aplicados:
// config.server.port === 3000
// config.server.host === 'localhost'
// config.server.cors.enabled === false
// config.ui.styling.mode === 'jamx-native'
// config.security.csrf === true
// config.security.rateLimit.enabled === true
// config.security.rateLimit.max === 100
// config.security.rateLimit.window === '1m'
```

## Configuración por entorno

### Usar variables de entorno
```typescript
// jamx.config.ts
export default defineConfig({
  targets: ['web'],
  server: {
    port: Number(process.env.PORT) || 3000,
    host: process.env.HOST || 'localhost',
  },
  auth: {
    session: {
      secret: process.env.JWT_SECRET,
    },
  },
  database: {
    primary: {
      url: process.env.DATABASE_URL,
    },
  },
});
```

### Diferentes configuraciones por entorno
```typescript
// jamx.config.ts
const env = process.env.NODE_ENV || 'development';

const config = defineConfig({
  targets: ['web'],
  server: {
    port: env === 'production' ? 80 : 3000,
    cors: {
      enabled: env === 'production',
      origins: env === 'production' ? ['https://example.com'] : [],
    },
  },
  security: {
    csrf: env === 'production',
    rateLimit: {
      enabled: env === 'production',
      max: env === 'production' ? 1000 : 100,
    },
  },
});

export default config;
```

## Testing

### Tests unitarios
```typescript
import { defineConfig, ConfigManager } from '@jamx-framework/config';
import { describe, it, expect, beforeEach } from 'vitest';

describe('Config', () => {
  it('should apply defaults', () => {
    const cfg = defineConfig({ targets: ['web'] });
    expect(cfg.server?.port).toBe(3000);
    expect(cfg.ui?.styling?.mode).toBe('jamx-native');
    expect(cfg.security?.csrf).toBe(true);
  });

  it('should respect custom values', () => {
    const cfg = defineConfig({
      targets: ['web', 'ios'],
      server: { port: 8080 },
    });
    expect(cfg.server?.port).toBe(8080);
    expect(cfg.targets).toContain('ios');
  });

  it('ConfigManager should load from object', async () => {
    const manager = new ConfigManager();
    await manager.load({ targets: ['web'] });
    expect(manager.get().server?.port).toBe(3000);
  });

  it('ConfigManager should provide sections', async () => {
    const manager = new ConfigManager();
    await manager.load({
      targets: ['web'],
      server: { port: 5000 },
    });
    const server = manager.section('server');
    expect(server?.port).toBe(5000);
  });
});
```

### Mock de ConfigManager en tests
```typescript
import { ConfigManager } from '@jamx-framework/config';

const mockConfig = new ConfigManager();
await mockConfig.load({
  targets: ['web'],
  server: { port: 0 }, // puerto 0 para tests
  security: { csrf: false }, // deshabilitar CSRF en tests
});
```

## Integración con otros paquetes

### Con `@jamx-framework/server`
```typescript
import { createServer } from '@jamx-framework/server';
import { ConfigManager } from '@jamx-framework/config';

const configManager = new ConfigManager();
await configManager.load('./');

const server = await createServer(configManager.get());
await server.listen();
```

### Con `@jamx-framework/cli`
El CLI carga automáticamente `jamx.config.ts` usando `loadConfig()` para comandos como `build`, `dev`, `db:migrate`, etc.

### Con `@jamx-framework/compiler`
El compilador lee `targets` y otras opciones de la configuración para determinar qué plataformas compilar y qué plugins activar.

## Consideraciones

### Tipos completos
Todos los tipos están definidos en TypeScript, proporcionando autocompletado en IDEs y validación en tiempo de compilación.

### Inmutabilidad
`defineConfig` retorna un nuevo objeto, no modifica el original. `ConfigManager` almacena una copia.

### Carga asíncrona
`loadConfig` y `ConfigManager.load` son asíncronos porque pueden importar módulos ES desde disco.

### Thread-safety
`ConfigManager` no está diseñado para uso concurrente; en un servidor se recomienda crear una instancia global al startup.

### Validación en runtime
La validación es mínima; se asume que el desarrollador usa TypeScript correctamente. Para validación más estricta, usar `zod` o `yup` externamente.

## Ejemplo completo

```typescript
// jamx.config.ts
import { defineConfig } from '@jamx-framework/config';

export default defineConfig({
  targets: ['web', 'ios', 'android'],
  server: {
    port: 3000,
    host: '0.0.0.0',
    cors: {
      enabled: true,
      origins: ['http://localhost:3000', 'https://myapp.com'],
    },
  },
  database: {
    primary: {
      driver: 'postgresql',
      url: process.env.DATABASE_URL!,
    },
  },
  auth: {
    strategies: ['credentials', 'oauth2'],
    session: {
      engine: 'jwt',
      duration: '7d',
      secret: process.env.JWT_SECRET!,
    },
  },
  security: {
    csrf: true,
    rateLimit: {
      enabled: true,
      max: 100,
      window: '1m',
    },
    headers: {
      hsts: true,
      frameOptions: 'DENY',
      contentTypeSniff: true,
    },
  },
  i18n: {
    defaultLocale: 'es',
    locales: ['es', 'en'],
    strategy: 'prefix',
  },
  ui: {
    styling: {
      mode: 'tailwind',
    },
  },
});
```

```typescript
// src/server/index.ts
import { createServer } from '@jamx-framework/server';
import { ConfigManager } from '@jamx-framework/config';

async function start() {
  const configManager = new ConfigManager();
  await configManager.load('./'); // carga jamx.config.ts

  const config = configManager.get();
  const server = await createServer(config);

  const port = config.server?.port ?? 3000;
  await server.listen({ port, host: config.server?.host });
  console.log(`Server running on http://${config.server?.host}:${port}`);
}

start().catch(console.error);
```

## Referencia rápida de defaults

| Propiedad | Default |
|-----------|---------|
| `server.port` | `3000` |
| `server.host` | `"localhost"` |
| `server.cors.enabled` | `false` |
| `server.cors.origins` | `[]` |
| `ui.styling.mode` | `"jamx-native"` |
| `security.csrf` | `true` |
| `security.rateLimit.enabled` | `true` |
| `security.rateLimit.max` | `100` |
| `security.rateLimit.window` | `"1m"` |
| `security.headers.hsts` | `undefined` |
| `security.headers.frameOptions` | `undefined` |
| `security.headers.contentTypeSniff` | `undefined` |

## Preguntas frecuentes

### ¿Puedo usar `defineConfig` en el navegador?
No, `defineConfig` es para build time. En el navegador usa `ConfigManager` con configuración pre-cargada.

### ¿Cómo sobreescribir configuración en producción?
Usa variables de entorno en `jamx.config.ts` o carga un archivo diferente con `ConfigManager.load('/path/to/prod-config.ts')`.

### ¿La configuración es validada en runtime?
Solo se validan tipos TypeScript. Para validación estricta, usa librerías externas como Zod.

### ¿Puedo tener múltiples instancias de ConfigManager?
Sí, pero usualmente se usa una instancia global. Cada instancia mantiene su propia configuración.

### ¿Qué pasa si `loadConfig` no encuentra el archivo?
Lanza un error. Asegúrate de que `jamx.config.ts` exista en el directorio del proyecto.

## Scripts del paquete
- `pnpm build` - Compila TypeScript a JavaScript
- `pnpm dev` - Compilación en watch mode
- `pnpm test` - Ejecuta tests unitarios
- `pnpm test:watch` - Tests en watch mode
- `pnpm type-check` - Verifica tipos sin compilar
- `pnpm clean` - Limpia archivos compilados

## Dependencias
- `@types/node` - Tipos de Node.js para desarrollo
- `typescript` - Compilador TypeScript
- `vitest` - Framework de testing
- `rimraf` - Limpieza de directorios

## Archivos importantes
- `src/index.ts` - Punto de entrada, contiene todos los tipos y exportaciones
- `tests/unit/config.test.ts` - Tests unitarios de `defineConfig` y `ConfigManager`