wu-framework 1.1.6 → 1.1.8

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 (90) hide show
  1. package/README.md +511 -977
  2. package/dist/wu-framework.cjs.js +3 -1
  3. package/dist/wu-framework.cjs.js.map +1 -0
  4. package/dist/wu-framework.dev.js +7533 -2761
  5. package/dist/wu-framework.dev.js.map +1 -1
  6. package/dist/wu-framework.esm.js +3 -0
  7. package/dist/wu-framework.esm.js.map +1 -0
  8. package/dist/wu-framework.umd.js +3 -1
  9. package/dist/wu-framework.umd.js.map +1 -0
  10. package/integrations/astro/README.md +127 -0
  11. package/integrations/astro/WuApp.astro +63 -0
  12. package/integrations/astro/WuShell.astro +39 -0
  13. package/integrations/astro/index.js +68 -0
  14. package/integrations/astro/package.json +38 -0
  15. package/integrations/astro/types.d.ts +53 -0
  16. package/package.json +94 -74
  17. package/src/adapters/angular/ai.js +30 -0
  18. package/src/adapters/angular/index.d.ts +154 -0
  19. package/src/adapters/angular/index.js +932 -0
  20. package/src/adapters/angular.d.ts +3 -154
  21. package/src/adapters/angular.js +3 -813
  22. package/src/adapters/index.js +35 -24
  23. package/src/adapters/lit/ai.js +20 -0
  24. package/src/adapters/lit/index.d.ts +120 -0
  25. package/src/adapters/lit/index.js +721 -0
  26. package/src/adapters/lit.d.ts +3 -120
  27. package/src/adapters/lit.js +3 -726
  28. package/src/adapters/preact/ai.js +33 -0
  29. package/src/adapters/preact/index.d.ts +108 -0
  30. package/src/adapters/preact/index.js +661 -0
  31. package/src/adapters/preact.d.ts +3 -108
  32. package/src/adapters/preact.js +3 -665
  33. package/src/adapters/react/ai.js +135 -0
  34. package/src/adapters/react/index.d.ts +246 -0
  35. package/src/adapters/react/index.js +689 -0
  36. package/src/adapters/react.d.ts +3 -212
  37. package/src/adapters/react.js +3 -513
  38. package/src/adapters/shared.js +64 -0
  39. package/src/adapters/solid/ai.js +32 -0
  40. package/src/adapters/solid/index.d.ts +101 -0
  41. package/src/adapters/solid/index.js +586 -0
  42. package/src/adapters/solid.d.ts +3 -101
  43. package/src/adapters/solid.js +3 -591
  44. package/src/adapters/svelte/ai.js +31 -0
  45. package/src/adapters/svelte/index.d.ts +166 -0
  46. package/src/adapters/svelte/index.js +798 -0
  47. package/src/adapters/svelte.d.ts +3 -166
  48. package/src/adapters/svelte.js +3 -803
  49. package/src/adapters/vanilla/ai.js +30 -0
  50. package/src/adapters/vanilla/index.d.ts +179 -0
  51. package/src/adapters/vanilla/index.js +785 -0
  52. package/src/adapters/vanilla.d.ts +3 -179
  53. package/src/adapters/vanilla.js +3 -791
  54. package/src/adapters/vue/ai.js +52 -0
  55. package/src/adapters/vue/index.d.ts +299 -0
  56. package/src/adapters/vue/index.js +608 -0
  57. package/src/adapters/vue.d.ts +3 -299
  58. package/src/adapters/vue.js +3 -611
  59. package/src/ai/wu-ai-actions.js +261 -0
  60. package/src/ai/wu-ai-browser.js +663 -0
  61. package/src/ai/wu-ai-context.js +332 -0
  62. package/src/ai/wu-ai-conversation.js +554 -0
  63. package/src/ai/wu-ai-permissions.js +381 -0
  64. package/src/ai/wu-ai-provider.js +605 -0
  65. package/src/ai/wu-ai-schema.js +225 -0
  66. package/src/ai/wu-ai-triggers.js +396 -0
  67. package/src/ai/wu-ai.js +474 -0
  68. package/src/core/wu-app.js +50 -8
  69. package/src/core/wu-cache.js +1 -1
  70. package/src/core/wu-core.js +645 -677
  71. package/src/core/wu-html-parser.js +121 -211
  72. package/src/core/wu-iframe-sandbox.js +328 -0
  73. package/src/core/wu-mcp-bridge.js +647 -0
  74. package/src/core/wu-overrides.js +510 -0
  75. package/src/core/wu-prefetch.js +414 -0
  76. package/src/core/wu-proxy-sandbox.js +398 -75
  77. package/src/core/wu-sandbox.js +86 -268
  78. package/src/core/wu-script-executor.js +79 -182
  79. package/src/core/wu-snapshot-sandbox.js +149 -106
  80. package/src/core/wu-strategies.js +13 -0
  81. package/src/core/wu-style-bridge.js +0 -2
  82. package/src/index.js +139 -665
  83. package/dist/wu-framework.hex.js +0 -23
  84. package/dist/wu-framework.min.js +0 -1
  85. package/dist/wu-framework.obf.js +0 -1
  86. package/scripts/build-protected.js +0 -366
  87. package/scripts/build.js +0 -212
  88. package/scripts/rollup-plugin-hex.js +0 -143
  89. package/src/core/wu-registry.js +0 -60
  90. package/src/core/wu-sandbox-pool.js +0 -390
