view-contracts 0.5.0 → 0.5.1
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
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
**Contract-first UI development for AI-native applications.**
|
|
4
4
|
|
|
5
|
+

|
|
6
|
+
|
|
5
7
|
view-contracts is a UI definition system that separates:
|
|
6
8
|
|
|
7
9
|
- **Screen semantics** → OpenAPI
|
|
@@ -112,6 +114,7 @@ The generated output is deterministic.
|
|
|
112
114
|
|
|
113
115
|
- React generation (`generated/react/` — screens, `runtime.ts`, `theme.css`, …)
|
|
114
116
|
- SwiftUI generation (`generated/swiftui/` — full DSL element coverage, theme variant dictionary, `SampleData.json` from mock YAML SSoT)
|
|
117
|
+
- Jetpack Compose generation (`generated/compose/` — full DSL element coverage, theme variant system, Android preview app)
|
|
115
118
|
- Design tokens + theme variants (`views/design/tokens.yaml`, `theme.yaml`)
|
|
116
119
|
- Live DSL preview (`dev:preview`) with `views/preview/*.mock.yaml` sample data
|
|
117
120
|
- Extension registry (`extensions.yaml`) with plain HTML/CSS implementations
|
|
@@ -119,13 +122,13 @@ The generated output is deterministic.
|
|
|
119
122
|
- Standalone app scaffold (`.vite/` per example project)
|
|
120
123
|
- Table DSL with configurable `separator` / `separatorColor` / `border` / `borderColor`
|
|
121
124
|
- Dynamic variant resolution via `IrConcat` (runtime theme dictionary in SwiftUI)
|
|
125
|
+
- 3-platform visual parity (iOS / Android / React from same DSL definition)
|
|
122
126
|
|
|
123
127
|
### Planned
|
|
124
128
|
|
|
125
|
-
- Jetpack Compose renderer
|
|
126
129
|
- PDF renderer
|
|
127
130
|
|
|
128
|
-
The contract model is designed for multi-target generation. React and
|
|
131
|
+
The contract model is designed for multi-target generation. React, SwiftUI, and Jetpack Compose are production-ready targets.
|
|
129
132
|
|
|
130
133
|
---
|
|
131
134
|
|
|
@@ -278,7 +281,7 @@ packages/
|
|
|
278
281
|
renderers/
|
|
279
282
|
├── react/ # lowering, element map, runtime, templates
|
|
280
283
|
├── swiftui/ # lowering, theme dictionary, modifier cascade
|
|
281
|
-
└── compose/ #
|
|
284
|
+
└── compose/ # lowering, theme system, modifier pipeline
|
|
282
285
|
|
|
283
286
|
docs/
|
|
284
287
|
├── design-tokens-and-theme.md
|
|
@@ -1084,7 +1084,7 @@ ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
|
|
1084
1084
|
`,s=ga(t,"entry.tsx");return await qp(s,o,"utf8"),s}async function Fp(t,e,r){let n=await Ye(t),o=(await Pt(n.bindings)).views.find(P=>P.screenName===e);if(!o)throw new Error(`No view binding for screen "${e}"`);if(!o.targets.includes("react"))throw new Error(`Screen "${e}" is not a react target`);let s=ye(t),[a,c,l]=await Promise.all([tb(t,e),rv(t,n,e),ji(n,t)]),d=BR(o.exportName),u=Ep(e),f=ga(qR(),`view-contracts-preview-${process.pid}`);await DR(f,{recursive:!0});let m=await UR(f,a,c,d,u),g=Re(),y=[er(s,"node_modules"),er(g,"node_modules")],w=await FR({entryPoints:[m],bundle:!0,write:!1,format:"esm",platform:"browser",target:"es2022",jsx:"automatic",jsxImportSource:"react",absWorkingDir:s,nodePaths:y,alias:HR(s),logLevel:"silent"});if(w.errors.length>0)throw new Error(w.errors.map(P=>P.text).join(`
|
|
1085
1085
|
`));let v=w.outputFiles[0]?.text??"";return{html:WR(),themeCss:l,bundleJs:v,version:r}}async function rb(t,e){let r=await Pt((await Ye(t)).bindings);if(e){let i=r.views.find(o=>o.screenName===e);if(!i)throw new Error(`Unknown screen "${e}" in view-bindings.yaml`);if(!i.targets.includes("react"))throw new Error(`Screen "${e}" is not a react target`);return e}let n=r.views.find(i=>i.targets.includes("react"));if(!n)throw new Error("No react target views in view-bindings.yaml");return n.screenName}jn();function so(t,e,r,n){t.writeHead(e,{"Content-Type":n,"Cache-Control":"no-store"}),t.end(r)}function JR(t,e,r){let n=t.url?.split("?")[0]??"/";if(n==="/"||n==="/index.html"){so(e,200,r.html,"text/html; charset=utf-8");return}if(n==="/bundle.js"){so(e,200,r.bundleJs,"text/javascript; charset=utf-8");return}if(n==="/theme.css"){so(e,200,r.themeCss,"text/css; charset=utf-8");return}if(n==="/__vc-reload"){so(e,200,JSON.stringify({version:r.version}),"application/json");return}so(e,404,"Not found","text/plain; charset=utf-8")}async function KR(t,e,r){let n=await Ye(t),i=ye(t),{tokensPath:o,themePath:s}=nr(n),a=await nv(t,n),c=Gv(),l=[Hr(i,n.viewsDir),Hr(i,o),Hr(i,s),a,c];n.extensions&&l.push(Hr(i,n.extensions)),n.design?.extensionsCss&&l.push(Hr(i,n.design.extensionsCss)),l.push(Hr(i,"contracts/view-models.ts")),l.push(Hr(i,"src/components/extensions.tsx"));let d,u=()=>{d&&clearTimeout(d),d=setTimeout(()=>r(),150)},f=[];for(let g of l)await Ln(g)&&f.push(g);let m=f.map(g=>GR(g,{recursive:!0},()=>u()));return()=>{d&&clearTimeout(d);for(let g of m)g.close()}}async function nb(t){let e=t.port??5173,r=t.host??"127.0.0.1",n=await rb(t.configPath,t.screen),i=0,o=await Fp(t.configPath,n,i),s=!1,a=!1,c=async()=>{if(s){a=!0;return}s=!0;try{i+=1,o=await Fp(t.configPath,n,i),console.log(`[preview] rebuilt (v${i})`)}catch(f){let m=f instanceof Error?f.message:String(f);console.error(`[preview] compile error:
|
|
1086
1086
|
${m}`)}finally{s=!1,a&&(a=!1,c())}},l=zR((f,m)=>JR(f,m,o));await new Promise((f,m)=>{l.once("error",m),l.listen(e,r,()=>f())});let d=`http://${r}:${e}/`;console.log(`[preview] ${n} \u2192 ${d}`),console.log("[preview] watching views/ and design files (Ctrl+C to stop)");let u=await KR(t.configPath,n,()=>{c()});await new Promise(f=>{let m=()=>{u(),l.close(()=>f())};process.once("SIGINT",m),process.once("SIGTERM",m)})}async function Hp(t){if(!t.extensions)return{allowlistExtra:t.allowlistExtra??[]};let e=await ss(t.extensions),r=[...new Set([...t.allowlistExtra??[],...Object.keys(e.extensions)])];return{extensionRegistry:e,allowlistExtra:r}}async function ib(t){await new Promise((e,r)=>{let n=ZR("npx",["micro-contracts",...t],{stdio:"inherit"});n.on("error",r),n.on("close",i=>{i===0?e():r(new Error(`micro-contracts exited with code ${i}`))})})}async function Bp(t){let e=await Ye(t),r=await Pt(e.bindings),{extensionRegistry:n,allowlistExtra:i}=await Hp(e),o=ye(t),{screens:s,partials:a}=await Sd(r.views.map(c=>({sourcePath:Vt(o,c.source),exportName:c.exportName,screenName:c.screenName,targets:c.targets})),{allowlistExtra:i,extensionRegistry:n});return{screens:new Map(r.views.map((c,l)=>[c.screenName,s[l]])),partials:a}}function Wr(t,e){let r=QR(ab(t),e).replace(/\\/g,"/");return r.startsWith(".")||(r=`./${r}`),r.replace(/\.tsx?$/,".js")}async function ob(t,e,r){let n=await Ye(t),i=ye(t),{allowlistExtra:o}=await Hp(n),s=await Pt(n.bindings);for(let a of r){let c=Vt(n.generated.reactDir,"components",`${a.exportName}.generated.tsx`),l=Wr(c,Vt(n.generated.reactDir,"runtime.ts")),d=Wr(c,Vt(n.generated.reactDir,"extensions.generated.ts")),u=Wr(c,Vt(i,"contracts/view-models.ts"));await jp(a,c,{runtimeImportFrom:l,extensionsImportFrom:d,contractsImportFrom:u,allowlistExtra:o}),console.log(` ${a.exportName} \u2192 ${c}`)}for(let a of s.views){if(!a.targets.includes("react"))continue;let c=e.get(a.screenName);if(!c)throw new Error(`Missing View IR for ${a.screenName}`);let l=zd(n,a),d=Wr(l,Vt(n.generated.reactDir,"runtime.ts")),u=Wr(l,Vt(n.generated.reactDir,"extensions.generated.ts")),f=Wr(l,Vt(i,"contracts/view-models.ts")),m=new Map;for(let g of r){let y=Kv(n.generated.reactDir,g);m.set(g.exportName,Wr(l,y))}await jp(c,l,{runtimeImportFrom:d,extensionsImportFrom:u,contractsImportFrom:f,allowlistExtra:o,viewRefImportFrom:m}),console.log(` ${a.exportName} \u2192 ${l}`)}}async function sb(t){let e=await Ye(t),r=await Pt(e.bindings),n=ye(t),o=["// Auto-generated view bridge registry \u2014 DO NOT EDIT",`// Generated from ${Gd(n,e.bindings)}`,"","export const viewBridgeRegistry = {"];for(let s of r.views)o.push(` ${s.screenConst}: {`),o.push(` id: ${JSON.stringify(s.id)},`),o.push(` screenId: ${JSON.stringify(s.screenId)},`),o.push(` screenName: ${JSON.stringify(s.screenName)},`),o.push(` route: ${JSON.stringify(s.route)},`),o.push(` operationId: ${JSON.stringify(s.operationId)},`),o.push(` viewModel: ${JSON.stringify(s.viewModel)},`),o.push(` source: ${JSON.stringify(s.source)},`),o.push(` targets: ${JSON.stringify(s.targets)},`),o.push(" },");o.push("} as const;",""),o.push("export type ViewBridgeKey = keyof typeof viewBridgeRegistry;",""),await XR(ab(Vt(e.generated.bridge)),{recursive:!0}),await YR(Vt(e.generated.bridge),o.join(`
|
|
1087
|
-
`),"utf-8"),console.log(`Bridge registry \u2192 ${e.generated.bridge} (${r.views.length} view(s))`)}var cb={compile:async(t,e)=>{let{screens:r,partials:n}=await Bp(await rr(t.config));console.log(`Compiled ${r.size} screen(s) and ${n.length} partial(s) to View IR (in-memory)`)},renderReact:async(t,e)=>{let r=await rr(t.config),{screens:n,partials:i}=await Bp(r);await ob(r,n,i)},renderBridge:async(t,e)=>{await sb(await rr(t.config))},build:async(t,e)=>{let r=await rr(t.config),n=await Ye(r),i=await Pt(n.bindings),{screens:o,partials:s}=await Bp(r);console.log(`Compiled ${o.size} screen(s) and ${s.length} partial(s) to View IR (in-memory)`);let{tokensPath:a,themePath:c}=nr(n),{tokenIR:l,themeIR:d}=await $n(a,c);await Nr(n,r);let{allowlistExtra:u}=await Hp(n),f=[...o.values(),...s];await fa(f,u),Li(f,d),await Yv(r,n),await ob(r,o,s),await sb(r),await Xv(r,n,i,{theme:d,tokens:l},{screens:o,partials:s}),await Qv(r,n,i,{theme:d,tokens:l},{screens:o,partials:s}),await eb(r,n,i)},contractsGenerate:async(t,e)=>{let r=await Ye(await rr(t.config));await ib(["generate","-c",r.microContractsConfig]),console.log("Contracts generated via micro-contracts")},contractsCheck:async(t,e)=>{let r=await Ye(await rr(t.config));await ib(["lint","-c",r.microContractsConfig])},preview:async(t,e)=>{try{await nb({configPath:await rr(t.config),port:t.port!=null?Number(t.port):void 0,host:t.host,screen:t.screen})}catch(r){console.error(r instanceof Error?r.message:r),process.exit(1)}}};var eT={version:"0.5.
|
|
1087
|
+
`),"utf-8"),console.log(`Bridge registry \u2192 ${e.generated.bridge} (${r.views.length} view(s))`)}var cb={compile:async(t,e)=>{let{screens:r,partials:n}=await Bp(await rr(t.config));console.log(`Compiled ${r.size} screen(s) and ${n.length} partial(s) to View IR (in-memory)`)},renderReact:async(t,e)=>{let r=await rr(t.config),{screens:n,partials:i}=await Bp(r);await ob(r,n,i)},renderBridge:async(t,e)=>{await sb(await rr(t.config))},build:async(t,e)=>{let r=await rr(t.config),n=await Ye(r),i=await Pt(n.bindings),{screens:o,partials:s}=await Bp(r);console.log(`Compiled ${o.size} screen(s) and ${s.length} partial(s) to View IR (in-memory)`);let{tokensPath:a,themePath:c}=nr(n),{tokenIR:l,themeIR:d}=await $n(a,c);await Nr(n,r);let{allowlistExtra:u}=await Hp(n),f=[...o.values(),...s];await fa(f,u),Li(f,d),await Yv(r,n),await ob(r,o,s),await sb(r),await Xv(r,n,i,{theme:d,tokens:l},{screens:o,partials:s}),await Qv(r,n,i,{theme:d,tokens:l},{screens:o,partials:s}),await eb(r,n,i)},contractsGenerate:async(t,e)=>{let r=await Ye(await rr(t.config));await ib(["generate","-c",r.microContractsConfig]),console.log("Contracts generated via micro-contracts")},contractsCheck:async(t,e)=>{let r=await Ye(await rr(t.config));await ib(["lint","-c",r.microContractsConfig])},preview:async(t,e)=>{try{await nb({configPath:await rr(t.config),port:t.port!=null?Number(t.port):void 0,host:t.host,screen:t.screen})}catch(r){console.error(r instanceof Error?r.message:r),process.exit(1)}}};var eT={version:"0.5.1"};ad(cb,eT.version).parseAsync(process.argv).catch(t=>{console.error(t instanceof Error?t.message:t),process.exit(1)});
|
|
1088
1088
|
/*! Bundled license information:
|
|
1089
1089
|
|
|
1090
1090
|
js-yaml/dist/js-yaml.mjs:
|