wu-framework 1.1.16 → 1.1.18
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 +52 -20
- package/package.json +48 -3
- package/src/adapters/alpine/index.d.ts +60 -0
- package/src/adapters/alpine/index.js +231 -0
- package/src/adapters/alpine.d.ts +3 -0
- package/src/adapters/alpine.js +3 -0
- package/src/adapters/htmx/index.d.ts +60 -0
- package/src/adapters/htmx/index.js +242 -0
- package/src/adapters/htmx.d.ts +3 -0
- package/src/adapters/htmx.js +3 -0
- package/src/adapters/index.js +60 -3
- package/src/adapters/qwik/index.d.ts +52 -0
- package/src/adapters/qwik/index.js +214 -0
- package/src/adapters/qwik.d.ts +3 -0
- package/src/adapters/qwik.js +3 -0
- package/src/adapters/react/ai.js +135 -135
- package/src/adapters/react/index.d.ts +246 -246
- package/src/adapters/react/index.js +695 -695
- package/src/adapters/stencil/index.d.ts +54 -0
- package/src/adapters/stencil/index.js +228 -0
- package/src/adapters/stencil.d.ts +3 -0
- package/src/adapters/stencil.js +3 -0
- package/src/adapters/stimulus/index.d.ts +60 -0
- package/src/adapters/stimulus/index.js +255 -0
- package/src/adapters/stimulus.d.ts +3 -0
- package/src/adapters/stimulus.js +3 -0
- package/src/adapters/svelte/index.js +1 -1
- package/src/adapters/vanilla/index.js +1 -1
- package/src/adapters/vue/index.js +8 -0
- package/src/core/wu-cache.js +24 -3
- package/src/core/wu-core.js +15 -1
- package/src/core/wu-error-boundary.js +17 -3
- package/src/core/wu-event-bus.js +43 -1
- package/src/core/wu-html-parser.js +13 -4
- package/src/core/wu-loader.js +162 -50
- package/src/core/wu-logger.js +21 -13
- package/src/core/wu-manifest.js +23 -0
- package/src/core/wu-plugin.js +57 -4
- package/src/core/wu-proxy-sandbox.js +2 -1
- package/src/core/wu-script-executor.js +48 -0
- package/src/core/wu-store.js +13 -3
- package/src/index.d.ts +317 -0
- package/src/index.js +11 -1
- package/dist/wu-framework.cjs.js +0 -3
- package/dist/wu-framework.cjs.js.map +0 -1
- package/dist/wu-framework.dev.js +0 -15302
- package/dist/wu-framework.dev.js.map +0 -1
- package/dist/wu-framework.esm.js +0 -3
- package/dist/wu-framework.esm.js.map +0 -1
- package/dist/wu-framework.umd.js +0 -3
- package/dist/wu-framework.umd.js.map +0 -1
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WU-FRAMEWORK HTMX ADAPTER
|
|
3
|
+
*
|
|
4
|
+
* Bridges HTMX's hypermedia-driven approach into Wu Framework's
|
|
5
|
+
* microfrontend graph. HTMX replaces JavaScript-heavy SPAs with
|
|
6
|
+
* HTML-over-the-wire -- the server returns HTML fragments and HTMX
|
|
7
|
+
* swaps them into the DOM. This adapter injects an HTML template
|
|
8
|
+
* containing hx-* attributes, then calls htmx.process() to activate
|
|
9
|
+
* the HTMX engine on that subtree.
|
|
10
|
+
*
|
|
11
|
+
* The inversion is elegant: instead of the client framework owning
|
|
12
|
+
* the render cycle, the server does. Wu merely provides the container
|
|
13
|
+
* and lifecycle boundaries.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* import { wuHtmx } from 'wu-framework/adapters/htmx';
|
|
17
|
+
*
|
|
18
|
+
* wuHtmx.register('search', {
|
|
19
|
+
* template: `
|
|
20
|
+
* <div>
|
|
21
|
+
* <input type="text" name="q"
|
|
22
|
+
* hx-get="/api/search"
|
|
23
|
+
* hx-trigger="keyup changed delay:300ms"
|
|
24
|
+
* hx-target="#results" />
|
|
25
|
+
* <div id="results"></div>
|
|
26
|
+
* </div>
|
|
27
|
+
* `
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* // With base URL configuration for HTMX endpoints
|
|
32
|
+
* wuHtmx.register('feed', {
|
|
33
|
+
* template: '<div hx-get="/api/feed" hx-trigger="load"></div>',
|
|
34
|
+
* baseUrl: 'https://api.example.com'
|
|
35
|
+
* }, {
|
|
36
|
+
* onMount: (container) => console.log('HTMX fragment active')
|
|
37
|
+
* });
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
// Adapter-scoped state
|
|
41
|
+
const adapterState = {
|
|
42
|
+
apps: new Map()
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Obtiene la instancia de Wu Framework
|
|
47
|
+
*/
|
|
48
|
+
function getWuInstance() {
|
|
49
|
+
if (typeof window === 'undefined') return null;
|
|
50
|
+
|
|
51
|
+
return window.wu
|
|
52
|
+
|| window.parent?.wu
|
|
53
|
+
|| window.top?.wu
|
|
54
|
+
|| null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Espera a que Wu Framework este disponible
|
|
59
|
+
*/
|
|
60
|
+
function waitForWu(timeout = 5000) {
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
const wu = getWuInstance();
|
|
63
|
+
if (wu) {
|
|
64
|
+
resolve(wu);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const startTime = Date.now();
|
|
69
|
+
|
|
70
|
+
const handleWuReady = () => {
|
|
71
|
+
cleanup();
|
|
72
|
+
resolve(getWuInstance());
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
window.addEventListener('wu:ready', handleWuReady);
|
|
76
|
+
window.addEventListener('wu:app:ready', handleWuReady);
|
|
77
|
+
|
|
78
|
+
const checkInterval = setInterval(() => {
|
|
79
|
+
const wu = getWuInstance();
|
|
80
|
+
if (wu) {
|
|
81
|
+
cleanup();
|
|
82
|
+
resolve(wu);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (Date.now() - startTime > timeout) {
|
|
87
|
+
cleanup();
|
|
88
|
+
reject(new Error(`Wu Framework not found after ${timeout}ms`));
|
|
89
|
+
}
|
|
90
|
+
}, 200);
|
|
91
|
+
|
|
92
|
+
function cleanup() {
|
|
93
|
+
clearInterval(checkInterval);
|
|
94
|
+
window.removeEventListener('wu:ready', handleWuReady);
|
|
95
|
+
window.removeEventListener('wu:app:ready', handleWuReady);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Registra una app HTMX como microfrontend
|
|
102
|
+
*
|
|
103
|
+
* @param {string} appName - Nombre unico del microfrontend
|
|
104
|
+
* @param {Object} config - Configuracion de la app HTMX
|
|
105
|
+
* @param {string} config.template - HTML string con atributos hx-*
|
|
106
|
+
* @param {string} [config.baseUrl] - URL base para endpoints HTMX (se inyecta como hx-base)
|
|
107
|
+
* @param {Object} [options] - Opciones adicionales
|
|
108
|
+
* @param {Function} [options.onMount] - Callback despues de montar
|
|
109
|
+
* @param {Function} [options.onUnmount] - Callback antes de desmontar
|
|
110
|
+
* @param {boolean} [options.standalone=true] - Permitir ejecucion standalone
|
|
111
|
+
* @param {string} [options.standaloneContainer='#app'] - Selector para modo standalone
|
|
112
|
+
* @returns {Promise<boolean>} true si el registro fue exitoso
|
|
113
|
+
*/
|
|
114
|
+
async function register(appName, config, options = {}) {
|
|
115
|
+
const { template, baseUrl = null } = config;
|
|
116
|
+
|
|
117
|
+
const {
|
|
118
|
+
onMount = null,
|
|
119
|
+
onUnmount = null,
|
|
120
|
+
standalone = true,
|
|
121
|
+
standaloneContainer = '#app'
|
|
122
|
+
} = options;
|
|
123
|
+
|
|
124
|
+
if (!template || typeof template !== 'string') {
|
|
125
|
+
throw new Error(`[WuHtmx] template (HTML string with hx-* attributes) is required for ${appName}`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Mount function
|
|
129
|
+
const mountApp = (container) => {
|
|
130
|
+
if (!container) {
|
|
131
|
+
console.error(`[WuHtmx] Mount failed for ${appName}: container is null`);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Prevent double-mount
|
|
136
|
+
if (adapterState.apps.has(appName)) {
|
|
137
|
+
console.warn(`[WuHtmx] ${appName} already mounted, unmounting first`);
|
|
138
|
+
unmountApp();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
container.innerHTML = '';
|
|
143
|
+
|
|
144
|
+
// Inject template
|
|
145
|
+
container.innerHTML = template;
|
|
146
|
+
|
|
147
|
+
// If baseUrl is provided, rewrite relative hx-get/hx-post/etc. paths
|
|
148
|
+
if (baseUrl) {
|
|
149
|
+
const hxAttributes = ['hx-get', 'hx-post', 'hx-put', 'hx-patch', 'hx-delete'];
|
|
150
|
+
hxAttributes.forEach((attr) => {
|
|
151
|
+
container.querySelectorAll(`[${attr}]`).forEach((el) => {
|
|
152
|
+
const val = el.getAttribute(attr);
|
|
153
|
+
if (val && val.startsWith('/')) {
|
|
154
|
+
el.setAttribute(attr, baseUrl + val);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Activate HTMX on this subtree
|
|
161
|
+
if (window.htmx) {
|
|
162
|
+
window.htmx.process(container);
|
|
163
|
+
} else {
|
|
164
|
+
console.warn(`[WuHtmx] window.htmx not found - template inserted but hx-* attributes will not function`);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
adapterState.apps.set(appName, { container, config });
|
|
168
|
+
|
|
169
|
+
console.log(`[WuHtmx] ${appName} mounted successfully`);
|
|
170
|
+
|
|
171
|
+
if (onMount) {
|
|
172
|
+
onMount(container);
|
|
173
|
+
}
|
|
174
|
+
} catch (error) {
|
|
175
|
+
console.error(`[WuHtmx] Mount error for ${appName}:`, error);
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// Unmount function
|
|
181
|
+
const unmountApp = (container) => {
|
|
182
|
+
const appData = adapterState.apps.get(appName);
|
|
183
|
+
|
|
184
|
+
if (appData) {
|
|
185
|
+
try {
|
|
186
|
+
if (onUnmount) {
|
|
187
|
+
onUnmount(appData.container);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Clear all HTMX-managed event listeners by wiping the subtree
|
|
191
|
+
appData.container.innerHTML = '';
|
|
192
|
+
adapterState.apps.delete(appName);
|
|
193
|
+
|
|
194
|
+
console.log(`[WuHtmx] ${appName} unmounted successfully`);
|
|
195
|
+
} catch (error) {
|
|
196
|
+
console.error(`[WuHtmx] Unmount error for ${appName}:`, error);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (container) {
|
|
201
|
+
container.innerHTML = '';
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// Register with Wu Framework or fall back to standalone
|
|
206
|
+
try {
|
|
207
|
+
const wu = await waitForWu(3000);
|
|
208
|
+
|
|
209
|
+
wu.define(appName, {
|
|
210
|
+
mount: mountApp,
|
|
211
|
+
unmount: unmountApp
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
console.log(`[WuHtmx] ${appName} registered with Wu Framework`);
|
|
215
|
+
return true;
|
|
216
|
+
|
|
217
|
+
} catch (error) {
|
|
218
|
+
console.warn(`[WuHtmx] Wu Framework not available for ${appName}`);
|
|
219
|
+
|
|
220
|
+
if (standalone) {
|
|
221
|
+
const containerElement = document.querySelector(standaloneContainer);
|
|
222
|
+
|
|
223
|
+
if (containerElement) {
|
|
224
|
+
console.log(`[WuHtmx] Running ${appName} in standalone mode`);
|
|
225
|
+
mountApp(containerElement);
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Public API
|
|
235
|
+
export const wuHtmx = {
|
|
236
|
+
register,
|
|
237
|
+
getWuInstance,
|
|
238
|
+
waitForWu
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
export { register, getWuInstance, waitForWu };
|
|
242
|
+
export default wuHtmx;
|
package/src/adapters/index.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* 🚀 WU-FRAMEWORK ADAPTERS
|
|
3
3
|
*
|
|
4
4
|
* Adapters oficiales para integrar Wu Framework con frameworks populares.
|
|
5
|
-
* Soporta: React, Vue, Angular, Svelte, Preact, Solid.js, Lit, Vanilla JS
|
|
5
|
+
* Soporta: React, Vue, Angular, Svelte, Preact, Solid.js, Lit, Vanilla JS,
|
|
6
|
+
* Alpine.js, Qwik, Stencil, HTMX, Stimulus
|
|
6
7
|
*
|
|
7
8
|
* Cada adapter incluye integración AI (Paradigma C: IA como Director de Orquesta).
|
|
8
9
|
*
|
|
@@ -16,12 +17,18 @@
|
|
|
16
17
|
* import { wuSolid } from 'wu-framework/adapters/solid';
|
|
17
18
|
* import { wuLit } from 'wu-framework/adapters/lit';
|
|
18
19
|
* import { wuVanilla } from 'wu-framework/adapters/vanilla';
|
|
20
|
+
* import { wuAlpine } from 'wu-framework/adapters/alpine';
|
|
21
|
+
* import { wuQwik } from 'wu-framework/adapters/qwik';
|
|
22
|
+
* import { wuStencil } from 'wu-framework/adapters/stencil';
|
|
23
|
+
* import { wuHtmx } from 'wu-framework/adapters/htmx';
|
|
24
|
+
* import { wuStimulus } from 'wu-framework/adapters/stimulus';
|
|
19
25
|
*
|
|
20
26
|
* @example
|
|
21
27
|
* // Importar todo
|
|
22
28
|
* import {
|
|
23
29
|
* wuReact, wuVue, wuAngular, wuSvelte,
|
|
24
|
-
* wuPreact, wuSolid, wuLit, wuVanilla
|
|
30
|
+
* wuPreact, wuSolid, wuLit, wuVanilla,
|
|
31
|
+
* wuAlpine, wuQwik, wuStencil, wuHtmx, wuStimulus
|
|
25
32
|
* } from 'wu-framework/adapters';
|
|
26
33
|
*/
|
|
27
34
|
|
|
@@ -134,6 +141,46 @@ export {
|
|
|
134
141
|
useWuAI as useVanillaWuAI
|
|
135
142
|
} from './vanilla/index.js';
|
|
136
143
|
|
|
144
|
+
// ============================================
|
|
145
|
+
// ALPINE.JS ADAPTER
|
|
146
|
+
// ============================================
|
|
147
|
+
export {
|
|
148
|
+
wuAlpine,
|
|
149
|
+
register as registerAlpine
|
|
150
|
+
} from './alpine/index.js';
|
|
151
|
+
|
|
152
|
+
// ============================================
|
|
153
|
+
// QWIK ADAPTER
|
|
154
|
+
// ============================================
|
|
155
|
+
export {
|
|
156
|
+
wuQwik,
|
|
157
|
+
register as registerQwik
|
|
158
|
+
} from './qwik/index.js';
|
|
159
|
+
|
|
160
|
+
// ============================================
|
|
161
|
+
// STENCIL ADAPTER
|
|
162
|
+
// ============================================
|
|
163
|
+
export {
|
|
164
|
+
wuStencil,
|
|
165
|
+
register as registerStencil
|
|
166
|
+
} from './stencil/index.js';
|
|
167
|
+
|
|
168
|
+
// ============================================
|
|
169
|
+
// HTMX ADAPTER
|
|
170
|
+
// ============================================
|
|
171
|
+
export {
|
|
172
|
+
wuHtmx,
|
|
173
|
+
register as registerHtmx
|
|
174
|
+
} from './htmx/index.js';
|
|
175
|
+
|
|
176
|
+
// ============================================
|
|
177
|
+
// STIMULUS ADAPTER
|
|
178
|
+
// ============================================
|
|
179
|
+
export {
|
|
180
|
+
wuStimulus,
|
|
181
|
+
register as registerStimulus
|
|
182
|
+
} from './stimulus/index.js';
|
|
183
|
+
|
|
137
184
|
// ============================================
|
|
138
185
|
// SHARED UTILITIES
|
|
139
186
|
// ============================================
|
|
@@ -150,6 +197,11 @@ import { wuPreact } from './preact/index.js';
|
|
|
150
197
|
import { wuSolid } from './solid/index.js';
|
|
151
198
|
import { wuLit } from './lit/index.js';
|
|
152
199
|
import { wuVanilla } from './vanilla/index.js';
|
|
200
|
+
import { wuAlpine } from './alpine/index.js';
|
|
201
|
+
import { wuQwik } from './qwik/index.js';
|
|
202
|
+
import { wuStencil } from './stencil/index.js';
|
|
203
|
+
import { wuHtmx } from './htmx/index.js';
|
|
204
|
+
import { wuStimulus } from './stimulus/index.js';
|
|
153
205
|
|
|
154
206
|
/**
|
|
155
207
|
* Objeto con todos los adapters disponibles
|
|
@@ -162,7 +214,12 @@ export const adapters = {
|
|
|
162
214
|
preact: wuPreact,
|
|
163
215
|
solid: wuSolid,
|
|
164
216
|
lit: wuLit,
|
|
165
|
-
vanilla: wuVanilla
|
|
217
|
+
vanilla: wuVanilla,
|
|
218
|
+
alpine: wuAlpine,
|
|
219
|
+
qwik: wuQwik,
|
|
220
|
+
stencil: wuStencil,
|
|
221
|
+
htmx: wuHtmx,
|
|
222
|
+
stimulus: wuStimulus
|
|
166
223
|
};
|
|
167
224
|
|
|
168
225
|
export default adapters;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WU-FRAMEWORK QWIK ADAPTER - TypeScript Declarations
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { WuCore } from '../../core/wu-core';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Qwik adapter registration options
|
|
9
|
+
*/
|
|
10
|
+
export interface QwikRegisterOptions {
|
|
11
|
+
/** Props to pass to the Qwik component */
|
|
12
|
+
props?: Record<string, any>;
|
|
13
|
+
/** Callback after mounting */
|
|
14
|
+
onMount?: (container: HTMLElement) => void;
|
|
15
|
+
/** Callback before unmounting */
|
|
16
|
+
onUnmount?: (container: HTMLElement) => void;
|
|
17
|
+
/** Allow standalone execution when Wu Framework is not available */
|
|
18
|
+
standalone?: boolean;
|
|
19
|
+
/** CSS selector for standalone mode container */
|
|
20
|
+
standaloneContainer?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Register a Qwik component as a Wu microfrontend
|
|
25
|
+
*/
|
|
26
|
+
export function register(
|
|
27
|
+
appName: string,
|
|
28
|
+
Component: any,
|
|
29
|
+
options?: QwikRegisterOptions
|
|
30
|
+
): Promise<boolean>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Get the current Wu Framework instance
|
|
34
|
+
*/
|
|
35
|
+
export function getWuInstance(): WuCore | null;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Wait for Wu Framework to become available
|
|
39
|
+
*/
|
|
40
|
+
export function waitForWu(timeout?: number): Promise<WuCore>;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Qwik adapter interface
|
|
44
|
+
*/
|
|
45
|
+
export interface WuQwikAdapter {
|
|
46
|
+
register: typeof register;
|
|
47
|
+
getWuInstance: typeof getWuInstance;
|
|
48
|
+
waitForWu: typeof waitForWu;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const wuQwik: WuQwikAdapter;
|
|
52
|
+
export default wuQwik;
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WU-FRAMEWORK QWIK ADAPTER
|
|
3
|
+
*
|
|
4
|
+
* Integrates Qwik components into Wu Framework's microfrontend
|
|
5
|
+
* orchestration. Qwik's resumability model -- where the framework
|
|
6
|
+
* serializes state into HTML and lazily hydrates on interaction --
|
|
7
|
+
* makes it a natural fit for microfrontend boundaries. The adapter
|
|
8
|
+
* leverages dynamic import of @builder.io/qwik to keep the shell
|
|
9
|
+
* bundle zero-cost until a Qwik island actually mounts.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* import { wuQwik } from 'wu-framework/adapters/qwik';
|
|
13
|
+
* import { App } from './App';
|
|
14
|
+
*
|
|
15
|
+
* wuQwik.register('my-qwik-app', App);
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // With props and lifecycle hooks
|
|
19
|
+
* wuQwik.register('dashboard', DashboardComponent, {
|
|
20
|
+
* props: { userId: 42 },
|
|
21
|
+
* onMount: (container) => console.log('Qwik island mounted'),
|
|
22
|
+
* onUnmount: (container) => console.log('Qwik island destroyed')
|
|
23
|
+
* });
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
// Adapter-scoped state
|
|
27
|
+
const adapterState = {
|
|
28
|
+
apps: new Map()
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Obtiene la instancia de Wu Framework
|
|
33
|
+
*/
|
|
34
|
+
function getWuInstance() {
|
|
35
|
+
if (typeof window === 'undefined') return null;
|
|
36
|
+
|
|
37
|
+
return window.wu
|
|
38
|
+
|| window.parent?.wu
|
|
39
|
+
|| window.top?.wu
|
|
40
|
+
|| null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Espera a que Wu Framework este disponible
|
|
45
|
+
*/
|
|
46
|
+
function waitForWu(timeout = 5000) {
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
const wu = getWuInstance();
|
|
49
|
+
if (wu) {
|
|
50
|
+
resolve(wu);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const startTime = Date.now();
|
|
55
|
+
|
|
56
|
+
const handleWuReady = () => {
|
|
57
|
+
cleanup();
|
|
58
|
+
resolve(getWuInstance());
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
window.addEventListener('wu:ready', handleWuReady);
|
|
62
|
+
window.addEventListener('wu:app:ready', handleWuReady);
|
|
63
|
+
|
|
64
|
+
const checkInterval = setInterval(() => {
|
|
65
|
+
const wu = getWuInstance();
|
|
66
|
+
if (wu) {
|
|
67
|
+
cleanup();
|
|
68
|
+
resolve(wu);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (Date.now() - startTime > timeout) {
|
|
73
|
+
cleanup();
|
|
74
|
+
reject(new Error(`Wu Framework not found after ${timeout}ms`));
|
|
75
|
+
}
|
|
76
|
+
}, 200);
|
|
77
|
+
|
|
78
|
+
function cleanup() {
|
|
79
|
+
clearInterval(checkInterval);
|
|
80
|
+
window.removeEventListener('wu:ready', handleWuReady);
|
|
81
|
+
window.removeEventListener('wu:app:ready', handleWuReady);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Registra un componente Qwik como microfrontend
|
|
88
|
+
*
|
|
89
|
+
* @param {string} appName - Nombre unico del microfrontend
|
|
90
|
+
* @param {Function} Component - Componente Qwik (creado con component$)
|
|
91
|
+
* @param {Object} [options] - Opciones adicionales
|
|
92
|
+
* @param {Object} [options.props={}] - Props a pasar al componente
|
|
93
|
+
* @param {Function} [options.onMount] - Callback despues de montar
|
|
94
|
+
* @param {Function} [options.onUnmount] - Callback antes de desmontar
|
|
95
|
+
* @param {boolean} [options.standalone=true] - Permitir ejecucion standalone
|
|
96
|
+
* @param {string} [options.standaloneContainer='#app'] - Selector para modo standalone
|
|
97
|
+
* @returns {Promise<boolean>} true si el registro fue exitoso
|
|
98
|
+
*/
|
|
99
|
+
async function register(appName, Component, options = {}) {
|
|
100
|
+
const {
|
|
101
|
+
props = {},
|
|
102
|
+
onMount = null,
|
|
103
|
+
onUnmount = null,
|
|
104
|
+
standalone = true,
|
|
105
|
+
standaloneContainer = '#app'
|
|
106
|
+
} = options;
|
|
107
|
+
|
|
108
|
+
if (!Component) {
|
|
109
|
+
throw new Error(`[WuQwik] Component is required for ${appName}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Mount function
|
|
113
|
+
const mountApp = (container) => {
|
|
114
|
+
if (!container) {
|
|
115
|
+
console.error(`[WuQwik] Mount failed for ${appName}: container is null`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Prevent double-mount
|
|
120
|
+
if (adapterState.apps.has(appName)) {
|
|
121
|
+
console.warn(`[WuQwik] ${appName} already mounted, unmounting first`);
|
|
122
|
+
unmountApp();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
container.innerHTML = '';
|
|
127
|
+
|
|
128
|
+
// Dynamic import keeps Qwik out of the critical path
|
|
129
|
+
import('@builder.io/qwik').then(({ render, jsx }) => {
|
|
130
|
+
render(container, jsx(Component, props));
|
|
131
|
+
|
|
132
|
+
adapterState.apps.set(appName, {
|
|
133
|
+
container,
|
|
134
|
+
Component,
|
|
135
|
+
props
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
console.log(`[WuQwik] ${appName} mounted successfully`);
|
|
139
|
+
|
|
140
|
+
if (onMount) {
|
|
141
|
+
onMount(container);
|
|
142
|
+
}
|
|
143
|
+
}).catch((err) => {
|
|
144
|
+
console.error(`[WuQwik] Failed to import @builder.io/qwik for ${appName}:`, err);
|
|
145
|
+
container.innerHTML = `<div style="color:#721c24;padding:1rem;">Failed to load Qwik runtime</div>`;
|
|
146
|
+
});
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error(`[WuQwik] Mount error for ${appName}:`, error);
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// Unmount function
|
|
154
|
+
const unmountApp = (container) => {
|
|
155
|
+
const appData = adapterState.apps.get(appName);
|
|
156
|
+
|
|
157
|
+
if (appData) {
|
|
158
|
+
try {
|
|
159
|
+
if (onUnmount) {
|
|
160
|
+
onUnmount(appData.container);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
appData.container.innerHTML = '';
|
|
164
|
+
adapterState.apps.delete(appName);
|
|
165
|
+
|
|
166
|
+
console.log(`[WuQwik] ${appName} unmounted successfully`);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.error(`[WuQwik] Unmount error for ${appName}:`, error);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (container) {
|
|
173
|
+
container.innerHTML = '';
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// Register with Wu Framework or fall back to standalone
|
|
178
|
+
try {
|
|
179
|
+
const wu = await waitForWu(3000);
|
|
180
|
+
|
|
181
|
+
wu.define(appName, {
|
|
182
|
+
mount: mountApp,
|
|
183
|
+
unmount: unmountApp
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
console.log(`[WuQwik] ${appName} registered with Wu Framework`);
|
|
187
|
+
return true;
|
|
188
|
+
|
|
189
|
+
} catch (error) {
|
|
190
|
+
console.warn(`[WuQwik] Wu Framework not available for ${appName}`);
|
|
191
|
+
|
|
192
|
+
if (standalone) {
|
|
193
|
+
const containerElement = document.querySelector(standaloneContainer);
|
|
194
|
+
|
|
195
|
+
if (containerElement) {
|
|
196
|
+
console.log(`[WuQwik] Running ${appName} in standalone mode`);
|
|
197
|
+
mountApp(containerElement);
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Public API
|
|
207
|
+
export const wuQwik = {
|
|
208
|
+
register,
|
|
209
|
+
getWuInstance,
|
|
210
|
+
waitForWu
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
export { register, getWuInstance, waitForWu };
|
|
214
|
+
export default wuQwik;
|