wu-framework 1.1.18 → 1.1.19
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 +24 -4
- package/package.json +11 -8
- package/src/adapters/qwik/index.js +32 -196
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
---
|
|
27
27
|
|
|
28
|
-
Run **React, Vue, Angular, Svelte, Solid, Preact, Lit, and Vanilla JS** micro-apps side by side in the same page. Each app lives in its own Shadow DOM with full CSS isolation. Apps communicate through a shared event bus and store — no tight coupling, no iframes.
|
|
28
|
+
Run **React, Vue, Angular, Svelte, Solid, Preact, Lit, Qwik, Alpine.js, Stencil, HTMX, Stimulus, and Vanilla JS** micro-apps side by side in the same page. Each app lives in its own Shadow DOM with full CSS isolation. Apps communicate through a shared event bus and store — no tight coupling, no iframes.
|
|
29
29
|
|
|
30
30
|
Add AI to any app with one line. Connect your own LLM (OpenAI, Anthropic, Ollama) and your app gains context-aware tool calling, autonomous agents, and cross-app orchestration. **WebMCP ready** for Chrome 146+.
|
|
31
31
|
|
|
@@ -68,7 +68,7 @@ await wu.ai.send('Add product SKU-42 to the cart');
|
|
|
68
68
|
|
|
69
69
|
| | **Wu Framework** | **single-spa** | **Module Federation** | **iframes** |
|
|
70
70
|
|---|:---:|:---:|:---:|:---:|
|
|
71
|
-
| Framework adapters | **
|
|
71
|
+
| Framework adapters | **13** | 4 | 1* | Any |
|
|
72
72
|
| Shadow DOM isolation | Yes | No | No | Yes (heavy) |
|
|
73
73
|
| Shared event bus | Built-in | Manual | Manual | postMessage |
|
|
74
74
|
| Shared store | Built-in | Manual | Manual | No |
|
|
@@ -87,7 +87,7 @@ await wu.ai.send('Add product SKU-42 to the cart');
|
|
|
87
87
|
|
|
88
88
|
### Core
|
|
89
89
|
|
|
90
|
-
- **
|
|
90
|
+
- **13 Framework Adapters** — React, Vue, Angular, Svelte, Solid, Preact, Lit, Qwik, Alpine.js, Stencil, HTMX, Stimulus, Vanilla
|
|
91
91
|
- **Shadow DOM Isolation** — CSS and DOM fully sandboxed per app
|
|
92
92
|
- **3 Sandbox Strategies** — Shadow DOM, Proxy, iframe — choose per app
|
|
93
93
|
- **3 CSS Isolation Modes** — `shared`, `isolated`, `fully-isolated` per app
|
|
@@ -138,6 +138,26 @@ wuAngular.registerStandalone('settings', AppComponent, { createApplication, crea
|
|
|
138
138
|
import { wuSvelte } from 'wu-framework/adapters/svelte';
|
|
139
139
|
wuSvelte.registerSvelte5('dashboard', App);
|
|
140
140
|
|
|
141
|
+
// Qwik
|
|
142
|
+
import { wuQwik } from 'wu-framework/adapters/qwik';
|
|
143
|
+
wuQwik.register('widget', QwikApp);
|
|
144
|
+
|
|
145
|
+
// Alpine.js
|
|
146
|
+
import { wuAlpine } from 'wu-framework/adapters/alpine';
|
|
147
|
+
wuAlpine.register('search', initFn);
|
|
148
|
+
|
|
149
|
+
// Stencil (Web Components)
|
|
150
|
+
import { wuStencil } from 'wu-framework/adapters/stencil';
|
|
151
|
+
wuStencil.register('badge', 'my-badge');
|
|
152
|
+
|
|
153
|
+
// HTMX
|
|
154
|
+
import { wuHtmx } from 'wu-framework/adapters/htmx';
|
|
155
|
+
wuHtmx.register('feed', { template: '<div hx-get="/api/feed">...</div>' });
|
|
156
|
+
|
|
157
|
+
// Stimulus
|
|
158
|
+
import { wuStimulus } from 'wu-framework/adapters/stimulus';
|
|
159
|
+
wuStimulus.register('form', { controllers: { hello: HelloController }, template: '...' });
|
|
160
|
+
|
|
141
161
|
// Same pattern for Solid, Preact, Lit, Vanilla
|
|
142
162
|
```
|
|
143
163
|
|
|
@@ -324,7 +344,7 @@ QA sets a cookie → only **their browser** sees the override. Everyone else see
|
|
|
324
344
|
| Source files | 79 |
|
|
325
345
|
| Lines of code | 23,442 |
|
|
326
346
|
| Test cases | **650** |
|
|
327
|
-
| Framework adapters |
|
|
347
|
+
| Framework adapters | 13 |
|
|
328
348
|
| AI modules | 12 |
|
|
329
349
|
| Core modules | 23 |
|
|
330
350
|
| Runtime dependencies | **0** |
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wu-framework",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "Universal Microfrontends Framework -
|
|
3
|
+
"version": "1.1.19",
|
|
4
|
+
"description": "Universal Microfrontends Framework - 13 frameworks, zero config, Shadow DOM isolation",
|
|
5
5
|
"main": "dist/wu-framework.cjs.js",
|
|
6
6
|
"module": "src/index.js",
|
|
7
7
|
"browser": "dist/wu-framework.umd.js",
|
|
@@ -149,18 +149,21 @@
|
|
|
149
149
|
},
|
|
150
150
|
"peerDependencies": {
|
|
151
151
|
"@angular/core": ">=14.0.0",
|
|
152
|
+
"@builder.io/qwik": ">=1.0.0",
|
|
153
|
+
"@hotwired/stimulus": ">=3.0.0",
|
|
154
|
+
"@stencil/core": ">=4.0.0",
|
|
155
|
+
"alpinejs": ">=3.0.0",
|
|
156
|
+
"htmx.org": ">=2.0.0",
|
|
152
157
|
"lit": ">=2.0.0",
|
|
153
158
|
"preact": ">=10.0.0",
|
|
154
159
|
"react": ">=16.8.0",
|
|
155
160
|
"react-dom": ">=16.8.0",
|
|
156
161
|
"solid-js": ">=1.0.0",
|
|
157
162
|
"svelte": ">=3.0.0",
|
|
158
|
-
"vue": ">=3.0.0"
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
"
|
|
162
|
-
"htmx.org": ">=2.0.0",
|
|
163
|
-
"@hotwired/stimulus": ">=3.0.0"
|
|
163
|
+
"vue": ">=3.0.0"
|
|
164
|
+
},
|
|
165
|
+
"overrides": {
|
|
166
|
+
"serialize-javascript": "^7.0.3"
|
|
164
167
|
},
|
|
165
168
|
"peerDependenciesMeta": {
|
|
166
169
|
"react": {
|
|
@@ -1,214 +1,50 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WU-FRAMEWORK QWIK ADAPTER
|
|
3
3
|
*
|
|
4
|
-
* Integrates Qwik components into Wu Framework's
|
|
5
|
-
* orchestration
|
|
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.
|
|
4
|
+
* Integrates Qwik components (component$) into Wu Framework's
|
|
5
|
+
* microfrontend orchestration via @builder.io/qwik render API.
|
|
10
6
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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
|
|
7
|
+
* Qwik uses QRL-based event delegation — onClick$, onInput$, etc.
|
|
8
|
+
* are NOT regular DOM listeners. The qwikloader script must be
|
|
9
|
+
* active in the document to intercept events and dispatch QRLs.
|
|
98
10
|
*/
|
|
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
11
|
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
};
|
|
12
|
+
export const wuQwik = {
|
|
13
|
+
async register(appName, Component, options = {}) {
|
|
14
|
+
const wu = window.wu || window.parent?.wu;
|
|
15
|
+
if (!wu) return;
|
|
152
16
|
|
|
153
|
-
|
|
154
|
-
const unmountApp = (container) => {
|
|
155
|
-
const appData = adapterState.apps.get(appName);
|
|
17
|
+
const qwik = await import('@builder.io/qwik');
|
|
156
18
|
|
|
157
|
-
|
|
19
|
+
// Inject qwikloader once per document (event delegation for QRLs)
|
|
20
|
+
if (!('__q_context__' in document)) {
|
|
158
21
|
try {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
console.log(`[WuQwik] ${appName} unmounted successfully`);
|
|
167
|
-
} catch (error) {
|
|
168
|
-
console.error(`[WuQwik] Unmount error for ${appName}:`, error);
|
|
22
|
+
const { QWIK_LOADER } = await import('@builder.io/qwik/loader');
|
|
23
|
+
const s = document.createElement('script');
|
|
24
|
+
s.textContent = QWIK_LOADER;
|
|
25
|
+
document.head.appendChild(s);
|
|
26
|
+
} catch (e) {
|
|
27
|
+
console.warn('[WuQwik] qwikloader not available:', e.message);
|
|
169
28
|
}
|
|
170
29
|
}
|
|
171
30
|
|
|
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
31
|
wu.define(appName, {
|
|
182
|
-
mount
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (containerElement) {
|
|
196
|
-
console.log(`[WuQwik] Running ${appName} in standalone mode`);
|
|
197
|
-
mountApp(containerElement);
|
|
198
|
-
return true;
|
|
32
|
+
async mount(container) {
|
|
33
|
+
container.innerHTML = '';
|
|
34
|
+
try {
|
|
35
|
+
const vnode = qwik.jsx(Component, options.props || {});
|
|
36
|
+
await qwik.render(container, vnode);
|
|
37
|
+
} catch (e) {
|
|
38
|
+
console.error('[WuQwik] render error:', e);
|
|
39
|
+
container.innerHTML = '<pre style="color:#f66;padding:1rem">' + e.message + '</pre>';
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
unmount(container) {
|
|
43
|
+
container.innerHTML = '';
|
|
199
44
|
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return false;
|
|
45
|
+
});
|
|
203
46
|
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Public API
|
|
207
|
-
export const wuQwik = {
|
|
208
|
-
register,
|
|
209
|
-
getWuInstance,
|
|
210
|
-
waitForWu
|
|
211
47
|
};
|
|
212
48
|
|
|
213
|
-
export { register
|
|
49
|
+
export const { register } = wuQwik;
|
|
214
50
|
export default wuQwik;
|