@mocklane/core 1.0.0 → 1.0.2

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
@@ -22,24 +22,42 @@ Mocklane turns a pasted data model and a plain-language request into a structure
22
22
  - Node.js 20 or newer
23
23
  - A modern browser with WebGPU for Transformers.js and WebLLM
24
24
  - Enough browser storage and memory for the selected local model
25
+ - pnpm 9 when installing from GitHub source
25
26
 
26
27
  Native Browser AI is shown only when the browser exposes the `LanguageModel` API. The first Transformers.js or WebLLM load downloads model assets; later loads can reuse the browser cache.
27
28
 
28
- ## Quick Start
29
+ ## Installation
30
+
31
+ Choose either npm or the GitHub source repository. Both launch the same `mocklane` CLI.
29
32
 
30
- Run Mocklane without installing it globally:
33
+ ### Option 1: npm
34
+
35
+ Run Mocklane once with `npx`:
31
36
 
32
37
  ```bash
33
- npx mocklane
38
+ npx @mocklane/core
34
39
  ```
35
40
 
36
- For a global installation:
41
+ Or install the package globally and run the installed `mocklane` command:
37
42
 
38
43
  ```bash
39
- npm install --global mocklane
44
+ npm install --global @mocklane/core
40
45
  mocklane
41
46
  ```
42
47
 
48
+ ### Option 2: GitHub
49
+
50
+ Clone the source, install dependencies, and launch the locally built CLI:
51
+
52
+ ```bash
53
+ git clone https://github.com/aasispaudel/Mocklane.git
54
+ cd Mocklane
55
+ pnpm install
56
+ pnpm mocklane:build
57
+ ```
58
+
59
+ ## Quick Start
60
+
43
61
  Mocklane starts two local servers and opens the browser workspace automatically:
44
62
 
45
63
  ```text
@@ -48,6 +66,8 @@ Mock API proxy: http://localhost:4000
48
66
  Upstream server: none (standalone mode)
49
67
  ```
50
68
 
69
+ If the default ports are busy, Mocklane automatically tries the next local pair, such as `4002/4003`, then `4004/4005`.
70
+
51
71
  In the browser:
52
72
 
53
73
  1. Select an AI provider and model.
@@ -133,7 +153,7 @@ Invalid JSON cannot be saved. Mocklane displays the parser error in the editor s
133
153
  Standalone mode is the default:
134
154
 
135
155
  ```bash
136
- npx mocklane
156
+ npx @mocklane/core
137
157
  ```
138
158
 
139
159
  Registered routes return their configured mock responses. Unmatched routes return `404`:
@@ -153,7 +173,7 @@ Mocks are matched by HTTP method and exact pathname. Query strings do not affect
153
173
  Use proxy mode when an application already has a backend and only selected routes should be mocked:
154
174
 
155
175
  ```bash
156
- npx mocklane --target http://localhost:8000
176
+ npx @mocklane/core --target http://localhost:8000
157
177
  ```
158
178
 
159
179
  Point the application at `http://localhost:4000` instead of the upstream server:
@@ -192,19 +212,20 @@ Options:
192
212
  ### Custom ports
193
213
 
194
214
  ```bash
195
- npx mocklane --proxy-port 5000 --ui-port 5001
215
+ npx @mocklane/core --proxy-port 5000 --ui-port 5001
196
216
  ```
197
217
 
198
218
  ### Proxy with custom ports
199
219
 
200
220
  ```bash
201
- npx mocklane \
221
+ npx @mocklane/core \
202
222
  --target http://localhost:8000 \
203
223
  --proxy-port 5000 \
204
224
  --ui-port 5001
205
225
  ```
206
226
 
207
227
  The proxy and UI ports must be different. Both servers bind to `127.0.0.1` and are not exposed to the local network.
228
+ When custom ports are provided, Mocklane uses those exact ports.
208
229
 
209
230
  ## Local Control API
210
231
 
