@muten/core 0.0.6 → 0.0.7

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,5 +1,6 @@
1
1
 
2
- <img width="157" height="157" alt="Group 21" src="https://github.com/user-attachments/assets/fe9a02e6-483d-4788-9286-142c1ddb7057" /> </br>
2
+ <img width="157" height="157" alt="Group 21" src="https://github.com/user-attachments/assets/fe9a02e6-483d-4788-9286-142c1ddb7057" />
3
+ <br/>
3
4
  An **AI-first** frontend framework. You write `.muten` files; muten compiles them to vanilla JS
4
5
  with fine-grained signals — **no virtual DOM, no framework runtime to ship**. The language is small,
5
6
  semantic and analyzable on purpose: an AI (or a person) can **locate and mutate** an app cheaply.
@@ -9,6 +10,20 @@ npm create muten@latest my-app # scaffold a new app (cross-platform: Windows +
9
10
  cd my-app && npm install && npm run dev
10
11
  ```
11
12
 
13
+ ## Why muten (the moat, measured)
14
+
15
+ For an AI the cost of code is **context + mistakes + edit-radius** — muten is built to cut all three. The
16
+ reproducible benchmark in [`playground/`](../playground) (same app in muten / React / Vue / Svelte) shows:
17
+
18
+ - **Smaller to read & ship** — ~447 code tokens vs ~562 (Svelte), and a **~2.6 KB gzip** runtime — **7–28×
19
+ less JS** than the React/Vue/Svelte builds (a static page ships *zero*).
20
+ - **A deterministic oracle** — `muten check --json` catches the typical mistakes (unknown state/action/part,
21
+ bad style token, illegal mutation) at compile time, in ms, no browser. The *bounded* surface is why it can.
22
+ - **The whole app in ~80 tokens** — `app.map.json` is the index an agent reads first, instead of grepping a tree.
23
+ - **Tiny edit radius** — adding a feature touches a few lines in one file, not a component graph.
24
+
25
+ That's the trade: a small, analyzable language an AI can hold in its head — not a general-purpose one it can't.
26
+
12
27
  ## Capabilities
13
28
 
14
29
  - **UI** — declarative primitives (layout, text, forms, tables, links), `when`/`each` control flow,
@@ -22,9 +37,37 @@ cd my-app && npm install && npm run dev
22
37
  - **Routing** — real-path URLs, params (`/product/:id` → `param id`), guards, a `/404` catch-all.
23
38
  - **SEO / SSR** — `muten build` pre-renders every route to real HTML (static pages ship zero JS; data-driven
24
39
  pages are fetched at build), with per-page `meta { title … description … }` (`og:*` auto-derived).
40
+ - **Interop, lowest-tier first** — style native HTML + CSS libs with `class()`; mount **vanilla JS** libs
41
+ (charts, maps, date-pickers) via `Custom`; pull JS logic into expressions with `use fmt from "./lib.ts"`;
42
+ and only when you need a real **Svelte/React** component (e.g. shadcn) reach for an **island**
43
+ (`use X from "react:./X.jsx"` — code-split, lazy `client:visible`). See *Three tiers* below.
25
44
  - **AI-native** — `lint == build`, one source of truth per concept, and the full language reference ships
26
45
  inside every scaffolded app under `.claude/` (an AGENTS guide + a Claude skill).
27
46
 
47
+ ## What a muten app can do — three tiers
48
+
49
+ muten the *language* stays tiny on purpose; a muten *app* reaches the whole web platform through bounded,
50
+ analyzable escapes. Reach for the **lowest tier that works**:
51
+
52
+ **1 · Pure muten** — the declarative 80%, zero extra deps: pages + routing (params, guards, shell, `/404`) ·
53
+ `state`/`store`/`get` signals · `action`s with optimistic CRUD · `query` over REST `sources` (`refetch`,
54
+ multi-backend) · `Form` from an entity (with validation) · `DataTable`, `when`/`each`, reactive
55
+ `class(when …)`, `on(event: action)` · SSG + SEO. → a real **CRUD / SaaS / catalog / dashboard / content**
56
+ app is *100% muten*.
57
+
58
+ **2 · muten + the platform** — the web, *no framework runtime*: native HTML (`<input type="date">`,
59
+ `<dialog>`, `<details>`) styled with `class()` · CSS component libs (Tailwind, DaisyUI) · **vanilla JS via
60
+ `Custom`** (charts → chart.js, maps → Leaflet, date-picker → flatpickr, rich-text → Quill, drag-drop →
61
+ SortableJS, grids → Tabulator) · web components · `use fmt from "./lib.ts"` for any JS logic (zod, date-fns).
62
+ → almost every "hard widget" lands here, **without React**.
63
+
64
+ **3 · Svelte / React island** — only when the component *is* a framework component (e.g. **shadcn/ui**, a
65
+ React-only lib) with no native/vanilla equivalent. Ships that framework's runtime (lazy, code-split via
66
+ `client:visible`); props ↓ + events ↑ wire it to muten state. The narrow last resort, not the default.
67
+
68
+ > "Not expressible in pure muten" usually means **tier 2 (platform)**, rarely **tier 3 (React)** — and every
69
+ > escape is *bounded* (the oracle still checks the border), so the language never grows into a UI kit.
70
+
28
71
  ## The app, by convention
29
72
 
30
73
  ```
