wu-framework 2.5.0 → 2.6.0

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,4 +1,4 @@
1
- /*! wu-framework v2.5.0 | MIT License */
1
+ /*! wu-framework v2.6.0 | MIT License */
2
2
  import { logger } from './core/wu-logger.js';
3
3
  export { enableAllLogs, silenceAllLogs } from './core/wu-logger.js';
4
4
  export { WuLoader } from './core/wu-loader.js';
@@ -7400,6 +7400,11 @@ class WuCore {
7400
7400
  // Estado
7401
7401
  this.isInitialized = false;
7402
7402
 
7403
+ // RBAC: principal actual (quién usa el sistema). Las apps que declaran
7404
+ // `roles` (en su config o manifest) solo se montan si el principal está
7405
+ // autorizado. null = sin principal (las apps restringidas se niegan).
7406
+ this._principal = null;
7407
+
7403
7408
  logger.wuInfo('Wu Framework initialized - Universal Microfrontends');
7404
7409
  }
7405
7410
 
@@ -7495,6 +7500,63 @@ class WuCore {
7495
7500
  }
7496
7501
  }
7497
7502
 
7503
+ // ── RBAC: aislamiento rol↔módulo (declarativo, enforced en mount) ──────────
7504
+
7505
+ /**
7506
+ * Fija el principal actual (rol/permisos del usuario). Las apps que declaran
7507
+ * `roles` solo se montan si el principal está autorizado. Se refleja también
7508
+ * en wu.store('auth.principal') para que apps/DevTools lo lean.
7509
+ * @param {{role?: string, roles?: string[], permissions?: string[]}|null} principal
7510
+ * @returns {Object|null} el principal efectivo
7511
+ */
7512
+ setPrincipal(principal) {
7513
+ this._principal = principal || null;
7514
+ try { this.store.set('auth.principal', this._principal); } catch { /* store opcional */ }
7515
+ this.eventBus.emit('principal:changed', { principal: this._principal });
7516
+ return this._principal;
7517
+ }
7518
+
7519
+ /** @returns {Object|null} el principal actual */
7520
+ getPrincipal() {
7521
+ return this._principal;
7522
+ }
7523
+
7524
+ /** Roles permitidos para montar `appName` (de config o manifest), o null si es público. */
7525
+ _requiredRoles(appName) {
7526
+ const app = this.apps.get(appName);
7527
+ const manifest = this.manifests.get(appName);
7528
+ const roles = app?.roles ?? manifest?.wu?.roles ?? manifest?.roles ?? null;
7529
+ return Array.isArray(roles) && roles.length ? roles : null;
7530
+ }
7531
+
7532
+ /** Roles que porta el principal actual (role + roles[]). */
7533
+ _principalRoles() {
7534
+ const p = this._principal;
7535
+ if (!p) return [];
7536
+ const out = [];
7537
+ if (typeof p.role === 'string') out.push(p.role);
7538
+ if (Array.isArray(p.roles)) out.push(...p.roles);
7539
+ return out;
7540
+ }
7541
+
7542
+ /**
7543
+ * ¿Puede el principal actual montar/acceder a `appName`?
7544
+ * App pública (sin `roles`) → siempre true. App restringida → requiere un
7545
+ * principal cuyos roles intersecten, o un permiso `mount:*` / `mount:<app>`.
7546
+ * @param {string} appName
7547
+ * @returns {boolean}
7548
+ */
7549
+ can(appName) {
7550
+ const required = this._requiredRoles(appName);
7551
+ if (!required) return true; // módulo público
7552
+ const p = this._principal;
7553
+ if (!p) return false; // restringido y sin principal
7554
+ const perms = Array.isArray(p.permissions) ? p.permissions : [];
7555
+ if (perms.includes('mount:*') || perms.includes(`mount:${appName}`)) return true;
7556
+ const roles = this._principalRoles();
7557
+ return roles.some((r) => required.includes(r));
7558
+ }
7559
+
7498
7560
  /**
7499
7561
  * Definir lifecycle de una micro-app
7500
7562
  * @param {string} appName - Nombre de la app
@@ -7533,6 +7595,27 @@ class WuCore {
7533
7595
  * @param {string} containerSelector - Selector del contenedor
7534
7596
  */
