@jamx-framework/config
v2.0.0
Published
JAMX Framework — centralized configuration
Maintainers
Readme
@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:
- Tipos: Definiciones TypeScript completas para todas las opciones de configuración
- defineConfig: Función que aplica valores por defecto y valida la estructura
- ConfigManager: Clase para cargar, almacenar y acceder a la configuración en runtime
- loadConfig: Función asíncrona que carga
jamx.config.tsdesde 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ónJamxConfig: Interfaz principal de configuraciónDatabaseConnection: Configuración de conexión a BD
Funciones
defineConfig
function defineConfig(config: JamxConfig): JamxConfigAplica defaults y retorna una configuración validada.
loadConfig
async function loadConfig(projectRoot: string): Promise<JamxConfig>Carga jamx.config.ts desde el sistema de archivos.
Clase ConfigManager
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
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
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
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
// 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
type Target = "web" | "ios" | "android" | "desktop";Plataformas de compilación objetivo.
StylingMode
type StylingMode = "jamx-native" | "tailwind" | "external" | "none";Modo de estilos de la aplicación.
DatabaseDriver
type DatabaseDriver = "postgresql" | "mysql" | "sqlite";Drivers de base de datos soportados.
SessionEngine
type SessionEngine = "jwt" | "database" | "redis";Motor de gestión de sesiones.
AuthStrategy
type AuthStrategy =
| "credentials"
| "oauth2"
| "magic-link"
| "passkey"
| "saml";Estrategias de autenticación disponibles.
JamxConfig
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
interface DatabaseConnection {
driver: DatabaseDriver;
url: string;
}Funciones
defineConfig
function defineConfig(config: JamxConfig): JamxConfigAplica valores por defecto a la configuración y la retorna validada.
Defaults aplicados:
targets: Requerido, sin defaultserver.port: 3000server.host: "localhost"server.cors.enabled: falseserver.cors.origins: []ui.styling.mode: "jamx-native"security.csrf: truesecurity.rateLimit.enabled: truesecurity.rateLimit.max: 100security.rateLimit.window: "1m"
loadConfig
async function loadConfig(projectRoot: string): Promise<JamxConfig>Carga la configuración desde jamx.config.ts en projectRoot.
Comportamiento:
- Busca
jamx.config.tsenprojectRoot - 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
defineConfigpara aplicar defaults
Ejemplo:
// jamx.config.ts
export default {
targets: ['web'],
server: { port: 8080 },
};
// En código
const config = await loadConfig('/path/to/project');
// config.server.port === 8080Clase ConfigManager
Constructor
new ConfigManager()Crea un gestor vacío.
load
async load(source?: JamxConfig | string): Promise<JamxConfig>Carga la configuración desde:
- Objeto
JamxConfig: aplicadefineConfig - Ruta de proyecto (string): llama a
loadConfig(source) - Sin argumentos: crea config mínima con
{ targets: ['web'] }
get
get(): JamxConfigRetorna la configuración cargada. Lanza error si no se ha llamado load() antes.
section
section<K extends keyof JamxConfig>(key: K): JamxConfig[K]Accede a una sección específica de la configuración.
Ejemplo:
const server = configManager.section('server');
const port = server?.port;Getters de conveniencia
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:
targets: ['web']; // solo web
targets: ['web', 'ios', 'android']; // multiplataformaserver
Configuración del servidor de desarrollo/producción:
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:
ui: {
styling: {
mode: 'tailwind', // 'jamx-native' | 'tailwind' | 'external' | 'none'
},
}database
Configuración de bases de datos:
database: {
primary: {
driver: 'postgresql', // 'postgresql' | 'mysql' | 'sqlite'
url: 'postgresql://...',
},
// conexiones adicionales
analytics: {
driver: 'mysql',
url: 'mysql://...',
},
}auth
Configuración de autenticación:
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:
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:
i18n: {
defaultLocale: 'es',
locales: ['es', 'en', 'fr'],
strategy: 'prefix', // 'prefix' | 'prefix-except-default' | 'domain'
}plugins
Array de configuraciones de plugins:
plugins: [
{ name: 'my-plugin', options: { ... } },
{ name: 'another-plugin', options: { ... } },
]Flujo de carga de configuración
1. Desde archivo (recomendado)
// 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
const manager = new ConfigManager();
await manager.load({
targets: ['web', 'ios'],
server: { port: 8080 },
});
const config = manager.get();3. Directo con defineConfig
import { defineConfig } from '@jamx-framework/config';
const config = defineConfig({
targets: ['web'],
});
// config ya tiene defaults aplicadosValidación y defaults
Validación
defineConfigvalida quetargetsesté 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:
- Nivel raíz:
targetses requerido, no tiene default - Nivel objeto: Si
serverexiste, se aplican defaults a sus propiedades - Nivel propiedad: Cada propiedad tiene su propio default
Ejemplo:
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
// 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
// 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
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
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
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
// 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',
},
},
});// 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 JavaScriptpnpm dev- Compilación en watch modepnpm test- Ejecuta tests unitariospnpm test:watch- Tests en watch modepnpm type-check- Verifica tipos sin compilarpnpm clean- Limpia archivos compilados
Dependencias
@types/node- Tipos de Node.js para desarrollotypescript- Compilador TypeScriptvitest- Framework de testingrimraf- Limpieza de directorios
Archivos importantes
src/index.ts- Punto de entrada, contiene todos los tipos y exportacionestests/unit/config.test.ts- Tests unitarios dedefineConfigyConfigManager
