wu-framework 1.1.18 → 1.2.0

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 CHANGED
@@ -9,10 +9,10 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <a href="https://www.npmjs.com/package/wu-framework"><img src="https://img.shields.io/npm/v/wu-framework.svg?color=8b5cf6&label=npm" alt="npm version" /></a>
13
- <a href="https://github.com/LuisPadre25/wu-framework/actions"><img src="https://img.shields.io/github/actions/workflow/status/LuisPadre25/wu-framework/ci.yml?label=tests&color=10b981" alt="tests" /></a>
14
- <img src="https://img.shields.io/badge/tests-650%20passed-10b981" alt="650 tests" />
15
- <img src="https://img.shields.io/badge/dependencies-0-8b5cf6" alt="zero deps" />
12
+ <a href="https://www.npmjs.com/package/wu-framework"><img src="https://img.shields.io/npm/v/wu-framework.svg?color=6366f1&label=npm" alt="npm version" /></a>
13
+ <a href="https://github.com/LuisPadre25/wu-framework/actions"><img src="https://img.shields.io/github/actions/workflow/status/LuisPadre25/wu-framework/ci.yml?label=tests&color=14b8a6" alt="tests" /></a>
14
+ <img src="https://img.shields.io/badge/tests-650%20passed-14b8a6" alt="650 tests" />
15
+ <img src="https://img.shields.io/badge/dependencies-0-6366f1" alt="zero deps" />
16
16
  <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License" /></a>
17
17
  </p>
18
18
 
@@ -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 | **8** | 4 | 1* | Any |
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
- - **8 Framework Adapters** — React, Vue, Angular, Svelte, Solid, Preact, Lit, Vanilla
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 | 8 |
347
+ | Framework adapters | 13 |
328
348
  | AI modules | 12 |
329
349
  | Core modules | 23 |
330
350
  | Runtime dependencies | **0** |
@@ -429,12 +449,41 @@ Something is coming. It learned from every LLM call you ever made. It doesn't ne
429
449
 
430
450
  ---
431
451
 
452
+ ## Wu CLI
453
+
454
+ The official CLI for wu-framework. One binary, one port, all your micro-apps.
455
+
456
+ ```bash
457
+ npm install -g @wu-framework/cli
458
+
459
+ wu create my-project # Interactive scaffolding (13 frameworks)
460
+ cd my-project
461
+ wu dev # Native Zig dev server on one port
462
+ wu build # Production build (parallel Vite)
463
+ wu serve # Serve production build
464
+ ```
465
+
466
+ Features:
467
+ - Native SIMD HTTP server written in Zig (16 bytes/cycle parsing)
468
+ - Three-tier compilation: Native Zig JSX (0-2ms) -> Compiler Daemon (10-50ms) -> Node fallback
469
+ - Two-level cache with 73-138x speedup on warm restart
470
+ - Theme-aware production shell with dark/light toggle
471
+ - 13 framework app templates with CSS variable theming
472
+
473
+ See [@wu-framework/cli](https://github.com/LuisPadre25/wu-cli) for full documentation.
474
+
475
+ ---
476
+
432
477
  ## Contributing
433
478
 
434
479
  Contributions welcome. Please open an issue first to discuss what you'd like to change.
435
480
 
436
481
  ## License
437
482
 
438
- [MIT](./LICENSE) Free for personal and commercial use.
483
+ [MIT](./LICENSE) -- Free for personal and commercial use.
439
484
 
440
485
  See [LICENSE-COMMERCIAL.md](./LICENSE-COMMERCIAL.md) for optional enterprise support and consulting.
486
+
487
+ ---
488
+
489
+ *2026 Wu Framework*
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "wu-framework",
3
- "version": "1.1.18",
4
- "description": "Universal Microfrontends Framework - 8 frameworks, zero config, Shadow DOM isolation",
3
+ "version": "1.2.0",
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
- "alpinejs": ">=3.0.0",
160
- "@builder.io/qwik": ">=1.0.0",
161
- "@stencil/core": ">=4.0.0",
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,108 @@
1
1
  /**
2
2
  * WU-FRAMEWORK QWIK ADAPTER
3
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.
4
+ * Integrates Qwik components (component$) into Wu Framework's
5
+ * microfrontend orchestration via @builder.io/qwik render API.
10
6
  *
11
- * @example
12
- * import { wuQwik } from 'wu-framework/adapters/qwik';
13
- * import { App } from './App';
7
+ * Shadow DOM compatibility:
8
+ * Qwik uses QRL-based event delegation — the qwikloader script
9
+ * attaches capture-phase listeners on `document` to intercept events
10
+ * and resolve QRL attributes (on:click, on:input, etc.) on elements.
14
11
  *
15
- * wuQwik.register('my-qwik-app', App);
12
+ * Inside Shadow DOM, event.target is retargeted to the shadow host,
13
+ * so qwikloader can't find QRL attributes on the actual elements.
14
+ * We fix this by wrapping qwikloader's document listeners with a
15
+ * Proxy that returns event.composedPath()[0] as the real target.
16
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
- * });
17
+ * See: https://github.com/QwikDev/qwik-evolution/issues/283
24
18
  */
25
19
 
26
- // Adapter-scoped state
27
- const adapterState = {
28
- apps: new Map()
29
- };
20
+ let _patched = false;
30
21
 
31
22
  /**
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
23
+ * Patches document.addEventListener so capture-phase handlers (used by
24
+ * qwikloader) receive the real event target from inside Shadow DOM via
25
+ * composedPath()[0], instead of the retargeted shadow host.
88
26
  *
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
27
+ * Only activates when there's actual Shadow DOM retargeting — events
28
+ * in the light DOM pass through with zero overhead (no Proxy created).
98
29
  */
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);
30
+ function patchDocumentListenersForShadowDOM() {
31
+ if (_patched) return;
32
+ _patched = true;
33
+
34
+ const origAdd = document.addEventListener;
35
+
36
+ document.addEventListener = function (type, handler, options) {
37
+ const isCapture = options === true || (options && options.capture);
38
+
39
+ if (isCapture && typeof handler === 'function') {
40
+ const wrapped = function (event) {
41
+ const path = event.composedPath();
42
+ // Only proxy when Shadow DOM retargeting occurred
43
+ if (path.length && path[0] !== event.target && path[0] instanceof Element) {
44
+ const realTarget = path[0];
45
+ const proxy = new Proxy(event, {
46
+ get(obj, prop) {
47
+ if (prop === 'target') return realTarget;
48
+ const val = Reflect.get(obj, prop);
49
+ return typeof val === 'function' ? val.bind(obj) : val;
50
+ }
51
+ });
52
+ return handler.call(this, proxy);
142
53
  }
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;
54
+ return handler.call(this, event);
55
+ };
56
+ return origAdd.call(this, type, wrapped, options);
150
57
  }
58
+
59
+ return origAdd.call(this, type, handler, options);
151
60
  };
61
+ }
152
62
 