@@ -0,0 +1,178 @@
1
+ import{r as c,j as a,w as Mt}from"./jsx-runtime-BZbza59V.js";/**
2
+ * @license lucide-react v1.17.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const Ke=(...e)=>e.filter((t,r,n)=>!!t&&t.trim()!==""&&n.indexOf(t)===r).join(" ").trim();/**
7
+ * @license lucide-react v1.17.0 - ISC
8
+ *
9
+ * This source code is licensed under the ISC license.
10
+ * See the LICENSE file in the root directory of this source tree.
11
+ */const Ct=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase();/**
12
+ * @license lucide-react v1.17.0 - ISC
13
+ *
14
+ * This source code is licensed under the ISC license.
15
+ * See the LICENSE file in the root directory of this source tree.
16
+ */const Lt=e=>e.replace(/^([A-Z])|[\s-_]+(\w)/g,(t,r,n)=>n?n.toUpperCase():r.toLowerCase());/**
17
+ * @license lucide-react v1.17.0 - ISC
18
+ *
19
+ * This source code is licensed under the ISC license.
20
+ * See the LICENSE file in the root directory of this source tree.
21
+ */const Je=e=>{const t=Lt(e);return t.charAt(0).toUpperCase()+t.slice(1)};/**
22
+ * @license lucide-react v1.17.0 - ISC
23
+ *
24
+ * This source code is licensed under the ISC license.
25
+ * See the LICENSE file in the root directory of this source tree.
26
+ */var me={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};/**
27
+ * @license lucide-react v1.17.0 - ISC
28
+ *
29
+ * This source code is licensed under the ISC license.
30
+ * See the LICENSE file in the root directory of this source tree.
31
+ */const At=e=>{for(const t in e)if(t.startsWith("aria-")||t==="role"||t==="title")return!0;return!1},Tt=c.createContext({}),Ot=()=>c.useContext(Tt),Et=c.forwardRef(({color:e,size:t,strokeWidth:r,absoluteStrokeWidth:n,className:i="",children:l,iconNode:d,...o},u)=>{const{size:p=24,strokeWidth:y=2,absoluteStrokeWidth:h=!1,color:g="currentColor",className:b=""}=Ot()??{},j=n??h?Number(r??y)*24/Number(t??p):r??y;return c.createElement("svg",{ref:u,...me,width:t??p??me.width,height:t??p??me.height,stroke:e??g,strokeWidth:j,className:Ke("lucide",b,i),...!l&&!At(o)&&{"aria-hidden":"true"},...o},[...d.map(([se,W])=>c.createElement(se,W)),...Array.isArray(l)?l:[l]])});/**
32
+ * @license lucide-react v1.17.0 - ISC
33
+ *
34
+ * This source code is licensed under the ISC license.
35
+ * See the LICENSE file in the root directory of this source tree.
36
+ */const v=(e,t)=>{const r=c.forwardRef(({className:n,...i},l)=>c.createElement(Et,{ref:l,iconNode:t,className:Ke(`lucide-${Ct(Je(e))}`,`lucide-${e}`,n),...i}));return r.displayName=Je(e),r};/**
37
+ * @license lucide-react v1.17.0 - ISC
38
+ *
39
+ * This source code is licensed under the ISC license.
40
+ * See the LICENSE file in the root directory of this source tree.
41
+ */const It=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],Pt=v("check",It);/**
42
+ * @license lucide-react v1.17.0 - ISC
43
+ *
44
+ * This source code is licensed under the ISC license.
45
+ * See the LICENSE file in the root directory of this source tree.
46
+ */const _t=[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]],De=v("chevron-down",_t);/**
47
+ * @license lucide-react v1.17.0 - ISC
48
+ *
49
+ * This source code is licensed under the ISC license.
50
+ * See the LICENSE file in the root directory of this source tree.
51
+ */const $t=[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]],Rt=v("copy",$t);/**
52
+ * @license lucide-react v1.17.0 - ISC
53
+ *
54
+ * This source code is licensed under the ISC license.
55
+ * See the LICENSE file in the root directory of this source tree.
56
+ */const St=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],Jt=v("download",St);/**
57
+ * @license lucide-react v1.17.0 - ISC
58
+ *
59
+ * This source code is licensed under the ISC license.
60
+ * See the LICENSE file in the root directory of this source tree.
61
+ */const Dt=[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]],Gt=v("loader-circle",Dt);/**
62
+ * @license lucide-react v1.17.0 - ISC
63
+ *
64
+ * This source code is licensed under the ISC license.
65
+ * See the LICENSE file in the root directory of this source tree.
66
+ */const Wt=[["path",{d:"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401",key:"kfwtm"}]],Bt=v("moon",Wt);/**
67
+ * @license lucide-react v1.17.0 - ISC
68
+ *
69
+ * This source code is licensed under the ISC license.
70
+ * See the LICENSE file in the root directory of this source tree.
71
+ */const Ut=[["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",key:"1a8usu"}],["path",{d:"m15 5 4 4",key:"1mk7zo"}]],zt=v("pencil",Ut);/**
72
+ * @license lucide-react v1.17.0 - ISC
73
+ *
74
+ * This source code is licensed under the ISC license.
75
+ * See the LICENSE file in the root directory of this source tree.
76
+ */const Ft=[["path",{d:"M6.3 20.3a2.4 2.4 0 0 0 3.4 0L12 18l-6-6-2.3 2.3a2.4 2.4 0 0 0 0 3.4Z",key:"goz73y"}],["path",{d:"m2 22 3-3",key:"19mgm9"}],["path",{d:"M7.5 13.5 10 11",key:"7xgeeb"}],["path",{d:"M10.5 16.5 13 14",key:"10btkg"}],["path",{d:"m18 3-4 4h6l-4 4",key:"16psg9"}]],qt=v("plug-zap",Ft);/**
77
+ * @license lucide-react v1.17.0 - ISC
78
+ *
79
+ * This source code is licensed under the ISC license.
80
+ * See the LICENSE file in the root directory of this source tree.
81
+ */const Ht=[["path",{d:"M12 22v-5",key:"1ega77"}],["path",{d:"M15 8V2",key:"18g5xt"}],["path",{d:"M17 8a1 1 0 0 1 1 1v4a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1z",key:"1xoxul"}],["path",{d:"M9 8V2",key:"14iosj"}]],Ge=v("plug",Ht);/**
82
+ * @license lucide-react v1.17.0 - ISC
83
+ *
84
+ * This source code is licensed under the ISC license.
85
+ * See the LICENSE file in the root directory of this source tree.
86
+ */const Vt=[["path",{d:"M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z",key:"1c8476"}],["path",{d:"M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7",key:"1ydtos"}],["path",{d:"M7 3v4a1 1 0 0 0 1 1h7",key:"t51u73"}]],Kt=v("save",Vt);/**
87
+ * @license lucide-react v1.17.0 - ISC
88
+ *
89
+ * This source code is licensed under the ISC license.
90
+ * See the LICENSE file in the root directory of this source tree.
91
+ */const Qt=[["circle",{cx:"12",cy:"12",r:"4",key:"4exip2"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M12 20v2",key:"1lh1kg"}],["path",{d:"m4.93 4.93 1.41 1.41",key:"149t6j"}],["path",{d:"m17.66 17.66 1.41 1.41",key:"ptbguv"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"M20 12h2",key:"1q8mjw"}],["path",{d:"m6.34 17.66-1.41 1.41",key:"1m8zz5"}],["path",{d:"m19.07 4.93-1.41 1.41",key:"1shlcs"}]],Zt=v("sun",Qt);/**
92
+ * @license lucide-react v1.17.0 - ISC
93
+ *
94
+ * This source code is licensed under the ISC license.
95
+ * See the LICENSE file in the root directory of this source tree.
96
+ */const Xt=[["path",{d:"m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72",key:"ul74o6"}],["path",{d:"m14 7 3 3",key:"1r5n42"}],["path",{d:"M5 6v4",key:"ilb8ba"}],["path",{d:"M19 14v4",key:"blhpug"}],["path",{d:"M10 2v2",key:"7u0qdc"}],["path",{d:"M7 8H3",key:"zfb6yr"}],["path",{d:"M21 16h-4",key:"1cnmox"}],["path",{d:"M11 3H9",key:"1obp7u"}]],Yt=v("wand-sparkles",Xt);/**
97
+ * @license lucide-react v1.17.0 - ISC
98
+ *
99
+ * This source code is licensed under the ISC license.
100
+ * See the LICENSE file in the root directory of this source tree.
101
+ */const er=[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]],tr=v("x",er);function We({id:e,label:t,meta:r,value:n,onChange:i,placeholder:l,spellCheck:d=!0,variant:o="default"}){return a.jsxs("div",{className:`field-group ${o==="compact"?"prompt-group":""}`,children:[a.jsxs("div",{className:"field-heading",children:[a.jsx("label",{htmlFor:e,children:t}),a.jsx("span",{children:r})]}),a.jsx("textarea",{id:e,value:n,spellCheck:d,placeholder:l,onChange:u=>i(u.target.value)})]})}const rr="modulepreload",ar=function(e){return"/"+e},Be={},Qe=function(t,r,n){let i=Promise.resolve();if(r&&r.length>0){let d=function(p){return Promise.all(p.map(y=>Promise.resolve(y).then(h=>({status:"fulfilled",value:h}),h=>({status:"rejected",reason:h}))))};document.getElementsByTagName("link");const o=document.querySelector("meta[property=csp-nonce]"),u=(o==null?void 0:o.nonce)||(o==null?void 0:o.getAttribute("nonce"));i=d(r.map(p=>{if(p=ar(p),p in Be)return;Be[p]=!0;const y=p.endsWith(".css"),h=y?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${p}"]${h}`))return;const g=document.createElement("link");if(g.rel=y?"stylesheet":rr,y||(g.as="script"),g.crossOrigin="",g.href=p,u&&g.setAttribute("nonce",u),document.head.appendChild(g),y)return new Promise((b,j)=>{g.addEventListener("load",b),g.addEventListener("error",()=>j(new Error(`Unable to preload CSS for ${p}`)))})}))}function l(d){const o=new Event("vite:preloadError",{cancelable:!0});if(o.payload=d,window.dispatchEvent(o),!o.defaultPrevented)throw d}return i.then(d=>{for(const o of d||[])o.status==="rejected"&&l(o.reason);return t().catch(l)})},x="Qwen2.5-0.5B-Instruct-q4f16_1-MLC",nr=5e4;let E=null,N=null,ke=!1;function ve(){return typeof navigator>"u"?{available:!1,label:"WebLLM unavailable"}:"gpu"in navigator?N?{available:!0,label:"WebLLM ready",detail:x}:E?{available:!0,label:"WebLLM warming up",detail:x}:ke?{available:!1,label:"WebLLM unavailable",detail:"WebLLM failed to load."}:{available:!0,label:"WebLLM available",detail:x}:{available:!1,label:"WebLLM unavailable",detail:"WebGPU is unavailable in this browser."}}function sr(e,t={}){if(!(!ve().available||E)){if(N&&!t.reload){e==null||e({available:!0,label:"WebLLM ready",detail:x});return}e==null||e({available:!0,label:"WebLLM warming up",detail:x}),ke=!1,E=lr(e).then(r=>(N=r,E=null,e==null||e({available:!0,label:"WebLLM ready",detail:x}),r)),E.catch(r=>{ke=!0,E=null,e==null||e({available:!1,label:"WebLLM unavailable",detail:r instanceof Error?r.message:"WebLLM failed to load."})})}}async function ir({model:e,prompt:t,onStatus:r}){var n;if(!N)return null;r==null||r({available:!0,label:"Generating locally",detail:x});try{const l=(n=(await N.chat.completions.create({messages:[{role:"system",content:Ze()},{role:"user",content:Xe({model:e,prompt:t})}],max_tokens:nr,temperature:.2,top_p:.9})).choices[0])==null?void 0:n.message.content;if(!l)return{message:"The local model returned an empty response.",promptOutput:""};try{const d=et(Ye(l),t);return JSON.parse(d)}catch{return{message:"The local model returned text that could not be parsed as JSON.",promptOutput:l}}}finally{r==null||r({available:!0,label:"WebLLM ready",detail:x})}}async function lr(e){if(N)return N.setInitProgressCallback(r=>{e==null||e({available:!0,label:"Loading local model",detail:Ue(r)})}),await N.reload(x),N;const{CreateMLCEngine:t}=await Qe(async()=>{const{CreateMLCEngine:r}=await import("./index-D9gteTdx.js");return{CreateMLCEngine:r}},[]);return t(x,{initProgressCallback:r=>{e==null||e({available:!0,label:"Loading local model",detail:Ue(r)})}})}function Ue(e){return`${Math.round(e.progress*100)}% ${e.text}`}function Ze(){return`You are Mocklane, a focused mock HTTP API response generator.
102
+
103
+ Rules:
104
+ - Only generate mock HTTP API JSON responses.
105
+ - Always return one raw valid JSON object.
106
+ - Do not use markdown, comments, explanations, or code fences.
107
+ - Do not include <think> blocks, hidden reasoning, or analysis.
108
+ - Do not wrap the JSON in a string field such as "promptOutput".
109
+ - Keep the response compact.
110
+ - Use realistic but fake values.
111
+ - Never include real secrets, real personal data, or explanatory text.
112
+
113
+ If the request is unrelated to mock APIs, mock endpoints, mock responses, or mock data, return exactly:
114
+ {"message":"My job is to make mock APIs. Drop a model or structure, then ask me for an endpoint response and I can help you."}
115
+
116
+ For mock API requests, return this object shape:
117
+ {"method":"GET","path":"/api/resource","status":200,"headers":{"content-type":"application/json"},"body":[{"resourcePlural":[...items]}]}
118
+
119
+ Response rules:
120
+ - If the user asks for N items, generate exactly N items. Never generate fewer. Never generate more.
121
+ - Infer body fields from the supplied model, schema, example, or structure.
122
+ - Infer method, path, status, count, and data theme from the user request.
123
+ - Default to method "GET" and status 200 unless the request clearly implies another method or status.
124
+ - If count is not specified, generate 3 items.
125
+ - The body must always be an array containing exactly one object.
126
+ - That one object must have exactly one plural lower-camel-case key for the resource collection, such as "products", "customers", "orders", or "supportTickets".
127
+ - The value of that plural key must be an array of generated objects.
128
+ - Preserve sensible data types from the supplied model.
129
+ - Use ISO-like date strings when generating dates.
130
+ - Use integer cents for fields ending in "Cents", such as "priceCents": 4999.
131
+
132
+ Example body shape:
133
+ {"body":[{"products":[{"id":"101","sku":"ABC-123","name":"Product A","category":"Electronics","priceCents":4999,"currency":"USD","inventoryCount":10,"rating":5,"tags":["Electronics","Tech"]}]}]}`}function Xe({model:e,prompt:t}){return`Model / Structure:
134
+ ${e}
135
+
136
+ Prompt:
137
+ ${t}`}function or(e){const t=e.trim();if(t.startsWith("{")&&t.endsWith("}"))return t;const r=t.indexOf("{"),n=t.lastIndexOf("}");if(r===-1||n===-1||n<=r)throw new Error("Local model did not return a JSON object.");return t.slice(r,n+1)}function Ye(e){const r=e.replace(/<think>[\s\S]*?<\/think>/gi,"").trim().replace(/^```(?:json)?\s*/i,"").replace(/\s*```$/i,"").trim();return cr(r)??or(r)}function cr(e){const t=ze(e,"{","}");if(t)return ee(t);const r=ze(e,"[","]");return r?ee(r):null}function ze(e,t,r){const n=e.indexOf(t);if(n===-1)return null;let i=0,l=!1,d=!1;for(let o=n;o<e.length;o+=1){const u=e[o];if(l){d?d=!1:u==="\\"?d=!0:u==='"'&&(l=!1);continue}if(u==='"')l=!0;else if(u===t)i+=1;else if(u===r&&(i-=1,i===0))return e.slice(n,o+1)}return null}function ee(e){try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}function et(e,t){const r=dr(t);if(!r||!/product/i.test(t))return e;try{const n=JSON.parse(e),i=ur(n);if(i.length===r)return ee(JSON.stringify(Fe(i)));const l=i[0]??pr(n),d=Array.from({length:r},(o,u)=>fr(l,u+1));return ee(JSON.stringify(Fe(d)))}catch{return e}}function dr(e){const t=e.match(/id\s+(\d+)\s*(?:to|-)\s*(\d+)/i);if(t){const i=Number(t[1]),l=Number(t[2]);if(Number.isFinite(i)&&Number.isFinite(l)&&l>=i)return l-i+1}const r=e.match(/(?:exactly\s+)?(\d+)\s+(?:productmodel\s+)?items?/i);if(!r)return null;const n=Number(r[1]);return Number.isFinite(n)&&n>0?n:null}function ur(e){if(!C(e))return[];if(Array.isArray(e.products))return e.products.filter(C);const t=Array.isArray(e.body)?e.body[0]:null;return C(t)&&Array.isArray(t.products)?t.products.filter(C):Array.isArray(e.body)?e.body.filter(te):te(e)?[e]:[]}function pr(e){if(!C(e))return null;const t=Array.isArray(e.body)?e.body[0]:null;return te(t)?t:te(e)?e:null}function Fe(e){return{method:"GET",path:"/api/products",status:200,headers:{"content-type":"application/json"},body:[{products:e}]}}function fr(e,t){var l;const r=["electronics","home","apparel","books"],n=["Laptop","Desk Lamp","Cotton Hoodie","Notebook Set","Wireless Mouse","Ceramic Mug","Running Shirt","Cookbook","Bluetooth Speaker","Storage Basket"],i=r[(t-1)%r.length];return{id:String(t),sku:`SKU-${String(t).padStart(3,"0")}`,name:n[t-1]??`Product ${t}`,category:i,priceCents:we(e==null?void 0:e.priceCents,1999)+(t-1)*250,currency:"USD",inventoryCount:Math.max(0,we(e==null?void 0:e.inventoryCount,100)-(t-1)*3),rating:Math.min(5,Math.round((4.1+t%5*.2)*10)/10),tags:[i,((l=n[t-1])==null?void 0:l.toLowerCase().replaceAll(" ","-"))??`product-${t}`],dimensions:{widthCm:be(e,"widthCm",20+t),heightCm:be(e,"heightCm",10+t),depthCm:be(e,"depthCm",5+t)},createdAt:`2023-04-${String(t).padStart(2,"0")}T00:00:00Z`}}function we(e,t){return typeof e=="number"&&Number.isFinite(e)?e:t}function be(e,t,r){const n=C(e==null?void 0:e.dimensions)?e.dimensions:null;return we(n==null?void 0:n[t],r)}function te(e){return C(e)&&"id"in e&&"sku"in e&&"name"in e&&"priceCents"in e&&"dimensions"in e}function C(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function mr(){return tt()?{available:!0,label:"Native Browser AI available",detail:"Gemini Nano"}:{available:!1,label:"Native Browser AI unavailable",detail:"The LanguageModel API is unavailable in this browser."}}async function br({model:e,prompt:t,onStatus:r}){var d;const n=tt();if(!n)return null;r==null||r({available:!0,label:"Native Browser AI preparing",detail:"Checking Gemini Nano availability..."});const i=Ze();let l;try{if((n.factory.availability?await n.factory.availability():"available")==="unavailable")throw new Error("The browser reports that Gemini Nano is unavailable.");l=await n.factory.create({...n.kind==="current"?{initialPrompts:[{role:"system",content:i}]}:{systemPrompt:i},monitor(u){u.addEventListener("downloadprogress",p=>{r==null||r({available:!0,label:"Native Browser AI preparing",detail:`Downloading Gemini Nano: ${Math.round(p.loaded*100)}%`})})}})}catch(o){throw r==null||r({available:!0,label:"Native Browser AI available",detail:o instanceof Error?o.message:"Gemini Nano failed to start."}),o}r==null||r({available:!0,label:"Generating with Native Browser AI",detail:"Gemini Nano"});try{const o=await l.prompt(Xe({model:e,prompt:t})),u=Ye(o),p=et(hr(u),t);try{return JSON.parse(p)}catch{return{message:"Native Browser AI returned text that could not be parsed as JSON.",promptOutput:o}}}finally{(d=l.destroy)==null||d.call(l),r==null||r({available:!0,label:"Native Browser AI available",detail:"Gemini Nano"})}}function hr(e){const t=e.trim(),r=[];let n=!1,i=!1;for(const d of t){if(n){i?i=!1:d==="\\"?i=!0:d==='"'&&(n=!1);continue}if(d==='"')n=!0;else if(d==="{")r.push("}");else if(d==="[")r.push("]");else if(d==="}"||d==="]"){if(r.at(-1)!==d)return e;r.pop()}}if(n||r.length===0)return e;const l=`${t}${r.reverse().join("")}`;try{return JSON.parse(l),l}catch{return e}}function tt(){var t;if(typeof window>"u")return null;const e=window;return e.LanguageModel?{factory:e.LanguageModel,kind:"current"}:(t=e.ai)!=null&&t.languageModel?{factory:e.ai.languageModel,kind:"legacy"}:null}const P=[{id:"onnx-community/Qwen3-0.6B-ONNX",label:"Qwen3 0.6B",detail:"Current baseline",dtype:"q4f16"},{id:"onnx-community/gemma-3-1b-it-ONNX-GQA",label:"Gemma 3 1B Instruct",detail:"Google alternative",dtype:"q4"}],je=P[0].id,yr=5e4,gr=`You are Mocklane, a JSON-only mock HTTP API response generator.
138
+ Return exactly one raw valid JSON object.
139
+ Do not include markdown, code fences, comments, explanations, analysis, or <think> blocks.
140
+ Do not reveal chain-of-thought or hidden reasoning.
141
+
142
+ For mock API requests, always return this response shape:
143
+ {"method":"GET","path":"/api/products","status":200,"headers":{"content-type":"application/json"},"body":[{"products":[]}]}
144
+
145
+ Rules:
146
+ - If the user asks for N items, generate exactly N items.
147
+ - If the user gives an id range, include every id in that range exactly once.
148
+ - For GET all product requests, body must be an array containing one object with a plural collection array.
149
+ - Preserve field names and sensible data types from the supplied model.
150
+ - Use string ids.
151
+ - Use integer cents for fields ending in Cents.
152
+ - Use ISO-like strings for date fields.
153
+ - Do not return a single item object unless the user explicitly asks for one item.`;let I=null,L=null,xe=!1,G=null,X=null;function Z(e=je){return typeof navigator>"u"?{available:!1,label:"Transformers.js unavailable"}:"gpu"in navigator?L&&G===e?{available:!0,label:"Transformers.js ready",detail:e}:I?{available:!0,label:"Transformers.js warming up",detail:X??e}:xe?{available:!1,label:"Transformers.js unavailable",detail:"Transformers.js failed to load."}:{available:!0,label:"Transformers.js available",detail:e}:{available:!1,label:"Transformers.js unavailable",detail:"WebGPU is unavailable in this browser."}}function kr(e,t={}){const r=Nr(t.modelId);if(typeof navigator>"u"||!("gpu"in navigator)||I)return;if(L&&G===r.id&&!t.reload){e==null||e({available:!0,label:"Transformers.js ready",detail:r.id});return}e==null||e({available:!0,label:"Transformers.js warming up",detail:r.id}),xe=!1,X=r.id;const n=L;L=null,G=null,I=xr(r.id).then(i=>wr(n,r,i,e)).then(i=>(L=i,G=r.id,X=null,I=null,e==null||e({available:!0,label:"Transformers.js ready",detail:r.id}),i)),I.catch(i=>{xe=!0,I=null,X=null,e==null||e({available:!1,label:"Transformers.js unavailable",detail:i instanceof Error?i.message:"Transformers.js failed to load."})})}async function vr({model:e,prompt:t,modelId:r=je,onStatus:n}){if(!L||G!==r)return null;n==null||n({available:!0,label:"Generating with Transformers.js",detail:r});try{const i=await L([{role:"system",content:gr},{role:"user",content:jr({model:e,prompt:t})}],{max_new_tokens:yr,do_sample:!1,return_full_text:!1,tokenizer_encode_kwargs:{enable_thinking:!1}}),l=Ar(Cr(i),t);try{return JSON.parse(l)}catch{return{message:"Transformers.js returned text that could not be parsed as JSON.",promptOutput:l}}}finally{n==null||n({available:!0,label:"Transformers.js ready",detail:r})}}async function wr(e,t,r,n){e&&await e.dispose();const{pipeline:i}=await Qe(async()=>{const{pipeline:l}=await import("./transformers.web-CjIQtwg9.js");return{pipeline:l}},[]);return i("text-generation",t.id,{device:"webgpu",dtype:t.dtype,progress_callback:l=>{n==null||n({available:!0,label:"Loading Transformers.js",detail:`${Mr(l)} - ${r?"Cache Load":"First Load"}`})}})}async function xr(e){if(!("caches"in globalThis))return!1;const t=e.split("/").at(-1),r=await caches.keys();for(const n of r)if((await(await caches.open(n)).keys()).some(l=>l.url.includes(e)||l.url.includes(t??e)))return!0;return!1}function Nr(e){return P.find(t=>t.id===e)??P[0]}function jr({model:e,prompt:t}){return`Model / Structure:
154
+ ${e}
155
+
156
+ Prompt:
157
+ ${t}`}function Mr(e){if(!e||typeof e!="object")return"Loading Transformers.js model";const t=e;return typeof t.progress=="number"?`${Math.round(t.progress)}% ${t.file??t.name??"model file"}`:typeof t.loaded=="number"&&typeof t.total=="number"?`${Math.round(t.loaded/t.total*100)}% ${t.file??t.name??"model file"}`:t.status??"Loading Transformers.js model"}function Cr(e){const t=Array.isArray(e)?e[0]:null;if(!t||typeof t!="object"||!("generated_text"in t))return JSON.stringify(e,null,2);const r=t.generated_text;if(typeof r=="string")return he(r);if(Array.isArray(r)){const n=r.at(-1);if(n&&typeof n=="object"&&"content"in n)return he(String(n.content??""))}return he(JSON.stringify(r,null,2))}function he(e){const r=e.replace(/<think>[\s\S]*?<\/think>/gi,"").trim().replace(/^```(?:json)?\s*/i,"").replace(/\s*```$/i,"").trim();return Lr(r)??r}function Lr(e){const t=qe(e,"{","}");if(t)return re(t);const r=qe(e,"[","]");return r?re(r):null}function qe(e,t,r){const n=e.indexOf(t);if(n===-1)return null;let i=0,l=!1,d=!1;for(let o=n;o<e.length;o+=1){const u=e[o];if(l){d?d=!1:u==="\\"?d=!0:u==='"'&&(l=!1);continue}if(u==='"')l=!0;else if(u===t)i+=1;else if(u===r&&(i-=1,i===0))return e.slice(n,o+1)}return null}function re(e){try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}function Ar(e,t){const r=Tr(t);if(!r||!/product/i.test(t))return e;try{const n=JSON.parse(e),i=Or(n);if(i.length===r)return re(JSON.stringify(He(i)));const l=i[0]??Er(n),d=Array.from({length:r},(o,u)=>Ir(l,u+1));return re(JSON.stringify(He(d)))}catch{return e}}function Tr(e){const t=e.match(/id\s+(\d+)\s*(?:to|-)\s*(\d+)/i);if(t){const i=Number(t[1]),l=Number(t[2]);if(Number.isFinite(i)&&Number.isFinite(l)&&l>=i)return l-i+1}const r=e.match(/(?:exactly\s+)?(\d+)\s+(?:productmodel\s+)?items?/i);if(!r)return null;const n=Number(r[1]);return Number.isFinite(n)&&n>0?n:null}function Or(e){if(!A(e))return[];if(Array.isArray(e.products))return e.products.filter(A);const t=Array.isArray(e.body)?e.body[0]:null;return A(t)&&Array.isArray(t.products)?t.products.filter(A):Array.isArray(e.body)?e.body.filter(ae):ae(e)?[e]:[]}function Er(e){if(!A(e))return null;const t=Array.isArray(e.body)?e.body[0]:null;return ae(t)?t:ae(e)?e:null}function He(e){return{method:"GET",path:"/api/products",status:200,headers:{"content-type":"application/json"},body:[{products:e}]}}function Ir(e,t){var l;const r=["electronics","home","apparel","books"],n=["Laptop","Desk Lamp","Cotton Hoodie","Notebook Set","Wireless Mouse","Ceramic Mug","Running Shirt","Cookbook","Bluetooth Speaker","Storage Basket"],i=r[(t-1)%r.length];return{id:String(t),sku:`SKU-${String(t).padStart(3,"0")}`,name:n[t-1]??`Product ${t}`,category:i,priceCents:Ne(e==null?void 0:e.priceCents,1999)+(t-1)*250,currency:"USD",inventoryCount:Math.max(0,Ne(e==null?void 0:e.inventoryCount,100)-(t-1)*3),rating:Math.min(5,Math.round((4.1+t%5*.2)*10)/10),tags:[i,((l=n[t-1])==null?void 0:l.toLowerCase().replaceAll(" ","-"))??`product-${t}`],dimensions:{widthCm:ye(e,"widthCm",20+t),heightCm:ye(e,"heightCm",10+t),depthCm:ye(e,"depthCm",5+t)},createdAt:`2023-04-${String(t).padStart(2,"0")}T00:00:00Z`}}function Ne(e,t){return typeof e=="number"&&Number.isFinite(e)?e:t}function ye(e,t,r){const n=A(e==null?void 0:e.dimensions)?e.dimensions:null;return Ne(n==null?void 0:n[t],r)}function ae(e){return A(e)&&"id"in e&&"sku"in e&&"name"in e&&"priceCents"in e&&"dimensions"in e}function A(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}const rt="/__mocklane/mocks";let Y=null;async function Pr(e){const t=await fetch(rt,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(e)});await at(t),Y=e}async function ge(){if(!Y)return;const e=await fetch(rt,{method:"DELETE",headers:{"content-type":"application/json"},body:JSON.stringify(Y)});await at(e),Y=null}function O(e){if(!e||typeof e!="object"||Array.isArray(e))return!1;const t=e;return typeof t.method=="string"&&typeof t.path=="string"&&typeof t.status=="number"&&!!t.headers&&typeof t.headers=="object"&&!Array.isArray(t.headers)&&"body"in t}async function at(e){if(e.ok)return;const t=await e.json().catch(()=>null);throw new Error((t==null?void 0:t.error)??"Mocklane local CLI is unavailable.")}const _r="My job is to make mock APIs. Drop a model or structure, then ask me for an endpoint response and I can help you.",$r="The selected local model is still warming up or unavailable. Mocklane will not manually fabricate this response; try again when the model is ready.",Rr="The local model responded, but Mocklane could not turn it into a valid mock API JSON response. Try a smaller request or ask for fewer items.";async function Sr({model:e,prompt:t,provider:r="webllm",transformersModelId:n,onStatus:i}){if(!e.trim()||!t.trim())return{message:_r};try{const l=r==="transformers"?await vr({model:e,prompt:t,modelId:n,onStatus:i}):r==="native"?await br({model:e,prompt:t,onStatus:i}):await ir({model:e,prompt:t,onStatus:i});if(l)return Jr(l)}catch{return{message:Rr}}return i==null||i({available:!1,label:"Local model not ready",detail:"No manual generation is active."}),{message:$r}}function Jr(e){if("message"in e)return e;const t=e.body??[];return{method:String(e.method||"GET").toUpperCase(),path:String(e.path||"/api/mock"),status:Number(e.status||200),headers:{"content-type":"application/json",...e.headers||{}},body:t}}const Dr=[{label:"Go struct detected",test:/^\s*package\s+\w+|\btype\s+\w+\s+struct\b|\bconst\s*\([^)]*\b\w+\s+\w+\s*=/is},{label:"TypeScript model detected",test:/\binterface\s+\w+\s*\{|\btype\s+\w+\s*=\s*(?:\{|"[^"]|[A-Z]\w*\s*[|&])/i},{label:"Python model detected",test:/\bclass\s+\w+|BaseModel|pydantic|:\s*(str|int|float|bool)\b/i},{label:"Java model detected",test:/\b(public\s+)?class\s+\w+|private\s+(String|Integer|Long|Double|Boolean)\b/i},{label:"Prisma model detected",test:/\bmodel\s+\w+\s*\{|@id|@default/i},{label:"SQL schema detected",test:/\bcreate\s+table\b|\b(varchar|uuid|timestamp|boolean)\b/i},{label:"JSON shape detected",test:/^\s*[\[{]/}];function Gr(e){return Dr.find(r=>r.test.test(e))??{label:e.trim()?"Structure detected":"Waiting for model"}}function Ve(e){return JSON.stringify(e,null,2)??""}function Wr(e){try{return JSON.parse(e)}catch{return null}}const Br={method:"GET",path:"/api/products",status:200,headers:{"content-type":"application/json"},body:[]},nt=[{id:"transformers",label:"Transformers.js models",detail:"Qwen3 0.6B ONNX",available:!1},{id:"webllm",label:"WebLLM models",detail:"Qwen2.5 0.5B Instruct",available:!1},{id:"native",label:"Native browser AI",detail:"window.ai / LanguageModel",available:!1}],Ur=`Make a GET all products response.
158
+ Return 10 ProductModel items.
159
+ The products array must contain exactly 10 items.
160
+ Use id values "1" through "10".`,zr=`export type ProductCategory = "electronics" | "home" | "apparel" | "books";
161
+
162
+ export interface ProductModel {
163
+ id: string;
164
+ sku: string;
165
+ name: string;
166
+ category: ProductCategory;
167
+ priceCents: number;
168
+ currency: "USD";
169
+ inventoryCount: number;
170
+ rating: number;
171
+ tags: string[];
172
+ dimensions: {
173
+ widthCm: number;
174
+ heightCm: number;
175
+ depthCm: number;
176
+ };
177
+ createdAt: string;
178
+ }`;function Zr(){return[{title:"Mocklane"},{name:"description",content:"Generate JSON HTTP mock API responses from pasted models."}]}const Xr=Mt(function(){const[t,r]=c.useState(zr),[n,i]=c.useState(Ur),[l,d]=c.useState(Br),[o,u]=c.useState({available:!1,label:"Checking local model"}),[p,y]=c.useState("light"),[h,g]=c.useState(!1),[b,j]=c.useState(!1),[se,W]=c.useState(!1),[B,U]=c.useState(!1),[Me,z]=c.useState(""),[ie,T]=c.useState(null),[F,le]=c.useState(""),[Ce,Le]=c.useState(!1),[Ae,Te]=c.useState(!1),[Oe,st]=c.useState(4e3),[_,q]=c.useState(!1),[Ee,oe]=c.useState(null),[it,H]=c.useState(null),[ce,lt]=c.useState(nt),[Ie,ot]=c.useState("transformers"),[de,ct]=c.useState(je),[ue,V]=c.useState(!1),[Pe,$]=c.useState(!1),R=c.useRef(0),S=c.useRef(null),_e=c.useRef(null),$e=c.useRef(null),dt=c.useMemo(()=>Gr(t),[t]),J=c.useMemo(()=>Ve(l),[l]),k=ce.find(s=>s.id===Ie)??ce[0],M=P.find(s=>s.id===de)??P[0],pe=qr(k),Re=Vr({option:k,capabilityAvailable:pe.available,modelStatus:o,isLoadingProvider:_}),K=Kr({option:k,modelStatus:o}),ut=Hr(k,o),pt=h?`${it??0}ms`:Ee===null?"Not run yet":`${Ee}ms`,ft=`http://localhost:${Oe}/${D(F)}`;c.useEffect(()=>{u(Z(de)),lt(Fr())},[de]),c.useEffect(()=>{let s=!1;async function m(){const f=await fetch("/__mocklane/health").catch(()=>null);if(!(f!=null&&f.ok))return;const w=await f.json().catch(()=>null);!s&&typeof(w==null?void 0:w.proxyPort)=="number"&&st(w.proxyPort)}return m(),()=>{s=!0}},[]),c.useEffect(()=>{function s(m){var f,w;(f=_e.current)!=null&&f.contains(m.target)||V(!1),(w=$e.current)!=null&&w.contains(m.target)||$(!1)}return document.addEventListener("pointerdown",s),()=>document.removeEventListener("pointerdown",s)},[]),c.useEffect(()=>{const s=window.localStorage.getItem("mocklane-theme");if(s==="light"||s==="dark"){y(s);return}window.matchMedia("(prefers-color-scheme: dark)").matches&&y("dark")},[]),c.useEffect(()=>{document.documentElement.dataset.theme=p,window.localStorage.setItem("mocklane-theme",p)},[p]),c.useEffect(()=>{if(!h||S.current===null)return;const s=()=>{S.current!==null&&H(Math.round(performance.now()-S.current))};s();const m=window.setInterval(s,100);return()=>window.clearInterval(m)},[h]);async function mt(){if(!K)return;const s=R.current+1,m=performance.now();R.current=s,S.current=m,H(0),oe(null),g(!0);try{const f=await Sr({model:t,prompt:n,provider:k.id,transformersModelId:M.id,onStatus:Q=>{R.current===s&&u(Q)}});if(R.current!==s)return;d(f),U(!1),T(null),W(O(f)),O(f)&&le(D(f.path));const w=Math.round(performance.now()-m);H(w),oe(w),j(!1),await ge()}finally{R.current===s&&(S.current=null,g(!1))}}async function bt(){await navigator.clipboard.writeText(J),Le(!0),window.setTimeout(()=>Le(!1),1100)}async function Se(){await navigator.clipboard.writeText(ft),Te(!0),window.setTimeout(()=>Te(!1),1100)}function ht(){z(J),T(null),U(!0)}function yt(){z(""),T(null),U(!1)}async function gt(){let s;try{s=JSON.parse(Me)}catch(f){T(f instanceof SyntaxError?f.message:"Invalid JSON.");return}b&&(await ge(),j(!1)),d(s);const m=O(s);W(m),O(s)&&le(D(s.path)),z(Ve(s)),T(null),U(!1)}function kt(){const s=Wr(J),f=`${(s&&typeof s=="object"&&"path"in s?String(s.path).replaceAll("/","_"):"mock_api").replace(/^_/,"")||"mock_api"}.json`,w=new Blob([J],{type:"application/json"}),Q=URL.createObjectURL(w),fe=document.createElement("a");fe.href=Q,fe.download=f,fe.click(),URL.revokeObjectURL(Q)}function vt(){y(s=>s==="dark"?"light":"dark")}async function wt(){if(b){await ge(),j(!1);return}O(l)&&(await Pr({...l,path:`/${D(F)}`}),j(!0))}function xt(s){s.available&&(ot(s.id),s.id==="transformers"?u(Z(M.id)):s.id==="webllm"?u(ve()):s.id==="native"&&u(mr()),V(!1),$(!1))}function Nt(s){if(s===M.id||_||h){$(!1);return}ct(s),u(Z(s)),$(!1),oe(null),H(null)}function jt(){if(!pe.available)return;if(k.id==="transformers"){const m=Z(M.id);u(m),q(!0),kr(f=>{u(f),ne(f)||q(!1)},{reload:m.label==="Transformers.js ready",modelId:M.id});return}if(k.id!=="webllm")return;const s=ve();u(s),q(!0),sr(m=>{u(m),ne(m)||q(!1)},{reload:s.label==="WebLLM ready"})}return a.jsxs("main",{className:"app-shell",children:[a.jsx("header",{className:"topbar",children:a.jsxs("div",{className:"topbar-inner",children:[a.jsxs("div",{className:"title-line",children:[a.jsxs("div",{className:"brand-logo","aria-label":"Mocklane",children:[a.jsx("span",{children:"MOCK"}),a.jsx("span",{children:"LANE"})]}),a.jsx("h1",{children:"Mock API response generator"})]}),a.jsx("div",{className:"topbar-actions",children:a.jsx("button",{className:"icon-button",type:"button",onClick:vt,"aria-label":p==="dark"?"Switch to light mode":"Switch to dark mode",title:p==="dark"?"Light mode":"Dark mode",children:p==="dark"?a.jsx(Zt,{size:17,"aria-hidden":"true"}):a.jsx(Bt,{size:17,"aria-hidden":"true"})})})]})}),a.jsxs("section",{className:"workspace","aria-label":"Mocklane mock API generator",children:[a.jsx("div",{className:"workspace-summary",children:a.jsxs("div",{className:"model-stat-grid","aria-label":"Local model runtime status",children:[a.jsxs("div",{className:"provider-stat",ref:_e,children:[a.jsxs("div",{className:"provider-stat-card",children:[a.jsx("button",{className:"provider-dropdown-trigger",type:"button",onClick:()=>V(s=>!s),"aria-haspopup":"listbox","aria-expanded":ue,children:a.jsxs("span",{className:"provider-copy",children:[a.jsx("span",{children:"Provider"}),a.jsx("strong",{children:k.label})]})}),a.jsx("div",{className:"provider-status-wrap",children:a.jsxs("span",{className:"provider-status",children:[a.jsx("span",{className:`provider-status-dot ${Re.tone}`,"aria-hidden":"true"}),a.jsx("span",{children:Re.label})]})}),a.jsx("button",{className:"provider-chevron-button",type:"button",onClick:()=>V(s=>!s),"aria-label":"Choose provider","aria-haspopup":"listbox","aria-expanded":ue,children:a.jsx(De,{size:16,"aria-hidden":"true"})})]}),ue?a.jsx("div",{className:"model-menu provider-menu",role:"listbox","aria-label":"Model provider",children:ce.map(s=>a.jsxs("button",{className:"model-menu-item",type:"button",role:"option",disabled:!s.available,"aria-selected":s.id===Ie,onClick:()=>xt(s),children:[a.jsxs("span",{children:[a.jsx("span",{className:"model-option-label",children:s.label}),a.jsx("span",{className:"model-option-detail",children:s.detail})]}),s.available?null:a.jsx("span",{className:"model-unavailable",children:"not available"})]},s.id))}):null]}),a.jsxs("div",{className:k.id==="transformers"?"model-stat-dropdown":void 0,ref:k.id==="transformers"?$e:void 0,children:[a.jsx("span",{children:"Model"}),k.id==="transformers"?a.jsxs(a.Fragment,{children:[a.jsxs("button",{className:"model-stat-trigger",type:"button",onClick:()=>$(s=>!s),disabled:_||h,"aria-haspopup":"listbox","aria-expanded":Pe,children:[a.jsx("strong",{children:M.label}),a.jsx(De,{size:15,"aria-hidden":"true"})]}),Pe?a.jsx("div",{className:"model-menu transformer-model-menu",role:"listbox","aria-label":"Transformers.js model",children:P.map(s=>a.jsxs("button",{className:"model-menu-item",type:"button",role:"option","aria-selected":s.id===M.id,onClick:()=>Nt(s.id),children:[a.jsxs("span",{children:[a.jsx("span",{className:"model-option-label",children:s.label}),a.jsx("span",{className:"model-option-detail",children:s.detail})]}),s.id===M.id?a.jsx(Pt,{size:16,"aria-hidden":"true"}):null]},s.id))}):null]}):a.jsx("strong",{children:k.detail})]}),a.jsxs("div",{children:[a.jsx("span",{children:"Elapsed"}),a.jsx("strong",{children:pt})]})]})}),a.jsxs("div",{className:"panels",children:[a.jsxs("section",{className:"input-panel","aria-label":"Mock API inputs",children:[a.jsx(We,{id:"promptInput",label:"Prompt",meta:"Always outputs JSON HTTP responses",value:n,onChange:i,placeholder:"Example: Make me a GET /api/products response with 10 cyberpunk e-commerce items.",variant:"compact"}),a.jsx(We,{id:"modelInput",label:"Model / Structure",meta:dt.label,value:t,onChange:r,spellCheck:!1,placeholder:"Drop a TypeScript interface, Python/Pydantic class, Go struct, Java DTO, Prisma model, OpenAPI schema, SQL table, or example JSON object."}),a.jsxs("div",{className:"actions",children:[a.jsxs("span",{className:`generate-control ${K?"":"needs-model"}`,tabIndex:K?-1:0,children:[a.jsxs("button",{type:"button",onClick:mt,disabled:h||!K,children:[a.jsx(Yt,{size:17,"aria-hidden":"true"}),h?"Generating":"Generate"]}),a.jsx("span",{className:"tooltip-bubble",children:"Load model first"})]}),k.id!=="native"?a.jsxs("button",{className:"load-provider-action",type:"button",onClick:jt,disabled:_||!pe.available,children:[_?a.jsx(Gt,{className:"spin-icon",size:16,"aria-hidden":"true"}):a.jsx(Ge,{size:16,"aria-hidden":"true"}),ut]}):null]}),o.detail?a.jsx("p",{className:"provider-progress-text",children:o.detail}):null]}),a.jsxs("section",{className:"output-panel","aria-label":"Generated mock API response",children:[a.jsx("div",{className:"output-head",children:a.jsx("p",{className:"eyebrow",children:"HTTP JSON"})}),a.jsxs("div",{className:"code-wrap",children:[a.jsx("div",{className:"code-actions","aria-label":"JSON output actions",children:B?a.jsxs(a.Fragment,{children:[a.jsxs("button",{className:"code-icon-button",type:"button",onClick:gt,"aria-label":"Save JSON",title:"Save JSON",children:[a.jsx(Kt,{size:18,"aria-hidden":"true"}),a.jsx("span",{className:"tooltip-bubble",children:"Save changes"})]}),a.jsxs("button",{className:"code-icon-button",type:"button",onClick:yt,"aria-label":"Cancel editing",title:"Cancel editing",children:[a.jsx(tr,{size:18,"aria-hidden":"true"}),a.jsx("span",{className:"tooltip-bubble",children:"Cancel editing"})]})]}):a.jsxs(a.Fragment,{children:[a.jsxs("button",{className:"code-icon-button",type:"button",onClick:ht,disabled:h,"aria-label":"Edit JSON",title:"Edit JSON",children:[a.jsx(zt,{size:18,"aria-hidden":"true"}),a.jsx("span",{className:"tooltip-bubble",children:"Click to edit"})]}),a.jsxs("button",{className:`code-icon-button copy-control ${Ce?"copied":""}`,type:"button",onClick:bt,"aria-label":"Copy JSON",children:[a.jsx(Rt,{size:18,"aria-hidden":"true"}),a.jsx("span",{className:"tooltip-bubble",children:Ce?"Copied!":"Click to copy"})]}),a.jsxs("button",{className:"code-icon-button",type:"button",onClick:kt,"aria-label":"Download JSON",title:"Download JSON",children:[a.jsx(Jt,{size:18,"aria-hidden":"true"}),a.jsx("span",{className:"tooltip-bubble",children:"Click to download"})]})]})}),B?a.jsxs("div",{className:"json-editor-shell",children:[a.jsx("textarea",{className:"json-editor",value:Me,onChange:s=>{z(s.target.value),ie&&T(null)},"aria-label":"Edit JSON response",spellCheck:!1,autoFocus:!0}),ie?a.jsxs("p",{className:"json-editor-error",role:"alert",children:["Invalid JSON: ",ie]}):null]}):a.jsx("pre",{tabIndex:0,children:a.jsx("code",{children:J})})]}),a.jsxs("div",{className:"mock-footer",children:[a.jsxs("div",{className:`mock-endpoint ${b?"copy-control mock-endpoint-locked":""} ${Ae?"copied":""}`,onClick:b?Se:void 0,onKeyDown:s=>{!b||s.key!=="Enter"&&s.key!==" "||(s.preventDefault(),Se())},role:b?"button":void 0,tabIndex:b?0:void 0,"aria-label":b?"Copy mock API URL":void 0,children:[a.jsxs("span",{className:"mock-endpoint-prefix",children:["localhost:",Oe,"/"]}),a.jsx("input",{type:"text",value:F,onChange:s=>le(s.target.value),placeholder:"api/products","aria-label":"Mock API path",disabled:b||B,spellCheck:!1}),b?a.jsx("span",{className:"tooltip-bubble",children:Ae?"Copied!":"Click to copy"}):null]}),a.jsxs("button",{className:b?"mock-active":"secondary",type:"button",onClick:wt,disabled:B||!se||!O(l)||!D(F),children:[b?a.jsx(qt,{size:16,"aria-hidden":"true"}):a.jsx(Ge,{size:16,"aria-hidden":"true"}),b?"Unmock API":"Mock API"]})]})]})]})]})]})});function D(e){return e.trim().replace(/^\/+/,"")}function Fr(){var n;const e=typeof navigator<"u"&&"gpu"in navigator,t=typeof navigator<"u"&&"gpu"in navigator,r=typeof window<"u"&&!!((n=window.ai)!=null&&n.languageModel||window.LanguageModel);return nt.map(i=>i.id==="webllm"?{...i,available:e}:i.id==="transformers"?{...i,available:t}:i.id==="native"?{...i,available:r}:i)}function qr(e){return e.id==="webllm"?{label:"WebGPU",available:typeof navigator<"u"&&"gpu"in navigator}:e.id==="native"?{label:"Browser AI",available:e.available}:{label:"Transformers.js",available:e.available}}function Hr(e,t){return e.id==="webllm"?t.label==="WebLLM ready"?"Reload WebLLM":"Load WebLLM":e.id==="transformers"?t.label==="Transformers.js ready"?"Reload Transformers.js":"Load Transformers.js":"Load Browser AI"}function Vr({option:e,capabilityAvailable:t,modelStatus:r,isLoadingProvider:n}){if(!t)return{label:"not available",tone:"missing"};if(e.id==="webllm"){if(n||ne(r))return{label:"loading",tone:"loading"};if(r.label==="Generating locally")return{label:"running",tone:"running"};if(r.label==="WebLLM ready")return{label:"ready",tone:"ready"}}if(e.id==="transformers"){if(n||ne(r))return{label:"loading",tone:"loading"};if(r.label==="Generating with Transformers.js")return{label:"running",tone:"running"};if(r.label==="Transformers.js ready")return{label:"ready",tone:"ready"}}return e.id==="native"?r.label==="Native Browser AI preparing"?{label:"loading",tone:"loading"}:r.label==="Generating with Native Browser AI"?{label:"running",tone:"running"}:{label:"available",tone:"available"}:{label:"available",tone:"available"}}function ne(e){return e.label==="WebLLM warming up"||e.label==="Loading local model"||e.label==="Transformers.js warming up"||e.label==="Loading Transformers.js"}function Kr({option:e,modelStatus:t}){return e.id==="webllm"?t.label==="WebLLM ready":e.id==="transformers"?t.label==="Transformers.js ready":e.id==="native"?e.available:!1}export{Xr as default,Zr as meta};
@@ -1 +1 @@
1
- window.__reactRouterManifest={"entry":{"module":"/assets/entry.client-Cw45cONA.js","imports":["/assets/jsx-runtime-BZbza59V.js"],"css":[]},"routes":{"root":{"id":"root","path":"","hasAction":false,"hasLoader":false,"hasClientAction":false,"hasClientLoader":false,"hasClientMiddleware":false,"hasDefaultExport":true,"hasErrorBoundary":false,"module":"/assets/root-Dlix2DV-.js","imports":["/assets/jsx-runtime-BZbza59V.js"],"css":["/assets/root-Di4etcLz.css"]},"routes/home":{"id":"routes/home","parentId":"root","index":true,"hasAction":false,"hasLoader":false,"hasClientAction":false,"hasClientLoader":false,"hasClientMiddleware":false,"hasDefaultExport":true,"hasErrorBoundary":false,"module":"/assets/home-CHNZ1EXV.js","imports":["/assets/jsx-runtime-BZbza59V.js"],"css":[]}},"url":"/assets/manifest-08a95dab.js","version":"08a95dab"};
1
+ window.__reactRouterManifest={"entry":{"module":"/assets/entry.client-Cw45cONA.js","imports":["/assets/jsx-runtime-BZbza59V.js"],"css":[]},"routes":{"root":{"id":"root","path":"","hasAction":false,"hasLoader":false,"hasClientAction":false,"hasClientLoader":false,"hasClientMiddleware":false,"hasDefaultExport":true,"hasErrorBoundary":false,"module":"/assets/root-BlLsNrT5.js","imports":["/assets/jsx-runtime-BZbza59V.js"],"css":["/assets/root-C9U4kG7U.css"]},"routes/home":{"id":"routes/home","parentId":"root","index":true,"hasAction":false,"hasLoader":false,"hasClientAction":false,"hasClientLoader":false,"hasClientMiddleware":false,"hasDefaultExport":true,"hasErrorBoundary":false,"module":"/assets/home-BctYtJJe.js","imports":["/assets/jsx-runtime-BZbza59V.js"],"css":[]}},"url":"/assets/manifest-cfa23094.js","version":"cfa23094"};
@@ -1 +1 @@
1
- :root{color-scheme:light;--bg: #f6f7f9;--panel: #ffffff;--panel-soft: #fbfcfe;--ink: #15181f;--muted: #657080;--line: #d9dee7;--line-strong: #bcc5d2;--primary: #1663d9;--primary-dark: #0f4ca7;--code: #111827;--code-ink: #d6e2ff;--brand-ink: #152b3f;--brand-blue: #1597df;--hover-soft: #eef2f7;--ok: #16825d;--warn: #ad5b00;--ready-line: #a7dbc8;--warn-line: #f3cf9c;--focus-ring: rgba(22, 99, 217, .12);font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}[data-theme=dark]{color-scheme:dark;--bg: #0c1118;--panel: #111821;--panel-soft: #0f151e;--ink: #edf3fb;--muted: #92a0b2;--line: #263241;--line-strong: #3a485a;--primary: #2f9ff0;--primary-dark: #1b84d2;--code: #070b12;--code-ink: #d8e7ff;--brand-ink: #f2f7fd;--brand-blue: #39aaf4;--hover-soft: #1a2431;--ok: #68d6ae;--warn: #ffb75d;--ready-line: #2f735d;--warn-line: #7a5427;--focus-ring: rgba(47, 159, 240, .22)}*{box-sizing:border-box}body{margin:0;min-height:100vh;background:var(--bg);color:var(--ink)}button,textarea{font:inherit}.app-shell{height:100vh;overflow:hidden;padding:0 24px 24px}.workspace{display:flex;height:calc(100vh - 92px);min-height:0;flex-direction:column;max-width:1440px;margin:0 auto}.workspace-summary{display:block;margin-bottom:18px}.product-description{max-width:760px;margin:0;color:var(--muted);font-size:14px;line-height:1.5}.topbar{width:calc(100% + 48px);display:flex;align-items:center;justify-content:space-between;gap:16px;margin:0 -24px 18px;border-top:1px solid #55bca9;padding:12px 24px;background:#2d3e50;color:#f6f8fb}.topbar-inner{display:flex;width:100%;max-width:1440px;margin:0 auto;align-items:center;justify-content:space-between;gap:16px}.title-line{display:flex;min-width:0;align-items:baseline;gap:16px}.topbar-actions{display:flex;align-items:center;gap:10px}.eyebrow{margin:0 0 5px;color:var(--primary-dark);font-size:12px;font-weight:700;letter-spacing:0;text-transform:uppercase}.brand-logo{display:inline-flex;align-items:baseline;flex:0 0 auto;margin-bottom:0;font-size:clamp(18px,1.8vw,24px);font-weight:900;line-height:1;letter-spacing:0}.brand-logo span:first-child{color:#f6f8fb}.brand-logo span:last-child{color:#42d0b6}h1,h2{margin:0;letter-spacing:0}h1{font-size:15px;font-weight:400;line-height:1.25;color:#c8d4df}h2{font-size:20px}.status-pill{display:inline-flex;flex:0 0 auto;align-items:center;gap:7px;border:1px solid var(--line);border-radius:999px;padding:8px 12px;background:#ffffff0a;color:#d8e2eb;font-size:13px;font-weight:650}.status-pill.ready{border-color:var(--ready-line);color:var(--ok)}.status-pill.fallback{border-color:var(--warn-line);color:var(--warn)}.panels{display:grid;min-height:0;flex:1;grid-template-columns:minmax(360px,.8fr) minmax(420px,1.2fr);gap:18px;align-items:stretch}.input-panel,.output-panel{min-width:0;min-height:0;border:1px solid var(--line);border-radius:8px;background:var(--panel)}.input-panel{display:flex;flex-direction:column;gap:16px;overflow:hidden;padding:18px}.field-group{display:flex;min-height:0;flex:1;flex-direction:column;gap:8px}.prompt-group{min-height:140px;flex:0 0 210px}.field-heading,.output-head{display:flex;align-items:center;justify-content:space-between;gap:12px}label{font-weight:760}.field-heading span{color:var(--muted);font-size:12px;font-weight:650;text-align:right}textarea{width:100%;min-height:0;flex:1;resize:none;border:1px solid var(--line-strong);border-radius:8px;padding:13px;color:var(--ink);background:var(--panel-soft);line-height:1.45;outline:none}textarea:focus{border-color:var(--primary);box-shadow:0 0 0 3px var(--focus-ring)}.actions{display:flex;gap:10px;flex-wrap:wrap}button{display:inline-flex;min-height:38px;align-items:center;justify-content:center;gap:7px;border:1px solid transparent;border-radius:8px;padding:0 14px;background:var(--primary);color:#fff;cursor:pointer;font-weight:760}button:hover{background:var(--primary-dark)}button:disabled{cursor:wait;opacity:.68}button.secondary{border-color:var(--line-strong);background:var(--panel);color:var(--ink)}button.secondary:hover{border-color:var(--primary);color:var(--primary-dark)}button.mock-active{border-color:var(--ready-line);background:var(--ok);color:#fff}button.mock-active:hover{border-color:var(--ready-line);background:var(--ok)}button.ghost{background:transparent;color:var(--muted)}button.ghost:hover{background:var(--hover-soft);color:var(--ink)}button.load-provider-action{border-color:var(--line-strong);background:var(--panel);color:var(--ink)}button.load-provider-action:hover{border-color:var(--primary);background:var(--hover-soft);color:var(--primary-dark)}button.load-provider-action:disabled{background:transparent;color:var(--muted);cursor:not-allowed;opacity:.72}.provider-progress-text{min-height:18px;margin:-6px 0 0;color:var(--muted);font-size:12px;font-weight:650;line-height:1.4}.spin-icon{animation:spin .9s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}button.icon-button{width:38px;padding:0;border-color:#ffffff2e;background:#ffffff0a;color:#d8e2eb}button.icon-button:hover{border-color:#42d0b6;background:#42d0b61f;color:#42d0b6}.output-panel{display:flex;min-height:0;overflow:visible;flex-direction:column;padding:18px}.output-head{margin-bottom:14px}.model-picker{position:relative;flex:0 0 auto}.model-select{min-width:260px;justify-content:space-between;border-color:var(--line-strong);background:var(--panel);color:var(--ink)}.model-select:hover{border-color:var(--primary);background:var(--panel);color:var(--primary-dark)}.model-select>span,.model-menu-item>span:first-child{display:grid;min-width:0;text-align:left}.model-select-label,.model-option-label{overflow:hidden;font-size:13px;font-weight:800;line-height:1.15;text-overflow:ellipsis;white-space:nowrap}.model-select-detail,.model-option-detail{overflow:hidden;color:var(--muted);font-size:11px;font-weight:650;line-height:1.25;text-overflow:ellipsis;white-space:nowrap}.model-menu{position:absolute;top:calc(100% + 8px);right:0;z-index:5;width:min(360px,calc(100vw - 48px));overflow:hidden;border:1px solid var(--line);border-radius:8px;background:var(--panel);box-shadow:0 18px 48px #1118272e}.model-menu-item{width:100%;min-height:52px;justify-content:space-between;gap:16px;border:0;border-radius:0;padding:9px 12px;background:var(--panel);color:var(--ink)}.model-menu-item:hover{background:var(--hover-soft);color:var(--ink)}.model-menu-item[aria-selected=true]{background:#1663d914}.model-menu-item:disabled{cursor:not-allowed;opacity:1}.model-menu-item:disabled .model-option-label,.model-menu-item:disabled .model-option-detail{color:var(--muted);opacity:.62}.model-unavailable{flex:0 0 auto;color:var(--muted);font-size:10px;font-weight:750;line-height:1;opacity:.7}.code-wrap{position:relative;display:grid;min-height:0;flex:1;overflow:visible;border-radius:8px;background:var(--code)}.code-actions{position:absolute;top:12px;right:26px;z-index:1000;display:flex;gap:8px}.code-icon-button{position:relative;width:36px;min-height:36px;padding:0;border-color:#d8e2eb33;background:#2d3e50db;color:#d8e2eb;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}.code-icon-button:hover{border-color:#42d0b6;background:#2d3e50;color:#42d0b6}.code-icon-button.copied{border-color:var(--ready-line);background:var(--ok);color:#fff}.json-editor-shell{display:flex;min-width:0;min-height:0;flex:1;flex-direction:column}.json-editor{width:100%;min-height:0;flex:1;resize:none;border:0;border-radius:8px;outline:none;padding:18px 112px 18px 18px;background:transparent;color:var(--code-ink);font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,ui-monospace,monospace;font-size:13px;line-height:1.55;white-space:pre}.json-editor:focus{box-shadow:inset 0 0 0 1px #42d0b6a6}.json-editor-error{margin:0;border-top:1px solid var(--warn-line);padding:9px 14px;background:#ad5b0021;color:var(--warn);font-size:12px;font-weight:700;line-height:1.4}.tooltip-bubble{position:absolute;z-index:1001;right:50%;bottom:calc(100% + 10px);transform:translate(50%);visibility:hidden;opacity:0;pointer-events:none;white-space:nowrap;border-radius:6px;padding:7px 9px;background:#2d3e50;color:#f6f8fb;font-size:12px;font-weight:700;transition:opacity .16s ease,visibility .16s ease}.tooltip-bubble:after{position:absolute;top:100%;left:50%;width:0;height:0;border:6px solid transparent;border-top-color:#2d3e50;content:"";transform:translate(-50%)}.copy-control:hover .tooltip-bubble,.copy-control:focus-visible .tooltip-bubble,.copy-control.copied .tooltip-bubble,.generate-control.needs-model:hover .tooltip-bubble,.generate-control.needs-model:focus-visible .tooltip-bubble,.generate-control.needs-model:focus-within .tooltip-bubble{visibility:visible;opacity:1}.copy-control.copied .tooltip-bubble{background:var(--ok)}.copy-control.copied .tooltip-bubble:after{border-top-color:var(--ok)}.generate-control{position:relative;display:inline-flex;border-radius:8px;outline:none}.generate-control:focus-visible{box-shadow:0 0 0 3px var(--focus-ring)}pre{min-height:0;overflow:auto;margin:0;border-radius:0;padding:18px 112px 18px 18px;background:transparent;color:var(--code-ink);font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,ui-monospace,monospace;font-size:13px;line-height:1.55;white-space:pre}.mock-footer{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:14px}.mock-endpoint{display:flex;width:min(600px,100%);min-width:0;height:38px;align-items:center;overflow:hidden;border:1px solid var(--line-strong);border-radius:8px;background:var(--panel);color:var(--muted);font-size:13px;font-weight:700}.mock-endpoint>span{padding-left:12px;white-space:nowrap}.mock-endpoint input{width:100%;min-width:100px;height:100%;border:0;outline:none;padding:0 12px 0 0;background:transparent;color:var(--ink);font:inherit}.mock-endpoint:focus-within{border-color:var(--primary);box-shadow:0 0 0 3px var(--focus-ring)}.mock-endpoint input:disabled{color:var(--muted);cursor:not-allowed}.mock-endpoint input::placeholder{color:var(--muted);opacity:.72}.model-stat-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}.model-stat-grid{grid-template-columns:repeat(3,minmax(120px,1fr));gap:8px}.model-stat-grid>div{display:grid;min-width:0;gap:5px;border:1px solid var(--line);border-radius:8px;padding:10px;background:var(--panel-soft)}.model-stat-grid>div{gap:4px;min-height:52px;padding:7px 10px}.model-stat-dropdown{position:relative}.model-stat-trigger{width:100%;min-height:22px;justify-content:space-between;border:0;border-radius:4px;padding:0;background:transparent;color:var(--ink)}.model-stat-trigger:hover{background:transparent;color:var(--primary-dark)}.model-stat-trigger:disabled{background:transparent;color:var(--muted);cursor:wait}.model-stat-trigger strong{max-width:calc(100% - 24px)}.transformer-model-menu{top:calc(100% + 8px);right:0;left:0;width:auto}.provider-stat{display:flex;position:relative;padding:0}.provider-stat:hover{background:var(--hover-soft)}.provider-stat-card{display:grid;align-items:center;grid-template-columns:minmax(0,1fr) auto auto;column-gap:10px;height:100%;width:100%;min-height:42px;background:transparent;color:var(--ink)}.provider-dropdown-trigger{width:100%;min-height:42px;justify-content:flex-start;border:0;border-radius:0;padding:0 0 0 12px;background:transparent;color:var(--ink)}.provider-chevron-button{width:30px;min-height:42px;border:0;border-radius:0;padding:0 10px 0 0;background:transparent;color:var(--muted)}.provider-chevron-button:hover,.provider-dropdown-trigger:hover{background:transparent;color:var(--muted)}.provider-stat:hover .provider-copy span,.provider-stat:hover .provider-copy strong,.provider-stat:hover .provider-status,.provider-dropdown-trigger:hover .provider-copy span,.provider-dropdown-trigger:hover .provider-copy strong,.provider-dropdown-trigger:hover .provider-status{color:var(--muted)}.provider-copy{display:grid;min-width:0;gap:4px;text-align:left}.provider-copy span{color:var(--muted);font-size:11px;font-weight:750}.provider-copy strong{overflow:hidden;font-size:12px;line-height:1.3;text-overflow:ellipsis;white-space:nowrap}.provider-status{display:inline-flex;flex:0 0 auto;align-items:center;gap:6px;color:var(--muted);font-size:11px;font-weight:800;white-space:nowrap}.provider-status-wrap{display:grid;justify-items:end}.provider-status-dot{width:9px;height:9px;border:0;border-radius:999px;background:var(--muted)}.provider-status-dot.ready{background:var(--ok)}.provider-status-dot.available{background:var(--primary)}.provider-status-dot.loading{background:var(--warn)}.provider-status-dot.running{background:var(--ok)}.provider-status-dot.missing{background:var(--muted)}.provider-menu{left:0;right:auto;width:min(360px,calc(100vw - 48px))}.model-stat-grid span{color:var(--muted);font-size:12px;font-weight:750}.model-stat-grid span{font-size:11px}.model-stat-grid strong{overflow:hidden;font-size:13px;line-height:1.3;text-overflow:ellipsis;white-space:nowrap}.model-stat-grid strong{font-size:12px}.model-stat-grid .provider-copy span,.model-stat-grid .provider-status{font-size:11px}.model-stat-grid .provider-status span{color:inherit;font-size:inherit;font-weight:inherit}@media(max-width:980px){.app-shell{height:auto;overflow:visible;padding:0 16px 16px}.workspace{height:auto}.workspace-summary{grid-template-columns:1fr}.topbar{width:calc(100% + 32px);margin:0 -16px 18px;padding:12px 16px}.topbar-inner{align-items:flex-start;flex-direction:column}.title-line{flex-direction:column;gap:4px}.panels{grid-template-columns:1fr}.input-panel{overflow:visible}.field-group{min-height:250px}.prompt-group{min-height:160px;flex:.65}.output-panel{min-height:520px}.model-stat-grid{grid-template-columns:repeat(3,minmax(0,1fr))}}@media(max-width:560px){.app-shell{padding:0 10px 10px}.topbar{width:calc(100% + 20px);margin:0 -10px 14px;padding:12px 10px}.input-panel,.output-panel{padding:12px}.workspace-summary{gap:12px;margin-bottom:14px}.field-heading,.output-head{align-items:flex-start;flex-direction:column}.model-picker,.model-select,.actions,.mock-footer{width:100%}.mock-footer{align-items:stretch;flex-direction:column}.mock-endpoint,.mock-endpoint input{width:100%}button{flex:1 1 120px}.model-stat-grid{grid-template-columns:1fr}}
1
+ :root{color-scheme:light;--bg: #f6f7f9;--panel: #ffffff;--panel-soft: #fbfcfe;--ink: #15181f;--muted: #657080;--line: #d9dee7;--line-strong: #bcc5d2;--primary: #1663d9;--primary-dark: #0f4ca7;--code: #111827;--code-ink: #d6e2ff;--brand-ink: #152b3f;--brand-blue: #1597df;--hover-soft: #eef2f7;--ok: #16825d;--warn: #ad5b00;--ready-line: #a7dbc8;--warn-line: #f3cf9c;--focus-ring: rgba(22, 99, 217, .12);font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}[data-theme=dark]{color-scheme:dark;--bg: #0c1118;--panel: #111821;--panel-soft: #0f151e;--ink: #edf3fb;--muted: #92a0b2;--line: #263241;--line-strong: #3a485a;--primary: #2f9ff0;--primary-dark: #1b84d2;--code: #070b12;--code-ink: #d8e7ff;--brand-ink: #f2f7fd;--brand-blue: #39aaf4;--hover-soft: #1a2431;--ok: #68d6ae;--warn: #ffb75d;--ready-line: #2f735d;--warn-line: #7a5427;--focus-ring: rgba(47, 159, 240, .22)}*{box-sizing:border-box}body{margin:0;min-height:100vh;background:var(--bg);color:var(--ink)}button,textarea{font:inherit}.app-shell{height:100vh;overflow:hidden;padding:0 24px 24px}.workspace{display:flex;height:calc(100vh - 92px);min-height:0;flex-direction:column;max-width:1440px;margin:0 auto}.workspace-summary{display:block;margin-bottom:18px}.product-description{max-width:760px;margin:0;color:var(--muted);font-size:14px;line-height:1.5}.topbar{width:calc(100% + 48px);display:flex;align-items:center;justify-content:space-between;gap:16px;margin:0 -24px 18px;border-top:1px solid #55bca9;padding:12px 24px;background:#2d3e50;color:#f6f8fb}.topbar-inner{display:flex;width:100%;max-width:1440px;margin:0 auto;align-items:center;justify-content:space-between;gap:16px}.title-line{display:flex;min-width:0;align-items:baseline;gap:16px}.topbar-actions{display:flex;align-items:center;gap:10px}.eyebrow{margin:0 0 5px;color:var(--primary-dark);font-size:12px;font-weight:700;letter-spacing:0;text-transform:uppercase}.brand-logo{display:inline-flex;align-items:baseline;flex:0 0 auto;margin-bottom:0;font-size:clamp(18px,1.8vw,24px);font-weight:900;line-height:1;letter-spacing:0}.brand-logo span:first-child{color:#f6f8fb}.brand-logo span:last-child{color:#42d0b6}h1,h2{margin:0;letter-spacing:0}h1{font-size:15px;font-weight:400;line-height:1.25;color:#c8d4df}h2{font-size:20px}.status-pill{display:inline-flex;flex:0 0 auto;align-items:center;gap:7px;border:1px solid var(--line);border-radius:999px;padding:8px 12px;background:#ffffff0a;color:#d8e2eb;font-size:13px;font-weight:650}.status-pill.ready{border-color:var(--ready-line);color:var(--ok)}.status-pill.fallback{border-color:var(--warn-line);color:var(--warn)}.panels{display:grid;min-height:0;flex:1;grid-template-columns:minmax(360px,.8fr) minmax(420px,1.2fr);gap:18px;align-items:stretch}.input-panel,.output-panel{min-width:0;min-height:0;border:1px solid var(--line);border-radius:8px;background:var(--panel)}.input-panel{display:flex;flex-direction:column;gap:16px;overflow:hidden;padding:18px}.field-group{display:flex;min-height:0;flex:1;flex-direction:column;gap:8px}.prompt-group{min-height:140px;flex:0 0 210px}.field-heading,.output-head{display:flex;align-items:center;justify-content:space-between;gap:12px}label{font-weight:760}.field-heading span{color:var(--muted);font-size:12px;font-weight:650;text-align:right}textarea{width:100%;min-height:0;flex:1;resize:none;border:1px solid var(--line-strong);border-radius:8px;padding:13px;color:var(--ink);background:var(--panel-soft);line-height:1.45;outline:none}textarea:focus{border-color:var(--primary);box-shadow:0 0 0 3px var(--focus-ring)}.actions{display:flex;gap:10px;flex-wrap:wrap}button{display:inline-flex;min-height:38px;align-items:center;justify-content:center;gap:7px;border:1px solid transparent;border-radius:8px;padding:0 14px;background:var(--primary);color:#fff;cursor:pointer;font-weight:760}button:hover{background:var(--primary-dark)}button:disabled{cursor:wait;opacity:.68}button.secondary{border-color:var(--line-strong);background:var(--panel);color:var(--ink)}button.secondary:hover{border-color:var(--primary);color:var(--primary-dark)}button.mock-active{border-color:var(--ready-line);background:var(--ok);color:#fff}button.mock-active:hover{border-color:var(--ready-line);background:var(--ok)}button.ghost{background:transparent;color:var(--muted)}button.ghost:hover{background:var(--hover-soft);color:var(--ink)}button.load-provider-action{border-color:var(--line-strong);background:var(--panel);color:var(--ink)}button.load-provider-action:hover{border-color:var(--primary);background:var(--hover-soft);color:var(--primary-dark)}button.load-provider-action:disabled{background:transparent;color:var(--muted);cursor:not-allowed;opacity:.72}.provider-progress-text{min-height:18px;margin:-6px 0 0;color:var(--muted);font-size:12px;font-weight:650;line-height:1.4}.spin-icon{animation:spin .9s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}button.icon-button{width:38px;padding:0;border-color:#ffffff2e;background:#ffffff0a;color:#d8e2eb}button.icon-button:hover{border-color:#42d0b6;background:#42d0b61f;color:#42d0b6}.output-panel{display:flex;min-height:0;overflow:visible;flex-direction:column;padding:18px}.output-head{margin-bottom:14px}.model-picker{position:relative;flex:0 0 auto}.model-select{min-width:260px;justify-content:space-between;border-color:var(--line-strong);background:var(--panel);color:var(--ink)}.model-select:hover{border-color:var(--primary);background:var(--panel);color:var(--primary-dark)}.model-select>span,.model-menu-item>span:first-child{display:grid;min-width:0;text-align:left}.model-select-label,.model-option-label{overflow:hidden;font-size:13px;font-weight:800;line-height:1.15;text-overflow:ellipsis;white-space:nowrap}.model-select-detail,.model-option-detail{overflow:hidden;color:var(--muted);font-size:11px;font-weight:650;line-height:1.25;text-overflow:ellipsis;white-space:nowrap}.model-menu{position:absolute;top:calc(100% + 8px);right:0;z-index:5;width:min(360px,calc(100vw - 48px));overflow:hidden;border:1px solid var(--line);border-radius:8px;background:var(--panel);box-shadow:0 18px 48px #1118272e}.model-menu-item{width:100%;min-height:52px;justify-content:space-between;gap:16px;border:0;border-radius:0;padding:9px 12px;background:var(--panel);color:var(--ink)}.model-menu-item:hover{background:var(--hover-soft);color:var(--ink)}.model-menu-item[aria-selected=true]{background:#1663d914}.model-menu-item:disabled{cursor:not-allowed;opacity:1}.model-menu-item:disabled .model-option-label,.model-menu-item:disabled .model-option-detail{color:var(--muted);opacity:.62}.model-unavailable{flex:0 0 auto;color:var(--muted);font-size:10px;font-weight:750;line-height:1;opacity:.7}.code-wrap{position:relative;display:grid;min-height:0;flex:1;overflow:visible;border-radius:8px;background:var(--code)}.code-actions{position:absolute;top:12px;right:26px;z-index:1000;display:flex;gap:8px}.code-icon-button{position:relative;width:36px;min-height:36px;padding:0;border-color:#d8e2eb33;background:#2d3e50db;color:#d8e2eb;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}.code-icon-button:hover{border-color:#42d0b6;background:#2d3e50;color:#42d0b6}.code-icon-button.copied{border-color:var(--ready-line);background:var(--ok);color:#fff}.json-editor-shell{display:flex;min-width:0;min-height:0;flex:1;flex-direction:column}.json-editor{width:100%;min-height:0;flex:1;resize:none;border:0;border-radius:8px;outline:none;padding:18px 112px 18px 18px;background:transparent;color:var(--code-ink);font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,ui-monospace,monospace;font-size:13px;line-height:1.55;white-space:pre}.json-editor:focus{box-shadow:inset 0 0 0 1px #42d0b6a6}.json-editor-error{margin:0;border-top:1px solid var(--warn-line);padding:9px 14px;background:#ad5b0021;color:var(--warn);font-size:12px;font-weight:700;line-height:1.4}.tooltip-bubble{position:absolute;z-index:1001;right:50%;bottom:calc(100% + 10px);transform:translate(50%);visibility:hidden;opacity:0;pointer-events:none;white-space:nowrap;border-radius:6px;padding:7px 9px;background:#2d3e50;color:#f6f8fb;font-size:12px;font-weight:700;transition:opacity .16s ease,visibility .16s ease}.tooltip-bubble:after{position:absolute;top:100%;left:50%;width:0;height:0;border:6px solid transparent;border-top-color:#2d3e50;content:"";transform:translate(-50%)}.copy-control:hover .tooltip-bubble,.copy-control:focus-visible .tooltip-bubble,.copy-control.copied .tooltip-bubble,.generate-control.needs-model:hover .tooltip-bubble,.generate-control.needs-model:focus-visible .tooltip-bubble,.generate-control.needs-model:focus-within .tooltip-bubble{visibility:visible;opacity:1}.copy-control.copied .tooltip-bubble{background:var(--ok)}.copy-control.copied .tooltip-bubble:after{border-top-color:var(--ok)}.generate-control{position:relative;display:inline-flex;border-radius:8px;outline:none}.generate-control:focus-visible{box-shadow:0 0 0 3px var(--focus-ring)}pre{min-height:0;overflow:auto;margin:0;border-radius:0;padding:18px 112px 18px 18px;background:transparent;color:var(--code-ink);font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,ui-monospace,monospace;font-size:13px;line-height:1.55;white-space:pre}.mock-footer{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:14px}.mock-endpoint{position:relative;display:flex;width:min(600px,100%);min-width:0;height:38px;align-items:center;overflow:visible;border:1px solid var(--line-strong);border-radius:8px;background:var(--panel);color:var(--muted);font-size:13px;font-weight:700}.mock-endpoint-prefix{flex:0 0 auto;padding:0 0 0 12px;color:var(--muted);white-space:nowrap}.mock-endpoint-locked{cursor:pointer}.mock-endpoint-locked:hover,.mock-endpoint-locked:focus-visible{border-color:var(--primary);box-shadow:0 0 0 3px var(--focus-ring);outline:none}.mock-endpoint input{width:100%;min-width:100px;height:100%;border:0;outline:none;padding:0 12px 0 0;background:transparent;color:var(--ink);font:inherit}.mock-endpoint:focus-within{border-color:var(--primary);box-shadow:0 0 0 3px var(--focus-ring)}.mock-endpoint input:disabled{color:var(--muted);cursor:pointer;pointer-events:none}.mock-endpoint input::placeholder{color:var(--muted);opacity:.72}.model-stat-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}.model-stat-grid{grid-template-columns:repeat(3,minmax(120px,1fr));gap:8px}.model-stat-grid>div{display:grid;min-width:0;gap:5px;border:1px solid var(--line);border-radius:8px;padding:10px;background:var(--panel-soft)}.model-stat-grid>div{gap:4px;min-height:52px;padding:7px 10px}.model-stat-dropdown{position:relative}.model-stat-trigger{width:100%;min-height:22px;justify-content:space-between;border:0;border-radius:4px;padding:0;background:transparent;color:var(--ink)}.model-stat-trigger:hover{background:transparent;color:var(--primary-dark)}.model-stat-trigger:disabled{background:transparent;color:var(--muted);cursor:wait}.model-stat-trigger strong{max-width:calc(100% - 24px)}.transformer-model-menu{top:calc(100% + 8px);right:0;left:0;width:auto}.provider-stat{display:flex;position:relative;padding:0}.provider-stat:hover{background:var(--hover-soft)}.provider-stat-card{display:grid;align-items:center;grid-template-columns:minmax(0,1fr) auto auto;column-gap:10px;height:100%;width:100%;min-height:42px;background:transparent;color:var(--ink)}.provider-dropdown-trigger{width:100%;min-height:42px;justify-content:flex-start;border:0;border-radius:0;padding:0 0 0 12px;background:transparent;color:var(--ink)}.provider-chevron-button{width:30px;min-height:42px;border:0;border-radius:0;padding:0 10px 0 0;background:transparent;color:var(--muted)}.provider-chevron-button:hover,.provider-dropdown-trigger:hover{background:transparent;color:var(--muted)}.provider-stat:hover .provider-copy span,.provider-stat:hover .provider-copy strong,.provider-stat:hover .provider-status,.provider-dropdown-trigger:hover .provider-copy span,.provider-dropdown-trigger:hover .provider-copy strong,.provider-dropdown-trigger:hover .provider-status{color:var(--muted)}.provider-copy{display:grid;min-width:0;gap:4px;text-align:left}.provider-copy span{color:var(--muted);font-size:11px;font-weight:750}.provider-copy strong{overflow:hidden;font-size:12px;line-height:1.3;text-overflow:ellipsis;white-space:nowrap}.provider-status{display:inline-flex;flex:0 0 auto;align-items:center;gap:6px;color:var(--muted);font-size:11px;font-weight:800;white-space:nowrap}.provider-status-wrap{display:grid;justify-items:end}.provider-status-dot{width:9px;height:9px;border:0;border-radius:999px;background:var(--muted)}.provider-status-dot.ready{background:var(--ok)}.provider-status-dot.available{background:var(--primary)}.provider-status-dot.loading{background:var(--warn)}.provider-status-dot.running{background:var(--ok)}.provider-status-dot.missing{background:var(--muted)}.provider-menu{left:0;right:auto;width:min(360px,calc(100vw - 48px))}.model-stat-grid span{color:var(--muted);font-size:12px;font-weight:750}.model-stat-grid span{font-size:11px}.model-stat-grid strong{overflow:hidden;font-size:13px;line-height:1.3;text-overflow:ellipsis;white-space:nowrap}.model-stat-grid strong{font-size:12px}.model-stat-grid .provider-copy span,.model-stat-grid .provider-status{font-size:11px}.model-stat-grid .provider-status span{color:inherit;font-size:inherit;font-weight:inherit}@media(max-width:980px){.app-shell{height:auto;overflow:visible;padding:0 16px 16px}.workspace{height:auto}.workspace-summary{grid-template-columns:1fr}.topbar{width:calc(100% + 32px);margin:0 -16px 18px;padding:12px 16px}.topbar-inner{align-items:flex-start;flex-direction:column}.title-line{flex-direction:column;gap:4px}.panels{grid-template-columns:1fr}.input-panel{overflow:visible}.field-group{min-height:250px}.prompt-group{min-height:160px;flex:.65}.output-panel{min-height:520px}.model-stat-grid{grid-template-columns:repeat(3,minmax(0,1fr))}}@media(max-width:560px){.app-shell{padding:0 10px 10px}.topbar{width:calc(100% + 20px);margin:0 -10px 14px;padding:12px 10px}.input-panel,.output-panel{padding:12px}.workspace-summary{gap:12px;margin-bottom:14px}.field-heading,.output-head{align-items:flex-start;flex-direction:column}.model-picker,.model-select,.actions,.mock-footer{width:100%}.mock-footer{align-items:stretch;flex-direction:column}.mock-endpoint,.mock-endpoint input{width:100%}button{flex:1 1 120px}.model-stat-grid{grid-template-columns:1fr}}
@@ -1,12 +1,12 @@
1
- <!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="modulepreload" href="/assets/manifest-08a95dab.js"/><link rel="modulepreload" href="/assets/entry.client-Cw45cONA.js"/><link rel="modulepreload" href="/assets/jsx-runtime-BZbza59V.js"/><link rel="modulepreload" href="/assets/root-Dlix2DV-.js"/><link rel="stylesheet" href="/assets/root-Di4etcLz.css"/></head><body><script>
1
+ <!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="modulepreload" href="/assets/manifest-cfa23094.js"/><link rel="modulepreload" href="/assets/entry.client-Cw45cONA.js"/><link rel="modulepreload" href="/assets/jsx-runtime-BZbza59V.js"/><link rel="modulepreload" href="/assets/root-BlLsNrT5.js"/><link rel="stylesheet" href="/assets/root-C9U4kG7U.css"/></head><body><script>
2
2
  console.log(
3
3
  "💿 Hey developer 👋. You can provide a way better UX than this " +
4
4
  "when your app is loading JS modules and/or running `clientLoader` " +
5
5
  "functions. Check out https://reactrouter.com/start/framework/route-module#hydratefallback " +
6
6
  "for more information."
7
7
  );
8
- </script><script>window.__reactRouterContext = {"basename":"/","future":{"unstable_optimizeDeps":false,"v8_passThroughRequests":false,"v8_trailingSlashAwareDataRequests":false,"unstable_previewServerPrerendering":false,"v8_middleware":false,"v8_splitRouteModules":false,"v8_viteEnvironmentApi":false},"routeDiscovery":{"mode":"initial"},"ssr":false,"isSpaMode":true};window.__reactRouterContext.stream = new ReadableStream({start(controller){window.__reactRouterContext.streamController = controller;}}).pipeThrough(new TextEncoderStream());</script><script type="module" async="">import "/assets/manifest-08a95dab.js";
9
- import * as route0 from "/assets/root-Dlix2DV-.js";
8
+ </script><script>window.__reactRouterContext = {"basename":"/","future":{"unstable_optimizeDeps":false,"v8_passThroughRequests":false,"v8_trailingSlashAwareDataRequests":false,"unstable_previewServerPrerendering":false,"v8_middleware":false,"v8_splitRouteModules":false,"v8_viteEnvironmentApi":false},"routeDiscovery":{"mode":"initial"},"ssr":false,"isSpaMode":true};window.__reactRouterContext.stream = new ReadableStream({start(controller){window.__reactRouterContext.streamController = controller;}}).pipeThrough(new TextEncoderStream());</script><script type="module" async="">import "/assets/manifest-cfa23094.js";
9
+ import * as route0 from "/assets/root-BlLsNrT5.js";
10
10
 
11
11
  window.__reactRouterRouteModules = {"root":route0};
12
12
 
package/dist/cli.js CHANGED
@@ -38,28 +38,40 @@ async function startMocklaneServers({
38
38
  proxyPort,
39
39
  uiPort,
40
40
  target,
41
- uiDirectory
41
+ uiDirectory,
42
+ allowPortFallback = false
42
43
  }) {
43
44
  const registry = new MockRegistry();
44
- const proxyServer = createServer((request, response) => {
45
- handleProxyRequest(request, response, registry, target);
46
- });
47
- const uiServer = createServer((request, response) => {
48
- void handleUiRequest(request, response, registry, uiDirectory, proxyPort);
49
- });
50
- try {
51
- await listen(proxyServer, proxyPort, host);
52
- await listen(uiServer, uiPort, host);
53
- } catch (error) {
54
- await closeServer(proxyServer);
55
- await closeServer(uiServer);
56
- throw error;
57
- }
58
- return {
59
- close: async () => {
60
- await Promise.all([closeServer(proxyServer), closeServer(uiServer)]);
45
+ let candidateProxyPort = proxyPort;
46
+ let candidateUiPort = uiPort;
47
+ while (candidateProxyPort <= 65534 && candidateUiPort <= 65535) {
48
+ const activeProxyPort = candidateProxyPort;
49
+ const activeUiPort = candidateUiPort;
50
+ const proxyServer = createServer((request, response) => {
51
+ handleProxyRequest(request, response, registry, target);
52
+ });
53
+ const uiServer = createServer((request, response) => {
54
+ void handleUiRequest(request, response, registry, uiDirectory, activeProxyPort);
55
+ });
56
+ try {
57
+ await listen(proxyServer, activeProxyPort, host);
58
+ await listen(uiServer, activeUiPort, host);
59
+ return {
60
+ proxyPort: activeProxyPort,
61
+ uiPort: activeUiPort,
62
+ close: async () => {
63
+ await Promise.all([closeServer(proxyServer), closeServer(uiServer)]);
64
+ }
65
+ };
66
+ } catch (error) {
67
+ await closeServer(proxyServer);
68
+ await closeServer(uiServer);
69
+ if (!allowPortFallback || !isAddressInUse(error)) throw error;
70
+ candidateProxyPort += 2;
71
+ candidateUiPort += 2;
61
72
  }
62
- };
73
+ }
74
+ throw new Error("Mocklane could not find an available local port pair.");
63
75
  }
64
76
  async function handleUiRequest(request, response, registry, uiDirectory, proxyPort) {
65
77
  const url = new URL(request.url ?? "/", "http://localhost");
@@ -225,6 +237,9 @@ function listen(server, port, host) {
225
237
  });
226
238
  });
227
239
  }
240
+ function isAddressInUse(error) {
241
+ return error instanceof Error && "code" in error && error.code === "EADDRINUSE";
242
+ }
228
243
  function closeServer(server) {
229
244
  return new Promise((resolvePromise) => {
230
245
  if (!server.listening) {
@@ -251,11 +266,15 @@ async function main() {
251
266
  proxyPort: options.proxyPort,
252
267
  uiPort: options.uiPort,
253
268
  target: options.target,
254
- uiDirectory
269
+ uiDirectory,
270
+ allowPortFallback: !options.hasCustomPorts
255
271
  });
256
- const uiUrl = `http://localhost:${options.uiPort}`;
272
+ const uiUrl = `http://localhost:${servers.uiPort}`;
273
+ if (!options.hasCustomPorts && (servers.proxyPort !== DEFAULT_PROXY_PORT || servers.uiPort !== DEFAULT_UI_PORT)) {
274
+ console.log(red(`Port ${DEFAULT_PROXY_PORT} is in use. Using ${servers.proxyPort} instead.`));
275
+ }
257
276
  console.log(`Mocklane UI: ${uiUrl}`);
258
- console.log(`Mock API proxy: http://localhost:${options.proxyPort}`);
277
+ console.log(`Mock API proxy: http://localhost:${servers.proxyPort}`);
259
278
  console.log(`Upstream server: ${options.target?.origin ?? "none (standalone mode)"}`);
260
279
  console.log("Press Ctrl+C to stop.");
261
280
  openBrowser(uiUrl);
@@ -271,16 +290,21 @@ function parseArguments(argumentsList) {
271
290
  let proxyPort = DEFAULT_PROXY_PORT;
272
291
  let uiPort = DEFAULT_UI_PORT;
273
292
  let help = false;
293
+ let hasCustomPorts = false;
274
294
  for (let index = 0; index < argumentsList.length; index += 1) {
275
295
  const argument = argumentsList[index];
276
296
  if (argument === "--target") target = new URL(requireValue(argumentsList, ++index, argument));
277
- else if (argument === "--proxy-port") proxyPort = parsePort(requireValue(argumentsList, ++index, argument));
278
- else if (argument === "--ui-port") uiPort = parsePort(requireValue(argumentsList, ++index, argument));
279
- else if (argument === "--help" || argument === "-h") help = true;
297
+ else if (argument === "--proxy-port") {
298
+ proxyPort = parsePort(requireValue(argumentsList, ++index, argument));
299
+ hasCustomPorts = true;
300
+ } else if (argument === "--ui-port") {
301
+ uiPort = parsePort(requireValue(argumentsList, ++index, argument));
302
+ hasCustomPorts = true;
303
+ } else if (argument === "--help" || argument === "-h") help = true;
280
304
  else throw new Error(`Unknown argument: ${argument}`);
281
305
  }
282
306
  if (proxyPort === uiPort) throw new Error("Proxy and UI ports must be different.");
283
- return { target, proxyPort, uiPort, help };
307
+ return { target, proxyPort, uiPort, help, hasCustomPorts };
284
308
  }
285
309
  function requireValue(argumentsList, index, flag) {
286
310
  const value = argumentsList[index];
@@ -299,6 +323,9 @@ function openBrowser(url) {
299
323
  const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
300
324
  spawn(command, args, { detached: true, stdio: "ignore" }).unref();
301
325
  }
326
+ function red(message) {
327
+ return `\x1B[31m${message}\x1B[0m`;
328
+ }
302
329
  function printHelp() {
303
330
  console.log(`Mocklane local mock proxy
304
331
 
package/dist/cli.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../cli/index.ts", "../cli/server.ts", "../cli/mockRegistry.ts"],
4
- "sourcesContent": ["#!/usr/bin/env node\n\nimport { spawn } from \"node:child_process\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { startMocklaneServers } from \"./server\";\n\nconst DEFAULT_HOST = \"127.0.0.1\";\nconst DEFAULT_PROXY_PORT = 4000;\nconst DEFAULT_UI_PORT = 4001;\n\nasync function main() {\n const options = parseArguments(process.argv.slice(2));\n if (options.help) {\n printHelp();\n return;\n }\n\n const uiDirectory = fileURLToPath(new URL(\"../build/client\", import.meta.url));\n const servers = await startMocklaneServers({\n host: DEFAULT_HOST,\n proxyPort: options.proxyPort,\n uiPort: options.uiPort,\n target: options.target,\n uiDirectory,\n });\n const uiUrl = `http://localhost:${options.uiPort}`;\n\n console.log(`Mocklane UI: ${uiUrl}`);\n console.log(`Mock API proxy: http://localhost:${options.proxyPort}`);\n console.log(`Upstream server: ${options.target?.origin ?? \"none (standalone mode)\"}`);\n console.log(\"Press Ctrl+C to stop.\");\n\n openBrowser(uiUrl);\n\n const shutdown = async () => {\n await servers.close();\n process.exit(0);\n };\n process.once(\"SIGINT\", () => void shutdown());\n process.once(\"SIGTERM\", () => void shutdown());\n}\n\nfunction parseArguments(argumentsList: string[]) {\n let target: URL | null = null;\n let proxyPort = DEFAULT_PROXY_PORT;\n let uiPort = DEFAULT_UI_PORT;\n let help = false;\n\n for (let index = 0; index < argumentsList.length; index += 1) {\n const argument = argumentsList[index];\n if (argument === \"--target\") target = new URL(requireValue(argumentsList, ++index, argument));\n else if (argument === \"--proxy-port\") proxyPort = parsePort(requireValue(argumentsList, ++index, argument));\n else if (argument === \"--ui-port\") uiPort = parsePort(requireValue(argumentsList, ++index, argument));\n else if (argument === \"--help\" || argument === \"-h\") help = true;\n else throw new Error(`Unknown argument: ${argument}`);\n }\n\n if (proxyPort === uiPort) throw new Error(\"Proxy and UI ports must be different.\");\n return { target, proxyPort, uiPort, help };\n}\n\nfunction requireValue(argumentsList: string[], index: number, flag: string) {\n const value = argumentsList[index];\n if (!value) throw new Error(`${flag} requires a value.`);\n return value;\n}\n\nfunction parsePort(value: string) {\n const port = Number(value);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n throw new Error(`Invalid port: ${value}`);\n }\n return port;\n}\n\nfunction openBrowser(url: string) {\n const command = process.platform === \"darwin\" ? \"open\" : process.platform === \"win32\" ? \"cmd\" : \"xdg-open\";\n const args = process.platform === \"win32\" ? [\"/c\", \"start\", \"\", url] : [url];\n spawn(command, args, { detached: true, stdio: \"ignore\" }).unref();\n}\n\nfunction printHelp() {\n console.log(`Mocklane local mock proxy\n\nUsage:\n mocklane [options]\n\nOptions:\n --target <url> Optional upstream API URL for unmatched routes\n --proxy-port <port> Mock/proxy port (default: 4000)\n --ui-port <port> Mocklane UI port (default: 4001)\n -h, --help Show this help`);\n}\n\nvoid main().catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : error);\n process.exit(1);\n});\n", "import { createReadStream, existsSync, statSync } from \"node:fs\";\nimport { createServer, type IncomingMessage, type Server, type ServerResponse } from \"node:http\";\nimport { request as requestHttp } from \"node:http\";\nimport { request as requestHttps } from \"node:https\";\nimport { extname, resolve } from \"node:path\";\n\nimport type { MockHttpResponse } from \"../src/types/mockApi\";\nimport { MockRegistry } from \"./mockRegistry\";\n\nconst MAX_CONTROL_BODY_BYTES = 10 * 1024 * 1024;\n\nexport type MocklaneServers = {\n close: () => Promise<void>;\n};\n\nexport async function startMocklaneServers({\n host,\n proxyPort,\n uiPort,\n target,\n uiDirectory,\n}: {\n host: string;\n proxyPort: number;\n uiPort: number;\n target: URL | null;\n uiDirectory: string;\n}): Promise<MocklaneServers> {\n const registry = new MockRegistry();\n const proxyServer = createServer((request, response) => {\n handleProxyRequest(request, response, registry, target);\n });\n const uiServer = createServer((request, response) => {\n void handleUiRequest(request, response, registry, uiDirectory, proxyPort);\n });\n\n try {\n await listen(proxyServer, proxyPort, host);\n await listen(uiServer, uiPort, host);\n } catch (error) {\n await closeServer(proxyServer);\n await closeServer(uiServer);\n throw error;\n }\n\n return {\n close: async () => {\n await Promise.all([closeServer(proxyServer), closeServer(uiServer)]);\n },\n };\n}\n\nasync function handleUiRequest(\n request: IncomingMessage,\n response: ServerResponse,\n registry: MockRegistry,\n uiDirectory: string,\n proxyPort: number,\n) {\n const url = new URL(request.url ?? \"/\", \"http://localhost\");\n\n if (url.pathname === \"/__mocklane/health\" && request.method === \"GET\") {\n sendJson(response, 200, { ready: true, proxyPort, mocks: registry.list().length });\n return;\n }\n\n if (url.pathname === \"/__mocklane/mocks\" && request.method === \"GET\") {\n sendJson(response, 200, registry.list());\n return;\n }\n\n if (url.pathname === \"/__mocklane/mocks\" && request.method === \"PUT\") {\n try {\n const mock = validateMockResponse(await readJsonBody(request));\n registry.set(mock);\n sendJson(response, 200, { active: true, method: mock.method, path: mock.path, proxyPort });\n } catch (error) {\n sendJson(response, 400, {\n error: error instanceof Error ? error.message : \"Invalid mock response.\",\n });\n }\n return;\n }\n\n if (url.pathname === \"/__mocklane/mocks\" && request.method === \"DELETE\") {\n try {\n const mock = validateMockResponse(await readJsonBody(request));\n registry.delete(mock.method, mock.path);\n sendJson(response, 200, { active: false, method: mock.method, path: mock.path });\n } catch (error) {\n sendJson(response, 400, {\n error: error instanceof Error ? error.message : \"Invalid mock response.\",\n });\n }\n return;\n }\n\n serveUiFile(response, uiDirectory, url.pathname, request.method === \"HEAD\");\n}\n\nfunction handleProxyRequest(\n request: IncomingMessage,\n response: ServerResponse,\n registry: MockRegistry,\n target: URL | null,\n) {\n const requestUrl = new URL(request.url ?? \"/\", target ?? \"http://localhost\");\n const preflightMethod = request.headers[\"access-control-request-method\"];\n const preflightMock =\n request.method === \"OPTIONS\" && typeof preflightMethod === \"string\"\n ? registry.get(preflightMethod, requestUrl.pathname)\n : undefined;\n if (preflightMock && typeof preflightMethod === \"string\") {\n response.writeHead(204, {\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-methods\": preflightMethod.toUpperCase(),\n \"access-control-allow-headers\": request.headers[\"access-control-request-headers\"] ?? \"content-type\",\n });\n response.end();\n return;\n }\n\n const mock = registry.get(request.method ?? \"GET\", requestUrl.pathname);\n\n if (mock) {\n response.writeHead(mock.status, {\n ...mock.headers,\n \"access-control-allow-origin\": mock.headers[\"access-control-allow-origin\"] ?? \"*\",\n });\n response.end(JSON.stringify(mock.body));\n return;\n }\n\n if (!target) {\n sendJson(response, 404, {\n error: \"No Mocklane mock matches this route.\",\n method: request.method ?? \"GET\",\n path: requestUrl.pathname,\n });\n return;\n }\n\n const upstreamUrl = new URL(request.url ?? \"/\", target);\n const upstreamRequest = (upstreamUrl.protocol === \"https:\" ? requestHttps : requestHttp)(\n upstreamUrl,\n {\n method: request.method,\n headers: { ...request.headers, host: upstreamUrl.host },\n },\n (upstreamResponse) => {\n response.writeHead(upstreamResponse.statusCode ?? 502, upstreamResponse.headers);\n upstreamResponse.pipe(response);\n },\n );\n\n upstreamRequest.on(\"error\", (error) => {\n if (!response.headersSent) {\n sendJson(response, 502, {\n error: \"Mocklane could not reach the upstream server.\",\n detail: error.message,\n });\n return;\n }\n response.destroy(error);\n });\n request.pipe(upstreamRequest);\n}\n\nfunction serveUiFile(\n response: ServerResponse,\n uiDirectory: string,\n pathname: string,\n headOnly: boolean,\n) {\n const requestedPath = pathname === \"/\" ? \"/index.html\" : pathname;\n const candidate = resolve(uiDirectory, `.${decodeURIComponent(requestedPath)}`);\n const uiRoot = resolve(uiDirectory);\n const filePath =\n candidate.startsWith(`${uiRoot}/`) && existsSync(candidate) && statSync(candidate).isFile()\n ? candidate\n : resolve(uiDirectory, \"index.html\");\n\n if (!existsSync(filePath)) {\n sendJson(response, 503, {\n error: \"Mocklane UI has not been built. Run `pnpm build` first.\",\n });\n return;\n }\n\n response.writeHead(200, {\n \"content-type\": contentType(filePath),\n \"cache-control\": filePath.endsWith(\"index.html\") ? \"no-cache\" : \"public, max-age=31536000, immutable\",\n });\n if (headOnly) {\n response.end();\n return;\n }\n createReadStream(filePath).pipe(response);\n}\n\nasync function readJsonBody(request: IncomingMessage) {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of request) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n size += buffer.length;\n if (size > MAX_CONTROL_BODY_BYTES) {\n throw new Error(\"Mock response exceeds the 10 MB limit.\");\n }\n chunks.push(buffer);\n }\n return JSON.parse(Buffer.concat(chunks).toString(\"utf8\")) as unknown;\n}\n\nfunction validateMockResponse(value: unknown): MockHttpResponse {\n if (!value || typeof value !== \"object\") throw new Error(\"Mock response must be an object.\");\n const mock = value as Partial<MockHttpResponse>;\n if (typeof mock.method !== \"string\" || !mock.method.trim()) throw new Error(\"Method is required.\");\n if (typeof mock.path !== \"string\" || !mock.path.startsWith(\"/\")) {\n throw new Error(\"Path must begin with `/`.\");\n }\n if (!Number.isInteger(mock.status) || Number(mock.status) < 100 || Number(mock.status) > 599) {\n throw new Error(\"Status must be a valid HTTP status code.\");\n }\n if (!mock.headers || typeof mock.headers !== \"object\" || Array.isArray(mock.headers)) {\n throw new Error(\"Headers must be an object.\");\n }\n return mock as MockHttpResponse;\n}\n\nfunction sendJson(response: ServerResponse, status: number, body: unknown) {\n response.writeHead(status, { \"content-type\": \"application/json; charset=utf-8\" });\n response.end(JSON.stringify(body));\n}\n\nfunction contentType(filePath: string) {\n const types: Record<string, string> = {\n \".css\": \"text/css; charset=utf-8\",\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".png\": \"image/png\",\n \".svg\": \"image/svg+xml\",\n \".wasm\": \"application/wasm\",\n };\n return types[extname(filePath)] ?? \"application/octet-stream\";\n}\n\nfunction listen(server: Server, port: number, host: string) {\n return new Promise<void>((resolvePromise, reject) => {\n server.once(\"error\", reject);\n server.listen(port, host, () => {\n server.off(\"error\", reject);\n resolvePromise();\n });\n });\n}\n\nfunction closeServer(server: Server) {\n return new Promise<void>((resolvePromise) => {\n if (!server.listening) {\n resolvePromise();\n return;\n }\n server.close(() => resolvePromise());\n });\n}\n", "import type { MockHttpResponse } from \"../src/types/mockApi\";\n\nexport class MockRegistry {\n readonly #mocks = new Map<string, MockHttpResponse>();\n\n set(response: MockHttpResponse) {\n this.#mocks.set(mockKey(response.method, response.path), response);\n }\n\n get(method: string, path: string) {\n return this.#mocks.get(mockKey(method, path));\n }\n\n delete(method: string, path: string) {\n return this.#mocks.delete(mockKey(method, path));\n }\n\n list() {\n return [...this.#mocks.values()];\n }\n}\n\nfunction mockKey(method: string, path: string) {\n return `${method.toUpperCase()} ${path}`;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["#!/usr/bin/env node\n\nimport { spawn } from \"node:child_process\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { startMocklaneServers } from \"./server\";\n\nconst DEFAULT_HOST = \"127.0.0.1\";\nconst DEFAULT_PROXY_PORT = 4000;\nconst DEFAULT_UI_PORT = 4001;\n\nasync function main() {\n const options = parseArguments(process.argv.slice(2));\n if (options.help) {\n printHelp();\n return;\n }\n\n const uiDirectory = fileURLToPath(new URL(\"../build/client\", import.meta.url));\n const servers = await startMocklaneServers({\n host: DEFAULT_HOST,\n proxyPort: options.proxyPort,\n uiPort: options.uiPort,\n target: options.target,\n uiDirectory,\n allowPortFallback: !options.hasCustomPorts,\n });\n const uiUrl = `http://localhost:${servers.uiPort}`;\n\n if (!options.hasCustomPorts && (servers.proxyPort !== DEFAULT_PROXY_PORT || servers.uiPort !== DEFAULT_UI_PORT)) {\n console.log(red(`Port ${DEFAULT_PROXY_PORT} is in use. Using ${servers.proxyPort} instead.`));\n }\n console.log(`Mocklane UI: ${uiUrl}`);\n console.log(`Mock API proxy: http://localhost:${servers.proxyPort}`);\n console.log(`Upstream server: ${options.target?.origin ?? \"none (standalone mode)\"}`);\n console.log(\"Press Ctrl+C to stop.\");\n\n openBrowser(uiUrl);\n\n const shutdown = async () => {\n await servers.close();\n process.exit(0);\n };\n process.once(\"SIGINT\", () => void shutdown());\n process.once(\"SIGTERM\", () => void shutdown());\n}\n\nfunction parseArguments(argumentsList: string[]) {\n let target: URL | null = null;\n let proxyPort = DEFAULT_PROXY_PORT;\n let uiPort = DEFAULT_UI_PORT;\n let help = false;\n let hasCustomPorts = false;\n\n for (let index = 0; index < argumentsList.length; index += 1) {\n const argument = argumentsList[index];\n if (argument === \"--target\") target = new URL(requireValue(argumentsList, ++index, argument));\n else if (argument === \"--proxy-port\") {\n proxyPort = parsePort(requireValue(argumentsList, ++index, argument));\n hasCustomPorts = true;\n } else if (argument === \"--ui-port\") {\n uiPort = parsePort(requireValue(argumentsList, ++index, argument));\n hasCustomPorts = true;\n }\n else if (argument === \"--help\" || argument === \"-h\") help = true;\n else throw new Error(`Unknown argument: ${argument}`);\n }\n\n if (proxyPort === uiPort) throw new Error(\"Proxy and UI ports must be different.\");\n return { target, proxyPort, uiPort, help, hasCustomPorts };\n}\n\nfunction requireValue(argumentsList: string[], index: number, flag: string) {\n const value = argumentsList[index];\n if (!value) throw new Error(`${flag} requires a value.`);\n return value;\n}\n\nfunction parsePort(value: string) {\n const port = Number(value);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n throw new Error(`Invalid port: ${value}`);\n }\n return port;\n}\n\nfunction openBrowser(url: string) {\n const command = process.platform === \"darwin\" ? \"open\" : process.platform === \"win32\" ? \"cmd\" : \"xdg-open\";\n const args = process.platform === \"win32\" ? [\"/c\", \"start\", \"\", url] : [url];\n spawn(command, args, { detached: true, stdio: \"ignore\" }).unref();\n}\n\nfunction red(message: string) {\n return `\\x1b[31m${message}\\x1b[0m`;\n}\n\nfunction printHelp() {\n console.log(`Mocklane local mock proxy\n\nUsage:\n mocklane [options]\n\nOptions:\n --target <url> Optional upstream API URL for unmatched routes\n --proxy-port <port> Mock/proxy port (default: 4000)\n --ui-port <port> Mocklane UI port (default: 4001)\n -h, --help Show this help`);\n}\n\nvoid main().catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : error);\n process.exit(1);\n});\n", "import { createReadStream, existsSync, statSync } from \"node:fs\";\nimport { createServer, type IncomingMessage, type Server, type ServerResponse } from \"node:http\";\nimport { request as requestHttp } from \"node:http\";\nimport { request as requestHttps } from \"node:https\";\nimport { extname, resolve } from \"node:path\";\n\nimport type { MockHttpResponse } from \"../src/types/mockApi\";\nimport { MockRegistry } from \"./mockRegistry\";\n\nconst MAX_CONTROL_BODY_BYTES = 10 * 1024 * 1024;\n\nexport type MocklaneServers = {\n proxyPort: number;\n uiPort: number;\n close: () => Promise<void>;\n};\n\nexport async function startMocklaneServers({\n host,\n proxyPort,\n uiPort,\n target,\n uiDirectory,\n allowPortFallback = false,\n}: {\n host: string;\n proxyPort: number;\n uiPort: number;\n target: URL | null;\n uiDirectory: string;\n allowPortFallback?: boolean;\n}): Promise<MocklaneServers> {\n const registry = new MockRegistry();\n let candidateProxyPort = proxyPort;\n let candidateUiPort = uiPort;\n\n while (candidateProxyPort <= 65534 && candidateUiPort <= 65535) {\n const activeProxyPort = candidateProxyPort;\n const activeUiPort = candidateUiPort;\n const proxyServer = createServer((request, response) => {\n handleProxyRequest(request, response, registry, target);\n });\n const uiServer = createServer((request, response) => {\n void handleUiRequest(request, response, registry, uiDirectory, activeProxyPort);\n });\n\n try {\n await listen(proxyServer, activeProxyPort, host);\n await listen(uiServer, activeUiPort, host);\n return {\n proxyPort: activeProxyPort,\n uiPort: activeUiPort,\n close: async () => {\n await Promise.all([closeServer(proxyServer), closeServer(uiServer)]);\n },\n };\n } catch (error) {\n await closeServer(proxyServer);\n await closeServer(uiServer);\n if (!allowPortFallback || !isAddressInUse(error)) throw error;\n candidateProxyPort += 2;\n candidateUiPort += 2;\n }\n }\n\n throw new Error(\"Mocklane could not find an available local port pair.\");\n}\n\nasync function handleUiRequest(\n request: IncomingMessage,\n response: ServerResponse,\n registry: MockRegistry,\n uiDirectory: string,\n proxyPort: number,\n) {\n const url = new URL(request.url ?? \"/\", \"http://localhost\");\n\n if (url.pathname === \"/__mocklane/health\" && request.method === \"GET\") {\n sendJson(response, 200, { ready: true, proxyPort, mocks: registry.list().length });\n return;\n }\n\n if (url.pathname === \"/__mocklane/mocks\" && request.method === \"GET\") {\n sendJson(response, 200, registry.list());\n return;\n }\n\n if (url.pathname === \"/__mocklane/mocks\" && request.method === \"PUT\") {\n try {\n const mock = validateMockResponse(await readJsonBody(request));\n registry.set(mock);\n sendJson(response, 200, { active: true, method: mock.method, path: mock.path, proxyPort });\n } catch (error) {\n sendJson(response, 400, {\n error: error instanceof Error ? error.message : \"Invalid mock response.\",\n });\n }\n return;\n }\n\n if (url.pathname === \"/__mocklane/mocks\" && request.method === \"DELETE\") {\n try {\n const mock = validateMockResponse(await readJsonBody(request));\n registry.delete(mock.method, mock.path);\n sendJson(response, 200, { active: false, method: mock.method, path: mock.path });\n } catch (error) {\n sendJson(response, 400, {\n error: error instanceof Error ? error.message : \"Invalid mock response.\",\n });\n }\n return;\n }\n\n serveUiFile(response, uiDirectory, url.pathname, request.method === \"HEAD\");\n}\n\nfunction handleProxyRequest(\n request: IncomingMessage,\n response: ServerResponse,\n registry: MockRegistry,\n target: URL | null,\n) {\n const requestUrl = new URL(request.url ?? \"/\", target ?? \"http://localhost\");\n const preflightMethod = request.headers[\"access-control-request-method\"];\n const preflightMock =\n request.method === \"OPTIONS\" && typeof preflightMethod === \"string\"\n ? registry.get(preflightMethod, requestUrl.pathname)\n : undefined;\n if (preflightMock && typeof preflightMethod === \"string\") {\n response.writeHead(204, {\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-methods\": preflightMethod.toUpperCase(),\n \"access-control-allow-headers\": request.headers[\"access-control-request-headers\"] ?? \"content-type\",\n });\n response.end();\n return;\n }\n\n const mock = registry.get(request.method ?? \"GET\", requestUrl.pathname);\n\n if (mock) {\n response.writeHead(mock.status, {\n ...mock.headers,\n \"access-control-allow-origin\": mock.headers[\"access-control-allow-origin\"] ?? \"*\",\n });\n response.end(JSON.stringify(mock.body));\n return;\n }\n\n if (!target) {\n sendJson(response, 404, {\n error: \"No Mocklane mock matches this route.\",\n method: request.method ?? \"GET\",\n path: requestUrl.pathname,\n });\n return;\n }\n\n const upstreamUrl = new URL(request.url ?? \"/\", target);\n const upstreamRequest = (upstreamUrl.protocol === \"https:\" ? requestHttps : requestHttp)(\n upstreamUrl,\n {\n method: request.method,\n headers: { ...request.headers, host: upstreamUrl.host },\n },\n (upstreamResponse) => {\n response.writeHead(upstreamResponse.statusCode ?? 502, upstreamResponse.headers);\n upstreamResponse.pipe(response);\n },\n );\n\n upstreamRequest.on(\"error\", (error) => {\n if (!response.headersSent) {\n sendJson(response, 502, {\n error: \"Mocklane could not reach the upstream server.\",\n detail: error.message,\n });\n return;\n }\n response.destroy(error);\n });\n request.pipe(upstreamRequest);\n}\n\nfunction serveUiFile(\n response: ServerResponse,\n uiDirectory: string,\n pathname: string,\n headOnly: boolean,\n) {\n const requestedPath = pathname === \"/\" ? \"/index.html\" : pathname;\n const candidate = resolve(uiDirectory, `.${decodeURIComponent(requestedPath)}`);\n const uiRoot = resolve(uiDirectory);\n const filePath =\n candidate.startsWith(`${uiRoot}/`) && existsSync(candidate) && statSync(candidate).isFile()\n ? candidate\n : resolve(uiDirectory, \"index.html\");\n\n if (!existsSync(filePath)) {\n sendJson(response, 503, {\n error: \"Mocklane UI has not been built. Run `pnpm build` first.\",\n });\n return;\n }\n\n response.writeHead(200, {\n \"content-type\": contentType(filePath),\n \"cache-control\": filePath.endsWith(\"index.html\") ? \"no-cache\" : \"public, max-age=31536000, immutable\",\n });\n if (headOnly) {\n response.end();\n return;\n }\n createReadStream(filePath).pipe(response);\n}\n\nasync function readJsonBody(request: IncomingMessage) {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of request) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n size += buffer.length;\n if (size > MAX_CONTROL_BODY_BYTES) {\n throw new Error(\"Mock response exceeds the 10 MB limit.\");\n }\n chunks.push(buffer);\n }\n return JSON.parse(Buffer.concat(chunks).toString(\"utf8\")) as unknown;\n}\n\nfunction validateMockResponse(value: unknown): MockHttpResponse {\n if (!value || typeof value !== \"object\") throw new Error(\"Mock response must be an object.\");\n const mock = value as Partial<MockHttpResponse>;\n if (typeof mock.method !== \"string\" || !mock.method.trim()) throw new Error(\"Method is required.\");\n if (typeof mock.path !== \"string\" || !mock.path.startsWith(\"/\")) {\n throw new Error(\"Path must begin with `/`.\");\n }\n if (!Number.isInteger(mock.status) || Number(mock.status) < 100 || Number(mock.status) > 599) {\n throw new Error(\"Status must be a valid HTTP status code.\");\n }\n if (!mock.headers || typeof mock.headers !== \"object\" || Array.isArray(mock.headers)) {\n throw new Error(\"Headers must be an object.\");\n }\n return mock as MockHttpResponse;\n}\n\nfunction sendJson(response: ServerResponse, status: number, body: unknown) {\n response.writeHead(status, { \"content-type\": \"application/json; charset=utf-8\" });\n response.end(JSON.stringify(body));\n}\n\nfunction contentType(filePath: string) {\n const types: Record<string, string> = {\n \".css\": \"text/css; charset=utf-8\",\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".png\": \"image/png\",\n \".svg\": \"image/svg+xml\",\n \".wasm\": \"application/wasm\",\n };\n return types[extname(filePath)] ?? \"application/octet-stream\";\n}\n\nfunction listen(server: Server, port: number, host: string) {\n return new Promise<void>((resolvePromise, reject) => {\n server.once(\"error\", reject);\n server.listen(port, host, () => {\n server.off(\"error\", reject);\n resolvePromise();\n });\n });\n}\n\nfunction isAddressInUse(error: unknown) {\n return error instanceof Error && \"code\" in error && error.code === \"EADDRINUSE\";\n}\n\nfunction closeServer(server: Server) {\n return new Promise<void>((resolvePromise) => {\n if (!server.listening) {\n resolvePromise();\n return;\n }\n server.close(() => resolvePromise());\n });\n}\n", "import type { MockHttpResponse } from \"../src/types/mockApi\";\n\nexport class MockRegistry {\n readonly #mocks = new Map<string, MockHttpResponse>();\n\n set(response: MockHttpResponse) {\n this.#mocks.set(mockKey(response.method, response.path), response);\n }\n\n get(method: string, path: string) {\n return this.#mocks.get(mockKey(method, path));\n }\n\n delete(method: string, path: string) {\n return this.#mocks.delete(mockKey(method, path));\n }\n\n list() {\n return [...this.#mocks.values()];\n }\n}\n\nfunction mockKey(method: string, path: string) {\n return `${method.toUpperCase()} ${path}`;\n}\n"],
5
+ "mappings": ";;;AAEA,SAAS,aAAa;AACtB,SAAS,qBAAqB;;;ACH9B,SAAS,kBAAkB,YAAY,gBAAgB;AACvD,SAAS,oBAA4E;AACrF,SAAS,WAAW,mBAAmB;AACvC,SAAS,WAAW,oBAAoB;AACxC,SAAS,SAAS,eAAe;;;ACF1B,IAAM,eAAN,MAAmB;AAAA,EACf,SAAS,oBAAI,IAA8B;AAAA,EAEpD,IAAI,UAA4B;AAC9B,SAAK,OAAO,IAAI,QAAQ,SAAS,QAAQ,SAAS,IAAI,GAAG,QAAQ;AAAA,EACnE;AAAA,EAEA,IAAI,QAAgB,MAAc;AAChC,WAAO,KAAK,OAAO,IAAI,QAAQ,QAAQ,IAAI,CAAC;AAAA,EAC9C;AAAA,EAEA,OAAO,QAAgB,MAAc;AACnC,WAAO,KAAK,OAAO,OAAO,QAAQ,QAAQ,IAAI,CAAC;AAAA,EACjD;AAAA,EAEA,OAAO;AACL,WAAO,CAAC,GAAG,KAAK,OAAO,OAAO,CAAC;AAAA,EACjC;AACF;AAEA,SAAS,QAAQ,QAAgB,MAAc;AAC7C,SAAO,GAAG,OAAO,YAAY,CAAC,IAAI,IAAI;AACxC;;;ADfA,IAAM,yBAAyB,KAAK,OAAO;AAQ3C,eAAsB,qBAAqB;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,oBAAoB;AACtB,GAO6B;AAC3B,QAAM,WAAW,IAAI,aAAa;AAClC,MAAI,qBAAqB;AACzB,MAAI,kBAAkB;AAEtB,SAAO,sBAAsB,SAAS,mBAAmB,OAAO;AAC9D,UAAM,kBAAkB;AACxB,UAAM,eAAe;AACrB,UAAM,cAAc,aAAa,CAAC,SAAS,aAAa;AACtD,yBAAmB,SAAS,UAAU,UAAU,MAAM;AAAA,IACxD,CAAC;AACD,UAAM,WAAW,aAAa,CAAC,SAAS,aAAa;AACnD,WAAK,gBAAgB,SAAS,UAAU,UAAU,aAAa,eAAe;AAAA,IAChF,CAAC;AAED,QAAI;AACF,YAAM,OAAO,aAAa,iBAAiB,IAAI;AAC/C,YAAM,OAAO,UAAU,cAAc,IAAI;AACzC,aAAO;AAAA,QACL,WAAW;AAAA,QACX,QAAQ;AAAA,QACR,OAAO,YAAY;AACjB,gBAAM,QAAQ,IAAI,CAAC,YAAY,WAAW,GAAG,YAAY,QAAQ,CAAC,CAAC;AAAA,QACrE;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,YAAM,YAAY,WAAW;AAC7B,YAAM,YAAY,QAAQ;AAC1B,UAAI,CAAC,qBAAqB,CAAC,eAAe,KAAK,EAAG,OAAM;AACxD,4BAAsB;AACtB,yBAAmB;AAAA,IACrB;AAAA,EACF;AAEA,QAAM,IAAI,MAAM,uDAAuD;AACzE;AAEA,eAAe,gBACb,SACA,UACA,UACA,aACA,WACA;AACA,QAAM,MAAM,IAAI,IAAI,QAAQ,OAAO,KAAK,kBAAkB;AAE1D,MAAI,IAAI,aAAa,wBAAwB,QAAQ,WAAW,OAAO;AACrE,aAAS,UAAU,KAAK,EAAE,OAAO,MAAM,WAAW,OAAO,SAAS,KAAK,EAAE,OAAO,CAAC;AACjF;AAAA,EACF;AAEA,MAAI,IAAI,aAAa,uBAAuB,QAAQ,WAAW,OAAO;AACpE,aAAS,UAAU,KAAK,SAAS,KAAK,CAAC;AACvC;AAAA,EACF;AAEA,MAAI,IAAI,aAAa,uBAAuB,QAAQ,WAAW,OAAO;AACpE,QAAI;AACF,YAAM,OAAO,qBAAqB,MAAM,aAAa,OAAO,CAAC;AAC7D,eAAS,IAAI,IAAI;AACjB,eAAS,UAAU,KAAK,EAAE,QAAQ,MAAM,QAAQ,KAAK,QAAQ,MAAM,KAAK,MAAM,UAAU,CAAC;AAAA,IAC3F,SAAS,OAAO;AACd,eAAS,UAAU,KAAK;AAAA,QACtB,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAClD,CAAC;AAAA,IACH;AACA;AAAA,EACF;AAEA,MAAI,IAAI,aAAa,uBAAuB,QAAQ,WAAW,UAAU;AACvE,QAAI;AACF,YAAM,OAAO,qBAAqB,MAAM,aAAa,OAAO,CAAC;AAC7D,eAAS,OAAO,KAAK,QAAQ,KAAK,IAAI;AACtC,eAAS,UAAU,KAAK,EAAE,QAAQ,OAAO,QAAQ,KAAK,QAAQ,MAAM,KAAK,KAAK,CAAC;AAAA,IACjF,SAAS,OAAO;AACd,eAAS,UAAU,KAAK;AAAA,QACtB,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAClD,CAAC;AAAA,IACH;AACA;AAAA,EACF;AAEA,cAAY,UAAU,aAAa,IAAI,UAAU,QAAQ,WAAW,MAAM;AAC5E;AAEA,SAAS,mBACP,SACA,UACA,UACA,QACA;AACA,QAAM,aAAa,IAAI,IAAI,QAAQ,OAAO,KAAK,UAAU,kBAAkB;AAC3E,QAAM,kBAAkB,QAAQ,QAAQ,+BAA+B;AACvE,QAAM,gBACJ,QAAQ,WAAW,aAAa,OAAO,oBAAoB,WACvD,SAAS,IAAI,iBAAiB,WAAW,QAAQ,IACjD;AACN,MAAI,iBAAiB,OAAO,oBAAoB,UAAU;AACxD,aAAS,UAAU,KAAK;AAAA,MACtB,+BAA+B;AAAA,MAC/B,gCAAgC,gBAAgB,YAAY;AAAA,MAC5D,gCAAgC,QAAQ,QAAQ,gCAAgC,KAAK;AAAA,IACvF,CAAC;AACD,aAAS,IAAI;AACb;AAAA,EACF;AAEA,QAAM,OAAO,SAAS,IAAI,QAAQ,UAAU,OAAO,WAAW,QAAQ;AAEtE,MAAI,MAAM;AACR,aAAS,UAAU,KAAK,QAAQ;AAAA,MAC9B,GAAG,KAAK;AAAA,MACR,+BAA+B,KAAK,QAAQ,6BAA6B,KAAK;AAAA,IAChF,CAAC;AACD,aAAS,IAAI,KAAK,UAAU,KAAK,IAAI,CAAC;AACtC;AAAA,EACF;AAEA,MAAI,CAAC,QAAQ;AACX,aAAS,UAAU,KAAK;AAAA,MACtB,OAAO;AAAA,MACP,QAAQ,QAAQ,UAAU;AAAA,MAC1B,MAAM,WAAW;AAAA,IACnB,CAAC;AACD;AAAA,EACF;AAEA,QAAM,cAAc,IAAI,IAAI,QAAQ,OAAO,KAAK,MAAM;AACtD,QAAM,mBAAmB,YAAY,aAAa,WAAW,eAAe;AAAA,IAC1E;AAAA,IACA;AAAA,MACE,QAAQ,QAAQ;AAAA,MAChB,SAAS,EAAE,GAAG,QAAQ,SAAS,MAAM,YAAY,KAAK;AAAA,IACxD;AAAA,IACA,CAAC,qBAAqB;AACpB,eAAS,UAAU,iBAAiB,cAAc,KAAK,iBAAiB,OAAO;AAC/E,uBAAiB,KAAK,QAAQ;AAAA,IAChC;AAAA,EACF;AAEA,kBAAgB,GAAG,SAAS,CAAC,UAAU;AACrC,QAAI,CAAC,SAAS,aAAa;AACzB,eAAS,UAAU,KAAK;AAAA,QACtB,OAAO;AAAA,QACP,QAAQ,MAAM;AAAA,MAChB,CAAC;AACD;AAAA,IACF;AACA,aAAS,QAAQ,KAAK;AAAA,EACxB,CAAC;AACD,UAAQ,KAAK,eAAe;AAC9B;AAEA,SAAS,YACP,UACA,aACA,UACA,UACA;AACA,QAAM,gBAAgB,aAAa,MAAM,gBAAgB;AACzD,QAAM,YAAY,QAAQ,aAAa,IAAI,mBAAmB,aAAa,CAAC,EAAE;AAC9E,QAAM,SAAS,QAAQ,WAAW;AAClC,QAAM,WACJ,UAAU,WAAW,GAAG,MAAM,GAAG,KAAK,WAAW,SAAS,KAAK,SAAS,SAAS,EAAE,OAAO,IACtF,YACA,QAAQ,aAAa,YAAY;AAEvC,MAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,aAAS,UAAU,KAAK;AAAA,MACtB,OAAO;AAAA,IACT,CAAC;AACD;AAAA,EACF;AAEA,WAAS,UAAU,KAAK;AAAA,IACtB,gBAAgB,YAAY,QAAQ;AAAA,IACpC,iBAAiB,SAAS,SAAS,YAAY,IAAI,aAAa;AAAA,EAClE,CAAC;AACD,MAAI,UAAU;AACZ,aAAS,IAAI;AACb;AAAA,EACF;AACA,mBAAiB,QAAQ,EAAE,KAAK,QAAQ;AAC1C;AAEA,eAAe,aAAa,SAA0B;AACpD,QAAM,SAAmB,CAAC;AAC1B,MAAI,OAAO;AACX,mBAAiB,SAAS,SAAS;AACjC,UAAM,SAAS,OAAO,SAAS,KAAK,IAAI,QAAQ,OAAO,KAAK,KAAK;AACjE,YAAQ,OAAO;AACf,QAAI,OAAO,wBAAwB;AACjC,YAAM,IAAI,MAAM,wCAAwC;AAAA,IAC1D;AACA,WAAO,KAAK,MAAM;AAAA,EACpB;AACA,SAAO,KAAK,MAAM,OAAO,OAAO,MAAM,EAAE,SAAS,MAAM,CAAC;AAC1D;AAEA,SAAS,qBAAqB,OAAkC;AAC9D,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,OAAM,IAAI,MAAM,kCAAkC;AAC3F,QAAM,OAAO;AACb,MAAI,OAAO,KAAK,WAAW,YAAY,CAAC,KAAK,OAAO,KAAK,EAAG,OAAM,IAAI,MAAM,qBAAqB;AACjG,MAAI,OAAO,KAAK,SAAS,YAAY,CAAC,KAAK,KAAK,WAAW,GAAG,GAAG;AAC/D,UAAM,IAAI,MAAM,2BAA2B;AAAA,EAC7C;AACA,MAAI,CAAC,OAAO,UAAU,KAAK,MAAM,KAAK,OAAO,KAAK,MAAM,IAAI,OAAO,OAAO,KAAK,MAAM,IAAI,KAAK;AAC5F,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AACA,MAAI,CAAC,KAAK,WAAW,OAAO,KAAK,YAAY,YAAY,MAAM,QAAQ,KAAK,OAAO,GAAG;AACpF,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAC9C;AACA,SAAO;AACT;AAEA,SAAS,SAAS,UAA0B,QAAgB,MAAe;AACzE,WAAS,UAAU,QAAQ,EAAE,gBAAgB,kCAAkC,CAAC;AAChF,WAAS,IAAI,KAAK,UAAU,IAAI,CAAC;AACnC;AAEA,SAAS,YAAY,UAAkB;AACrC,QAAM,QAAgC;AAAA,IACpC,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,OAAO;AAAA,IACP,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,SAAS;AAAA,EACX;AACA,SAAO,MAAM,QAAQ,QAAQ,CAAC,KAAK;AACrC;AAEA,SAAS,OAAO,QAAgB,MAAc,MAAc;AAC1D,SAAO,IAAI,QAAc,CAAC,gBAAgB,WAAW;AACnD,WAAO,KAAK,SAAS,MAAM;AAC3B,WAAO,OAAO,MAAM,MAAM,MAAM;AAC9B,aAAO,IAAI,SAAS,MAAM;AAC1B,qBAAe;AAAA,IACjB,CAAC;AAAA,EACH,CAAC;AACH;AAEA,SAAS,eAAe,OAAgB;AACtC,SAAO,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS;AACrE;AAEA,SAAS,YAAY,QAAgB;AACnC,SAAO,IAAI,QAAc,CAAC,mBAAmB;AAC3C,QAAI,CAAC,OAAO,WAAW;AACrB,qBAAe;AACf;AAAA,IACF;AACA,WAAO,MAAM,MAAM,eAAe,CAAC;AAAA,EACrC,CAAC;AACH;;;ADvRA,IAAM,eAAe;AACrB,IAAM,qBAAqB;AAC3B,IAAM,kBAAkB;AAExB,eAAe,OAAO;AACpB,QAAM,UAAU,eAAe,QAAQ,KAAK,MAAM,CAAC,CAAC;AACpD,MAAI,QAAQ,MAAM;AAChB,cAAU;AACV;AAAA,EACF;AAEA,QAAM,cAAc,cAAc,IAAI,IAAI,mBAAmB,YAAY,GAAG,CAAC;AAC7E,QAAM,UAAU,MAAM,qBAAqB;AAAA,IACzC,MAAM;AAAA,IACN,WAAW,QAAQ;AAAA,IACnB,QAAQ,QAAQ;AAAA,IAChB,QAAQ,QAAQ;AAAA,IAChB;AAAA,IACA,mBAAmB,CAAC,QAAQ;AAAA,EAC9B,CAAC;AACD,QAAM,QAAQ,oBAAoB,QAAQ,MAAM;AAEhD,MAAI,CAAC,QAAQ,mBAAmB,QAAQ,cAAc,sBAAsB,QAAQ,WAAW,kBAAkB;AAC/G,YAAQ,IAAI,IAAI,QAAQ,kBAAkB,qBAAqB,QAAQ,SAAS,WAAW,CAAC;AAAA,EAC9F;AACA,UAAQ,IAAI,qBAAqB,KAAK,EAAE;AACxC,UAAQ,IAAI,sCAAsC,QAAQ,SAAS,EAAE;AACrE,UAAQ,IAAI,qBAAqB,QAAQ,QAAQ,UAAU,wBAAwB,EAAE;AACrF,UAAQ,IAAI,uBAAuB;AAEnC,cAAY,KAAK;AAEjB,QAAM,WAAW,YAAY;AAC3B,UAAM,QAAQ,MAAM;AACpB,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,UAAQ,KAAK,UAAU,MAAM,KAAK,SAAS,CAAC;AAC5C,UAAQ,KAAK,WAAW,MAAM,KAAK,SAAS,CAAC;AAC/C;AAEA,SAAS,eAAe,eAAyB;AAC/C,MAAI,SAAqB;AACzB,MAAI,YAAY;AAChB,MAAI,SAAS;AACb,MAAI,OAAO;AACX,MAAI,iBAAiB;AAErB,WAAS,QAAQ,GAAG,QAAQ,cAAc,QAAQ,SAAS,GAAG;AAC5D,UAAM,WAAW,cAAc,KAAK;AACpC,QAAI,aAAa,WAAY,UAAS,IAAI,IAAI,aAAa,eAAe,EAAE,OAAO,QAAQ,CAAC;AAAA,aACnF,aAAa,gBAAgB;AACpC,kBAAY,UAAU,aAAa,eAAe,EAAE,OAAO,QAAQ,CAAC;AACpE,uBAAiB;AAAA,IACnB,WAAW,aAAa,aAAa;AACnC,eAAS,UAAU,aAAa,eAAe,EAAE,OAAO,QAAQ,CAAC;AACjE,uBAAiB;AAAA,IACnB,WACS,aAAa,YAAY,aAAa,KAAM,QAAO;AAAA,QACvD,OAAM,IAAI,MAAM,qBAAqB,QAAQ,EAAE;AAAA,EACtD;AAEA,MAAI,cAAc,OAAQ,OAAM,IAAI,MAAM,uCAAuC;AACjF,SAAO,EAAE,QAAQ,WAAW,QAAQ,MAAM,eAAe;AAC3D;AAEA,SAAS,aAAa,eAAyB,OAAe,MAAc;AAC1E,QAAM,QAAQ,cAAc,KAAK;AACjC,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,GAAG,IAAI,oBAAoB;AACvD,SAAO;AACT;AAEA,SAAS,UAAU,OAAe;AAChC,QAAM,OAAO,OAAO,KAAK;AACzB,MAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,KAAK,OAAO,OAAO;AACvD,UAAM,IAAI,MAAM,iBAAiB,KAAK,EAAE;AAAA,EAC1C;AACA,SAAO;AACT;AAEA,SAAS,YAAY,KAAa;AAChC,QAAM,UAAU,QAAQ,aAAa,WAAW,SAAS,QAAQ,aAAa,UAAU,QAAQ;AAChG,QAAM,OAAO,QAAQ,aAAa,UAAU,CAAC,MAAM,SAAS,IAAI,GAAG,IAAI,CAAC,GAAG;AAC3E,QAAM,SAAS,MAAM,EAAE,UAAU,MAAM,OAAO,SAAS,CAAC,EAAE,MAAM;AAClE;AAEA,SAAS,IAAI,SAAiB;AAC5B,SAAO,WAAW,OAAO;AAC3B;AAEA,SAAS,YAAY;AACnB,UAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sCASwB;AACtC;AAEA,KAAK,KAAK,EAAE,MAAM,CAAC,UAAmB;AACpC,UAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,KAAK;AAC5D,UAAQ,KAAK,CAAC;AAChB,CAAC;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mocklane/core",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Browser-local AI mock API generator and proxy.",
5
5
  "author": "Asis Paudel",
6
6
  "license": "MIT",
@@ -1,178 +0,0 @@
1
- import{r as d,j as a,w as vt}from"./jsx-runtime-BZbza59V.js";/**
2
- * @license lucide-react v1.17.0 - ISC
3
- *
4
- * This source code is licensed under the ISC license.
5
- * See the LICENSE file in the root directory of this source tree.
6
- */const Fe=(...e)=>e.filter((t,r,n)=>!!t&&t.trim()!==""&&n.indexOf(t)===r).join(" ").trim();/**
7
- * @license lucide-react v1.17.0 - ISC
8
- *
9
- * This source code is licensed under the ISC license.
10
- * See the LICENSE file in the root directory of this source tree.
11
- */const kt=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase();/**
12
- * @license lucide-react v1.17.0 - ISC
13
- *
14
- * This source code is licensed under the ISC license.
15
- * See the LICENSE file in the root directory of this source tree.
16
- */const wt=e=>e.replace(/^([A-Z])|[\s-_]+(\w)/g,(t,r,n)=>n?n.toUpperCase():r.toLowerCase());/**
17
- * @license lucide-react v1.17.0 - ISC
18
- *
19
- * This source code is licensed under the ISC license.
20
- * See the LICENSE file in the root directory of this source tree.
21
- */const Pe=e=>{const t=wt(e);return t.charAt(0).toUpperCase()+t.slice(1)};/**
22
- * @license lucide-react v1.17.0 - ISC
23
- *
24
- * This source code is licensed under the ISC license.
25
- * See the LICENSE file in the root directory of this source tree.
26
- */var pe={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};/**
27
- * @license lucide-react v1.17.0 - ISC
28
- *
29
- * This source code is licensed under the ISC license.
30
- * See the LICENSE file in the root directory of this source tree.
31
- */const xt=e=>{for(const t in e)if(t.startsWith("aria-")||t==="role"||t==="title")return!0;return!1},Nt=d.createContext({}),jt=()=>d.useContext(Nt),Mt=d.forwardRef(({color:e,size:t,strokeWidth:r,absoluteStrokeWidth:n,className:s="",children:l,iconNode:c,...o},u)=>{const{size:m=24,strokeWidth:h=2,absoluteStrokeWidth:f=!1,color:y="currentColor",className:k=""}=jt()??{},N=n??f?Number(r??h)*24/Number(t??m):r??h;return d.createElement("svg",{ref:u,...pe,width:t??m??pe.width,height:t??m??pe.height,stroke:e??y,strokeWidth:N,className:Fe("lucide",k,s),...!l&&!xt(o)&&{"aria-hidden":"true"},...o},[...c.map(([ne,D])=>d.createElement(ne,D)),...Array.isArray(l)?l:[l]])});/**
32
- * @license lucide-react v1.17.0 - ISC
33
- *
34
- * This source code is licensed under the ISC license.
35
- * See the LICENSE file in the root directory of this source tree.
36
- */const v=(e,t)=>{const r=d.forwardRef(({className:n,...s},l)=>d.createElement(Mt,{ref:l,iconNode:t,className:Fe(`lucide-${kt(Pe(e))}`,`lucide-${e}`,n),...s}));return r.displayName=Pe(e),r};/**
37
- * @license lucide-react v1.17.0 - ISC
38
- *
39
- * This source code is licensed under the ISC license.
40
- * See the LICENSE file in the root directory of this source tree.
41
- */const Lt=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],Ct=v("check",Lt);/**
42
- * @license lucide-react v1.17.0 - ISC
43
- *
44
- * This source code is licensed under the ISC license.
45
- * See the LICENSE file in the root directory of this source tree.
46
- */const At=[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]],$e=v("chevron-down",At);/**
47
- * @license lucide-react v1.17.0 - ISC
48
- *
49
- * This source code is licensed under the ISC license.
50
- * See the LICENSE file in the root directory of this source tree.
51
- */const Tt=[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]],Ot=v("copy",Tt);/**
52
- * @license lucide-react v1.17.0 - ISC
53
- *
54
- * This source code is licensed under the ISC license.
55
- * See the LICENSE file in the root directory of this source tree.
56
- */const Et=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],It=v("download",Et);/**
57
- * @license lucide-react v1.17.0 - ISC
58
- *
59
- * This source code is licensed under the ISC license.
60
- * See the LICENSE file in the root directory of this source tree.
61
- */const _t=[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]],Pt=v("loader-circle",_t);/**
62
- * @license lucide-react v1.17.0 - ISC
63
- *
64
- * This source code is licensed under the ISC license.
65
- * See the LICENSE file in the root directory of this source tree.
66
- */const $t=[["path",{d:"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401",key:"kfwtm"}]],Rt=v("moon",$t);/**
67
- * @license lucide-react v1.17.0 - ISC
68
- *
69
- * This source code is licensed under the ISC license.
70
- * See the LICENSE file in the root directory of this source tree.
71
- */const St=[["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",key:"1a8usu"}],["path",{d:"m15 5 4 4",key:"1mk7zo"}]],Jt=v("pencil",St);/**
72
- * @license lucide-react v1.17.0 - ISC
73
- *
74
- * This source code is licensed under the ISC license.
75
- * See the LICENSE file in the root directory of this source tree.
76
- */const Gt=[["path",{d:"M6.3 20.3a2.4 2.4 0 0 0 3.4 0L12 18l-6-6-2.3 2.3a2.4 2.4 0 0 0 0 3.4Z",key:"goz73y"}],["path",{d:"m2 22 3-3",key:"19mgm9"}],["path",{d:"M7.5 13.5 10 11",key:"7xgeeb"}],["path",{d:"M10.5 16.5 13 14",key:"10btkg"}],["path",{d:"m18 3-4 4h6l-4 4",key:"16psg9"}]],Dt=v("plug-zap",Gt);/**
77
- * @license lucide-react v1.17.0 - ISC
78
- *
79
- * This source code is licensed under the ISC license.
80
- * See the LICENSE file in the root directory of this source tree.
81
- */const Wt=[["path",{d:"M12 22v-5",key:"1ega77"}],["path",{d:"M15 8V2",key:"18g5xt"}],["path",{d:"M17 8a1 1 0 0 1 1 1v4a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1z",key:"1xoxul"}],["path",{d:"M9 8V2",key:"14iosj"}]],Re=v("plug",Wt);/**
82
- * @license lucide-react v1.17.0 - ISC
83
- *
84
- * This source code is licensed under the ISC license.
85
- * See the LICENSE file in the root directory of this source tree.
86
- */const Bt=[["path",{d:"M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z",key:"1c8476"}],["path",{d:"M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7",key:"1ydtos"}],["path",{d:"M7 3v4a1 1 0 0 0 1 1h7",key:"t51u73"}]],zt=v("save",Bt);/**
87
- * @license lucide-react v1.17.0 - ISC
88
- *
89
- * This source code is licensed under the ISC license.
90
- * See the LICENSE file in the root directory of this source tree.
91
- */const Ut=[["circle",{cx:"12",cy:"12",r:"4",key:"4exip2"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M12 20v2",key:"1lh1kg"}],["path",{d:"m4.93 4.93 1.41 1.41",key:"149t6j"}],["path",{d:"m17.66 17.66 1.41 1.41",key:"ptbguv"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"M20 12h2",key:"1q8mjw"}],["path",{d:"m6.34 17.66-1.41 1.41",key:"1m8zz5"}],["path",{d:"m19.07 4.93-1.41 1.41",key:"1shlcs"}]],Ft=v("sun",Ut);/**
92
- * @license lucide-react v1.17.0 - ISC
93
- *
94
- * This source code is licensed under the ISC license.
95
- * See the LICENSE file in the root directory of this source tree.
96
- */const qt=[["path",{d:"m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72",key:"ul74o6"}],["path",{d:"m14 7 3 3",key:"1r5n42"}],["path",{d:"M5 6v4",key:"ilb8ba"}],["path",{d:"M19 14v4",key:"blhpug"}],["path",{d:"M10 2v2",key:"7u0qdc"}],["path",{d:"M7 8H3",key:"zfb6yr"}],["path",{d:"M21 16h-4",key:"1cnmox"}],["path",{d:"M11 3H9",key:"1obp7u"}]],Ht=v("wand-sparkles",qt);/**
97
- * @license lucide-react v1.17.0 - ISC
98
- *
99
- * This source code is licensed under the ISC license.
100
- * See the LICENSE file in the root directory of this source tree.
101
- */const Vt=[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]],Kt=v("x",Vt);function Se({id:e,label:t,meta:r,value:n,onChange:s,placeholder:l,spellCheck:c=!0,variant:o="default"}){return a.jsxs("div",{className:`field-group ${o==="compact"?"prompt-group":""}`,children:[a.jsxs("div",{className:"field-heading",children:[a.jsx("label",{htmlFor:e,children:t}),a.jsx("span",{children:r})]}),a.jsx("textarea",{id:e,value:n,spellCheck:c,placeholder:l,onChange:u=>s(u.target.value)})]})}const Qt="modulepreload",Zt=function(e){return"/"+e},Je={},qe=function(t,r,n){let s=Promise.resolve();if(r&&r.length>0){let c=function(m){return Promise.all(m.map(h=>Promise.resolve(h).then(f=>({status:"fulfilled",value:f}),f=>({status:"rejected",reason:f}))))};document.getElementsByTagName("link");const o=document.querySelector("meta[property=csp-nonce]"),u=(o==null?void 0:o.nonce)||(o==null?void 0:o.getAttribute("nonce"));s=c(r.map(m=>{if(m=Zt(m),m in Je)return;Je[m]=!0;const h=m.endsWith(".css"),f=h?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${m}"]${f}`))return;const y=document.createElement("link");if(y.rel=h?"stylesheet":Qt,h||(y.as="script"),y.crossOrigin="",y.href=m,u&&y.setAttribute("nonce",u),document.head.appendChild(y),h)return new Promise((k,N)=>{y.addEventListener("load",k),y.addEventListener("error",()=>N(new Error(`Unable to preload CSS for ${m}`)))})}))}function l(c){const o=new Event("vite:preloadError",{cancelable:!0});if(o.payload=c,window.dispatchEvent(o),!o.defaultPrevented)throw c}return s.then(c=>{for(const o of c||[])o.status==="rejected"&&l(o.reason);return t().catch(l)})},w="Qwen2.5-0.5B-Instruct-q4f16_1-MLC",Xt=5e4;let E=null,x=null,ve=!1;function ke(){return typeof navigator>"u"?{available:!1,label:"WebLLM unavailable"}:"gpu"in navigator?x?{available:!0,label:"WebLLM ready",detail:w}:E?{available:!0,label:"WebLLM warming up",detail:w}:ve?{available:!1,label:"WebLLM unavailable",detail:"WebLLM failed to load."}:{available:!0,label:"WebLLM available",detail:w}:{available:!1,label:"WebLLM unavailable",detail:"WebGPU is unavailable in this browser."}}function Yt(e,t={}){if(!(!ke().available||E)){if(x&&!t.reload){e==null||e({available:!0,label:"WebLLM ready",detail:w});return}e==null||e({available:!0,label:"WebLLM warming up",detail:w}),ve=!1,E=tr(e).then(r=>(x=r,E=null,e==null||e({available:!0,label:"WebLLM ready",detail:w}),r)),E.catch(r=>{ve=!0,E=null,e==null||e({available:!1,label:"WebLLM unavailable",detail:r instanceof Error?r.message:"WebLLM failed to load."})})}}async function er({model:e,prompt:t,onStatus:r}){var n;if(!x)return null;r==null||r({available:!0,label:"Generating locally",detail:w});try{const l=(n=(await x.chat.completions.create({messages:[{role:"system",content:He()},{role:"user",content:Ve({model:e,prompt:t})}],max_tokens:Xt,temperature:.2,top_p:.9})).choices[0])==null?void 0:n.message.content;if(!l)return{message:"The local model returned an empty response.",promptOutput:""};try{const c=Qe(Ke(l),t);return JSON.parse(c)}catch{return{message:"The local model returned text that could not be parsed as JSON.",promptOutput:l}}}finally{r==null||r({available:!0,label:"WebLLM ready",detail:w})}}async function tr(e){if(x)return x.setInitProgressCallback(r=>{e==null||e({available:!0,label:"Loading local model",detail:Ge(r)})}),await x.reload(w),x;const{CreateMLCEngine:t}=await qe(async()=>{const{CreateMLCEngine:r}=await import("./index-D9gteTdx.js");return{CreateMLCEngine:r}},[]);return t(w,{initProgressCallback:r=>{e==null||e({available:!0,label:"Loading local model",detail:Ge(r)})}})}function Ge(e){return`${Math.round(e.progress*100)}% ${e.text}`}function He(){return`You are Mocklane, a focused mock HTTP API response generator.
102
-
103
- Rules:
104
- - Only generate mock HTTP API JSON responses.
105
- - Always return one raw valid JSON object.
106
- - Do not use markdown, comments, explanations, or code fences.
107
- - Do not include <think> blocks, hidden reasoning, or analysis.
108
- - Do not wrap the JSON in a string field such as "promptOutput".
109
- - Keep the response compact.
110
- - Use realistic but fake values.
111
- - Never include real secrets, real personal data, or explanatory text.
112
-
113
- If the request is unrelated to mock APIs, mock endpoints, mock responses, or mock data, return exactly:
114
- {"message":"My job is to make mock APIs. Drop a model or structure, then ask me for an endpoint response and I can help you."}
115
-
116
- For mock API requests, return this object shape:
117
- {"method":"GET","path":"/api/resource","status":200,"headers":{"content-type":"application/json"},"body":[{"resourcePlural":[...items]}]}
118
-
119
- Response rules:
120
- - If the user asks for N items, generate exactly N items. Never generate fewer. Never generate more.
121
- - Infer body fields from the supplied model, schema, example, or structure.
122
- - Infer method, path, status, count, and data theme from the user request.
123
- - Default to method "GET" and status 200 unless the request clearly implies another method or status.
124
- - If count is not specified, generate 3 items.
125
- - The body must always be an array containing exactly one object.
126
- - That one object must have exactly one plural lower-camel-case key for the resource collection, such as "products", "customers", "orders", or "supportTickets".
127
- - The value of that plural key must be an array of generated objects.
128
- - Preserve sensible data types from the supplied model.
129
- - Use ISO-like date strings when generating dates.
130
- - Use integer cents for fields ending in "Cents", such as "priceCents": 4999.
131
-
132
- Example body shape:
133
- {"body":[{"products":[{"id":"101","sku":"ABC-123","name":"Product A","category":"Electronics","priceCents":4999,"currency":"USD","inventoryCount":10,"rating":5,"tags":["Electronics","Tech"]}]}]}`}function Ve({model:e,prompt:t}){return`Model / Structure:
134
- ${e}
135
-
136
- Prompt:
137
- ${t}`}function rr(e){const t=e.trim();if(t.startsWith("{")&&t.endsWith("}"))return t;const r=t.indexOf("{"),n=t.lastIndexOf("}");if(r===-1||n===-1||n<=r)throw new Error("Local model did not return a JSON object.");return t.slice(r,n+1)}function Ke(e){const r=e.replace(/<think>[\s\S]*?<\/think>/gi,"").trim().replace(/^```(?:json)?\s*/i,"").replace(/\s*```$/i,"").trim();return ar(r)??rr(r)}function ar(e){const t=De(e,"{","}");if(t)return Y(t);const r=De(e,"[","]");return r?Y(r):null}function De(e,t,r){const n=e.indexOf(t);if(n===-1)return null;let s=0,l=!1,c=!1;for(let o=n;o<e.length;o+=1){const u=e[o];if(l){c?c=!1:u==="\\"?c=!0:u==='"'&&(l=!1);continue}if(u==='"')l=!0;else if(u===t)s+=1;else if(u===r&&(s-=1,s===0))return e.slice(n,o+1)}return null}function Y(e){try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}function Qe(e,t){const r=nr(t);if(!r||!/product/i.test(t))return e;try{const n=JSON.parse(e),s=sr(n);if(s.length===r)return Y(JSON.stringify(We(s)));const l=s[0]??ir(n),c=Array.from({length:r},(o,u)=>lr(l,u+1));return Y(JSON.stringify(We(c)))}catch{return e}}function nr(e){const t=e.match(/id\s+(\d+)\s*(?:to|-)\s*(\d+)/i);if(t){const s=Number(t[1]),l=Number(t[2]);if(Number.isFinite(s)&&Number.isFinite(l)&&l>=s)return l-s+1}const r=e.match(/(?:exactly\s+)?(\d+)\s+(?:productmodel\s+)?items?/i);if(!r)return null;const n=Number(r[1]);return Number.isFinite(n)&&n>0?n:null}function sr(e){if(!L(e))return[];if(Array.isArray(e.products))return e.products.filter(L);const t=Array.isArray(e.body)?e.body[0]:null;return L(t)&&Array.isArray(t.products)?t.products.filter(L):Array.isArray(e.body)?e.body.filter(ee):ee(e)?[e]:[]}function ir(e){if(!L(e))return null;const t=Array.isArray(e.body)?e.body[0]:null;return ee(t)?t:ee(e)?e:null}function We(e){return{method:"GET",path:"/api/products",status:200,headers:{"content-type":"application/json"},body:[{products:e}]}}function lr(e,t){var l;const r=["electronics","home","apparel","books"],n=["Laptop","Desk Lamp","Cotton Hoodie","Notebook Set","Wireless Mouse","Ceramic Mug","Running Shirt","Cookbook","Bluetooth Speaker","Storage Basket"],s=r[(t-1)%r.length];return{id:String(t),sku:`SKU-${String(t).padStart(3,"0")}`,name:n[t-1]??`Product ${t}`,category:s,priceCents:we(e==null?void 0:e.priceCents,1999)+(t-1)*250,currency:"USD",inventoryCount:Math.max(0,we(e==null?void 0:e.inventoryCount,100)-(t-1)*3),rating:Math.min(5,Math.round((4.1+t%5*.2)*10)/10),tags:[s,((l=n[t-1])==null?void 0:l.toLowerCase().replaceAll(" ","-"))??`product-${t}`],dimensions:{widthCm:be(e,"widthCm",20+t),heightCm:be(e,"heightCm",10+t),depthCm:be(e,"depthCm",5+t)},createdAt:`2023-04-${String(t).padStart(2,"0")}T00:00:00Z`}}function we(e,t){return typeof e=="number"&&Number.isFinite(e)?e:t}function be(e,t,r){const n=L(e==null?void 0:e.dimensions)?e.dimensions:null;return we(n==null?void 0:n[t],r)}function ee(e){return L(e)&&"id"in e&&"sku"in e&&"name"in e&&"priceCents"in e&&"dimensions"in e}function L(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function or(){return Ze()?{available:!0,label:"Native Browser AI available",detail:"Gemini Nano"}:{available:!1,label:"Native Browser AI unavailable",detail:"The LanguageModel API is unavailable in this browser."}}async function cr({model:e,prompt:t,onStatus:r}){var c;const n=Ze();if(!n)return null;r==null||r({available:!0,label:"Native Browser AI preparing",detail:"Checking Gemini Nano availability..."});const s=He();let l;try{if((n.factory.availability?await n.factory.availability():"available")==="unavailable")throw new Error("The browser reports that Gemini Nano is unavailable.");l=await n.factory.create({...n.kind==="current"?{initialPrompts:[{role:"system",content:s}]}:{systemPrompt:s},monitor(u){u.addEventListener("downloadprogress",m=>{r==null||r({available:!0,label:"Native Browser AI preparing",detail:`Downloading Gemini Nano: ${Math.round(m.loaded*100)}%`})})}})}catch(o){throw r==null||r({available:!0,label:"Native Browser AI available",detail:o instanceof Error?o.message:"Gemini Nano failed to start."}),o}r==null||r({available:!0,label:"Generating with Native Browser AI",detail:"Gemini Nano"});try{const o=await l.prompt(Ve({model:e,prompt:t})),u=Ke(o),m=Qe(dr(u),t);try{return JSON.parse(m)}catch{return{message:"Native Browser AI returned text that could not be parsed as JSON.",promptOutput:o}}}finally{(c=l.destroy)==null||c.call(l),r==null||r({available:!0,label:"Native Browser AI available",detail:"Gemini Nano"})}}function dr(e){const t=e.trim(),r=[];let n=!1,s=!1;for(const c of t){if(n){s?s=!1:c==="\\"?s=!0:c==='"'&&(n=!1);continue}if(c==='"')n=!0;else if(c==="{")r.push("}");else if(c==="[")r.push("]");else if(c==="}"||c==="]"){if(r.at(-1)!==c)return e;r.pop()}}if(n||r.length===0)return e;const l=`${t}${r.reverse().join("")}`;try{return JSON.parse(l),l}catch{return e}}function Ze(){var t;if(typeof window>"u")return null;const e=window;return e.LanguageModel?{factory:e.LanguageModel,kind:"current"}:(t=e.ai)!=null&&t.languageModel?{factory:e.ai.languageModel,kind:"legacy"}:null}const _=[{id:"onnx-community/Qwen3-0.6B-ONNX",label:"Qwen3 0.6B",detail:"Current baseline",dtype:"q4f16"},{id:"onnx-community/gemma-3-1b-it-ONNX-GQA",label:"Gemma 3 1B Instruct",detail:"Google alternative",dtype:"q4"}],je=_[0].id,ur=5e4,mr=`You are Mocklane, a JSON-only mock HTTP API response generator.
138
- Return exactly one raw valid JSON object.
139
- Do not include markdown, code fences, comments, explanations, analysis, or <think> blocks.
140
- Do not reveal chain-of-thought or hidden reasoning.
141
-
142
- For mock API requests, always return this response shape:
143
- {"method":"GET","path":"/api/products","status":200,"headers":{"content-type":"application/json"},"body":[{"products":[]}]}
144
-
145
- Rules:
146
- - If the user asks for N items, generate exactly N items.
147
- - If the user gives an id range, include every id in that range exactly once.
148
- - For GET all product requests, body must be an array containing one object with a plural collection array.
149
- - Preserve field names and sensible data types from the supplied model.
150
- - Use string ids.
151
- - Use integer cents for fields ending in Cents.
152
- - Use ISO-like strings for date fields.
153
- - Do not return a single item object unless the user explicitly asks for one item.`;let I=null,C=null,xe=!1,G=null,Z=null;function K(e=je){return typeof navigator>"u"?{available:!1,label:"Transformers.js unavailable"}:"gpu"in navigator?C&&G===e?{available:!0,label:"Transformers.js ready",detail:e}:I?{available:!0,label:"Transformers.js warming up",detail:Z??e}:xe?{available:!1,label:"Transformers.js unavailable",detail:"Transformers.js failed to load."}:{available:!0,label:"Transformers.js available",detail:e}:{available:!1,label:"Transformers.js unavailable",detail:"WebGPU is unavailable in this browser."}}function fr(e,t={}){const r=yr(t.modelId);if(typeof navigator>"u"||!("gpu"in navigator)||I)return;if(C&&G===r.id&&!t.reload){e==null||e({available:!0,label:"Transformers.js ready",detail:r.id});return}e==null||e({available:!0,label:"Transformers.js warming up",detail:r.id}),xe=!1,Z=r.id;const n=C;C=null,G=null,I=hr(r.id).then(s=>br(n,r,s,e)).then(s=>(C=s,G=r.id,Z=null,I=null,e==null||e({available:!0,label:"Transformers.js ready",detail:r.id}),s)),I.catch(s=>{xe=!0,I=null,Z=null,e==null||e({available:!1,label:"Transformers.js unavailable",detail:s instanceof Error?s.message:"Transformers.js failed to load."})})}async function pr({model:e,prompt:t,modelId:r=je,onStatus:n}){if(!C||G!==r)return null;n==null||n({available:!0,label:"Generating with Transformers.js",detail:r});try{const s=await C([{role:"system",content:mr},{role:"user",content:gr({model:e,prompt:t})}],{max_new_tokens:ur,do_sample:!1,return_full_text:!1,tokenizer_encode_kwargs:{enable_thinking:!1}}),l=xr(kr(s),t);try{return JSON.parse(l)}catch{return{message:"Transformers.js returned text that could not be parsed as JSON.",promptOutput:l}}}finally{n==null||n({available:!0,label:"Transformers.js ready",detail:r})}}async function br(e,t,r,n){e&&await e.dispose();const{pipeline:s}=await qe(async()=>{const{pipeline:l}=await import("./transformers.web-CjIQtwg9.js");return{pipeline:l}},[]);return s("text-generation",t.id,{device:"webgpu",dtype:t.dtype,progress_callback:l=>{n==null||n({available:!0,label:"Loading Transformers.js",detail:`${vr(l)} - ${r?"Cache Load":"First Load"}`})}})}async function hr(e){if(!("caches"in globalThis))return!1;const t=e.split("/").at(-1),r=await caches.keys();for(const n of r)if((await(await caches.open(n)).keys()).some(l=>l.url.includes(e)||l.url.includes(t??e)))return!0;return!1}function yr(e){return _.find(t=>t.id===e)??_[0]}function gr({model:e,prompt:t}){return`Model / Structure:
154
- ${e}
155
-
156
- Prompt:
157
- ${t}`}function vr(e){if(!e||typeof e!="object")return"Loading Transformers.js model";const t=e;return typeof t.progress=="number"?`${Math.round(t.progress)}% ${t.file??t.name??"model file"}`:typeof t.loaded=="number"&&typeof t.total=="number"?`${Math.round(t.loaded/t.total*100)}% ${t.file??t.name??"model file"}`:t.status??"Loading Transformers.js model"}function kr(e){const t=Array.isArray(e)?e[0]:null;if(!t||typeof t!="object"||!("generated_text"in t))return JSON.stringify(e,null,2);const r=t.generated_text;if(typeof r=="string")return he(r);if(Array.isArray(r)){const n=r.at(-1);if(n&&typeof n=="object"&&"content"in n)return he(String(n.content??""))}return he(JSON.stringify(r,null,2))}function he(e){const r=e.replace(/<think>[\s\S]*?<\/think>/gi,"").trim().replace(/^```(?:json)?\s*/i,"").replace(/\s*```$/i,"").trim();return wr(r)??r}function wr(e){const t=Be(e,"{","}");if(t)return te(t);const r=Be(e,"[","]");return r?te(r):null}function Be(e,t,r){const n=e.indexOf(t);if(n===-1)return null;let s=0,l=!1,c=!1;for(let o=n;o<e.length;o+=1){const u=e[o];if(l){c?c=!1:u==="\\"?c=!0:u==='"'&&(l=!1);continue}if(u==='"')l=!0;else if(u===t)s+=1;else if(u===r&&(s-=1,s===0))return e.slice(n,o+1)}return null}function te(e){try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}function xr(e,t){const r=Nr(t);if(!r||!/product/i.test(t))return e;try{const n=JSON.parse(e),s=jr(n);if(s.length===r)return te(JSON.stringify(ze(s)));const l=s[0]??Mr(n),c=Array.from({length:r},(o,u)=>Lr(l,u+1));return te(JSON.stringify(ze(c)))}catch{return e}}function Nr(e){const t=e.match(/id\s+(\d+)\s*(?:to|-)\s*(\d+)/i);if(t){const s=Number(t[1]),l=Number(t[2]);if(Number.isFinite(s)&&Number.isFinite(l)&&l>=s)return l-s+1}const r=e.match(/(?:exactly\s+)?(\d+)\s+(?:productmodel\s+)?items?/i);if(!r)return null;const n=Number(r[1]);return Number.isFinite(n)&&n>0?n:null}function jr(e){if(!A(e))return[];if(Array.isArray(e.products))return e.products.filter(A);const t=Array.isArray(e.body)?e.body[0]:null;return A(t)&&Array.isArray(t.products)?t.products.filter(A):Array.isArray(e.body)?e.body.filter(re):re(e)?[e]:[]}function Mr(e){if(!A(e))return null;const t=Array.isArray(e.body)?e.body[0]:null;return re(t)?t:re(e)?e:null}function ze(e){return{method:"GET",path:"/api/products",status:200,headers:{"content-type":"application/json"},body:[{products:e}]}}function Lr(e,t){var l;const r=["electronics","home","apparel","books"],n=["Laptop","Desk Lamp","Cotton Hoodie","Notebook Set","Wireless Mouse","Ceramic Mug","Running Shirt","Cookbook","Bluetooth Speaker","Storage Basket"],s=r[(t-1)%r.length];return{id:String(t),sku:`SKU-${String(t).padStart(3,"0")}`,name:n[t-1]??`Product ${t}`,category:s,priceCents:Ne(e==null?void 0:e.priceCents,1999)+(t-1)*250,currency:"USD",inventoryCount:Math.max(0,Ne(e==null?void 0:e.inventoryCount,100)-(t-1)*3),rating:Math.min(5,Math.round((4.1+t%5*.2)*10)/10),tags:[s,((l=n[t-1])==null?void 0:l.toLowerCase().replaceAll(" ","-"))??`product-${t}`],dimensions:{widthCm:ye(e,"widthCm",20+t),heightCm:ye(e,"heightCm",10+t),depthCm:ye(e,"depthCm",5+t)},createdAt:`2023-04-${String(t).padStart(2,"0")}T00:00:00Z`}}function Ne(e,t){return typeof e=="number"&&Number.isFinite(e)?e:t}function ye(e,t,r){const n=A(e==null?void 0:e.dimensions)?e.dimensions:null;return Ne(n==null?void 0:n[t],r)}function re(e){return A(e)&&"id"in e&&"sku"in e&&"name"in e&&"priceCents"in e&&"dimensions"in e}function A(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}const Xe="/__mocklane/mocks";let X=null;async function Cr(e){const t=await fetch(Xe,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(e)});await Ye(t),X=e}async function ge(){if(!X)return;const e=await fetch(Xe,{method:"DELETE",headers:{"content-type":"application/json"},body:JSON.stringify(X)});await Ye(e),X=null}function O(e){if(!e||typeof e!="object"||Array.isArray(e))return!1;const t=e;return typeof t.method=="string"&&typeof t.path=="string"&&typeof t.status=="number"&&!!t.headers&&typeof t.headers=="object"&&!Array.isArray(t.headers)&&"body"in t}async function Ye(e){if(e.ok)return;const t=await e.json().catch(()=>null);throw new Error((t==null?void 0:t.error)??"Mocklane local CLI is unavailable.")}const Ar="My job is to make mock APIs. Drop a model or structure, then ask me for an endpoint response and I can help you.",Tr="The selected local model is still warming up or unavailable. Mocklane will not manually fabricate this response; try again when the model is ready.",Or="The local model responded, but Mocklane could not turn it into a valid mock API JSON response. Try a smaller request or ask for fewer items.";async function Er({model:e,prompt:t,provider:r="webllm",transformersModelId:n,onStatus:s}){if(!e.trim()||!t.trim())return{message:Ar};try{const l=r==="transformers"?await pr({model:e,prompt:t,modelId:n,onStatus:s}):r==="native"?await cr({model:e,prompt:t,onStatus:s}):await er({model:e,prompt:t,onStatus:s});if(l)return Ir(l)}catch{return{message:Or}}return s==null||s({available:!1,label:"Local model not ready",detail:"No manual generation is active."}),{message:Tr}}function Ir(e){if("message"in e)return e;const t=e.body??[];return{method:String(e.method||"GET").toUpperCase(),path:String(e.path||"/api/mock"),status:Number(e.status||200),headers:{"content-type":"application/json",...e.headers||{}},body:t}}const _r=[{label:"Go struct detected",test:/^\s*package\s+\w+|\btype\s+\w+\s+struct\b|\bconst\s*\([^)]*\b\w+\s+\w+\s*=/is},{label:"TypeScript model detected",test:/\binterface\s+\w+\s*\{|\btype\s+\w+\s*=\s*(?:\{|"[^"]|[A-Z]\w*\s*[|&])/i},{label:"Python model detected",test:/\bclass\s+\w+|BaseModel|pydantic|:\s*(str|int|float|bool)\b/i},{label:"Java model detected",test:/\b(public\s+)?class\s+\w+|private\s+(String|Integer|Long|Double|Boolean)\b/i},{label:"Prisma model detected",test:/\bmodel\s+\w+\s*\{|@id|@default/i},{label:"SQL schema detected",test:/\bcreate\s+table\b|\b(varchar|uuid|timestamp|boolean)\b/i},{label:"JSON shape detected",test:/^\s*[\[{]/}];function Pr(e){return _r.find(r=>r.test.test(e))??{label:e.trim()?"Structure detected":"Waiting for model"}}function Ue(e){return JSON.stringify(e,null,2)??""}function $r(e){try{return JSON.parse(e)}catch{return null}}const Rr={method:"GET",path:"/api/products",status:200,headers:{"content-type":"application/json"},body:[]},et=[{id:"transformers",label:"Transformers.js models",detail:"Qwen3 0.6B ONNX",available:!1},{id:"webllm",label:"WebLLM models",detail:"Qwen2.5 0.5B Instruct",available:!1},{id:"native",label:"Native browser AI",detail:"window.ai / LanguageModel",available:!1}],Sr=`Make a GET all products response.
158
- Return 10 ProductModel items.
159
- The products array must contain exactly 10 items.
160
- Use id values "1" through "10".`,Jr=`export type ProductCategory = "electronics" | "home" | "apparel" | "books";
161
-
162
- export interface ProductModel {
163
- id: string;
164
- sku: string;
165
- name: string;
166
- category: ProductCategory;
167
- priceCents: number;
168
- currency: "USD";
169
- inventoryCount: number;
170
- rating: number;
171
- tags: string[];
172
- dimensions: {
173
- widthCm: number;
174
- heightCm: number;
175
- depthCm: number;
176
- };
177
- createdAt: string;
178
- }`;function Fr(){return[{title:"Mocklane"},{name:"description",content:"Generate JSON HTTP mock API responses from pasted models."}]}const qr=vt(function(){const[t,r]=d.useState(Jr),[n,s]=d.useState(Sr),[l,c]=d.useState(Rr),[o,u]=d.useState({available:!1,label:"Checking local model"}),[m,h]=d.useState("light"),[f,y]=d.useState(!1),[k,N]=d.useState(!1),[ne,D]=d.useState(!1),[W,B]=d.useState(!1),[Me,z]=d.useState(""),[se,T]=d.useState(null),[ie,le]=d.useState(""),[Le,Ce]=d.useState(!1),[P,U]=d.useState(!1),[Ae,oe]=d.useState(null),[tt,F]=d.useState(null),[ce,rt]=d.useState(et),[Te,at]=d.useState("transformers"),[de,nt]=d.useState(je),[ue,q]=d.useState(!1),[Oe,$]=d.useState(!1),R=d.useRef(0),S=d.useRef(null),Ee=d.useRef(null),Ie=d.useRef(null),st=d.useMemo(()=>Pr(t),[t]),J=d.useMemo(()=>Ue(l),[l]),g=ce.find(i=>i.id===Te)??ce[0],j=_.find(i=>i.id===de)??_[0],me=Dr(g),_e=Br({option:g,capabilityAvailable:me.available,modelStatus:o,isLoadingProvider:P}),H=zr({option:g,modelStatus:o}),it=Wr(g,o),lt=f?`${tt??0}ms`:Ae===null?"Not run yet":`${Ae}ms`;d.useEffect(()=>{u(K(de)),rt(Gr())},[de]),d.useEffect(()=>{function i(p){var b,M;(b=Ee.current)!=null&&b.contains(p.target)||q(!1),(M=Ie.current)!=null&&M.contains(p.target)||$(!1)}return document.addEventListener("pointerdown",i),()=>document.removeEventListener("pointerdown",i)},[]),d.useEffect(()=>{const i=window.localStorage.getItem("mocklane-theme");if(i==="light"||i==="dark"){h(i);return}window.matchMedia("(prefers-color-scheme: dark)").matches&&h("dark")},[]),d.useEffect(()=>{document.documentElement.dataset.theme=m,window.localStorage.setItem("mocklane-theme",m)},[m]),d.useEffect(()=>{if(!f||S.current===null)return;const i=()=>{S.current!==null&&F(Math.round(performance.now()-S.current))};i();const p=window.setInterval(i,100);return()=>window.clearInterval(p)},[f]);async function ot(){if(!H)return;const i=R.current+1,p=performance.now();R.current=i,S.current=p,F(0),oe(null),y(!0);try{const b=await Er({model:t,prompt:n,provider:g.id,transformersModelId:j.id,onStatus:V=>{R.current===i&&u(V)}});if(R.current!==i)return;c(b),B(!1),T(null),D(O(b)),O(b)&&le(Q(b.path));const M=Math.round(performance.now()-p);F(M),oe(M),N(!1),await ge()}finally{R.current===i&&(S.current=null,y(!1))}}async function ct(){await navigator.clipboard.writeText(J),Ce(!0),window.setTimeout(()=>Ce(!1),1100)}function dt(){z(J),T(null),B(!0)}function ut(){z(""),T(null),B(!1)}async function mt(){let i;try{i=JSON.parse(Me)}catch(b){T(b instanceof SyntaxError?b.message:"Invalid JSON.");return}k&&(await ge(),N(!1)),c(i);const p=O(i);D(p),O(i)&&le(Q(i.path)),z(Ue(i)),T(null),B(!1)}function ft(){const i=$r(J),b=`${(i&&typeof i=="object"&&"path"in i?String(i.path).replaceAll("/","_"):"mock_api").replace(/^_/,"")||"mock_api"}.json`,M=new Blob([J],{type:"application/json"}),V=URL.createObjectURL(M),fe=document.createElement("a");fe.href=V,fe.download=b,fe.click(),URL.revokeObjectURL(V)}function pt(){h(i=>i==="dark"?"light":"dark")}async function bt(){if(k){await ge(),N(!1);return}O(l)&&(await Cr({...l,path:`/${Q(ie)}`}),N(!0))}function ht(i){i.available&&(at(i.id),i.id==="transformers"?u(K(j.id)):i.id==="webllm"?u(ke()):i.id==="native"&&u(or()),q(!1),$(!1))}function yt(i){if(i===j.id||P||f){$(!1);return}nt(i),u(K(i)),$(!1),oe(null),F(null)}function gt(){if(!me.available)return;if(g.id==="transformers"){const p=K(j.id);u(p),U(!0),fr(b=>{u(b),ae(b)||U(!1)},{reload:p.label==="Transformers.js ready",modelId:j.id});return}if(g.id!=="webllm")return;const i=ke();u(i),U(!0),Yt(p=>{u(p),ae(p)||U(!1)},{reload:i.label==="WebLLM ready"})}return a.jsxs("main",{className:"app-shell",children:[a.jsx("header",{className:"topbar",children:a.jsxs("div",{className:"topbar-inner",children:[a.jsxs("div",{className:"title-line",children:[a.jsxs("div",{className:"brand-logo","aria-label":"Mocklane",children:[a.jsx("span",{children:"MOCK"}),a.jsx("span",{children:"LANE"})]}),a.jsx("h1",{children:"Mock API response generator"})]}),a.jsx("div",{className:"topbar-actions",children:a.jsx("button",{className:"icon-button",type:"button",onClick:pt,"aria-label":m==="dark"?"Switch to light mode":"Switch to dark mode",title:m==="dark"?"Light mode":"Dark mode",children:m==="dark"?a.jsx(Ft,{size:17,"aria-hidden":"true"}):a.jsx(Rt,{size:17,"aria-hidden":"true"})})})]})}),a.jsxs("section",{className:"workspace","aria-label":"Mocklane mock API generator",children:[a.jsx("div",{className:"workspace-summary",children:a.jsxs("div",{className:"model-stat-grid","aria-label":"Local model runtime status",children:[a.jsxs("div",{className:"provider-stat",ref:Ee,children:[a.jsxs("div",{className:"provider-stat-card",children:[a.jsx("button",{className:"provider-dropdown-trigger",type:"button",onClick:()=>q(i=>!i),"aria-haspopup":"listbox","aria-expanded":ue,children:a.jsxs("span",{className:"provider-copy",children:[a.jsx("span",{children:"Provider"}),a.jsx("strong",{children:g.label})]})}),a.jsx("div",{className:"provider-status-wrap",children:a.jsxs("span",{className:"provider-status",children:[a.jsx("span",{className:`provider-status-dot ${_e.tone}`,"aria-hidden":"true"}),a.jsx("span",{children:_e.label})]})}),a.jsx("button",{className:"provider-chevron-button",type:"button",onClick:()=>q(i=>!i),"aria-label":"Choose provider","aria-haspopup":"listbox","aria-expanded":ue,children:a.jsx($e,{size:16,"aria-hidden":"true"})})]}),ue?a.jsx("div",{className:"model-menu provider-menu",role:"listbox","aria-label":"Model provider",children:ce.map(i=>a.jsxs("button",{className:"model-menu-item",type:"button",role:"option",disabled:!i.available,"aria-selected":i.id===Te,onClick:()=>ht(i),children:[a.jsxs("span",{children:[a.jsx("span",{className:"model-option-label",children:i.label}),a.jsx("span",{className:"model-option-detail",children:i.detail})]}),i.available?null:a.jsx("span",{className:"model-unavailable",children:"not available"})]},i.id))}):null]}),a.jsxs("div",{className:g.id==="transformers"?"model-stat-dropdown":void 0,ref:g.id==="transformers"?Ie:void 0,children:[a.jsx("span",{children:"Model"}),g.id==="transformers"?a.jsxs(a.Fragment,{children:[a.jsxs("button",{className:"model-stat-trigger",type:"button",onClick:()=>$(i=>!i),disabled:P||f,"aria-haspopup":"listbox","aria-expanded":Oe,children:[a.jsx("strong",{children:j.label}),a.jsx($e,{size:15,"aria-hidden":"true"})]}),Oe?a.jsx("div",{className:"model-menu transformer-model-menu",role:"listbox","aria-label":"Transformers.js model",children:_.map(i=>a.jsxs("button",{className:"model-menu-item",type:"button",role:"option","aria-selected":i.id===j.id,onClick:()=>yt(i.id),children:[a.jsxs("span",{children:[a.jsx("span",{className:"model-option-label",children:i.label}),a.jsx("span",{className:"model-option-detail",children:i.detail})]}),i.id===j.id?a.jsx(Ct,{size:16,"aria-hidden":"true"}):null]},i.id))}):null]}):a.jsx("strong",{children:g.detail})]}),a.jsxs("div",{children:[a.jsx("span",{children:"Elapsed"}),a.jsx("strong",{children:lt})]})]})}),a.jsxs("div",{className:"panels",children:[a.jsxs("section",{className:"input-panel","aria-label":"Mock API inputs",children:[a.jsx(Se,{id:"promptInput",label:"Prompt",meta:"Always outputs JSON HTTP responses",value:n,onChange:s,placeholder:"Example: Make me a GET /api/products response with 10 cyberpunk e-commerce items.",variant:"compact"}),a.jsx(Se,{id:"modelInput",label:"Model / Structure",meta:st.label,value:t,onChange:r,spellCheck:!1,placeholder:"Drop a TypeScript interface, Python/Pydantic class, Go struct, Java DTO, Prisma model, OpenAPI schema, SQL table, or example JSON object."}),a.jsxs("div",{className:"actions",children:[a.jsxs("span",{className:`generate-control ${H?"":"needs-model"}`,tabIndex:H?-1:0,children:[a.jsxs("button",{type:"button",onClick:ot,disabled:f||!H,children:[a.jsx(Ht,{size:17,"aria-hidden":"true"}),f?"Generating":"Generate"]}),a.jsx("span",{className:"tooltip-bubble",children:"Load model first"})]}),g.id!=="native"?a.jsxs("button",{className:"load-provider-action",type:"button",onClick:gt,disabled:P||!me.available,children:[P?a.jsx(Pt,{className:"spin-icon",size:16,"aria-hidden":"true"}):a.jsx(Re,{size:16,"aria-hidden":"true"}),it]}):null]}),o.detail?a.jsx("p",{className:"provider-progress-text",children:o.detail}):null]}),a.jsxs("section",{className:"output-panel","aria-label":"Generated mock API response",children:[a.jsx("div",{className:"output-head",children:a.jsx("p",{className:"eyebrow",children:"HTTP JSON"})}),a.jsxs("div",{className:"code-wrap",children:[a.jsx("div",{className:"code-actions","aria-label":"JSON output actions",children:W?a.jsxs(a.Fragment,{children:[a.jsxs("button",{className:"code-icon-button",type:"button",onClick:mt,"aria-label":"Save JSON",title:"Save JSON",children:[a.jsx(zt,{size:18,"aria-hidden":"true"}),a.jsx("span",{className:"tooltip-bubble",children:"Save changes"})]}),a.jsxs("button",{className:"code-icon-button",type:"button",onClick:ut,"aria-label":"Cancel editing",title:"Cancel editing",children:[a.jsx(Kt,{size:18,"aria-hidden":"true"}),a.jsx("span",{className:"tooltip-bubble",children:"Cancel editing"})]})]}):a.jsxs(a.Fragment,{children:[a.jsxs("button",{className:"code-icon-button",type:"button",onClick:dt,disabled:f,"aria-label":"Edit JSON",title:"Edit JSON",children:[a.jsx(Jt,{size:18,"aria-hidden":"true"}),a.jsx("span",{className:"tooltip-bubble",children:"Click to edit"})]}),a.jsxs("button",{className:`code-icon-button copy-control ${Le?"copied":""}`,type:"button",onClick:ct,"aria-label":"Copy JSON",children:[a.jsx(Ot,{size:18,"aria-hidden":"true"}),a.jsx("span",{className:"tooltip-bubble",children:Le?"Copied!":"Click to copy"})]}),a.jsxs("button",{className:"code-icon-button",type:"button",onClick:ft,"aria-label":"Download JSON",title:"Download JSON",children:[a.jsx(It,{size:18,"aria-hidden":"true"}),a.jsx("span",{className:"tooltip-bubble",children:"Click to download"})]})]})}),W?a.jsxs("div",{className:"json-editor-shell",children:[a.jsx("textarea",{className:"json-editor",value:Me,onChange:i=>{z(i.target.value),se&&T(null)},"aria-label":"Edit JSON response",spellCheck:!1,autoFocus:!0}),se?a.jsxs("p",{className:"json-editor-error",role:"alert",children:["Invalid JSON: ",se]}):null]}):a.jsx("pre",{tabIndex:0,children:a.jsx("code",{children:J})})]}),a.jsxs("div",{className:"mock-footer",children:[a.jsxs("label",{className:"mock-endpoint",children:[a.jsx("span",{children:"localhost:4000/"}),a.jsx("input",{type:"text",value:ie,onChange:i=>le(i.target.value),placeholder:"api/products","aria-label":"Mock API path",disabled:k||W,spellCheck:!1})]}),a.jsxs("button",{className:k?"mock-active":"secondary",type:"button",onClick:bt,disabled:W||!ne||!O(l)||!Q(ie),children:[k?a.jsx(Dt,{size:16,"aria-hidden":"true"}):a.jsx(Re,{size:16,"aria-hidden":"true"}),k?"Unmock API":"Mock API"]})]})]})]})]})]})});function Q(e){return e.trim().replace(/^\/+/,"")}function Gr(){var n;const e=typeof navigator<"u"&&"gpu"in navigator,t=typeof navigator<"u"&&"gpu"in navigator,r=typeof window<"u"&&!!((n=window.ai)!=null&&n.languageModel||window.LanguageModel);return et.map(s=>s.id==="webllm"?{...s,available:e}:s.id==="transformers"?{...s,available:t}:s.id==="native"?{...s,available:r}:s)}function Dr(e){return e.id==="webllm"?{label:"WebGPU",available:typeof navigator<"u"&&"gpu"in navigator}:e.id==="native"?{label:"Browser AI",available:e.available}:{label:"Transformers.js",available:e.available}}function Wr(e,t){return e.id==="webllm"?t.label==="WebLLM ready"?"Reload WebLLM":"Load WebLLM":e.id==="transformers"?t.label==="Transformers.js ready"?"Reload Transformers.js":"Load Transformers.js":"Load Browser AI"}function Br({option:e,capabilityAvailable:t,modelStatus:r,isLoadingProvider:n}){if(!t)return{label:"not available",tone:"missing"};if(e.id==="webllm"){if(n||ae(r))return{label:"loading",tone:"loading"};if(r.label==="Generating locally")return{label:"running",tone:"running"};if(r.label==="WebLLM ready")return{label:"ready",tone:"ready"}}if(e.id==="transformers"){if(n||ae(r))return{label:"loading",tone:"loading"};if(r.label==="Generating with Transformers.js")return{label:"running",tone:"running"};if(r.label==="Transformers.js ready")return{label:"ready",tone:"ready"}}return e.id==="native"?r.label==="Native Browser AI preparing"?{label:"loading",tone:"loading"}:r.label==="Generating with Native Browser AI"?{label:"running",tone:"running"}:{label:"available",tone:"available"}:{label:"available",tone:"available"}}function ae(e){return e.label==="WebLLM warming up"||e.label==="Loading local model"||e.label==="Transformers.js warming up"||e.label==="Loading Transformers.js"}function zr({option:e,modelStatus:t}){return e.id==="webllm"?t.label==="WebLLM ready":e.id==="transformers"?t.label==="Transformers.js ready":e.id==="native"?e.available:!1}export{qr as default,Fr as meta};