wu-framework 1.1.8 → 1.1.10

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
@@ -1,135 +1,144 @@
1
- # Wu Framework
1
+ <p align="center">
2
+ <img src="https://wu-framework.dev/favicon.svg" width="80" alt="Wu Framework" />
3
+ </p>
4
+
5
+ <h1 align="center">Wu Framework</h1>
6
+
7
+ <p align="center">
8
+ <strong>Universal microfrontends with built-in AI. Zero dependencies.</strong>
9
+ </p>
10
+
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/wu-framework/wu-framework/actions"><img src="https://img.shields.io/github/actions/workflow/status/wu-framework/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" />
16
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License" /></a>
17
+ </p>
18
+
19
+ <p align="center">
20
+ <a href="https://wu-framework.dev">Documentation</a> &middot;
21
+ <a href="https://wu-framework.dev/docs/quick-start">Quick Start</a> &middot;
22
+ <a href="https://wu-framework.dev/docs/ai/overview">AI Integration</a> &middot;
23
+ <a href="#wucommerce--real-world-example">Live Example</a>
24
+ </p>
2
25
 
3
- **The easiest way to turn any app into a microfrontend — and the only one that doesn't force you to stop working as a standalone app.**
26
+ ---
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.
4
29
 
5
- Wu speaks the language of your framework. One line to register a React component, a Vue app, an Angular module. Your app works inside a microfrontend shell AND as a standalone app same code, zero changes.
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+.
6
31
 
7
32
  ```bash
8
33
  npm install wu-framework
9
34
  ```
10
35
 
11
- ---
12
-
13
- ## Why Wu?
14
-
15
- Every microfrontend framework forces you to learn its language. Single-spa makes you export `bootstrap`, `mount`, `unmount`. Module Federation requires webpack config. Qiankun needs manual registration with lifecycle callbacks.
16
-
17
- Wu doesn't. You write React code. You write Vue code. You write Angular code. Wu adapts to you.
18
-
19
- ```jsx
20
- // That's it. Your React app is now a microfrontend.
21
- wuReact.register('header', App);
22
- ```
36
+ ## 30-Second Demo
23
37
 
24
- If Wu isn't running? Your app mounts directly to `#root` and works by itself. No lock-in. No dependency on a shell being alive. **Microfrontends that don't chain you.**
38
+ ```js
39
+ import { wu } from 'wu-framework';
40
+ import { wuReact } from 'wu-framework/adapters/react';
41
+ import { wuVue } from 'wu-framework/adapters/vue';
25
42
 
26
- ### What makes Wu different
43
+ // Register micro-apps from different frameworks
44
+ wuReact.register('cart', CartApp);
45
+ wuVue.register('catalog', CatalogApp);
27
46
 
28
- | | single-spa | qiankun | Module Federation | **Wu** |
29
- |---|---|---|---|---|
30
- | **Registration** | Export lifecycle functions | Manual lifecycle callbacks | Webpack config | **One line, framework-native** |
31
- | **CSS isolation** | Manual | CSS prefixing (fragile) | None | **Shadow DOM + 3 configurable modes** |
32
- | **JS isolation** | None | Basic Proxy | None | **Proxy sandbox with side-effect cleanup** |
33
- | **Standalone mode** | No | No | No | **Automatic — works without shell** |
34
- | **Framework API** | Generic | Generic | Generic | **Native hooks, composables, stores per framework** |
35
- | **Prefetch** | Manual | Manual preload | None | **Speculation Rules API + fallbacks** |
36
- | **QA Overrides** | None | None | None | **Cookie-based URL overrides (secure)** |
37
- | **Config required** | Extensive | Moderate | Webpack plugin | **Zero** |
47
+ // Mount them each gets its own Shadow DOM
48
+ await wu.mount('cart', '#cart-container');
49
+ await wu.mount('catalog', '#catalog-container');
38
50
 
39
- ---
51
+ // They talk to each other via events
52
+ wu.emit('cart:item-added', { productId: 'SKU-42' });
53
+ wu.on('catalog:product-selected', (e) => console.log(e.data));
40
54
 
41
- ## WuCommerceReal-World Example
55
+ // Add AI with one line BYOL (Bring Your Own LLM)
56
+ wu.ai.provider('openai', { endpoint: '/api/ai/chat', model: 'gpt-4o' });
57
+ wu.ai.action('addToCart', {
58
+ description: 'Add a product to the shopping cart',
59
+ parameters: { productId: { type: 'string', required: true } },
60
+ handler: async (params) => wu.emit('cart:item-added', params),
61
+ });
42
62
 
43
- Wu Framework ships with **WuCommerce**, a Shopify-like merchant dashboard where every section is a real micro-app built with a different framework. The narrative: each module was built by a different team or acquired, unified under wu-framework.
63
+ // Now the AI can control your app
64
+ await wu.ai.send('Add product SKU-42 to the cart');
65
+ ```
44
66
 
45
- | Micro-app | Framework | What it does |
46
- |-----------|-----------|-------------|
47
- | **Topbar** | Preact | Store name, nav tabs, search, notifications, theme toggle |
48
- | **Dashboard** | Svelte 5 | KPI cards, sparklines, recent orders, revenue chart |
49
- | **Orders** | React | Order table with filters, search, status badges |
50
- | **Products** | Vue 3 | Product catalog grid, stock badges, category filter |
51
- | **Customers** | Solid.js | Customer list, segment badges, click-to-filter-orders |
52
- | **Analytics** | Lit | Revenue bar chart, traffic donut, top products |
53
- | **Chat** | Vanilla JS | Floating chat widget with conversations and messages |
54
- | **Settings** | Angular 21 | Store config form, shipping zones, payment methods |
67
+ ## Why Wu?
55
68
 