7535
7597
  async mount(appName, containerSelector) {
7598
+ // ── RBAC: negar el montaje de módulos para los que el principal no está
7599
+ // autorizado. Se chequea ANTES del ref-counting → un deny es un early-throw
7600
+ // limpio, sin efectos. Emite `access:denied` (bus) y `wu:access:denied` (DOM)
7601
+ // para que el shell muestre un fallback. NOTA: es enforcement client-side —
7602
+ // mejora corrección/UX; la barrera dura es el server (no servir el bundle).
7603
+ if (!this.can(appName)) {
7604
+ const required = this._requiredRoles(appName) || [];
7605
+ const role = this._principal?.role ?? null;
7606
+ this.eventBus.emit('access:denied', { appName, role, required }, { appName });
7607
+ try {
7608
+ window.dispatchEvent(new CustomEvent('wu:access:denied', { detail: { appName, role, required } }));
7609
+ } catch { /* no-DOM env */ }
7610
+ const err = new Error(
7611
+ `[Wu] Access denied: el rol '${role ?? 'ninguno'}' no puede montar '${appName}' (requiere: ${required.join(', ') || 'un principal'})`,
7612
+ );
7613
+ err.code = 'WU_ACCESS_DENIED';
7614
+ err.appName = appName;
7615
+ err.required = required;
7616
+ throw err;
7617
+ }
7618
+
7536
7619
  // ── StrictMode + Suspense compat: reference counting ──
7537
7620
  // Old approach was a single 60ms grace timer cancelled by the second
7538
7621
  // mount(). That works for one mount→unmount→mount cycle but breaks
@@ -9445,9 +9528,9 @@ if (typeof window !== 'undefined') {
9445
9528
  // Augment the instance everywhere (browser AND Node/SSR) — the d.ts declares
9446
9529
  // these members unconditionally on WuCore, so they must exist in any runtime.
9447
9530
  if (!wu.version) {
9448
- // "2.5.0" is replaced at build time with package.json version.
9531
+ // "2.6.0" is replaced at build time with package.json version.
9449
9532
  // Fallback handles raw-source imports (no bundler).
9450
- wu.version = "2.5.0" ;
9533
+ wu.version = "2.6.0" ;
9451
9534
  wu.info = {
9452
9535
  name: 'Wu Framework',
9453
9536
  description: 'Universal Microfrontends',
@@ -9646,6 +9729,12 @@ const useHook = (phase, middleware, opts) => wu.hooks.use(phase, middleware, opt
9646
9729
  const provide = (name, impl, opts) => wu.contracts.provide(name, impl, opts);
9647
9730
  const consume = (name, range, opts) => wu.contracts.consume(name, range, opts);
9648
9731
 
9732
+ // RBAC: aislamiento rol↔módulo (v2.6+). `setPrincipal` fija quién usa el sistema;
9733
+ // las apps que declaran `roles` solo se montan si el principal está autorizado.
9734
+ const setPrincipal = (principal) => wu.setPrincipal(principal);
9735
+ const getPrincipal = () => wu.getPrincipal();
9736
+ const can = (appName) => wu.can(appName);
9737
+
9649
9738
  // --- AI subsystem ---
9650
9739
  // AI classes were previously re-exported from this entry point. Doing so anchored
9651
9740
  // the ~80 KB AI chunk to the main bundle. As of v2.0, AI is loaded lazily via the
@@ -9658,5 +9747,5 @@ const consume = (name, range, opts) => wu.contracts.consume(name, range, opts);
9658
9747
  //
9659
9748
  // For runtime use, prefer `wu.ai.*` — it auto-loads the chunk on first method call.
9660
9749
 
9661
- export { WuApp, WuCache, WuContracts, WuCore, WuErrorBoundary, WuEventBus, WuLifecycleHooks, WuLoadingStrategy, WuManifest, WuOverrides, WuPerformance, WuPluginSystem, WuPrefetch, WuProxySandbox, WuSandbox, WuStore, WuStyleBridge, app, clearOverrides, compareVersions, consume, createConditionalHook, createGuardHook, createPlugin, createSimpleHook, createTimedHook, createTransformHook, wu_default as default, define, destroy, emit, endMeasure, generatePerformanceReport, getOverrides, getState, hide, init, isHidden, mount, normalizeStyleMode, off, on, onStateChange, once, override, parseVersion, prefetch, prefetchAll, provide, removeOverride, satisfies, setState, show, startMeasure, store, syncStore, unmount, useHook, usePlugin, wu };
9750
+ export { WuApp, WuCache, WuContracts, WuCore, WuErrorBoundary, WuEventBus, WuLifecycleHooks, WuLoadingStrategy, WuManifest, WuOverrides, WuPerformance, WuPluginSystem, WuPrefetch, WuProxySandbox, WuSandbox, WuStore, WuStyleBridge, app, can, clearOverrides, compareVersions, consume, createConditionalHook, createGuardHook, createPlugin, createSimpleHook, createTimedHook, createTransformHook, wu_default as default, define, destroy, emit, endMeasure, generatePerformanceReport, getOverrides, getPrincipal, getState, hide, init, isHidden, mount, normalizeStyleMode, off, on, onStateChange, once, override, parseVersion, prefetch, prefetchAll, provide, removeOverride, satisfies, setPrincipal, setState, show, startMeasure, store, syncStore, unmount, useHook, usePlugin, wu };
9662
9751
  //# sourceMappingURL=wu-framework.dev.js.map