@@ -0,0 +1,932 @@
1
+ /**
2
+ * 🚀 WU-FRAMEWORK ANGULAR ADAPTER
3
+ *
4
+ * Integrates Angular (14+) with Wu Framework as microfrontends.
5
+ * Supports NgModule-based apps, standalone components, and Angular Elements.
6
+ * Works inside Shadow DOM (wu-framework's default isolation mode).
7
+ *
8
+ * ## Quick Start — Standalone Component (recommended)
9
+ *
10
+ * ```ts
11
+ * // main.ts
12
+ * import 'zone.js';
13
+ * import '@angular/compiler'; // JIT mode (no AOT plugin needed)
14
+ * import { createApplication } from '@angular/platform-browser';
15
+ * import { createComponent, provideZoneChangeDetection } from '@angular/core';
16
+ * import { wuAngular } from 'wu-framework/adapters/angular';
17
+ * import { AppComponent } from './app/app.component';
18
+ *
19
+ * wuAngular.registerStandalone('my-app', AppComponent, {
20
+ * createApplication, // pass Angular APIs to avoid bundler issues
21
+ * createComponent,
22
+ * provideZoneChangeDetection,
23
+ * });
24
+ * ```
25
+ *
26
+ * ## Using Wu Events & Store inside Angular
27
+ *
28
+ * ```ts
29
+ * import { createWuService } from 'wu-framework/adapters/angular';
30
+ *
31
+ * @Component({ ... })
32
+ * export class MyComponent implements OnInit, OnDestroy {
33
+ * private wu = createWuService();
34
+ *
35
+ * ngOnInit() {
36
+ * this.wu.on('some:event', (data) => { ... });
37
+ * const user = this.wu.getState('user');
38
+ * }
39
+ *
40
+ * ngOnDestroy() {
41
+ * this.wu.destroy(); // cleans up all subscriptions
42
+ * }
43
+ * }
44
+ * ```
45
+ *
46
+ * ## Vite Setup (no AnalogJS required)
47
+ *
48
+ * ```ts
49
+ * // vite.config.ts
50
+ * import { defineConfig } from 'vite';
51
+ * export default defineConfig({
52
+ * server: { port: 5008, cors: true },
53
+ * esbuild: { target: 'es2022' },
54
+ * });
55
+ * ```
56
+ *
57
+ * ```json
58
+ * // tsconfig.json — enable decorators for esbuild
59
+ * { "compilerOptions": { "experimentalDecorators": true, "useDefineForClassFields": false } }
60
+ * ```
61
+ *
62
+ * ## Why pass Angular APIs as options?
63
+ *
64
+ * When wu-framework is linked via `file:` or a monorepo, bundlers (Vite, Rollup)
65
+ * resolve imports relative to the adapter's source file — NOT your app's node_modules.
66
+ * Passing `createApplication`, `createComponent`, etc. from your own imports ensures
67
+ * the bundler resolves them from your app's dependencies. The adapter falls back to
68
+ * dynamic imports for environments where this isn't an issue (Webpack, non-bundled).
69
+ */
70
+
71
+ /**
72
+ * Dynamic import helper — passes the module path through a function parameter
73
+ * so that bundlers (Vite, Rollup, Webpack) cannot statically resolve it.
74
+ * This is necessary because Angular dependencies (@angular/platform-browser-dynamic,
75
+ * @angular/elements, etc.) are optional and may not be installed.
76
+ */
77
+ function _optionalImport(modulePath) {
78
+ return import(/* @vite-ignore */ modulePath);
79
+ }
80
+
81
+ // Estado global del adapter
82
+ const adapterState = {
83
+ apps: new Map(),
84
+ platformRef: null,
85
+ initialized: false
86
+ };
87
+
88
+ /**
89
+ * Obtiene la instancia de Wu Framework
90
+ */
91
+ function getWuInstance() {
92
+ if (typeof window === 'undefined') return null;
93
+
94
+ return window.wu
95
+ || window.parent?.wu
96
+ || window.top?.wu
97
+ || null;
98
+ }
99
+
100
+ /**
101
+ * Espera a que Wu Framework esté disponible
102
+ */
103
+ function waitForWu(timeout = 5000) {
104
+ return new Promise((resolve, reject) => {
105
+ const wu = getWuInstance();
106
+ if (wu) {
107
+ resolve(wu);
108
+ return;
109
+ }
110
+
111
+ const startTime = Date.now();
112
+
113
+ const handleWuReady = () => {
114
+ cleanup();
115
+ resolve(getWuInstance());
116
+ };
117
+
118
+ window.addEventListener('wu:ready', handleWuReady);
119
+ window.addEventListener('wu:app:ready', handleWuReady);
120
+
121
+ const checkInterval = setInterval(() => {
122
+ const wu = getWuInstance();
123
+ if (wu) {
124
+ cleanup();
125
+ resolve(wu);
126
+ return;
127
+ }
128
+
129
+ if (Date.now() - startTime > timeout) {
130
+ cleanup();
131
+ reject(new Error(`Wu Framework not found after ${timeout}ms`));
132
+ }
133
+ }, 200);
134
+
135
+ function cleanup() {
136
+ clearInterval(checkInterval);
137
+ window.removeEventListener('wu:ready', handleWuReady);
138
+ window.removeEventListener('wu:app:ready', handleWuReady);
139
+ }
140
+ });
141
+ }
142
+
143
+ /**
144
+ * Register an NgModule-based Angular app as a microfrontend.
145
+ *
146
+ * NOTE: Uses platformBrowserDynamic + bootstrapModule which does NOT work inside
147
+ * Shadow DOM (it calls document.querySelector internally). For Shadow DOM compatibility,
148
+ * use registerStandalone() instead — it uses createApplication + createComponent with
149
+ * an explicit hostElement, which works everywhere.
150
+ *
151
+ * @param {string} appName - Unique name for the microfrontend
152
+ * @param {Type<any>} AppModule - Main Angular module (e.g. AppModule)
153
+ * @param {Object} options
154
+ * @param {Function} options.platformFactory - platformBrowserDynamic (pass it to avoid bundler issues)
155
+ * @param {Array} options.providers - Additional bootstrap providers
156
+ * @param {Function} options.onMount - Called after mount
157
+ * @param {Function} options.onUnmount - Called before unmount
158
+ * @param {boolean} options.standalone - Allow standalone fallback (default: true)
159
+ * @param {string} options.standaloneContainer - Selector for standalone mode (default: '#root')
160
+ * @param {string} options.rootSelector - Root component selector (default: 'app-root')
161
+ *
162
+ * @example
163
+ * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
164
+ * import { wuAngular } from 'wu-framework/adapters/angular';
165
+ * import { AppModule } from './app/app.module';
166
+ *
167
+ * wuAngular.register('my-app', AppModule, {
168
+ * platformFactory: platformBrowserDynamic, // pass to avoid bundler issues
169
+ * });
170
+ */
171
+ async function register(appName, AppModule, options = {}) {
172
+ const {
173
+ platformFactory = null,
174
+ providers = [],
175
+ onMount = null,
176
+ onUnmount = null,
177
+ standalone = true,
178
+ standaloneContainer = '#root',
179
+ rootSelector = 'app-root'
180
+ } = options;
181
+
182
+ // Función de mount interna
183
+ const mountApp = async (container) => {
184
+ if (!container) {
185
+ console.error(`[WuAngular] Mount failed for ${appName}: container is null`);
186
+ return;
187
+ }
188
+
189
+ // Evitar doble mount
190
+ if (adapterState.apps.has(appName)) {
191
+ console.warn(`[WuAngular] ${appName} already mounted, unmounting first`);
192
+ await unmountApp();
193
+ }
194
+
195
+ try {
196
+ // Crear elemento root para Angular
197
+ const appElement = document.createElement(rootSelector);
198
+ appElement.setAttribute('data-wu-angular-root', appName);
199
+ container.innerHTML = '';
200
+ container.appendChild(appElement);
201
+
202
+ // Obtener platformBrowserDynamic
203
+ let platform;
204
+ if (platformFactory) {
205
+ platform = platformFactory;
206
+ } else {
207
+ // Intentar import dinámico
208
+ try {
209
+ const platformModule = await _optionalImport('@angular/platform-browser-dynamic');
210
+ platform = platformModule.platformBrowserDynamic;
211
+ } catch (e) {
212
+ // Intentar desde window
213
+ if (window.ng?.platformBrowserDynamic) {
214
+ platform = window.ng.platformBrowserDynamic;
215
+ } else {
216
+ throw new Error('platformBrowserDynamic not available. Please provide it via options.platformFactory');
217
+ }
218
+ }
219
+ }
220
+
221
+ // Bootstrap del módulo
222
+ const platformRef = platform(providers);
223
+ const moduleRef = await platformRef.bootstrapModule(AppModule);
224
+
225
+ // Guardar referencias
226
+ adapterState.apps.set(appName, {
227
+ platformRef,
228
+ moduleRef,
229
+ container,
230
+ rootElement: appElement
231
+ });
232
+
233
+ console.log(`[WuAngular] ✅ ${appName} mounted successfully`);
234
+
235
+ if (onMount) {
236
+ onMount(container, moduleRef);
237
+ }
238
+ } catch (error) {
239
+ console.error(`[WuAngular] Mount error for ${appName}:`, error);
240
+ throw error;
241
+ }
242
+ };
243
+
244
+ // Función de unmount interna
245
+ const unmountApp = async (container) => {
246
+ const instance = adapterState.apps.get(appName);
247
+
248
+ if (instance) {
249
+ try {
250
+ if (onUnmount) {
251
+ onUnmount(instance.container, instance.moduleRef);
252
+ }
253
+
254
+ // Destruir el módulo
255
+ if (instance.moduleRef) {
256
+ instance.moduleRef.destroy();
257
+ }
258
+
259
+ // Destruir la plataforma
260
+ if (instance.platformRef) {
261
+ instance.platformRef.destroy();
262
+ }
263
+
264
+ // Limpiar DOM
265
+ if (instance.rootElement && instance.rootElement.parentNode) {
266
+ instance.rootElement.parentNode.removeChild(instance.rootElement);
267
+ }
268
+
269
+ adapterState.apps.delete(appName);
270
+
271
+ console.log(`[WuAngular] ✅ ${appName} unmounted successfully`);
272
+ } catch (error) {
273
+ console.error(`[WuAngular] Unmount error for ${appName}:`, error);
274
+ }
275
+ }
276
+
277
+ // Limpiar container si se proporciona
278
+ if (container) {
279
+ container.innerHTML = '';
280
+ }
281
+ };
282
+
283
+ // Intentar registrar con Wu Framework
284
+ try {
285
+ const wu = await waitForWu(3000);
286
+
287
+ wu.define(appName, {
288
+ mount: mountApp,
289
+ unmount: unmountApp
290
+ });
291
+
292
+ console.log(`[WuAngular] ✅ ${appName} registered with Wu Framework`);
293
+ return true;
294
+
295
+ } catch (error) {
296
+ console.warn(`[WuAngular] Wu Framework not available for ${appName}`);
297
+
298
+ // Modo standalone si está habilitado
299
+ if (standalone) {
300
+ const containerElement = document.querySelector(standaloneContainer);
301
+
302
+ if (containerElement) {
303
+ console.log(`[WuAngular] Running ${appName} in standalone mode`);
304
+ await mountApp(containerElement);
305
+ return true;
306
+ } else {
307
+ console.warn(`[WuAngular] Standalone container ${standaloneContainer} not found`);
308
+ }
309
+ }
310
+
311
+ return false;
312
+ }
313
+ }
314
+
315
+ /**
316
+ * Registra un componente Angular standalone como microfrontend (Angular 14+)
317
+ *
318
+ * @param {string} appName - Nombre único del microfrontend
319
+ * @param {Type<any>} RootComponent - Componente standalone principal
320
+ * @param {Object} options - Opciones adicionales
321
+ * @param {ApplicationConfig} options.appConfig - Configuración de la aplicación
322
+ * @param {Function} options.onMount - Callback después de montar
323
+ * @param {Function} options.onUnmount - Callback antes de desmontar
324
+ * @param {boolean} options.standalone - Permitir ejecución standalone (default: true)
325
+ * @param {string} options.standaloneContainer - Selector para modo standalone (default: '#root')
326
+ * @param {Function} options.createApplication - createApplication from @angular/platform-browser (recommended to avoid bundler issues)
327
+ * @param {Function} options.createComponent - createComponent from @angular/core
328
+ * @param {Function} options.provideZoneChangeDetection - provideZoneChangeDetection from @angular/core
329
+ *
330
+ * @example
331
+ * // Angular 14+ con standalone components
332
+ * import { AppComponent } from './app/app.component';
333
+ * import { createApplication } from '@angular/platform-browser';
334
+ * import { createComponent, provideZoneChangeDetection } from '@angular/core';
335
+ *
336
+ * wuAngular.registerStandalone('my-app', AppComponent, {
337
+ * createApplication,
338
+ * createComponent,
339
+ * provideZoneChangeDetection,
340
+ * });
341
+ */
342
+ async function registerStandalone(appName, RootComponent, options = {}) {
343
+ const {
344
+ appConfig = {},
345
+ onMount = null,
346
+ onUnmount = null,
347
+ standalone = true,
348
+ standaloneContainer = '#root',
349
+ createApplication: createApplicationOpt = null,
350
+ createComponent: createComponentOpt = null,
351
+ provideZoneChangeDetection: provideZoneChangeDetectionOpt = null
352
+ } = options;
353
+
354
+ // Función de mount para standalone components
355
+ const mountApp = async (container) => {
356
+ if (!container) {
357
+ console.error(`[WuAngular] Mount failed for ${appName}: container is null`);
358
+ return;
359
+ }
360
+
361
+ // Evitar doble mount
362
+ if (adapterState.apps.has(appName)) {
363
+ console.warn(`[WuAngular] ${appName} already mounted, unmounting first`);
364
+ await unmountApp();
365
+ }
366
+
367
+ try {
368
+ // Resolve Angular APIs: prefer options > dynamic import > window.ng
369
+ let createApplicationFn = createApplicationOpt;
370
+ let createComponentFn = createComponentOpt;
371
+ let provideZoneChangeDetectionFn = provideZoneChangeDetectionOpt;
372
+
373
+ if (!createApplicationFn) {
374
+ try {
375
+ const browserModule = await _optionalImport('@angular/platform-browser');
376
+ createApplicationFn = browserModule.createApplication;
377
+ const coreModule = await _optionalImport('@angular/core');
378
+ createComponentFn = coreModule.createComponent;
379
+ provideZoneChangeDetectionFn = coreModule.provideZoneChangeDetection;
380
+ } catch (e) {
381
+ if (window.ng?.createApplication) {
382
+ createApplicationFn = window.ng.createApplication;
383
+ createComponentFn = window.ng.createComponent;
384
+ provideZoneChangeDetectionFn = window.ng.provideZoneChangeDetection;
385
+ } else {
386
+ throw new Error(
387
+ 'Angular APIs not available. Pass createApplication, createComponent via options, ' +
388
+ 'or ensure @angular/platform-browser is resolvable. See docs for example.'
389
+ );
390
+ }
391
+ }
392
+ }
393
+
394
+ // Merge providers: add zone change detection if available
395
+ const providers = [...(appConfig.providers || [])];
396
+ if (provideZoneChangeDetectionFn) {
397
+ providers.push(provideZoneChangeDetectionFn({ eventCoalescing: true }));
398
+ }
399
+
400
+ // Create Angular application
401
+ const appRef = await createApplicationFn({ providers });
402
+
403
+ // Create host element inside the container (Shadow DOM compatible)
404
+ container.innerHTML = '';
405
+ const selector = RootComponent.ɵcmp?.selectors?.[0]?.[0]
406
+ || RootComponent.__annotations__?.[0]?.selector
407
+ || 'app-root';
408
+ const hostEl = document.createElement(selector);
409
+ container.appendChild(hostEl);
410
+
411
+ // Create and attach the component using hostElement (bypasses document.querySelector)
412
+ const compRef = createComponentFn(RootComponent, {
413
+ environmentInjector: appRef.injector,
414
+ hostElement: hostEl,
415
+ });
416
+ appRef.attachView(compRef.hostView);
417
+
418
+ // Guardar referencias
419
+ adapterState.apps.set(appName, {
420
+ appRef,
421
+ compRef,
422
+ container,
423
+ hostElement: hostEl,
424
+ isStandalone: true
425
+ });
426
+
427
+ console.log(`[WuAngular] ✅ ${appName} (standalone) mounted successfully`);
428
+
429
+ if (onMount) {
430
+ onMount(container, appRef);
431
+ }
432
+ } catch (error) {
433
+ console.error(`[WuAngular] Mount error for ${appName}:`, error);
434
+ throw error;
435
+ }
436
+ };
437
+
438
+ // Función de unmount para standalone
439
+ const unmountApp = async (container) => {
440
+ const instance = adapterState.apps.get(appName);
441
+
442
+ if (instance) {
443
+ try {
444
+ if (onUnmount) {
445
+ onUnmount(instance.container, instance.appRef);
446
+ }
447
+
448
+ // Destruir el componente
449
+ if (instance.compRef) {
450
+ instance.compRef.destroy();
451
+ }
452
+
453
+ // Destruir la aplicación
454
+ if (instance.appRef) {
455
+ instance.appRef.destroy();
456
+ }
457
+
458
+ adapterState.apps.delete(appName);
459
+
460
+ console.log(`[WuAngular] ✅ ${appName} (standalone) unmounted successfully`);
461
+ } catch (error) {
462
+ console.error(`[WuAngular] Unmount error for ${appName}:`, error);
463
+ }
464
+ }
465
+
466
+ if (container) {
467
+ container.innerHTML = '';
468
+ }
469
+ };
470
+
471
+ // Intentar registrar con Wu Framework
472
+ try {
473
+ const wu = await waitForWu(3000);
474
+
475
+ wu.define(appName, {
476
+ mount: mountApp,
477
+ unmount: unmountApp
478
+ });
479
+
480
+ console.log(`[WuAngular] ✅ ${appName} (standalone) registered with Wu Framework`);
481
+ return true;
482
+
483
+ } catch (error) {
484
+ console.warn(`[WuAngular] Wu Framework not available for ${appName}`);
485
+
486
+ if (standalone) {
487
+ const containerElement = document.querySelector(standaloneContainer);
488
+
489
+ if (containerElement) {
490
+ console.log(`[WuAngular] Running ${appName} in standalone mode`);
491
+ await mountApp(containerElement);
492
+ return true;
493
+ }
494
+ }
495
+
496
+ return false;
497
+ }
498
+ }
499
+
500
+ /**
501
+ * Register an Angular Elements (Web Component) as a microfrontend.
502
+ * Requires @angular/elements to be installed.
503
+ *
504
+ * @param {string} appName - Unique microfrontend name
505
+ * @param {Type<any>} Component - Angular standalone component
506
+ * @param {Object} options
507
+ * @param {string} options.elementTag - Custom element tag (default: `${appName}-element`)
508
+ * @param {ApplicationConfig} options.appConfig - Angular application config
509
+ * @param {Function} options.onMount - Called after mount
510
+ * @param {Function} options.onUnmount - Called before unmount
511
+ * @param {boolean} options.standalone - Allow standalone fallback (default: true)
512
+ * @param {string} options.standaloneContainer - Selector for standalone mode (default: '#root')
513
+ *
514
+ * @example
515
+ * import { wuAngular } from 'wu-framework/adapters/angular';
516
+ * import { AppComponent } from './app/app.component';
517
+ *
518
+ * wuAngular.registerElement('mfe-angular', AppComponent, {
519
+ * elementTag: 'mfe-angular-content',
520
+ * });
521
+ */
522
+ async function registerElement(appName, Component, options = {}) {
523
+ const {
524
+ elementTag = `${appName}-element`,
525
+ appConfig = {},
526
+ onMount = null,
527
+ onUnmount = null,
528
+ standalone = true,
529
+ standaloneContainer = '#root'
530
+ } = options;
531
+
532
+ let customElementRegistered = false;
533
+
534
+ // Función para inicializar Angular Elements
535
+ const initializeElement = async () => {
536
+ if (customElementRegistered) return true;
537
+
538
+ try {
539
+ // Import dinámico de Angular
540
+ const [{ createApplication }, { createCustomElement }] = await Promise.all([
541
+ _optionalImport('@angular/platform-browser'),
542
+ _optionalImport('@angular/elements')
543
+ ]);
544
+
545
+ // Crear aplicación Angular
546
+ const app = await createApplication(appConfig);
547
+
548
+ // Crear y registrar el custom element
549
+ const CustomElement = createCustomElement(Component, { injector: app.injector });
550
+
551
+ if (!customElements.get(elementTag)) {
552
+ customElements.define(elementTag, CustomElement);
553
+ console.log(`[WuAngular] ✅ Custom element registered: ${elementTag}`);
554
+ }
555
+
556
+ customElementRegistered = true;
557
+
558
+ // Guardar referencia
559
+ adapterState.apps.set(`${appName}:element`, {
560
+ app,
561
+ elementTag,
562
+ CustomElement
563
+ });
564
+
565
+ return true;
566
+ } catch (error) {
567
+ console.error(`[WuAngular] Failed to initialize Angular Element:`, error);
568
+ throw error;
569
+ }
570
+ };
571
+
572
+ // Función de mount
573
+ const mountApp = async (container) => {
574
+ if (!container) {
575
+ console.error(`[WuAngular] Mount failed for ${appName}: container is null`);
576
+ return;
577
+ }
578
+
579
+ try {
580
+ // Asegurar que el elemento está registrado
581
+ await initializeElement();
582
+
583
+ // Crear el elemento custom
584
+ const element = document.createElement(elementTag);
585
+ element.setAttribute('data-wu-angular-element', appName);
586
+
587
+ // Limpiar y agregar al container
588
+ container.innerHTML = '';
589
+ container.appendChild(element);
590
+
591
+ // Guardar referencia del mount
592
+ adapterState.apps.set(appName, {
593
+ element,
594
+ container,
595
+ elementTag
596
+ });
597
+
598
+ console.log(`[WuAngular] ✅ ${appName} (element) mounted successfully`);
599
+
600
+ if (onMount) {
601
+ onMount(container, element);
602
+ }
603
+
604
+ return element;
605
+ } catch (error) {
606
+ console.error(`[WuAngular] Mount error for ${appName}:`, error);
607
+ throw error;
608
+ }
609
+ };
610
+
611
+ // Función de unmount
612
+ const unmountApp = async (container) => {
613
+ const instance = adapterState.apps.get(appName);
614
+
615
+ if (instance) {
616
+ try {
617
+ if (onUnmount) {
618
+ onUnmount(instance.container, instance.element);
619
+ }
620
+
621
+ // Remover elemento del DOM
622
+ if (instance.element && instance.element.parentNode) {
623
+ instance.element.remove();
624
+ }
625
+
626
+ adapterState.apps.delete(appName);
627
+
628
+ console.log(`[WuAngular] ✅ ${appName} (element) unmounted successfully`);
629
+ } catch (error) {
630
+ console.error(`[WuAngular] Unmount error for ${appName}:`, error);
631
+ }
632
+ }
633
+
634
+ if (container) {
635
+ container.innerHTML = '';
636
+ }
637
+ };
638
+
639
+ // Intentar registrar con Wu Framework
640
+ try {
641
+ const wu = await waitForWu(3000);
642
+
643
+ wu.define(appName, {
644
+ mount: mountApp,
645
+ unmount: unmountApp
646
+ });
647
+
648
+ console.log(`[WuAngular] ✅ ${appName} (element) registered with Wu Framework`);
649
+ return true;
650
+
651
+ } catch (error) {
652
+ console.warn(`[WuAngular] Wu Framework not available for ${appName}`);
653
+
654
+ if (standalone) {
655
+ const containerElement = document.querySelector(standaloneContainer);
656
+
657
+ if (containerElement) {
658
+ console.log(`[WuAngular] Running ${appName} in standalone mode`);
659
+ await mountApp(containerElement);
660
+ return true;
661
+ }
662
+ }
663
+
664
+ return false;
665
+ }
666
+ }
667
+
668
+ /**
669
+ * Creates a lightweight service for wu-framework events and store access.
670
+ * Call destroy() in ngOnDestroy to clean up all subscriptions.
671
+ *
672
+ * @returns {{ emit, on, once, off, getState, setState, onStateChange, destroy, wu }}
673
+ *
674
+ * @example
675
+ * import { createWuService } from 'wu-framework/adapters/angular';
676
+ *
677
+ * @Component({ selector: 'app-root', standalone: true, template: '...' })
678
+ * export class AppComponent implements OnInit, OnDestroy {
679
+ * private wu = createWuService();
680
+ *
681
+ * ngOnInit() {
682
+ * this.wu.on('order:new', (e) => this.orders.push(e.data));
683
+ * this.wu.onStateChange('theme.mode', (e) => this.theme = e.value);
684
+ * const user = this.wu.getState('user');
685
+ * }
686
+ *
687
+ * save() {
688
+ * this.wu.setState('store.name', this.storeName);
689
+ * this.wu.emit('settings:saved', { name: this.storeName });
690
+ * }
691
+ *
692
+ * ngOnDestroy() {
693
+ * this.wu.destroy(); // removes all on/onStateChange listeners
694
+ * }
695
+ * }
696
+ */
697
+ function createWuService() {
698
+ const subscriptions = [];
699
+
700
+ return {
701
+ // Event Bus
702
+ emit: (event, data, options) => {
703
+ const wu = getWuInstance();
704
+ if (wu?.eventBus) {
705
+ wu.eventBus.emit(event, data, options);
706
+ } else {
707
+ console.warn('[WuService] Wu Framework not available');
708
+ }
709
+ },
710
+
711
+ on: (event, callback) => {
712
+ const wu = getWuInstance();
713
+ if (wu?.eventBus) {
714
+ const unsubscribe = wu.eventBus.on(event, callback);
715
+ subscriptions.push(unsubscribe);
716
+ return unsubscribe;
717
+ }
718
+ console.warn('[WuService] Wu Framework not available');
719
+ return () => {};
720
+ },
721
+
722
+ once: (event, callback) => {
723
+ const wu = getWuInstance();
724
+ if (wu?.eventBus) {
725
+ return wu.eventBus.once(event, callback);
726
+ }
727
+ return () => {};
728
+ },
729
+
730
+ off: (event, callback) => {
731
+ const wu = getWuInstance();
732
+ if (wu?.eventBus) {
733
+ wu.eventBus.off(event, callback);
734
+ }
735
+ },
736
+
737
+ // Store
738
+ getState: (path) => {
739
+ const wu = getWuInstance();
740
+ return wu?.store?.get(path) || null;
741
+ },
742
+
743
+ setState: (path, value) => {
744
+ const wu = getWuInstance();
745
+ if (wu?.store) {
746
+ wu.store.set(path, value);
747
+ }
748
+ },
749
+
750
+ onStateChange: (pattern, callback) => {
751
+ const wu = getWuInstance();
752
+ if (wu?.store) {
753
+ const unsubscribe = wu.store.on(pattern, callback);
754
+ subscriptions.push(unsubscribe);
755
+ return unsubscribe;
756
+ }
757
+ return () => {};
758
+ },
759
+
760
+ // Cleanup
761
+ destroy: () => {
762
+ subscriptions.forEach(unsub => unsub());
763
+ subscriptions.length = 0;
764
+ },
765
+
766
+ // Access to raw Wu instance
767
+ get wu() {
768
+ return getWuInstance();
769
+ }
770
+ };
771
+ }
772
+
773
+ /**
774
+ * Crea un componente Angular para cargar microfrontends (para el Shell)
775
+ * Retorna la configuración del componente para ser usado con @Component
776
+ *
777
+ * @example
778
+ * // wu-slot.component.ts
779
+ * import { Component, Input, Output, EventEmitter } from '@angular/core';
780
+ * import { createWuSlotComponent } from 'wu-framework/adapters/angular';
781
+ *
782
+ * const config = createWuSlotComponent();
783
+ *
784
+ * @Component({
785
+ * selector: 'wu-slot',
786
+ * template: config.template,
787
+ * styles: config.styles
788
+ * })
789
+ * export class WuSlotComponent {
790
+ * @Input() name!: string;
791
+ * @Input() url!: string;
792
+ * @Output() load = new EventEmitter();
793
+ * @Output() error = new EventEmitter();
794
+ *
795
+ * // ... implement lifecycle methods from config.methods
796
+ * }
797
+ */
798
+ function createWuSlotComponent() {
799
+ return {
800
+ selector: 'wu-slot',
801
+
802
+ template: `
803
+ <div
804
+ #container
805
+ class="wu-slot"
806
+ [class.wu-slot-loading]="loading"
807
+ [class.wu-slot-error]="error"
808
+ [attr.data-wu-app]="name"
809
+ [attr.data-wu-url]="url"
810
+ style="min-height: 100px; position: relative;">
811
+
812
+ <div *ngIf="error" class="wu-slot-error-message"
813
+ style="padding: 1rem; border: 1px solid #f5c6cb; border-radius: 4px; background: #f8d7da; color: #721c24;">
814
+ <strong>Error loading {{ name }}</strong>
815
+ <p style="margin: 0.5rem 0 0 0;">{{ error }}</p>
816
+ </div>
817
+
818
+ <div *ngIf="loading && !error" class="wu-slot-loading-message"
819
+ style="display: flex; align-items: center; justify-content: center; padding: 2rem; color: #666;">
820
+ {{ fallbackText || 'Loading ' + name + '...' }}
821
+ </div>
822
+ </div>
823
+ `,
824
+
825
+ styles: [`
826
+ .wu-slot {
827
+ width: 100%;
828
+ min-height: 100px;
829
+ }
830
+ `],
831
+
832
+ // Métodos para implementar en el componente
833
+ methods: {
834
+ async ngOnInit() {
835
+ await this.mountMicrofrontend();
836
+ },
837
+
838
+ ngOnDestroy() {
839
+ this.unmountMicrofrontend();
840
+ },
841
+
842
+ async mountMicrofrontend() {
843
+ try {
844
+ this.loading = true;
845
+ this.error = null;
846
+
847
+ const wu = getWuInstance();
848
+ if (!wu) {
849
+ throw new Error('Wu Framework not initialized');
850
+ }
851
+
852
+ // Crear container único
853
+ const containerId = `wu-slot-${this.name}-${Date.now()}`;
854
+ const innerContainer = document.createElement('div');
855
+ innerContainer.id = containerId;
856
+ innerContainer.style.width = '100%';
857
+ innerContainer.style.height = '100%';
858
+
859
+ this.container.nativeElement.innerHTML = '';
860
+ this.container.nativeElement.appendChild(innerContainer);
861
+
862
+ // Crear y montar la app
863
+ const app = wu.app(this.name, {
864
+ url: this.url,
865
+ container: `#${containerId}`,
866
+ autoInit: true
867
+ });
868
+
869
+ this.appInstance = app;
870
+ await app.mount();
871
+
872
+ this.loading = false;
873
+ this.load.emit({ name: this.name, url: this.url });
874
+
875
+ } catch (err) {
876
+ console.error(`[WuSlot] Error loading ${this.name}:`, err);
877
+ this.error = err.message || 'Failed to load microfrontend';
878
+ this.loading = false;
879
+ this.errorEvent.emit(err);
880
+ }
881
+ },
882
+
883
+ async unmountMicrofrontend() {
884
+ if (this.appInstance) {
885
+ try {
886
+ await this.appInstance.unmount();
887
+ } catch (err) {
888
+ console.warn(`[WuSlot] Error unmounting ${this.name}:`, err);
889
+ }
890
+ this.appInstance = null;
891
+ }
892
+ }
893
+ }
894
+ };
895
+ }
896
+
897
+ /**
898
+ * Helper para crear un módulo Angular que exporta WuSlotComponent
899
+ * Útil para shells que quieren usar <wu-slot> directamente
900
+ */
901
+ function getWuSlotModuleConfig() {
902
+ return {
903
+ imports: ['CommonModule'],
904
+ declarations: ['WuSlotComponent'],
905
+ exports: ['WuSlotComponent']
906
+ };
907
+ }
908
+
909
+ // ============================================
910
+ // AI INTEGRATION (placeholder — ai.js loaded on demand)
911
+ // ============================================
912
+ function createWuAIService(...args) {
913
+ throw new Error('[WuAngular] AI module not available. Install wu-framework AI extension.');
914
+ }
915
+
916
+ // Named exports for direct imports (e.g. import { createWuService } from 'wu-framework/adapters/angular')
917
+ export { createWuService, register, registerStandalone, registerElement, createWuSlotComponent, getWuSlotModuleConfig, getWuInstance, waitForWu };
918
+
919
+ // API pública del adapter
920
+ export const wuAngular = {
921
+ register,
922
+ registerStandalone,
923
+ registerElement,
924
+ createWuService,
925
+ createWuSlotComponent,
926
+ getWuSlotModuleConfig,
927
+ createWuAIService,
928
+ getWuInstance,
929
+ waitForWu
930
+ };
931
+
932
+ export default wuAngular;