@@ -117,3 +160,58 @@ file-level conventions (≤500 lines, honest types, data-table dispatch, no magi
117
160
  muten imposes no theme. A page lays itself out with `style(…)` tokens (analyzable, resolved against
118
161
  `theme.muten`) and skins itself via `class("…")` (your CSS / Tailwind / anything). For behavior the
119
162
  primitives can't express, drop to a `Custom` component (`src/components/<Name>.js`).
163
+
164
+ ## Islands — Svelte & React
165
+
166
+ When a page needs a genuinely interactive widget or a framework UI lib muten can't express, mount a real
167
+ Svelte/React component as an **island**. The `svelte:` / `react:` prefix on `use … from` is the only marker;
168
+ the component file is plain Svelte/React and owns its own tooling.
169
+
170
+ ```
171
+ screen home
172
+
173
+ use Counter from "svelte:./Counter.svelte" # a Svelte island
174
+ use Likes from "react:./Likes.jsx" # a React island
175
+
176
+ state { total = 10 : number }
177
+ action setTotal mutates total <- n { total.set(n) }
178
+
179
+ Page style(padding.xl, gap.md) {
180
+ Counter(start: @total, onChange: setTotal) # props ↓ as signals, events ↑ to actions
181
+ Likes(start: @total, onLike: setTotal) client:visible # code-split, hydrated when scrolled into view
182
+ Text "muten state ← islands: {total}"
183
+ }
184
+ ```
185
+
186
+ `prop: @state` sends a value **down** (a React island re-renders when the signal changes; Svelte mounts once);
187
+ `onX: action` sends a callback that fires a muten action — that's how an island writes **back** to muten state.
188
+ No `client:` directive = hydrate on load. Add the framework's Vite plugin next to `muten()`:
189
+
190
+ ```js
191
+ // vite.config.mjs
192
+ import muten from '@muten/core/vite-plugin-muten.js';
193
+ import { svelte } from '@sveltejs/vite-plugin-svelte';
194
+ import react from '@vitejs/plugin-react';
195
+ export default { plugins: [muten(), svelte(), react()] };
196
+ ```
197
+
198
+ ## Status & roadmap (honest)
199
+
200
+ **Pre-1.0 — the core is solid, the edges are young.** Build real apps with it; don't bet a critical
201
+ production system on it yet (small ecosystem, one maintainer, not yet battle-tested).
202
+
203
+ **Solid today:** the language + compiler, the `check` / `build` / `map` CLI + oracle, the Vite plugin + dev
204
+ server + HMR, the VS Code extension (live-lint + autocomplete), Svelte & React islands, the reproducible benchmark.
205
+
206
+ **Experimental:** full island **SSR** — `muten build` server-renders an island's HTML (first paint + SEO),
207
+ but client hydration of that island still needs its framework bundled (pair the SSG HTML with the Vite client build).
208
+
209
+ **Next, toward 1.0:**
210
+ - richer `Form` field types (`date` · `number` · `select` · `bool` · `textarea` · …) — completes the
211
+ entity→form model, so more is pure-muten with *no* library.
212
+ - bounded aggregates in expressions (`sum` · `count`) — e.g. a cart total without an escape.
213
+ - keyed `each` (large-list perf); a live `source` (SSE / websocket) for real-time.
214
+
215
+ **By design (the moat, not a bug):** muten is declarative + bounded — no loops / `map` / `reduce` in the DSL
216
+ (use a `use` JS function), no widget primitives (use tier 2 / 3). The ceiling is what keeps it small and
217
+ analyzable; closing it would just make another general-purpose framework.
package/dist/build.js CHANGED
@@ -1,12 +1,12 @@
1
- import{writeFileSync as $,mkdirSync as P,readFileSync as N,existsSync as C,rmSync as F}from"node:fs";import{join as o,relative as M}from"node:path";import{Nt as B,Fmt as H}from"#engine/shared/vocab.js";import{readRoutes as I,readApi as J}from"#engine/project/routes.js";import{renderSsrBody as q,fetchSources as z}from"#engine/project/ssr.js";import{routeEntry as G}from"#engine/project/map.js";import{load as K,loadAllParts as L}from"#engine/project/load.js";import{validate as Q}from"#engine/ir/validate.js";import{compile as b}from"#engine/compile/compile.js";import{formatDiagnostic as k,ParseError as T}from"#engine/shared/diagnostics.js";async function te(s,r=o(s,"dist")){const n=e=>M(s,e);F(r,{recursive:!0,force:!0});const l=await L(s);Object.keys(l).length&&console.log(`Parts: ${Object.keys(l).join(", ")}`);const j=I(s),f=J(s);console.log(`Host app: ${s}`),console.log(`Pages: ${j.map(e=>"/"+e.route).join(", ")}
2
- `);const m=[],S={app:s.split(/[\\/]/).pop()||"",parts:Object.keys(l),routes:{}};for(const e of j){if(e.route.includes(":")){console.log(`\u2022 /${e.route} \u2014 skipped (route params run in the SPA runtime, not the static build)`);continue}let a;try{a=await K(e.screenPath,l)}catch(t){if(!(t instanceof T))throw t;const c={code:t.code,severity:"error",message:t.message,loc:t.loc,suggestion:null};throw new Error(`/${e.route}
3
- `+k(c,n(e.screenPath)))}const{doc:i,data:h,sources:p,styles:d,partNames:O}=a,{ok:x,diagnostics:A}=Q(i,{parts:O});if(!x)throw new Error(`/${e.route}
4
- `+A.map(t=>" "+k(t,n(e.screenPath))).join(`
5
- `));const E=[...new Set(Object.values(i.nodes).filter(t=>t.type===B.Custom).map(t=>t.props?.component))],g={};for(const t of E){if(!t)continue;const c=o(s,"src","components",t+".js");if(!C(c))throw new Error(`/${e.route}: Custom component not found: src/components/${t}.js`);g[t]=N(c,"utf8")}const u=b(i,h,d.css,g,p,{api:f});let v=u,w=!1;if(u.includes('<div id="app"></div>'))try{const t=Object.keys(p).length?{...h,...await z(p,f)}:h,c=q(b(i,t,d.css,g,p,{format:H.Ssr,api:f}));v=u.replace('<div id="app"></div>',`<div id="app">${c}</div>`),w=!0}catch{}const y=o(r,e.route);P(y,{recursive:!0}),$(o(y,"index.html"),v),console.log(`\u2713 /${e.route} \u2192 ${n(o(y,"index.html"))} (${Object.keys(i.nodes).length} nodes${w?", SSR":u.includes("<script")?", CSR":", static"}${d.from?", + "+d.from:""})`),m.push(e.route),S.routes["/"+e.route]=G(n(e.screenPath),i,p)}if(P(r,{recursive:!0}),!m.includes("")){const e=m.map(a=>`<li><a href="./${a}/">/${a}</a></li>`).join(`
6
- `);$(o(r,"index.html"),`<!doctype html><meta charset="utf-8"><title>app</title>
1
+ import{writeFileSync as k,mkdirSync as T,readFileSync as z,existsSync as B,rmSync as H}from"node:fs";import{join as i,relative as J,resolve as R,dirname as U}from"node:path";import{createRequire as G}from"node:module";import{pathToFileURL as O}from"node:url";import{Nt as K,Fmt as Q}from"#engine/shared/vocab.js";import{readRoutes as W,readApi as X}from"#engine/project/routes.js";import{renderSsrBody as Y,fetchSources as Z}from"#engine/project/ssr.js";import{routeEntry as _}from"#engine/project/map.js";import{load as ee,loadAllParts as te}from"#engine/project/load.js";import{validate as re}from"#engine/ir/validate.js";import{compile as C}from"#engine/compile/compile.js";import{formatDiagnostic as L,ParseError as se}from"#engine/shared/diagnostics.js";async function ve(o,l=i(o,"dist")){const m=e=>J(o,e);H(l,{recursive:!0,force:!0});const f=await te(o);Object.keys(f).length&&console.log(`Parts: ${Object.keys(f).join(", ")}`);const x=W(o),h=X(o);console.log(`Host app: ${o}`),console.log(`Pages: ${x.map(e=>"/"+e.route).join(", ")}
2
+ `);const g=[],E={app:o.split(/[\\/]/).pop()||"",parts:Object.keys(f),routes:{}},d={vite:null},y=G(i(o,"package.json")),A=e=>import(O(y.resolve(e)).href),N=async()=>{if(d.vite)return d.vite;const{createServer:e}=await import("vite"),s=[];try{const n=await A("@sveltejs/vite-plugin-svelte");s.push(n.svelte({compilerOptions:{dev:!1}}))}catch{}try{const n=await A("@vitejs/plugin-react");s.push(n.default())}catch{}const r=await e({configFile:!1,root:o,mode:"production",appType:"custom",server:{middlewareMode:!0},logLevel:"silent",plugins:s,resolve:{dedupe:["svelte"]},optimizeDeps:{include:["react","react-dom","react/jsx-runtime","react/jsx-dev-runtime"]}});return d.vite=r,r},V=async(e,s)=>{const r=await N(),n=(await r.ssrLoadModule(R(s,e.path))).default;if(e.adapter==="svelte")return(await r.ssrLoadModule("svelte/server")).render(n,{props:e.props}).body;if(e.adapter==="react"){const a=await import(O(y.resolve("react-dom/server")).href),p=await import(O(y.resolve("react")).href),w=a.renderToString||a.default.renderToString,$=p.createElement||p.default.createElement;return w($(n,e.props))}return""};for(const e of x){if(e.route.includes(":")){console.log(`\u2022 /${e.route} \u2014 skipped (route params run in the SPA runtime, not the static build)`);continue}let s;try{s=await ee(e.screenPath,f)}catch(t){if(!(t instanceof se))throw t;const c={code:t.code,severity:"error",message:t.message,loc:t.loc,suggestion:null};throw new Error(`/${e.route}
3
+ `+L(c,m(e.screenPath)))}const{doc:r,data:n,sources:a,styles:p,partNames:w}=s,{ok:$,diagnostics:D}=re(r,{parts:w});if(!$)throw new Error(`/${e.route}
4
+ `+D.map(t=>" "+L(t,m(e.screenPath))).join(`
5
+ `));const q=[...new Set(Object.values(r.nodes).filter(t=>t.type===K.Custom).map(t=>t.props?.component))],j={};for(const t of q){if(!t)continue;const c=i(o,"src","components",t+".js");if(!B(c))throw new Error(`/${e.route}: Custom component not found: src/components/${t}.js`);j[t]=z(c,"utf8")}const v=C(r,n,p.css,j,a,{api:h});let F=v,I=!1;if(v.includes('<div id="app"></div>'))try{const t=Object.keys(a).length?{...n,...await Z(a,h)}:n,c=[];let P=Y(C(r,t,p.css,j,a,{format:Q.Ssr,api:h}),c);for(let u=0;u<c.length;u++){let M="";try{M=await V(c[u],U(e.screenPath))}catch(b){console.log(` \u2022 island ${c[u].path} \u2192 client-only (${b instanceof Error?b.message:b})`)}P=P.replace(`<!--mi:${u}-->`,M)}F=v.replace('<div id="app"></div>',`<div id="app">${P}</div>`),I=!0}catch{}const S=i(l,e.route);T(S,{recursive:!0}),k(i(S,"index.html"),F),console.log(`\u2713 /${e.route} \u2192 ${m(i(S,"index.html"))} (${Object.keys(r.nodes).length} nodes${I?", SSR":v.includes("<script")?", CSR":", static"}${p.from?", + "+p.from:""})`),g.push(e.route),E.routes["/"+e.route]=_(m(e.screenPath),r,a)}if(d.vite&&await d.vite.close(),T(l,{recursive:!0}),!g.includes("")){const e=g.map(s=>`<li><a href="./${s}/">/${s}</a></li>`).join(`
6
+ `);k(i(l,"index.html"),`<!doctype html><meta charset="utf-8"><title>app</title>
7
7
  <h1>Routes</h1>
8
8
  <ul>
9
9
  ${e}
10
10
  </ul>
11
11
  `),console.log(`
12
- \u2713 ${n(o(r,"index.html"))} \u2192 route index`)}return $(o(r,"app.map.json"),JSON.stringify(S,null,2)),console.log(`\u2713 ${n(o(r,"app.map.json"))} \u2192 app graph (the root the AI reads)`),{routes:m,outDir:r}}export{te as buildApp};
12
+ \u2713 ${m(i(l,"index.html"))} \u2192 route index`)}return k(i(l,"app.map.json"),JSON.stringify(E,null,2)),console.log(`\u2713 ${m(i(l,"app.map.json"))} \u2192 app graph (the root the AI reads)`),{routes:g,outDir:l}}export{ve as buildApp};
@@ -1,11 +1,13 @@
1
- import{tokenClass as Z,resolveToken as ge,defaultTheme as _e}from"#engine/style/tokens.js";import{Nt as l,Ek as de,Fmt as y,Fk as q}from"#engine/shared/vocab.js";import{customValue as ye,CONTAINERS as ee,parseClause as be,editableFields as Se}from"#engine/compile/helpers.js";import{emitStore as Ne,emitStatic as Ce,emitStaticHtml as Ee,emitSsr as ve,emitModule as Oe,emitHtml as xe}from"#engine/compile/emit.js";import{Logic as ke}from"#engine/compile/logic.js";function Te(m,x={},k="",w={},J={},$={}){return te(m,x,k,w,J,{...$,format:y.Module})}function Fe(m={},x={},k={}){const{state:w={},gets:J={},actions:$={},effects:d=[],entities:N={}}=m;return te({screen:"store",entities:N,state:w,actions:$,gets:J,effects:d,consts:{},constraints:{},rootId:void 0,nodes:{}},x,"",{},k,{format:y.Store})}function te(m,x={},k="",w={},J={},$={}){const{nodes:d,rootId:N,state:j,entities:A,screen:ne}=m,B=$.theme||_e;let t=[],M=!1;const H=e=>{const s=t;t=[],e();const o=t;return t=s,o},W=new Set,h=(e,s)=>{for(const o of s.style||[])W.add(o);return[e,...(s.style||[]).map(Z),...(s.class||[]).filter(o=>typeof o=="string")].join(" ")},se=new Set(Object.keys(j)),K=new Set(Object.entries(j).filter(([,e])=>typeof e.source=="string"&&e.source.startsWith("query:")).map(([e])=>e)),U=new Set,oe={state:j,entities:A,actions:m.actions,consts:m.consts||{},gets:m.gets||{},effects:m.effects||[],stateKeys:se,queryStates:K,stores:$.stores||{},usedStores:U,params:new Set(m.params||[]),format:$.format},u=new ke(oe),C={locals:new Set},E=(e,s)=>{for(const o of s.class||[])typeof o!="string"&&t.push(`effect(() => el_${e}.classList.toggle(${JSON.stringify(o.name)}, !!(${u.compileExpr(o.cond,C)})));`);for(const[o,n]of Object.entries(s.on||{}))typeof n=="string"&&t.push(`el_${e}.addEventListener(${JSON.stringify(o)}, () => ${u.actionRef(n)}());`)},D=(e,s)=>{for(const o of d[e].children)G(o,s)},z=e=>e.parts.map(s=>typeof s=="string"?JSON.stringify(s):`String(${u.compileExpr(s,C)} ?? '')`).join(" + ");function R(e,s,o,n,f){if(t.push(`const el_${e} = document.createElement('${s}');`),t.push(`el_${e}.className = ${JSON.stringify(o)};`),typeof n=="string")t.push(`el_${e}.textContent = ${JSON.stringify(n)};`);else if(n&&"kind"in n){const c=z(n),i=n.parts.some(p=>typeof p!="string");t.push(i?`effect(() => { el_${e}.textContent = ${c}; });`:`el_${e}.textContent = ${c};`)}t.push(`${f}.appendChild(el_${e});`)}function I(e,s,o){if(typeof o=="string")t.push(`el_${e}.${s} = ${JSON.stringify(o)};`);else if(o&&"kind"in o){const n=z(o),f=o.parts.some(c=>typeof c!="string");t.push(f?`effect(() => { el_${e}.${s} = ${n}; });`:`el_${e}.${s} = ${n};`)}}function G(e,s){const o=d[e],n=o.props||{},f=ee[o.type];if(f){const[c,i]=f;t.push(`const el_${e} = document.createElement('${c}');`),t.push(`el_${e}.className = ${JSON.stringify(h(i,n))};`),o.type===l.Nav&&typeof n.label=="string"&&t.push(`el_${e}.setAttribute('aria-label', ${JSON.stringify(n.label)});`),t.push(`${s}.appendChild(el_${e});`),E(e,n),D(e,`el_${e}`);return}switch(o.type){case l.SearchField:{const c=u.bindSig(n.bind);t.push(`const el_${e} = document.createElement('input');`),t.push(`el_${e}.type = 'search';`),t.push(`el_${e}.className = ${JSON.stringify(h("search",n))};`),typeof n.placeholder=="string"&&t.push(`el_${e}.placeholder = ${JSON.stringify(n.placeholder)};`),t.push(`el_${e}.value = ${c}.get();`),t.push(`el_${e}.addEventListener('input', (e) => ${c}.set(e.target.value));`),t.push(`${s}.appendChild(el_${e});`);break}case l.DataTable:{const c=u.bindSig(n.data),i=K.has(c)?`${c}.get().data`:`${c}.get()`,p=n.columns||[],_=(n.where||[]).map(be),L=_.filter(r=>!r.dynamic).map(r=>`.filter((row) => ${r.expr})`).join(""),S=_.filter(r=>r.dynamic).map(r=>`.filter((row) => ${r.expr})`).join(""),a=o.children.map(r=>d[r]).filter(r=>r.type===l.RowAction);t.push(`const el_${e} = document.createElement('table');`),t.push(`el_${e}.className = ${JSON.stringify(h("datatable",n))};`),t.push(`const head_${e} = el_${e}.createTHead().insertRow();`);for(const r of p)t.push(`{ const th = document.createElement('th'); th.textContent = ${JSON.stringify(r)}; head_${e}.appendChild(th); }`);a.length&&t.push(`head_${e}.appendChild(document.createElement('th'));`),t.push(`const body_${e} = el_${e}.createTBody();`),t.push(`${s}.appendChild(el_${e});`),t.push(`function renderRow_${e}(row) {`),t.push(" const tr = document.createElement('tr');");for(const r of p)t.push(` { const td = document.createElement('td'); td.textContent = row[${JSON.stringify(r)}] ?? ''; tr.appendChild(td); }`);for(const r of a){const g=r.props||{},he=g.arg!==void 0?u.compileExpr(g.arg,{locals:new Set(["row"])}):"";t.push(` { const td = document.createElement('td'); const b = document.createElement('button'); b.className = ${JSON.stringify(h("row-action",g))}; b.textContent = ${JSON.stringify(g.label)}; b.addEventListener('click', () => ${u.actionRef(g.action)}(${he})); td.appendChild(b); tr.appendChild(td); }`)}t.push(" return tr;"),t.push("}"),t.push(`function base_${e}() { return ${i}${L}; }`),t.push("effect(() => {"),t.push(` const rows = base_${e}()${S};`),t.push(` body_${e}.replaceChildren(...rows.map(renderRow_${e}));`),t.push("});");break}case l.Form:{const c=u.bindSig(n.bind),i=j[c].type,p=Se(A[i]),_=(m.constraints||{})[i]||{};t.push(`const el_${e} = document.createElement('form');`),t.push(`el_${e}.className = ${JSON.stringify(h("form",n))};`),t.push(`{ const t = document.createElement('div'); t.className = 'form-title'; t.textContent = ${JSON.stringify("New "+i)}; el_${e}.appendChild(t); }`);const L=[];for(const a of p){const r=`f_${e}_${a.name}`;if(L.push({...a,var:r,c:_[a.name]}),a.kind===q.Enum){t.push(`const ${r} = document.createElement('select');`),t.push(`${r}.className = 'field';`);for(const g of a.options)t.push(`{ const o = document.createElement('option'); o.value = ${JSON.stringify(g)}; o.textContent = ${JSON.stringify(g)}; ${r}.appendChild(o); }`)}else t.push(`const ${r} = document.createElement('input');`),t.push(`${r}.type = ${JSON.stringify(a.kind===q.Email?"email":"text")};`),t.push(`${r}.className = 'field';`),t.push(`${r}.placeholder = ${JSON.stringify(a.name)};`);t.push(`${r}.addEventListener('input', (e) => ${c}.set({ ...${c}.get(), ${JSON.stringify(a.name)}: e.target.value }));`),t.push(`el_${e}.appendChild(${r});`),_[a.name]&&t.push(`const err_${r} = document.createElement('small'); err_${r}.className = 'field-error'; el_${e}.appendChild(err_${r});`)}t.push(`{ const sb = document.createElement('button'); sb.type = 'submit'; sb.className = 'submit'; sb.textContent = ${JSON.stringify(typeof n.submitLabel=="string"?n.submitLabel:"Submit")}; el_${e}.appendChild(sb); }`);const S=[];for(const a of L){if(!a.c)continue;const r=`err_${a.var}`,g=`String(__d[${JSON.stringify(a.name)}] ?? '')`;S.push(`${r}.textContent = '';`),a.c.required&&S.push(`if (!${g}.trim()) { ${r}.textContent = 'Required'; __ok = false; }`),a.c.min!=null&&S.push(`if (${g} && ${g}.length < ${a.c.min}) { ${r}.textContent = 'Min ${a.c.min} characters'; __ok = false; }`),a.c.max!=null&&S.push(`if (${g}.length > ${a.c.max}) { ${r}.textContent = 'Max ${a.c.max} characters'; __ok = false; }`)}S.length?t.push(`el_${e}.addEventListener('submit', (e) => { e.preventDefault(); const __d = ${c}.get(); let __ok = true; ${S.join(" ")} if (__ok) ${u.actionRef(n.submit)}(); });`):t.push(`el_${e}.addEventListener('submit', (e) => { e.preventDefault(); ${u.actionRef(n.submit)}(); });`),t.push("effect(() => {"),t.push(` const d = ${c}.get();`);for(const a of L){const r=a.kind===q.Enum?JSON.stringify(a.options[0]):"''";t.push(` { const v = d[${JSON.stringify(a.name)}] ?? ${r}; if (${a.var}.value !== v) ${a.var}.value = v; }`)}t.push("});"),t.push(`${s}.appendChild(el_${e});`);break}case l.Text:R(e,"p",h("text",n),n.value,s),E(e,n);break;case l.Span:R(e,"span",h("span",n),n.value,s),E(e,n);break;case l.Title:R(e,n.level||"h1",h("title",n),n.value,s),E(e,n);break;case l.Image:{t.push(`const el_${e} = document.createElement('img');`),t.push(`el_${e}.className = ${JSON.stringify(h("image",n))};`),I(e,"src",n.src),I(e,"alt",n.alt??""),t.push(`${s}.appendChild(el_${e});`);break}case l.When:{if(!n.cond)throw new Error("when without a condition");const c=u.compileExpr(n.cond,C),i=H(()=>D(e,"__p"));t.push(`function build_${e}(__p) {`);for(const p of i)t.push(" "+p);t.push("}"),t.push(`const anchor_${e} = document.createComment('when');`),t.push(`${s}.appendChild(anchor_${e});`),t.push(`let shown_${e} = [];`),t.push("effect(() => {"),t.push(` if (${c}) {`),t.push(` if (!shown_${e}.length) { const __f = document.createDocumentFragment(); build_${e}(__f); shown_${e} = [...__f.childNodes]; anchor_${e}.parentNode.insertBefore(__f, anchor_${e}); }`),t.push(` } else if (shown_${e}.length) { for (const __n of shown_${e}) __n.remove(); shown_${e} = []; }`),t.push("});");break}case l.Each:{if(!n.list||!n.as)throw new Error("each without a list or item variable");const c=u.compileExpr(n.list,C),i=H(()=>D(e,"__p"));t.push(`function buildItem_${e}(__p, ${n.as}) {`);for(const p of i)t.push(" "+p);t.push("}"),t.push(`const anchor_${e} = document.createComment('each');`),t.push(`${s}.appendChild(anchor_${e});`),t.push(`let items_${e} = [];`),t.push("effect(() => {"),t.push(` for (const __n of items_${e}) __n.remove();`),t.push(" const __f = document.createDocumentFragment();"),t.push(` for (const ${n.as} of (${c} ?? [])) buildItem_${e}(__f, ${n.as});`),t.push(` items_${e} = [...__f.childNodes];`),t.push(` anchor_${e}.parentNode.insertBefore(__f, anchor_${e});`),t.push("});");break}case l.Custom:{t.push(`const el_${e} = document.createElement('div');`),t.push(`el_${e}.className = ${JSON.stringify(h("custom",n))};`),t.push(`${s}.appendChild(el_${e});`);const c=Object.entries(n.inputs||{}).map(([p,_])=>`${JSON.stringify(p)}: ${ye(_)}`).join(", "),i=Object.entries(n.on||{}).map(([p,_])=>`${JSON.stringify(p)}: (...__a) => ${u.actionRef(typeof _=="string"?_:"")}(...__a)`).join(", ");t.push(`if (typeof __custom_${n.component} === 'function') __custom_${n.component}(el_${e}, { ${c} }, { ${i} });`);break}case l.Button:{if(t.push(`const el_${e} = document.createElement('button');`),t.push(`el_${e}.className = ${JSON.stringify(h("button",n))};`),o.children&&o.children.length?D(e,`el_${e}`):n.label!==void 0&&I(e,"textContent",n.label),n.action){const c=n.arg!==void 0?u.compileExpr(n.arg,C):"";t.push(`el_${e}.addEventListener('click', () => ${u.actionRef(n.action)}(${c}));`)}E(e,n),t.push(`${s}.appendChild(el_${e});`);break}case l.Link:{t.push(`const el_${e} = document.createElement('a');`),t.push(`el_${e}.className = ${JSON.stringify(h("link",n))};`),I(e,"href",n.to??"/"),o.children&&o.children.length?D(e,`el_${e}`):n.label!==void 0&&I(e,"textContent",n.label),E(e,n),t.push(`${s}.appendChild(el_${e});`);break}case l.Slot:{M=!0,t.push("const __outlet = document.createElement('div');"),t.push("__outlet.className = 'muten-outlet';"),t.push(`${s}.appendChild(__outlet);`);break}default:throw new Error("unsupported primitive: "+o.type)}}const v=e=>String(e??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"),P=e=>v(e).replace(/"/g,"&quot;"),b=e=>typeof e=="string"?e:"";function re(){if($.format===y.Store||(m.params||[]).length)return!1;const e=new Set([l.When,l.Each,l.Custom,l.Form,l.SearchField,l.DataTable,l.Slot]),s=["action","bind","submit","on","inputs","data"],o=["value","src","alt","label","to"];for(const n of Object.keys(d)){const f=d[n],c=f.props||{};if(e.has(f.type)||s.some(i=>c[i]!==void 0)||(c.class||[]).some(i=>typeof i!="string")||o.some(i=>{const p=c[i];return!!p&&typeof p=="object"&&"kind"in p&&p.kind===de.Interp}))return!1}return!0}function Q(e){const s=d[e],o=s.props||{},n=()=>(d[e].children||[]).map(Q).join(""),f=i=>` class="${P(h(i,o))}"`,c=ee[s.type];if(c){const[i,p]=c;return`<${i}${f(p)}>${n()}</${i}>`}switch(s.type){case l.Text:return`<p${f("text")}>${v(b(o.value))}</p>`;case l.Span:return`<span${f("span")}>${v(b(o.value))}</span>`;case l.Title:{const i=o.level||"h1";return`<${i}${f("title")}>${v(b(o.value))}</${i}>`}case l.Image:return`<img${f("image")} src="${P(b(o.src))}" alt="${P(b(o.alt))}">`;case l.Link:return`<a${f("link")} href="${P(b(o.to)||"/")}">${s.children&&s.children.length?n():v(b(o.label))}</a>`;case l.Button:return`<button${f("button")}>${s.children&&s.children.length?n():v(b(o.label))}</button>`;default:return""}}const V=$.format===y.Ssr?!1:re(),ce=(m.params||[]).map(e=>`const ${e} = (__params || {})[${JSON.stringify(e)}] ?? '';`).join(`
2
- `),ie=u.genState(),ae=u.genActions(),le=Object.entries(m.gets||{}).map(([e,s])=>`export const ${e} = computed(() => ${u.compileExpr(s,C)});`).join(`
3
- `),pe=u.genEffects();let X=null;V?X=N?Q(N):"":$.format!==y.Store&&N&&G(N,"app");const ue=t.join(`
4
- `),Y={};for(const e of Object.values(j))if(typeof e.source=="string"&&e.source.startsWith("query:")){const s=e.source.slice(6),o=(e.type.match(/^list<(.+)>$/)||[])[1];Y[s]=o?u.uuidFields(o):[]}const fe=[...W].map(e=>{const s=ge(e,B);if(!s)return"";const o=`.${Z(e)}{${s}}`,n=e.indexOf(":"),f=n>0&&B.breakpoints[e.slice(0,n)];return f?`@media (min-width:${f}){${o}}`:o}).filter(Boolean).join(`
5
- `),me=Object.entries(w).map(([e,s])=>`const __custom_${e} = (function () {
1
+ import{tokenClass as ne,resolveToken as Ee,defaultTheme as Oe}from"#engine/style/tokens.js";import{Nt as l,Ek as ve,Fmt as b,Fk as B}from"#engine/shared/vocab.js";import{customValue as se,CONTAINERS as oe,parseClause as xe,editableFields as ke}from"#engine/compile/helpers.js";import{emitStore as we,emitStatic as Je,emitStaticHtml as je,emitSsr as Ie,emitModule as Te,emitHtml as De}from"#engine/compile/emit.js";import{Logic as Re}from"#engine/compile/logic.js";function Be(m,k={},w="",J={},j={},$={}){return re(m,k,w,J,j,{...$,format:b.Module})}function He(m={},k={},w={}){const{state:J={},gets:j={},actions:$={},effects:y=[],entities:C={}}=m;return re({screen:"store",entities:C,state:J,actions:$,gets:j,effects:y,consts:{},constraints:{},rootId:void 0,nodes:{}},k,"",{},w,{format:b.Store})}function re(m,k={},w="",J={},j={},$={}){const{nodes:y,rootId:C,state:I,entities:H,screen:ce}=m,M=$.theme||Oe,ie={svelte:(e,s,o)=>`import('svelte').then((__s) => import(${JSON.stringify(o)}).then((__c) => (${e}.hasChildNodes() ? __s.hydrate : __s.mount)(__c.default, { target: ${e}, props: ${s} })))`,react:(e,s,o)=>`Promise.all([import('react-dom/client'), import('react'), import(${JSON.stringify(o)})]).then(([__d, __r, __c]) => { const __root = ${e}.hasChildNodes() ? __d.hydrateRoot(${e}, __r.createElement(__c.default, ${s})) : __d.createRoot(${e}); effect(() => __root.render(__r.createElement(__c.default, ${s}))); })`},P={};for(const e of m.imports||[]){const s=/^(svelte|react):(.+)$/.exec(e.from);if(s)for(const o of e.names)P[o]={adapter:s[1],path:s[2]}}let t=[],W=!1;const K=e=>{const s=t;t=[],e();const o=t;return t=s,o},U=new Set,h=(e,s)=>{for(const o of s.style||[])U.add(o);return[e,...(s.style||[]).map(ne),...(s.class||[]).filter(o=>typeof o=="string")].join(" ")},ae=new Set(Object.keys(I)),V=new Set(Object.entries(I).filter(([,e])=>typeof e.source=="string"&&e.source.startsWith("query:")).map(([e])=>e)),z=new Set,le={state:I,entities:H,actions:m.actions,consts:m.consts||{},gets:m.gets||{},effects:m.effects||[],stateKeys:ae,queryStates:V,stores:$.stores||{},usedStores:z,params:new Set(m.params||[]),format:$.format},u=new Re(le),E={locals:new Set},O=(e,s)=>{for(const o of s.class||[])typeof o!="string"&&t.push(`effect(() => el_${e}.classList.toggle(${JSON.stringify(o.name)}, !!(${u.compileExpr(o.cond,E)})));`);for(const[o,n]of Object.entries(s.on||{}))typeof n=="string"&&t.push(`el_${e}.addEventListener(${JSON.stringify(o)}, () => ${u.actionRef(n)}());`)},T=(e,s)=>{for(const o of y[e].children)Q(o,s)},G=e=>e.parts.map(s=>typeof s=="string"?JSON.stringify(s):`String(${u.compileExpr(s,E)} ?? '')`).join(" + ");function q(e,s,o,n,f){if(t.push(`const el_${e} = document.createElement('${s}');`),t.push(`el_${e}.className = ${JSON.stringify(o)};`),typeof n=="string")t.push(`el_${e}.textContent = ${JSON.stringify(n)};`);else if(n&&"kind"in n){const r=G(n),i=n.parts.some(p=>typeof p!="string");t.push(i?`effect(() => { el_${e}.textContent = ${r}; });`:`el_${e}.textContent = ${r};`)}t.push(`${f}.appendChild(el_${e});`)}function D(e,s,o){if(typeof o=="string")t.push(`el_${e}.${s} = ${JSON.stringify(o)};`);else if(o&&"kind"in o){const n=G(o),f=o.parts.some(r=>typeof r!="string");t.push(f?`effect(() => { el_${e}.${s} = ${n}; });`:`el_${e}.${s} = ${n};`)}}function Q(e,s){const o=y[e],n=o.props||{};if(P[o.type]){const r=P[o.type];t.push(`const el_${e} = document.createElement('div');`),t.push(`${s}.appendChild(el_${e});`);const i="{ "+Object.entries(o.args||{}).map(([_,d])=>typeof d=="string"&&!d.startsWith("@")&&(d in(m.actions||{})||d.includes("."))?`${JSON.stringify(_)}: (...__a) => ${u.actionRef(d)}(...__a)`:`${JSON.stringify(_)}: ${se(d)}`).join(", ")+" }";if($.format===b.Ssr){t.push(`el_${e}.innerHTML = __ssrIsland(${JSON.stringify(r.adapter)}, ${JSON.stringify(r.path)}, ${i});`);return}const p=ie[r.adapter](`el_${e}`,i,r.path);t.push(n.hydrate==="visible"?`__onVisible(el_${e}, () => ${p});`:n.hydrate==="idle"?`__onIdle(() => ${p});`:`${p};`);return}const f=oe[o.type];if(f){const[r,i]=f;t.push(`const el_${e} = document.createElement('${r}');`),t.push(`el_${e}.className = ${JSON.stringify(h(i,n))};`),o.type===l.Nav&&typeof n.label=="string"&&t.push(`el_${e}.setAttribute('aria-label', ${JSON.stringify(n.label)});`),t.push(`${s}.appendChild(el_${e});`),O(e,n),T(e,`el_${e}`);return}switch(o.type){case l.SearchField:{const r=u.bindSig(n.bind);t.push(`const el_${e} = document.createElement('input');`),t.push(`el_${e}.type = 'search';`),t.push(`el_${e}.className = ${JSON.stringify(h("search",n))};`),typeof n.placeholder=="string"&&t.push(`el_${e}.placeholder = ${JSON.stringify(n.placeholder)};`),t.push(`el_${e}.value = ${r}.get();`),t.push(`el_${e}.addEventListener('input', (e) => ${r}.set(e.target.value));`),t.push(`${s}.appendChild(el_${e});`);break}case l.DataTable:{const r=u.bindSig(n.data),i=V.has(r)?`${r}.get().data`:`${r}.get()`,p=n.columns||[],_=(n.where||[]).map(xe),d=_.filter(c=>!c.dynamic).map(c=>`.filter((row) => ${c.expr})`).join(""),N=_.filter(c=>c.dynamic).map(c=>`.filter((row) => ${c.expr})`).join(""),a=o.children.map(c=>y[c]).filter(c=>c.type===l.RowAction);t.push(`const el_${e} = document.createElement('table');`),t.push(`el_${e}.className = ${JSON.stringify(h("datatable",n))};`),t.push(`const head_${e} = el_${e}.createTHead().insertRow();`);for(const c of p)t.push(`{ const th = document.createElement('th'); th.textContent = ${JSON.stringify(c)}; head_${e}.appendChild(th); }`);a.length&&t.push(`head_${e}.appendChild(document.createElement('th'));`),t.push(`const body_${e} = el_${e}.createTBody();`),t.push(`${s}.appendChild(el_${e});`),t.push(`function renderRow_${e}(row) {`),t.push(" const tr = document.createElement('tr');");for(const c of p)t.push(` { const td = document.createElement('td'); td.textContent = row[${JSON.stringify(c)}] ?? ''; tr.appendChild(td); }`);for(const c of a){const g=c.props||{},Ce=g.arg!==void 0?u.compileExpr(g.arg,{locals:new Set(["row"])}):"";t.push(` { const td = document.createElement('td'); const b = document.createElement('button'); b.className = ${JSON.stringify(h("row-action",g))}; b.textContent = ${JSON.stringify(g.label)}; b.addEventListener('click', () => ${u.actionRef(g.action)}(${Ce})); td.appendChild(b); tr.appendChild(td); }`)}t.push(" return tr;"),t.push("}"),t.push(`function base_${e}() { return ${i}${d}; }`),t.push("effect(() => {"),t.push(` const rows = base_${e}()${N};`),t.push(` body_${e}.replaceChildren(...rows.map(renderRow_${e}));`),t.push("});");break}case l.Form:{const r=u.bindSig(n.bind),i=I[r].type,p=ke(H[i]),_=(m.constraints||{})[i]||{};t.push(`const el_${e} = document.createElement('form');`),t.push(`el_${e}.className = ${JSON.stringify(h("form",n))};`),t.push(`{ const t = document.createElement('div'); t.className = 'form-title'; t.textContent = ${JSON.stringify("New "+i)}; el_${e}.appendChild(t); }`);const d=[];for(const a of p){const c=`f_${e}_${a.name}`;if(d.push({...a,var:c,c:_[a.name]}),a.kind===B.Enum){t.push(`const ${c} = document.createElement('select');`),t.push(`${c}.className = 'field';`);for(const g of a.options)t.push(`{ const o = document.createElement('option'); o.value = ${JSON.stringify(g)}; o.textContent = ${JSON.stringify(g)}; ${c}.appendChild(o); }`)}else t.push(`const ${c} = document.createElement('input');`),t.push(`${c}.type = ${JSON.stringify(a.kind===B.Email?"email":"text")};`),t.push(`${c}.className = 'field';`),t.push(`${c}.placeholder = ${JSON.stringify(a.name)};`);t.push(`${c}.addEventListener('input', (e) => ${r}.set({ ...${r}.get(), ${JSON.stringify(a.name)}: e.target.value }));`),t.push(`el_${e}.appendChild(${c});`),_[a.name]&&t.push(`const err_${c} = document.createElement('small'); err_${c}.className = 'field-error'; el_${e}.appendChild(err_${c});`)}t.push(`{ const sb = document.createElement('button'); sb.type = 'submit'; sb.className = 'submit'; sb.textContent = ${JSON.stringify(typeof n.submitLabel=="string"?n.submitLabel:"Submit")}; el_${e}.appendChild(sb); }`);const N=[];for(const a of d){if(!a.c)continue;const c=`err_${a.var}`,g=`String(__d[${JSON.stringify(a.name)}] ?? '')`;N.push(`${c}.textContent = '';`),a.c.required&&N.push(`if (!${g}.trim()) { ${c}.textContent = 'Required'; __ok = false; }`),a.c.min!=null&&N.push(`if (${g} && ${g}.length < ${a.c.min}) { ${c}.textContent = 'Min ${a.c.min} characters'; __ok = false; }`),a.c.max!=null&&N.push(`if (${g}.length > ${a.c.max}) { ${c}.textContent = 'Max ${a.c.max} characters'; __ok = false; }`)}N.length?t.push(`el_${e}.addEventListener('submit', (e) => { e.preventDefault(); const __d = ${r}.get(); let __ok = true; ${N.join(" ")} if (__ok) ${u.actionRef(n.submit)}(); });`):t.push(`el_${e}.addEventListener('submit', (e) => { e.preventDefault(); ${u.actionRef(n.submit)}(); });`),t.push("effect(() => {"),t.push(` const d = ${r}.get();`);for(const a of d){const c=a.kind===B.Enum?JSON.stringify(a.options[0]):"''";t.push(` { const v = d[${JSON.stringify(a.name)}] ?? ${c}; if (${a.var}.value !== v) ${a.var}.value = v; }`)}t.push("});"),t.push(`${s}.appendChild(el_${e});`);break}case l.Text:q(e,"p",h("text",n),n.value,s),O(e,n);break;case l.Span:q(e,"span",h("span",n),n.value,s),O(e,n);break;case l.Title:q(e,n.level||"h1",h("title",n),n.value,s),O(e,n);break;case l.Image:{t.push(`const el_${e} = document.createElement('img');`),t.push(`el_${e}.className = ${JSON.stringify(h("image",n))};`),D(e,"src",n.src),D(e,"alt",n.alt??""),t.push(`${s}.appendChild(el_${e});`);break}case l.When:{if(!n.cond)throw new Error("when without a condition");const r=u.compileExpr(n.cond,E),i=K(()=>T(e,"__p"));t.push(`function build_${e}(__p) {`);for(const p of i)t.push(" "+p);t.push("}"),t.push(`const anchor_${e} = document.createComment('when');`),t.push(`${s}.appendChild(anchor_${e});`),t.push(`let shown_${e} = [];`),t.push("effect(() => {"),t.push(` if (${r}) {`),t.push(` if (!shown_${e}.length) { const __f = document.createDocumentFragment(); build_${e}(__f); shown_${e} = [...__f.childNodes]; anchor_${e}.parentNode.insertBefore(__f, anchor_${e}); }`),t.push(` } else if (shown_${e}.length) { for (const __n of shown_${e}) __n.remove(); shown_${e} = []; }`),t.push("});");break}case l.Each:{if(!n.list||!n.as)throw new Error("each without a list or item variable");const r=u.compileExpr(n.list,E),i=K(()=>T(e,"__p"));t.push(`function buildItem_${e}(__p, ${n.as}) {`);for(const p of i)t.push(" "+p);t.push("}"),t.push(`const anchor_${e} = document.createComment('each');`),t.push(`${s}.appendChild(anchor_${e});`),t.push(`let items_${e} = [];`),t.push("effect(() => {"),t.push(` for (const __n of items_${e}) __n.remove();`),t.push(" const __f = document.createDocumentFragment();"),t.push(` for (const ${n.as} of (${r} ?? [])) buildItem_${e}(__f, ${n.as});`),t.push(` items_${e} = [...__f.childNodes];`),t.push(` anchor_${e}.parentNode.insertBefore(__f, anchor_${e});`),t.push("});");break}case l.Custom:{t.push(`const el_${e} = document.createElement('div');`),t.push(`el_${e}.className = ${JSON.stringify(h("custom",n))};`),t.push(`${s}.appendChild(el_${e});`);const r=Object.entries(n.inputs||{}).map(([p,_])=>`${JSON.stringify(p)}: ${se(_)}`).join(", "),i=Object.entries(n.on||{}).map(([p,_])=>`${JSON.stringify(p)}: (...__a) => ${u.actionRef(typeof _=="string"?_:"")}(...__a)`).join(", ");t.push(`if (typeof __custom_${n.component} === 'function') __custom_${n.component}(el_${e}, { ${r} }, { ${i} });`);break}case l.Button:{if(t.push(`const el_${e} = document.createElement('button');`),t.push(`el_${e}.className = ${JSON.stringify(h("button",n))};`),o.children&&o.children.length?T(e,`el_${e}`):n.label!==void 0&&D(e,"textContent",n.label),n.action){const r=n.arg!==void 0?u.compileExpr(n.arg,E):"";t.push(`el_${e}.addEventListener('click', () => ${u.actionRef(n.action)}(${r}));`)}O(e,n),t.push(`${s}.appendChild(el_${e});`);break}case l.Link:{t.push(`const el_${e} = document.createElement('a');`),t.push(`el_${e}.className = ${JSON.stringify(h("link",n))};`),D(e,"href",n.to??"/"),o.children&&o.children.length?T(e,`el_${e}`):n.label!==void 0&&D(e,"textContent",n.label),O(e,n),t.push(`${s}.appendChild(el_${e});`);break}case l.Slot:{W=!0,t.push("const __outlet = document.createElement('div');"),t.push("__outlet.className = 'muten-outlet';"),t.push(`${s}.appendChild(__outlet);`);break}default:throw new Error("unsupported primitive: "+o.type)}}const v=e=>String(e??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"),L=e=>v(e).replace(/"/g,"&quot;"),S=e=>typeof e=="string"?e:"";function pe(){if($.format===b.Store||(m.params||[]).length)return!1;const e=new Set([l.When,l.Each,l.Custom,l.Form,l.SearchField,l.DataTable,l.Slot]),s=["action","bind","submit","on","inputs","data"],o=["value","src","alt","label","to"];for(const n of Object.keys(y)){const f=y[n],r=f.props||{};if(P[f.type]||e.has(f.type)||s.some(i=>r[i]!==void 0)||(r.class||[]).some(i=>typeof i!="string")||o.some(i=>{const p=r[i];return!!p&&typeof p=="object"&&"kind"in p&&p.kind===ve.Interp}))return!1}return!0}function X(e){const s=y[e],o=s.props||{},n=()=>(y[e].children||[]).map(X).join(""),f=i=>` class="${L(h(i,o))}"`,r=oe[s.type];if(r){const[i,p]=r;return`<${i}${f(p)}>${n()}</${i}>`}switch(s.type){case l.Text:return`<p${f("text")}>${v(S(o.value))}</p>`;case l.Span:return`<span${f("span")}>${v(S(o.value))}</span>`;case l.Title:{const i=o.level||"h1";return`<${i}${f("title")}>${v(S(o.value))}</${i}>`}case l.Image:return`<img${f("image")} src="${L(S(o.src))}" alt="${L(S(o.alt))}">`;case l.Link:return`<a${f("link")} href="${L(S(o.to)||"/")}">${s.children&&s.children.length?n():v(S(o.label))}</a>`;case l.Button:return`<button${f("button")}>${s.children&&s.children.length?n():v(S(o.label))}</button>`;default:return""}}const Y=$.format===b.Ssr?!1:pe(),ue=(m.params||[]).map(e=>`const ${e} = (__params || {})[${JSON.stringify(e)}] ?? '';`).join(`
2
+ `),fe=u.genState(),me=u.genActions(),$e=Object.entries(m.gets||{}).map(([e,s])=>`export const ${e} = computed(() => ${u.compileExpr(s,E)});`).join(`
3
+ `),he=u.genEffects();let Z=null;Y?Z=C?X(C):"":$.format!==b.Store&&C&&Q(C,"app");const _e=t.join(`
4
+ `),ee={};for(const e of Object.values(I))if(typeof e.source=="string"&&e.source.startsWith("query:")){const s=e.source.slice(6),o=(e.type.match(/^list<(.+)>$/)||[])[1];ee[s]=o?u.uuidFields(o):[]}const ge=[...U].map(e=>{const s=Ee(e,M);if(!s)return"";const o=`.${ne(e)}{${s}}`,n=e.indexOf(":"),f=n>0&&M.breakpoints[e.slice(0,n)];return f?`@media (min-width:${f}){${o}}`:o}).filter(Boolean).join(`
5
+ `),de=Object.entries(J).map(([e,s])=>`const __custom_${e} = (function () {
6
6
  ${s}
7
7
  return mount;
8
8
  })();`).join(`
9
9
 
10
- `),$e=[...U].map(e=>`import * as __store_${e} from 'virtual:muten/store/${e}';`).join(`
11
- `),T=m.meta||{},F={...T};T.title&&!F["og:title"]&&(F["og:title"]=T.title),T.description&&!F["og:description"]&&(F["og:description"]=T.description);const O={screen:ne,tokenCss:fe,projectCss:k,data:x,sources:J,api:$.api||{},meta:F,queryUuids:Y,stateDecls:ie,paramDecls:ce,actionDecls:ae,getDecls:le,effectDecls:pe,componentDecls:me,storeImports:$e,renderBody:ue,staticHtml:X??"",hasSlot:M};return $.format===y.Store?Ne(O):$.format===y.Ssr?ve(O):V?$.format===y.Module?Ce(O):Ee(O):$.format===y.Module?Oe(O):xe(O)}export{te as compile,Te as compileModule,Fe as compileStore};
10
+ `),ye=[...z].map(e=>`import * as __store_${e} from 'virtual:muten/store/${e}';`).join(`
11
+ `),be=e=>/^(svelte|react):/.test(e),Se=(m.imports||[]).filter(e=>!be(e.from)).map(e=>`import { ${e.names.join(", ")} } from ${JSON.stringify(e.from)};`).join(`
12
+ `),te=new Set(Object.values(y).map(e=>e.props?.hydrate).filter(Boolean)),A=[];te.has("visible")&&A.push("const __onVisible = (el, cb) => { const o = new IntersectionObserver((es) => { if (es[0].isIntersecting) { o.disconnect(); cb(); } }); o.observe(el); };"),te.has("idle")&&A.push("const __onIdle = (cb) => (typeof requestIdleCallback === 'function' ? requestIdleCallback : (f) => setTimeout(f, 1))(cb);");const Ne=A.join(`
13
+ `),R=m.meta||{},F={...R};R.title&&!F["og:title"]&&(F["og:title"]=R.title),R.description&&!F["og:description"]&&(F["og:description"]=R.description);const x={screen:ce,tokenCss:ge,projectCss:w,data:k,sources:j,api:$.api||{},meta:F,queryUuids:ee,stateDecls:fe,paramDecls:ue,actionDecls:me,getDecls:$e,effectDecls:he,componentDecls:de,storeImports:ye,externImports:Se,islandImports:Ne,renderBody:_e,staticHtml:Z??"",hasSlot:W};return $.format===b.Store?we(x):$.format===b.Ssr?Ie(x):Y?$.format===b.Module?Je(x):je(x):$.format===b.Module?Te(x):De(x)}export{re as compile,Be as compileModule,He as compileStore};
@@ -24,6 +24,7 @@ import{sourceRequest as a,sourceRows as l}from"#engine/shared/source.js";const i
24
24
  function __refetch(name, params, sig) { const q = __req(__SOURCES[name], __API); const qs = Object.keys(params).map((k) => encodeURIComponent(k) + '=' + encodeURIComponent(params[k])).join('&'); const url = qs ? q.url + (q.url.indexOf('?') >= 0 ? '&' : '?') + qs : q.url; sig.set({ ...sig.get(), loading: true, error: null }); fetch(url, { method: q.method, headers: { ...q.headers } }).then((r) => { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); }).then((j) => sig.set({ data: __fill(name, __rows(j, q.at)), loading: false, error: null })).catch((e) => sig.set({ ...sig.get(), loading: false, error: String(e) })); }
25
25
  function __send(url, method, body) { let d = { url: url, method: method }; const ci = url.indexOf(':'); if (ci > 0 && __API[url.slice(0, ci)]) d = { api: url.slice(0, ci), url: url.slice(ci + 1), method: method }; const q = __req(d, __API); const init = { method: q.method, headers: { ...q.headers } }; if (body != null) { init.body = JSON.stringify(body); if (!init.headers['content-type'] && !init.headers['Content-Type']) init.headers['content-type'] = 'application/json'; } return fetch(q.url, init).then((r) => { if (!r.ok) throw new Error('HTTP ' + r.status); return r.status === 204 ? null : r.json().catch(() => null); }); }
26
26
  function query(name) { const sig = signal({ data: [], loading: true, error: null }); __fetch(name).then((d) => sig.set({ data: d, loading: false, error: null })).catch((e) => sig.set({ data: [], loading: false, error: String(e) })); return sig; }`}function d(e){return`import { signal, computed, effect, __id, __has } from 'virtual:muten/runtime';
27
+ ${e.externImports}
27
28
 
28
29
  ${n(e)}
29
30
 
@@ -76,6 +77,8 @@ ${e.staticHtml}
76
77
  </html>
77
78
  `}function h(e){return`import { signal, effect, __id, __has } from 'virtual:muten/runtime';
78
79
  ${e.storeImports}
80
+ ${e.externImports}
81
+ ${e.islandImports}
79
82
  export const screen = ${JSON.stringify(e.screen)};
80
83
  export const css = ${JSON.stringify(`${e.tokenCss}
81
84
  ${e.projectCss}`)};
@@ -1,4 +1,4 @@
1
- import{Ek as h,StOp as a,BOp as f,UOp as g,Fmt as $}from"#engine/shared/vocab.js";import{JS_BINOP as d}from"#engine/compile/helpers.js";class y{constructor(t){this.ctx=t}ctx;inStore(t,i,e){const r=this.ctx.stores[t];return!!r&&(r[e]||[]).includes(i)}actionRef(t){if(!t)return"";const[i,e]=t.split(".");return e&&this.inStore(i,e,"actions")?(this.ctx.usedStores.add(i),`__store_${i}.${e}`):t}bindSig(t){if(typeof t!="string")return"";if(t.startsWith("@"))return t.slice(1);const[i,e]=t.split(".");return e&&this.ctx.stores[i]?(this.ctx.usedStores.add(i),`__store_${i}.${e}`):t}uuidFields(t){const i=this.ctx.entities[t]||{};return Object.entries(i).filter(([,e])=>e==="uuid").map(([e])=>e)}bodyHasWrite(t){return t.some(i=>i.op===a.Create||i.op===a.Update||i.op===a.Delete||i.op===a.Request||i.op===a.If&&(this.bodyHasWrite(i.then||[])||this.bodyHasWrite(i.else||[])))}writeActionsSet=null;writeActions(){return this.writeActionsSet||(this.writeActionsSet=new Set(Object.entries(this.ctx.actions).filter(([,t])=>this.bodyHasWrite(t.body||[])).map(([t])=>t))),this.writeActionsSet}resolveRef(t,i){const[e,...r]=t.split("."),s=r.length?"."+r.join("."):"";if(i.locals.has(e)||this.ctx.params.has(e))return e+s;if(this.ctx.queryStates.has(e))return r[0]==="loading"||r[0]==="error"?`${e}.get()${s}`:`${e}.get().data${s}`;if(this.ctx.stateKeys.has(e))return`${e}.get()`+s;if(this.ctx.stores[e]){const o=r[0],n=r.length>1?"."+r.slice(1).join("."):"";if(this.inStore(e,o,"state")||this.inStore(e,o,"gets"))return this.ctx.usedStores.add(e),`__store_${e}.${o}.get()${n}`}return this.ctx.consts[e]!==void 0?JSON.stringify(r.length?null:this.ctx.consts[e]):this.writeActions().has(e)&&(r[0]==="pending"||r[0]==="error")?`${r[0]==="pending"?"__pending_":"__error_"}${e}.get()`:e+s}compileExpr(t,i){if(t.kind===h.Lit)return JSON.stringify(t.value);if(t.kind===h.Ref)return this.resolveRef(t.name,i);if(t.kind===h.Tern)return`(${this.compileExpr(t.cond,i)} ? ${this.compileExpr(t.then,i)} : ${this.compileExpr(t.else,i)})`;if(t.kind===h.Un){if(t.op===g.Not)return`!(${this.compileExpr(t.operand,i)})`;throw new Error("unsupported unary operator")}if(t.kind===h.Bin){const e=this.compileExpr(t.left,i),r=this.compileExpr(t.right,i);if(t.op===f.Contains)return`__has(${e}, ${r})`;const s=d[t.op];if(s)return`(${e} ${s} ${r})`;throw new Error("unsupported operator: "+t.op)}throw new Error("unsupported expression")}stmtLines(t,i,e=!1){const r=this.ctx,s=[];if(t.op===a.If){s.push(`if (${this.compileExpr(t.cond,i)}) {`);for(const o of t.then||[])for(const n of this.stmtLines(o,i,e))s.push(" "+n);if(t.else){s.push("} else {");for(const o of t.else)for(const n of this.stmtLines(o,i,e))s.push(" "+n)}return s.push("}"),s}if(t.op===a.Reset)s.push(`${t.target}.set(${JSON.stringify(r.state[t.target].initial??null)});`);else if(t.op===a.Set)s.push(`${t.target}.set(${this.compileExpr(t.arg,i)});`);else if(t.op===a.Push){const o=(r.state[t.target].type.match(/^list<(.+)>$/)||[])[1],n=o&&r.entities[o],_=p=>r.queryStates.has(t.target)?`${t.target}.set({ ...${t.target}.get(), data: [...${t.target}.get().data, ${p}] });`:`${t.target}.set([...${t.target}.get(), ${p}]);`;if(n){s.push(`{ const __it = { ...${this.compileExpr(t.arg,i)} };`);for(const p of this.uuidFields(o))s.push(` if (__it.${p} === null || __it.${p} === undefined) __it.${p} = __id(); // auto uuid`);s.push(` ${_("__it")} }`)}else s.push(`${_(this.compileExpr(t.arg,i))}`)}else if(t.op===a.Remove){const o={...i,locals:new Set([...i.locals,t.param])},n=this.compileExpr(t.pred,o);s.push(r.queryStates.has(t.target)?`${t.target}.set({ ...${t.target}.get(), data: ${t.target}.get().data.filter((${t.param}) => !(${n})) });`:`${t.target}.set(${t.target}.get().filter((${t.param}) => !(${n})));`)}else if(t.op===a.Create||t.op===a.Update||t.op===a.Delete){const o=r.queryStates.has(t.target),n=o?`${t.target}.get().data`:`${t.target}.get()`,_=l=>o?`${t.target}.set({ ...${t.target}.get(), data: ${l} })`:`${t.target}.set(${l})`,p=o?`.catch((__e) => ${t.target}.set({ ...${t.target}.get(), error: String(__e) }))`:"",c=JSON.stringify(t.target),u=this.compileExpr(t.arg,i);e?t.op===a.Create?s.push(`{ const __i = { ...${u} }; if (__i.id == null) __i.id = __id(); const __prev = ${n}; ${_("[...__prev, __i]")}; try { const __r = await __write(${c}, 'POST', null, __i); ${_(`${n}.map((__x) => __x.id === __i.id ? __r : __x)`)}; } catch (__e) { ${_("__prev")}; throw __e; } }`):t.op===a.Update?s.push(`{ const __i = ${u}; const __prev = ${n}; ${_("__prev.map((__x) => __x.id === __i.id ? __i : __x)")}; try { const __r = await __write(${c}, 'PUT', __i.id, __i); ${_(`${n}.map((__x) => __x.id === __i.id ? __r : __x)`)}; } catch (__e) { ${_("__prev")}; throw __e; } }`):s.push(`{ const __i = ${u}; const __prev = ${n}; ${_("__prev.filter((__x) => __x.id !== __i.id)")}; try { await __write(${c}, 'DELETE', __i.id, null); } catch (__e) { ${_("__prev")}; throw __e; } }`):t.op===a.Create?s.push(`{ const __i = ${u}; __write(${c}, 'POST', null, __i).then((__r) => ${_(`[...${n}, __r]`)})${p}; }`):t.op===a.Update?s.push(`{ const __i = ${u}; __write(${c}, 'PUT', __i.id, __i).then((__r) => ${_(`${n}.map((__x) => __x.id === __i.id ? __r : __x)`)})${p}; }`):s.push(`{ const __i = ${u}; __write(${c}, 'DELETE', __i.id, null).then(() => ${_(`${n}.filter((__x) => __x.id !== __i.id)`)})${p}; }`)}else if(t.op===a.Refetch){const o=Object.entries(t.params).map(([n,_])=>`${JSON.stringify(n)}: ${this.compileExpr(_,i)}`).join(", ");s.push(`__refetch(${JSON.stringify(t.target)}, { ${o} }, ${t.target});`)}else if(t.op===a.Request){const o=typeof t.url=="string"?JSON.stringify(t.url):t.url.parts.map(_=>typeof _=="string"?JSON.stringify(_):`String(${this.compileExpr(_,i)})`).join(" + "),n=t.body?this.compileExpr(t.body,i):"null";s.push(e?`await __send(${o}, ${JSON.stringify(t.method)}, ${n});`:`__send(${o}, ${JSON.stringify(t.method)}, ${n}).catch(() => {});`)}return s}genState(){const t=this.ctx.format===$.Store?"export ":"",i=[];for(const[e,r]of Object.entries(this.ctx.state))typeof r.source=="string"&&r.source.startsWith("query:")?i.push(`${t}const ${e} = query(${JSON.stringify(r.source.slice(6))}); // async: ${e}.loading / .error / .data`):i.push(`${t}const ${e} = signal(${JSON.stringify(r.initial??null)});`);return i.join(`
1
+ import{Ek as l,StOp as a,BOp as f,UOp as g,Fmt as $}from"#engine/shared/vocab.js";import{JS_BINOP as d}from"#engine/compile/helpers.js";class y{constructor(t){this.ctx=t}ctx;inStore(t,i,e){const r=this.ctx.stores[t];return!!r&&(r[e]||[]).includes(i)}actionRef(t){if(!t)return"";const[i,e]=t.split(".");return e&&this.inStore(i,e,"actions")?(this.ctx.usedStores.add(i),`__store_${i}.${e}`):t}bindSig(t){if(typeof t!="string")return"";if(t.startsWith("@"))return t.slice(1);const[i,e]=t.split(".");return e&&this.ctx.stores[i]?(this.ctx.usedStores.add(i),`__store_${i}.${e}`):t}uuidFields(t){const i=this.ctx.entities[t]||{};return Object.entries(i).filter(([,e])=>e==="uuid").map(([e])=>e)}bodyHasWrite(t){return t.some(i=>i.op===a.Create||i.op===a.Update||i.op===a.Delete||i.op===a.Request||i.op===a.If&&(this.bodyHasWrite(i.then||[])||this.bodyHasWrite(i.else||[])))}writeActionsSet=null;writeActions(){return this.writeActionsSet||(this.writeActionsSet=new Set(Object.entries(this.ctx.actions).filter(([,t])=>this.bodyHasWrite(t.body||[])).map(([t])=>t))),this.writeActionsSet}resolveRef(t,i){const[e,...r]=t.split("."),s=r.length?"."+r.join("."):"";if(i.locals.has(e)||this.ctx.params.has(e))return e+s;if(this.ctx.queryStates.has(e))return r[0]==="loading"||r[0]==="error"?`${e}.get()${s}`:`${e}.get().data${s}`;if(this.ctx.stateKeys.has(e))return`${e}.get()`+s;if(this.ctx.stores[e]){const o=r[0],n=r.length>1?"."+r.slice(1).join("."):"";if(this.inStore(e,o,"state")||this.inStore(e,o,"gets"))return this.ctx.usedStores.add(e),`__store_${e}.${o}.get()${n}`}return this.ctx.consts[e]!==void 0?JSON.stringify(r.length?null:this.ctx.consts[e]):this.writeActions().has(e)&&(r[0]==="pending"||r[0]==="error")?`${r[0]==="pending"?"__pending_":"__error_"}${e}.get()`:e+s}compileExpr(t,i){if(t.kind===l.Lit)return JSON.stringify(t.value);if(t.kind===l.Ref)return this.resolveRef(t.name,i);if(t.kind===l.Call)return`${t.fn}(${t.args.map(e=>this.compileExpr(e,i)).join(", ")})`;if(t.kind===l.Tern)return`(${this.compileExpr(t.cond,i)} ? ${this.compileExpr(t.then,i)} : ${this.compileExpr(t.else,i)})`;if(t.kind===l.Un){if(t.op===g.Not)return`!(${this.compileExpr(t.operand,i)})`;throw new Error("unsupported unary operator")}if(t.kind===l.Bin){const e=this.compileExpr(t.left,i),r=this.compileExpr(t.right,i);if(t.op===f.Contains)return`__has(${e}, ${r})`;const s=d[t.op];if(s)return`(${e} ${s} ${r})`;throw new Error("unsupported operator: "+t.op)}throw new Error("unsupported expression")}stmtLines(t,i,e=!1){const r=this.ctx,s=[];if(t.op===a.If){s.push(`if (${this.compileExpr(t.cond,i)}) {`);for(const o of t.then||[])for(const n of this.stmtLines(o,i,e))s.push(" "+n);if(t.else){s.push("} else {");for(const o of t.else)for(const n of this.stmtLines(o,i,e))s.push(" "+n)}return s.push("}"),s}if(t.op===a.Reset)s.push(`${t.target}.set(${JSON.stringify(r.state[t.target].initial??null)});`);else if(t.op===a.Set)s.push(`${t.target}.set(${this.compileExpr(t.arg,i)});`);else if(t.op===a.Push){const o=(r.state[t.target].type.match(/^list<(.+)>$/)||[])[1],n=o&&r.entities[o],_=p=>r.queryStates.has(t.target)?`${t.target}.set({ ...${t.target}.get(), data: [...${t.target}.get().data, ${p}] });`:`${t.target}.set([...${t.target}.get(), ${p}]);`;if(n){s.push(`{ const __it = { ...${this.compileExpr(t.arg,i)} };`);for(const p of this.uuidFields(o))s.push(` if (__it.${p} === null || __it.${p} === undefined) __it.${p} = __id(); // auto uuid`);s.push(` ${_("__it")} }`)}else s.push(`${_(this.compileExpr(t.arg,i))}`)}else if(t.op===a.Remove){const o={...i,locals:new Set([...i.locals,t.param])},n=this.compileExpr(t.pred,o);s.push(r.queryStates.has(t.target)?`${t.target}.set({ ...${t.target}.get(), data: ${t.target}.get().data.filter((${t.param}) => !(${n})) });`:`${t.target}.set(${t.target}.get().filter((${t.param}) => !(${n})));`)}else if(t.op===a.Create||t.op===a.Update||t.op===a.Delete){const o=r.queryStates.has(t.target),n=o?`${t.target}.get().data`:`${t.target}.get()`,_=h=>o?`${t.target}.set({ ...${t.target}.get(), data: ${h} })`:`${t.target}.set(${h})`,p=o?`.catch((__e) => ${t.target}.set({ ...${t.target}.get(), error: String(__e) }))`:"",c=JSON.stringify(t.target),u=this.compileExpr(t.arg,i);e?t.op===a.Create?s.push(`{ const __i = { ...${u} }; if (__i.id == null) __i.id = __id(); const __prev = ${n}; ${_("[...__prev, __i]")}; try { const __r = await __write(${c}, 'POST', null, __i); ${_(`${n}.map((__x) => __x.id === __i.id ? __r : __x)`)}; } catch (__e) { ${_("__prev")}; throw __e; } }`):t.op===a.Update?s.push(`{ const __i = ${u}; const __prev = ${n}; ${_("__prev.map((__x) => __x.id === __i.id ? __i : __x)")}; try { const __r = await __write(${c}, 'PUT', __i.id, __i); ${_(`${n}.map((__x) => __x.id === __i.id ? __r : __x)`)}; } catch (__e) { ${_("__prev")}; throw __e; } }`):s.push(`{ const __i = ${u}; const __prev = ${n}; ${_("__prev.filter((__x) => __x.id !== __i.id)")}; try { await __write(${c}, 'DELETE', __i.id, null); } catch (__e) { ${_("__prev")}; throw __e; } }`):t.op===a.Create?s.push(`{ const __i = ${u}; __write(${c}, 'POST', null, __i).then((__r) => ${_(`[...${n}, __r]`)})${p}; }`):t.op===a.Update?s.push(`{ const __i = ${u}; __write(${c}, 'PUT', __i.id, __i).then((__r) => ${_(`${n}.map((__x) => __x.id === __i.id ? __r : __x)`)})${p}; }`):s.push(`{ const __i = ${u}; __write(${c}, 'DELETE', __i.id, null).then(() => ${_(`${n}.filter((__x) => __x.id !== __i.id)`)})${p}; }`)}else if(t.op===a.Refetch){const o=Object.entries(t.params).map(([n,_])=>`${JSON.stringify(n)}: ${this.compileExpr(_,i)}`).join(", ");s.push(`__refetch(${JSON.stringify(t.target)}, { ${o} }, ${t.target});`)}else if(t.op===a.Request){const o=typeof t.url=="string"?JSON.stringify(t.url):t.url.parts.map(_=>typeof _=="string"?JSON.stringify(_):`String(${this.compileExpr(_,i)})`).join(" + "),n=t.body?this.compileExpr(t.body,i):"null";s.push(e?`await __send(${o}, ${JSON.stringify(t.method)}, ${n});`:`__send(${o}, ${JSON.stringify(t.method)}, ${n}).catch(() => {});`)}return s}genState(){const t=this.ctx.format===$.Store?"export ":"",i=[];for(const[e,r]of Object.entries(this.ctx.state))typeof r.source=="string"&&r.source.startsWith("query:")?i.push(`${t}const ${e} = query(${JSON.stringify(r.source.slice(6))}); // async: ${e}.loading / .error / .data`):i.push(`${t}const ${e} = signal(${JSON.stringify(r.initial??null)});`);return i.join(`
2
2
  `)}genActions(){const t=this.ctx.format===$.Store?"export ":"",i=[],e=[];for(const[r,s]of Object.entries(this.ctx.actions)){const o=this.ctx.stateKeys.has(s.input),n={locals:new Set,input:s.input,inputIsState:o},_=o?"":s.input;if(this.writeActions().has(r)){i.push(`${t}const __pending_${r} = signal(false);`,`${t}const __error_${r} = signal(null);`),e.push(`${t}async function ${r}(${_}) {`),e.push(` __pending_${r}.set(true); __error_${r}.set(null);`),e.push(" try {");for(const p of s.body||[])for(const c of this.stmtLines(p,n,!0))e.push(" "+c);e.push(` } catch (__e) { __error_${r}.set(String(__e)); }`),e.push(` __pending_${r}.set(false);`),e.push("}")}else{e.push(`${t}function ${r}(${_}) {`);for(const p of s.body||[])for(const c of this.stmtLines(p,n))e.push(" "+c);e.push("}")}}return[...i,...e].join(`
3
3
  `)}genEffects(){const t={locals:new Set};return this.ctx.effects.map(i=>`effect(() => {
4
4
  ${i.map(e=>this.stmtLines(e,t).map(r=>" "+r).join(`
@@ -1 +1 @@
1
- import{Ek as f}from"#engine/shared/vocab.js";function A(t,n){const e=new Set;return{tree:t&&o(t,n,e),used:[...e]}}function o(t,n,e){const u=n[t.type];if(u){e.add(t.type);const c=s(u.tree,t.args||{});return o(c,n,e)}const i={type:t.type};return t.loc&&(i.loc=t.loc),t.args&&(i.args=t.args),t.props&&(i.props=t.props),t.children&&(i.children=t.children.map(c=>o(c,n,e))),i}function s(t,n){const e={type:t.type};return t.props&&(e.props=g(t.props,n)),t.args&&(e.args=a(t.args,n)),t.children&&(e.children=t.children.map(u=>s(u,n))),e}function g(t,n){const e={...t};return t.value!==void 0&&(e.value=d(t.value,n)),t.label!==void 0&&(e.label=d(t.label,n)),t.src!==void 0&&(e.src=d(t.src,n)),t.alt!==void 0&&(e.alt=d(t.alt,n)),t.placeholder!==void 0&&(e.placeholder=d(t.placeholder,n)),t.submitLabel!==void 0&&(e.submitLabel=d(t.submitLabel,n)),t.cond!==void 0&&(e.cond=r(t.cond,n)),t.list!==void 0&&(e.list=r(t.list,n)),t.arg!==void 0&&(e.arg=r(t.arg,n)),t.action!==void 0&&(e.action=l(t.action,n)),t.submit!==void 0&&(e.submit=l(t.submit,n)),t.bind!==void 0&&(e.bind=l(t.bind,n)),t.to!==void 0&&(e.to=typeof t.to=="string"?t.to:b(t.to,n)),t.inputs!==void 0&&(e.inputs=a(t.inputs,n)),t.on!==void 0&&(e.on=a(t.on,n)),e}function d(t,n){if(typeof t=="string")return t;if("$param"in t){const e=n[t.$param];return typeof e=="number"?String(e):e}return b(t,n)}function r(t,n){return t.kind===f.Ref?{kind:f.Ref,name:l(t.name,n)}:t.kind===f.Bin?{...t,left:r(t.left,n),right:r(t.right,n)}:t.kind===f.Un?{...t,operand:r(t.operand,n)}:t.kind===f.Tern?{...t,cond:r(t.cond,n),then:r(t.then,n),else:r(t.else,n)}:t}function b(t,n){return{kind:f.Interp,parts:t.parts.map(e=>typeof e=="string"?e:r(e,n))}}function a(t,n){const e={};for(const[u,i]of Object.entries(t))e[u]=m(i,n);return e}function m(t,n){return typeof t=="string"?t.startsWith("$")?l(t,n):t:typeof t=="number"?t:n[t.$param]}function l(t,n){if(!t.startsWith("$"))return t;const e=t.slice(1).split(".")[0],u=t.slice(1+e.length),i=n[e];return(typeof i=="string"?i:e)+u}export{A as compose};
1
+ import{Ek as f}from"#engine/shared/vocab.js";import{toDoc as h}from"#engine/ir/flatten.js";function p(t,n){const e=new Set;return{tree:t&&l(t,n,e),used:[...e]}}function N(t,n){const{tree:e,used:r}=p(t.tree,n),i={...t.entities},o={...t.state};for(const m of r){const a=n[m];a&&(Object.assign(i,a.entities),Object.assign(o,a.state))}return{doc:h({...t,entities:i,state:o,tree:e}),used:r}}function l(t,n,e){const r=n[t.type];if(r){e.add(t.type);const o=b(r.tree,t.args||{});return l(o,n,e)}const i={type:t.type};return t.loc&&(i.loc=t.loc),t.args&&(i.args=t.args),t.props&&(i.props=t.props),t.children&&(i.children=t.children.map(o=>l(o,n,e))),i}function b(t,n){const e={type:t.type};return t.props&&(e.props=A(t.props,n)),t.args&&(e.args=s(t.args,n)),t.children&&(e.children=t.children.map(r=>b(r,n))),e}function A(t,n){const e={...t};return t.value!==void 0&&(e.value=c(t.value,n)),t.label!==void 0&&(e.label=c(t.label,n)),t.src!==void 0&&(e.src=c(t.src,n)),t.alt!==void 0&&(e.alt=c(t.alt,n)),t.placeholder!==void 0&&(e.placeholder=c(t.placeholder,n)),t.submitLabel!==void 0&&(e.submitLabel=c(t.submitLabel,n)),t.cond!==void 0&&(e.cond=u(t.cond,n)),t.list!==void 0&&(e.list=u(t.list,n)),t.arg!==void 0&&(e.arg=u(t.arg,n)),t.action!==void 0&&(e.action=d(t.action,n)),t.submit!==void 0&&(e.submit=d(t.submit,n)),t.bind!==void 0&&(e.bind=d(t.bind,n)),t.to!==void 0&&(e.to=typeof t.to=="string"?t.to:g(t.to,n)),t.inputs!==void 0&&(e.inputs=s(t.inputs,n)),t.on!==void 0&&(e.on=s(t.on,n)),e}function c(t,n){if(typeof t=="string")return t;if("$param"in t){const e=n[t.$param];return typeof e=="number"?String(e):e}return g(t,n)}function u(t,n){return t.kind===f.Ref?{kind:f.Ref,name:d(t.name,n)}:t.kind===f.Bin?{...t,left:u(t.left,n),right:u(t.right,n)}:t.kind===f.Un?{...t,operand:u(t.operand,n)}:t.kind===f.Tern?{...t,cond:u(t.cond,n),then:u(t.then,n),else:u(t.else,n)}:t.kind===f.Call?{...t,args:t.args.map(e=>u(e,n))}:t}function g(t,n){return{kind:f.Interp,parts:t.parts.map(e=>typeof e=="string"?e:u(e,n))}}function s(t,n){const e={};for(const[r,i]of Object.entries(t))e[r]=y(i,n);return e}function y(t,n){return typeof t=="string"?t.startsWith("$")?d(t,n):t:typeof t=="number"?t:n[t.$param]}function d(t,n){if(!t.startsWith("$"))return t;const e=t.slice(1).split(".")[0],r=t.slice(1+e.length),i=n[e];return(typeof i=="string"?i:e)+r}export{p as compose,N as composeDoc};
@@ -1 +1 @@
1
- function a(t){const e={};let n=0;const c=o=>{const r="n"+ ++n,s={id:r,type:o.type,props:o.props||{},children:[]};return o.loc&&(s.loc=o.loc),o.args&&(s.args=o.args),e[r]=s,s.children=(o.children||[]).map(c),r};return{rootId:c(t),nodes:e}}function i(t){const{rootId:e,nodes:n}=t.tree?a(t.tree):{rootId:void 0,nodes:{}};return{screen:t.screen,entities:t.entities,state:t.state,actions:t.actions,consts:t.consts||{},constraints:t.constraints||{},params:t.params,meta:t.meta,rootId:e,nodes:n}}export{i as toDoc};
1
+ function a(t){const s={};let n=0;const c=o=>{const r="n"+ ++n,e={id:r,type:o.type,props:o.props||{},children:[]};return o.loc&&(e.loc=o.loc),o.args&&(e.args=o.args),s[r]=e,e.children=(o.children||[]).map(c),r};return{rootId:c(t),nodes:s}}function i(t){const{rootId:s,nodes:n}=t.tree?a(t.tree):{rootId:void 0,nodes:{}};return{screen:t.screen,entities:t.entities,state:t.state,actions:t.actions,consts:t.consts||{},constraints:t.constraints||{},params:t.params,meta:t.meta,imports:t.imports,rootId:s,nodes:n}}export{i as toDoc};
@@ -1,26 +1,26 @@
1
- import{Ek as $,StOp as o,Nt as y,Mod as c}from"#engine/shared/vocab.js";const a=" ",B=t=>!!t&&typeof t=="object"&&"kind"in t&&t.kind===$.Interp,A=t=>typeof t=="object"&&"$param"in t,O=t=>/^[A-Za-z_][A-Za-z0-9_.]*$/.test(t);function s(t){switch(t.kind){case $.Lit:return j(t.value);case $.Ref:return t.name;case $.Un:return`${t.op} ${f(t.operand)}`;case $.Bin:return`${f(t.left)} ${t.op} ${f(t.right)}`;case $.Tern:return`${f(t.cond)} ? ${f(t.then)} : ${f(t.else)}`}}const f=t=>t.kind===$.Bin||t.kind===$.Tern?`(${s(t)})`:s(t),j=t=>typeof t=="string"?JSON.stringify(t):String(t);function l(t){return t===null?"null":Array.isArray(t)?`[${t.map(l).join(", ")}]`:typeof t=="object"?`{ ${Object.entries(t).map(([r,e])=>`${r}: ${l(e)}`).join(", ")} }`:j(t)}const S=t=>'"'+t.parts.map(r=>typeof r=="string"?r:`{${s(r)}}`).join("")+'"',R=t=>typeof t=="string"?t:t.parts.map(r=>typeof r=="string"?r:`{${s(r)}}`).join("");function I(t){return typeof t=="string"?JSON.stringify(t):A(t)?"$"+t.$param:S(t)}const E=t=>typeof t=="number"?String(t):typeof t=="string"?t:"$"+t.$param,h=t=>Object.entries(t).map(([r,e])=>`${r}: ${E(e)}`).join(", "),N=t=>typeof t=="string"?O(t)?t:JSON.stringify(t):`${O(t.name)?t.name:JSON.stringify(t.name)} when ${s(t.cond)}`;function m(t,r){switch(t.op){case o.Push:return`${t.target}.push(${s(t.arg)})`;case o.Set:return`${t.target}.set(${s(t.arg)})`;case o.Reset:return`${t.target}.reset()`;case o.Remove:return`${t.target}.remove(${t.param} => ${s(t.pred)})`;case o.Create:return`${t.target}.create(${s(t.arg)})`;case o.Update:return`${t.target}.update(${s(t.arg)})`;case o.Delete:return`${t.target}.delete(${s(t.arg)})`;case o.Refetch:return`${t.target}.refetch(${Object.entries(t.params).map(([e,n])=>`${e}: ${s(n)}`).join(", ")})`;case o.Request:return`${t.method.toLowerCase()} ${typeof t.url=="string"?JSON.stringify(t.url):S(t.url)}${t.body?` body ${s(t.body)}`:""}`;case o.If:{const e=t.then.map(i=>r+a+m(i,r+a)).join(`
1
+ import{Ek as $,StOp as a,Nt as j,Mod as c}from"#engine/shared/vocab.js";const o=" ",B=t=>!!t&&typeof t=="object"&&"kind"in t&&t.kind===$.Interp,A=t=>typeof t=="object"&&"$param"in t,O=t=>/^[A-Za-z_][A-Za-z0-9_.]*$/.test(t);function i(t){switch(t.kind){case $.Lit:return y(t.value);case $.Ref:return t.name;case $.Un:return`${t.op} ${f(t.operand)}`;case $.Bin:return`${f(t.left)} ${t.op} ${f(t.right)}`;case $.Tern:return`${f(t.cond)} ? ${f(t.then)} : ${f(t.else)}`;case $.Call:return`${t.fn}(${t.args.map(i).join(", ")})`}}const f=t=>t.kind===$.Bin||t.kind===$.Tern?`(${i(t)})`:i(t),y=t=>typeof t=="string"?JSON.stringify(t):String(t);function l(t){return t===null?"null":Array.isArray(t)?`[${t.map(l).join(", ")}]`:typeof t=="object"?`{ ${Object.entries(t).map(([r,e])=>`${r}: ${l(e)}`).join(", ")} }`:y(t)}const S=t=>'"'+t.parts.map(r=>typeof r=="string"?r:`{${i(r)}}`).join("")+'"',N=t=>typeof t=="string"?t:t.parts.map(r=>typeof r=="string"?r:`{${i(r)}}`).join("");function I(t){return typeof t=="string"?JSON.stringify(t):A(t)?"$"+t.$param:S(t)}const R=t=>typeof t=="number"?String(t):typeof t=="string"?t:"$"+t.$param,h=t=>Object.entries(t).map(([r,e])=>`${r}: ${R(e)}`).join(", "),E=t=>typeof t=="string"?O(t)?t:JSON.stringify(t):`${O(t.name)?t.name:JSON.stringify(t.name)} when ${i(t.cond)}`;function m(t,r){switch(t.op){case a.Push:return`${t.target}.push(${i(t.arg)})`;case a.Set:return`${t.target}.set(${i(t.arg)})`;case a.Reset:return`${t.target}.reset()`;case a.Remove:return`${t.target}.remove(${t.param} => ${i(t.pred)})`;case a.Create:return`${t.target}.create(${i(t.arg)})`;case a.Update:return`${t.target}.update(${i(t.arg)})`;case a.Delete:return`${t.target}.delete(${i(t.arg)})`;case a.Refetch:return`${t.target}.refetch(${Object.entries(t.params).map(([e,n])=>`${e}: ${i(n)}`).join(", ")})`;case a.Request:return`${t.method.toLowerCase()} ${typeof t.url=="string"?JSON.stringify(t.url):S(t.url)}${t.body?` body ${i(t.body)}`:""}`;case a.If:{const e=t.then.map(s=>r+o+m(s,r+o)).join(`
2
2
  `),n=t.else?` else {
3
- ${t.else.map(i=>r+a+m(i,r+a)).join(`
3
+ ${t.else.map(s=>r+o+m(s,r+o)).join(`
4
4
  `)}
5
- ${r}}`:"";return`if ${s(t.cond)} {
5
+ ${r}}`:"";return`if ${i(t.cond)} {
6
6
  ${e}
7
- ${r}}${n}`}}}function d(t,r){if(t.args)return`${r}${t.type}(${h(t.args)})`;if(t.type===y.Slot)return`${r}slot`;const e=t.props||{};let n;if(t.type===y.When)n=`when ${s(e.cond)}`;else if(t.type===y.Each)n=`each ${s(e.list)} as ${e.as}`;else{n=t.type;const i=e.value??e.label??e.src??e.placeholder??e.submitLabel;i!==void 0&&(n+=` ${I(i)}`),e.to!==void 0?n+=` -> ${R(e.to)}`:e.action&&(n+=` -> ${e.action}${e.arg!==void 0?`(${s(e.arg)})`:""}`),e.data&&(n+=` @${e.data}`),e.bind&&(n+=` bind ${e.bind.includes(".")?e.bind:"@"+e.bind}`),e.where&&(n+=` ${c.Where}(${e.where.join(", ")})`),e.columns&&(n+=` ${c.Columns}(${e.columns.join(", ")})`),e.style&&(n+=` ${c.Style}(${e.style.join(", ")})`),e.class&&(n+=` ${c.Class}(${e.class.map(N).join(", ")})`),e.alt!==void 0&&(n+=` ${c.Alt} ${I(e.alt)}`),e.inputs&&(n+=` ${c.Inputs}(${h(e.inputs)})`),e.on&&(n+=` ${c.On}(${h(e.on)})`),e.submit&&(n+=` ${c.Submit} ${e.submit}`)}return t.children&&t.children.length?`${r}${n} {
8
- ${t.children.map(i=>d(i,r+a)).join(`
7
+ ${r}}${n}`}}}function d(t,r){if(t.args)return`${r}${t.type}(${h(t.args)})`;if(t.type===j.Slot)return`${r}slot`;const e=t.props||{};let n;if(t.type===j.When)n=`when ${i(e.cond)}`;else if(t.type===j.Each)n=`each ${i(e.list)} as ${e.as}`;else{n=t.type;const s=e.value??e.label??e.src??e.placeholder??e.submitLabel;s!==void 0&&(n+=` ${I(s)}`),e.to!==void 0?n+=` -> ${N(e.to)}`:e.action&&(n+=` -> ${e.action}${e.arg!==void 0?`(${i(e.arg)})`:""}`),e.data&&(n+=` @${e.data}`),e.bind&&(n+=` bind ${e.bind.includes(".")?e.bind:"@"+e.bind}`),e.where&&(n+=` ${c.Where}(${e.where.join(", ")})`),e.columns&&(n+=` ${c.Columns}(${e.columns.join(", ")})`),e.style&&(n+=` ${c.Style}(${e.style.join(", ")})`),e.class&&(n+=` ${c.Class}(${e.class.map(E).join(", ")})`),e.alt!==void 0&&(n+=` ${c.Alt} ${I(e.alt)}`),e.inputs&&(n+=` ${c.Inputs}(${h(e.inputs)})`),e.on&&(n+=` ${c.On}(${h(e.on)})`),e.submit&&(n+=` ${c.Submit} ${e.submit}`)}return t.children&&t.children.length?`${r}${n} {
8
+ ${t.children.map(s=>d(s,r+o)).join(`
9
9
  `)}
10
- ${r}}`:`${r}${n}`}const C=(t,r,e)=>{const n=Object.entries(r).map(([i,k])=>{let g=`${i} ${k}`;const u=e?.[i];return u?.required&&(g+=" required"),u?.min!==void 0&&(g+=` min:${u.min}`),u?.max!==void 0&&(g+=` max:${u.max}`),g});return`entity ${t} { ${n.join(" ")} }`},P=(t,r)=>{const e=r.source?.startsWith("query:")?`query ${r.source.slice(6)}`:l(r.initial??null);return`${t} = ${e} : ${r.type}`},V=(t,r)=>`${`action ${t}${r.mutates.length?` mutates ${r.mutates.join(", ")}`:""}${r.input?` <- ${r.input}`:""}`} {
11
- ${r.body.map(n=>a+m(n,a)).join(`
10
+ ${r}}`:`${r}${n}`}const C=(t,r,e)=>{const n=Object.entries(r).map(([s,k])=>{let g=`${s} ${k}`;const u=e?.[s];return u?.required&&(g+=" required"),u?.min!==void 0&&(g+=` min:${u.min}`),u?.max!==void 0&&(g+=` max:${u.max}`),g});return`entity ${t} { ${n.join(" ")} }`},P=(t,r)=>{const e=r.source?.startsWith("query:")?`query ${r.source.slice(6)}`:l(r.initial??null);return`${t} = ${e} : ${r.type}`},V=(t,r)=>`${`action ${t}${r.mutates.length?` mutates ${r.mutates.join(", ")}`:""}${r.input?` <- ${r.input}`:""}`} {
11
+ ${r.body.map(n=>o+m(n,o)).join(`
12
12
  `)}
13
13
  }`,x=t=>`${t.url} -> ${t.page}${t.guard?` guard ${t.guardNeg?"not ":""}${t.guard}${t.redirect?` else ${t.redirect}`:""}`:""}`,w=(t,r)=>`part ${t}(${r.params.map(e=>`${e.name}: ${e.type}`).join(", ")}) {
14
- ${d(r.tree,a)}
14
+ ${d(r.tree,o)}
15
15
  }`,p=(t,r)=>`${t} {
16
16
  ${r}
17
- }`,b=(t,r,e)=>p(t,r.map(([n,i])=>`${a}${n}${e} ${l(i)}`).join(`
18
- `));function M(t){const r=[],e=n=>{n&&r.push(n)};if(t.consts)for(const[n,i]of Object.entries(t.consts))e(`const ${n} = ${j(i)}`);if(e(t.screen?`screen ${t.screen}`:void 0),t.meta&&e(p("meta",Object.entries(t.meta).map(([n,i])=>`${a}${n} ${JSON.stringify(i)}`).join(`
19
- `))),t.params)for(const n of t.params)e(`param ${n}`);if(t.api&&e(b("api",Object.entries(t.api),":")),t.entities)for(const[n,i]of Object.entries(t.entities))e(C(n,i,t.constraints?.[n]));if(t.state&&Object.keys(t.state).length&&e(p("state",Object.entries(t.state).map(([n,i])=>a+P(n,i)).join(`
20
- `))),t.store&&Object.keys(t.store).length&&e(p("store",Object.entries(t.store).map(([n,i])=>a+P(n,i)).join(`
21
- `))),t.gets)for(const[n,i]of Object.entries(t.gets))e(`get ${n} = ${s(i)}`);if(t.sources&&e(b("sources",Object.entries(t.sources),":")),t.mock&&e(b("mock",Object.entries(t.mock),":")),t.actions)for(const[n,i]of Object.entries(t.actions))e(V(n,i));if(t.effects)for(const n of t.effects)e(p("effect",n.map(i=>a+m(i,a)).join(`
22
- `)));if(t.parts)for(const[n,i]of Object.entries(t.parts))e(w(n,i));return t.shell&&e(p("shell",(t.shell.children||[]).map(n=>d(n,a)).join(`
23
- `))),t.tree&&e(d(t.tree,"")),t.routes&&e(p("routes",t.routes.map(n=>a+x(n)).join(`
17
+ }`,b=(t,r,e)=>p(t,r.map(([n,s])=>`${o}${n}${e} ${l(s)}`).join(`
18
+ `));function M(t){const r=[],e=n=>{n&&r.push(n)};if(t.consts)for(const[n,s]of Object.entries(t.consts))e(`const ${n} = ${y(s)}`);if(e(t.screen?`screen ${t.screen}`:void 0),t.imports)for(const n of t.imports)e(`use ${n.names.join(", ")} from ${JSON.stringify(n.from)}`);if(t.meta&&e(p("meta",Object.entries(t.meta).map(([n,s])=>`${o}${n} ${JSON.stringify(s)}`).join(`
19
+ `))),t.params)for(const n of t.params)e(`param ${n}`);if(t.api&&e(b("api",Object.entries(t.api),":")),t.entities)for(const[n,s]of Object.entries(t.entities))e(C(n,s,t.constraints?.[n]));if(t.state&&Object.keys(t.state).length&&e(p("state",Object.entries(t.state).map(([n,s])=>o+P(n,s)).join(`
20
+ `))),t.store&&Object.keys(t.store).length&&e(p("store",Object.entries(t.store).map(([n,s])=>o+P(n,s)).join(`
21
+ `))),t.gets)for(const[n,s]of Object.entries(t.gets))e(`get ${n} = ${i(s)}`);if(t.sources&&e(b("sources",Object.entries(t.sources),":")),t.mock&&e(b("mock",Object.entries(t.mock),":")),t.actions)for(const[n,s]of Object.entries(t.actions))e(V(n,s));if(t.effects)for(const n of t.effects)e(p("effect",n.map(s=>o+m(s,o)).join(`
22
+ `)));if(t.parts)for(const[n,s]of Object.entries(t.parts))e(w(n,s));return t.shell&&e(p("shell",(t.shell.children||[]).map(n=>d(n,o)).join(`
23
+ `))),t.tree&&e(d(t.tree,"")),t.routes&&e(p("routes",t.routes.map(n=>o+x(n)).join(`
24
24
  `))),r.join(`
25
25
 
26
26
  `)+`
@@ -1 +1 @@
1
- import{resolveToken as F,SUGGESTED as I,defaultTheme as q,isKnownTokenShape as _}from"#engine/style/tokens.js";import{diag as a,closest as p}from"#engine/shared/diagnostics.js";import{PRIMITIVE_NAMES as C,ACTION_OPS as K,PRIMITIVES as U}from"#engine/lang/manifest.js";import{Nt as g,Ek as y,StOp as v}from"#engine/shared/vocab.js";const T=new Set([...C,g.Shell]),G=["bind","data"],M=new Set(K),R=["text","number","bool","uuid","email","string"];function d(i,c=[]){return i.kind===y.Ref?c.push(i.name):i.kind===y.Un?d(i.operand,c):i.kind===y.Bin?(d(i.left,c),d(i.right,c)):i.kind===y.Tern&&(d(i.cond,c),d(i.then,c),d(i.else,c)),c}function H(i,c={}){const r=[],h=new Set(Object.keys(i.state||{})),P=new Set(c.stores||[]),V=new Set(Object.keys(i.consts||{})),x=new Set(i.params||[]),k=new Set(Object.keys(i.actions||{})),A=i.nodes||{},w=new Map;for(const[s,n]of Object.entries(i.state||{})){if(!n.source?.startsWith("query:"))continue;const t=new Set(["loading","error","data"]),e=i.entities?.[n.type];if(e){t.add("id");for(const o of Object.keys(e))t.add(o)}w.set(s,t)}const b=new Map;for(const[s,n]of Object.entries(c.storeMembers||{}))b.set(s,new Set(n));const $=(s,n,t)=>{const e=w.get(s);if(e){e.has(n)||r.push(a("unknown-member",`"${n}" is not a member of query "${s}"`,{loc:t.loc,suggestion:p(n,[...e])}));return}const o=b.get(s);o&&!o.has(n)&&r.push(a("unknown-member",`"${n}" is not a member of store "${s}"`,{loc:t.loc,suggestion:p(n,[...o])}))},O=Object.keys(i.entities||{});for(const[s,n]of Object.entries(i.state||{})){const t=n.type;if(t==="list")r.push(a("untyped-list",`state "${s}" is an untyped "list" \u2014 declare the element type, e.g. list<uuid> or list<User>`,{loc:n.loc,suggestion:"list<uuid>"}));else if(t.startsWith("list<")){const e=t.slice(5,-1);!R.includes(e)&&!O.includes(e)&&r.push(a("unknown-type",`list element "${e}" is not a known entity or scalar type`,{loc:n.loc,suggestion:p(e,[...O,...R])}))}}const W=(s,n)=>{if(typeof s=="string"&&s.startsWith("@")){const t=s.slice(1).split(".")[0];if(!h.has(t)){const e=p(t,[...h]);r.push(a("unknown-ref",`"@${t}" is not a declared state`,{loc:n.loc,suggestion:e?"@"+e:null}))}}},N=(s,n)=>{if(!(!s||s.startsWith("$"))){if(s.includes(".")){const t=s.indexOf(".");$(s.slice(0,t),s.slice(t+1).split(".")[0],n);return}k.has(s)||r.push(a("unknown-action",`"${s}" is not a declared action`,{loc:n.loc,suggestion:p(s,[...k])}))}},S=(s,n,t)=>{for(const e of d(s)){const o=e.indexOf("."),f=o===-1?e:e.slice(0,o);if(!(t.has(f)||h.has(f)||P.has(f)||V.has(f)||x.has(f)||k.has(f))){r.push(a("unknown-ref",`"${f}" is not a known state or item variable here`,{loc:n.loc,suggestion:p(f,[...h,...t])}));continue}o!==-1&&$(f,e.slice(o+1).split(".")[0],n)}},j=new Set,E=(s,n)=>{const t=A[s];if(!t){r.push(a("missing-node",`node ${s} does not exist`));return}if(j.has(s)){r.push(a("dup-node",`${s} is referenced twice`,{loc:t.loc}));return}if(j.add(s),!T.has(t.type))t.args?r.push(a("unknown-part",`"${t.type}" is not a known part`,{loc:t.loc,suggestion:p(t.type,c.parts||[])})):r.push(a("unknown-type",`"${t.type}" is not a known primitive`,{loc:t.loc,suggestion:p(t.type,[...T])}));else{const l=U[t.type],m=l?l.props:{};for(const[u,D]of Object.entries(m))!D.endsWith("?")&&!(u in(t.props||{}))&&r.push(a("missing-prop",`${t.type} is missing the required "${u}"`,{loc:t.loc}))}const e=t.props||{};for(const l of G)l in e&&W(e[l],t);if(e.action&&N(e.action,t),e.submit&&N(e.submit,t),Array.isArray(e.style)){const l=c.theme||q,m=Object.keys(l.space||{}).length>0;for(const u of e.style)_(u)?m&&F(u,l)===null&&r.push(a("unknown-token",`"${u}": that step isn't in your theme scale`,{loc:t.loc,suggestion:p(u,I)})):r.push(a("unknown-token",`"${u}" is not an accepted style token`,{loc:t.loc,suggestion:p(u,I)}))}t.type===g.When&&e.cond&&S(e.cond,t,n),t.type===g.Each&&e.list&&S(e.list,t,n);const o=[];(t.type===g.Text||t.type===g.Title||t.type===g.Span)&&e.value&&o.push(e.value),t.type===g.Image&&(e.src&&o.push(e.src),e.alt&&o.push(e.alt)),t.type===g.Link&&e.to&&o.push(e.to),e.label&&o.push(e.label);for(const l of o)if(typeof l=="object"&&"kind"in l&&l.kind===y.Interp)for(const m of l.parts)typeof m!="string"&&S(m,t,n);const f=t.type===g.Each&&e.as?new Set([...n,e.as]):n;for(const l of t.children||[])E(l,f)};if(i.rootId?E(i.rootId,new Set):c.kind!=="store"&&r.push(a("no-root","the doc is missing a rootId")),c.kind==="store")for(const[s,n]of Object.entries(i.gets||{}))for(const t of d(n)){const e=t.split(".")[0];h.has(e)||r.push(a("unknown-ref",`get "${s}": "${e}" is not a state of this store`,{suggestion:p(e,[...h])}))}for(const[s,n]of Object.entries(i.actions||{})){const t=new Set(n.mutates||[]),e=o=>{if(o.op===v.If){for(const f of o.then||[])e(f);for(const f of o.else||[])e(f);return}M.has(o.op)||r.push(a("unknown-op",`action "${s}" uses unknown op "${o.op}"`,{suggestion:p(o.op,[...M])})),"target"in o&&o.target&&!t.has(o.target)&&r.push(a("undeclared-mutation",`action "${s}" mutates "${o.target}" but only declares mutates(${[...t].join(", ")||"\u2205"})`,{suggestion:p(o.target,[...t])}))};for(const o of n.body||[])e(o)}return{ok:r.length===0,diagnostics:r}}export{H as validate};
1
+ import{resolveToken as K,SUGGESTED as P,defaultTheme as L,isKnownTokenShape as U}from"#engine/style/tokens.js";import{diag as f,closest as p}from"#engine/shared/diagnostics.js";import{PRIMITIVE_NAMES as B,ACTION_OPS as G,PRIMITIVES as Y}from"#engine/lang/manifest.js";import{Nt as d,Ek as u,StOp as z}from"#engine/shared/vocab.js";const V=new Set([...B,d.Shell]),H=["bind","data"],A=new Set(G),C=["text","number","bool","uuid","email","string"];function h(s,r=[]){if(s.kind===u.Ref)r.push(s.name);else if(s.kind===u.Un)h(s.operand,r);else if(s.kind===u.Bin)h(s.left,r),h(s.right,r);else if(s.kind===u.Tern)h(s.cond,r),h(s.then,r),h(s.else,r);else if(s.kind===u.Call)for(const a of s.args)h(a,r);return r}function m(s,r=[]){if(s.kind===u.Call){r.push(s.fn);for(const a of s.args)m(a,r)}else s.kind===u.Un?m(s.operand,r):s.kind===u.Bin?(m(s.left,r),m(s.right,r)):s.kind===u.Tern&&(m(s.cond,r),m(s.then,r),m(s.else,r));return r}function tt(s,r={}){const a=[],k=new Set(Object.keys(s.state||{})),W=new Set(r.stores||[]),D=new Set(Object.keys(s.consts||{})),F=new Set(s.params||[]),S=new Set(Object.keys(s.actions||{})),$=n=>/^(svelte|react):/.test(n),O=new Set((s.imports||[]).filter(n=>!$(n.from)).flatMap(n=>n.names)),N=new Set((s.imports||[]).filter(n=>$(n.from)).flatMap(n=>n.names)),q=s.nodes||{},w=(n,o)=>{for(const t of m(n))O.has(t)||a.push(f("unknown-function",`"${t}" is not a use'd function`,{loc:o,suggestion:p(t,[...O])}))},E=new Map;for(const[n,o]of Object.entries(s.state||{})){if(!o.source?.startsWith("query:"))continue;const t=new Set(["loading","error","data"]),e=s.entities?.[o.type];if(e){t.add("id");for(const i of Object.keys(e))t.add(i)}E.set(n,t)}const j=new Map;for(const[n,o]of Object.entries(r.storeMembers||{}))j.set(n,new Set(o));const I=(n,o,t)=>{const e=E.get(n);if(e){e.has(o)||a.push(f("unknown-member",`"${o}" is not a member of query "${n}"`,{loc:t.loc,suggestion:p(o,[...e])}));return}const i=j.get(n);i&&!i.has(o)&&a.push(f("unknown-member",`"${o}" is not a member of store "${n}"`,{loc:t.loc,suggestion:p(o,[...i])}))},M=Object.keys(s.entities||{});for(const[n,o]of Object.entries(s.state||{})){const t=o.type;if(t==="list")a.push(f("untyped-list",`state "${n}" is an untyped "list" \u2014 declare the element type, e.g. list<uuid> or list<User>`,{loc:o.loc,suggestion:"list<uuid>"}));else if(t.startsWith("list<")){const e=t.slice(5,-1);!C.includes(e)&&!M.includes(e)&&a.push(f("unknown-type",`list element "${e}" is not a known entity or scalar type`,{loc:o.loc,suggestion:p(e,[...M,...C])}))}}const _=(n,o)=>{if(typeof n=="string"&&n.startsWith("@")){const t=n.slice(1).split(".")[0];if(!k.has(t)){const e=p(t,[...k]);a.push(f("unknown-ref",`"@${t}" is not a declared state`,{loc:o.loc,suggestion:e?"@"+e:null}))}}},T=(n,o)=>{if(!(!n||n.startsWith("$"))){if(n.includes(".")){const t=n.indexOf(".");I(n.slice(0,t),n.slice(t+1).split(".")[0],o);return}S.has(n)||a.push(f("unknown-action",`"${n}" is not a declared action`,{loc:o.loc,suggestion:p(n,[...S])}))}},b=(n,o,t)=>{w(n,o.loc);for(const e of h(n)){const i=e.indexOf("."),l=i===-1?e:e.slice(0,i);if(!(t.has(l)||k.has(l)||W.has(l)||D.has(l)||F.has(l)||S.has(l))){a.push(f("unknown-ref",`"${l}" is not a known state or item variable here`,{loc:o.loc,suggestion:p(l,[...k,...t])}));continue}i!==-1&&I(l,e.slice(i+1).split(".")[0],o)}},x=new Set,R=(n,o)=>{const t=q[n];if(!t){a.push(f("missing-node",`node ${n} does not exist`));return}if(x.has(n)){a.push(f("dup-node",`${n} is referenced twice`,{loc:t.loc}));return}if(x.add(n),!N.has(t.type))if(!V.has(t.type))t.args?a.push(f("unknown-part",`"${t.type}" is not a known part`,{loc:t.loc,suggestion:p(t.type,[...r.parts||[],...N])})):a.push(f("unknown-type",`"${t.type}" is not a known primitive`,{loc:t.loc,suggestion:p(t.type,[...V])}));else{const c=Y[t.type],y=c?c.props:{};for(const[g,v]of Object.entries(y))!v.endsWith("?")&&!(g in(t.props||{}))&&a.push(f("missing-prop",`${t.type} is missing the required "${g}"`,{loc:t.loc}))}const e=t.props||{};for(const c of H)c in e&&_(e[c],t);if(e.action&&T(e.action,t),e.submit&&T(e.submit,t),Array.isArray(e.style)){const c=r.theme||L,y=Object.keys(c.space||{}).length>0;for(const g of e.style)U(g)?y&&K(g,c)===null&&a.push(f("unknown-token",`"${g}": that step isn't in your theme scale`,{loc:t.loc,suggestion:p(g,P)})):a.push(f("unknown-token",`"${g}" is not an accepted style token`,{loc:t.loc,suggestion:p(g,P)}))}t.type===d.When&&e.cond&&b(e.cond,t,o),t.type===d.Each&&e.list&&b(e.list,t,o);const i=[];(t.type===d.Text||t.type===d.Title||t.type===d.Span)&&e.value&&i.push(e.value),t.type===d.Image&&(e.src&&i.push(e.src),e.alt&&i.push(e.alt)),t.type===d.Link&&e.to&&i.push(e.to),e.label&&i.push(e.label);for(const c of i)if(typeof c=="object"&&"kind"in c&&c.kind===u.Interp)for(const y of c.parts)typeof y!="string"&&b(y,t,o);const l=t.type===d.Each&&e.as?new Set([...o,e.as]):o;for(const c of t.children||[])R(c,l)};if(s.rootId?R(s.rootId,new Set):r.kind!=="store"&&a.push(f("no-root","the doc is missing a rootId")),r.kind==="store")for(const[n,o]of Object.entries(s.gets||{})){w(o);for(const t of h(o)){const e=t.split(".")[0];k.has(e)||a.push(f("unknown-ref",`get "${n}": "${e}" is not a state of this store`,{suggestion:p(e,[...k])}))}}for(const[n,o]of Object.entries(s.actions||{})){const t=new Set(o.mutates||[]),e=i=>{if(i.op===z.If){w(i.cond);for(const l of i.then||[])e(l);for(const l of i.else||[])e(l);return}A.has(i.op)||a.push(f("unknown-op",`action "${n}" uses unknown op "${i.op}"`,{suggestion:p(i.op,[...A])})),"target"in i&&i.target&&!t.has(i.target)&&a.push(f("undeclared-mutation",`action "${n}" mutates "${i.target}" but only declares mutates(${[...t].join(", ")||"\u2205"})`,{suggestion:p(i.target,[...t])})),"arg"in i&&i.arg&&w(i.arg)};for(const i of o.body||[])e(i)}return{ok:a.length===0,diagnostics:a}}export{tt as validate};
@@ -1,2 +1,2 @@
1
- import{ParseError as l}from"#engine/shared/diagnostics.js";import{tokenize as d}from"#engine/lang/lexer.js";import{Tk as n,Pn as r,Kw as o,BOp as s,UOp as f,Ek as a}from"#engine/shared/vocab.js";const k=[[n.Eq,void 0,s.Eq],[n.Neq,void 0,s.Neq],[n.Lte,void 0,s.Lte],[n.Gte,void 0,s.Gte],[n.Punct,r.Lt,s.Lt],[n.Punct,r.Gt,s.Gt],[n.Ident,o.Contains,s.Contains]],p=new Map([[o.True,!0],[o.False,!1],[o.Null,null]]);class c{toks;pos=0;lineStarts=[0];constructor(t){this.toks=d(t);for(let e=0;e<t.length;e++)t[e]===`
2
- `&&this.lineStarts.push(e+1)}peek(){return this.toks[this.pos]}at(t,e){const i=this.peek();return i.t===t&&(e===void 0||i.v===e)}next(){return this.toks[this.pos++]}eat(t,e){if(!this.at(t,e)){const i=this.peek();throw new l(`expected ${t}${e?' "'+e+'"':""}, got ${i.t} "${i.v}"`,this.locOf(i.pos))}return this.next()}locOf(t){let e=0,i=this.lineStarts.length-1;for(;e<i;){const h=e+i+1>>1;this.lineStarts[h]<=t?e=h:i=h-1}return{line:e+1,col:t-this.lineStarts[e]+1}}parseExpr(){return this.ternary()}ternary(){const t=this.or();if(!this.at(n.Punct,r.Question))return t;this.next();const e=this.ternary();return this.eat(n.Punct,r.Colon),{kind:a.Tern,cond:t,then:e,else:this.ternary()}}or(){let t=this.and();for(;this.at(n.Ident,o.Or);)this.next(),t={kind:a.Bin,op:s.Or,left:t,right:this.and()};return t}and(){let t=this.cmp();for(;this.at(n.Ident,o.And);)this.next(),t={kind:a.Bin,op:s.And,left:t,right:this.cmp()};return t}comparison(){for(const[t,e,i]of k)if(this.at(t,e))return i;return null}cmp(){let t=this.add();for(let e=this.comparison();e;e=this.comparison())this.next(),t={kind:a.Bin,op:e,left:t,right:this.add()};return t}add(){let t=this.mul();for(;this.at(n.Punct,r.Plus)||this.at(n.Punct,r.Dash);){const e=this.peek().v===r.Plus?s.Add:s.Sub;this.next(),t={kind:a.Bin,op:e,left:t,right:this.mul()}}return t}mul(){let t=this.unary();for(;this.at(n.Punct,r.Star)||this.at(n.Punct,r.Slash);){const e=this.peek().v===r.Star?s.Mul:s.Div;this.next(),t={kind:a.Bin,op:e,left:t,right:this.unary()}}return t}unary(){return this.at(n.Ident,o.Not)?(this.next(),{kind:a.Un,op:f.Not,operand:this.unary()}):this.primary()}primary(){if(this.at(n.Punct,r.ParenL)){this.next();const i=this.ternary();return this.eat(n.Punct,r.ParenR),i}if(this.at(n.String))return{kind:a.Lit,value:this.next().v};if(this.at(n.Number))return{kind:a.Lit,value:Number(this.next().v)};let t=this.at(n.Param)?"$"+this.next().v:this.eat(n.Ident).v;const e=p.get(t);if(e!==void 0)return{kind:a.Lit,value:e};for(;this.at(n.Punct,r.Dot);)this.next(),t+="."+this.eat(n.Ident).v;return{kind:a.Ref,name:t}}parseInterpolation(t){if(!t.includes("{"))return t;const e=[];let i=0;for(;i<t.length;){const h=t.indexOf("{",i);if(h<0){e.push(t.slice(i));break}h>i&&e.push(t.slice(i,h));const u=t.indexOf("}",h);if(u<0){e.push(t.slice(h));break}e.push(new c(t.slice(h+1,u)).parseExpr()),i=u+1}return{kind:a.Interp,parts:e}}parseScalar(){if(this.at(n.String))return this.next().v;if(this.at(n.Number))return Number(this.next().v);const t=this.eat(n.Ident).v,e=p.get(t);return e!==void 0?e:t}parseValue(){return this.at(n.Punct,r.BrackL)?this.parseArray():this.at(n.Punct,r.BraceL)?this.parseObject():this.parseScalar()}parseArray(){this.eat(n.Punct,r.BrackL);const t=[];for(;!this.at(n.Punct,r.BrackR);)t.push(this.parseValue()),this.at(n.Punct,r.Comma)&&this.next();return this.eat(n.Punct,r.BrackR),t}parseObject(){this.eat(n.Punct,r.BraceL);const t={};for(;!this.at(n.Punct,r.BraceR);){const e=this.at(n.String)?this.next().v:this.eat(n.Ident).v;this.eat(n.Punct,r.Colon),t[e]=this.parseValue(),this.at(n.Punct,r.Comma)&&this.next()}return this.eat(n.Punct,r.BraceR),t}}export{c as Grammar};
1
+ import{ParseError as l}from"#engine/shared/diagnostics.js";import{tokenize as d}from"#engine/lang/lexer.js";import{Tk as e,Pn as r,Kw as o,BOp as s,UOp as f,Ek as a}from"#engine/shared/vocab.js";const k=[[e.Eq,void 0,s.Eq],[e.Neq,void 0,s.Neq],[e.Lte,void 0,s.Lte],[e.Gte,void 0,s.Gte],[e.Punct,r.Lt,s.Lt],[e.Punct,r.Gt,s.Gt],[e.Ident,o.Contains,s.Contains]],p=new Map([[o.True,!0],[o.False,!1],[o.Null,null]]);class c{toks;pos=0;lineStarts=[0];constructor(t){this.toks=d(t);for(let n=0;n<t.length;n++)t[n]===`
2
+ `&&this.lineStarts.push(n+1)}peek(){return this.toks[this.pos]}at(t,n){const i=this.peek();return i.t===t&&(n===void 0||i.v===n)}next(){return this.toks[this.pos++]}eat(t,n){if(!this.at(t,n)){const i=this.peek();throw new l(`expected ${t}${n?' "'+n+'"':""}, got ${i.t} "${i.v}"`,this.locOf(i.pos))}return this.next()}locOf(t){let n=0,i=this.lineStarts.length-1;for(;n<i;){const h=n+i+1>>1;this.lineStarts[h]<=t?n=h:i=h-1}return{line:n+1,col:t-this.lineStarts[n]+1}}parseExpr(){return this.ternary()}ternary(){const t=this.or();if(!this.at(e.Punct,r.Question))return t;this.next();const n=this.ternary();return this.eat(e.Punct,r.Colon),{kind:a.Tern,cond:t,then:n,else:this.ternary()}}or(){let t=this.and();for(;this.at(e.Ident,o.Or);)this.next(),t={kind:a.Bin,op:s.Or,left:t,right:this.and()};return t}and(){let t=this.cmp();for(;this.at(e.Ident,o.And);)this.next(),t={kind:a.Bin,op:s.And,left:t,right:this.cmp()};return t}comparison(){for(const[t,n,i]of k)if(this.at(t,n))return i;return null}cmp(){let t=this.add();for(let n=this.comparison();n;n=this.comparison())this.next(),t={kind:a.Bin,op:n,left:t,right:this.add()};return t}add(){let t=this.mul();for(;this.at(e.Punct,r.Plus)||this.at(e.Punct,r.Dash);){const n=this.peek().v===r.Plus?s.Add:s.Sub;this.next(),t={kind:a.Bin,op:n,left:t,right:this.mul()}}return t}mul(){let t=this.unary();for(;this.at(e.Punct,r.Star)||this.at(e.Punct,r.Slash);){const n=this.peek().v===r.Star?s.Mul:s.Div;this.next(),t={kind:a.Bin,op:n,left:t,right:this.unary()}}return t}unary(){return this.at(e.Ident,o.Not)?(this.next(),{kind:a.Un,op:f.Not,operand:this.unary()}):this.primary()}primary(){if(this.at(e.Punct,r.ParenL)){this.next();const i=this.ternary();return this.eat(e.Punct,r.ParenR),i}if(this.at(e.String))return{kind:a.Lit,value:this.next().v};if(this.at(e.Number))return{kind:a.Lit,value:Number(this.next().v)};let t=this.at(e.Param)?"$"+this.next().v:this.eat(e.Ident).v;const n=p.get(t);if(n!==void 0)return{kind:a.Lit,value:n};for(;this.at(e.Punct,r.Dot);)this.next(),t+="."+this.eat(e.Ident).v;if(this.at(e.Punct,r.ParenL)){this.next();const i=[];for(;!this.at(e.Punct,r.ParenR);)i.push(this.ternary()),this.at(e.Punct,r.Comma)&&this.next();return this.eat(e.Punct,r.ParenR),{kind:a.Call,fn:t,args:i}}return{kind:a.Ref,name:t}}parseInterpolation(t){if(!t.includes("{"))return t;const n=[];let i=0;for(;i<t.length;){const h=t.indexOf("{",i);if(h<0){n.push(t.slice(i));break}h>i&&n.push(t.slice(i,h));const u=t.indexOf("}",h);if(u<0){n.push(t.slice(h));break}n.push(new c(t.slice(h+1,u)).parseExpr()),i=u+1}return{kind:a.Interp,parts:n}}parseScalar(){if(this.at(e.String))return this.next().v;if(this.at(e.Number))return Number(this.next().v);const t=this.eat(e.Ident).v,n=p.get(t);return n!==void 0?n:t}parseValue(){return this.at(e.Punct,r.BrackL)?this.parseArray():this.at(e.Punct,r.BraceL)?this.parseObject():this.parseScalar()}parseArray(){this.eat(e.Punct,r.BrackL);const t=[];for(;!this.at(e.Punct,r.BrackR);)t.push(this.parseValue()),this.at(e.Punct,r.Comma)&&this.next();return this.eat(e.Punct,r.BrackR),t}parseObject(){this.eat(e.Punct,r.BraceL);const t={};for(;!this.at(e.Punct,r.BraceR);){const n=this.at(e.String)?this.next().v:this.eat(e.Ident).v;this.eat(e.Punct,r.Colon),t[n]=this.parseValue(),this.at(e.Punct,r.Comma)&&this.next()}return this.eat(e.Punct,r.BraceR),t}}export{c as Grammar};
@@ -12,4 +12,4 @@ import{SUGGESTED as e,resolveToken as t}from"#engine/style/tokens.js";const a={S
12
12
  $0
13
13
  }`},Text:{string:"value",props:{value:"text",style:"tokens?"},children:!1,interp:!0,doc:'Paragraph text (<p>). Interpolates state reactively: `Text "Hi, {user.name}"`.',snippet:'Text "$1"'},Title:{string:"value",props:{value:"text",style:"tokens?"},children:!1,interp:!0,doc:'Heading. Level via keyword: `Title "Hi" h2` \u2192 <h2> (h1\u2026h6; default h1). Prefer one h1 per page. Interpolates state.',snippet:'Title "$1"'},Span:{string:"value",props:{value:"text",style:"tokens?"},children:!1,interp:!0,doc:'Inline text (<span>). Interpolates state: `Span "{cart.total}"`.',snippet:'Span "$1"'},Image:{string:"src",props:{src:"text",alt:"text",style:"tokens?"},children:!1,interp:!0,doc:'Image (<img>). `alt` is required (a11y/SEO): `Image "{p.image}" alt "{p.title}"`. Use alt "" for decorative images.',snippet:'Image "{${1:item.image}}" alt "${2:description}"'},SearchField:{string:"placeholder",props:{bind:"state",placeholder:"text?"},children:!1,doc:"Search input two-way bound to a text state.",snippet:'SearchField bind @${1:search} "${2:Search by name}"'},DataTable:{props:{data:"state",where:"clauses?",columns:"fields",style:"tokens?"},children:!0,doc:"Reactive table over a list/query. Static `where` filters are pushed to the query; dynamic ones stay reactive.",snippet:"DataTable @${1:items}\n columns(${2:name})"},RowAction:{string:"label",props:{label:"text",action:"action",arg:"expr?"},children:!1,doc:'A button rendered in each DataTable row: `RowAction "Delete" -> deleteItem(row.id)`.',snippet:'RowAction "${1:Delete}" -> ${2:action}(row.id)'},Button:{string:"label",props:{label:"text?",action:"action?",arg:"expr?",style:"tokens?"},children:!0,interp:!0,doc:'Clickable button \u2192 runs an action. Label interpolates (`Button "{x}"`) OR use `{ }` children for a clickable card. `-> action(arg)`; arg may be a ref or a literal.',snippet:'Button "${1:label}" -> ${2:action}($3)'},Form:{string:"submitLabel",props:{bind:"state",submit:"action",submitLabel:"text?"},children:!1,doc:"Auto-form: one field per entity field, two-way bound to a draft state.",snippet:'Form bind @${1:draft} submit ${2:createItem} "${3:Save}"'},Link:{string:"label",props:{label:"text?",to:"route",style:"tokens?"},children:!0,interp:!0,doc:'Navigation link: `Link "Catalog" -> /catalog`. Label interpolates, OR use `{ }` children for a clickable card that navigates. Client-side (no full reload).',snippet:'Link "${1:label}" -> /${2:route}'},slot:{props:{},children:!1,doc:"The outlet in a `shell { }` where the active route\u2019s page mounts.",snippet:"slot"},When:{props:{cond:"expr"},children:!0,control:!0,doc:"Conditional render: `when <expr> { ... }`. Mounts/unmounts reactively.",snippet:`when \${1:cond} {
14
14
  $0
15
- }`},Each:{props:{list:"expr",as:"ident"},children:!0,control:!0,doc:"List render: `each <list> as <item> { ... }`. The item is a scope variable in the template.",snippet:"each ${1:items} as ${2:item} {\n $0\n}"},Custom:{props:{component:"name",inputs:"map?",on:"map?"},children:!1,doc:"Escape hatch (\xA77): mount a host component from `src/components/<Name>.js`. Opaque to the IR; connected via inputs/on.",snippet:"Custom ${1:Name} inputs(${2:prop}: ${3}) on(${4:event}: ${5:action})"}},r=["bind","submit","where","columns","style","class","alt","inputs","on"],o={bind:"Two-way bind to a @state, e.g. `bind @search`.",submit:"Action to run on form submit, e.g. `submit createUser`.",where:"Filter clauses: `where(role == admin, name contains @q)`.",columns:"Columns to show: `columns(name, email, role)`.",style:"Layout & typography tokens (Muten builds, doesn\u2019t skin): `style(row, gap.md, text.lg)`.",class:'Raw CSS class(es) for LOOK \u2014 your CSS or a third-party like Tailwind: `class(card)` or `class("flex gap-4")`. Muten stays agnostic about appearance.',alt:'Required accessible/SEO text for an Image: `alt "{p.title}"`. Use "" for decorative images.',inputs:"Custom component inputs: `inputs(data: @sales)`.",on:"Custom component events wired to actions: `on(select: pick)`."},n=["screen","entity","state","store","const","theme","get","effect","action","mutates","mock","sources","api","meta","routes","shell","guard","else","part","param","query","post","put","delete","body","if","when","each","as","and","or","not","contains"],i={screen:"Declares the screen name: `screen users_dashboard`.",entity:"Declares a data shape + validation: `entity User { name text required email email required password text min:8 }` (implicit uuid id). Constraints: `required`, `min:N`, `max:N`.",state:'Declares reactive state: `state { search = "" : text users = query listUsers : list<User> }`.',store:"App-GLOBAL reactive state (shared across pages, no prop drilling): `store { cart = [] : list<number> }`. Referenced by name like local state.",const:"A compile-time IMMUTABLE scalar, inlined (never reactive): `const TAX = 0.21`. Scalars only \u2014 structured config uses a block (e.g. theme).",theme:'The project theme block (theme.muten): `theme { space { md "16px" } breakpoints { md "768px" } }`. Supplies the token SCALE; the engine owns only the vocabulary. The reset/base CSS lives in your stylesheet.',get:"A `.store` derived/memoized value (getter): `get total = items.length`. Read as `domain.total`, recomputes when deps change.",effect:"A `.store` reactive side-effect (Angular-style): `effect { ... }`. Re-runs automatically when the store state it reads changes.",action:"Declares a mutation: `action delete mutates users <- id { users.remove(u => u.id == id) }`.",mutates:"Lists the state an action may mutate \u2014 the linter enforces it.",mock:'Inline mock data for queries: `mock { listUsers: [ { name: "Ana", role: admin } ] }`.',sources:'Real data sources for queries: `sources { listChars: { url: "https://api...", at: "results" } }`.',api:'App-wide backend config in app.muten: `api { base: "https://\u2026" headers: { \u2026 } }`. A relative `sources` url is joined to `base`; headers merge (the source wins).',meta:'Page <head> metadata: `meta { title "\u2026" description "\u2026" }` \u2192 `<title>` + `<meta>` tags (og:* auto-derived). Applied on navigation.',post:'Explicit non-REST request in an action: `post "shop:/orders" body item` (escape hatch when CRUD ops do not fit).',put:'Explicit non-REST request in an action: `put "shop:/orders/{id}" body item`.',body:'The JSON body of an explicit `post`/`put` request: `post "shop:/x" body item`.',routes:"App root (app.muten): maps URLs to pages, `routes { /url -> page }`. The single source of truth the AI reads.",shell:"Persistent app chrome in app.muten: `shell { Header { \u2026 } slot Footer { \u2026 } }`. Wraps every route; `slot` is where the active Page (<main>) mounts.",guard:"Route guard in app.muten: `routes { /cart -> cart guard auth.loggedIn else /login }`. If the store boolean is false on navigation, redirect. Guest-only page: `guard not auth.loggedIn else /catalog`.",else:"The redirect target of a route `guard`: `guard auth.loggedIn else /login`.",part:"Reusable composition: `part Card(item: Item, onPick: action) { ... }`. Pass OBJECTS (`$item.field`) and ACTION callbacks (`-> $onPick(...)`). Inlined at build time.",param:"Declares a route param read from the URL: `param id` for a route `/x/:id`. Usable in interpolation/`when`/expressions like a read-only string.",query:"An async data source. The state exposes `.loading`, `.error` and `.data`.",if:"Conditional INSIDE an action body: `if <expr> { \u2026 } else { \u2026 }` \u2014 the only branching in actions (toggles, validation, add-or-remove).",when:"Conditional render: `when <expr> { ... }`.",each:"List render: `each <list> as <item> { ... }`.",as:"Names the item variable in an `each`.",and:"Logical AND.",or:"Logical OR.",not:"Logical NOT, e.g. `when not (cart.isEmpty)`.",contains:"Case-insensitive substring match: `name contains @q`."},l=["push","remove","reset","set","create","update","delete","refetch"],c={push:"Append to a list state: `users.push(draft)` (auto-fills uuid fields).",remove:"Remove matching items locally: `users.remove(u => u.id == id)`.",reset:"Reset a state to its declared initial: `draft.reset()`.",set:"Set a state value: `rating.set(v)`.",create:"POST an item to a source-backed list, then append the result: `orders.create(draft)`.",update:"PUT an item (by id) to a source-backed list, then replace it: `orders.update(order)`.",delete:"DELETE an item (by id) from a source-backed list, then drop it: `orders.delete(order)`.",refetch:"Re-run a query with N query-string params (paginate / search / filter): `products.refetch(q: term, page: n)`."},p=Object.keys(a),d=e;export{l as ACTION_OPS,c as ACTION_OP_DOCS,n as KEYWORDS,i as KEYWORD_DOCS,r as MODIFIERS,o as MODIFIER_DOCS,a as PRIMITIVES,p as PRIMITIVE_NAMES,d as TOKEN_NAMES,t as resolveToken};
15
+ }`},Each:{props:{list:"expr",as:"ident"},children:!0,control:!0,doc:"List render: `each <list> as <item> { ... }`. The item is a scope variable in the template.",snippet:"each ${1:items} as ${2:item} {\n $0\n}"},Custom:{props:{component:"name",inputs:"map?",on:"map?"},children:!1,doc:"Escape hatch (\xA77): mount a host component from `src/components/<Name>.js`. Opaque to the IR; connected via inputs/on.",snippet:"Custom ${1:Name} inputs(${2:prop}: ${3}) on(${4:event}: ${5:action})"}},r=["bind","submit","where","columns","style","class","alt","inputs","on"],o={bind:"Two-way bind to a @state, e.g. `bind @search`.",submit:"Action to run on form submit, e.g. `submit createUser`.",where:"Filter clauses: `where(role == admin, name contains @q)`.",columns:"Columns to show: `columns(name, email, role)`.",style:"Layout & typography tokens (Muten builds, doesn\u2019t skin): `style(row, gap.md, text.lg)`.",class:'Raw CSS class(es) for LOOK \u2014 your CSS or a third-party like Tailwind: `class(card)` or `class("flex gap-4")`. Muten stays agnostic about appearance.',alt:'Required accessible/SEO text for an Image: `alt "{p.title}"`. Use "" for decorative images.',inputs:"Custom component inputs: `inputs(data: @sales)`.",on:"Custom component events wired to actions: `on(select: pick)`."},n=["screen","entity","state","store","const","theme","get","effect","action","mutates","mock","sources","api","meta","routes","shell","guard","else","part","param","query","post","put","delete","body","if","when","each","as","and","or","not","contains","use"],i={screen:"Declares the screen name: `screen users_dashboard`.",entity:"Declares a data shape + validation: `entity User { name text required email email required password text min:8 }` (implicit uuid id). Constraints: `required`, `min:N`, `max:N`.",state:'Declares reactive state: `state { search = "" : text users = query listUsers : list<User> }`.',store:"App-GLOBAL reactive state (shared across pages, no prop drilling): `store { cart = [] : list<number> }`. Referenced by name like local state.",const:"A compile-time IMMUTABLE scalar, inlined (never reactive): `const TAX = 0.21`. Scalars only \u2014 structured config uses a block (e.g. theme).",theme:'The project theme block (theme.muten): `theme { space { md "16px" } breakpoints { md "768px" } }`. Supplies the token SCALE; the engine owns only the vocabulary. The reset/base CSS lives in your stylesheet.',get:"A `.store` derived/memoized value (getter): `get total = items.length`. Read as `domain.total`, recomputes when deps change.",effect:"A `.store` reactive side-effect (Angular-style): `effect { ... }`. Re-runs automatically when the store state it reads changes.",action:"Declares a mutation: `action delete mutates users <- id { users.remove(u => u.id == id) }`.",mutates:"Lists the state an action may mutate \u2014 the linter enforces it.",mock:'Inline mock data for queries: `mock { listUsers: [ { name: "Ana", role: admin } ] }`.',sources:'Real data sources for queries: `sources { listChars: { url: "https://api...", at: "results" } }`.',api:'App-wide backend config in app.muten: `api { base: "https://\u2026" headers: { \u2026 } }`. A relative `sources` url is joined to `base`; headers merge (the source wins).',meta:'Page <head> metadata: `meta { title "\u2026" description "\u2026" }` \u2192 `<title>` + `<meta>` tags (og:* auto-derived). Applied on navigation.',post:'Explicit non-REST request in an action: `post "shop:/orders" body item` (escape hatch when CRUD ops do not fit).',put:'Explicit non-REST request in an action: `put "shop:/orders/{id}" body item`.',body:'The JSON body of an explicit `post`/`put` request: `post "shop:/x" body item`.',routes:"App root (app.muten): maps URLs to pages, `routes { /url -> page }`. The single source of truth the AI reads.",shell:"Persistent app chrome in app.muten: `shell { Header { \u2026 } slot Footer { \u2026 } }`. Wraps every route; `slot` is where the active Page (<main>) mounts.",guard:"Route guard in app.muten: `routes { /cart -> cart guard auth.loggedIn else /login }`. If the store boolean is false on navigation, redirect. Guest-only page: `guard not auth.loggedIn else /catalog`.",else:"The redirect target of a route `guard`: `guard auth.loggedIn else /login`.",part:"Reusable composition: `part Card(item: Item, onPick: action) { ... }`. Pass OBJECTS (`$item.field`) and ACTION callbacks (`-> $onPick(...)`). Inlined at build time.",param:"Declares a route param read from the URL: `param id` for a route `/x/:id`. Usable in interpolation/`when`/expressions like a read-only string.",query:"An async data source. The state exposes `.loading`, `.error` and `.data`.",if:"Conditional INSIDE an action body: `if <expr> { \u2026 } else { \u2026 }` \u2014 the only branching in actions (toggles, validation, add-or-remove).",when:"Conditional render: `when <expr> { ... }`.",each:"List render: `each <list> as <item> { ... }`.",as:"Names the item variable in an `each`.",and:"Logical AND.",or:"Logical OR.",not:"Logical NOT, e.g. `when not (cart.isEmpty)`.",contains:"Case-insensitive substring match: `name contains @q`."},l=["push","remove","reset","set","create","update","delete","refetch"],c={push:"Append to a list state: `users.push(draft)` (auto-fills uuid fields).",remove:"Remove matching items locally: `users.remove(u => u.id == id)`.",reset:"Reset a state to its declared initial: `draft.reset()`.",set:"Set a state value: `rating.set(v)`.",create:"POST an item to a source-backed list, then append the result: `orders.create(draft)`.",update:"PUT an item (by id) to a source-backed list, then replace it: `orders.update(order)`.",delete:"DELETE an item (by id) from a source-backed list, then drop it: `orders.delete(order)`.",refetch:"Re-run a query with N query-string params (paginate / search / filter): `products.refetch(q: term, page: n)`."},p=Object.keys(a),d=e;export{l as ACTION_OPS,c as ACTION_OP_DOCS,n as KEYWORDS,i as KEYWORD_DOCS,r as MODIFIERS,o as MODIFIER_DOCS,a as PRIMITIVES,p as PRIMITIVE_NAMES,d as TOKEN_NAMES,t as resolveToken};
@@ -1 +1 @@
1
- import{ParseError as f}from"#engine/shared/diagnostics.js";import{PRIMITIVES as g}from"#engine/lang/manifest.js";import{Grammar as R}from"#engine/lang/grammar.js";import{Tk as t,Pn as i,Kw as n,Nt as P,Mod as u,StOp as o,Ek as x}from"#engine/shared/vocab.js";const v={};for(const[c,e]of Object.entries(g))e.string&&(v[c]=e.string);const S=new Set(Object.entries(g).filter(([,c])=>c.interp).map(([c])=>c)),y=c=>c==="text"?"string":c,k=c=>/^h[1-6]$/.test(c);class w extends R{modifiers;statements;constructor(e){super(e),this.modifiers=new Map([[u.Bind,s=>{s.bind=this.at(t.Ref)?this.eat(t.Ref).v:this.parseDotted()}],[u.Submit,s=>{s.submit=this.parseDotted()}],[u.Where,s=>{s.where=this.parseParenList(()=>this.rebuildClause())}],[u.Columns,s=>{s.columns=this.parseParenList(()=>this.eat(t.Ident).v)}],[u.Style,s=>{s.style=this.parseParenList(()=>this.parseStyleToken())}],[u.Class,s=>{s.class=this.parseParenList(()=>{const a=this.at(t.String)?this.next().v:this.eat(t.Ident).v;return this.at(t.Ident,n.When)?(this.next(),{name:a,cond:this.parseExpr()}):a})}],[u.Alt,s=>{s.alt=this.parseInterpolation(this.eat(t.String).v)}],[u.Inputs,s=>{s.inputs=this.parseArgs()}],[u.On,s=>{s.on=this.parseArgs()}]]),this.statements=new Map([[o.Push,s=>({op:o.Push,target:s,arg:this.parseExpr()})],[o.Set,s=>({op:o.Set,target:s,arg:this.parseExpr()})],[o.Reset,s=>({op:o.Reset,target:s})],[o.Remove,s=>{const a=this.eat(t.Ident).v;return this.eat(t.FatArrow),{op:o.Remove,target:s,param:a,pred:this.parseExpr()}}],[o.Create,s=>({op:o.Create,target:s,arg:this.parseExpr()})],[o.Update,s=>({op:o.Update,target:s,arg:this.parseExpr()})],[o.Delete,s=>({op:o.Delete,target:s,arg:this.parseExpr()})],[o.Refetch,s=>{const a={};for(;!this.at(t.Punct,i.ParenR);){const r=this.eat(t.Ident).v;this.eat(t.Punct,i.Colon),a[r]=this.parseExpr(),this.at(t.Punct,i.Comma)&&this.next()}return{op:o.Refetch,target:s,params:a}}]])}parse(){const e={screen:"",entities:{},state:{},actions:{},tree:null},s=new Map([[n.Screen,()=>{this.next(),e.screen=this.eat(t.Ident).v}],[n.Entity,()=>this.parseEntity(e)],[n.State,()=>this.parseState(n.State,e.state)],[n.Store,()=>{e.store=e.store||{},this.parseState(n.Store,e.store)}],[n.Get,()=>this.parseGet(e)],[n.Effect,()=>{this.next(),(e.effects=e.effects||[]).push(this.parseActionBody())}],[n.Action,()=>this.parseAction(e)],[n.Mock,()=>this.parseMock(e)],[n.Sources,()=>this.parseSources(e)],[n.Api,()=>this.parseApi(e)],[n.Meta,()=>this.parseMeta(e)],[n.Routes,()=>this.parseRoutes(e)],[n.Shell,()=>this.parseShell(e)],[n.Part,()=>this.parsePart(e)],[n.Const,()=>this.parseConst(e)],[n.Theme,()=>this.parseTheme(e)],[n.Param,()=>{this.next(),(e.params=e.params||[]).push(this.eat(t.Ident).v)}]]);for(;!this.at(t.Eof);){const a=this.peek(),r=a.t===t.Ident?s.get(a.v):void 0;r?r():e.tree=this.parseNode()}return e}parseEntity(e){this.eat(t.Ident,n.Entity);const s=this.eat(t.Ident).v;this.eat(t.Punct,i.BraceL);const a={id:"uuid"},r={};for(;!this.at(t.Punct,i.BraceR);){const p=this.eat(t.Ident).v,h=[this.eat(t.Ident).v];for(;this.at(t.Punct,i.Pipe);)this.next(),h.push(this.eat(t.Ident).v);a[p]=h.length>1?"enum:"+h.join("|"):y(h[0]);const d=this.parseConstraints();Object.keys(d).length&&(r[p]=d)}this.eat(t.Punct,i.BraceR),e.entities[s]=a,Object.keys(r).length&&((e.constraints=e.constraints||{})[s]=r)}parseConstraints(){const e={};for(;this.at(t.Ident,n.Required)||this.at(t.Ident,n.Min)||this.at(t.Ident,n.Max);){const s=this.next().v;if(s===n.Required){e.required=!0;continue}this.eat(t.Punct,i.Colon);const a=Number(this.eat(t.Number).v);s===n.Min?e.min=a:e.max=a}return e}parseState(e,s){for(this.eat(t.Ident,e),this.eat(t.Punct,i.BraceL);!this.at(t.Punct,i.BraceR);){const a=this.eat(t.Ident);this.eat(t.Punct,i.Assign);let r,p,h=!1;this.at(t.Ident,n.Query)?(this.next(),r="query:"+this.eat(t.Ident).v):this.at(t.Punct,i.BraceL)||this.at(t.Punct,i.BrackL)?(p=this.parseValue(),h=!0):this.at(t.String)?(p=this.next().v,h=!0):this.at(t.Number)?(p=Number(this.next().v),h=!0):this.at(t.Ident,n.True)||this.at(t.Ident,n.False)?(p=this.next().v===n.True,h=!0):(p=this.next().v,h=!0),this.eat(t.Punct,i.Colon);const d=this.parseType(),m=this.locOf(a.pos);s[a.v]=r?{type:d,source:r,loc:m}:{type:d,initial:h?p:null,loc:m}}this.eat(t.Punct,i.BraceR)}parseGet(e){this.eat(t.Ident,n.Get);const s=this.eat(t.Ident).v;this.eat(t.Punct,i.Assign),(e.gets=e.gets||{})[s]=this.parseExpr()}parseAction(e){this.eat(t.Ident,n.Action);const s=this.eat(t.Ident).v,a=[];if(this.at(t.Ident,n.Mutates))for(this.next(),a.push(this.eat(t.Ident).v);this.at(t.Punct,i.Comma);)this.next(),a.push(this.eat(t.Ident).v);let r="";this.at(t.LArrow)&&(this.next(),r=this.eat(t.Ident).v),e.actions[s]={mutates:a,input:r,body:this.parseActionBody()}}parseActionBody(){this.eat(t.Punct,i.BraceL);const e=[];for(;!this.at(t.Punct,i.BraceR);)e.push(this.parseStatement());return this.eat(t.Punct,i.BraceR),e}parseIf(){this.eat(t.Ident,n.If);const e=this.parseExpr(),s=this.parseActionBody(),a=this.at(t.Ident,n.Else)?(this.next(),this.parseActionBody()):null;return{op:o.If,cond:e,then:s,else:a}}parseRequest(){const e=this.eat(t.Ident).v.toUpperCase(),s=this.parseInterpolation(this.eat(t.String).v);let a=null;return this.at(t.Ident,n.Body)&&(this.next(),a=this.parseExpr()),{op:o.Request,method:e,url:s,body:a}}parseStatement(){if(this.at(t.Ident,n.If))return this.parseIf();if(this.at(t.Ident,"post")||this.at(t.Ident,"put")||this.at(t.Ident,"delete"))return this.parseRequest();const e=this.eat(t.Ident).v;this.eat(t.Punct,i.Dot);const s=this.eat(t.Ident).v;this.eat(t.Punct,i.ParenL);const a=this.statements.get(s);if(!a)throw new f(`unknown action method "${s}" on "${e}"`,this.locOf(this.peek().pos));const r=a(e);return this.eat(t.Punct,i.ParenR),r}parseMock(e){this.eat(t.Ident,n.Mock);const s=e.mock||{};this.parseEntries(a=>{s[a]=this.parseValue()}),e.mock=s}parseSources(e){this.eat(t.Ident,n.Sources);const s=e.sources||{};this.parseEntries(a=>{s[a]=this.parseValue()}),e.sources=s}parseApi(e){this.eat(t.Ident,n.Api);const s=e.api||{};this.parseEntries(a=>{s[a]=this.parseValue()}),e.api=s}parseMeta(e){this.eat(t.Ident,n.Meta),this.eat(t.Punct,i.BraceL);const s=e.meta||{};for(;!this.at(t.Punct,i.BraceR);){const a=this.eat(t.Ident).v;s[a]=this.eat(t.String).v}this.eat(t.Punct,i.BraceR),e.meta=s}parseRoutes(e){this.eat(t.Ident,n.Routes),this.eat(t.Punct,i.BraceL);const s=e.routes||[];for(;!this.at(t.Punct,i.BraceR);){const a=this.peek(),r=this.locOf(a.pos).line,p=this.pathOnLine(r);this.eat(t.Arrow);const h={url:p,page:this.eat(t.Ident).v,loc:this.locOf(a.pos)};this.at(t.Ident,n.Guard)&&(this.next(),h.guardNeg=this.at(t.Ident,n.Not)?(this.next(),!0):!1,h.guard=this.parseDotted(),this.eat(t.Ident,n.Else),h.redirect=this.pathOnLine(r)),s.push(h)}this.eat(t.Punct,i.BraceR),e.routes=s}parseShell(e){this.eat(t.Ident,n.Shell),e.shell={type:P.Shell,props:{},children:this.parseChildren()}}parseConst(e){this.eat(t.Ident,n.Const);const s=this.eat(t.Ident).v;if(this.eat(t.Punct,i.Assign),this.at(t.Punct,i.BraceL)||this.at(t.Punct,i.BrackL))throw new f("const holds a single value (string/number/bool) \u2014 use a block like `theme { \u2026 }` for structured data",this.locOf(this.peek().pos));(e.consts=e.consts||{})[s]=this.parseScalar()}parseTheme(e){this.eat(t.Ident,n.Theme),this.eat(t.Punct,i.BraceL);const s={};for(;!this.at(t.Punct,i.BraceR);){const a=this.eat(t.Ident).v;this.eat(t.Punct,i.BraceL);const r={};for(;!this.at(t.Punct,i.BraceR);)r[this.eat(t.Ident).v]=this.eat(t.String).v;this.eat(t.Punct,i.BraceR),s[a]=r}this.eat(t.Punct,i.BraceR),e.theme=s}parsePart(e){this.eat(t.Ident,n.Part);const s=this.eat(t.Ident).v;this.eat(t.Punct,i.ParenL);const a=[];for(;!this.at(t.Punct,i.ParenR);){const p=this.eat(t.Ident).v;this.eat(t.Punct,i.Colon),a.push({name:p,type:this.parseType()}),this.at(t.Punct,i.Comma)&&this.next()}this.eat(t.Punct,i.ParenR);const r=this.parseChildren();e.parts=e.parts||{},e.parts[s]={params:a,tree:r.length===1?r[0]:{type:P.Stack,props:{},children:r}}}parseWhen(){const e=this.eat(t.Ident,n.When),s=this.parseExpr();return{type:P.When,props:{cond:s},children:this.parseChildren(),loc:this.locOf(e.pos)}}parseEach(){const e=this.eat(t.Ident,n.Each),s=this.parseExpr();this.eat(t.Ident,n.As);const a=this.eat(t.Ident).v;return{type:P.Each,props:{list:s,as:a},children:this.parseChildren(),loc:this.locOf(e.pos)}}parseNode(){if(this.at(t.Ident,n.When))return this.parseWhen();if(this.at(t.Ident,n.Each))return this.parseEach();const e=this.eat(t.Ident),s=e.v,a=this.locOf(e.pos);if(this.at(t.Punct,i.ParenL))return{type:s,args:this.parseArgs(),loc:a};const r={},p=[];s===P.Custom&&(r.component=this.eat(t.Ident).v);let h=!0;for(;h;){const m=this.peek();switch(m.t){case t.String:{const l=v[s]||"label";r[l]=S.has(s)?this.parseInterpolation(this.next().v):this.next().v;break}case t.Param:{const l=v[s]||"label";r[l]={$param:this.next().v};break}case t.Ref:r.data=this.next().v;break;case t.Arrow:this.parseArrow(s,r);break;case t.Ident:{const l=m.v;if(s===P.Title&&k(l)){this.next(),r.level=l;break}const I=this.modifiers.get(l);if(!I){h=!1;break}this.next(),I(r);break}case t.Punct:if(m.v===i.BraceL){for(this.next();!this.at(t.Punct,i.BraceR);)p.push(this.parseNode());this.eat(t.Punct,i.BraceR)}h=!1;break;default:h=!1}}const d={type:s,props:r,loc:a};return p.length&&(d.children=p),d}parseArrow(e,s){if(this.next(),e===P.Link){s.to=this.parsePath();return}s.action=this.parseDotted(),this.at(t.Punct,i.ParenL)&&(this.next(),this.at(t.Punct,i.ParenR)||(s.arg=this.parseExpr()),this.eat(t.Punct,i.ParenR))}parseChildren(){this.eat(t.Punct,i.BraceL);const e=[];for(;!this.at(t.Punct,i.BraceR);)e.push(this.parseNode());return this.eat(t.Punct,i.BraceR),e}parseType(){let e=this.eat(t.Ident).v;return this.at(t.Punct,i.Lt)&&(this.next(),e+="<"+this.eat(t.Ident).v+">",this.eat(t.Punct,i.Gt)),e}parseStyleToken(){const e=()=>this.at(t.Number)?this.next().v:this.eat(t.Ident).v;let s=e();for(this.at(t.Punct,i.Colon)&&(this.next(),s+=":"+e());this.at(t.Punct,i.Dot);)this.next(),s+="."+e();return s}parseDotted(){let e=this.at(t.Param)?"$"+this.next().v:this.eat(t.Ident).v;for(;this.at(t.Punct,i.Dot);)this.next(),e+="."+this.eat(t.Ident).v;return e}parseParenList(e){this.eat(t.Punct,i.ParenL);const s=[];for(;!this.at(t.Punct,i.ParenR);)s.push(e()),this.at(t.Punct,i.Comma)&&this.next();return this.eat(t.Punct,i.ParenR),s}rebuildClause(){const e=[];for(;!this.at(t.Punct,i.Comma)&&!this.at(t.Punct,i.ParenR);)e.push(this.next().v);return e.join(" ")}parseEntries(e){for(this.eat(t.Punct,i.BraceL);!this.at(t.Punct,i.BraceR);){const s=this.eat(t.Ident).v;this.eat(t.Punct,i.Colon),e(s),this.at(t.Punct,i.Comma)&&this.next()}this.eat(t.Punct,i.BraceR)}parsePath(){const e=[];let s="";for(;this.at(t.Punct,i.Slash);){const a=this.next();s+="/",this.at(t.Punct,i.BraceL)&&this.peek().pos===a.pos+1?(e.push(s),s="",this.next(),e.push(this.parseExpr()),this.eat(t.Punct,i.BraceR)):this.at(t.Ident)&&this.peek().pos===a.pos+1&&(s+=this.eat(t.Ident).v)}return e.length?(s&&e.push(s),{kind:x.Interp,parts:e}):s}pathOnLine(e){let s="";for(;this.at(t.Punct,i.Slash)&&this.locOf(this.peek().pos).line===e;)this.next(),s+="/",this.at(t.Punct,i.Colon)?(this.next(),s+=":"+this.eat(t.Ident).v):this.at(t.Ident)&&(s+=this.eat(t.Ident).v);return s}parseArgs(){this.eat(t.Punct,i.ParenL);const e={};for(;!this.at(t.Punct,i.ParenR);){const s=this.eat(t.Ident).v;this.eat(t.Punct,i.Colon),e[s]=this.parseArgValue(),this.at(t.Punct,i.Comma)&&this.next()}return this.eat(t.Punct,i.ParenR),e}parseArgValue(){return this.at(t.String)?this.next().v:this.at(t.Number)?Number(this.next().v):this.at(t.Ref)?this.next().v:this.at(t.Param)?{$param:this.next().v}:this.parseDotted()}}function A(c){return new w(c).parse()}export{w as Parser,A as parse};
1
+ import{ParseError as f}from"#engine/shared/diagnostics.js";import{PRIMITIVES as g}from"#engine/lang/manifest.js";import{Grammar as R}from"#engine/lang/grammar.js";import{Tk as t,Pn as i,Kw as n,Nt as m,Mod as d,StOp as o,Ek as x}from"#engine/shared/vocab.js";const v={};for(const[c,e]of Object.entries(g))e.string&&(v[c]=e.string);const S=new Set(Object.entries(g).filter(([,c])=>c.interp).map(([c])=>c)),y=c=>c==="text"?"string":c,k=c=>/^h[1-6]$/.test(c);class w extends R{modifiers;statements;constructor(e){super(e),this.modifiers=new Map([[d.Bind,s=>{s.bind=this.at(t.Ref)?this.eat(t.Ref).v:this.parseDotted()}],[d.Submit,s=>{s.submit=this.parseDotted()}],[d.Where,s=>{s.where=this.parseParenList(()=>this.rebuildClause())}],[d.Columns,s=>{s.columns=this.parseParenList(()=>this.eat(t.Ident).v)}],[d.Style,s=>{s.style=this.parseParenList(()=>this.parseStyleToken())}],[d.Class,s=>{s.class=this.parseParenList(()=>{const a=this.at(t.String)?this.next().v:this.eat(t.Ident).v;return this.at(t.Ident,n.When)?(this.next(),{name:a,cond:this.parseExpr()}):a})}],[d.Alt,s=>{s.alt=this.parseInterpolation(this.eat(t.String).v)}],[d.Inputs,s=>{s.inputs=this.parseArgs()}],[d.On,s=>{s.on=this.parseArgs()}]]),this.statements=new Map([[o.Push,s=>({op:o.Push,target:s,arg:this.parseExpr()})],[o.Set,s=>({op:o.Set,target:s,arg:this.parseExpr()})],[o.Reset,s=>({op:o.Reset,target:s})],[o.Remove,s=>{const a=this.eat(t.Ident).v;return this.eat(t.FatArrow),{op:o.Remove,target:s,param:a,pred:this.parseExpr()}}],[o.Create,s=>({op:o.Create,target:s,arg:this.parseExpr()})],[o.Update,s=>({op:o.Update,target:s,arg:this.parseExpr()})],[o.Delete,s=>({op:o.Delete,target:s,arg:this.parseExpr()})],[o.Refetch,s=>{const a={};for(;!this.at(t.Punct,i.ParenR);){const r=this.eat(t.Ident).v;this.eat(t.Punct,i.Colon),a[r]=this.parseExpr(),this.at(t.Punct,i.Comma)&&this.next()}return{op:o.Refetch,target:s,params:a}}]])}parse(){const e={screen:"",entities:{},state:{},actions:{},tree:null},s=new Map([[n.Screen,()=>{this.next(),e.screen=this.eat(t.Ident).v}],[n.Entity,()=>this.parseEntity(e)],[n.State,()=>this.parseState(n.State,e.state)],[n.Store,()=>{e.store=e.store||{},this.parseState(n.Store,e.store)}],[n.Get,()=>this.parseGet(e)],[n.Effect,()=>{this.next(),(e.effects=e.effects||[]).push(this.parseActionBody())}],[n.Action,()=>this.parseAction(e)],[n.Mock,()=>this.parseMock(e)],[n.Sources,()=>this.parseSources(e)],[n.Api,()=>this.parseApi(e)],[n.Meta,()=>this.parseMeta(e)],[n.Routes,()=>this.parseRoutes(e)],[n.Shell,()=>this.parseShell(e)],[n.Part,()=>this.parsePart(e)],[n.Const,()=>this.parseConst(e)],[n.Theme,()=>this.parseTheme(e)],[n.Param,()=>{this.next(),(e.params=e.params||[]).push(this.eat(t.Ident).v)}],[n.Use,()=>{this.next();const a=[this.eat(t.Ident).v];for(;this.at(t.Punct,i.Comma);)this.next(),a.push(this.eat(t.Ident).v);this.eat(t.Ident,n.From),(e.imports=e.imports||[]).push({names:a,from:this.eat(t.String).v})}]]);for(;!this.at(t.Eof);){const a=this.peek(),r=a.t===t.Ident?s.get(a.v):void 0;r?r():e.tree=this.parseNode()}return e}parseEntity(e){this.eat(t.Ident,n.Entity);const s=this.eat(t.Ident).v;this.eat(t.Punct,i.BraceL);const a={id:"uuid"},r={};for(;!this.at(t.Punct,i.BraceR);){const p=this.eat(t.Ident).v,h=[this.eat(t.Ident).v];for(;this.at(t.Punct,i.Pipe);)this.next(),h.push(this.eat(t.Ident).v);a[p]=h.length>1?"enum:"+h.join("|"):y(h[0]);const l=this.parseConstraints();Object.keys(l).length&&(r[p]=l)}this.eat(t.Punct,i.BraceR),e.entities[s]=a,Object.keys(r).length&&((e.constraints=e.constraints||{})[s]=r)}parseConstraints(){const e={};for(;this.at(t.Ident,n.Required)||this.at(t.Ident,n.Min)||this.at(t.Ident,n.Max);){const s=this.next().v;if(s===n.Required){e.required=!0;continue}this.eat(t.Punct,i.Colon);const a=Number(this.eat(t.Number).v);s===n.Min?e.min=a:e.max=a}return e}parseState(e,s){for(this.eat(t.Ident,e),this.eat(t.Punct,i.BraceL);!this.at(t.Punct,i.BraceR);){const a=this.eat(t.Ident);this.eat(t.Punct,i.Assign);let r,p,h=!1;this.at(t.Ident,n.Query)?(this.next(),r="query:"+this.eat(t.Ident).v):this.at(t.Punct,i.BraceL)||this.at(t.Punct,i.BrackL)?(p=this.parseValue(),h=!0):this.at(t.String)?(p=this.next().v,h=!0):this.at(t.Number)?(p=Number(this.next().v),h=!0):this.at(t.Ident,n.True)||this.at(t.Ident,n.False)?(p=this.next().v===n.True,h=!0):(p=this.next().v,h=!0),this.eat(t.Punct,i.Colon);const l=this.parseType(),u=this.locOf(a.pos);s[a.v]=r?{type:l,source:r,loc:u}:{type:l,initial:h?p:null,loc:u}}this.eat(t.Punct,i.BraceR)}parseGet(e){this.eat(t.Ident,n.Get);const s=this.eat(t.Ident).v;this.eat(t.Punct,i.Assign),(e.gets=e.gets||{})[s]=this.parseExpr()}parseAction(e){this.eat(t.Ident,n.Action);const s=this.eat(t.Ident).v,a=[];if(this.at(t.Ident,n.Mutates))for(this.next(),a.push(this.eat(t.Ident).v);this.at(t.Punct,i.Comma);)this.next(),a.push(this.eat(t.Ident).v);let r="";this.at(t.LArrow)&&(this.next(),r=this.eat(t.Ident).v),e.actions[s]={mutates:a,input:r,body:this.parseActionBody()}}parseActionBody(){this.eat(t.Punct,i.BraceL);const e=[];for(;!this.at(t.Punct,i.BraceR);)e.push(this.parseStatement());return this.eat(t.Punct,i.BraceR),e}parseIf(){this.eat(t.Ident,n.If);const e=this.parseExpr(),s=this.parseActionBody(),a=this.at(t.Ident,n.Else)?(this.next(),this.parseActionBody()):null;return{op:o.If,cond:e,then:s,else:a}}parseRequest(){const e=this.eat(t.Ident).v.toUpperCase(),s=this.parseInterpolation(this.eat(t.String).v);let a=null;return this.at(t.Ident,n.Body)&&(this.next(),a=this.parseExpr()),{op:o.Request,method:e,url:s,body:a}}parseStatement(){if(this.at(t.Ident,n.If))return this.parseIf();if(this.at(t.Ident,"post")||this.at(t.Ident,"put")||this.at(t.Ident,"delete"))return this.parseRequest();const e=this.eat(t.Ident).v;this.eat(t.Punct,i.Dot);const s=this.eat(t.Ident).v;this.eat(t.Punct,i.ParenL);const a=this.statements.get(s);if(!a)throw new f(`unknown action method "${s}" on "${e}"`,this.locOf(this.peek().pos));const r=a(e);return this.eat(t.Punct,i.ParenR),r}parseMock(e){this.eat(t.Ident,n.Mock);const s=e.mock||{};this.parseEntries(a=>{s[a]=this.parseValue()}),e.mock=s}parseSources(e){this.eat(t.Ident,n.Sources);const s=e.sources||{};this.parseEntries(a=>{s[a]=this.parseValue()}),e.sources=s}parseApi(e){this.eat(t.Ident,n.Api);const s=e.api||{};this.parseEntries(a=>{s[a]=this.parseValue()}),e.api=s}parseMeta(e){this.eat(t.Ident,n.Meta),this.eat(t.Punct,i.BraceL);const s=e.meta||{};for(;!this.at(t.Punct,i.BraceR);){const a=this.eat(t.Ident).v;s[a]=this.eat(t.String).v}this.eat(t.Punct,i.BraceR),e.meta=s}parseRoutes(e){this.eat(t.Ident,n.Routes),this.eat(t.Punct,i.BraceL);const s=e.routes||[];for(;!this.at(t.Punct,i.BraceR);){const a=this.peek(),r=this.locOf(a.pos).line,p=this.pathOnLine(r);this.eat(t.Arrow);const h={url:p,page:this.eat(t.Ident).v,loc:this.locOf(a.pos)};this.at(t.Ident,n.Guard)&&(this.next(),h.guardNeg=this.at(t.Ident,n.Not)?(this.next(),!0):!1,h.guard=this.parseDotted(),this.eat(t.Ident,n.Else),h.redirect=this.pathOnLine(r)),s.push(h)}this.eat(t.Punct,i.BraceR),e.routes=s}parseShell(e){this.eat(t.Ident,n.Shell),e.shell={type:m.Shell,props:{},children:this.parseChildren()}}parseConst(e){this.eat(t.Ident,n.Const);const s=this.eat(t.Ident).v;if(this.eat(t.Punct,i.Assign),this.at(t.Punct,i.BraceL)||this.at(t.Punct,i.BrackL))throw new f("const holds a single value (string/number/bool) \u2014 use a block like `theme { \u2026 }` for structured data",this.locOf(this.peek().pos));(e.consts=e.consts||{})[s]=this.parseScalar()}parseTheme(e){this.eat(t.Ident,n.Theme),this.eat(t.Punct,i.BraceL);const s={};for(;!this.at(t.Punct,i.BraceR);){const a=this.eat(t.Ident).v;this.eat(t.Punct,i.BraceL);const r={};for(;!this.at(t.Punct,i.BraceR);)r[this.eat(t.Ident).v]=this.eat(t.String).v;this.eat(t.Punct,i.BraceR),s[a]=r}this.eat(t.Punct,i.BraceR),e.theme=s}parsePart(e){this.eat(t.Ident,n.Part);const s=this.eat(t.Ident).v;this.eat(t.Punct,i.ParenL);const a=[];for(;!this.at(t.Punct,i.ParenR);){const p=this.eat(t.Ident).v;this.eat(t.Punct,i.Colon),a.push({name:p,type:this.parseType()}),this.at(t.Punct,i.Comma)&&this.next()}this.eat(t.Punct,i.ParenR);const r=this.parseChildren();e.parts=e.parts||{},e.parts[s]={params:a,tree:r.length===1?r[0]:{type:m.Stack,props:{},children:r}}}parseWhen(){const e=this.eat(t.Ident,n.When),s=this.parseExpr();return{type:m.When,props:{cond:s},children:this.parseChildren(),loc:this.locOf(e.pos)}}parseEach(){const e=this.eat(t.Ident,n.Each),s=this.parseExpr();this.eat(t.Ident,n.As);const a=this.eat(t.Ident).v;return{type:m.Each,props:{list:s,as:a},children:this.parseChildren(),loc:this.locOf(e.pos)}}parseNode(){if(this.at(t.Ident,n.When))return this.parseWhen();if(this.at(t.Ident,n.Each))return this.parseEach();const e=this.eat(t.Ident),s=e.v,a=this.locOf(e.pos);if(this.at(t.Punct,i.ParenL)){const u=this.parseArgs();return this.at(t.Ident,n.Client)?(this.next(),this.eat(t.Punct,i.Colon),{type:s,args:u,props:{hydrate:this.eat(t.Ident).v},loc:a}):{type:s,args:u,loc:a}}const r={},p=[];s===m.Custom&&(r.component=this.eat(t.Ident).v);let h=!0;for(;h;){const u=this.peek();switch(u.t){case t.String:{const P=v[s]||"label";r[P]=S.has(s)?this.parseInterpolation(this.next().v):this.next().v;break}case t.Param:{const P=v[s]||"label";r[P]={$param:this.next().v};break}case t.Ref:r.data=this.next().v;break;case t.Arrow:this.parseArrow(s,r);break;case t.Ident:{const P=u.v;if(s===m.Title&&k(P)){this.next(),r.level=P;break}const I=this.modifiers.get(P);if(!I){h=!1;break}this.next(),I(r);break}case t.Punct:if(u.v===i.BraceL){for(this.next();!this.at(t.Punct,i.BraceR);)p.push(this.parseNode());this.eat(t.Punct,i.BraceR)}h=!1;break;default:h=!1}}const l={type:s,props:r,loc:a};return p.length&&(l.children=p),l}parseArrow(e,s){if(this.next(),e===m.Link){s.to=this.parsePath();return}s.action=this.parseDotted(),this.at(t.Punct,i.ParenL)&&(this.next(),this.at(t.Punct,i.ParenR)||(s.arg=this.parseExpr()),this.eat(t.Punct,i.ParenR))}parseChildren(){this.eat(t.Punct,i.BraceL);const e=[];for(;!this.at(t.Punct,i.BraceR);)e.push(this.parseNode());return this.eat(t.Punct,i.BraceR),e}parseType(){let e=this.eat(t.Ident).v;return this.at(t.Punct,i.Lt)&&(this.next(),e+="<"+this.eat(t.Ident).v+">",this.eat(t.Punct,i.Gt)),e}parseStyleToken(){const e=()=>this.at(t.Number)?this.next().v:this.eat(t.Ident).v;let s=e();for(this.at(t.Punct,i.Colon)&&(this.next(),s+=":"+e());this.at(t.Punct,i.Dot);)this.next(),s+="."+e();return s}parseDotted(){let e=this.at(t.Param)?"$"+this.next().v:this.eat(t.Ident).v;for(;this.at(t.Punct,i.Dot);)this.next(),e+="."+this.eat(t.Ident).v;return e}parseParenList(e){this.eat(t.Punct,i.ParenL);const s=[];for(;!this.at(t.Punct,i.ParenR);)s.push(e()),this.at(t.Punct,i.Comma)&&this.next();return this.eat(t.Punct,i.ParenR),s}rebuildClause(){const e=[];for(;!this.at(t.Punct,i.Comma)&&!this.at(t.Punct,i.ParenR);)e.push(this.next().v);return e.join(" ")}parseEntries(e){for(this.eat(t.Punct,i.BraceL);!this.at(t.Punct,i.BraceR);){const s=this.eat(t.Ident).v;this.eat(t.Punct,i.Colon),e(s),this.at(t.Punct,i.Comma)&&this.next()}this.eat(t.Punct,i.BraceR)}parsePath(){const e=[];let s="";for(;this.at(t.Punct,i.Slash);){const a=this.next();s+="/",this.at(t.Punct,i.BraceL)&&this.peek().pos===a.pos+1?(e.push(s),s="",this.next(),e.push(this.parseExpr()),this.eat(t.Punct,i.BraceR)):this.at(t.Ident)&&this.peek().pos===a.pos+1&&(s+=this.eat(t.Ident).v)}return e.length?(s&&e.push(s),{kind:x.Interp,parts:e}):s}pathOnLine(e){let s="";for(;this.at(t.Punct,i.Slash)&&this.locOf(this.peek().pos).line===e;)this.next(),s+="/",this.at(t.Punct,i.Colon)?(this.next(),s+=":"+this.eat(t.Ident).v):this.at(t.Ident)&&(s+=this.eat(t.Ident).v);return s}parseArgs(){this.eat(t.Punct,i.ParenL);const e={};for(;!this.at(t.Punct,i.ParenR);){const s=this.eat(t.Ident).v;this.eat(t.Punct,i.Colon),e[s]=this.parseArgValue(),this.at(t.Punct,i.Comma)&&this.next()}return this.eat(t.Punct,i.ParenR),e}parseArgValue(){return this.at(t.String)?this.next().v:this.at(t.Number)?Number(this.next().v):this.at(t.Ref)?this.next().v:this.at(t.Param)?{$param:this.next().v}:this.parseDotted()}}function A(c){return new w(c).parse()}export{w as Parser,A as parse};
@@ -1 +1 @@
1
- import f from"node:fs";import{dirname as d,join as u}from"node:path";import{parse as l}from"#engine/lang/parse.js";import{toDoc as b}from"#engine/ir/flatten.js";import{compose as k}from"#engine/ir/compose.js";import{validate as j}from"#engine/ir/validate.js";import{closest as O,diag as y,ParseError as T}from"#engine/shared/diagnostics.js";import{PRIMITIVE_NAMES as w}from"#engine/lang/manifest.js";import{mergeTheme as h}from"#engine/style/tokens.js";function S(r){const e=m(r);if(!e)return h({});try{return h(l(f.readFileSync(u(e,"theme.muten"),"utf8")).theme||{})}catch{return h({})}}function R(r){const e={};let t;try{t=f.readdirSync(r)}catch{return e}for(const n of t){if(!n.endsWith(".muten"))continue;let o;try{o=l(f.readFileSync(u(r,n),"utf8"))}catch{continue}for(const[i,c]of Object.entries(o.parts||{}))e[i]={...c,state:o.state||{},entities:o.entities||{}}}return e}function m(r){let e=d(r);for(let t=0;t<30;t++){if(f.existsSync(u(e,"src","pages")))return e;const n=d(e);if(n===e)break;e=n}return null}function x(r){const e=[],t=n=>{let o;try{o=f.readdirSync(n,{withFileTypes:!0})}catch{return}for(const i of o){if(!i.isDirectory())continue;const c=u(n,i.name);i.name==="parts"?e.push(c):t(c)}};return t(u(r,"src")),e}function v(r){const e=m(r);if(!e)return[];const t=[],n=o=>{let i;try{i=f.readdirSync(o,{withFileTypes:!0})}catch{return}for(const c of i){const s=u(o,c.name);c.isDirectory()?n(s):c.name.endsWith(".store")&&t.push(c.name.slice(0,-6))}};return n(u(e,"src")),t}function D(r){const e=m(r);if(!e)return R(u(d(r),"parts"));const t={};for(const n of x(e))Object.assign(t,R(n));return t}function P(r,e){const t=m(r),n=t?u(t,"src","pages"):null;let o=[];if(n)try{o=f.readdirSync(n,{withFileTypes:!0}).filter(s=>s.isDirectory()).map(s=>s.name)}catch{}const i=[],c=new Set;for(const s of e)c.has(s.url)&&i.push(y("dup-route",`duplicate route "${s.url}"`,{loc:s.loc})),c.add(s.url),n&&!o.includes(s.page)&&i.push(y("unknown-page",`route "${s.url}" \u2192 page "${s.page}" not found in src/pages/`,{loc:s.loc,suggestion:O(s.page,o)}));return{ok:i.length===0,diagnostics:i}}function z(r,e){let t;try{t=l(e)}catch(p){return p instanceof T&&p.loc?{ok:!1,diagnostics:[y("syntax",p.message,{loc:p.loc})]}:{ok:!0,diagnostics:[]}}if(t.routes)return P(r,t.routes);if(t.theme)return{ok:!0,diagnostics:[]};if(r.endsWith(".store"))return j({screen:"store",state:t.state||{},actions:t.actions||{},entities:t.entities||{},gets:t.gets||{},consts:{},constraints:{},rootId:void 0,nodes:{}},{kind:"store"});const n=D(r),{tree:o,used:i}=k(t.tree,n),c={...t.entities},s={...t.state};for(const p of i){const g=n[p];g&&(Object.assign(c,g.entities),Object.assign(s,g.state))}const a=b({screen:t.screen,entities:c,state:s,actions:t.actions,consts:t.consts,constraints:t.constraints,tree:o});return j(a,{parts:Object.keys(n),stores:v(r),theme:S(r)})}function A(r,e){let t=null;try{t=l(e)}catch{}const n=D(r),o=Object.entries(n).map(([s,a])=>({name:s,params:a.params||[]})),i=[],c=(s,a)=>{i.push({name:s,type:a.type||"",query:typeof a.source=="string"&&a.source.startsWith("query:")})};for(const[s,a]of Object.entries(t?.state||{}))c(s,a);for(const s of Object.values(n))for(const[a,p]of Object.entries(s.state||{}))c(a,p);return{parts:o,state:i,actions:Object.keys(t?.actions||{}),primitives:w,theme:S(r)}}export{z as analyze,A as completion,D as projectParts,v as projectStores,S as projectTheme};
1
+ import p from"node:fs";import{dirname as m,join as u}from"node:path";import{parse as f}from"#engine/lang/parse.js";import{composeDoc as D}from"#engine/ir/compose.js";import{validate as y}from"#engine/ir/validate.js";import{closest as k,diag as g,ParseError as b}from"#engine/shared/diagnostics.js";import{PRIMITIVE_NAMES as O}from"#engine/lang/manifest.js";import{mergeTheme as d}from"#engine/style/tokens.js";function h(r){const e=l(r);if(!e)return d({});try{return d(f(p.readFileSync(u(e,"theme.muten"),"utf8")).theme||{})}catch{return d({})}}function S(r){const e={};let t;try{t=p.readdirSync(r)}catch{return e}for(const s of t){if(!s.endsWith(".muten"))continue;let i;try{i=f(p.readFileSync(u(r,s),"utf8"))}catch{continue}for(const[o,c]of Object.entries(i.parts||{}))e[o]={...c,state:i.state||{},entities:i.entities||{}}}return e}function l(r){let e=m(r);for(let t=0;t<30;t++){if(p.existsSync(u(e,"src","pages")))return e;const s=m(e);if(s===e)break;e=s}return null}function T(r){const e=[],t=s=>{let i;try{i=p.readdirSync(s,{withFileTypes:!0})}catch{return}for(const o of i){if(!o.isDirectory())continue;const c=u(s,o.name);o.name==="parts"?e.push(c):t(c)}};return t(u(r,"src")),e}function w(r){const e=l(r);if(!e)return[];const t=[],s=i=>{let o;try{o=p.readdirSync(i,{withFileTypes:!0})}catch{return}for(const c of o){const n=u(i,c.name);c.isDirectory()?s(n):c.name.endsWith(".store")&&t.push(c.name.slice(0,-6))}};return s(u(e,"src")),t}function R(r){const e=l(r);if(!e)return S(u(m(r),"parts"));const t={};for(const s of T(e))Object.assign(t,S(s));return t}function x(r,e){const t=l(r),s=t?u(t,"src","pages"):null;let i=[];if(s)try{i=p.readdirSync(s,{withFileTypes:!0}).filter(n=>n.isDirectory()).map(n=>n.name)}catch{}const o=[],c=new Set;for(const n of e)c.has(n.url)&&o.push(g("dup-route",`duplicate route "${n.url}"`,{loc:n.loc})),c.add(n.url),s&&!i.includes(n.page)&&o.push(g("unknown-page",`route "${n.url}" \u2192 page "${n.page}" not found in src/pages/`,{loc:n.loc,suggestion:k(n.page,i)}));return{ok:o.length===0,diagnostics:o}}function L(r,e){let t;try{t=f(e)}catch(o){return o instanceof b&&o.loc?{ok:!1,diagnostics:[g("syntax",o.message,{loc:o.loc})]}:{ok:!0,diagnostics:[]}}if(t.routes)return x(r,t.routes);if(t.theme)return{ok:!0,diagnostics:[]};if(r.endsWith(".store"))return y({screen:"store",state:t.state||{},actions:t.actions||{},entities:t.entities||{},gets:t.gets||{},consts:{},constraints:{},rootId:void 0,nodes:{}},{kind:"store"});const s=R(r),{doc:i}=D(t,s);return y(i,{parts:Object.keys(s),stores:w(r),theme:h(r)})}function $(r,e){let t=null;try{t=f(e)}catch{}const s=R(r),i=Object.entries(s).map(([n,a])=>({name:n,params:a.params||[]})),o=[],c=(n,a)=>{o.push({name:n,type:a.type||"",query:typeof a.source=="string"&&a.source.startsWith("query:")})};for(const[n,a]of Object.entries(t?.state||{}))c(n,a);for(const n of Object.values(s))for(const[a,j]of Object.entries(n.state||{}))c(a,j);return{parts:i,state:o,actions:Object.keys(t?.actions||{}),primitives:O,theme:h(r)}}export{L as analyze,$ as completion,R as projectParts,w as projectStores,h as projectTheme};
@@ -1,5 +1,5 @@
1
- import{readFileSync as l,existsSync as k,readdirSync as u}from"node:fs";import{join as m,dirname as x,basename as R}from"node:path";import{parse as d}from"#engine/lang/parse.js";import{toDoc as F}from"#engine/ir/flatten.js";import{resolveStyles as b}from"#engine/project/styles.js";import{compose as I}from"#engine/ir/compose.js";function V(e,n={}){let t;try{t=u(e,{withFileTypes:!0})}catch{return n}for(const o of t){const s=m(e,o.name);o.isDirectory()?V(s,n):o.name.endsWith(".store")&&(n[R(o.name,".store")]=d(l(s,"utf8")))}return n}async function h(e){const n={};if(!k(e))return n;for(const t of u(e)){if(!t.endsWith(".muten"))continue;const o=m(e,t),s=d(l(o,"utf8")),{css:r}=await b(o);for(const[a,i]of Object.entries(s.parts||{}))n[a]={...i,state:s.state||{},entities:s.entities||{},mock:s.mock||{},css:r}}return n}async function C(e){const n={},t=[],o=s=>{let r;try{r=u(s,{withFileTypes:!0})}catch{return}for(const a of r){if(!a.isDirectory())continue;const i=m(s,a.name);a.name==="parts"?t.push(i):o(i)}};o(m(e,"src"));for(const s of t)Object.assign(n,await h(s));return n}async function J(e,n={}){const t=d(l(e,"utf8")),o=await h(m(x(e),"parts")),s={};for(const[f,c]of Object.entries(t.parts||{}))s[f]={...c,state:{},entities:{},mock:{},css:""};const r={...n,...o,...s},{tree:a,used:i}=I(t.tree,r),y={...t.entities},g={...t.state};let p={...t.mock||{}};for(const f of i){const c=r[f];c&&(Object.assign(y,c.entities),Object.assign(g,c.state),p={...p,...c.mock})}const w=F({screen:t.screen,entities:y,state:g,actions:t.actions,consts:t.consts,constraints:t.constraints,tree:a}),P=e.replace(/\.muten$/,".data.json"),D={...k(P)?JSON.parse(l(P,"utf8")):{},...p},j=await b(e),O=i.map(f=>r[f]?.css).filter(Boolean).join(`
1
+ import{readFileSync as l,existsSync as g,readdirSync as u}from"node:fs";import{join as f,dirname as S,basename as b}from"node:path";import{parse as d}from"#engine/lang/parse.js";import{resolveStyles as j}from"#engine/project/styles.js";import{composeDoc as x}from"#engine/ir/compose.js";function O(e,n={}){let s;try{s=u(e,{withFileTypes:!0})}catch{return n}for(const r of s){const t=f(e,r.name);r.isDirectory()?O(t,n):r.name.endsWith(".store")&&(n[b(r.name,".store")]=d(l(t,"utf8")))}return n}async function k(e){const n={};if(!g(e))return n;for(const s of u(e)){if(!s.endsWith(".muten"))continue;const r=f(e,s),t=d(l(r,"utf8")),{css:o}=await j(r);for(const[a,i]of Object.entries(t.parts||{}))n[a]={...i,state:t.state||{},entities:t.entities||{},mock:t.mock||{},css:o}}return n}async function L(e){const n={},s=[],r=t=>{let o;try{o=u(t,{withFileTypes:!0})}catch{return}for(const a of o){if(!a.isDirectory())continue;const i=f(t,a.name);a.name==="parts"?s.push(i):r(i)}};r(f(e,"src"));for(const t of s)Object.assign(n,await k(t));return n}async function N(e,n={}){const s=d(l(e,"utf8")),r=await k(f(S(e),"parts")),t={};for(const[c,m]of Object.entries(s.parts||{}))t[c]={...m,state:{},entities:{},mock:{},css:""};const o={...n,...r,...t},{doc:a,used:i}=x(s,o);let p={...s.mock||{}};for(const c of i){const m=o[c];m&&(p={...p,...m.mock})}const y=e.replace(/\.muten$/,".data.json"),h={...g(y)?JSON.parse(l(y,"utf8")):{},...p},P=await j(e),w=i.map(c=>o[c]?.css).filter(Boolean).join(`
2
2
 
3
- `),S=[j.css,O].filter(Boolean).join(`
3
+ `),D=[P.css,w].filter(Boolean).join(`
4
4
 
5
- `);return{ir:t,doc:w,data:D,sources:t.sources||{},styles:{css:S,from:j.from},partNames:Object.keys(r)}}export{V as findStores,J as load,C as loadAllParts,h as loadParts};
5
+ `);return{ir:s,doc:a,data:h,sources:s.sources||{},styles:{css:D,from:P.from},partNames:Object.keys(o)}}export{O as findStores,N as load,L as loadAllParts,k as loadParts};
@@ -1 +1 @@
1
- import{sourceRequest as m,sourceRows as x}from"#engine/shared/source.js";const S=new Set(["img","input","br","hr","meta","link","source"]),l=e=>e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"),u=e=>l(e).replace(/"/g,"&quot;");class h{parentNode=null;remove(){const n=this.parentNode;if(!n)return;const t=n.children.indexOf(this);t>=0&&n.children.splice(t,1),this.parentNode=null}}class f extends h{constructor(t){super();this.text=t}text}class g extends h{constructor(t){super();this.text=t}text}class c extends h{constructor(t){super();this.tag=t}tag;children=[];attrs={};className="";src="";alt="";href="";type="";value="";placeholder="";text="";get childNodes(){return this.children}get textContent(){return this.text}set textContent(t){this.text=t;for(const r of this.children)r.parentNode=null;this.children=[]}appendChild(t){if(t instanceof a){for(const r of[...t.children])this.appendChild(r);return t}return t.remove(),t.parentNode=this,this.children.push(t),this.text="",t}insertBefore(t,r){const o=t instanceof a?[...t.children]:[t];for(const i of o)i.remove();const s=r?this.children.indexOf(r):-1;this.children.splice(s>=0?s:this.children.length,0,...o);for(const i of o)i.parentNode=this;return t instanceof a&&(t.children=[]),t}replaceChildren(...t){for(const r of this.children)r.parentNode=null;this.children=[];for(const r of t)this.appendChild(r)}setAttribute(t,r){this.attrs[t]=r}addEventListener(){}createTHead(){const t=new c("thead");return this.appendChild(t),t}createTBody(){const t=new c("tbody");return this.appendChild(t),t}insertRow(){const t=new c("tr");return this.appendChild(t),t}}class a extends c{constructor(){super("#fragment")}}class N{createElement(n){return new c(n)}createComment(n){return new g(n)}createDocumentFragment(){return new a}createTextNode(n){return new f(n)}}function p(e){if(e instanceof f)return l(e.text);if(e instanceof g||!(e instanceof c))return"";if(e.tag==="#fragment")return e.children.map(p).join("");const n=[];e.className&&n.push(`class="${u(e.className)}"`);const t=(s,i)=>{i&&n.push(`${s}="${u(i)}"`)};t("src",e.src),t("alt",e.alt),t("href",e.href),t("type",e.type),t("value",e.value),t("placeholder",e.placeholder);for(const[s,i]of Object.entries(e.attrs))n.push(`${s}="${u(i)}"`);const r=`<${e.tag}${n.length?" "+n.join(" "):""}>`;if(S.has(e.tag))return r;const o=e.children.length?e.children.map(p).join(""):l(e.textContent);return`${r}${o}</${e.tag}>`}function $(e){const n=new N,t=new c("div");return new Function("document","app","__params",e)(n,t,{}),t.children.map(p).join("")}async function v(e,n={}){const t={};for(const[r,o]of Object.entries(e)){const s=m(o,n);if(s.url){if(s.method!=="GET"){console.log(` \u2022 source "${r}" is ${s.method} \u2014 not fetched at build (runs client-side)`);continue}try{const i=await(await fetch(s.url,{headers:s.headers})).json(),d=x(i,s.at);t[r]=d,console.log(` \u2022 fetched source "${r}" (${d.length} rows) \u2190 ${s.url}`)}catch{}}}return t}export{v as fetchSources,$ as renderSsrBody};
1
+ import{sourceRequest as x,sourceRows as w}from"#engine/shared/source.js";const N=new Set(["img","input","br","hr","meta","link","source"]),u=e=>e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"),h=e=>u(e).replace(/"/g,"&quot;");class p{parentNode=null;remove(){const r=this.parentNode;if(!r)return;const t=r.children.indexOf(this);t>=0&&r.children.splice(t,1),this.parentNode=null}}class g extends p{constructor(t){super();this.text=t}text}class f extends p{constructor(t){super();this.text=t}text}class o extends p{constructor(t){super();this.tag=t}tag;children=[];attrs={};className="";src="";alt="";href="";type="";value="";placeholder="";rawHTML=null;text="";get childNodes(){return this.children}get textContent(){return this.text}set textContent(t){this.text=t;for(const n of this.children)n.parentNode=null;this.children=[]}set innerHTML(t){this.rawHTML=t;for(const n of this.children)n.parentNode=null;this.children=[],this.text=""}appendChild(t){if(t instanceof l){for(const n of[...t.children])this.appendChild(n);return t}return t.remove(),t.parentNode=this,this.children.push(t),this.text="",t}insertBefore(t,n){const c=t instanceof l?[...t.children]:[t];for(const i of c)i.remove();const s=n?this.children.indexOf(n):-1;this.children.splice(s>=0?s:this.children.length,0,...c);for(const i of c)i.parentNode=this;return t instanceof l&&(t.children=[]),t}replaceChildren(...t){for(const n of this.children)n.parentNode=null;this.children=[];for(const n of t)this.appendChild(n)}setAttribute(t,n){this.attrs[t]=n}addEventListener(){}createTHead(){const t=new o("thead");return this.appendChild(t),t}createTBody(){const t=new o("tbody");return this.appendChild(t),t}insertRow(){const t=new o("tr");return this.appendChild(t),t}}class l extends o{constructor(){super("#fragment")}}class S{createElement(r){return new o(r)}createComment(r){return new f(r)}createDocumentFragment(){return new l}createTextNode(r){return new g(r)}}function d(e){if(e instanceof g)return u(e.text);if(e instanceof f||!(e instanceof o))return"";if(e.tag==="#fragment")return e.children.map(d).join("");const r=[];e.className&&r.push(`class="${h(e.className)}"`);const t=(s,i)=>{i&&r.push(`${s}="${h(i)}"`)};t("src",e.src),t("alt",e.alt),t("href",e.href),t("type",e.type),t("value",e.value),t("placeholder",e.placeholder);for(const[s,i]of Object.entries(e.attrs))r.push(`${s}="${h(i)}"`);const n=`<${e.tag}${r.length?" "+r.join(" "):""}>`;if(N.has(e.tag))return n;const c=e.rawHTML!=null?e.rawHTML:e.children.length?e.children.map(d).join(""):u(e.textContent);return`${n}${c}</${e.tag}>`}function T(e,r=[]){const t=new S,n=new o("div"),c=(i,a,m)=>(r.push({adapter:i,path:a,props:m}),`<!--mi:${r.length-1}-->`);return new Function("document","app","__params","__ssrIsland",e)(t,n,{},c),n.children.map(d).join("")}async function v(e,r={}){const t={};for(const[n,c]of Object.entries(e)){const s=x(c,r);if(s.url){if(s.method!=="GET"){console.log(` \u2022 source "${n}" is ${s.method} \u2014 not fetched at build (runs client-side)`);continue}try{const i=await(await fetch(s.url,{headers:s.headers})).json(),a=w(i,s.at);t[n]=a,console.log(` \u2022 fetched source "${n}" (${a.length} rows) \u2190 ${s.url}`)}catch{}}}return t}export{v as fetchSources,T as renderSsrBody};
@@ -1 +1 @@
1
- var c=(a=>(a.Ident="ident",a.String="string",a.Number="number",a.Ref="ref",a.Param="param",a.Punct="punct",a.Arrow="arrow",a.LArrow="larrow",a.Eq="eq",a.Neq="neq",a.Lte="lte",a.Gte="gte",a.FatArrow="fatarrow",a.Eof="eof",a))(c||{}),h=(r=>(r.BraceL="{",r.BraceR="}",r.ParenL="(",r.ParenR=")",r.BrackL="[",r.BrackR="]",r.Comma=",",r.Pipe="|",r.Colon=":",r.Assign="=",r.Lt="<",r.Gt=">",r.Dot=".",r.Slash="/",r.Plus="+",r.Star="*",r.Question="?",r.Dash="-",r))(h||{}),d=(e=>(e.Screen="screen",e.Entity="entity",e.State="state",e.Store="store",e.Get="get",e.Effect="effect",e.Action="action",e.Mutates="mutates",e.Mock="mock",e.Sources="sources",e.Routes="routes",e.Shell="shell",e.Part="part",e.Const="const",e.Theme="theme",e.Query="query",e.Param="param",e.Api="api",e.Body="body",e.Meta="meta",e.When="when",e.Each="each",e.As="as",e.If="if",e.Else="else",e.Guard="guard",e.Not="not",e.And="and",e.Or="or",e.Contains="contains",e.Required="required",e.Min="min",e.Max="max",e.True="true",e.False="false",e.Null="null",e))(d||{}),f=(t=>(t.Shell="Shell",t.Header="Header",t.Nav="Nav",t.Sidebar="Sidebar",t.Footer="Footer",t.Page="Page",t.Stack="Stack",t.Text="Text",t.Title="Title",t.Span="Span",t.Image="Image",t.Link="Link",t.Button="Button",t.Form="Form",t.SearchField="SearchField",t.DataTable="DataTable",t.RowAction="RowAction",t.Custom="Custom",t.When="When",t.Each="Each",t.Slot="slot",t))(f||{}),x=(l=>(l.Module="module",l.Store="store",l.Html="html",l.Ssr="ssr",l))(x||{}),S=(i=>(i.Text="text",i.Email="email",i.Enum="enum",i))(S||{}),A=(o=>(o.Or="or",o.And="and",o.Eq="==",o.Neq="!=",o.Lte="<=",o.Gte=">=",o.Lt="<",o.Gt=">",o.Contains="contains",o.Add="+",o.Sub="-",o.Mul="*",o.Div="/",o))(A||{}),R=(m=>(m.Not="not",m))(R||{}),b=(s=>(s.Lit="lit",s.Ref="ref",s.Un="un",s.Bin="bin",s.Tern="tern",s.Interp="interp",s))(b||{}),q=(n=>(n.Push="push",n.Set="set",n.Reset="reset",n.Remove="remove",n.Create="create",n.Update="update",n.Delete="delete",n.Refetch="refetch",n.Request="request",n.If="if",n))(q||{}),L=(u=>(u.Bind="bind",u.Submit="submit",u.Where="where",u.Columns="columns",u.Style="style",u.Class="class",u.Alt="alt",u.Inputs="inputs",u.On="on",u))(L||{});export{A as BOp,b as Ek,S as Fk,x as Fmt,d as Kw,L as Mod,f as Nt,h as Pn,q as StOp,c as Tk,R as UOp};
1
+ var c=(a=>(a.Ident="ident",a.String="string",a.Number="number",a.Ref="ref",a.Param="param",a.Punct="punct",a.Arrow="arrow",a.LArrow="larrow",a.Eq="eq",a.Neq="neq",a.Lte="lte",a.Gte="gte",a.FatArrow="fatarrow",a.Eof="eof",a))(c||{}),h=(r=>(r.BraceL="{",r.BraceR="}",r.ParenL="(",r.ParenR=")",r.BrackL="[",r.BrackR="]",r.Comma=",",r.Pipe="|",r.Colon=":",r.Assign="=",r.Lt="<",r.Gt=">",r.Dot=".",r.Slash="/",r.Plus="+",r.Star="*",r.Question="?",r.Dash="-",r))(h||{}),f=(e=>(e.Screen="screen",e.Entity="entity",e.State="state",e.Store="store",e.Get="get",e.Effect="effect",e.Action="action",e.Mutates="mutates",e.Mock="mock",e.Sources="sources",e.Routes="routes",e.Shell="shell",e.Part="part",e.Const="const",e.Theme="theme",e.Query="query",e.Param="param",e.Api="api",e.Body="body",e.Meta="meta",e.Use="use",e.From="from",e.Client="client",e.When="when",e.Each="each",e.As="as",e.If="if",e.Else="else",e.Guard="guard",e.Not="not",e.And="and",e.Or="or",e.Contains="contains",e.Required="required",e.Min="min",e.Max="max",e.True="true",e.False="false",e.Null="null",e))(f||{}),d=(t=>(t.Shell="Shell",t.Header="Header",t.Nav="Nav",t.Sidebar="Sidebar",t.Footer="Footer",t.Page="Page",t.Stack="Stack",t.Text="Text",t.Title="Title",t.Span="Span",t.Image="Image",t.Link="Link",t.Button="Button",t.Form="Form",t.SearchField="SearchField",t.DataTable="DataTable",t.RowAction="RowAction",t.Custom="Custom",t.When="When",t.Each="Each",t.Slot="slot",t))(d||{}),x=(l=>(l.Module="module",l.Store="store",l.Html="html",l.Ssr="ssr",l))(x||{}),S=(i=>(i.Text="text",i.Email="email",i.Enum="enum",i))(S||{}),A=(o=>(o.Or="or",o.And="and",o.Eq="==",o.Neq="!=",o.Lte="<=",o.Gte=">=",o.Lt="<",o.Gt=">",o.Contains="contains",o.Add="+",o.Sub="-",o.Mul="*",o.Div="/",o))(A||{}),R=(m=>(m.Not="not",m))(R||{}),b=(u=>(u.Lit="lit",u.Ref="ref",u.Un="un",u.Bin="bin",u.Tern="tern",u.Interp="interp",u.Call="call",u))(b||{}),C=(n=>(n.Push="push",n.Set="set",n.Reset="reset",n.Remove="remove",n.Create="create",n.Update="update",n.Delete="delete",n.Refetch="refetch",n.Request="request",n.If="if",n))(C||{}),q=(s=>(s.Bind="bind",s.Submit="submit",s.Where="where",s.Columns="columns",s.Style="style",s.Class="class",s.Alt="alt",s.Inputs="inputs",s.On="on",s))(q||{});export{A as BOp,b as Ek,S as Fk,x as Fmt,f as Kw,q as Mod,d as Nt,h as Pn,C as StOp,c as Tk,R as UOp};
@@ -1,4 +1,4 @@
1
- import{readFileSync as d,existsSync as g}from"node:fs";import{fileURLToPath as P}from"node:url";import{dirname as N,join as m}from"node:path";import{parse as W}from"#engine/lang/parse.js";import{toDoc as C}from"#engine/ir/flatten.js";import{load as D,loadAllParts as E,findStores as T}from"#engine/project/load.js";import{validate as H}from"#engine/ir/validate.js";import{compileModule as b,compileStore as F}from"#engine/compile/compile.js";import{mergeTheme as j}from"#engine/style/tokens.js";import{Nt as $}from"#engine/shared/vocab.js";const v="virtual:muten/runtime",h="virtual:muten/store/",w="virtual:muten/shell",M=N(P(import.meta.url)),J=d(m(M,"runtime.js"),"utf8");function L(y={}){const k=y.store!==!1;let u=j(y.theme),c=process.cwd(),O={},S={};const l={};let p,f=null;const I=()=>{const t=new Set,o=(p?.routes||[]).map(s=>{const n=JSON.stringify("/"+s.url.replace(/^\//,"")),i=`() => import(${JSON.stringify("/src/pages/"+s.page+"/"+s.page+".muten")})`;if(s.guard){const[a,r]=s.guard.split(".");return t.add(a),` ${n}: { load: ${i}, guard: () => ${s.guardNeg?"!":""}__store_${a}.${r}.get(), redirect: ${JSON.stringify(s.redirect)} },`}return` ${n}: { load: ${i} },`}).join(`
1
+ import{readFileSync as d,existsSync as g}from"node:fs";import{fileURLToPath as P}from"node:url";import{dirname as N,join as m}from"node:path";import{parse as W}from"#engine/lang/parse.js";import{toDoc as C}from"#engine/ir/flatten.js";import{load as D,loadAllParts as E,findStores as T}from"#engine/project/load.js";import{validate as H}from"#engine/ir/validate.js";import{compileModule as b,compileStore as F}from"#engine/compile/compile.js";import{mergeTheme as j}from"#engine/style/tokens.js";import{Nt as $}from"#engine/shared/vocab.js";const v="virtual:muten/runtime",h="virtual:muten/store/",w="virtual:muten/shell",M=N(P(import.meta.url)),J=d(m(M,"runtime.js"),"utf8");function L(y={}){const k=y.store!==!1;let p=j(y.theme),c=process.cwd(),O={},S={};const l={};let u,f=null;const I=()=>{const t=new Set,o=(u?.routes||[]).map(s=>{const n=JSON.stringify("/"+s.url.replace(/^\//,"")),i=`() => import(${JSON.stringify("/src/pages/"+s.page+"/"+s.page+".muten")})`;if(s.guard){const[a,r]=s.guard.split(".");return t.add(a),` ${n}: { load: ${i}, guard: () => ${s.guardNeg?"!":""}__store_${a}.${r}.get(), redirect: ${JSON.stringify(s.redirect)} },`}return` ${n}: { load: ${i} },`}).join(`
2
2
  `),e=[...t].map(s=>`import * as __store_${s} from '${h}${s}';`).join(`
3
3
  `);return`import * as __shell from '${w}';
4
4
  import { route, injectCss } from '${v}';
@@ -12,4 +12,4 @@ if (root) {
12
12
  injectCss(__shell.css);
13
13
  const outlet = __shell.mount(root);
14
14
  route(outlet, routes);
15
- }`},R=async()=>{O=await E(c);for(const e of Object.keys(l))delete l[e];if(k){S=T(m(c,"src"));for(const[e,s]of Object.entries(S))l[e]={state:Object.keys(s.state||{}),gets:Object.keys(s.gets||{}),actions:Object.keys(s.actions||{})}}const t=m(c,"src","app.muten");p=g(t)?W(d(t,"utf8")):void 0;const o=m(c,"theme.muten");u=g(o)?j(W(d(o,"utf8")).theme||{}):j(y.theme),f=null;for(const e of["styles.css","styles.scss"])if(g(m(c,"src",e))){f="/src/"+e;break}};return{name:"vite-plugin-muten",enforce:"pre",async configResolved(t){c=t.root,await R()},resolveId(t){if(t===v||t===w||t.startsWith(h))return"\0"+t},load(t){if(t==="\0"+v)return J;if(t.startsWith("\0"+h)){const o=S[t.slice(("\0"+h).length)];if(o)return F({state:o.state||{},gets:o.gets||{},actions:o.actions||{},effects:o.effects||[],entities:o.entities||{}},o.mock||{},o.sources||{})}if(t==="\0"+w){const o=p?.shell||{type:$.Shell,props:{},children:[{type:$.Slot,props:{}}]},e=C({screen:"shell",entities:{},state:{},actions:{},tree:o});return b(e,{},"",{},{},{stores:l,theme:u})}},async transform(t,o){if(!o.endsWith(".muten"))return null;if(o.replace(/\\/g,"/").endsWith("/src/app.muten"))return{code:I(),map:null};const e=await D(o,O),{ok:s,diagnostics:n}=H(e.doc,{parts:e.partNames,stores:Object.keys(l),theme:u});if(!s)throw new Error("muten: "+n.map(r=>r.message).join(" \xB7 "));const i=[...new Set(Object.values(e.doc.nodes).filter(r=>r.type===$.Custom).map(r=>r.props?.component))],a={};for(const r of i){if(!r)continue;const _=m(c,"src","components",r+".js");g(_)&&(a[r]=d(_,"utf8"))}return{code:b(e.doc,e.data,e.styles.css,a,e.sources,{stores:l,theme:u,api:p?.api||{}}),map:null}},handleHotUpdate(t){(t.file.endsWith(".muten")||t.file.endsWith(".store"))&&t.server.ws.send({type:"full-reload"})},configureServer(t){const o=s=>{const n=s.replace(/\\/g,"/");return n.includes("/parts/")&&n.endsWith(".muten")||n.endsWith(".store")||n.endsWith("/app.muten")||n.endsWith("/theme.muten")||n.endsWith("/styles.css")||n.endsWith("/styles.scss")},e=s=>{o(s)&&R().then(()=>t.ws.send({type:"full-reload"}))};t.watcher.on("add",e),t.watcher.on("change",e),t.watcher.on("unlink",e),t.middlewares.use((s,n,i)=>{if((s.url||"").split("?")[0]!=="/src/app.muten"){i();return}t.transformRequest("/src/app.muten").then(a=>{if(!a){i();return}n.setHeader("Content-Type","text/javascript"),n.end(a.code)},i)})}}}export{L as default};
15
+ }`},R=async()=>{O=await E(c);for(const e of Object.keys(l))delete l[e];if(k){S=T(m(c,"src"));for(const[e,s]of Object.entries(S))l[e]={state:Object.keys(s.state||{}),gets:Object.keys(s.gets||{}),actions:Object.keys(s.actions||{})}}const t=m(c,"src","app.muten");u=g(t)?W(d(t,"utf8")):void 0;const o=m(c,"theme.muten");p=g(o)?j(W(d(o,"utf8")).theme||{}):j(y.theme),f=null;for(const e of["styles.css","styles.scss"])if(g(m(c,"src",e))){f="/src/"+e;break}};return{name:"vite-plugin-muten",enforce:"pre",async configResolved(t){c=t.root,await R()},resolveId(t){if(t===v||t===w||t.startsWith(h))return"\0"+t},load(t){if(t==="\0"+v)return J;if(t.startsWith("\0"+h)){const o=S[t.slice(("\0"+h).length)];if(o)return F({state:o.state||{},gets:o.gets||{},actions:o.actions||{},effects:o.effects||[],entities:o.entities||{}},o.mock||{},o.sources||{})}if(t==="\0"+w){const o=u?.shell||{type:$.Shell,props:{},children:[{type:$.Slot,props:{}}]},e=C({...u||{},screen:"shell",entities:{},state:{},actions:{},tree:o});return b(e,{},"",{},{},{stores:l,theme:p})}},async transform(t,o){if(!o.endsWith(".muten"))return null;if(o.replace(/\\/g,"/").endsWith("/src/app.muten"))return{code:I(),map:null};const e=await D(o,O),{ok:s,diagnostics:n}=H(e.doc,{parts:e.partNames,stores:Object.keys(l),theme:p});if(!s)throw new Error("muten: "+n.map(r=>r.message).join(" \xB7 "));const i=[...new Set(Object.values(e.doc.nodes).filter(r=>r.type===$.Custom).map(r=>r.props?.component))],a={};for(const r of i){if(!r)continue;const _=m(c,"src","components",r+".js");g(_)&&(a[r]=d(_,"utf8"))}return{code:b(e.doc,e.data,e.styles.css,a,e.sources,{stores:l,theme:p,api:u?.api||{}}),map:null}},handleHotUpdate(t){(t.file.endsWith(".muten")||t.file.endsWith(".store"))&&t.server.ws.send({type:"full-reload"})},configureServer(t){const o=s=>{const n=s.replace(/\\/g,"/");return n.includes("/parts/")&&n.endsWith(".muten")||n.endsWith(".store")||n.endsWith("/app.muten")||n.endsWith("/theme.muten")||n.endsWith("/styles.css")||n.endsWith("/styles.scss")},e=s=>{o(s)&&R().then(()=>t.ws.send({type:"full-reload"}))};t.watcher.on("add",e),t.watcher.on("change",e),t.watcher.on("unlink",e),t.middlewares.use((s,n,i)=>{if((s.url||"").split("?")[0]!=="/src/app.muten"){i();return}t.transformRequest("/src/app.muten").then(a=>{if(!a){i();return}n.setHeader("Content-Type","text/javascript"),n.end(a.code)},i)})}}}export{L as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@muten/core",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "AI-first frontend framework — compiles .muten files to vanilla JS + signals.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -42,7 +42,7 @@
42
42
  "scripts": {
43
43
  "prepare": "npm run build",
44
44
  "build": "tsc && node --experimental-strip-types esbuild.ts",
45
- "test": "npm run build && node --experimental-strip-types test/parse.ts && node --experimental-strip-types test/expr.ts && node --experimental-strip-types test/parts.ts && node --experimental-strip-types test/diagnostics.ts && node --experimental-strip-types test/routes.ts && node --experimental-strip-types test/params.ts && node --experimental-strip-types test/ssr.ts && node --experimental-strip-types test/writes.ts && node --experimental-strip-types test/dynamics.ts && node --experimental-strip-types test/runtime.ts && node --experimental-strip-types test/lang.ts && node --experimental-strip-types test/forms.ts && node --experimental-strip-types test/smoke.ts"
45
+ "test": "npm run build && node --experimental-strip-types test/parse.ts && node --experimental-strip-types test/expr.ts && node --experimental-strip-types test/parts.ts && node --experimental-strip-types test/diagnostics.ts && node --experimental-strip-types test/routes.ts && node --experimental-strip-types test/params.ts && node --experimental-strip-types test/ssr.ts && node --experimental-strip-types test/writes.ts && node --experimental-strip-types test/dynamics.ts && node --experimental-strip-types test/runtime.ts && node --experimental-strip-types test/lang.ts && node --experimental-strip-types test/forms.ts && node --experimental-strip-types test/smoke.ts && node --experimental-strip-types test/print.ts && node --experimental-strip-types test/externs.ts && node --experimental-strip-types test/islands.ts"
46
46
  },
47
47
  "optionalDependencies": {
48
48
  "sass": "^1.101.0"