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,474 @@
1
+ /**
2
+ * WU-AI: Central orchestrator for AI integration
3
+ *
4
+ * This is the main entry point for wu.ai — it wires together all sub-modules
5
+ * and exposes the public API. Lazy-initialized on first use.
6
+ *
7
+ * Architecture:
8
+ * WuAI (this file)
9
+ * ├── WuAIProvider → BYOL provider management
10
+ * ├── WuAIPermissions → Security, rate limiting, circuit breaker
11
+ * ├── WuAIContext → Auto context collection for LLM
12
+ * ├── WuAIActions → Tool/action registry and execution
13
+ * ├── WuAIConversation → Multi-turn conversation manager
14
+ * └── WuAITriggers → Event-to-AI bridge (reactive AI)
15
+ *
16
+ * Public API (accessible via wu.ai):
17
+ * wu.ai.provider(name, config) → Register LLM provider
18
+ * wu.ai.send(message, opts) → Send message (non-streaming)
19
+ * wu.ai.stream(message, opts) → Send message (streaming)
20
+ * wu.ai.action(name, config) → Register an action/tool
21
+ * wu.ai.trigger(name, config) → Register an event trigger
22
+ * wu.ai.context.configure(...) → Configure context collection
23
+ * wu.ai.abort(namespace?) → Abort active request
24
+ *
25
+ * Paradigm 2 (External agent access):
26
+ * wu.ai.tools() → Get all registered tools (for CDP/WebMCP)
27
+ * wu.ai.execute(name, params) → Execute action directly (for external agents)
28
+ * wu.ai.expose() → Register tools via WebMCP (navigator.modelContext)
29
+ */
30
+
31
+ import { logger } from '../core/wu-logger.js';
32
+ import { WuAIProvider } from './wu-ai-provider.js';
33
+ import { WuAIPermissions } from './wu-ai-permissions.js';
34
+ import { WuAIContext } from './wu-ai-context.js';
35
+ import { WuAIActions } from './wu-ai-actions.js';
36
+ import { WuAIConversation } from './wu-ai-conversation.js';
37
+ import { WuAITriggers } from './wu-ai-triggers.js';
38
+ import { registerBrowserActions } from './wu-ai-browser.js';
39
+
40
+ export class WuAI {
41
+ /**
42
+ * @param {object} deps - Injected from WuCore
43
+ * @param {object} deps.eventBus - WuEventBus instance
44
+ * @param {object} deps.store - WuStore instance
45
+ * @param {object} deps.core - WuCore instance (for mounted apps)
46
+ */
47
+ constructor({ eventBus, store, core }) {
48
+ this._eventBus = eventBus;
49
+ this._store = store;
50
+ this._core = core;
51
+ this._initialized = false;
52
+ this._modules = {};
53
+ }
54
+
55
+ // ─── Lazy Initialization ───────────────────────────────────────
56
+
57
+ /**
58
+ * Initialize all sub-modules. Called automatically on first use,
59
+ * or can be called explicitly with configuration.
60
+ *
61
+ * @param {object} [config]
62
+ * @param {object} [config.permissions] - Permission overrides
63
+ * @param {object} [config.rateLimit] - Rate limit config
64
+ * @param {object} [config.circuitBreaker] - Circuit breaker config
65
+ * @param {object} [config.loopProtection] - Loop protection config
66
+ * @param {object} [config.context] - Context collection config
67
+ * @param {object} [config.conversation] - Conversation defaults
68
+ * @param {object} [config.triggers] - Trigger system config
69
+ */
70
+ init(config = {}) {
71
+ if (this._initialized) {
72
+ // Reconfigure if already initialized
73
+ this._reconfigure(config);
74
+ return this;
75
+ }
76
+
77
+ // 1. Permissions (independent — no deps)
78
+ this._modules.permissions = new WuAIPermissions({
79
+ permissions: config.permissions,
80
+ rateLimit: config.rateLimit,
81
+ circuitBreaker: config.circuitBreaker,
82
+ loopProtection: config.loopProtection,
83
+ allowedDomains: config.allowedDomains,
84
+ });
85
+
86
+ // 2. Provider (independent — no deps)
87
+ this._modules.provider = new WuAIProvider();
88
+
89
+ // 3. Context (depends on store, eventBus, core)
90
+ this._modules.context = new WuAIContext({
91
+ store: this._store,
92
+ eventBus: this._eventBus,
93
+ core: this._core,
94
+ });
95
+ if (config.context) {
96
+ this._modules.context.configure(config.context);
97
+ }
98
+
99
+ // 4. Actions (depends on eventBus, store, permissions)
100
+ this._modules.actions = new WuAIActions({
101
+ eventBus: this._eventBus,
102
+ store: this._store,
103
+ permissions: this._modules.permissions,
104
+ });
105
+
106
+ // 5. Conversation (depends on provider, actions, context, permissions, eventBus)
107
+ this._modules.conversation = new WuAIConversation({
108
+ provider: this._modules.provider,
109
+ actions: this._modules.actions,
110
+ context: this._modules.context,
111
+ permissions: this._modules.permissions,
112
+ eventBus: this._eventBus,
113
+ });
114
+ if (config.conversation) {
115
+ this._modules.conversation.configure(config.conversation);
116
+ }
117
+
118
+ // 6. Triggers (depends on eventBus, conversation, permissions)
119
+ this._modules.triggers = new WuAITriggers({
120
+ eventBus: this._eventBus,
121
+ conversation: this._modules.conversation,
122
+ permissions: this._modules.permissions,
123
+ });
124
+ if (config.triggers) {
125
+ this._modules.triggers.configure(config.triggers);
126
+ }
127
+
128
+ this._initialized = true;
129
+ logger.wuInfo('[wu-ai] Initialized');
130
+
131
+ // 7. Browser automation actions (screenshot, click, type, network, etc.)
132
+ // Must be AFTER _initialized = true to prevent recursive init loop
133
+ if (typeof window !== 'undefined') {
134
+ registerBrowserActions(this, this._core);
135
+ logger.wuInfo('[wu-ai] Browser actions registered (10 tools)');
136
+ }
137
+
138
+ this._eventBus.emit('ai:initialized', {}, { appName: 'wu-ai' });
139
+
140
+ return this;
141
+ }
142
+
143
+ // ─── Provider Management ───────────────────────────────────────
144
+
145
+ /**
146
+ * Register an LLM provider.
147
+ *
148
+ * @param {string} name - Provider name ('openai', 'anthropic', 'ollama', or custom)
149
+ * @param {object} config - { endpoint, apiKey?, model?, adapter?, send?, stream? }
150
+ *
151
+ * @example
152
+ * // OpenAI via proxy (recommended)
153
+ * wu.ai.provider('openai', { endpoint: '/api/ai/chat', model: 'gpt-4o' });
154
+ *
155
+ * // Anthropic direct (development only)
156
+ * wu.ai.provider('anthropic', {
157
+ * endpoint: 'https://api.anthropic.com/v1/messages',
158
+ * apiKey: 'sk-...',
159
+ * model: 'claude-sonnet-4-5-20250929',
160
+ * });
161
+ *
162
+ * // Local Ollama
163
+ * wu.ai.provider('ollama', { endpoint: 'http://localhost:11434/api/chat', model: 'llama3' });
164
+ *
165
+ * // Custom provider
166
+ * wu.ai.provider('my-llm', { send: async (messages, opts) => ({ content: '...' }) });
167
+ */
168
+ provider(name, config) {
169
+ this._ensureInit();
170
+ this._modules.provider.register(name, config);
171
+ return this;
172
+ }
173
+
174
+ // ─── Paradigm 1: App → LLM (Conversation) ─────────────────────
175
+
176
+ /**
177
+ * Send a message to the LLM and get a complete response.
178
+ *
179
+ * @param {string} message - User message
180
+ * @param {object} [options] - { namespace, systemPrompt, templateVars, temperature, maxTokens, signal }
181
+ * @returns {Promise<{ content: string, tool_results?: Array, usage?: object, namespace: string }>}
182
+ *
183
+ * @example
184
+ * const response = await wu.ai.send('What items are in the cart?');
185
+ * console.log(response.content);
186
+ *
187
+ * // With namespace for separate conversation
188
+ * const response = await wu.ai.send('Analyze this chart', { namespace: 'analytics' });
189
+ */
190
+ async send(message, options = {}) {
191
+ this._ensureInit();
192
+ return this._modules.conversation.send(message, options);
193
+ }
194
+
195
+ /**
196
+ * Send a message and stream the response.
197
+ *
198
+ * @param {string} message - User message
199
+ * @param {object} [options] - Same as send()
200
+ * @yields {{ type: 'text'|'tool_result'|'done'|'error', content?: string }}
201
+ *
202
+ * @example
203
+ * for await (const chunk of wu.ai.stream('Tell me about this page')) {
204
+ * if (chunk.type === 'text') outputEl.textContent += chunk.content;
205
+ * if (chunk.type === 'done') console.log('Done!');
206
+ * }
207
+ */
208
+ async *stream(message, options = {}) {
209
+ this._ensureInit();
210
+ yield* this._modules.conversation.stream(message, options);
211
+ }
212
+
213
+ /**
214
+ * Abort active request(s).
215
+ *
216
+ * @param {string} [namespace] - Specific namespace, or all if omitted
217
+ */
218
+ abort(namespace) {
219
+ if (!this._initialized) return;
220
+ if (namespace) {
221
+ this._modules.conversation.abort(namespace);
222
+ } else {
223
+ this._modules.conversation.abortAll();
224
+ }
225
+ }
226
+
227
+ // ─── Actions / Tools ───────────────────────────────────────────
228
+
229
+ /**
230
+ * Register an action that the LLM can call.
231
+ *
232
+ * @param {string} name - Action name (used in tool_call)
233
+ * @param {object} config - { description, parameters, handler, confirm?, permissions?, dangerous? }
234
+ *
235
+ * @example
236
+ * wu.ai.action('addToCart', {
237
+ * description: 'Add an item to the shopping cart',
238
+ * parameters: {
239
+ * productId: { type: 'string', required: true },
240
+ * quantity: { type: 'number' },
241
+ * },
242
+ * handler: async (params, api) => {
243
+ * api.setState('cart.items', [...api.getState('cart.items'), params]);
244
+ * api.emit('cart:updated', params);
245
+ * return { added: params.productId };
246
+ * },
247
+ * confirm: true, // require user confirmation
248
+ * });
249
+ */
250
+ action(name, config) {
251
+ this._ensureInit();
252
+ this._modules.actions.register(name, config);
253
+ return this;
254
+ }
255
+
256
+ /**
257
+ * Execute an action directly (used by external agents via CDP/WebMCP).
258
+ *
259
+ * @param {string} name - Action name
260
+ * @param {object} params - Parameters
261
+ * @returns {Promise<{ success: boolean, result?: any, reason?: string }>}
262
+ */
263
+ async execute(name, params) {
264
+ this._ensureInit();
265
+ const traceId = this._modules.permissions.loopProtection.createTraceId();
266
+ return this._modules.actions.execute(name, params, { traceId, depth: 0 });
267
+ }
268
+
269
+ // ─── Triggers (Reactive AI) ────────────────────────────────────
270
+
271
+ /**
272
+ * Register a trigger that automatically sends messages to the LLM
273
+ * when specific events occur.
274
+ *
275
+ * @param {string} name - Trigger name
276
+ * @param {object} config - { pattern, prompt, condition?, debounce?, priority?, onResult? }
277
+ *
278
+ * @example
279
+ * wu.ai.trigger('cartAnalysis', {
280
+ * pattern: 'cart:updated',
281
+ * prompt: 'The cart was updated: {{data}}. Suggest complementary products.',
282
+ * debounce: 3000,
283
+ * priority: 'low',
284
+ * onResult: (result) => {
285
+ * wu.emit('ai:suggestions', { suggestions: result.content });
286
+ * },
287
+ * });
288
+ */
289
+ trigger(name, config) {
290
+ this._ensureInit();
291
+ this._modules.triggers.register(name, config);
292
+ return this;
293
+ }
294
+
295
+ /**
296
+ * Fire a trigger manually.
297
+ */
298
+ async fireTrigger(name, eventData) {
299
+ this._ensureInit();
300
+ return this._modules.triggers.fire(name, eventData);
301
+ }
302
+
303
+ // ─── Context ───────────────────────────────────────────────────
304
+
305
+ /**
306
+ * Context configuration sub-API.
307
+ * Access via wu.ai.context
308
+ */
309
+ get context() {
310
+ this._ensureInit();
311
+ return {
312
+ configure: (config) => this._modules.context.configure(config),
313
+ register: (name, config) => this._modules.context.register(name, config),
314
+ collect: () => this._modules.context.collect(),
315
+ getSnapshot: () => this._modules.context.getSnapshot(),
316
+ };
317
+ }
318
+
319
+ // ─── Conversation Management ───────────────────────────────────
320
+
321
+ /**
322
+ * Conversation sub-API for direct history management.
323
+ */
324
+ get conversation() {
325
+ this._ensureInit();
326
+ return {
327
+ getHistory: (ns) => this._modules.conversation.getHistory(ns),
328
+ clear: (ns) => this._modules.conversation.clear(ns),
329
+ clearAll: () => this._modules.conversation.clearAll(),
330
+ inject: (role, content, opts) => this._modules.conversation.inject(role, content, opts),
331
+ getNamespaces: () => this._modules.conversation.getNamespaces(),
332
+ deleteNamespace: (ns) => this._modules.conversation.deleteNamespace(ns),
333
+ };
334
+ }
335
+
336
+ // ─── Permissions ───────────────────────────────────────────────
337
+
338
+ /**
339
+ * Permissions sub-API.
340
+ */
341
+ get permissions() {
342
+ this._ensureInit();
343
+ return {
344
+ configure: (config) => this._modules.permissions.configure(config),
345
+ check: (perm) => this._modules.permissions.check(perm),
346
+ getPermissions: () => this._modules.permissions.getPermissions(),
347
+ setAllowedDomains: (domains) => this._modules.permissions.setAllowedDomains(domains),
348
+ };
349
+ }
350
+
351
+ // ─── Paradigm 2: LLM → App (External Agent Access) ────────────
352
+
353
+ /**
354
+ * Get all registered tools (for external agents).
355
+ * An agent connected via CDP can call: window.wu.ai.tools()
356
+ *
357
+ * @returns {Array<{ name, description, parameters }>}
358
+ */
359
+ tools() {
360
+ this._ensureInit();
361
+ return this._modules.actions.getToolSchemas();
362
+ }
363
+
364
+ /**
365
+ * Expose tools via WebMCP (Chrome 146+ / W3C proposal).
366
+ * Registers all actions with navigator.modelContext.registerTool()
367
+ *
368
+ * @returns {boolean} Whether WebMCP is available
369
+ */
370
+ expose() {
371
+ this._ensureInit();
372
+
373
+ if (typeof navigator === 'undefined' || !navigator.modelContext) {
374
+ logger.wuDebug('[wu-ai] WebMCP not available (navigator.modelContext missing)');
375
+ return false;
376
+ }
377
+
378
+ const tools = this._modules.actions.getToolSchemas();
379
+ const actionNames = this._modules.actions.getNames();
380
+
381
+ for (let i = 0; i < tools.length; i++) {
382
+ const tool = tools[i];
383
+ const actionName = actionNames[i];
384
+
385
+ try {
386
+ navigator.modelContext.registerTool({
387
+ name: tool.name,
388
+ description: tool.description,
389
+ inputSchema: tool.parameters,
390
+ handler: async (params) => {
391
+ const result = await this.execute(actionName, params);
392
+ return result.success ? result.result : { error: result.reason };
393
+ },
394
+ });
395
+ } catch (err) {
396
+ logger.wuDebug(`[wu-ai] WebMCP register failed for '${tool.name}': ${err.message}`);
397
+ }
398
+ }
399
+
400
+ logger.wuInfo(`[wu-ai] Exposed ${tools.length} tools via WebMCP`);
401
+
402
+ this._eventBus.emit('ai:webmcp:exposed', {
403
+ toolCount: tools.length,
404
+ tools: tools.map(t => t.name),
405
+ }, { appName: 'wu-ai' });
406
+
407
+ return true;
408
+ }
409
+
410
+ /**
411
+ * Confirm a pending tool call (for UI integration).
412
+ */
413
+ confirmTool(callId) {
414
+ if (!this._initialized) return;
415
+ this._modules.actions.confirmTool(callId);
416
+ }
417
+
418
+ /**
419
+ * Reject a pending tool call.
420
+ */
421
+ rejectTool(callId) {
422
+ if (!this._initialized) return;
423
+ this._modules.actions.rejectTool(callId);
424
+ }
425
+
426
+ // ─── Stats & Debug ─────────────────────────────────────────────
427
+
428
+ getStats() {
429
+ if (!this._initialized) return { initialized: false };
430
+
431
+ return {
432
+ initialized: true,
433
+ provider: this._modules.provider.getStats(),
434
+ permissions: this._modules.permissions.getStats(),
435
+ context: this._modules.context.getStats(),
436
+ actions: this._modules.actions.getStats(),
437
+ conversation: this._modules.conversation.getStats(),
438
+ triggers: this._modules.triggers.getStats(),
439
+ };
440
+ }
441
+
442
+ /**
443
+ * Destroy the AI system and clean up all resources.
444
+ */
445
+ destroy() {
446
+ if (!this._initialized) return;
447
+
448
+ this._modules.conversation.abortAll();
449
+ this._modules.triggers.destroy();
450
+ this._modules = {};
451
+ this._initialized = false;
452
+
453
+ logger.wuInfo('[wu-ai] Destroyed');
454
+ this._eventBus.emit('ai:destroyed', {}, { appName: 'wu-ai' });
455
+ }
456
+
457
+ // ─── Private ───────────────────────────────────────────────────
458
+
459
+ _ensureInit() {
460
+ if (!this._initialized) {
461
+ this.init();
462
+ }
463
+ }
464
+
465
+ _reconfigure(config) {
466
+ if (config.permissions) this._modules.permissions.configure(config.permissions);
467
+ if (config.rateLimit) this._modules.permissions.rateLimiter.configure(config.rateLimit);
468
+ if (config.circuitBreaker) this._modules.permissions.circuitBreaker.configure(config.circuitBreaker);
469
+ if (config.loopProtection) this._modules.permissions.loopProtection.configure(config.loopProtection);
470
+ if (config.context) this._modules.context.configure(config.context);
471
+ if (config.conversation) this._modules.conversation.configure(config.conversation);
472
+ if (config.triggers) this._modules.triggers.configure(config.triggers);
473
+ }
474
+ }
@@ -17,6 +17,7 @@ export class WuApp {
17
17
  this.name = name
18
18
  this.url = config.url
19
19
  this.container = config.container
20
+ this.keepAlive = config.keepAlive || false
20
21
  this._wu = wu
21
22
  this._mounted = false
22
23
  this._autoInit = config.autoInit !== false // Default true
@@ -33,10 +34,10 @@ export class WuApp {
33
34
  */
34
35
  _registerApp() {
35
36
  if (!this._wu.apps.has(this.name)) {
36
- // Usar el método interno de wu-framework para registrar
37
37
  this._wu.apps.set(this.name, {
38
38
  name: this.name,
39
39
  url: this.url,
40
+ keepAlive: this.keepAlive,
40
41
  status: 'registered'
41
42
  })
42
43
  console.log(`📦 App registered: ${this.name} at ${this.url}`)
@@ -56,7 +57,7 @@ export class WuApp {
56
57
  }
57
58
 
58
59
  // Asegurar que wu-framework está inicializado
59
- if (!this._wu.initialized) {
60
+ if (!this._wu.isInitialized) {
60
61
  await this._wu.init({
61
62
  apps: [{ name: this.name, url: this.url }]
62
63
  })
@@ -70,18 +71,51 @@ export class WuApp {
70
71
  }
71
72
 
72
73
  /**
73
- * Desmontar la app
74
- * @returns {Promise<void>}
74
+ * Desmontar la app.
75
+ * If keepAlive is configured, hides instead of destroying.
76
+ *
77
+ * @param {Object} [options] - { keepAlive, force }
78
+ * @returns {Promise<WuApp>}
75
79
  */
76
- async unmount() {
80
+ async unmount(options = {}) {
81
+ if (!this._mounted && !this._wu.isHidden(this.name)) {
82
+ console.warn(`⚠️ App ${this.name} is not mounted`)
83
+ return this
84
+ }
85
+
86
+ await this._wu.unmount(this.name, options)
87
+ this._mounted = !this._wu.isHidden(this.name)
88
+
89
+ return this
90
+ }
91
+
92
+ /**
93
+ * Hide the app (keep-alive). Preserves all state for instant re-show.
94
+ * @returns {Promise<WuApp>}
95
+ */
96
+ async hide() {
77
97
  if (!this._mounted) {
78
98
  console.warn(`⚠️ App ${this.name} is not mounted`)
79
99
  return this
80
100
  }
81
101
 
82
- await this._wu.unmount(this.name)
102
+ await this._wu.hide(this.name)
83
103
  this._mounted = false
104
+ return this
105
+ }
84
106
 
107
+ /**
108
+ * Show a hidden (keep-alive) app instantly.
109
+ * @returns {Promise<WuApp>}
110
+ */
111
+ async show() {
112
+ if (!this._wu.isHidden(this.name)) {
113
+ console.warn(`⚠️ App ${this.name} is not in keep-alive state`)
114
+ return this
115
+ }
116
+
117
+ await this._wu.show(this.name)
118
+ this._mounted = true
85
119
  return this
86
120
  }
87
121
 
@@ -91,7 +125,7 @@ export class WuApp {
91
125
  * @returns {Promise<void>}
92
126
  */
93
127
  async remount(container) {
94
- await this.unmount()
128
+ await this.unmount({ force: true })
95
129
  await this.mount(container)
96
130
  return this
97
131
  }
@@ -104,6 +138,14 @@ export class WuApp {
104
138
  return this._mounted && this._wu.mounted?.has(this.name)
105
139
  }
106
140
 
141
+ /**
142
+ * Check if the app is in keep-alive (hidden) state
143
+ * @returns {boolean}
144
+ */
145
+ get isHidden() {
146
+ return this._wu.isHidden(this.name)
147
+ }
148
+
107
149
  /**
108
150
  * Obtener información de la app
109
151
  * @returns {Object}
@@ -184,7 +226,7 @@ export class WuApp {
184
226
  * Destruir la app completamente
185
227
  */
186
228
  async destroy() {
187
- await this.unmount()
229
+ await this.unmount({ force: true })
188
230
  this._wu.apps.delete(this.name)
189
231
  this._mounted = false
190
232
  console.log(`🗑️ App destroyed: ${this.name}`)
@@ -346,8 +346,8 @@ export class WuCache {
346
346
  * @param {Object} entry - Entrada
347
347
  */
348
348
  saveToStorage(key, entry) {
349
+ const storage = this.getStorage();
349
350
  try {
350
- const storage = this.getStorage();
351
351
  storage.setItem(`wu_cache_${key}`, JSON.stringify(entry));
352
352
  } catch (error) {
353
353
  // Storage lleno, limpiar entradas antiguas