153
- // Unmount function
154
- const unmountApp = (container) => {
155
- const appData = adapterState.apps.get(appName);
63
+ export const wuQwik = {
64
+ async register(appName, Component, options = {}) {
65
+ const wu = window.wu || window.parent?.wu;
66
+ if (!wu) return;
156
67
 
157
- if (appData) {
158
- try {
159
- if (onUnmount) {
160
- onUnmount(appData.container);
161
- }
68
+ const qwik = await import('@builder.io/qwik');
162
69
 
163
- appData.container.innerHTML = '';
164
- adapterState.apps.delete(appName);
70
+ // Patch document listeners BEFORE injecting qwikloader so its
71
+ // capture-phase handlers are wrapped with Shadow DOM awareness.
72
+ // Must stay active permanently — qwikloader registers event types
73
+ // lazily as new on: attributes appear in the DOM.
74
+ patchDocumentListenersForShadowDOM();
165
75
 
166
- console.log(`[WuQwik] ${appName} unmounted successfully`);
167
- } catch (error) {
168
- console.error(`[WuQwik] Unmount error for ${appName}:`, error);
76
+ // Inject qwikloader once per document (event delegation for QRLs)
77
+ if (!document.__wu_qwikloader) {
78
+ document.__wu_qwikloader = true;
79
+ try {
80
+ const { QWIK_LOADER } = await import('@builder.io/qwik/loader');
81
+ const s = document.createElement('script');
82
+ s.textContent = QWIK_LOADER;
83
+ document.head.appendChild(s);
84
+ } catch (e) {
85
+ console.warn('[WuQwik] qwikloader not available:', e.message);
169
86
  }
170
87
  }
171
88
 
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
89
  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;
90
+ async mount(container) {
91
+ container.innerHTML = '';
92
+ try {
93
+ const vnode = qwik.jsx(Component, options.props || {});
94
+ await qwik.render(container, vnode);
95
+ } catch (e) {
96
+ console.error('[WuQwik] render error:', e);
97
+ container.innerHTML = '<pre style="color:#f66;padding:1rem">' + e.message + '</pre>';
98
+ }
99
+ },
100
+ unmount(container) {
101
+ container.innerHTML = '';
199
102
  }
200
- }
201
-
202
- return false;
103
+ });
203
104
  }
204
- }
205
-
206
- // Public API
207
- export const wuQwik = {
208
- register,
209
- getWuInstance,
210
- waitForWu
211
105
  };
212
106
 
213
- export { register, getWuInstance, waitForWu };
107
+ export const { register } = wuQwik;
214
108
  export default wuQwik;