56
- All 8 apps communicate through Wu's event bus and shared store. Click a customer → orders filter by that customer. Toggle theme → all 8 apps update. Change store name in settings → topbar updates.
69
+ | | **Wu Framework** | **single-spa** | **Module Federation** | **iframes** |
70
+ |---|:---:|:---:|:---:|:---:|
71
+ | Framework adapters | **8** | 4 | 1* | Any |
72
+ | Shadow DOM isolation | Yes | No | No | Yes (heavy) |
73
+ | Shared event bus | Built-in | Manual | Manual | postMessage |
74
+ | Shared store | Built-in | Manual | Manual | No |
75
+ | Standalone mode | Automatic | No | No | N/A |
76
+ | AI integration | Built-in | No | No | No |
77
+ | WebMCP (Chrome 146+) | Built-in | No | No | No |
78
+ | MCP Server (dev tools) | Built-in | No | No | No |
79
+ | Dependencies | **0** | 0 | Webpack 5 | N/A |
80
+ | Bundle size (min) | ~174 KB | ~10 KB | Varies | N/A |
81
+
82
+ *Module Federation is Webpack-coupled; Wu is bundler-agnostic.
57
83
 
58
- ```
59
- npm run dev # starts all 9 servers (1 shell + 8 micro-apps)
60
- open http://localhost:4321
61
- ```
84
+ ---
62
85
 
63
- Source: `examples/astro-multi-framework/`
86
+ ## Features
87
+
88
+ ### Core
89
+
90
+ - **8 Framework Adapters** — React, Vue, Angular, Svelte, Solid, Preact, Lit, Vanilla
91
+ - **Shadow DOM Isolation** — CSS and DOM fully sandboxed per app
92
+ - **3 Sandbox Strategies** — Shadow DOM, Proxy, iframe — choose per app
93
+ - **3 CSS Isolation Modes** — `shared`, `isolated`, `fully-isolated` per app
94
+ - **Event Bus** — Namespaced pub/sub with wildcards, replay, and middleware
95
+ - **Shared Store** — Cross-app reactive state with dot-path notation and persistence
96
+ - **Plugin System** — Extend Wu with lifecycle hooks
97
+ - **Performance Monitor** — Mount time, memory, FPS tracking per app
98
+ - **Error Boundaries** — Catch and recover from micro-app failures
99
+ - **Keep-Alive** — Preserve app state when hiding/showing
100
+ - **Prefetch** — Speculation Rules API with automatic fallback chain
101
+ - **Cookie Overrides** — QA redirects individual apps to different URLs per-browser
102
+ - **Standalone Mode** — Every micro-app works without a shell, zero lock-in
103
+
104
+ ### AI (BYOL — Bring Your Own LLM)
105
+
106
+ - **4 Paradigms** — App→LLM, LLM→App, Autonomous Agent, Cross-App Orchestration
107
+ - **Tool Calling** — Register actions the AI executes autonomously
108
+ - **Streaming** — Async generator for real-time responses
109
+ - **Multi-turn** — Namespaced conversations with history
110
+ - **Reactive Triggers** — Events automatically invoke the AI
111
+ - **Auto Context** — Store state, apps, events injected into the system prompt
112
+ - **10 Browser Actions** — Screenshot, click, type, navigate, read console/network
113
+ - **WebMCP** — `wu.ai.expose()` registers tools via `navigator.modelContext` (Chrome 146+)
114
+ - **MCP Server** — Connect Claude Code, Cursor, or any MCP client to your live app
115
+ - **4-Layer Security** — Permissions, rate limiting, circuit breaker, loop protection
116
+ - **Workflows** — Reusable parameterized AI recipes
64
117
 
65
118
  ---
66
119
 
67
120
  ## Quick Start
68
121
 
69
- ### 1. Register your micro app
122
+ ### 1. Register your micro-app
70
123
 
71
- **React:**
72
124
  ```jsx
125
+ // React
73
126
  import { wuReact } from 'wu-framework/adapters/react';
74
- import App from './App';
75
127
  wuReact.register('orders', App);
76
- ```
77
128
 
78
- **Vue:**
79
- ```js
129
+ // Vue
80
130
  import { wuVue } from 'wu-framework/adapters/vue';
81
- import App from './App.vue';
82
131
  wuVue.register('products', App);
83
- ```
84
132
 
85
- **Angular (standalone component):**
86
- ```ts
87
- import 'zone.js';
88
- import '@angular/compiler';
89
- import { createApplication } from '@angular/platform-browser';
90
- import { createComponent, provideZoneChangeDetection } from '@angular/core';
133
+ // Angular (standalone)
91
134
  import { wuAngular } from 'wu-framework/adapters/angular';
92
- import { AppComponent } from './app/app.component';
135
+ wuAngular.registerStandalone('settings', AppComponent, { createApplication, createComponent });
93
136
 
94
- wuAngular.registerStandalone('settings', AppComponent, {
95
- createApplication,
96
- createComponent,
97
- provideZoneChangeDetection,
98
- });
99
- ```
100
-
101
- **Svelte 5:**
102
- ```js
137
+ // Svelte 5
103
138
  import { wuSvelte } from 'wu-framework/adapters/svelte';
104
- import App from './App.svelte';
105
139
  wuSvelte.registerSvelte5('dashboard', App);
106
- ```
107
-
108
- **Solid:**
109
- ```jsx
110
- import { wuSolid } from 'wu-framework/adapters/solid';
111
- import App from './App';
112
- wuSolid.register('customers', App);
113
- ```
114
-
115
- **Preact:**
116
- ```jsx
117
- import { wuPreact } from 'wu-framework/adapters/preact';
118
- import App from './App';
119
- wuPreact.register('topbar', App);
120
- ```
121
140
 
122
- **Lit:**
123
- ```js
124
- import { wuLit } from 'wu-framework/adapters/lit';
125
- import { MyApp } from './my-app.js';
126
- wuLit.register('analytics', MyApp);
127
- ```
128
-
129
- **Vanilla JS:**
130
- ```js
131
- import wuVanilla from 'wu-framework/adapters/vanilla';
132
- // use wuVanilla.useWuEvents() and wuVanilla.useWuStore() inside mount/unmount
141
+ // Same pattern for Solid, Preact, Lit, Vanilla
133
142
  ```
134
143
 
135
144
  ### 2. Mount from the shell
@@ -141,7 +150,7 @@ await wu.init({
141
150
  apps: [
142
151
  { name: 'header', url: 'http://localhost:3001' },
143
152
  { name: 'sidebar', url: 'http://localhost:3002' },
144
- { name: 'content', url: 'http://localhost:3003' }
153
+ { name: 'content', url: 'http://localhost:3003' },
145
154
  ]
146
155
  });
