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.
- package/README.md +511 -977
- package/dist/wu-framework.cjs.js +3 -1
- package/dist/wu-framework.cjs.js.map +1 -0
- package/dist/wu-framework.dev.js +7533 -2761
- package/dist/wu-framework.dev.js.map +1 -1
- package/dist/wu-framework.esm.js +3 -0
- package/dist/wu-framework.esm.js.map +1 -0
- package/dist/wu-framework.umd.js +3 -1
- package/dist/wu-framework.umd.js.map +1 -0
- package/integrations/astro/README.md +127 -0
- package/integrations/astro/WuApp.astro +63 -0
- package/integrations/astro/WuShell.astro +39 -0
- package/integrations/astro/index.js +68 -0
- package/integrations/astro/package.json +38 -0
- package/integrations/astro/types.d.ts +53 -0
- package/package.json +94 -74
- package/src/adapters/angular/ai.js +30 -0
- package/src/adapters/angular/index.d.ts +154 -0
- package/src/adapters/angular/index.js +932 -0
- package/src/adapters/angular.d.ts +3 -154
- package/src/adapters/angular.js +3 -813
- package/src/adapters/index.js +35 -24
- package/src/adapters/lit/ai.js +20 -0
- package/src/adapters/lit/index.d.ts +120 -0
- package/src/adapters/lit/index.js +721 -0
- package/src/adapters/lit.d.ts +3 -120
- package/src/adapters/lit.js +3 -726
- package/src/adapters/preact/ai.js +33 -0
- package/src/adapters/preact/index.d.ts +108 -0
- package/src/adapters/preact/index.js +661 -0
- package/src/adapters/preact.d.ts +3 -108
- package/src/adapters/preact.js +3 -665
- package/src/adapters/react/ai.js +135 -0
- package/src/adapters/react/index.d.ts +246 -0
- package/src/adapters/react/index.js +689 -0
- package/src/adapters/react.d.ts +3 -212
- package/src/adapters/react.js +3 -513
- package/src/adapters/shared.js +64 -0
- package/src/adapters/solid/ai.js +32 -0
- package/src/adapters/solid/index.d.ts +101 -0
- package/src/adapters/solid/index.js +586 -0
- package/src/adapters/solid.d.ts +3 -101
- package/src/adapters/solid.js +3 -591
- package/src/adapters/svelte/ai.js +31 -0
- package/src/adapters/svelte/index.d.ts +166 -0
- package/src/adapters/svelte/index.js +798 -0
- package/src/adapters/svelte.d.ts +3 -166
- package/src/adapters/svelte.js +3 -803
- package/src/adapters/vanilla/ai.js +30 -0
- package/src/adapters/vanilla/index.d.ts +179 -0
- package/src/adapters/vanilla/index.js +785 -0
- package/src/adapters/vanilla.d.ts +3 -179
- package/src/adapters/vanilla.js +3 -791
- package/src/adapters/vue/ai.js +52 -0
- package/src/adapters/vue/index.d.ts +299 -0
- package/src/adapters/vue/index.js +608 -0
- package/src/adapters/vue.d.ts +3 -299
- package/src/adapters/vue.js +3 -611
- package/src/ai/wu-ai-actions.js +261 -0
- package/src/ai/wu-ai-browser.js +663 -0
- package/src/ai/wu-ai-context.js +332 -0
- package/src/ai/wu-ai-conversation.js +554 -0
- package/src/ai/wu-ai-permissions.js +381 -0
- package/src/ai/wu-ai-provider.js +605 -0
- package/src/ai/wu-ai-schema.js +225 -0
- package/src/ai/wu-ai-triggers.js +396 -0
- package/src/ai/wu-ai.js +474 -0
- package/src/core/wu-app.js +50 -8
- package/src/core/wu-cache.js +1 -1
- package/src/core/wu-core.js +645 -677
- package/src/core/wu-html-parser.js +121 -211
- package/src/core/wu-iframe-sandbox.js +328 -0
- package/src/core/wu-mcp-bridge.js +647 -0
- package/src/core/wu-overrides.js +510 -0
- package/src/core/wu-prefetch.js +414 -0
- package/src/core/wu-proxy-sandbox.js +398 -75
- package/src/core/wu-sandbox.js +86 -268
- package/src/core/wu-script-executor.js +79 -182
- package/src/core/wu-snapshot-sandbox.js +149 -106
- package/src/core/wu-strategies.js +13 -0
- package/src/core/wu-style-bridge.js +0 -2
- package/src/index.js +139 -665
- package/dist/wu-framework.hex.js +0 -23
- package/dist/wu-framework.min.js +0 -1
- package/dist/wu-framework.obf.js +0 -1
- package/scripts/build-protected.js +0 -366
- package/scripts/build.js +0 -212
- package/scripts/rollup-plugin-hex.js +0 -143
- package/src/core/wu-registry.js +0 -60
- package/src/core/wu-sandbox-pool.js +0 -390
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🚀 WU-FRAMEWORK VUE ADAPTER
|
|
3
|
+
*
|
|
4
|
+
* Simplifica la integración de Vue 3 con Wu Framework.
|
|
5
|
+
* Convierte componentes Vue en microfrontends con UNA línea de código.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* // Microfrontend (main.ts)
|
|
9
|
+
* import { wuVue } from 'wu-framework/adapters/vue';
|
|
10
|
+
* import App from './App.vue';
|
|
11
|
+
*
|
|
12
|
+
* wuVue.register('my-app', App);
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* // Shell (cargar microfrontend)
|
|
16
|
+
* import { WuSlot } from 'wu-framework/adapters/vue';
|
|
17
|
+
*
|
|
18
|
+
* <WuSlot name="my-app" url="http://localhost:3001" />
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
// Estado global del adapter
|
|
22
|
+
const adapterState = {
|
|
23
|
+
apps: new Map(),
|
|
24
|
+
Vue: null,
|
|
25
|
+
createApp: null,
|
|
26
|
+
initialized: false
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Detecta y obtiene Vue del contexto global o lo importa
|
|
31
|
+
*/
|
|
32
|
+
async function ensureVue() {
|
|
33
|
+
if (adapterState.initialized) return true;
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
// Intentar obtener de window
|
|
37
|
+
if (typeof window !== 'undefined' && window.Vue) {
|
|
38
|
+
adapterState.Vue = window.Vue;
|
|
39
|
+
adapterState.createApp = window.Vue.createApp;
|
|
40
|
+
adapterState.initialized = true;
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Intentar import dinámico
|
|
45
|
+
const Vue = await import('vue');
|
|
46
|
+
adapterState.Vue = Vue;
|
|
47
|
+
adapterState.createApp = Vue.createApp;
|
|
48
|
+
adapterState.initialized = true;
|
|
49
|
+
return true;
|
|
50
|
+
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error('[WuVue] Failed to load Vue:', error);
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Obtiene la instancia de Wu Framework
|
|
59
|
+
*/
|
|
60
|
+
function getWuInstance() {
|
|
61
|
+
if (typeof window === 'undefined') return null;
|
|
62
|
+
|
|
63
|
+
return window.wu
|
|
64
|
+
|| window.parent?.wu
|
|
65
|
+
|| window.top?.wu
|
|
66
|
+
|| null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Espera a que Wu Framework esté disponible
|
|
71
|
+
*/
|
|
72
|
+
function waitForWu(timeout = 5000) {
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
const wu = getWuInstance();
|
|
75
|
+
if (wu) {
|
|
76
|
+
resolve(wu);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const startTime = Date.now();
|
|
81
|
+
|
|
82
|
+
const handleWuReady = () => {
|
|
83
|
+
cleanup();
|
|
84
|
+
resolve(getWuInstance());
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
window.addEventListener('wu:ready', handleWuReady);
|
|
88
|
+
window.addEventListener('wu:app:ready', handleWuReady);
|
|
89
|
+
|
|
90
|
+
const checkInterval = setInterval(() => {
|
|
91
|
+
const wu = getWuInstance();
|
|
92
|
+
if (wu) {
|
|
93
|
+
cleanup();
|
|
94
|
+
resolve(wu);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (Date.now() - startTime > timeout) {
|
|
99
|
+
cleanup();
|
|
100
|
+
reject(new Error(`Wu Framework not found after ${timeout}ms`));
|
|
101
|
+
}
|
|
102
|
+
}, 200);
|
|
103
|
+
|
|
104
|
+
function cleanup() {
|
|
105
|
+
clearInterval(checkInterval);
|
|
106
|
+
window.removeEventListener('wu:ready', handleWuReady);
|
|
107
|
+
window.removeEventListener('wu:app:ready', handleWuReady);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Registra un componente Vue como microfrontend
|
|
114
|
+
*
|
|
115
|
+
* @param {string} appName - Nombre único del microfrontend (debe coincidir con wu.json)
|
|
116
|
+
* @param {Object} RootComponent - Componente Vue principal (App.vue)
|
|
117
|
+
* @param {Object} options - Opciones adicionales
|
|
118
|
+
* @param {Function} options.setup - Función para configurar la app Vue (plugins, router, etc.)
|
|
119
|
+
* @param {Object} options.props - Props iniciales para el componente
|
|
120
|
+
* @param {Function} options.onMount - Callback después de montar
|
|
121
|
+
* @param {Function} options.onUnmount - Callback antes de desmontar
|
|
122
|
+
* @param {boolean} options.standalone - Permitir ejecución standalone (default: true)
|
|
123
|
+
* @param {string} options.standaloneContainer - Selector para modo standalone (default: '#app')
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* // Básico
|
|
127
|
+
* wuVue.register('my-app', App);
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* // Con plugins (Pinia, Router, etc.)
|
|
131
|
+
* wuVue.register('my-app', App, {
|
|
132
|
+
* setup: (app) => {
|
|
133
|
+
* app.use(createPinia());
|
|
134
|
+
* app.use(router);
|
|
135
|
+
* app.component('MyGlobal', MyComponent);
|
|
136
|
+
* }
|
|
137
|
+
* });
|
|
138
|
+
*/
|
|
139
|
+
async function register(appName, RootComponent, options = {}) {
|
|
140
|
+
const {
|
|
141
|
+
setup = null,
|
|
142
|
+
props = {},
|
|
143
|
+
onMount = null,
|
|
144
|
+
onUnmount = null,
|
|
145
|
+
standalone = true,
|
|
146
|
+
standaloneContainer = '#app'
|
|
147
|
+
} = options;
|
|
148
|
+
|
|
149
|
+
// Asegurar que Vue está disponible
|
|
150
|
+
const hasVue = await ensureVue();
|
|
151
|
+
if (!hasVue) {
|
|
152
|
+
console.error(`[WuVue] Cannot register ${appName}: Vue not available`);
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const { createApp } = adapterState;
|
|
157
|
+
|
|
158
|
+
// Función de mount interna
|
|
159
|
+
const mountApp = (container) => {
|
|
160
|
+
if (!container) {
|
|
161
|
+
console.error(`[WuVue] Mount failed for ${appName}: container is null`);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Evitar doble mount
|
|
166
|
+
if (adapterState.apps.has(appName)) {
|
|
167
|
+
console.warn(`[WuVue] ${appName} already mounted, unmounting first`);
|
|
168
|
+
unmountApp();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
// Detectar si el container está dentro de un Shadow DOM
|
|
173
|
+
let shadowRoot = null;
|
|
174
|
+
let element = container;
|
|
175
|
+
while (element && element !== document.body) {
|
|
176
|
+
if (element.getRootNode && element.getRootNode() instanceof ShadowRoot) {
|
|
177
|
+
shadowRoot = element.getRootNode();
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
element = element.parentElement || element.host;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Crear la aplicación Vue
|
|
184
|
+
const app = createApp(RootComponent, props);
|
|
185
|
+
|
|
186
|
+
// Ejecutar setup personalizado (plugins, router, etc.)
|
|
187
|
+
if (setup && typeof setup === 'function') {
|
|
188
|
+
setup(app);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Proveer información del contexto Wu
|
|
192
|
+
app.provide('wuAppName', appName);
|
|
193
|
+
app.provide('wuInstance', getWuInstance());
|
|
194
|
+
|
|
195
|
+
// Montar
|
|
196
|
+
app.mount(container);
|
|
197
|
+
|
|
198
|
+
// Si está en Shadow DOM, copiar estilos de Vue al Shadow DOM
|
|
199
|
+
if (shadowRoot) {
|
|
200
|
+
// Esperar un poco para que Vue inyecte los estilos en el head
|
|
201
|
+
setTimeout(() => {
|
|
202
|
+
const vueStyles = document.querySelectorAll('style[data-vite-dev-id*="/' + appName + '/"], style[data-vite-dev-id*="\\' + appName + '\\"]');
|
|
203
|
+
vueStyles.forEach(style => {
|
|
204
|
+
// Verificar que no esté ya en el Shadow DOM
|
|
205
|
+
const viteId = style.getAttribute('data-vite-dev-id');
|
|
206
|
+
if (viteId && !shadowRoot.querySelector(`style[data-vite-dev-id="${viteId}"]`)) {
|
|
207
|
+
const clonedStyle = style.cloneNode(true);
|
|
208
|
+
shadowRoot.insertBefore(clonedStyle, shadowRoot.firstChild);
|
|
209
|
+
console.log(`[WuVue] ✅ Injected style into Shadow DOM: ${viteId}`);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// También copiar estilos que contengan rutas del app en el viteId
|
|
214
|
+
const allStyles = document.querySelectorAll('style[data-vite-dev-id]');
|
|
215
|
+
allStyles.forEach(style => {
|
|
216
|
+
const viteId = style.getAttribute('data-vite-dev-id');
|
|
217
|
+
if (viteId && (viteId.includes(`/${appName}/`) || viteId.includes(`\\${appName}\\`))) {
|
|
218
|
+
if (!shadowRoot.querySelector(`style[data-vite-dev-id="${viteId}"]`)) {
|
|
219
|
+
const clonedStyle = style.cloneNode(true);
|
|
220
|
+
shadowRoot.insertBefore(clonedStyle, shadowRoot.firstChild);
|
|
221
|
+
console.log(`[WuVue] ✅ Injected app style into Shadow DOM: ${viteId}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
}, 100);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Guardar referencia
|
|
229
|
+
adapterState.apps.set(appName, { app, container });
|
|
230
|
+
|
|
231
|
+
console.log(`[WuVue] ✅ ${appName} mounted successfully`);
|
|
232
|
+
|
|
233
|
+
if (onMount) {
|
|
234
|
+
onMount(container, app);
|
|
235
|
+
}
|
|
236
|
+
} catch (error) {
|
|
237
|
+
console.error(`[WuVue] Mount error for ${appName}:`, error);
|
|
238
|
+
throw error;
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
// Función de unmount interna
|
|
243
|
+
const unmountApp = (container) => {
|
|
244
|
+
const instance = adapterState.apps.get(appName);
|
|
245
|
+
|
|
246
|
+
if (instance) {
|
|
247
|
+
try {
|
|
248
|
+
if (onUnmount) {
|
|
249
|
+
onUnmount(instance.container, instance.app);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
instance.app.unmount();
|
|
253
|
+
adapterState.apps.delete(appName);
|
|
254
|
+
|
|
255
|
+
console.log(`[WuVue] ✅ ${appName} unmounted successfully`);
|
|
256
|
+
} catch (error) {
|
|
257
|
+
console.error(`[WuVue] Unmount error for ${appName}:`, error);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Limpiar container si se proporciona
|
|
262
|
+
if (container) {
|
|
263
|
+
container.innerHTML = '';
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
// Intentar registrar con Wu Framework
|
|
268
|
+
try {
|
|
269
|
+
const wu = await waitForWu(3000);
|
|
270
|
+
|
|
271
|
+
wu.define(appName, {
|
|
272
|
+
mount: mountApp,
|
|
273
|
+
unmount: unmountApp
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
console.log(`[WuVue] ✅ ${appName} registered with Wu Framework`);
|
|
277
|
+
return true;
|
|
278
|
+
|
|
279
|
+
} catch (error) {
|
|
280
|
+
console.warn(`[WuVue] Wu Framework not available for ${appName}`);
|
|
281
|
+
|
|
282
|
+
// Modo standalone si está habilitado
|
|
283
|
+
if (standalone) {
|
|
284
|
+
const containerElement = document.querySelector(standaloneContainer);
|
|
285
|
+
|
|
286
|
+
if (containerElement) {
|
|
287
|
+
console.log(`[WuVue] Running ${appName} in standalone mode`);
|
|
288
|
+
mountApp(containerElement);
|
|
289
|
+
return true;
|
|
290
|
+
} else {
|
|
291
|
+
console.warn(`[WuVue] Standalone container ${standaloneContainer} not found`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Crea un componente Vue para cargar microfrontends (para el Shell)
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
* <script setup>
|
|
304
|
+
* import { WuSlot } from 'wu-framework/adapters/vue';
|
|
305
|
+
* </script>
|
|
306
|
+
*
|
|
307
|
+
* <template>
|
|
308
|
+
* <WuSlot name="my-app" url="http://localhost:3001" />
|
|
309
|
+
* </template>
|
|
310
|
+
*/
|
|
311
|
+
const WuSlot = {
|
|
312
|
+
name: 'WuSlot',
|
|
313
|
+
|
|
314
|
+
props: {
|
|
315
|
+
name: {
|
|
316
|
+
type: String,
|
|
317
|
+
required: true
|
|
318
|
+
},
|
|
319
|
+
url: {
|
|
320
|
+
type: String,
|
|
321
|
+
required: true
|
|
322
|
+
},
|
|
323
|
+
appName: {
|
|
324
|
+
type: String,
|
|
325
|
+
default: null
|
|
326
|
+
},
|
|
327
|
+
fallbackText: {
|
|
328
|
+
type: String,
|
|
329
|
+
default: 'Loading...'
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
|
|
333
|
+
emits: ['load', 'error', 'mount', 'unmount'],
|
|
334
|
+
|
|
335
|
+
data() {
|
|
336
|
+
return {
|
|
337
|
+
loading: true,
|
|
338
|
+
error: null,
|
|
339
|
+
appInstance: null,
|
|
340
|
+
containerId: null
|
|
341
|
+
};
|
|
342
|
+
},
|
|
343
|
+
|
|
344
|
+
computed: {
|
|
345
|
+
actualAppName() {
|
|
346
|
+
return this.appName || this.name;
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
|
|
350
|
+
async mounted() {
|
|
351
|
+
await this.mountMicrofrontend();
|
|
352
|
+
},
|
|
353
|
+
|
|
354
|
+
beforeUnmount() {
|
|
355
|
+
this.unmountMicrofrontend();
|
|
356
|
+
},
|
|
357
|
+
|
|
358
|
+
methods: {
|
|
359
|
+
async mountMicrofrontend() {
|
|
360
|
+
try {
|
|
361
|
+
this.loading = true;
|
|
362
|
+
this.error = null;
|
|
363
|
+
|
|
364
|
+
const wu = getWuInstance();
|
|
365
|
+
if (!wu) {
|
|
366
|
+
throw new Error('Wu Framework not initialized');
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Crear container único
|
|
370
|
+
this.containerId = `wu-slot-${this.actualAppName}-${Date.now()}`;
|
|
371
|
+
const innerContainer = document.createElement('div');
|
|
372
|
+
innerContainer.id = this.containerId;
|
|
373
|
+
innerContainer.style.width = '100%';
|
|
374
|
+
innerContainer.style.height = '100%';
|
|
375
|
+
|
|
376
|
+
this.$refs.container.innerHTML = '';
|
|
377
|
+
this.$refs.container.appendChild(innerContainer);
|
|
378
|
+
|
|
379
|
+
// Crear y montar la app
|
|
380
|
+
const app = wu.app(this.actualAppName, {
|
|
381
|
+
url: this.url,
|
|
382
|
+
container: `#${this.containerId}`,
|
|
383
|
+
autoInit: true
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
this.appInstance = app;
|
|
387
|
+
await app.mount();
|
|
388
|
+
|
|
389
|
+
this.loading = false;
|
|
390
|
+
this.$emit('load', { name: this.actualAppName, url: this.url });
|
|
391
|
+
this.$emit('mount', { name: this.actualAppName, container: innerContainer });
|
|
392
|
+
|
|
393
|
+
} catch (err) {
|
|
394
|
+
console.error(`[WuSlot] Error loading ${this.actualAppName}:`, err);
|
|
395
|
+
this.error = err.message || 'Failed to load microfrontend';
|
|
396
|
+
this.loading = false;
|
|
397
|
+
this.$emit('error', err);
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
|
|
401
|
+
async unmountMicrofrontend() {
|
|
402
|
+
if (this.appInstance) {
|
|
403
|
+
this.$emit('unmount', { name: this.actualAppName });
|
|
404
|
+
|
|
405
|
+
try {
|
|
406
|
+
await this.appInstance.unmount();
|
|
407
|
+
} catch (err) {
|
|
408
|
+
console.warn(`[WuSlot] Error unmounting ${this.actualAppName}:`, err);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
this.appInstance = null;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
|
|
416
|
+
template: `
|
|
417
|
+
<div
|
|
418
|
+
ref="container"
|
|
419
|
+
class="wu-slot"
|
|
420
|
+
:class="{ 'wu-slot-loading': loading, 'wu-slot-error': error }"
|
|
421
|
+
:data-wu-app="actualAppName"
|
|
422
|
+
:data-wu-url="url"
|
|
423
|
+
style="min-height: 100px; position: relative;"
|
|
424
|
+
>
|
|
425
|
+
<div v-if="error" class="wu-slot-error-message" style="padding: 1rem; border: 1px solid #f5c6cb; border-radius: 4px; background: #f8d7da; color: #721c24;">
|
|
426
|
+
<strong>Error loading {{ name }}</strong>
|
|
427
|
+
<p style="margin: 0.5rem 0 0 0;">{{ error }}</p>
|
|
428
|
+
</div>
|
|
429
|
+
<div v-else-if="loading" class="wu-slot-loading-message" style="display: flex; align-items: center; justify-content: center; padding: 2rem; color: #666;">
|
|
430
|
+
{{ fallbackText || 'Loading ' + name + '...' }}
|
|
431
|
+
</div>
|
|
432
|
+
</div>
|
|
433
|
+
`
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Composable para usar el EventBus de Wu Framework en Vue 3
|
|
438
|
+
*
|
|
439
|
+
* @example
|
|
440
|
+
* <script setup>
|
|
441
|
+
* import { useWuEvents } from 'wu-framework/adapters/vue';
|
|
442
|
+
*
|
|
443
|
+
* const { emit, on } = useWuEvents();
|
|
444
|
+
*
|
|
445
|
+
* onMounted(() => {
|
|
446
|
+
* on('user:login', (data) => console.log(data));
|
|
447
|
+
* });
|
|
448
|
+
* </script>
|
|
449
|
+
*/
|
|
450
|
+
function useWuEvents() {
|
|
451
|
+
const subscriptions = [];
|
|
452
|
+
|
|
453
|
+
const emit = (event, data, options) => {
|
|
454
|
+
const wu = getWuInstance();
|
|
455
|
+
if (wu?.eventBus) {
|
|
456
|
+
wu.eventBus.emit(event, data, options);
|
|
457
|
+
} else {
|
|
458
|
+
console.warn('[useWuEvents] Wu Framework not available');
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
const on = (event, callback) => {
|
|
463
|
+
const wu = getWuInstance();
|
|
464
|
+
if (wu?.eventBus) {
|
|
465
|
+
const unsubscribe = wu.eventBus.on(event, callback);
|
|
466
|
+
subscriptions.push(unsubscribe);
|
|
467
|
+
return unsubscribe;
|
|
468
|
+
}
|
|
469
|
+
console.warn('[useWuEvents] Wu Framework not available');
|
|
470
|
+
return () => {};
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
const once = (event, callback) => {
|
|
474
|
+
const wu = getWuInstance();
|
|
475
|
+
if (wu?.eventBus) {
|
|
476
|
+
return wu.eventBus.once(event, callback);
|
|
477
|
+
}
|
|
478
|
+
console.warn('[useWuEvents] Wu Framework not available');
|
|
479
|
+
return () => {};
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
const off = (event, callback) => {
|
|
483
|
+
const wu = getWuInstance();
|
|
484
|
+
if (wu?.eventBus) {
|
|
485
|
+
wu.eventBus.off(event, callback);
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
// Cleanup - llamar en onUnmounted
|
|
490
|
+
const cleanup = () => {
|
|
491
|
+
subscriptions.forEach(unsub => unsub());
|
|
492
|
+
subscriptions.length = 0;
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
return { emit, on, once, off, cleanup };
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Composable para usar el Store de Wu Framework en Vue 3
|
|
500
|
+
*
|
|
501
|
+
* @example
|
|
502
|
+
* <script setup>
|
|
503
|
+
* import { useWuStore } from 'wu-framework/adapters/vue';
|
|
504
|
+
*
|
|
505
|
+
* const { state, setState, getState } = useWuStore('user');
|
|
506
|
+
*
|
|
507
|
+
* // state es reactivo!
|
|
508
|
+
* console.log(state.value);
|
|
509
|
+
* </script>
|
|
510
|
+
*/
|
|
511
|
+
function useWuStore(namespace = '') {
|
|
512
|
+
// Importar ref y watch de Vue si están disponibles
|
|
513
|
+
const Vue = adapterState.Vue;
|
|
514
|
+
let state;
|
|
515
|
+
let unsubscribe = null;
|
|
516
|
+
|
|
517
|
+
// Crear estado reactivo si Vue está disponible
|
|
518
|
+
if (Vue?.ref) {
|
|
519
|
+
const wu = getWuInstance();
|
|
520
|
+
const initialValue = wu?.store?.get(namespace) || null;
|
|
521
|
+
state = Vue.ref(initialValue);
|
|
522
|
+
|
|
523
|
+
// Suscribirse a cambios
|
|
524
|
+
if (wu?.store) {
|
|
525
|
+
const pattern = namespace ? `${namespace}.*` : '*';
|
|
526
|
+
unsubscribe = wu.store.on(pattern, () => {
|
|
527
|
+
state.value = wu.store.get(namespace);
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
} else {
|
|
531
|
+
// Fallback sin reactividad
|
|
532
|
+
state = { value: null };
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const setState = (path, value) => {
|
|
536
|
+
const wu = getWuInstance();
|
|
537
|
+
if (wu?.store) {
|
|
538
|
+
const fullPath = namespace ? `${namespace}.${path}` : path;
|
|
539
|
+
wu.store.set(fullPath, value);
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
const getState = (path = '') => {
|
|
544
|
+
const wu = getWuInstance();
|
|
545
|
+
if (wu?.store) {
|
|
546
|
+
const fullPath = namespace ? (path ? `${namespace}.${path}` : namespace) : path;
|
|
547
|
+
return wu.store.get(fullPath);
|
|
548
|
+
}
|
|
549
|
+
return null;
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
const cleanup = () => {
|
|
553
|
+
if (unsubscribe) {
|
|
554
|
+
unsubscribe();
|
|
555
|
+
unsubscribe = null;
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
return { state, setState, getState, cleanup };
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Plugin de Vue para instalar Wu Framework globalmente
|
|
564
|
+
*
|
|
565
|
+
* @example
|
|
566
|
+
* import { createApp } from 'vue';
|
|
567
|
+
* import { wuVuePlugin } from 'wu-framework/adapters/vue';
|
|
568
|
+
*
|
|
569
|
+
* const app = createApp(App);
|
|
570
|
+
* app.use(wuVuePlugin);
|
|
571
|
+
*/
|
|
572
|
+
const wuVuePlugin = {
|
|
573
|
+
install(app, options = {}) {
|
|
574
|
+
// Registrar componente WuSlot globalmente
|
|
575
|
+
app.component('WuSlot', WuSlot);
|
|
576
|
+
|
|
577
|
+
// Proveer acceso global a Wu
|
|
578
|
+
app.provide('wu', getWuInstance());
|
|
579
|
+
|
|
580
|
+
// Agregar propiedades globales
|
|
581
|
+
app.config.globalProperties.$wu = getWuInstance();
|
|
582
|
+
app.config.globalProperties.$wuEvents = useWuEvents();
|
|
583
|
+
app.config.globalProperties.$wuStore = (ns) => useWuStore(ns);
|
|
584
|
+
|
|
585
|
+
console.log('[WuVue] Plugin installed');
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
// ============================================
|
|
590
|
+
// AI INTEGRATION
|
|
591
|
+
// ============================================
|
|
592
|
+
export { createUseWuAI, useWuAI } from './ai.js';
|
|
593
|
+
import { createUseWuAI, useWuAI } from './ai.js';
|
|
594
|
+
|
|
595
|
+
// API pública del adapter
|
|
596
|
+
export const wuVue = {
|
|
597
|
+
register,
|
|
598
|
+
WuSlot,
|
|
599
|
+
useWuEvents,
|
|
600
|
+
useWuStore,
|
|
601
|
+
wuVuePlugin,
|
|
602
|
+
createUseWuAI,
|
|
603
|
+
useWuAI,
|
|
604
|
+
getWuInstance,
|
|
605
|
+
waitForWu
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
export default wuVue;
|