Module Integrator
Ecosistema completo para organización y gestión de contenido con interfaces espaciales
Librería Module Integrator (Core)
Propósito: orquestador central responsable del ciclo de vida completo de módulos, gestionando auto-registro, activación basada en configuración API, y coordinación de la integración entre módulos en aplicaciones web Angular.
Estado planificado
- Tipo: librería Nx de Angular.
- Ruta de importación:
@libs-core/module-integrator. - Prioridad:
Muy Alta- núcleo del sistema de módulos dinámicos locales en navegador. - Entorno: Cliente web con carga dinámica de módulos locales usando dynamic imports y APIs REST.
Arquitectura del Orquestador Principal
El Module Integrator actúa como el cerebro del sistema, coordinando el auto-registro de módulos, gestionando la activación basada en configuración del API, y orquestando el ciclo de vida completo de todos los módulos. Es responsable de la integración con todas las librerías core del ecosistema.
API pública planificada
Core Services
- IntegratorService: orquestador principal para navegador
- ModuleRegistryService: registro de módulos cargados dinámicamente
- DependencySolverService: resolución de dependencias en tiempo de ejecución
- LifecycleManagerService: gestión del ciclo de vida optimizada para navegador
Local Module Loading Layer
- LocalModuleLoader: carga dinámica de módulos locales compilados
- DynamicImportService: carga lazy usando dynamic imports nativo
- ModuleBundleManager: gestión de chunks locales y code splitting
- ModuleRouteResolver: resolución de rutas lazy para módulos locales
Navigation & UI Integration Layer
- NavigationManagerService: gestión de menús y navegación inter-módulo
- EmbeddableComponentRegistry: registro de componentes embebibles
- ToolbarManagerService: gestión de toolbar y acciones globales
- BreadcrumbService: gestión de breadcrumbs dinámicos
Inter-Module Communication Layer
- ModuleProviderRegistry: registro de servicios/datos que proporcionan módulos
- ModuleConsumerResolver: resolución de dependencias entre módulos
- CrossModuleActionService: ejecución de acciones entre módulos
- AssetSharingService: compartir assets entre módulos
Browser Bootstrap Layer
- BrowserBootstrapper: inicialización optimizada para navegador
- ConfigMergerService: unificación de configuraciones via API
- StartupSequencer: secuenciación basada en prioridades de carga
- ModuleActivator: activación y desactivación de módulos en runtime
Validation Layer
- ContractValidator: validación de contratos en runtime
- DependencyValidator: validación de disponibilidad en navegador
- BrowserCompatibilityChecker: verificación de compatibilidad del navegador
- SecurityValidator: validación de CSP y origen de módulos
Browser Orchestration Layer
- WorkflowCoordinator: coordinación usando Web Workers cuando sea necesario
- AsyncOperationCoordinator: gestión de operaciones asíncronas
- EventCoordinator: coordinación de eventos cross-module
- StateCoordinator: sincronización de estados usando BroadcastChannel
Performance Monitoring Layer
- BrowserPerformanceMonitor: métricas usando Performance API
- MemoryUsageMonitor: monitoreo de uso de memoria
- NetworkMonitor: análisis de carga de recursos de red
- UserExperienceMetrics: métricas de UX (LCP, FID, CLS)
Estructura del paquete
src/
├── lib/
│ ├── core/
│ │ ├── integrator.service.ts
│ │ ├── module-registry.service.ts
│ │ ├── dependency-solver.service.ts
│ │ └── lifecycle-manager.service.ts
│ ├── loaders/
│ │ ├── local-module-loader.ts
│ │ ├── dynamic-import.service.ts
│ │ ├── module-bundle-manager.ts
│ │ └── module-route-resolver.ts
│ ├── navigation/
│ │ ├── navigation-manager.service.ts
│ │ ├── embeddable-component-registry.ts
│ │ ├── toolbar-manager.service.ts
│ │ └── breadcrumb.service.ts
│ ├── communication/
│ │ ├── module-provider-registry.ts
│ │ ├── module-consumer-resolver.ts
│ │ ├── cross-module-action.service.ts
│ │ └── asset-sharing.service.ts
│ ├── bootstrap/
│ │ ├── browser-bootstrapper.ts
│ │ ├── config-merger.service.ts
│ │ ├── startup-sequencer.ts
│ │ └── module-activator.ts
│ ├── validation/
│ │ ├── contract-validator.ts
│ │ ├── dependency-validator.ts
│ │ ├── browser-compatibility-checker.ts
│ │ └── security-validator.ts
│ ├── orchestration/
│ │ ├── workflow-coordinator.ts
│ │ ├── async-operation-coordinator.ts
│ │ ├── event-coordinator.ts
│ │ └── state-coordinator.ts
│ ├── monitoring/
│ │ ├── browser-performance-monitor.ts
│ │ ├── memory-usage-monitor.ts
│ │ ├── network-monitor.ts
│ │ └── ux-metrics.service.ts
│ ├── models/
│ │ ├── module-manifest.ts
│ │ ├── dependency-graph.ts
│ │ ├── browser-lifecycle-state.ts
│ │ ├── local-module-config.ts
│ │ ├── navigation-config.ts
│ │ ├── embeddable-component.ts
│ │ └── performance-metrics.ts
│ ├── workers/
│ │ ├── module-loader.worker.ts
│ │ ├── dependency-resolver.worker.ts
│ │ └── performance-analyzer.worker.ts
│ └── utils/
│ ├── local-module-resolver.ts
│ ├── browser-feature-detector.ts
│ ├── route-lazy-loader.ts
│ ├── component-embedder.ts
│ └── module-cache-manager.ts
└── index.ts
Funcionalidad clave
1. Carga dinámica de módulos locales
// Carga dinámica usando dynamic imports locales
@Injectable()
export class IntegratorService {
async loadModule(moduleId: string): Promise<ModuleInstance> {
// 1. Validar permisos y contratos del módulo
await this.securityValidator.validateModulePermissions(moduleId);
// 2. Resolver dependencias en tiempo de ejecución
const dependencies = await this.dependencySolver.resolveLocalDependencies(moduleId);
// 3. Cargar dependencias usando dynamic imports
await this.loadDependenciesParallel(dependencies);
// 4. Cargar módulo local via dynamic import
const moduleInstance = await this.localModuleLoader.loadLocalModule(moduleId);
// 5. Inicializar en el contexto del navegador
await this.lifecycleManager.initializeInBrowser(moduleInstance);
// 6. Registrar y configurar rutas lazy
this.moduleRegistry.register(moduleInstance);
await this.routerService.registerLazyRoutes(moduleInstance.routes);
return moduleInstance;
}
}
2. Local Module Loader
// Cargador de módulos locales compilados
@Injectable()
export class LocalModuleLoader {
async loadLocalModule(moduleId: string): Promise<ModuleInstance> {
const config = await this.getLocalModuleConfig(moduleId);
try {
// Cargar módulo local usando dynamic import
const moduleEntry = await this.resolveLazyRoute(config.lazyRoute);
const moduleFactory = await import(moduleEntry);
// Crear instancia del módulo
const moduleInstance = await moduleFactory.default();
return this.wrapModuleInstance(moduleInstance, config);
} catch (error) {
// Log error y propagar
this.logger.error(`Failed to load local module ${moduleId}:`, error);
throw new ModuleLoadError(moduleId, error);
}
}
private async resolveLazyRoute(lazyRoute: string): Promise<string> {
// Resolver ruta lazy basada en configuración de routing
const routeConfig = await this.routeResolver.resolve(lazyRoute);
// Validar que el chunk existe en el bundle
if (!(await this.bundleManager.chunkExists(routeConfig.chunkName))) {
throw new Error(`Chunk ${routeConfig.chunkName} not found`);
}
return routeConfig.importPath;
}
}
3. Configuración dinámica via API
// Configuración desde APIs REST
@Injectable()
export class ConfigMergerService {
async loadAndMergeConfigs(moduleIds: string[]): Promise<ApplicationConfig> {
// Cargar configuraciones desde API
const configPromises = moduleIds.map((id) => this.apiClient.get<ModuleConfig>(`/api/modules/${id}/config`));
const configs = await Promise.all(configPromises);
// Merge inteligente con validación
return configs.reduce((merged, config) => {
return this.deepMergeWithValidation(merged, config, {
conflictStrategy: 'api-priority',
validateSchema: true,
sanitizeInputs: true,
});
}, {} as ApplicationConfig);
}
private deepMergeWithValidation(target: any, source: any, options: MergeOptions): any {
// Validar esquema antes de merge
this.configValidator.validate(source, options.schema);
// Sanitizar inputs para prevenir XSS
const sanitizedSource = this.sanitizer.sanitize(source);
// Merge recursivo con detección de conflictos
return this.performDeepMerge(target, sanitizedSource, options);
}
}
4. Coordinación con Web Workers
// Coordinación usando Web Workers para operaciones pesadas
@Injectable()
export class EventCoordinator {
private workerPool: WorkerPool;
async coordinateEvent(event: SystemEvent): Promise<void> {
const interestedModules = this.findInterestedModules(event);
// Para eventos pesados, usar Web Workers
if (this.isHeavyEvent(event)) {
return this.coordinateInWorker(event, interestedModules);
}
// Para eventos ligeros, ejecutar en main thread
return this.coordinateInMainThread(event, interestedModules);
}
private async coordinateInWorker(event: SystemEvent, modules: ModuleInstance[]): Promise<void> {
const worker = await this.workerPool.getAvailableWorker();
return new Promise((resolve, reject) => {
worker.postMessage({
type: 'COORDINATE_EVENT',
event: this.serializeEvent(event),
modules: modules.map((m) => m.id),
});
worker.onmessage = (result) => {
if (result.data.success) {
resolve(result.data.results);
} else {
reject(new Error(result.data.error));
}
};
});
}
private async coordinateInMainThread(event: SystemEvent, modules: ModuleInstance[]): Promise<void> {
// Usar requestIdleCallback para no bloquear UI
return new Promise((resolve) => {
requestIdleCallback(async () => {
const results = await Promise.allSettled(modules.map((module) => this.executeEventHandler(module, event)));
await this.handleResults(results, event);
resolve();
});
});
}
}
5. Navegación y UI inter-módulo
// Gestor de navegación que integra múltiples módulos
@Injectable({ providedIn: 'root' })
export class NavigationManagerService {
private readonly navigationItems = signal<NavigationMenuItem[]>([]);
private readonly toolbarActions = signal<ToolbarAction[]>([]);
// Computed signals para navegación organizada
readonly mainMenuItems = computed(() =>
this.navigationItems()
.filter((item) => !item.parent)
.sort((a, b) => a.order - b.order)
);
readonly globalToolbarActions = computed(() =>
this.toolbarActions()
.filter((action) => action.position === 'global')
.sort((a, b) => a.order - b.order)
);
constructor(private router = inject(Router), private authService = inject(AuthService)) {}
// Registrar navegación de un módulo
registerModuleNavigation(moduleId: string, navigation: ModuleNavigationConfig): void {
// Registrar items de menú
navigation.menuItems.forEach((item) => {
if (this.hasPermission(item.permissions)) {
this.navigationItems.update((items) => [...items, { ...item, moduleId }]);
}
});
// Registrar acciones de toolbar
navigation.toolbar?.forEach((action) => {
if (this.hasPermission(action.permissions)) {
this.toolbarActions.update((actions) => [...actions, { ...action, moduleId }]);
}
});
}
// Ejemplo: Navegación del módulo de productos
private registerProductsNavigation(): void {
this.registerModuleNavigation('products', {
menuItems: [
{
id: 'products-main',
label: 'Productos',
icon: 'inventory',
route: '/products',
order: 10,
permissions: ['products:read'],
badge: () => this.getProductCount(),
},
{
id: 'products-create',
label: 'Nuevo Producto',
icon: 'add',
route: '/products/create',
parent: 'products-main',
order: 1,
permissions: ['products:write'],
},
],
});
}
private hasPermission(permissions?: string[]): boolean {
if (!permissions) return true;
return permissions.some((p) => this.authService.hasPermission(p));
}
private getProductCount(): Observable<number> {
// Retorna observable con count de productos
return this.moduleProviderRegistry.get('products', 'product-count') || of(0);
}
}
6. Registro de componentes embebibles
// Registro de componentes que pueden ser embebidos entre módulos
@Injectable({ providedIn: 'root' })
export class EmbeddableComponentRegistry {
private readonly registeredComponents = signal<Map<string, ModuleEmbeddableComponent>>(new Map());
// Computed signals para componentes por categoría
readonly widgetComponents = computed(() => Array.from(this.registeredComponents().values()).filter((comp) => comp.category === 'widget'));
readonly dashboardComponents = computed(() => Array.from(this.registeredComponents().values()).filter((comp) => comp.category === 'dashboard'));
// Registrar componente embebible
registerComponent(moduleId: string, component: ModuleEmbeddableComponent): void {
const key = `${moduleId}:${component.id}`;
this.registeredComponents.update((components) => {
const newMap = new Map(components);
newMap.set(key, { ...component, moduleId });
return newMap;
});
}
// Crear instancia de componente embebible
async createEmbeddableComponent(componentId: string, inputs?: Record<string, any>): Promise<ComponentRef<any>> {
const component = this.getComponent(componentId);
if (!component) {
throw new Error(`Component ${componentId} not found`);
}
// Usar createComponent API de Angular 20
const componentRef = await createComponent(component.componentType, {
environmentInjector: this.injector,
});
// Configurar inputs si existen
if (inputs && componentRef.setInput) {
Object.entries(inputs).forEach(([key, value]) => {
componentRef.setInput(key, value);
});
}
return componentRef;
}
// Obtener componentes disponibles para un dashboard
getAvailableWidgetsForDashboard(): ModuleEmbeddableComponent[] {
return this.widgetComponents().filter((comp) => this.authService.hasPermissions(comp.permissions));
}
// Ejemplo: Usar widget de productos en dashboard
async embedProductListInDashboard(dashboardContainer: ViewContainerRef, config: { category?: string; limit?: number }): Promise<ComponentRef<any>> {
const productWidget = await this.createEmbeddableComponent('products:product-list-widget', {
category: config.category,
limit: config.limit || 5,
title: 'Productos Destacados',
});
dashboardContainer.insert(productWidget.hostView);
return productWidget;
}
}
7. Comunicación cross-module
// Servicio para comunicación entre módulos
@Injectable({ providedIn: 'root' })
export class CrossModuleActionService {
constructor(private actionRegistry = inject(ActionRegistryService), private moduleRegistry = inject(ModuleRegistryService), private assetSharing = inject(AssetSharingService)) {}
// Ejemplo: CMS ejecutando acción de productos
async executeCrossModuleAction(fromModuleId: string, targetModuleId: string, actionId: string, parameters: any): Promise<any> {
// Verificar que el módulo target esté cargado
const targetModule = this.moduleRegistry.getModule(targetModuleId);
if (!targetModule) {
throw new Error(`Target module ${targetModuleId} not loaded`);
}
// Verificar permisos cross-module
await this.validateCrossModulePermissions(fromModuleId, targetModuleId, actionId);
// Ejecutar acción
const fullActionId = `${targetModuleId}.${actionId}`;
return this.actionRegistry.execute(fullActionId, parameters);
}
// Ejemplo específico: CMS usando acción de compra de productos
async purchaseProductFromCms(productId: string, cmsPostId: string): Promise<any> {
return this.executeCrossModuleAction('cms', 'products', 'purchase-product', {
productId,
referrer: `cms:${cmsPostId}`,
context: 'cms-integration',
});
}
// Obtener datos de otro módulo
async getDataFromModule<T>(moduleId: string, providerId: string, parameters?: any): Promise<T> {
const provider = this.moduleProviderRegistry.getProvider(moduleId, providerId);
if (!provider) {
throw new Error(`Provider ${providerId} not found in module ${moduleId}`);
}
if (provider.type === 'service') {
const service = this.injector.get(provider.implementation);
return service.getData(parameters);
} else if (provider.type === 'data') {
return provider.implementation(parameters);
}
throw new Error(`Unsupported provider type: ${provider.type}`);
}
}
8. Manejo de assets entre módulos
// Servicio para compartir assets entre módulos
@Injectable({ providedIn: 'root' })
export class AssetSharingService {
private readonly assetProviders = signal<Map<string, AssetProvider>>(new Map());
// Registrar proveedor de assets de un módulo
registerAssetProvider(moduleId: string, provider: AssetProvider): void {
this.assetProviders.update((providers) => {
const newMap = new Map(providers);
newMap.set(moduleId, provider);
return newMap;
});
}
// Obtener URL de asset de cualquier módulo
getAssetUrl(moduleId: string, assetId: string, options?: AssetOptions): string {
const provider = this.assetProviders().get(moduleId);
if (!provider) {
throw new Error(`Asset provider for module ${moduleId} not found`);
}
return provider.getAssetUrl(assetId, options);
}
// Ejemplo: CMS usando imágenes del módulo de productos
getProductImageForCms(productId: string, size: 'thumbnail' | 'medium' | 'large' = 'medium'): string {
return this.getAssetUrl('products', `product-${productId}`, { size });
}
// Ejemplo: Dashboard mostrando assets de múltiples módulos
getModuleAssets(moduleId: string, category?: string): Observable<Asset[]> {
const provider = this.assetProviders().get(moduleId);
if (!provider) {
return of([]);
}
return provider.getAssets({ category });
}
}
Integración con el ecosistema
Dependencias principales
- @libs-core/module-base: contratos y estructuras base de módulos
- @libs-core/action-registry: registro y ejecución de acciones
- @libs-core/data-access: acceso a datos y estado
- @libs-core/event-system: sistema de eventos del núcleo
- @libs-core/log-system: logging y auditoría
- Seguridad (futura): autenticación y autorización
Interacción con Action Registry
// Registro automático de acciones de módulos
async initializeModule(module: ModuleInstance): Promise<void> {
// Descubrir y registrar acciones del módulo
const actions = await this.actionDiscovery.discover(module);
await this.actionRegistry.registerBatch(actions);
// Configurar permisos y metadata
await this.setupActionSecurity(actions, module.permissions);
}
Interacción con Data Access
// Configuración de contexto de datos por módulo
async setupDataContext(module: ModuleInstance): Promise<void> {
// Configurar tenant context específico
const tenantConfig = module.manifest.tenantConfig;
await this.tenantContext.configure(module.id, tenantConfig);
// Registrar repositorios y estados NGXS
await this.dataAccess.registerModuleRepositories(module);
await this.stateManager.registerModuleStates(module);
}
Ciclo de vida de módulos
Estados del ciclo de vida
- DISCOVERING: descubrimiento de módulo y manifest
- VALIDATING: validación de contratos y dependencias
- LOADING: carga de código del módulo
- INITIALIZING: inicialización de servicios
- CONFIGURING: configuración y merge de config
- STARTING: arranque de funcionalidades
- RUNNING: estado activo y operacional
- STOPPING: parada controlada
- UNLOADING: descarga de memoria
- ERROR: estado de error recuperable
Transiciones de estado
@Injectable()
export class LifecycleManagerService {
async transitionTo(moduleId: string, targetState: LifecycleState): Promise<void> {
const currentState = this.getModuleState(moduleId);
const transitions = this.getValidTransitions(currentState, targetState);
for (const transition of transitions) {
await this.executeTransition(moduleId, transition);
await this.validateTransition(moduleId, transition);
}
}
}
Patrones de integración
1. Plugin Architecture
// Extensibilidad a través de plugins
interface IntegratorPlugin {
name: string;
hooks: {
beforeModuleLoad?: (manifest: ModuleManifest) => Promise<void>;
afterModuleLoad?: (module: ModuleInstance) => Promise<void>;
onModuleError?: (error: ModuleError) => Promise<void>;
};
}
2. Micro-frontend Support
// Soporte para micro-frontends
class MicrofrontendLoader implements ModuleLoader {
async load(manifest: ModuleManifest): Promise<ModuleInstance> {
if (manifest.type === 'microfrontend') {
return this.loadRemoteModule(manifest.remoteEntry);
}
return this.loadLocalModule(manifest.entry);
}
}
Monitoreo y observabilidad
Métricas clave
- Tiempo de carga de módulos
- Uso de memoria por módulo
- Errores y reintentos
- Dependencias más utilizadas
- Performance de transacciones distribuidas
Health checks
@Injectable()
export class HealthCheckService {
async checkModuleHealth(moduleId: string): Promise<HealthStatus> {
const checks = [this.checkMemoryUsage(moduleId), this.checkResponseTime(moduleId), this.checkDependencies(moduleId), this.checkEventProcessing(moduleId)];
return this.aggregateResults(await Promise.all(checks));
}
}
Comandos de mantenimiento
- Ejecutar pruebas:
nx test module-integrator - Linting:
nx lint module-integrator - Build:
nx build module-integrator
Casos de uso principales
Casos generales:
- Carga dinámica: cargar módulos bajo demanda basado en rutas o permisos
- Hot reload: actualizar módulos sin reiniciar la aplicación
- A/B Testing: cargar diferentes versiones de módulos
- Feature flags: habilitar/deshabilitar módulos dinámicamente
- Multi-tenancy: configuración específica de módulos por tenant
Casos específicos implementados:
Módulo de Productos:
- ✅ Navegación automática: Registro de menús "Productos" y "Nuevo Producto" en navegación principal
- ✅ Rutas refinadas: Soporte para
/products/create,/products/:id/edit, etc. - ✅ Embedding en dashboard: Widget
product-list-widgetembebible en otros módulos - ✅ Assets compartidos: Imágenes de productos accesibles desde otros módulos
- ✅ Acciones públicas:
purchase-productyadd-to-cartejecutables desde cualquier módulo
Módulo CMS:
- ✅ Creación global: Toolbar
quick-postpara crear entradas desde cualquier lugar - ✅ Navegación contextual: Menús dinámicos basados en permisos
- ✅ Assets cross-module: Uso de imágenes del módulo de productos en posts
- ✅ Acciones integradas: Ejecutar
purchase-productdesde contenido CMS - ✅ Widgets embebibles:
post-creator-widgetdisponible en cualquier módulo
Flujos de trabajo específicos:
Ejemplo 1: Dashboard con productos
// Dashboard carga widget de productos automáticamente
async loadDashboard() {
const productWidget = await this.embeddableRegistry.embedProductListInDashboard(
this.dashboardContainer,
{ category: 'featured', limit: 6 }
);
// Widget muestra productos con imágenes del módulo de assets
// Incluye botones de compra que ejecutan acciones del módulo productos
}
Ejemplo 2: CMS referenciando productos
// Editor CMS puede insertar referencias a productos
async insertProductReference(productId: string) {
// Obtener datos del producto desde el módulo productos
const product = await this.crossModuleAction.getDataFromModule(
'products',
'product-data-service',
{ productId }
);
// Obtener imagen del producto
const imageUrl = this.assetSharing.getProductImageForCms(productId, 'medium');
// Insertar en editor con acción de compra
const productHTML = `
<div class="product-reference">
<img src="${imageUrl}" alt="${product.name}" />
<button onclick="purchaseProduct('${productId}')">Comprar ${product.name}</button>
</div>
`;
}
Ejemplo 3: Navegación contextual
// Navegación se actualiza automáticamente según módulos cargados
const navigation = {
// Productos (si tiene permisos)
Productos: [
'Ver Productos', // /products
'Nuevo Producto', // /products/create
'Categorías', // /products/categories
],
// CMS (si tiene permisos)
Contenido: [
'Entradas', // /cms/posts
'Nueva Entrada', // /cms/posts/create
'Páginas', // /cms/pages
],
};
Consideraciones de diseño para navegador
- Browser Performance: optimización para Main Thread y evitar bloqueos de UI
- Memory Management: gestión eficiente de memoria con descarga automática de módulos
- Network Optimization: carga inteligente con prefetch y cache estratégico
- Security & CSP: validación de Content Security Policy y origen de módulos
- Progressive Loading: carga progresiva basada en visibilidad y prioridad
- Offline Capability: funcionamiento degradado sin conectividad
- Bundle Optimization: tree-shaking y code-splitting automático
- Browser Compatibility: soporte para navegadores modernos con fallbacks
Estrategias avanzadas
Circuit Breaker Pattern
// Protección contra fallos en cascada
class ModuleCircuitBreaker {
async execute<T>(moduleId: string, operation: () => Promise<T>): Promise<T> {
const state = this.getCircuitState(moduleId);
if (state === 'OPEN') {
throw new CircuitOpenError(moduleId);
}
try {
const result = await operation();
this.recordSuccess(moduleId);
return result;
} catch (error) {
this.recordFailure(moduleId);
throw error;
}
}
}
Notas de implementación para navegador (módulos locales)
- Local Dynamic Imports: uso de import() nativo para carga lazy de módulos compilados localmente
- Angular Lazy Loading: integración con sistema de rutas lazy de Angular
- Code Splitting: configuración automática de chunks por módulo
- Web Workers: procesamiento pesado sin bloquear Main Thread
- Service Workers: cache avanzado y funcionamiento offline
- Performance API: métricas detalladas de carga y rendimiento
- Intersection Observer: carga basada en visibilidad de componentes
- Idle Callbacks: ejecución de tareas en momentos de baja actividad
- Memory API: monitoreo y optimización de uso de memoria
- IndexedDB: persistencia local de configuraciones y cache
- BroadcastChannel: comunicación entre pestañas del mismo origen
- MutationObserver: detección de cambios DOM para optimización
- ResizeObserver: adaptación responsiva de módulos cargados
- Webpack Bundle Analysis: análisis de chunks y optimización de bundles locales