147
156
 
@@ -150,568 +159,184 @@ await wu.mount('sidebar', '#sidebar-container');
150
159
  await wu.mount('content', '#content-container');
151
160
  ```
152
161
 
153
- Each app runs inside its own Shadow DOM with a JS sandbox that tracks and cleans up all side effects on unmount.
162
+ ### 3. Cross-app communication
154
163
 
155
- ---
164
+ ```js
165
+ import { emit, on, getState, setState } from 'wu-framework';
156
166
 
157
- ## 8 Framework Adapters
167
+ // Events
168
+ emit('user:login', { userId: 123 });
169
+ on('user:*', (event) => console.log(event.data));
158
170
 
159
- Wu doesn't give you a generic API and tell you to figure it out. Each adapter speaks the native language of its framework.
171
+ // Shared store
172
+ setState('user.name', 'John');
173
+ getState('user.name'); // 'John'
174
+ ```
160
175
 
161
- | Framework | Adapter | Register | Native APIs |
162
- |-----------|---------|----------|-------------|
163
- | React | `wuReact` | `.register(name, Component)` | `createUseWuEvents(React)`, `createUseWuStore(React)` |
164
- | Vue 3 | `wuVue` | `.register(name, Component)` | `useWuEvents()`, `useWuStore()`, `WuSlot`, plugin |
165
- | Angular 14+ | `wuAngular` | `.registerStandalone(name, Component, opts)` | `createWuService()` |
166
- | Angular (NgModule) | `wuAngular` | `.register(name, Module)` | Same |
167
- | Angular Elements | `wuAngular` | `.registerElement(name, Component)` | Same |
168
- | Svelte 3/4 | `wuSvelte` | `.register(name, Component)` | `createWuStore()`, `useWuEvents()` |
169
- | Svelte 5 | `wuSvelte` | `.registerSvelte5(name, Component)` | Same + `createWuEventStore()` |
170
- | Preact | `wuPreact` | `.register(name, Component)` | `createUseWuEvents(hooks)`, `createUseWuStore(hooks)` |
171
- | Preact (compat) | `wuPreact` | `.registerCompat(name, Component)` | Same |
172
- | Solid.js | `wuSolid` | `.register(name, Component)` | `useWuEvents()`, `createWuStore()`, `createWuEvent()` |
173
- | Lit | `wuLit` | `.register(name, Element)` | `WuMixin(LitElement)`, `wuProperty()` decorator |
174
- | Web Component | `wuLit` | `.registerWebComponent(name, Element)` | Same |
175
- | Vanilla JS | `wuVanilla` | `.register(name, config)` | `useWuEvents()`, `useWuStore()`, `createComponent()` |
176
- | Vanilla (class) | `wuVanilla` | `.registerClass(name, Class)` | Same |
177
- | Vanilla (template) | `wuVanilla` | `.registerTemplate(name, html)` | Same |
176
+ ### 4. Add AI (optional)
178
177
 
179
- > **Note:** React and Preact hooks are *factory functions* — pass `React` or `{ useCallback, useEffect, useRef }` to get the actual hooks. Vue composables work directly with no arguments. Angular's `registerStandalone` is recommended over `register` because it works inside Shadow DOM.
178
+ ```js
179
+ wu.ai.provider('ollama', {
180
+ endpoint: 'http://localhost:11434/api/chat',
181
+ model: 'llama3',
182
+ });
180
183
 
181
- Every adapter detects if Wu is present. If not, the app mounts standalone. Your microfrontend never stops working.
184
+ const response = await wu.ai.send('What apps are mounted?');
185
+ ```
182
186
 
183
187
  ---
184
188
 
185
- ## 3 CSS Isolation Modes
186
-
187
- This is something no other microfrontend framework offers: **per-app configurable style isolation**.
188
-
189
- | Mode | What happens | When to use |
190
- |------|-------------|-------------|
191
- | `shared` | Host document styles are injected into Shadow DOM | Apps sharing a design system (Tailwind, Element Plus) |
192
- | `isolated` | Pure Shadow DOM — no external styles enter | Fully independent apps with their own CSS |
193
- | `fully-isolated` | Only the app's own styles are injected | Apps that need their CSS but not global CSS |
189
+ ## WuCommerce Real-World Example
194
190
 
195
- Set it in `wu.json`:
191
+ Wu ships with **WuCommerce**, a Shopify-like merchant dashboard where every section is a real micro-app built with a different framework.
196
192
 
197
- ```json
198
- {
199
- "name": "my-app",
200
- "entry": "index.js",
201
- "styleMode": "isolated"
202
- }
203
- ```
193
+ | Micro-app | Framework | What it does |
194
+ |-----------|-----------|-------------|
195
+ | **Topbar** | Preact | Store name, nav tabs, search, notifications, theme toggle |
196
+ | **Dashboard** | Svelte 5 | KPI cards, sparklines, recent orders, revenue chart |
197
+ | **Orders** | React | Order table with filters, search, status badges |
198
+ | **Products** | Vue 3 | Product catalog grid, stock badges, category filter |
199
+ | **Customers** | Solid.js | Customer list, segment badges, click-to-filter-orders |
200
+ | **Analytics** | Lit | Revenue bar chart, traffic donut, top products |
201
+ | **Chat** | Vanilla JS | Floating chat widget with conversations and messages |
202
+ | **Settings** | Angular 21 | Store config form, shipping zones, payment methods |
204
203
 
205
- In a real team, your header might need `shared` (same design system as the shell), your analytics dashboard might need `isolated` (completely independent), and your sidebar might need `fully-isolated` (own styles only). Wu handles all three in the same page.
204
+ All 8 apps communicate through Wu's event bus and shared store. Click a customer orders filter. Toggle theme all 8 apps update. Change store name in settings topbar updates.
206
205
 
207
206
  ---
208
207
 
209
- ## JS Sandbox — Three Modes, Real Isolation
210
-
211
- Wu offers three sandbox modes. Pick the one that fits your workflow:
208
+ ## 3 Sandbox Strategies
212
209
 
213
210
  | Mode | How it works | Tree shaking | Source maps | HMR | JS isolation |
214
211
  |------|-------------|:---:|:---:|:---:|:---:|
215
- | `module` (default) | `import()` + Proxy side-effect tracking | | | | Side effects only |
216
- | `strict` | Hidden iframe + real `import()` | | | | **Full** (separate window) |
217
- | `eval` | Fetch HTML → parse → `with(proxy){}` | | | | **Full** (proxy traps) |
212
+ | `module` (default) | `import()` + Proxy side-effect tracking | Yes | Yes | Yes | Side effects only |
213
+ | `strict` | Hidden iframe + real `import()` | Yes | Yes | Yes | **Full** (separate window) |
214
+ | `eval` | Fetch HTML → parse → `with(proxy){}` | No | No | No | **Full** (proxy traps) |
218
215
 
219
216
  ```js
220
- // Global setting
221
- await wu.init({
222
- sandbox: 'strict', // or 'module' (default) or 'eval'
223
- apps: [...]
224
- });
225
-
226
- // Per-app override — mix modes in the same page
227
217
  await wu.init({
228
218
  apps: [
229
- { name: 'header', url: '...', sandbox: 'module' }, // Fast dev, no isolation
230
- { name: 'analytics', url: '...', sandbox: 'strict' }, // Full isolation + modern DX
231
- { name: 'legacy', url: '...', sandbox: 'eval' }, // UMD bundle, max isolation
219
+ { name: 'header', url: '...', sandbox: 'module' },
220
+ { name: 'analytics', url: '...', sandbox: 'strict' },
221
+ { name: 'legacy', url: '...', sandbox: 'eval' },
232
222
  ]
233
223
  });
234
224
  ```
235
225
 
236
- **Module mode** loads your app via `import()`. Side effects (timers, listeners, storage) are tracked through the Proxy and cleaned on unmount. App code runs in the global scope — the proxy is a cleanup tracker. Works with ES modules, Vite HMR, and all modern tooling.
226
+ **Auto-cleaned on unmount:** timers, intervals, rAF, event listeners, localStorage keys, DOM mutations.
237
227
 
238
- **Strict mode** creates a hidden iframe as an isolated execution context, then runs `import()` inside it. Your app gets a **separate `window`** — globals, timers, and listeners are isolated by the browser itself. Document operations (`querySelector`, `createElement`, `body`) are proxied to the Shadow DOM container. Destroying the iframe nukes all side effects at once. If `import()` fails (CORS), it falls back to eval mode automatically.
228
+ ---
239
229
 
240
- **Eval mode** fetches your app's HTML, parses out `<script>` and `<style>` tags, and executes all scripts inside the Proxy sandbox using `with(proxy){}`. Every unqualified identifier (`setTimeout`, `document`, `fetch`) resolves through the proxy's traps. This is qiankun-level isolation. Requires bundled apps (UMD/IIFE), not ES modules.
230
+ ## 3 CSS Isolation Modes
241
231
 
242
- ### How strict mode works
232
+ | Mode | What happens | When to use |
233
+ |------|-------------|-------------|
234
+ | `shared` | Host styles injected into Shadow DOM | Apps sharing a design system (Tailwind) |
235
+ | `isolated` | Pure Shadow DOM — no external styles | Fully independent apps |
236
+ | `fully-isolated` | Only the app's own styles | Apps that need their CSS but not global CSS |
243
237
 
238
+ ```json
239
+ { "name": "my-app", "entry": "index.js", "styleMode": "isolated" }
244
240
  ```
245
- ┌── Main Window ──────────────────────────────────┐
246
- │ ┌── Shadow DOM ──────────────────────────────┐ │
247
- │ │ App renders here (CSS isolated) │ │
248
- │ │ document.querySelector → scoped here │ │
249
- │ └────────────────────────────────────────────┘ │
250
- │ ┌── Hidden iframe ──────────────────────────┐ │
251
- │ │ import() runs here (REAL ES modules) │ │
252
- │ │ window = separate context (ISOLATED) │ │
253
- │ │ document.createElement → main document │ │
254
- │ │ Destroy iframe = kill everything │ │
255
- │ └────────────────────────────────────────────┘ │
256
- └──────────────────────────────────────────────────┘
257
- ```
258
-
259
- Strict mode preserves your entire development workflow:
260
- - **Tree shaking** — real `import()`, bundler optimizations work as normal
261
- - **Source maps** — browser loads real modules, maps resolve correctly
262
- - **Vite HMR** — HMR client runs inside iframe, WebSocket connects to dev server
263
- - **Full isolation** — separate `window` context, no global leaks between apps
264
-
265
- **What gets tracked and auto-cleaned (all modes):**
266
- - `setTimeout` / `setInterval` / `requestAnimationFrame` — all IDs tracked, all cleared
267
- - `addEventListener` / `removeEventListener` — all listeners tracked, all removed
268
- - `document.querySelector` — scoped to the app's Shadow DOM
269
- - `localStorage` / `sessionStorage` — keys prefixed with `wu_{appName}_`
270
-
271
- **277 tests verify this works.** Unit tests for every isolation feature, integration tests proving two sandboxes don't contaminate each other.
272
241
 
273
242
  ---
274
243
 
275
- ## Cross-App Communication
244
+ ## AI Paradigms
276
245
 
277
- ### Event Bus
246
+ ### 1. App → LLM → App
278
247
 
279
248
  ```js
280
- import { emit, on, once, off } from 'wu-framework';
281
-
282
- emit('user:login', { userId: 123 });
283
-
284
- const unsub = on('user:*', (event) => {
285
- console.log(event.name, event.data);
286
- });
287
-
288
- once('app:ready', (e) => console.log('Ready'));
289
- unsub();
249
+ const response = await wu.ai.send('What items are in the cart?');
290
250
  ```
291
251
 
292
- Features: wildcard patterns (`user:*`, `*.error`, `app.*.ready`), event replay for late-joining apps, strict mode (only registered apps can emit), history buffer.
293
-
294
- ### Store
252
+ ### 2. LLM App LLM (WebMCP)
295
253
 
296
254
  ```js
297
- import { getState, setState, onStateChange } from 'wu-framework';
298
-
299
- setState('user.name', 'John');
300
- setState('user.preferences.theme', 'dark');
301
-
302
- getState('user.name'); // 'John'
303
-
304
- const unsub = onStateChange('user.*', (value, path) => {
305
- console.log(`${path} = ${value}`);
306
- });
307
-
308
- // Batch updates
309
- wu.store.batch({ 'cart.count': 5, 'cart.total': 99.99 });
310
- ```
311
-
312
- Ring buffer event history. Wildcard subscriptions. Dot-notation paths with auto-creation of nested objects.
313
-
314
- ---
315
-
316
- ## Framework-Native Integration Examples
317
-
318
- > All examples below are from **WuCommerce**, a real merchant dashboard built with Wu Framework.
319
- > 8 micro-apps, 8 different frameworks, running together in one shell.
320
- > See `examples/astro-multi-framework/` for the full source.
321
-
322
- ### React — Orders (mf-eventlab)
323
-
324
- ```jsx
325
- // main.jsx
326
- import { wuReact } from 'wu-framework/adapters/react';
327
- import App from './App';
328
- wuReact.register('orders', App);
329
-
330
- // App.jsx — hooks are factories, pass React to get the hook
331
- import React, { useState, useEffect, useMemo } from 'react';
332
- import { createUseWuEvents, createUseWuStore } from 'wu-framework/adapters/react';
333
-
334
- const useWuEvents = createUseWuEvents(React);
335
- const useWuStore = createUseWuStore(React);
336
-
337
- function Orders() {
338
- const { emit, on } = useWuEvents();
339
- const [filter, setFilter] = useState('all');
340
-
341
- useEffect(() => {
342
- const unsub = on('customer:selected', (e) => {
343
- setFilter(e.data?.customerName || 'all');
344
- });
345
- return unsub;
346
- }, []);
347
-
348
- return <button onClick={() => emit('order:new', { id: 1001, total: 49.99 })}>New Order</button>;
349
- }
350
- ```
351
-
352
- ### Vue 3 — Products (mf-statebridge)
353
-
354
- ```vue
355
- <!-- main.js -->
356
- <!-- import { wuVue } from 'wu-framework/adapters/vue'; -->
357
- <!-- import App from './App.vue'; -->
358
- <!-- wuVue.register('products', App); -->
359
-
360
- <script setup>
361
- import { ref, computed, onMounted, onUnmounted } from 'vue';
362
- import { wuVue } from 'wu-framework/adapters/vue';
363
-
364
- const { useWuEvents, useWuStore } = wuVue;
365
- const events = useWuEvents();
366
-
367
- onMounted(() => {
368
- events.on('search:global', (e) => {
369
- search.value = e.data?.query || '';
370
- });
371
- });
372
-
373
- function handleStockAlert(product) {
374
- events.emit('product:stockAlert', { productId: product.id, stock: product.stock });
375
- }
376
-
377
- onUnmounted(() => events.cleanup());
378
- </script>
255
+ wu.ai.expose(); // Registers all tools via navigator.modelContext (Chrome 146+)
379
256
  ```
380
257
 
381
- ### Svelte 5 — Dashboard (mf-hero)
382
-
383
- ```svelte
384
- <!-- main.js: wuSvelte.registerSvelte5('dashboard', App) -->
385
- <script>
386
- import { wuSvelte } from 'wu-framework/adapters/svelte';
387
-
388
- const { createWuStore, createWuEventStore, useWuEvents } = wuSvelte;
389
- const { emit, on, cleanup } = useWuEvents();
390
-
391
- let stats = $state({ revenue: 12847, orders: 156, visitors: 2341, conversion: 4.2 });
392
-
393
- on('theme:toggle', (e) => console.log('Theme:', e.data?.theme));
394
-
395
- function handleNewOrder() {
396
- emit('order:new', { orderId: `ORD-${Date.now()}`, total: 49.99, status: 'pending' });
397
- }
398
-
399
- // cleanup is called automatically on component destroy
400
- </script>
401
- ```
402
-
403
- ### Solid.js — Customers (mf-features)
404
-
405
- ```jsx
406
- // main.jsx: wuSolid.register('customers', App)
407
- import { createSignal, onMount } from 'solid-js';
408
- import { wuSolid } from 'wu-framework/adapters/solid';
409
-
410
- export default function Customers() {
411
- const [search, setSearch] = createSignal('');
412
- const { emit, on } = wuSolid.useWuEvents(); // auto-cleanup via onCleanup
413
-
414
- onMount(() => {
415
- on('search:global', (e) => setSearch(e.data?.query || ''));
416
- });
417
-
418
- const handleRowClick = (customer) => {
419
- emit('customer:selected', { customerId: customer.id, customerName: customer.name });
420
- emit('nav:section', { section: 'orders' });
421
- };
422
-
423
- return <div>...</div>;
424
- }
425
- ```
426
-
427
- ### Preact — Topbar (mf-header)
428
-
429
- ```jsx
430
- // main.jsx: wuPreact.register('topbar', App)
431
- import { useState, useCallback, useEffect, useRef } from 'preact/hooks';
432
- import { wuPreact } from 'wu-framework/adapters/preact';
433
-
434
- // Factory pattern — pass hooks as argument
435
- const useWuEvents = wuPreact.createUseWuEvents({ useCallback, useEffect, useRef });
436
- const useWuStore = wuPreact.createUseWuStore({ useState, useCallback, useEffect });
437
-
438
- function Topbar() {
439
- const { emit, on } = useWuEvents();
440
- const storeData = useWuStore('store');
441
-
442
- const handleSearch = (query) => emit('search:global', { query });
443
- const toggleTheme = () => emit('theme:toggle', { theme: isDark ? 'light' : 'dark' });
444
-
445
- return <header>...</header>;
446
- }
447
- ```
448
-
449
- ### Angular 21 — Settings (mf-pluginperf)
450
-
451
- ```ts
452
- // main.ts — full setup (JIT mode, no AnalogJS needed)
453
- import 'zone.js';
454
- import '@angular/compiler';
455
- import { createApplication } from '@angular/platform-browser';
456
- import { createComponent, provideZoneChangeDetection } from '@angular/core';
457
- import { wuAngular } from 'wu-framework/adapters/angular';
458
- import { SettingsComponent } from './app/settings.component';
459
-
460
- wuAngular.registerStandalone('settings', SettingsComponent, {
461
- standaloneContainer: '#app',
462
- createApplication, // pass Angular APIs to avoid bundler issues
463
- createComponent,
464
- provideZoneChangeDetection,
465
- });
466
-
467
- // settings.component.ts — use createWuService for events/store
468
- import { Component, OnInit, OnDestroy } from '@angular/core';
469
- import { createWuService } from 'wu-framework/adapters/angular';
470
-
471
- @Component({ selector: 'app-settings', standalone: true, template: '...' })
472
- export class SettingsComponent implements OnInit, OnDestroy {
473
- private wu = createWuService();
474
- storeName = '';
475
-
476
- ngOnInit() {
477
- this.storeName = this.wu.getState('store')?.name || '';
478
- this.wu.on('theme:toggle', (e) => console.log('Theme:', e.data?.theme));
479
- }
480
-
481
- save() {
482
- this.wu.setState('store', { name: this.storeName });
483
- this.wu.emit('settings:saved', { name: this.storeName });
484
- }
485
-
486
- ngOnDestroy() { this.wu.destroy(); }
487
- }
488
- ```
489
-
490
- > **Angular + Vite setup:** No AnalogJS or Angular CLI required. Plain Vite with
491
- > `esbuild: { target: 'es2022' }` and `tsconfig.json` with `experimentalDecorators: true`.
492
- > `@angular/compiler` import enables JIT mode at runtime. Pass `createApplication`,
493
- > `createComponent`, `provideZoneChangeDetection` as options to `registerStandalone`
494
- > so the bundler resolves them from your app's `node_modules`.
495
-
496
- ### Lit — Analytics (mf-showcase)
258
+ ### 3. Autonomous Agent
497
259
 
498
260
  ```js
499
- // main.js: wuLit.register('analytics', AnalyticsApp)
500
- import { LitElement, html, css } from 'lit';
501
- import { WuMixin } from 'wu-framework/adapters/lit';
502
-
503
- class AnalyticsApp extends WuMixin(LitElement) {
504
- static styles = css`...`;
505
-
506
- connectedCallback() {
507
- super.connectedCallback();
508
- this.wuOn('theme:toggle', () => this.requestUpdate());
509
- const stats = this.wuGetState('stats');
510
- }
511
-
512
- render() {
513
- return html`
514
- <button @click=${() => this.wuEmit('analytics:export')}>Export</button>
515
- `;
516
- }
261
+ for await (const step of wu.ai.agent('Find the top customer and show their profile')) {
262
+ console.log(`Step ${step.step}: ${step.content}`);
517
263
  }
518
- customElements.define('analytics-app', AnalyticsApp);
519
264
  ```
520
265
 
521
- ### Vanilla JS — Chat Widget (mf-sandbox)
266
+ ### 4. Cross-App Orchestration
522
267
 
523
268
  ```js
524
- // main.js default import
525
- import wuVanilla from 'wu-framework/adapters/vanilla';
526
-
527
- const { useWuEvents, useWuStore } = wuVanilla;
528
- const events = useWuEvents();
529
- const store = useWuStore();
530
-
531
- function mount(container) {
532
- events.on('theme:toggle', (e) => applyTheme(e.data?.theme));
533
- const user = store.get('user');
534
- // render chat UI into container...
535
- }
536
-
537
- function unmount(container) {
538
- events.cleanup();
539
- container.innerHTML = '';
540
- }
269
+ const result = await wu.ai.intent('Find customer Emma and refund order #4821');
270
+ console.log(result.appsInvolved); // ['customers', 'orders']
541
271
  ```
542
272
 
543
273
  ---
544
274
 
545
- ## Plugins & Lifecycle Hooks
275
+ ## Plugins & Hooks
546
276
 
547
277
  ```js
548
- import { usePlugin, createPlugin, useHook, createGuardHook } from 'wu-framework';
278
+ import { usePlugin, createPlugin, useHook } from 'wu-framework';
549
279
 
550
- // Plugin
551
280
  usePlugin(createPlugin({
552
281
  name: 'analytics',
553
282
  install: (api) => api.on('app:mounted', (e) => track(e)),
554
283
  afterMount: async (ctx) => log('mounted in', ctx.mountTime, 'ms')
555
284
  }));
556
285
 
557
- // Lifecycle hook
558
286
  useHook('beforeMount', async (context, next) => {
559
287
  console.log('Mounting:', context.appName);
560
288
  await next();
561
289
  });
562
-
563
- // Guard — can cancel mount
564
- useHook('beforeMount', createGuardHook(async () => isAuthenticated()));
565
290
  ```
566
291
 
567
292
  Phases: `beforeInit` → `afterInit` → `beforeLoad` → `afterLoad` → `beforeMount` → `afterMount` → `beforeUnmount` → `afterUnmount`
568
293
 
569
294
  ---
570
295
 
571
- ## Loading Strategies
572
-
573
- ```js
574
- await wu.init({
575
- apps: [
576
- { name: 'header', url: '/mfe/header', strategy: 'eager' },
577
- { name: 'content', url: '/mfe/content', strategy: 'lazy' },
578
- { name: 'analytics', url: '/mfe/analytics', strategy: 'idle' }
579
- ]
580
- });
581
- ```
582
-
583
- | Strategy | Behavior |
584
- |----------|----------|
585
- | `lazy` | Load when mounted (default) |
586
- | `eager` | Load immediately |
587
- | `preload` | `<link rel="prefetch">` |
588
- | `idle` | `requestIdleCallback` |
589
- | `speculate` | Speculation Rules API with automatic fallback chain |
590
-
591
- ---
592
-
593
- ## Intelligent Prefetch (Speculation Rules API)
594
-
595
- Wu uses the browser-native [Speculation Rules API](https://developer.chrome.com/docs/web-platform/prerender-pages) (Chrome 121+) for near-instant microfrontend loads, with automatic fallbacks for older browsers.
596
-
597
- ### Fallback chain
598
-
599
- ```
600
- Speculation Rules API → <link rel="modulepreload"> → <link rel="prefetch">
601
- (Chrome 121+) (modern browsers) (all browsers)
602
- ```
603
-
604
- ### Usage
296
+ ## Prefetch (Speculation Rules API)
605
297
 
606
298
  ```js
607
- // Prefetch specific apps
608
299
  wu.prefetch(['sidebar', 'analytics']);
609
-
610
- // Prefetch all registered apps
300
+ wu.prefetch(['sidebar'], { trigger: 'hover', action: 'prerender' });
611
301
  wu.prefetchAll();
612
-
613
- // With options
614
- wu.prefetch(['sidebar'], {
615
- trigger: 'hover', // 'immediate' | 'hover' | 'visible' | 'idle'
616
- action: 'prefetch', // 'prefetch' | 'prerender'
617
- eagerness: 'moderate', // 'immediate' | 'eager' | 'moderate' | 'conservative'
618
- });
619
302
  ```
620
303
 
621
- ### Triggers
622
-
623
- | Trigger | Behavior |
624
- |---------|----------|
625
- | `immediate` | Prefetch now (default) |
626
- | `hover` | Prefetch when user hovers over the app's container |
627
- | `visible` | Prefetch when app's container enters viewport (IntersectionObserver, 200px margin) |
628
- | `idle` | Prefetch during browser idle time (`requestIdleCallback`) |
629
-
630
- ### As loading strategy
631
-
632
- ```js
633
- await wu.init({
634
- apps: [
635
- { name: 'header', url: '/mfe/header', strategy: 'eager' },
636
- { name: 'sidebar', url: '/mfe/sidebar', strategy: 'speculate' },
637
- { name: 'analytics', url: '/mfe/analytics', strategy: 'idle' },
638
- ]
639
- });
640
- ```
641
-
642
- The `speculate` strategy uses the Speculation Rules API by default with `immediate` trigger. It degrades gracefully — your app works on any browser, it just loads faster on Chrome 121+.
304
+ Fallback chain: Speculation Rules API (Chrome 121+) → `<link rel="modulepreload">` → `<link rel="prefetch">`
643
305
 
644
306
  ---
645
307
 
646
- ## Cookie Overrides for QA/Testing
647
-
648
- Redirect individual microfrontends to different URLs without changing code or deploying. QA sets a cookie, and only **their browser** sees the override — everyone else sees production.
649
-
650
- ### How it works
651
-
652
- ```
653
- 1. QA opens DevTools → Application → Cookies
654
- 2. Sets: wu-override:sidebar=http://localhost:5174
655
- 3. Reloads → only "sidebar" loads from localhost
656
- 4. Everyone else sees production sidebar
657
- ```
658
-
659
- ### Programmatic API
308
+ ## Cookie Overrides for QA
660
309
 
661
310
  ```js
662
- // Set an override (persists as cookie for 24h)
663
311
  wu.override('sidebar', 'http://localhost:5174');
664
-
665
- // Check active overrides
666
- wu.getOverrides();
667
- // → { sidebar: 'http://localhost:5174' }
668
-
669
- // Remove one
312
+ wu.getOverrides(); // { sidebar: 'http://localhost:5174' }
670
313
  wu.removeOverride('sidebar');
671
-
672
- // Clear all
673
- wu.clearOverrides();
674
314
  ```
675
315
 
676
- ### Security model (3 layers)
677
-
678
- | Layer | Protection |
679
- |-------|-----------|
680
- | **Environment gate** | Disabled in production by default. Auto-enabled only on `localhost` / `127.0.0.1`. Must explicitly opt-in for production. |
681
- | **Domain allowlist** | Only overrides to whitelisted domains are accepted. Supports wildcards: `*.company.com`. |
682
- | **Visual indicator** | Fixed yellow banner shows active overrides. Prevents silent phishing. Click to hide, double-click to clear. |
683
-
684
- ### Enable in production (for QA teams)
685
-
686
- ```js
687
- wu.overrides.configure({
688
- enabled: true,
689
- allowedDomains: ['*.mycompany.com', 'localhost', '*.vercel.app'],
690
- showIndicator: true,
691
- });
692
- ```
316
+ QA sets a cookie → only **their browser** sees the override. Everyone else sees production. 3-layer security: environment gate, domain allowlist, visual indicator.
693
317
 
694
- ### Cookie format
695
-
696
- ```
697
- wu-override:<appName>=<url>
318
+ ---
698
319
 
699
- # Examples:
700
- wu-override:sidebar=http://localhost:5174
701
- wu-override:header=https://staging.mycompany.com/header
702
- ```
320
+ ## Project Stats
703
321
 
704
- No proxy. No server changes. Pure client-side URL substitution at init time.
322
+ | Metric | Value |
323
+ |---|---|
324
+ | Source files | 79 |
325
+ | Lines of code | 23,442 |
326
+ | Test cases | **650** |
327
+ | Framework adapters | 8 |
328
+ | AI modules | 12 |
329
+ | Core modules | 23 |
330
+ | Runtime dependencies | **0** |
331
+ | Bundle (ESM, minified) | ~174 KB |
705
332
 
706
333
  ---
707
334
 
708
- ## Build & Test
335
+ ## Build
709
336
 
710
337
  ```bash
711
338
  npm run build # ESM + CJS + UMD + Dev
712
- npm run build:prod # Production (minified)
713
- npm run build:dev # Development (unminified)
714
- npm run test # 277 tests (Vitest)
339
+ npm run test # 650 tests (Vitest)
715
340
  npm run test:coverage # Coverage report
716
341
  ```
717
342
 
@@ -728,80 +353,56 @@ npm run test:coverage # Coverage report
728
353
 
729
354
  ```
730
355
  ┌───────────────────────────────────────────────────────────────────────┐
731
- │ SHELL (Astro)
732
- │ Sidebar + Section Containers + wu.init() + wu.mount() │
733
- ├──────────┬──────────┬──────────┬──────────┬──────────┬────────────────┤
734
- Shadow Shadow Shadow Shadow Shadow Shadow ...
735
- ┌──────┐┌──────┐┌──────┐┌──────┐┌──────┐┌──────┐
736
- │ │Topbar│ │ │Dashbd│ │ │Orders│ │ │Produc│ │ │Custo.│ │ │Analyt
737
- │Preact││Svelte││ReactVue 3 │ │ │Solid │ │ │ Lit │ │
738
- │ └──────┘ │ └──────┘ │ └──────┘ │ └──────┘ │ └──────┘ │ └──────┘ │
739
- ├──────────┴──────────┴──────────┴──────────┴──────────┴────────────────┤
740
- WU FRAMEWORK CORE
741
- Sandbox (module/strict/eval) · EventBus (wildcards, replay)
742
- Store (dot-paths, batch) · StyleBridge (shared/isolated/fully-iso)
743
- │ Loader · Hooks · Plugins · Cache · Prefetch · Overrides │
744
- └───────────────────────────────────────────────────────────────────────┘
356
+ │ SHELL (any framework)
357
+ ├──────────┬──────────┬──────────┬──────────┬──────────┬───────────────┤
358
+ │ Shadow │ Shadow │ Shadow │ Shadow │ Shadow │ Shadow ... │
359
+ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
360
+ │Topbar││Dashbd││Orders││Produc││Custo.│ │ │Analyt│ │
361
+ │ │Preact│ │ │Svelte│ │ │React │ │ │Vue 3 │ │ │Solid │ │ Lit
362
+ └──────┘└──────┘└──────┘└──────┘└──────┘└──────┘
363
+ ├──────────┴──────────┴──────────┴──────────┴──────────┴───────────────┤
364
+ │ WU FRAMEWORK CORE │
365
+ Sandbox (module/strict/eval) · EventBus (wildcards, replay)
366
+ Store (dot-paths, batch) · StyleBridge (shared/isolated/fully-iso)
367
+ Loader · Hooks · Plugins · Cache · Prefetch · Overrides
368
+ ├──────────────────────────────────────────────────────────────────────┤
369
+ │ WU AI (BYOL) │
370
+ │ Provider · Actions · Agent · Orchestrate · Triggers · Context │
371
+ │ Browser Actions · WebMCP · MCP Server · Workflows · Security │
372
+ └──────────────────────────────────────────────────────────────────────┘
745
373
  ```
746
374
 
747
- Each micro-app runs inside its own Shadow DOM with configurable CSS isolation and a JS sandbox that tracks and auto-cleans all side effects on unmount.
748
-
749
375
  ---
750
376
 
751
- ## API Reference
752
-
753
- ```js
754
- // Core
755
- import { wu, init, mount, unmount, destroy, app, define } from 'wu-framework';
756
-
757
- // Events
758
- import { emit, on, once, off } from 'wu-framework';
377
+ ## Documentation
759
378
 
760
- // State
761
- import { getState, setState, onStateChange } from 'wu-framework';
379
+ Full documentation at **[wu-framework.dev](https://wu-framework.dev)**
762
380
 
763
- // Plugins & Hooks
764
- import { usePlugin, createPlugin, useHook } from 'wu-framework';
765
- import { createGuardHook, createTimedHook, createTransformHook } from 'wu-framework';
381
+ - [Quick Start](https://wu-framework.dev/docs/quick-start) — Get running in 5 minutes
382
+ - [Getting Started](https://wu-framework.dev/docs/getting-started) Deeper tutorial with cross-app communication
383
+ - [API Reference](https://wu-framework.dev/docs/core/api) Full API docs
384
+ - [Event Bus](https://wu-framework.dev/docs/core/event-bus) — Inter-app communication
385
+ - [Shared Store](https://wu-framework.dev/docs/core/store) — Cross-app reactive state
386
+ - [Sandbox](https://wu-framework.dev/docs/core/sandbox) — JS isolation strategies
387
+ - [AI Overview](https://wu-framework.dev/docs/ai/overview) — 4 AI paradigms
388
+ - [AI Actions](https://wu-framework.dev/docs/ai/actions) — Register tools for the LLM
389
+ - [Browser Actions](https://wu-framework.dev/docs/ai/browser-actions) — 10 built-in browser tools
390
+ - [MCP Server](https://wu-framework.dev/docs/ai/mcp-server) — Connect AI agents to live apps
391
+ - [CSS Isolation](https://wu-framework.dev/docs/guides/css-isolation) — Shadow DOM style modes
392
+ - [Deployment](https://wu-framework.dev/docs/guides/deployment) — Production deployment guide
766
393
 
767
- // Performance
768
- import { startMeasure, endMeasure, generatePerformanceReport } from 'wu-framework';
394
+ ---
769
395
 
770
- // Prefetch (Speculation Rules API)
771
- import { prefetch, prefetchAll } from 'wu-framework';
396
+ ## Browser Support
772
397
 
773
- // Overrides (QA/Testing)
774
- import { override, removeOverride, getOverrides, clearOverrides } from 'wu-framework';
398
+ Chrome 80+, Firefox 78+, Safari 14+, Edge 80+. Shadow DOM v1 required.
775
399
 
776
- // Adapters
777
- import { wuReact } from 'wu-framework/adapters/react';
778
- import { wuVue } from 'wu-framework/adapters/vue';
779
- import { wuAngular } from 'wu-framework/adapters/angular';
780
- import { wuSvelte } from 'wu-framework/adapters/svelte';
781
- import { wuPreact } from 'wu-framework/adapters/preact';
782
- import { wuSolid } from 'wu-framework/adapters/solid';
783
- import { wuLit } from 'wu-framework/adapters/lit';
784
- import { wuVanilla } from 'wu-framework/adapters/vanilla';
400
+ ## Contributing
785
401
 
786
- // Prefetch
787
- wu.prefetch(['sidebar', 'analytics']);
788
- wu.prefetch(['sidebar'], { trigger: 'hover' });
789
- wu.prefetchAll();
790
-
791
- // Overrides (QA)
792
- wu.override('sidebar', 'http://localhost:5174');
793
- wu.removeOverride('sidebar');
794
- wu.getOverrides();
795
- wu.clearOverrides();
796
- wu.overrides.configure({ enabled: true, allowedDomains: ['*.company.com'] });
797
-
798
- // Log control
799
- wu.silence();
800
- wu.verbose();
801
- ```
802
-
803
- ---
402
+ Contributions welcome. Please open an issue first to discuss what you'd like to change.
804
403
 
805
404
  ## License
806
405
 
807
- MIT
406
+ [MIT](./LICENSE) — Free for personal and commercial use.
407
+
408
+ See [LICENSE-COMMERCIAL.md](./LICENSE-COMMERCIAL.md) for optional enterprise support and consulting.