infra-kit 0.17.3 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/chunk-7A4LVTRJ.js +3 -0
  2. package/dist/chunk-7A4LVTRJ.js.map +7 -0
  3. package/dist/chunk-BQO7ZGXK.js +14 -0
  4. package/dist/chunk-BQO7ZGXK.js.map +7 -0
  5. package/dist/chunk-HICPDIZI.js +10 -0
  6. package/dist/{chunk-S72CNBLL.js.map → chunk-HICPDIZI.js.map} +4 -4
  7. package/dist/chunk-Q5BVOBPD.js +12 -0
  8. package/dist/chunk-Q5BVOBPD.js.map +7 -0
  9. package/dist/chunk-SAHE3MZJ.js +2 -0
  10. package/dist/chunk-SAHE3MZJ.js.map +7 -0
  11. package/dist/{chunk-E2IO3K4A.js → chunk-U2H5ZS4W.js} +2 -2
  12. package/dist/{chunk-E2IO3K4A.js.map → chunk-U2H5ZS4W.js.map} +1 -1
  13. package/dist/chunk-UG45WH3J.js +7 -0
  14. package/dist/{chunk-F5OGA74S.js.map → chunk-UG45WH3J.js.map} +3 -3
  15. package/dist/{chunk-IT6XJWTW.js → chunk-XES5EOEE.js} +2 -2
  16. package/dist/cli.js +140 -164
  17. package/dist/cli.js.map +4 -4
  18. package/dist/dev-server.js +19 -19
  19. package/dist/dev-server.js.map +3 -3
  20. package/dist/dev-wizard-run-LZYQQOXT.js +2 -0
  21. package/dist/dev-wizard-run-LZYQQOXT.js.map +7 -0
  22. package/dist/env-load-C37GZO6V.js +2 -0
  23. package/dist/mcp-proxy.js +1 -1
  24. package/dist/update-check.js +1 -1
  25. package/package.json +13 -13
  26. package/readme.md +4 -4
  27. package/dist/chunk-2Q5PAVNA.js +0 -3
  28. package/dist/chunk-2Q5PAVNA.js.map +0 -7
  29. package/dist/chunk-4CQ6YW4B.js +0 -14
  30. package/dist/chunk-4CQ6YW4B.js.map +0 -7
  31. package/dist/chunk-5AXJ5XHA.js +0 -12
  32. package/dist/chunk-5AXJ5XHA.js.map +0 -7
  33. package/dist/chunk-F5OGA74S.js +0 -7
  34. package/dist/chunk-S72CNBLL.js +0 -10
  35. package/dist/chunk-Y3RW74TE.js +0 -2
  36. package/dist/chunk-Y3RW74TE.js.map +0 -7
  37. package/dist/dev-wizard-run-ZRBSMQIB.js +0 -2
  38. package/dist/dev-wizard-run-ZRBSMQIB.js.map +0 -7
  39. package/dist/env-load-2STPH3MX.js +0 -2
  40. /package/dist/{chunk-IT6XJWTW.js.map → chunk-XES5EOEE.js.map} +0 -0
  41. /package/dist/{env-load-2STPH3MX.js.map → env-load-C37GZO6V.js.map} +0 -0
@@ -1,26 +1,26 @@
1
- import{a as qe,b as N,c as j,d as Xe,e as W,g as L,h as Qe,l as Ze,n as et,r as tt,s as rt,t as nt,u as ae,v as it}from"./chunk-S72CNBLL.js";import{a as O,b as M,c as Be,d as Me,e as H,f as He,g as I,h as je,i as We,j as Ge,k as Ve,l as Ke,m as Je,n as ze}from"./chunk-Y3RW74TE.js";import{c as ne,k as ie,n as Ie,o as Ne,u as Le}from"./chunk-IT6XJWTW.js";import"./chunk-AWFGP6GY.js";import{P as T,Y as Fe,d as _e,da as oe,ga as Ye,i as se,x as Ue}from"./chunk-4CQ6YW4B.js";import"./chunk-MFAFQVTW.js";import"./chunk-E2IO3K4A.js";import{d as $e,f as Ce,n as Oe}from"./chunk-I23MDR4X.js";import{a as _,b as st}from"./chunk-DVXTZPQ5.js";import{Command as ii}from"commander";import k from"node:process";import{pathToFileURL as si}from"node:url";import le from"node:process";var ue=n=>{try{le.stderr.write(n)}catch{}},U=(n,e,t=!0)=>{let r=e instanceof Error?`${e.message}
1
+ import{a as N,b as j,c as Je,d as ze,e as W,f as qe,g as L,h as Ye,i as Xe,j as Qe,k as Ze,l as et}from"./chunk-SAHE3MZJ.js";import{a as C,b as M,c as Be,d as H,f as I,g as Me,k as He,m as je,q as We,r as Ge,s as Ve,t as oe,u as Ke}from"./chunk-HICPDIZI.js";import{d as ne,l as ie,o as Ce,p as Ie,v as Ne}from"./chunk-XES5EOEE.js";import"./chunk-AWFGP6GY.js";import{P as T,Z as Ue,d as Le,ga as Fe,i as se,x as _e}from"./chunk-BQO7ZGXK.js";import"./chunk-MFAFQVTW.js";import"./chunk-U2H5ZS4W.js";import{d as Te,f as $e,n as Oe}from"./chunk-I23MDR4X.js";import{a as _,b as tt}from"./chunk-DVXTZPQ5.js";import{Command as Zn}from"commander";import k from"node:process";import{pathToFileURL as ei}from"node:url";import ae from"node:process";var le=n=>{try{ae.stderr.write(n)}catch{}},U=(n,e,t=!0)=>{let r=e instanceof Error?`${e.message}
2
2
  ${e.stack??"(no stack)"}`:String(e);return`
3
3
  \u26A0\uFE0F ${n}: ${r}
4
4
  ${t?` dev-server kept alive (likely a bug in a handler's async path); restart if it misbehaves.
5
5
  `:` dev-server is shutting down: its stdio is unwritable, so reporting this fault would loop forever.
6
- `}`},gr=(n,e)=>{ue(U(n,e))},fr=(n,e)=>{ue(U(n,e,!1))},mr=n=>{ue(`
6
+ `}`},dr=(n,e)=>{le(U(n,e))},pr=(n,e)=>{le(U(n,e,!1))},cr=n=>{le(`
7
7
  \u2717 dev-server exiting: ${n}
8
- `),N(),le.exit(1)},vr=(n,e)=>{le.on(n,e)},ot=({onFault:n=gr,register:e=vr,isTerminalDead:t=()=>!1,fileFault:r=fr,onFatal:i=mr}={})=>{let s=!0,o=a=>l=>{if(!s){r(a,l);return}if(t()){r(a,l),i(`${a} while stdio is unwritable`);return}n(a,l)};return e("uncaughtException",o("uncaughtException")),e("unhandledRejection",o("unhandledRejection")),{disarm:()=>{s=!1}}};import{DEV_CONTEXT_WIRE_VERSION as Rn,DEV_SERVING_MARKER as Xt,loadPackageConfig as Qt,readAppAliasName as Se,readCloudOrigin as Zt,readLocalContext as En}from"@slip-stream-kit/config/internal";import xn from"chokidar";import{exec as Tn}from"node:child_process";import*as w from"node:fs";import $n from"node:net";import rr from"node:os";import*as y from"node:path";import v from"node:process";import Cn from"node:util";import{z as Pe}from"zod";import{loadDev as yr}from"@slip-stream-kit/config/internal";var at=async(n,e,t)=>{let r=e.find(a=>a.name===n),i=t.find(a=>a.name===n);if(!r&&!i)return null;let s={},o=[];if(r&&(s[`${n}/api`]={}),i){let a={};for(let[l,u]of Object.entries((await yr(i.path))?.proxy?.routes??{})){if(!u.from.includes("local"))continue;let d=e.find(h=>h.packageName===u.packageName);if(!d){o.push(`${l} (${u.packageName})`);continue}s[`${d.name}/api`]={},a[l]="local"}s[`${n}/ui`]=Object.keys(a).length>0?{proxy:a}:{}}return{preset:{apps:s},unprovided:o}};import{createHash as wr}from"node:crypto";import*as A from"node:fs";import*as b from"node:path";var br=n=>[n,b.join(n,"dist")].flatMap(e=>{try{return A.readdirSync(e).filter(t=>t.endsWith(".tsbuildinfo")).map(t=>b.join(e,t))}catch{return[]}}),G=n=>{let e;for(let t of br(n))try{let{mtimeMs:r}=A.statSync(t);(!e||r>e.mtimeMs)&&(e={file:t,mtimeMs:r})}catch{}if(e)try{let t=JSON.parse(A.readFileSync(e.file,"utf8"));return{dir:b.dirname(e.file),mtimeMs:e.mtimeMs,data:t}}catch{return}},lt=(n,e)=>e.every(t=>{try{return A.statSync(t).mtimeMs<=n.mtimeMs}catch{return!0}}),Sr=n=>{let e=n.messageText;return typeof e=="string"?e:e?.messageText??""},ut=(n,e)=>{let{data:t}=n,r=a=>{let l=t.fileNames?.[a-1];return l===void 0?void 0:b.relative(e,b.resolve(n.dir,l))},i=[],s=[];for(let a of[...t.semanticDiagnosticsPerFile??[],...t.emitDiagnosticsPerFile??[]]){if(typeof a=="number"){s.push({file:r(a),total:1});continue}let[l,u]=a;for(let d of u)i.push({file:r(l),detail:`TS${d.code} ${Sr(d)}`,total:1})}let o=[...i,...s];return o.length===0?t.errors===!0?{total:1}:void 0:{...o[0],total:o.length}},dt=(n,e)=>{let t=e.total>1?` (+${e.total-1} more)`:"";return e.file===void 0?`${n} has build errors (see the watch log)${t}`:e.detail===void 0?`${n} has errors: ${e.file} was not type-checked (syntax error)${t}`:`${n} has type errors: ${e.file} ${e.detail}${t}`},Pr="\uFEFF",Dr=n=>wr("sha256").update(n).digest("hex"),de=(n,e)=>{let t=n.data.fileNames??[],r=n.data.fileInfos??[],i=[];for(let[s,o]of t.entries()){let a=b.resolve(n.dir,o),l=b.relative(e,a);if(l.startsWith("..")||b.isAbsolute(l)||l.split(b.sep).includes("node_modules"))continue;let u=r[s],d=typeof u=="string"?u:u?.version;if(d===void 0)continue;let h;try{h=A.readFileSync(a,"utf8")}catch{continue}let f=Dr(h.startsWith(Pr)?h.slice(1):h);f!==d&&i.push({file:a,hash:f})}return i},pe=n=>{try{let{name:e}=JSON.parse(A.readFileSync(b.join(n,"package.json"),"utf8"));if(typeof e=="string"&&e!=="")return e}catch{}return b.basename(n)};import{execFile as kr}from"node:child_process";import*as pt from"node:fs";import*as V from"node:path";import{promisify as Ar}from"node:util";var Rr=Ar(kr),Er=n=>{try{let e=JSON.parse(pt.readFileSync(V.join(n,"package.json"),"utf-8"));return typeof e.name=="string"?e.name:void 0}catch{return}},xr=n=>{let t=(JSON.parse(n).tasks??[]).map(r=>r.package).filter(r=>typeof r=="string");return[...new Set(t)]},Tr=n=>async e=>{let{stdout:t}=await Rr("pnpm",["exec","turbo","run","build","--dry=json",`--filter=...${e}`],{cwd:n,maxBuffer:33554432});return xr(t)},$r=n=>{let e=new Map,t=new Map;for(let r of I(n)){let i=Er(V.dirname(r));i!==void 0&&(e.set(r,i),t.set(i,r))}return{packageNameByDir:e,dirByName:t}},ct=async(n,e,t=Tr(n))=>{let{packageNameByDir:r,dirByName:i}=$r(n),s=new Map,o=await Promise.all(e.map(async a=>({app:a,closure:await t(a.packageName)})));for(let{app:a,closure:l}of o)for(let u of l){let d=i.get(u);if(d===void 0)continue;let h=s.get(d)??new Set;h.add(a.name),s.set(d,h)}return{dependentsByPackageDir:s,packageNameByDir:r}},ht=(n,e,t)=>{if(e===null||t===void 0)return null;let r=e.dependentsByPackageDir.get(t)??new Set;return n.filter(i=>i.watchDeps&&r.has(i.name))};var Cr="this run never launched it",Or=n=>n.default??n.from[0]??"cloud",gt=(n,e)=>n.from.includes("local")&&e?.has(n.packageName)?"local":Or(n),ft=n=>n.uis.flatMap(e=>Object.entries(e.routes).flatMap(([t,r])=>{let i=Ir(e,t,r,n);return i?[i]:[]})),Ir=(n,e,t,{wanted:r,running:i,reasons:s,held:o})=>{let{packageName:a}=t,l=r.has(a)||t.pinnedLocal===!0;if(!t.from.includes("local")||!l||i.has(a))return null;let u=gt(t,o),d=s.get(a),h=u==="cloud"?n.cloudUrl:void 0;return{uiApp:n.app,route:e,packageName:a,fallback:u,apiApp:d?.app,reason:d?.reason??Cr,cloudTarget:h}},mt=n=>{let{uis:e,running:t,localOrigin:r,held:i}=n;return e.flatMap(s=>Object.entries(s.routes).sort(([o],[a])=>o.localeCompare(a)).map(([o,a])=>{let l=a.from.includes("local")&&t.has(a.packageName)?"local":gt(a,i),u=l==="local"?r(a.packageName):s.cloudUrl;return{uiApp:s.app,route:o,packageName:a.packageName,source:l,target:u}}))},Nr=n=>{if(n.fallback==="local")return`would proxy ${n.route} at the local alias for "${n.packageName}", which nothing is serving \u2014 every request will fail`;let e=n.cloudTarget??"the cloud backend";return`would proxy ${n.route} to ${e} instead of your local backend`},vt=n=>n.apiApp!=null,Lr=n=>vt(n)?`fix ${n.apiApp}/api and re-run (or drop --no-watch to retry it on the next save)`:`this run never launched "${n.packageName}" \u2014 drop the --app/--self narrowing, add its api to the preset, or set "${n.route}" to "cloud" if you meant to develop against cloud`,yt=(n,e)=>{let t=n.map(r=>{let i=vt(r)?`${r.apiApp}/api`:`"${r.packageName}"`;return` ${r.uiApp}/ui ${r.route} \u2192 ${r.packageName}: ${r.uiApp}/ui ${Nr(r)}.
8
+ `),C(),ae.exit(1)},hr=(n,e)=>{ae.on(n,e)},rt=({onFault:n=dr,register:e=hr,isTerminalDead:t=()=>!1,fileFault:r=pr,onFatal:i=cr}={})=>{let s=!0,o=a=>l=>{if(!s){r(a,l);return}if(t()){r(a,l),i(`${a} while stdio is unwritable`);return}n(a,l)};return e("uncaughtException",o("uncaughtException")),e("unhandledRejection",o("unhandledRejection")),{disarm:()=>{s=!1}}};import{DEV_CONTEXT_WIRE_VERSION as Sn,DEV_SERVING_MARKER as Jt,loadPackageConfig as zt,readAppAliasName as be,readCloudOrigin as qt,readLocalContext as Pn}from"@slip-stream-kit/config/internal";import kn from"chokidar";import{exec as An}from"node:child_process";import*as m from"node:fs";import Dn from"node:net";import Qt from"node:os";import*as f from"node:path";import g from"node:process";import Rn from"node:util";import{z as Se}from"zod";import{loadDev as gr}from"@slip-stream-kit/config/internal";var nt=async(n,e,t)=>{let r=e.find(a=>a.name===n),i=t.find(a=>a.name===n);if(!r&&!i)return null;let s={},o=[];if(r&&(s[`${n}/api`]={}),i){let a={};for(let[l,u]of Object.entries((await gr(i.path))?.proxy?.routes??{})){if(!u.from.includes("local"))continue;let d=e.find(h=>h.packageName===u.packageName);if(!d){o.push(`${l} (${u.packageName})`);continue}s[`${d.name}/api`]={},a[l]="local"}s[`${n}/ui`]=Object.keys(a).length>0?{proxy:a}:{}}return{preset:{apps:s},unprovided:o}};import{createHash as fr}from"node:crypto";import*as D from"node:fs";import*as w from"node:path";var mr=n=>[n,w.join(n,"dist")].flatMap(e=>{try{return D.readdirSync(e).filter(t=>t.endsWith(".tsbuildinfo")).map(t=>w.join(e,t))}catch{return[]}}),G=n=>{let e;for(let t of mr(n))try{let{mtimeMs:r}=D.statSync(t);(!e||r>e.mtimeMs)&&(e={file:t,mtimeMs:r})}catch{}if(e)try{let t=JSON.parse(D.readFileSync(e.file,"utf8"));return{dir:w.dirname(e.file),mtimeMs:e.mtimeMs,data:t}}catch{return}},it=(n,e)=>e.every(t=>{try{return D.statSync(t).mtimeMs<=n.mtimeMs}catch{return!0}}),vr=n=>{let e=n.messageText;return typeof e=="string"?e:e?.messageText??""},st=(n,e)=>{let{data:t}=n,r=a=>{let l=t.fileNames?.[a-1];return l===void 0?void 0:w.relative(e,w.resolve(n.dir,l))},i=[],s=[];for(let a of[...t.semanticDiagnosticsPerFile??[],...t.emitDiagnosticsPerFile??[]]){if(typeof a=="number"){s.push({file:r(a),total:1});continue}let[l,u]=a;for(let d of u)i.push({file:r(l),detail:`TS${d.code} ${vr(d)}`,total:1})}let o=[...i,...s];return o.length===0?t.errors===!0?{total:1}:void 0:{...o[0],total:o.length}},ot=(n,e)=>{let t=e.total>1?` (+${e.total-1} more)`:"";return e.file===void 0?`${n} has build errors (see the watch log)${t}`:e.detail===void 0?`${n} has errors: ${e.file} was not type-checked (syntax error)${t}`:`${n} has type errors: ${e.file} ${e.detail}${t}`},yr="\uFEFF",wr=n=>fr("sha256").update(n).digest("hex"),ue=(n,e)=>{let t=n.data.fileNames??[],r=n.data.fileInfos??[],i=[];for(let[s,o]of t.entries()){let a=w.resolve(n.dir,o),l=w.relative(e,a);if(l.startsWith("..")||w.isAbsolute(l)||l.split(w.sep).includes("node_modules"))continue;let u=r[s],d=typeof u=="string"?u:u?.version;if(d===void 0)continue;let h;try{h=D.readFileSync(a,"utf8")}catch{continue}let y=wr(h.startsWith(yr)?h.slice(1):h);y!==d&&i.push({file:a,hash:y})}return i},de=n=>{try{let{name:e}=JSON.parse(D.readFileSync(w.join(n,"package.json"),"utf8"));if(typeof e=="string"&&e!=="")return e}catch{}return w.basename(n)};import{execFile as br}from"node:child_process";import*as at from"node:fs";import*as V from"node:path";import{promisify as Sr}from"node:util";var Pr=Sr(br),kr=n=>{try{let e=JSON.parse(at.readFileSync(V.join(n,"package.json"),"utf-8"));return typeof e.name=="string"?e.name:void 0}catch{return}},Ar=n=>{let t=(JSON.parse(n).tasks??[]).map(r=>r.package).filter(r=>typeof r=="string");return[...new Set(t)]},Dr=n=>async e=>{let{stdout:t}=await Pr("pnpm",["exec","turbo","run","build","--dry=json",`--filter=...${e}`],{cwd:n,maxBuffer:33554432});return Ar(t)},Rr=n=>{let e=new Map,t=new Map;for(let r of L(n)){let i=kr(V.dirname(r));i!==void 0&&(e.set(r,i),t.set(i,r))}return{packageNameByDir:e,dirByName:t}},lt=async(n,e,t=Dr(n))=>{let{packageNameByDir:r,dirByName:i}=Rr(n),s=new Map,o=await Promise.all(e.map(async a=>({app:a,closure:await t(a.packageName)})));for(let{app:a,closure:l}of o)for(let u of l){let d=i.get(u);if(d===void 0)continue;let h=s.get(d)??new Set;h.add(a.name),s.set(d,h)}return{dependentsByPackageDir:s,packageNameByDir:r}},ut=(n,e,t)=>{if(e===null||t===void 0)return null;let r=e.dependentsByPackageDir.get(t)??new Set;return n.filter(i=>r.has(i.name))};var Er="this run never launched it",xr=n=>n.default??n.from[0]??"cloud",dt=(n,e)=>n.from.includes("local")&&e?.has(n.packageName)?"local":xr(n),pt=n=>n.uis.flatMap(e=>Object.entries(e.routes).flatMap(([t,r])=>{let i=Tr(e,t,r,n);return i?[i]:[]})),Tr=(n,e,t,{wanted:r,running:i,reasons:s,held:o})=>{let{packageName:a}=t,l=r.has(a)||t.pinnedLocal===!0;if(!t.from.includes("local")||!l||i.has(a))return null;let u=dt(t,o),d=s.get(a),h=u==="cloud"?n.cloudUrl:void 0;return{uiApp:n.app,route:e,packageName:a,fallback:u,apiApp:d?.app,reason:d?.reason??Er,cloudTarget:h}},ct=n=>{let{uis:e,running:t,localOrigin:r,held:i}=n;return e.flatMap(s=>Object.entries(s.routes).sort(([o],[a])=>o.localeCompare(a)).map(([o,a])=>{let l=a.from.includes("local")&&t.has(a.packageName)?"local":dt(a,i),u=l==="local"?r(a.packageName):s.cloudUrl;return{uiApp:s.app,route:o,packageName:a.packageName,source:l,target:u}}))},$r=n=>{if(n.fallback==="local")return`would proxy ${n.route} at the local alias for "${n.packageName}", which nothing is serving \u2014 every request will fail`;let e=n.cloudTarget??"the cloud backend";return`would proxy ${n.route} to ${e} instead of your local backend`},ht=n=>n.apiApp!=null,Or=n=>ht(n)?`fix ${n.apiApp}/api and re-run (or drop --no-watch to retry it on the next save)`:`this run never launched "${n.packageName}" \u2014 drop the --app/--self narrowing, add its api to --target, or set "${n.route}" to "cloud" if you meant to develop against cloud`,gt=(n,e)=>{let t=n.map(r=>{let i=ht(r)?`${r.apiApp}/api`:`"${r.packageName}"`;return` ${r.uiApp}/ui ${r.route} \u2192 ${r.packageName}: ${r.uiApp}/ui ${$r(r)}.
9
9
  ${i}: ${r.reason}
10
- \u2192 ${Lr(r)}`});return`infra-kit dev: "${e}" is supposed to serve a backend locally, but it is not running \u2014 refusing to bring the frontend up silently pointed somewhere else.
10
+ \u2192 ${Or(r)}`});return`infra-kit dev: "${e}" is supposed to serve a backend locally, but it is not running \u2014 refusing to bring the frontend up silently pointed somewhere else.
11
11
 
12
12
  ${t.join(`
13
13
 
14
- `)}`};import{AsyncLocalStorage as _r}from"node:async_hooks";var ce=new _r,wt=(n,e)=>ce.run(n,e),bt=n=>{ce.enterWith(n)},K=()=>ce.getStore();import{Buffer as Dt}from"node:buffer";import D from"node:fs";import $ from"node:path";import x from"node:process";import z from"node:process";var Ur=new Set(["EAGAIN","EINTR"]),J=!1,St=()=>J,Fr=(n,e)=>n===z.stdout?"stdout":n===z.stderr||e===1?"stderr":"stdout",Pt=({streams:n=[z.stdout,z.stderr],onDeath:e}={})=>{let t=!1,r=[];for(let[i,s]of n.entries()){let o=Fr(s,i),a=l=>{let u=l??new Error("stdio error");u.code!=null&&Ur.has(u.code)||(J=!0,!t&&(t=!0,e?.(o,u)))};s.on("error",a),r.push({stream:s,listener:a})}return{isDead:()=>J,uninstall:()=>{for(let{stream:i,listener:s}of r)i.removeListener("error",s);r.length=0,J=!1,t=!1}}};var Br=()=>Object.getPrototypeOf(x.stdout).write,he=n=>St()?!0:Br().call(x.stdout,n);var Rt=()=>new Proxy(x.stdout,{get(n,e,t){if(e==="write")return i=>he(String(i));let r=Reflect.get(n,e,t);return typeof r=="function"?r.bind(n):r}}),kt=()=>({errors:0,warns:0,lastErrorAt:null}),Et=n=>{let e=n.split(/\W+/).filter(t=>t!=="").join("-");return e===""?null:e},Mr=()=>{let n=x.env[$e]??"",e=Et(n)??"no-session";return $.join(Oe(),e,"dev",String(x.pid))},At=n=>`${Et(n)??"unnamed"}.log`,Hr=10080*60*1e3,jr=n=>{let e=Number(n);if(!Number.isInteger(e)||e<=0)return!1;try{return x.kill(e,0),!0}catch(t){return t.code==="EPERM"}},Wr=(n,e)=>{let t;try{t=D.readdirSync(n,{withFileTypes:!0})}catch{return}for(let r of t){if(!r.isDirectory()||jr(r.name))continue;let i=$.join(n,r.name);try{e-D.statSync(i).mtimeMs>Hr&&D.rmSync(i,{recursive:!0,force:!0})}catch{}}},Gr=256*1024*1024,Vr="INFRA_KIT_DEV_LOG_MAX_BYTES",Kr=()=>{let n=Number(x.env[Vr]);return Number.isInteger(n)&&n>0?n:Gr},q=class{dir;fds=new Map;counters=new Map;bytes=new Map;capped=new Set;maxBytes;closed=!1;constructor(e=Mr(),t=Kr()){this.dir=e,this.maxBytes=t,D.mkdirSync(this.dir,{recursive:!0}),Wr($.dirname(this.dir),Date.now()),this.linkLatest()}linkLatest(){let e=$.join($.dirname(this.dir),"latest");try{D.rmSync(e,{force:!0}),D.symlinkSync(this.dir,e,"dir")}catch{}}pathFor(e){return $.join(this.dir,At(e))}fdFor(e){let t=this.fds.get(e);if(t!=null)return t;try{let r=D.openSync(this.pathFor(e),"a");this.fds.set(e,r);try{this.bytes.set(e,D.fstatSync(r).size)}catch{this.bytes.set(e,0)}return r}catch{return null}}write(e,t,r={}){if(this.closed)return;let i=this.counters.get(e)??kt();if(r.level==="error"?(i.errors+=1,i.lastErrorAt=Date.now()):r.level==="warn"&&(i.warns+=1),this.counters.set(e,i),this.capped.has(e))return;let s=this.fdFor(e);if(s==null)return;let o=t.endsWith(`
14
+ `)}`};import{AsyncLocalStorage as Cr}from"node:async_hooks";var pe=new Cr,ft=(n,e)=>pe.run(n,e),mt=n=>{pe.enterWith(n)},K=()=>pe.getStore();import{Buffer as wt}from"node:buffer";import P from"node:fs";import $ from"node:path";import x from"node:process";import z from"node:process";var Ir=new Set(["EAGAIN","EINTR"]),J=!1,vt=()=>J,Nr=(n,e)=>n===z.stdout?"stdout":n===z.stderr||e===1?"stderr":"stdout",yt=({streams:n=[z.stdout,z.stderr],onDeath:e}={})=>{let t=!1,r=[];for(let[i,s]of n.entries()){let o=Nr(s,i),a=l=>{let u=l??new Error("stdio error");u.code!=null&&Ir.has(u.code)||(J=!0,!t&&(t=!0,e?.(o,u)))};s.on("error",a),r.push({stream:s,listener:a})}return{isDead:()=>J,uninstall:()=>{for(let{stream:i,listener:s}of r)i.removeListener("error",s);r.length=0,J=!1,t=!1}}};var Lr=()=>Object.getPrototypeOf(x.stdout).write,ce=n=>vt()?!0:Lr().call(x.stdout,n);var Pt=()=>new Proxy(x.stdout,{get(n,e,t){if(e==="write")return i=>ce(String(i));let r=Reflect.get(n,e,t);return typeof r=="function"?r.bind(n):r}}),bt=()=>({errors:0,warns:0,lastErrorAt:null}),kt=n=>{let e=n.split(/\W+/).filter(t=>t!=="").join("-");return e===""?null:e},_r=()=>{let n=x.env[Te]??"",e=kt(n)??"no-session";return $.join(Oe(),e,"dev",String(x.pid))},St=n=>`${kt(n)??"unnamed"}.log`,Ur=10080*60*1e3,Fr=n=>{let e=Number(n);if(!Number.isInteger(e)||e<=0)return!1;try{return x.kill(e,0),!0}catch(t){return t.code==="EPERM"}},Br=(n,e)=>{let t;try{t=P.readdirSync(n,{withFileTypes:!0})}catch{return}for(let r of t){if(!r.isDirectory()||Fr(r.name))continue;let i=$.join(n,r.name);try{e-P.statSync(i).mtimeMs>Ur&&P.rmSync(i,{recursive:!0,force:!0})}catch{}}},Mr=256*1024*1024,Hr="INFRA_KIT_DEV_LOG_MAX_BYTES",jr=()=>{let n=Number(x.env[Hr]);return Number.isInteger(n)&&n>0?n:Mr},q=class{dir;fds=new Map;counters=new Map;bytes=new Map;capped=new Set;maxBytes;closed=!1;constructor(e=_r(),t=jr()){this.dir=e,this.maxBytes=t,P.mkdirSync(this.dir,{recursive:!0}),Br($.dirname(this.dir),Date.now()),this.linkLatest()}linkLatest(){let e=$.join($.dirname(this.dir),"latest");try{P.rmSync(e,{force:!0}),P.symlinkSync(this.dir,e,"dir")}catch{}}pathFor(e){return $.join(this.dir,St(e))}fdFor(e){let t=this.fds.get(e);if(t!=null)return t;try{let r=P.openSync(this.pathFor(e),"a");this.fds.set(e,r);try{this.bytes.set(e,P.fstatSync(r).size)}catch{this.bytes.set(e,0)}return r}catch{return null}}write(e,t,r={}){if(this.closed)return;let i=this.counters.get(e)??bt();if(r.level==="error"?(i.errors+=1,i.lastErrorAt=Date.now()):r.level==="warn"&&(i.warns+=1),this.counters.set(e,i),this.capped.has(e))return;let s=this.fdFor(e);if(s==null)return;let o=t.endsWith(`
15
15
  `)?t:`${t}
16
- `,a=Dt.byteLength(o);if((this.bytes.get(e)??0)+a>this.maxBytes){this.capped.add(e),this.writeRaw(s,e,`[capped] ${At(e)} hit ${this.maxBytes} bytes; further lines dropped (likely a logging loop)
17
- `);return}this.writeRaw(s,e,o)}writeRaw(e,t,r){try{D.writeSync(e,r),this.bytes.set(t,(this.bytes.get(t)??0)+Dt.byteLength(r))}catch{}}statsFor(e){return this.counters.get(e)??kt()}close(){if(!this.closed){this.closed=!0;for(let e of this.fds.values())try{D.closeSync(e)}catch{}this.fds.clear()}}};import*as Ct from"node:fs";import Jr from"node:module";import*as C from"node:path";import zr from"node:process";import{fileURLToPath as Ot}from"node:url";var qr="ikgen",ge=2e9,xt=null,Tt=!1,$t=new Map,me=n=>{let e=$t.get(n);if(e!==void 0)return e;try{let t=Ct.realpathSync(n);return $t.set(n,t),t}catch{return null}},fe=(n,e)=>n===e||n.startsWith(e+C.sep),Yr=(n,e)=>{if(!n.startsWith("file:"))return null;let t;try{t=Ot(n)}catch{return null}let r=me(t);if(r===null||!fe(r,e.root)||r.includes(`${C.sep}node_modules${C.sep}`)||fe(r,e.selfDist))return null;e.busted.add(r);let i=new URL(n);return i.searchParams.set(qr,String(e.generation)),i.href},It=()=>Jr.registerHooks,Nt=()=>typeof It()=="function",ve=n=>{if(n==="0"||n?.toLowerCase()==="off")return{stopEnabled:!1,stopBytes:ge};let e=Number(n);return Number.isInteger(e)&&e>0?{stopEnabled:!0,stopBytes:e}:{stopEnabled:!0,stopBytes:ge}},Lt=()=>{let n=C.dirname(Ot(import.meta.url));return me(n)??n},_t=n=>{let e=It();if(typeof e!="function")return null;let t={busted:new Set,generation:0,root:n.root,selfDist:n.selfDist},r=n.stopBytes??ge,i=n.stopEnabled??!0,s=r/2,o=n.warnStrideBytes??r/8,a=n.sampleRss??(()=>zr.memoryUsage.rss()),l=0,u=0;return xt=t,Tt||(Tt=!0,e({resolve:(d,h,f)=>{let p=f(d,h),g=xt;if(g===null)return p;let m=Yr(p.url,g);return m===null?p:{...p,url:m}}})),{bump:()=>(t.generation+=1,t.busted=new Set,t.generation),budgetState:()=>{let d=a();if(i&&d>r){if(l+=1,l>=2)return"stop"}else l=0;return d>=s&&d>=u+o?(u=d,"warn"):"ok"},bustedUnder:d=>{let h=me(d)??d;for(let f of t.busted)if(fe(f,h))return!0;return!1},generation:()=>t.generation}};import ye from"node:process";import Xr from"node:util";var we={log:"info",info:"info",debug:"debug",trace:"debug",warn:"warn",error:"error"},Ut=({sink:n,fallbackService:e,currentService:t})=>{let r=!0,i=()=>t()??e,s=(d,h)=>{n.write(i(),d,{level:h})},o={};for(let d of Object.keys(we))o[d]=console[d],console[d]=(...h)=>{if(!r){o[d](...h);return}s(Xr.format(...h),we[d])};let a=(d,h)=>{let f=d.write.bind(d),p="",g=()=>{p!==""&&(n.write(i(),p,{level:h}),p="")};return d.write=((m,...P)=>{if(!r||typeof m!="string")return f(m,...P);p+=m;let c=p.split(`
18
- `);p=c.pop()??"";for(let E of c)E!==""&&n.write(i(),E,{level:h});p.length>8192&&g();let S=P.find(E=>typeof E=="function");return typeof S=="function"&&ye.nextTick(S),!0}),()=>{g(),Reflect.deleteProperty(d,"write")}},l=a(ye.stdout,"info"),u=a(ye.stderr,"warn");return{uninstall:()=>{if(r){r=!1,l(),u();for(let d of Object.keys(we))console[d]=o[d]}}}};var Qr="/api/v1";function Ft(n){if(n==null||n==="")return;let e=parseInt(n.trim().replace(/^["']|["']$/g,""),10);return Number.isNaN(e)?void 0:e}function Bt(n,e,t,r=!0){let s=`${n.replace(/-/g,"_").toUpperCase()}_PORT`,o=Ft(e[s]);if(o!=null)return o;if(r){let a=Ft(e.PORT);if(a!=null)return a}return t[n]?.port??void 0}function Mt(n,e){return e[n]?.prefixUrl??Qr}function Ht(n){let e=n.map(i=>i.port),t=e.filter((i,s)=>e.indexOf(i)!==s),r=n.filter(i=>t.includes(i.port));return{duplicatePorts:t,conflictingApps:r}}import*as Y from"node:fs";import*as R from"node:path";import{parse as Zr}from"yaml";var jt=new Map;var en=(n,e)=>{let t=new Set,r;try{r=Zr(Y.readFileSync(n,"utf8"))}catch{return t}for(let i of Object.values(r?.functions??{})){let s=i?.handler;if(typeof s!="string")continue;let o=s.split(".")[0]??"";o!==""&&t.add(R.normalize(R.join(e,`${o}.js`)))}return t},tn=n=>{let e=R.join(n,"serverless.yml"),t;try{t=Y.statSync(e).mtimeMs}catch{return new Set}let r=jt.get(n);if(r&&r.mtimeMs===t)return r.entries;let i=en(e,n);return jt.set(n,{entries:i,mtimeMs:t}),i},Wt=(n,e)=>tn(e).has(R.normalize(n)),rn=2,Gt=n=>{let e=n.slice(0,rn).map(r=>R.basename(r)),t=n.length-e.length;return t>0?`${e.join(", ")} (+${t} more)`:e.join(", ")};import{Logger as nn}from"@aws-lambda-powertools/logger";import sn from"fastify";import*as Kt from"node:fs";import*as F from"node:path";import be from"node:process";import{pathToFileURL as on}from"node:url";import{parse as an}from"yaml";var ln=n=>n?.code==="EADDRINUSE",un=3e4,dn=()=>{let n=Number.parseInt(be.env.DEV_SERVER_TIMEOUT_MS??"",10);return Number.isNaN(n)?un:n},Vt=()=>be.env.DEV_SERVER_REQUEST_LOG==="1",X=class{constructor(e){this.serverConfig=e;this.importCacheBust=`${Date.now()}`,this.logger=new nn({serviceName:"LocalServer",logLevel:"DEBUG"}),this.serverConfig.prefixUrl=this.serverConfig.prefixUrl??"",this.server=sn({logger:!1}),this.server.addHook("onRequest",async(r,i)=>{let s=this.serverConfig.serviceTag;s!=null&&bt(s),i.header("Access-Control-Allow-Origin","*"),i.header("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, PATCH, OPTIONS"),i.header("Access-Control-Allow-Headers","Content-Type, Authorization, X-Requested-With"),r.method==="OPTIONS"&&i.status(204).send()});let t=this.serverConfig.onRequestLog;(Vt()||t)&&this.server.addHook("onResponse",async(r,i)=>{let s=Math.round(i.elapsedTime),o=r.url.split("?")[0]??r.url;t?.({method:r.method,path:o,status:i.statusCode,ms:s}),Vt()&&be.stdout.write(`${r.method} ${r.url} \u2192 ${i.statusCode} ${s}ms
19
- `)})}serverConfig;importCacheBust;logger;server;controllers={};registeredRouteKeys=new Set;getRegisteredRoutes(){return[...this.registeredRouteKeys].sort()}async start(){this.registerHealthRoute(),await Promise.all(this.loadRoutes());let e=await this.listenWithFallback();return this.serverConfig.port=e,this.logger.info(`Server listening on http://127.0.0.1:${e}`,{address:`http://127.0.0.1:${e}`}),e}async listenWithFallback(){let e=this.serverConfig.port;if(e!=null)try{return await this.server.listen({port:e,host:"127.0.0.1"}),this.readBoundPort()}catch(t){if(!ln(t))throw t}return await this.server.listen({port:0,host:"127.0.0.1"}),this.readBoundPort()}readBoundPort(){let e=this.server.server.address();if(e==null||typeof e=="string")throw new Error("Server address unavailable after listen()");return e.port}async close(){let e=this.server.server;typeof e.closeAllConnections=="function"&&e.closeAllConnections(),await this.server.close()}registerHealthRoute(){this.server.route({method:"GET",url:"/__health",handler:(e,t)=>t.code(200).send({status:"ok",app:this.serverConfig.appName??null,port:this.serverConfig.port})})}loadRoutes(){let e=F.join(this.serverConfig.controllersPath,"serverless.yml"),t=Kt.readFileSync(e,"utf8"),r=an(t),i=[];if(!r?.functions)return i;for(let s of Object.values(r.functions))if(s?.events?.length)for(let o of s.events){let a=o?.http;a&&i.push(this.defineRoute(a,s))}return i}async defineRoute(e,t){let r=e.path.toString();r=r.replaceAll("{",":").replaceAll("}","");let i=F.posix.join(this.serverConfig.prefixUrl??"",r);i=i[0]==="/"?i:`/${i}`;let s=["GET","POST","PUT","DELETE","PATCH","HEAD","OPTIONS"],o=String(e.method).toUpperCase();if(!s.includes(o))throw new Error(`Invalid HTTP method: "${e.method}" for URL: ${i}`);let a=`${o} ${i}`;if(this.registeredRouteKeys.has(a))throw new Error(`Duplicate route: ${a}`);this.registeredRouteKeys.add(a);let u=(t.handler??"").split("."),d=u[0]??"",h=u[1]??"",f=F.join(this.serverConfig.controllersPath,`${d}.js`),p=on(f);p.searchParams.set("v",this.importCacheBust);let g=this.serverConfig.serviceTag,m=async()=>await import(p.href),P=g==null?await m():await wt(g,m);this.controllers[a]={action:P,handler:h};let c=this.logger.createChild({serviceName:"RequestLogger"});this.server.route({method:o,url:i,handler:async(S,E)=>{let B=this.controllers[a];if(!B)throw new Error(`No controller for ${a}`);let Te=B.action[B.handler];if(!Te)throw new Error(`No handler ${B.handler} for ${i}`);let re=await Te(this.getEventObj(S.body,S.query,S.params,S.headers,o,i),this.getContext(),c),hr=JSON.parse(re.body);return E.headers(re.headers??{}),E.code(re?.statusCode??500).send(hr)}})}getEventObj(e,t,r,i,s="",o=""){let a={body:e?JSON.stringify(e):null,headers:i??{},multiValueHeaders:{},httpMethod:s,isBase64Encoded:!1,path:o,pathParameters:r??null,queryStringParameters:t??null,multiValueQueryStringParameters:null,stageVariables:null,requestContext:{accountId:"",apiId:"",authorizer:void 0,protocol:"",httpMethod:s,identity:{accessKey:null,accountId:null,apiKey:null,apiKeyId:null,caller:null,clientCert:null,cognitoAuthenticationProvider:null,cognitoAuthenticationType:null,cognitoIdentityId:null,cognitoIdentityPoolId:null,principalOrgId:null,sourceIp:"devIp",user:null,userAgent:null,userArn:null},path:o,stage:"",requestId:"",requestTimeEpoch:0,resourceId:"",resourcePath:o},resource:o};return a.source="aws.events",a}getContext(){let e=Date.now(),t=dn(),r=new Date().toISOString().split("T")[0]??"";return{callbackWaitsForEmptyEventLoop:!1,functionName:"local-dev",functionVersion:"1.0.0",invokedFunctionArn:"arn:aws:lambda:local:000000000000:function:local-dev",memoryLimitInMB:"1024",awsRequestId:`local-${Date.now()}`,logGroupName:"/aws/lambda/local-dev",logStreamName:`${r}/local`,getRemainingTimeInMillis:()=>Math.max(0,t-(Date.now()-e)),done:(i,s)=>{},fail:i=>{},succeed:i=>{}}}};import{spawn as pn}from"node:child_process";import*as Q from"node:fs";var cn=(n,e)=>[...n.map(t=>`--filter=...${t}`),...e.map(t=>`--filter=${t}^...`)],Jt=({depInclusive:n,depClosure:e,cwd:t,logFile:r,onUnexpectedExit:i,openLog:s})=>{let o=cn(n,e),a=s?s(r):Q.openSync(r,"a"),l=pn("pnpm",["exec","turbo","watch","build",...o,"--continue=dependencies-successful","--env-mode=loose","--log-order=stream"],{cwd:t,detached:!0,stdio:["ignore",a,a]});return Q.closeSync(a),j(l,void 0,i)};import{spawn as hn}from"node:child_process";import gn from"node:process";var fn=[/^error\b/i,/^[✘✖×]/,/^\[vite\][^\n]*\berror\b/i,/^\w*Error: /,/\berror TS\d+\b/,/^Failed to (?:resolve|load url|parse|compile)\b/i,/^(?:ENOENT|EADDRINUSE|ECONNREFUSED)\b/,/pre-transform error/i,/\b(?:build|transform|compilation) failed\b/i],mn=n=>fn.some(e=>e.test(n))?"error":"info",vn=/\u001B\][\s\S]*?(?:\u0007|\u001B\\)/g,yn=/\u001B\[[0-9;?]*[\u0020-\u002F]*[\u0040-\u007E]/g,wn=/[\u0000-\u0008\u000B-\u001F\u007F]/g,qt=n=>n.replace(vn,"").replace(yn,"").replace(wn,""),bn=/^([^\s:]+):dev:[ \t]?/,Sn=/^([^\s#]+)#dev:\s+(?:WARNING|ERROR)\s+command\b/,Pn=n=>Sn.exec(qt(n).trimEnd())?.[1]??null,Dn=n=>/^cache (?:bypass|hit|miss)/.test(n)||n.startsWith("$ "),kn=n=>{let e=qt(n).trimEnd(),t=bn.exec(e);if(t==null)return null;let r=e.slice(t[0].length);return r.trim()===""||Dn(r.trim())?null:{pkg:t[1],text:r,level:mn(r)}},An=64*1024,zt=(n,e)=>{if(n==null)return;let t="",r=i=>{let s=Pn(i);if(s!=null){e.onTaskFailure?.(s);return}let o=kn(i);o!=null&&e.onLine?.(o)};n.setEncoding("utf-8"),n.on("data",i=>{e.appendLog?.(i),t+=i;let s=t.split(`
20
- `);t=s.pop()??"";for(let o of s)r(o);t.length>An&&(r(t),t="")}),n.on("end",()=>{t!==""&&(r(t),t="")}),n.on("error",i=>{e.appendLog?.(`[infra-kit] ui dev stream error: ${i.message}
21
- `)})},Yt=({packageNames:n,cwd:e,concurrency:t,env:r,appendLog:i,onLine:s,onTaskFailure:o,onUnexpectedExit:a})=>{let l=n.map(d=>`--filter=${d}`),u=hn("pnpm",["exec","turbo","run","dev",...l,`--concurrency=${t}`,"--env-mode=loose","--only","--continue=dependencies-successful","--output-logs=new-only","--log-order=stream","--no-update-notifier","--ui=stream"],{cwd:e,detached:!0,stdio:["ignore","pipe","pipe"],env:{...gn.env,...r}});return zt(u.stdout,{appendLog:i,onLine:s,onTaskFailure:o}),zt(u.stderr,{appendLog:i,onLine:s,onTaskFailure:o}),j(u,void 0,a)};var ee="runner",On="turbo",In="watch",De=n=>`${Math.round(n/1e6)} MB`;function Z(n){let e=rr.homedir();return n===e||n.startsWith(`${e}${y.sep}`)?`~${n.slice(e.length)}`:n}function er(n){return n.map(e=>e.packageName).join(", ")}var Nn=Cn.promisify(Tn);var Ln=async(n,e)=>{try{let{stderr:t}=await Nn(n,{maxBuffer:33554432});t&&e&&e(` (build) ${t.trim()}`,"debug"),t&&!e&&console.error("stderr:",t)}catch(t){let r=t;throw e&&(r.stdout||r.stderr)&&(r.stdout&&e(` stdout: ${r.stdout.trim()}`,"error"),r.stderr&&e(` stderr: ${r.stderr.trim()}`,"error")),t}},te=null;function nr(n){te?.write(ee,n)}var _n=()=>new Promise((n,e)=>{let t=$n.createServer();t.unref(),t.on("error",e),t.listen(0,"127.0.0.1",()=>{let r=t.address(),i=typeof r=="object"&&r!==null?r.port:0;t.close(()=>n(i))})}),tr="dev aliases are now scoped by repo (`<release>.<package>.<repo>.localhost`), and only that helper aims Playwright at them. An older one would test whatever another repo serves on the old repo-less host.",Un="dev URLs are now HTTPS, and only that `infraKitDev` helper understands them. An older one would proxy plain HTTP at a TLS listener, silently.",Fn=[{name:"@slip-stream-kit/vite",floor:"0.15.0",why:tr},{name:"@slip-stream-kit/config",floor:"0.15.0",why:tr},{name:"infra-kit",floor:"0.1.132",why:Un}],Bn=(n,e)=>{let t=s=>s.split(".").map(o=>Number.parseInt(o,10)||0),r=t(n),i=t(e);for(let s=0;s<Math.max(r.length,i.length);s+=1){let o=r[s]??0,a=i[s]??0;if(o!==a)return o<a}return!1},Mn=n=>{let e=[n];try{for(let t of w.readdirSync(y.join(n,"apps"),{withFileTypes:!0}))if(t.isDirectory())for(let r of["api","ui"])e.push(y.join(n,"apps",t.name,r))}catch{}return e},Hn=(n,e)=>{try{let t=JSON.parse(w.readFileSync(y.join(n,"package.json"),"utf-8"));return["dependencies","devDependencies"].some(r=>t[r]?.[e]!=null)}catch{return!1}},jn=(n,e,t)=>{let r=t.split("/"),i=e;for(;;){let s=y.join(i,"node_modules",...r);if(w.existsSync(s))return s;let o=y.dirname(i);if(i===n||o===i)return;i=o}},Wn=(n,e)=>{try{let t=w.realpathSync(e),r=w.realpathSync(n);return t.startsWith(r+y.sep)&&!t.includes(`${y.sep}node_modules${y.sep}`)}catch{return!1}},Gn=n=>{try{return w.realpathSync(n)}catch{return n}},Vn=(n,e,t)=>{let{name:r,floor:i,why:s}=e;if(Wn(n,t))return;let o;try{let a=JSON.parse(w.readFileSync(y.join(t,"package.json"),"utf-8"));if(typeof a.version!="string")throw new Error("no version field");o=a.version}catch{throw new Error(`infra-kit dev: could not read the version of the pinned \`${r}\` helper (${t}). Refusing to start rather than risk proxying plain HTTP at a TLS listener. Run \`pnpm install\`.`)}if(Bn(o,i))throw new Error(`infra-kit dev: this repo pins ${r} ${o}; bump ${r} to >=${i}. From ${i} on, ${s}
22
- pnpm add -D ${r}@^${i}`)},Kn=n=>{let e=Mn(n);for(let t of Fn){let{name:r}=t,i=e.filter(o=>Hn(o,r)),s=new Map;for(let o of[...i,n]){let a=jn(n,o,r);a&&s.set(Gn(a),a)}if(s.size===0){if(i.length===0)continue;throw new Error(`infra-kit dev: ${i[0]}/package.json depends on ${r} but it does not resolve from there, so the \`infraKitDev\` helper version cannot be verified. Run \`pnpm install\`.`)}for(let o of s.values())Vn(n,t,o)}},Jn=n=>{let t=(n instanceof Error?n.message:String(n)).split(`
23
- `)[0]?.trim()??"";return t===""?"unknown error (see the log)":t},ke=class n{monorepoRoot;devContextDir;appServers=[];reqTimes=new Map;lastSummary=null;readyAt=0;watchDebounceTimers=new Map;pendingChanges=new Map;moduleGeneration=null;watcher=null;static WATCH_DEBOUNCE_MS=400;watchedDistDirs=[];nudgedStaleness=new Map;static STALE_BUILD_GRACE_MS=1e3;restartWorkChain=Promise.resolve();static PORT_RELEASE_DELAY_MS=200;livenessTimer=null;reusing=!1;keepAlive=null;servingAnnounced=!1;targetLabel="";health=new Map;firstErrorLogged=new Set;uiTagByPackage=new Map;static LIVENESS_INTERVAL_MS=5e3;static LIVENESS_FAILURE_THRESHOLD=2;static NEVER_UP_DOWN_THRESHOLD=6;options;runBuild;turboWatchFactory;turboWatch=null;uiDevFactory;uiDev=null;sink;intercept;dryRunner;failedApps=[];degradedRoutes=[];heldLocalPkgs=new Set;launchedUis=[];proxyRoutes=[];renderer;healthProbe;proxy;proxyPort=Fe;registeredAliases=new Set;uiPortMap={};shuttingDown=!1;closureMap=null;closureBuild=Promise.resolve();teardown=null;stage="idle";constructor(e={},t=Ln,r=Jt,i=Yt,s,o,a=nt,l=Ne(),u){this.options=e,this.runBuild=t,this.turboWatchFactory=r,this.uiDevFactory=i,this.dryRunner=s,this.proxy=l,this.sink=u??new q,te=this.sink;let d=ir(this.options);this.intercept=d?Ut({sink:this.sink,fallbackService:ee,currentService:K}):null,this.renderer=o??new st({appendLog:nr,verbose:this.options.verbose??!1,isTTY:d}),this.healthProbe=a,this.devContextDir=y.join(v.cwd(),".infra-kit","dev-context"),this.monorepoRoot=O(v.cwd()),(v.env.DOPPLER_PROJECT!=null||v.env.DOPPLER_ENVIRONMENT!=null)&&this.renderer.log("\u{1F510} Doppler env detected (DOPPLER_PROJECT / DOPPLER_ENVIRONMENT)","debug")}discoverApiApps(e){return M(this.monorepoRoot).map(t=>({...t,preferredPort:this.resolvePreferredPort(t.name,e),appScopedPort:this.resolvePreferredPort(t.name,e,!1),prefixUrl:this.resolvePrefixUrl(t.name,e),watchDeps:!0}))}async loadDevConfig(){try{return(await oe()).dev??{}}catch{return{}}}normalizeAppInclude(){return H(this.options.include)}resolvePreferredPort(e,t,r=!0){return Bt(e,v.env,t,r)}resolvePrefixUrl(e,t){return Mt(e,t)}async loadDevPresets(){try{return(await oe()).devServersPresets??{}}catch{return{}}}async resolvePresetDef(e,t,r){if(this.options.presetDef!=null)return this.options.presetDef;let i=this.options.preset;if(i==null)return{};let s=e[i];if(s)return s;let o=await at(i,t,r);if(o)return o.unprovided.length>0&&this.renderer.log(`\u26A0\uFE0F No apps/*/api provides these local-capable routes, so they stay on cloud: ${o.unprovided.join(", ")}`,"warn"),o.preset;let[a,...l]=[...new Set([...Object.keys(e),...[...t,...r].map(u=>u.name).toSorted()])];throw a===void 0?new se(void 0,{operation:`run "${i}"`,remediation:"omit the name to run every app",stderrExcerpt:"no devServersPresets are defined and no apps were discovered"}):new Ye({status:"argument_required",argument:"preset",choices:Pe.toJSONSchema(Pe.object({preset:Pe.enum([a,...l])})),agentMode:_e.source},2,{operation:"pick what infra-kit dev runs",remediation:`pass a preset or an app: ${[a,...l].join(", ")}`,stderrExcerpt:`"${i}" is neither a dev preset nor an app`})}assertPresetKeysValid(e){let t=this.options.preset??"dev",r=Ve({[t]:e});if(r.length!==0)throw new Error(`infra-kit dev: cannot run this preset \u2014 ${r.map(i=>i.message).join("; ")}`)}async assertPresetProxyLocalityValid(e,t,r,i){let s={[e]:t},{ctx:o,loadErrors:a}=await qe(this.monorepoRoot,s,r,i);if(a.length>0)return;let l=ze(s,o);if(l.length!==0)throw new Error(`infra-kit dev: cannot run this preset \u2014 ${l.map(u=>u.message).join("; ")}`)}async start(){let e=Date.now(),t=this.normalizeAppInclude(),r=this.options.watch??!1,i=await this.loadDevConfig();v.env.POWERTOOLS_DEV??="true",v.env.LOG_LEVEL??="DEBUG",this.renderer.narrate("\u{1F680} Starting Development Server Runner"),r&&this.renderer.narrate("\u{1F440} Watch mode: will rebuild and restart on file save");let{apps:s,uiApps:o,apiAppsAll:a,uiAppsAll:l,wantedLocalPkgs:u,presetProxy:d}=await this.resolveRunPlan(i,t);if(await this.endsBeforeBoot(s,o))return;this.warnUiMissingVitePlugin(o),r&&this.installModuleGeneration(),await this.bringUpProxy(s),await this.buildAll(s,o,r),s.length>0&&(this.renderer.bootStep("starting servers"),await this.startAllApps(s),this.renderer.narrate(this.failedApps.length===0?"\u{1F389} All servers started!":`\u26A0\uFE0F ${this.appServers.length}/${s.length} servers started \u2014 ${this.failedApps.length} failed`),this.renderer.narrate(`\u{1F4DD} Logs \u2192 ${Z(this.sink.dir)} (one file per service)`),this.assertNoBootFailuresWhenReusing(s.length));let h=Je({preset:this.options.preset,running:[...s.map(f=>`${f.name}/api`),...o.map(f=>`${f.name}/ui`)],discovered:[...a.map(f=>`${f.name}/api`),...l.map(f=>`${f.name}/ui`)]});if(this.targetLabel=h,r&&this.holdFailedBackendsLocal(),this.degradedRoutes=await this.collectDegradedRoutes(o,u,d),this.degradedRoutes.length>0&&!r)throw new Error(yt(this.degradedRoutes,h));if(await this.printReady(s,o,e,h),this.announceServingOnce(),this.options.routes&&this.printRouteDump(),this.appServers.length===0&&o.length===0&&this.failedApps.length>0)throw new Error(`infra-kit dev: no app started (${this.failedApps.length} of ${s.length} failed). First failure: ${this.failedApps[0]?.reason??"unknown"}`);this.armWatch(s,o,r),o.length>0&&this.startUiDev(o),this.startLivenessMonitor()}async resolveRunPlan(e,t){let r=this.discoverApiApps(e),i=Me(this.monorepoRoot),s=await this.loadDevPresets(),o=await this.resolvePresetDef(s,r,i);this.assertPresetKeysValid(o);let a={api:r.map(c=>c.name),ui:i.map(c=>c.name)};this.options.presetDef==null&&this.options.preset!=null&&await this.assertPresetProxyLocalityValid(this.options.preset,o,a,r);let l=Ke(o,a);l.unmatched.length>0&&this.renderer.log(`\u26A0\uFE0F Preset targets not found (skipped): ${l.unmatched.join(", ")}`,"warn");let u=new Set(l.targets.filter(c=>c.part==="api").map(c=>c.app)),d=new Set(l.targets.filter(c=>c.part==="ui").map(c=>c.app)),h=c=>!t||t.includes(c),f=new Map(l.targets.filter(c=>c.part==="api").map(c=>[c.app,c.watchDeps])),p=r.filter(c=>u.has(c.name)&&h(c.name)),g=p.map(c=>({...c,watchDeps:f.get(c.name)??c.watchDeps,preferredPort:p.length===1?c.preferredPort:c.appScopedPort})),m=i.filter(c=>d.has(c.name)&&h(c.name)),P=new Set(r.filter(c=>u.has(c.name)).map(c=>c.packageName));return{apps:g,uiApps:m,apiAppsAll:r,uiAppsAll:i,wantedLocalPkgs:P,presetProxy:l.proxy}}async collectDegradedRoutes(e,t,r){if(e.length===0)return[];let i=[];for(let s of e){let o=await Qt(s.path).catch(()=>{}),a=o?.dev;if(!a?.proxy)continue;let l=r[s.name]??{},u=Object.fromEntries(Object.entries(a.proxy.routes).map(([d,h])=>[d,{...h,pinnedLocal:l[d]==="local"}]));i.push({app:s.name,routes:u,cloudUrl:Zt(o?.deployedUrlEnv).url})}return this.launchedUis=i,ft({uis:i,wanted:t,running:new Set(this.appServers.map(({app:s})=>s.packageName)),reasons:new Map(this.failedApps.map(({app:s,reason:o})=>[s.packageName,{app:s.name,reason:o}])),held:this.heldLocalPkgs})}warnUiMissingVitePlugin(e){for(let t of e)!t.managedPort||Be(t.path)||this.renderer.log(`\u26A0\uFE0F ${t.name}/ui is wired with the infra-kit vite helper directly, not the infraKit() plugin \u2014 its proxy is baked at config load and won't react to backend routing changes. Wire the infraKit() vite plugin (from @slip-stream-kit/vite) instead.`,"warn")}proxiesFor(e){let t=this.proxyRoutes.filter(r=>r.uiApp===e).map(r=>({route:r.route,source:r.source,target:r.target}));return t.length>0?t:void 0}async bringUpProxy(e){Kn(O(v.cwd())),await this.ensureProxy(),e.length>0&&this.assertNoPortConflicts(e)}async buildAll(e,t,r){let i=[e.length>0?`building ${er(e)}`:"",t.length>0?`warming ${er(t)}`:""].filter(Boolean).join(" \xB7 ");this.renderer.bootStep(i),e.length>0&&await this.buildApps(e,r),t.length>0&&await this.buildUiApps(t)}armWatch(e,t,r){r&&(this.appServers.length>0||t.length>0)&&(this.setupWatch(e,t),this.closureBuild=this.buildClosureMapSafe(e).then(i=>{this.closureMap=i}))}async buildUiApps(e){let t=e.map(r=>`--filter=${r.packageName}^...`).join(" ");try{await this.runBuild(`pnpm exec turbo run build ${t} --env-mode=loose --output-logs=errors-only --no-update-notifier`,this.renderer.logFn),this.renderer.narrate("\u2705 UI deps built")}catch(r){this.renderer.log(`\u26A0\uFE0F UI dep build failed (continuing; vite will report unresolved deps): ${String(r)}`,"warn")}}startUiDev(e){let t=e.map(o=>o.name).join(", ");this.renderer.narrate(`\u{1F3A8} Starting ${e.length} UI dev server(s) via \`turbo run dev\`: ${t}`),this.renderer.narrate(" (framework output is routed into the live tail; full detail in the runner log)");let r=Object.keys(this.uiPortMap).length>0?{INFRA_KIT_UI_PORTS:JSON.stringify(this.uiPortMap)}:void 0,i=v.env.NODE_EXTRA_CA_CERTS==null&&w.existsSync(ie())?{NODE_EXTRA_CA_CERTS:ie()}:void 0,s=new Map(e.map(o=>[o.packageName,`${o.name}/ui`]));this.uiDev=this.uiDevFactory({packageNames:e.map(o=>o.packageName),cwd:v.cwd(),concurrency:Math.max(e.length+4,12),env:r||i?{...r,...i}:void 0,appendLog:o=>{this.sink.write(On,o)},onLine:({pkg:o,text:a,level:l})=>{let u=s.get(o)??`${o}/ui`;this.sink.write(u,a,{level:l})},onTaskFailure:o=>{this.markUiTaskFailed(s.get(o)??`${o}/ui`)},onUnexpectedExit:o=>{this.markUiEngineDead(),this.reportEngineDeath("UI dev engine (`turbo run dev`)","frontends stopped reloading",o)}})}formatAppList(e){return e.map(t=>`${t.name}:${t.port}`).join(", ")}assertNoPortConflicts(e){let t=e.filter(s=>s.preferredPort!=null).map(s=>({name:s.name,port:s.preferredPort})),{duplicatePorts:r,conflictingApps:i}=Ht(t);if(r.length!==0)throw this.renderer.log(`\u26A0\uFE0F Port conflict detected! ${r.join(", ")}`,"error"),this.renderer.log(`Conflicting apps: ${this.formatAppList(i)}`,"error"),this.renderer.log("\n\u{1F4A1} Tip: give each app a distinct port via `{APP}_PORT` env (e.g. `CLIENT_PORT=`,","error"),this.renderer.log(" `SEARCH_ENGINE_PORT=`) or `dev.<app>.port` in infra-kit.json; or run a subset with","error"),this.renderer.log(" `--app=<name>,<name>`.\n","error"),new Error(`Port conflict detected: ${r.join(", ")}`)}async buildApps(e,t){let i=`pnpm exec turbo run build ${e.map(s=>`--filter=${s.packageName}`).join(" ")} --env-mode=loose --output-logs=errors-only --no-update-notifier${t?" --force":""}`;try{await this.runBuild(i,this.renderer.logFn),this.renderer.narrate("\u2705 Build complete")}catch(s){this.renderer.log(`\u274C Build failed: ${String(s)}`,"error"),s instanceof Error&&s.message&&this.renderer.log(` ${s.message}`,"error");let o=s;throw o.stdout&&this.renderer.log(` stdout: ${o.stdout.trim()}`,"error"),o.stderr&&this.renderer.log(` stderr: ${o.stderr.trim()}`,"error"),s}}async startAllApps(e){await Promise.all(e.map(async t=>{try{let r=await this.startOneApp(t);r&&this.appServers.push({app:t,...r,startedAt:Date.now(),restarts:0})}catch(r){this.renderer.log(`\u274C Failed to start ${t.name}: ${String(r)}`,"error"),this.failedApps.push({app:t,reason:Jn(r)})}}))}async ensureProxy(){let e=this.proxy.binPath();if(e==null||!await this.proxy.isAvailable())throw new Error("infra-kit dev: portless is not installed, so no dev URL can resolve. It ships as a dependency of infra-kit \u2014 reinstall with `pnpm install`.");let t=Le(e,this.options.portlessLink??{home:rr.homedir()}),r=await this.proxy.probeProxy(this.proxyPort,!0);if(r!=="serving")throw r==="not-portless"?new Error(`infra-kit dev: something else is already listening on :${this.proxyPort}, and it is not portless (no \`X-Portless\` header on the wire). Find and stop it, e.g. \`sudo lsof -iTCP:${this.proxyPort} -sTCP:LISTEN\`. Installing or trusting portless will not help while another process holds the port.`):this.proxy.isServiceInstalled()==="no"?new Error(`infra-kit dev: no portless daemon is serving HTTPS on :${this.proxyPort}, and its OS service is not installed yet, so no dev URL can resolve. Install it once (this is the only step that needs root):
16
+ `,a=wt.byteLength(o);if((this.bytes.get(e)??0)+a>this.maxBytes){this.capped.add(e),this.writeRaw(s,e,`[capped] ${St(e)} hit ${this.maxBytes} bytes; further lines dropped (likely a logging loop)
17
+ `);return}this.writeRaw(s,e,o)}writeRaw(e,t,r){try{P.writeSync(e,r),this.bytes.set(t,(this.bytes.get(t)??0)+wt.byteLength(r))}catch{}}statsFor(e){return this.counters.get(e)??bt()}close(){if(!this.closed){this.closed=!0;for(let e of this.fds.values())try{P.closeSync(e)}catch{}this.fds.clear()}}};import*as Et from"node:fs";import Wr from"node:module";import*as O from"node:path";import Gr from"node:process";import{fileURLToPath as xt}from"node:url";var Vr="ikgen",he=2e9,At=null,Dt=!1,Rt=new Map,fe=n=>{let e=Rt.get(n);if(e!==void 0)return e;try{let t=Et.realpathSync(n);return Rt.set(n,t),t}catch{return null}},ge=(n,e)=>n===e||n.startsWith(e+O.sep),Kr=(n,e)=>{if(!n.startsWith("file:"))return null;let t;try{t=xt(n)}catch{return null}let r=fe(t);if(r===null||!ge(r,e.root)||r.includes(`${O.sep}node_modules${O.sep}`)||ge(r,e.selfDist))return null;e.busted.add(r);let i=new URL(n);return i.searchParams.set(Vr,String(e.generation)),i.href},Tt=()=>Wr.registerHooks,$t=()=>typeof Tt()=="function",me=n=>{if(n==="0"||n?.toLowerCase()==="off")return{stopEnabled:!1,stopBytes:he};let e=Number(n);return Number.isInteger(e)&&e>0?{stopEnabled:!0,stopBytes:e}:{stopEnabled:!0,stopBytes:he}},Ot=()=>{let n=O.dirname(xt(import.meta.url));return fe(n)??n},Ct=n=>{let e=Tt();if(typeof e!="function")return null;let t={busted:new Set,generation:0,root:n.root,selfDist:n.selfDist},r=n.stopBytes??he,i=n.stopEnabled??!0,s=r/2,o=n.warnStrideBytes??r/8,a=n.sampleRss??(()=>Gr.memoryUsage.rss()),l=0,u=0;return At=t,Dt||(Dt=!0,e({resolve:(d,h,y)=>{let c=y(d,h),p=At;if(p===null)return c;let v=Kr(c.url,p);return v===null?c:{...c,url:v}}})),{bump:()=>(t.generation+=1,t.busted=new Set,t.generation),budgetState:()=>{let d=a();if(i&&d>r){if(l+=1,l>=2)return"stop"}else l=0;return d>=s&&d>=u+o?(u=d,"warn"):"ok"},bustedUnder:d=>{let h=fe(d)??d;for(let y of t.busted)if(ge(y,h))return!0;return!1},generation:()=>t.generation}};import ve from"node:process";import Jr from"node:util";var ye={log:"info",info:"info",debug:"debug",trace:"debug",warn:"warn",error:"error"},It=({sink:n,fallbackService:e,currentService:t})=>{let r=!0,i=()=>t()??e,s=(d,h)=>{n.write(i(),d,{level:h})},o={};for(let d of Object.keys(ye))o[d]=console[d],console[d]=(...h)=>{if(!r){o[d](...h);return}s(Jr.format(...h),ye[d])};let a=(d,h)=>{let y=d.write.bind(d),c="",p=()=>{c!==""&&(n.write(i(),c,{level:h}),c="")};return d.write=((v,...A)=>{if(!r||typeof v!="string")return y(v,...A);c+=v;let S=c.split(`
18
+ `);c=S.pop()??"";for(let E of S)E!==""&&n.write(i(),E,{level:h});c.length>8192&&p();let b=A.find(E=>typeof E=="function");return typeof b=="function"&&ve.nextTick(b),!0}),()=>{p(),Reflect.deleteProperty(d,"write")}},l=a(ve.stdout,"info"),u=a(ve.stderr,"warn");return{uninstall:()=>{if(r){r=!1,l(),u();for(let d of Object.keys(ye))console[d]=o[d]}}}};var Lt="/api/v1";function Nt(n){if(n==null||n==="")return;let e=parseInt(n.trim().replace(/^["']|["']$/g,""),10);return Number.isNaN(e)?void 0:e}function _t(n,e,t=!0){let i=`${n.replace(/-/g,"_").toUpperCase()}_PORT`,s=Nt(e[i]);return s??(t?Nt(e.PORT):void 0)}function Ut(n){let e=n.map(i=>i.port),t=e.filter((i,s)=>e.indexOf(i)!==s),r=n.filter(i=>t.includes(i.port));return{duplicatePorts:t,conflictingApps:r}}import*as Y from"node:fs";import*as R from"node:path";import{parse as zr}from"yaml";var Ft=new Map;var qr=(n,e)=>{let t=new Set,r;try{r=zr(Y.readFileSync(n,"utf8"))}catch{return t}for(let i of Object.values(r?.functions??{})){let s=i?.handler;if(typeof s!="string")continue;let o=s.split(".")[0]??"";o!==""&&t.add(R.normalize(R.join(e,`${o}.js`)))}return t},Yr=n=>{let e=R.join(n,"serverless.yml"),t;try{t=Y.statSync(e).mtimeMs}catch{return new Set}let r=Ft.get(n);if(r&&r.mtimeMs===t)return r.entries;let i=qr(e,n);return Ft.set(n,{entries:i,mtimeMs:t}),i},Bt=(n,e)=>Yr(e).has(R.normalize(n)),Xr=2,Mt=n=>{let e=n.slice(0,Xr).map(r=>R.basename(r)),t=n.length-e.length;return t>0?`${e.join(", ")} (+${t} more)`:e.join(", ")};import{Logger as Qr}from"@aws-lambda-powertools/logger";import Zr from"fastify";import*as jt from"node:fs";import*as F from"node:path";import we from"node:process";import{pathToFileURL as en}from"node:url";import{parse as tn}from"yaml";var rn=n=>n?.code==="EADDRINUSE",nn=3e4,sn=()=>{let n=Number.parseInt(we.env.DEV_SERVER_TIMEOUT_MS??"",10);return Number.isNaN(n)?nn:n},Ht=()=>we.env.DEV_SERVER_REQUEST_LOG==="1",X=class{constructor(e){this.serverConfig=e;this.importCacheBust=`${Date.now()}`,this.logger=new Qr({serviceName:"LocalServer",logLevel:"DEBUG"}),this.serverConfig.prefixUrl=this.serverConfig.prefixUrl??"",this.server=Zr({logger:!1}),this.server.addHook("onRequest",async(r,i)=>{let s=this.serverConfig.serviceTag;s!=null&&mt(s),i.header("Access-Control-Allow-Origin","*"),i.header("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, PATCH, OPTIONS"),i.header("Access-Control-Allow-Headers","Content-Type, Authorization, X-Requested-With"),r.method==="OPTIONS"&&i.status(204).send()});let t=this.serverConfig.onRequestLog;(Ht()||t)&&this.server.addHook("onResponse",async(r,i)=>{let s=Math.round(i.elapsedTime),o=r.url.split("?")[0]??r.url;t?.({method:r.method,path:o,status:i.statusCode,ms:s}),Ht()&&we.stdout.write(`${r.method} ${r.url} \u2192 ${i.statusCode} ${s}ms
19
+ `)})}serverConfig;importCacheBust;logger;server;controllers={};registeredRouteKeys=new Set;getRegisteredRoutes(){return[...this.registeredRouteKeys].sort()}async start(){this.registerHealthRoute(),await Promise.all(this.loadRoutes());let e=await this.listenWithFallback();return this.serverConfig.port=e,this.logger.info(`Server listening on http://127.0.0.1:${e}`,{address:`http://127.0.0.1:${e}`}),e}async listenWithFallback(){let e=this.serverConfig.port;if(e!=null)try{return await this.server.listen({port:e,host:"127.0.0.1"}),this.readBoundPort()}catch(t){if(!rn(t))throw t}return await this.server.listen({port:0,host:"127.0.0.1"}),this.readBoundPort()}readBoundPort(){let e=this.server.server.address();if(e==null||typeof e=="string")throw new Error("Server address unavailable after listen()");return e.port}async close(){let e=this.server.server;typeof e.closeAllConnections=="function"&&e.closeAllConnections(),await this.server.close()}registerHealthRoute(){this.server.route({method:"GET",url:"/__health",handler:(e,t)=>t.code(200).send({status:"ok",app:this.serverConfig.appName??null,port:this.serverConfig.port})})}loadRoutes(){let e=F.join(this.serverConfig.controllersPath,"serverless.yml"),t=jt.readFileSync(e,"utf8"),r=tn(t),i=[];if(!r?.functions)return i;for(let s of Object.values(r.functions))if(s?.events?.length)for(let o of s.events){let a=o?.http;a&&i.push(this.defineRoute(a,s))}return i}async defineRoute(e,t){let r=e.path.toString();r=r.replaceAll("{",":").replaceAll("}","");let i=F.posix.join(this.serverConfig.prefixUrl??"",r);i=i[0]==="/"?i:`/${i}`;let s=["GET","POST","PUT","DELETE","PATCH","HEAD","OPTIONS"],o=String(e.method).toUpperCase();if(!s.includes(o))throw new Error(`Invalid HTTP method: "${e.method}" for URL: ${i}`);let a=`${o} ${i}`;if(this.registeredRouteKeys.has(a))throw new Error(`Duplicate route: ${a}`);this.registeredRouteKeys.add(a);let u=(t.handler??"").split("."),d=u[0]??"",h=u[1]??"",y=F.join(this.serverConfig.controllersPath,`${d}.js`),c=en(y);c.searchParams.set("v",this.importCacheBust);let p=this.serverConfig.serviceTag,v=async()=>await import(c.href),A=p==null?await v():await ft(p,v);this.controllers[a]={action:A,handler:h};let S=this.logger.createChild({serviceName:"RequestLogger"});this.server.route({method:o,url:i,handler:async(b,E)=>{let B=this.controllers[a];if(!B)throw new Error(`No controller for ${a}`);let xe=B.action[B.handler];if(!xe)throw new Error(`No handler ${B.handler} for ${i}`);let re=await xe(this.getEventObj(b.body,b.query,b.params,b.headers,o,i),this.getContext(),S),ur=JSON.parse(re.body);return E.headers(re.headers??{}),E.code(re?.statusCode??500).send(ur)}})}getEventObj(e,t,r,i,s="",o=""){let a={body:e?JSON.stringify(e):null,headers:i??{},multiValueHeaders:{},httpMethod:s,isBase64Encoded:!1,path:o,pathParameters:r??null,queryStringParameters:t??null,multiValueQueryStringParameters:null,stageVariables:null,requestContext:{accountId:"",apiId:"",authorizer:void 0,protocol:"",httpMethod:s,identity:{accessKey:null,accountId:null,apiKey:null,apiKeyId:null,caller:null,clientCert:null,cognitoAuthenticationProvider:null,cognitoAuthenticationType:null,cognitoIdentityId:null,cognitoIdentityPoolId:null,principalOrgId:null,sourceIp:"devIp",user:null,userAgent:null,userArn:null},path:o,stage:"",requestId:"",requestTimeEpoch:0,resourceId:"",resourcePath:o},resource:o};return a.source="aws.events",a}getContext(){let e=Date.now(),t=sn(),r=new Date().toISOString().split("T")[0]??"";return{callbackWaitsForEmptyEventLoop:!1,functionName:"local-dev",functionVersion:"1.0.0",invokedFunctionArn:"arn:aws:lambda:local:000000000000:function:local-dev",memoryLimitInMB:"1024",awsRequestId:`local-${Date.now()}`,logGroupName:"/aws/lambda/local-dev",logStreamName:`${r}/local`,getRemainingTimeInMillis:()=>Math.max(0,t-(Date.now()-e)),done:(i,s)=>{},fail:i=>{},succeed:i=>{}}}};import{spawn as on}from"node:child_process";import*as Q from"node:fs";var an=(n,e)=>[...n.map(t=>`--filter=...${t}`),...e.map(t=>`--filter=${t}^...`)],Wt=({depInclusive:n,depClosure:e,cwd:t,logFile:r,onUnexpectedExit:i,openLog:s})=>{let o=an(n,e),a=s?s(r):Q.openSync(r,"a"),l=on("pnpm",["exec","turbo","watch","build",...o,"--continue=dependencies-successful","--env-mode=loose","--log-order=stream"],{cwd:t,detached:!0,stdio:["ignore",a,a]});return Q.closeSync(a),M(l,void 0,i)};import{spawn as ln}from"node:child_process";import un from"node:process";var dn=[/^error\b/i,/^[✘✖×]/,/^\[vite\][^\n]*\berror\b/i,/^\w*Error: /,/\berror TS\d+\b/,/^Failed to (?:resolve|load url|parse|compile)\b/i,/^(?:ENOENT|EADDRINUSE|ECONNREFUSED)\b/,/pre-transform error/i,/\b(?:build|transform|compilation) failed\b/i],pn=n=>dn.some(e=>e.test(n))?"error":"info",cn=/\u001B\][\s\S]*?(?:\u0007|\u001B\\)/g,hn=/\u001B\[[0-9;?]*[\u0020-\u002F]*[\u0040-\u007E]/g,gn=/[\u0000-\u0008\u000B-\u001F\u007F]/g,Vt=n=>n.replace(cn,"").replace(hn,"").replace(gn,""),fn=/^([^\s:]+):dev:[ \t]?/,mn=/^([^\s#]+)#dev:\s+(?:WARNING|ERROR)\s+command\b/,vn=n=>mn.exec(Vt(n).trimEnd())?.[1]??null,yn=n=>/^cache (?:bypass|hit|miss)/.test(n)||n.startsWith("$ "),wn=n=>{let e=Vt(n).trimEnd(),t=fn.exec(e);if(t==null)return null;let r=e.slice(t[0].length);return r.trim()===""||yn(r.trim())?null:{pkg:t[1],text:r,level:pn(r)}},bn=64*1024,Gt=(n,e)=>{if(n==null)return;let t="",r=i=>{let s=vn(i);if(s!=null){e.onTaskFailure?.(s);return}let o=wn(i);o!=null&&e.onLine?.(o)};n.setEncoding("utf-8"),n.on("data",i=>{e.appendLog?.(i),t+=i;let s=t.split(`
20
+ `);t=s.pop()??"";for(let o of s)r(o);t.length>bn&&(r(t),t="")}),n.on("end",()=>{t!==""&&(r(t),t="")}),n.on("error",i=>{e.appendLog?.(`[infra-kit] ui dev stream error: ${i.message}
21
+ `)})},Kt=({packageNames:n,cwd:e,concurrency:t,env:r,appendLog:i,onLine:s,onTaskFailure:o,onUnexpectedExit:a})=>{let l=n.map(d=>`--filter=${d}`),u=ln("pnpm",["exec","turbo","run","dev",...l,`--concurrency=${t}`,"--env-mode=loose","--only","--continue=dependencies-successful","--output-logs=new-only","--log-order=stream","--no-update-notifier","--ui=stream"],{cwd:e,detached:!0,stdio:["ignore","pipe","pipe"],env:{...un.env,...r}});return Gt(u.stdout,{appendLog:i,onLine:s,onTaskFailure:o}),Gt(u.stderr,{appendLog:i,onLine:s,onTaskFailure:o}),M(u,void 0,a)};var ee="runner",En="turbo",xn="watch",Pe=n=>`${Math.round(n/1e6)} MB`;function Z(n){let e=Qt.homedir();return n===e||n.startsWith(`${e}${f.sep}`)?`~${n.slice(e.length)}`:n}function Yt(n){return n.map(e=>e.packageName).join(", ")}var Tn=Rn.promisify(An);var $n=async(n,e)=>{try{let{stderr:t}=await Tn(n,{maxBuffer:33554432});t&&e&&e(` (build) ${t.trim()}`,"debug"),t&&!e&&console.error("stderr:",t)}catch(t){let r=t;throw e&&(r.stdout||r.stderr)&&(r.stdout&&e(` stdout: ${r.stdout.trim()}`,"error"),r.stderr&&e(` stderr: ${r.stderr.trim()}`,"error")),t}},te=null;function Zt(n){te?.write(ee,n)}var On=()=>new Promise((n,e)=>{let t=Dn.createServer();t.unref(),t.on("error",e),t.listen(0,"127.0.0.1",()=>{let r=t.address(),i=typeof r=="object"&&r!==null?r.port:0;t.close(()=>n(i))})}),Xt="dev aliases are now scoped by repo (`<release>.<package>.<repo>.localhost`), and only that helper aims Playwright at them. An older one would test whatever another repo serves on the old repo-less host.",Cn="dev URLs are now HTTPS, and only that `infraKitDev` helper understands them. An older one would proxy plain HTTP at a TLS listener, silently.",In=[{name:"@slip-stream-kit/vite",floor:"0.15.0",why:Xt},{name:"@slip-stream-kit/config",floor:"0.15.0",why:Xt},{name:"infra-kit",floor:"0.1.132",why:Cn}],Nn=(n,e)=>{let t=s=>s.split(".").map(o=>Number.parseInt(o,10)||0),r=t(n),i=t(e);for(let s=0;s<Math.max(r.length,i.length);s+=1){let o=r[s]??0,a=i[s]??0;if(o!==a)return o<a}return!1},Ln=n=>{let e=[n];try{for(let t of m.readdirSync(f.join(n,"apps"),{withFileTypes:!0}))if(t.isDirectory())for(let r of["api","ui"])e.push(f.join(n,"apps",t.name,r))}catch{}return e},_n=(n,e)=>{try{let t=JSON.parse(m.readFileSync(f.join(n,"package.json"),"utf-8"));return["dependencies","devDependencies"].some(r=>t[r]?.[e]!=null)}catch{return!1}},Un=(n,e,t)=>{let r=t.split("/"),i=e;for(;;){let s=f.join(i,"node_modules",...r);if(m.existsSync(s))return s;let o=f.dirname(i);if(i===n||o===i)return;i=o}},Fn=(n,e)=>{try{let t=m.realpathSync(e),r=m.realpathSync(n);return t.startsWith(r+f.sep)&&!t.includes(`${f.sep}node_modules${f.sep}`)}catch{return!1}},Bn=n=>{try{return m.realpathSync(n)}catch{return n}},Mn=(n,e,t)=>{let{name:r,floor:i,why:s}=e;if(Fn(n,t))return;let o;try{let a=JSON.parse(m.readFileSync(f.join(t,"package.json"),"utf-8"));if(typeof a.version!="string")throw new Error("no version field");o=a.version}catch{throw new Error(`infra-kit dev: could not read the version of the pinned \`${r}\` helper (${t}). Refusing to start rather than risk proxying plain HTTP at a TLS listener. Run \`pnpm install\`.`)}if(Nn(o,i))throw new Error(`infra-kit dev: this repo pins ${r} ${o}; bump ${r} to >=${i}. From ${i} on, ${s}
22
+ pnpm add -D ${r}@^${i}`)},Hn=n=>{let e=Ln(n);for(let t of In){let{name:r}=t,i=e.filter(o=>_n(o,r)),s=new Map;for(let o of[...i,n]){let a=Un(n,o,r);a&&s.set(Bn(a),a)}if(s.size===0){if(i.length===0)continue;throw new Error(`infra-kit dev: ${i[0]}/package.json depends on ${r} but it does not resolve from there, so the \`infraKitDev\` helper version cannot be verified. Run \`pnpm install\`.`)}for(let o of s.values())Mn(n,t,o)}},jn=n=>{let t=(n instanceof Error?n.message:String(n)).split(`
23
+ `)[0]?.trim()??"";return t===""?"unknown error (see the log)":t},ke=class n{monorepoRoot;devContextDir;appServers=[];reqTimes=new Map;lastSummary=null;readyAt=0;watchDebounceTimers=new Map;pendingChanges=new Map;moduleGeneration=null;watcher=null;static WATCH_DEBOUNCE_MS=400;watchedDistDirs=[];nudgedStaleness=new Map;static STALE_BUILD_GRACE_MS=1e3;restartWorkChain=Promise.resolve();static PORT_RELEASE_DELAY_MS=200;livenessTimer=null;reusing=!1;keepAlive=null;servingAnnounced=!1;targetLabel="";health=new Map;firstErrorLogged=new Set;uiTagByPackage=new Map;static LIVENESS_INTERVAL_MS=5e3;static LIVENESS_FAILURE_THRESHOLD=2;static NEVER_UP_DOWN_THRESHOLD=6;options;runBuild;turboWatchFactory;turboWatch=null;uiDevFactory;uiDev=null;sink;intercept;dryRunner;failedApps=[];degradedRoutes=[];heldLocalPkgs=new Set;launchedUis=[];proxyRoutes=[];renderer;healthProbe;proxy;proxyPort=Ue;registeredAliases=new Set;uiPortMap={};shuttingDown=!1;closureMap=null;closureBuild=Promise.resolve();teardown=null;stage="idle";constructor(e={},t=$n,r=Wt,i=Kt,s,o,a=Ve,l=Ie(),u){this.options=e,this.runBuild=t,this.turboWatchFactory=r,this.uiDevFactory=i,this.dryRunner=s,this.proxy=l,this.sink=u??new q,te=this.sink;let d=er(this.options);this.intercept=d?It({sink:this.sink,fallbackService:ee,currentService:K}):null,this.renderer=o??new tt({appendLog:Zt,verbose:this.options.verbose??!1,isTTY:d}),this.healthProbe=a,this.devContextDir=f.join(g.cwd(),".infra-kit","dev-context"),this.monorepoRoot=N(g.cwd()),(g.env.DOPPLER_PROJECT!=null||g.env.DOPPLER_ENVIRONMENT!=null)&&this.renderer.log("\u{1F510} Doppler env detected (DOPPLER_PROJECT / DOPPLER_ENVIRONMENT)","debug")}discoverApiApps(){return j(this.monorepoRoot).map(e=>({...e,preferredPort:this.resolvePreferredPort(e.name),appScopedPort:this.resolvePreferredPort(e.name,!1),prefixUrl:Lt}))}normalizeAppInclude(){return W(this.options.include)}resolvePreferredPort(e,t=!0){return _t(e,g.env,t)}async resolvePresetDef(e,t){if(this.options.presetDef!=null)return this.options.presetDef;let r=this.options.app;if(r==null)return{};let i=await nt(r,e,t);if(i)return i.unprovided.length>0&&this.renderer.log(`\u26A0\uFE0F No apps/*/api provides these local-capable routes, so they stay on cloud: ${i.unprovided.join(", ")}`,"warn"),i.preset;let[s,...o]=[...new Set([...e,...t].map(a=>a.name).toSorted())];throw s===void 0?new se(void 0,{operation:`run "${r}"`,remediation:"omit the name to run every app",stderrExcerpt:"no apps were discovered"}):new Fe({status:"argument_required",argument:"app",choices:Se.toJSONSchema(Se.object({app:Se.enum([s,...o])})),agentMode:Le.source},2,{operation:"pick what infra-kit dev runs",remediation:`pass an app: ${[s,...o].join(", ")}`,stderrExcerpt:`"${r}" is not an app`})}async start(){let e=Date.now(),t=this.normalizeAppInclude(),r=this.options.watch??!1;g.env.POWERTOOLS_DEV??="true",g.env.LOG_LEVEL??="DEBUG",this.renderer.narrate("\u{1F680} Starting Development Server Runner"),r&&this.renderer.narrate("\u{1F440} Watch mode: will rebuild and restart on file save");let{apps:i,uiApps:s,apiAppsAll:o,uiAppsAll:a,wantedLocalPkgs:l,presetProxy:u}=await this.resolveRunPlan(t);if(await this.endsBeforeBoot(i,s))return;this.warnUiMissingVitePlugin(s),r&&this.installModuleGeneration(),await this.bringUpProxy(i),await this.buildAll(i,s,r),i.length>0&&(this.renderer.bootStep("starting servers"),await this.startAllApps(i),this.renderer.narrate(this.failedApps.length===0?"\u{1F389} All servers started!":`\u26A0\uFE0F ${this.appServers.length}/${i.length} servers started \u2014 ${this.failedApps.length} failed`),this.renderer.narrate(`\u{1F4DD} Logs \u2192 ${Z(this.sink.dir)} (one file per service)`),this.assertNoBootFailuresWhenReusing(i.length));let d=et({running:[...i.map(h=>`${h.name}/api`),...s.map(h=>`${h.name}/ui`)],discovered:[...o.map(h=>`${h.name}/api`),...a.map(h=>`${h.name}/ui`)]});if(this.targetLabel=d,r&&this.holdFailedBackendsLocal(),this.degradedRoutes=await this.collectDegradedRoutes(s,l,u),this.degradedRoutes.length>0&&!r)throw new Error(gt(this.degradedRoutes,d));if(await this.printReady(i,s,e,d),this.announceServingOnce(),this.options.routes&&this.printRouteDump(),this.appServers.length===0&&s.length===0&&this.failedApps.length>0)throw new Error(`infra-kit dev: no app started (${this.failedApps.length} of ${i.length} failed). First failure: ${this.failedApps[0]?.reason??"unknown"}`);this.armWatch(i,s,r),s.length>0&&this.startUiDev(s),this.startLivenessMonitor()}async resolveRunPlan(e){let t=this.discoverApiApps(),r=ze(this.monorepoRoot),i=await this.resolvePresetDef(t,r),s={api:t.map(p=>p.name),ui:r.map(p=>p.name)},o=Ze(i,s);o.unmatched.length>0&&this.renderer.log(`\u26A0\uFE0F Targets not found (skipped): ${o.unmatched.join(", ")}`,"warn");let a=new Set(o.targets.filter(p=>p.part==="api").map(p=>p.app)),l=new Set(o.targets.filter(p=>p.part==="ui").map(p=>p.app)),u=p=>!e||e.includes(p),d=t.filter(p=>a.has(p.name)&&u(p.name)),h=d.map(p=>({...p,preferredPort:d.length===1?p.preferredPort:p.appScopedPort})),y=r.filter(p=>l.has(p.name)&&u(p.name)),c=new Set(t.filter(p=>a.has(p.name)).map(p=>p.packageName));return{apps:h,uiApps:y,apiAppsAll:t,uiAppsAll:r,wantedLocalPkgs:c,presetProxy:o.proxy}}async collectDegradedRoutes(e,t,r){if(e.length===0)return[];let i=[];for(let s of e){let o=await zt(s.path).catch(()=>{}),a=o?.dev;if(!a?.proxy)continue;let l=r[s.name]??{},u=Object.fromEntries(Object.entries(a.proxy.routes).map(([d,h])=>[d,{...h,pinnedLocal:l[d]==="local"}]));i.push({app:s.name,routes:u,cloudUrl:qt(o?.deployedUrlEnv).url})}return this.launchedUis=i,pt({uis:i,wanted:t,running:new Set(this.appServers.map(({app:s})=>s.packageName)),reasons:new Map(this.failedApps.map(({app:s,reason:o})=>[s.packageName,{app:s.name,reason:o}])),held:this.heldLocalPkgs})}warnUiMissingVitePlugin(e){for(let t of e)!t.managedPort||Je(t.path)||this.renderer.log(`\u26A0\uFE0F ${t.name}/ui is wired with the infra-kit vite helper directly, not the infraKit() plugin \u2014 its proxy is baked at config load and won't react to backend routing changes. Wire the infraKit() vite plugin (from @slip-stream-kit/vite) instead.`,"warn")}proxiesFor(e){let t=this.proxyRoutes.filter(r=>r.uiApp===e).map(r=>({route:r.route,source:r.source,target:r.target}));return t.length>0?t:void 0}async bringUpProxy(e){Hn(N(g.cwd())),await this.ensureProxy(),e.length>0&&this.assertNoPortConflicts(e)}async buildAll(e,t,r){let i=[e.length>0?`building ${Yt(e)}`:"",t.length>0?`warming ${Yt(t)}`:""].filter(Boolean).join(" \xB7 ");this.renderer.bootStep(i),e.length>0&&await this.buildApps(e,r),t.length>0&&await this.buildUiApps(t)}armWatch(e,t,r){r&&(this.appServers.length>0||t.length>0)&&(this.setupWatch(e,t),this.closureBuild=this.buildClosureMapSafe(e).then(i=>{this.closureMap=i}))}async buildUiApps(e){let t=e.map(r=>`--filter=${r.packageName}^...`).join(" ");try{await this.runBuild(`pnpm exec turbo run build ${t} --env-mode=loose --output-logs=errors-only --no-update-notifier`,this.renderer.logFn),this.renderer.narrate("\u2705 UI deps built")}catch(r){this.renderer.log(`\u26A0\uFE0F UI dep build failed (continuing; vite will report unresolved deps): ${String(r)}`,"warn")}}startUiDev(e){let t=e.map(o=>o.name).join(", ");this.renderer.narrate(`\u{1F3A8} Starting ${e.length} UI dev server(s) via \`turbo run dev\`: ${t}`),this.renderer.narrate(" (framework output is routed into the live tail; full detail in the runner log)");let r=Object.keys(this.uiPortMap).length>0?{INFRA_KIT_UI_PORTS:JSON.stringify(this.uiPortMap)}:void 0,i=g.env.NODE_EXTRA_CA_CERTS==null&&m.existsSync(ie())?{NODE_EXTRA_CA_CERTS:ie()}:void 0,s=new Map(e.map(o=>[o.packageName,`${o.name}/ui`]));this.uiDev=this.uiDevFactory({packageNames:e.map(o=>o.packageName),cwd:g.cwd(),concurrency:Math.max(e.length+4,12),env:r||i?{...r,...i}:void 0,appendLog:o=>{this.sink.write(En,o)},onLine:({pkg:o,text:a,level:l})=>{let u=s.get(o)??`${o}/ui`;this.sink.write(u,a,{level:l})},onTaskFailure:o=>{this.markUiTaskFailed(s.get(o)??`${o}/ui`)},onUnexpectedExit:o=>{this.markUiEngineDead(),this.reportEngineDeath("UI dev engine (`turbo run dev`)","frontends stopped reloading",o)}})}formatAppList(e){return e.map(t=>`${t.name}:${t.port}`).join(", ")}assertNoPortConflicts(e){let t=e.filter(s=>s.preferredPort!=null).map(s=>({name:s.name,port:s.preferredPort})),{duplicatePorts:r,conflictingApps:i}=Ut(t);if(r.length!==0)throw this.renderer.log(`\u26A0\uFE0F Port conflict detected! ${r.join(", ")}`,"error"),this.renderer.log(`Conflicting apps: ${this.formatAppList(i)}`,"error"),this.renderer.log("\n\u{1F4A1} Tip: give each app a distinct port via `{APP}_PORT` env (e.g. `CLIENT_PORT=`,","error"),this.renderer.log(" `SEARCH_ENGINE_PORT=`); or run a subset with","error"),this.renderer.log(" `--app=<name>,<name>`.\n","error"),new Error(`Port conflict detected: ${r.join(", ")}`)}async buildApps(e,t){let i=`pnpm exec turbo run build ${e.map(s=>`--filter=${s.packageName}`).join(" ")} --env-mode=loose --output-logs=errors-only --no-update-notifier${t?" --force":""}`;try{await this.runBuild(i,this.renderer.logFn),this.renderer.narrate("\u2705 Build complete")}catch(s){this.renderer.log(`\u274C Build failed: ${String(s)}`,"error"),s instanceof Error&&s.message&&this.renderer.log(` ${s.message}`,"error");let o=s;throw o.stdout&&this.renderer.log(` stdout: ${o.stdout.trim()}`,"error"),o.stderr&&this.renderer.log(` stderr: ${o.stderr.trim()}`,"error"),s}}async startAllApps(e){await Promise.all(e.map(async t=>{try{let r=await this.startOneApp(t);r&&this.appServers.push({app:t,...r,startedAt:Date.now(),restarts:0})}catch(r){this.renderer.log(`\u274C Failed to start ${t.name}: ${String(r)}`,"error"),this.failedApps.push({app:t,reason:jn(r)})}}))}async ensureProxy(){let e=this.proxy.binPath();if(e==null||!await this.proxy.isAvailable())throw new Error("infra-kit dev: portless is not installed, so no dev URL can resolve. It ships as a dependency of infra-kit \u2014 reinstall with `pnpm install`.");let t=Ne(e,this.options.portlessLink??{home:Qt.homedir()}),r=await this.proxy.probeProxy(this.proxyPort,!0);if(r!=="serving")throw r==="not-portless"?new Error(`infra-kit dev: something else is already listening on :${this.proxyPort}, and it is not portless (no \`X-Portless\` header on the wire). Find and stop it, e.g. \`sudo lsof -iTCP:${this.proxyPort} -sTCP:LISTEN\`. Installing or trusting portless will not help while another process holds the port.`):this.proxy.isServiceInstalled()==="no"?new Error(`infra-kit dev: no portless daemon is serving HTTPS on :${this.proxyPort}, and its OS service is not installed yet, so no dev URL can resolve. Install it once (this is the only step that needs root):
24
24
  ${t}
25
25
  Then trust its local CA (no sudo needed):
26
26
  ${ne(["trust"],{bin:e})}
@@ -28,12 +28,12 @@ Then trust its local CA (no sudo needed):
28
28
  ${ne(["service","status"],{bin:e})}
29
29
  Reinstalling restarts it (still the only step that needs root):
30
30
  ${t}
31
- \`infra-kit doctor\` checks the daemon and CA trust state.`)}async registerAppAlias(e,t,r){let i=Se(e,t);if(!await this.proxy.registerAlias(i,r))throw new Error(`infra-kit dev: portless refused the alias "${i}" \u2192 127.0.0.1:${r}.`);return this.registeredAliases.add(i),`${i}.localhost`}async startOneApp(e){this.renderer.narrate(`\u{1F504} Starting ${e.name}...`);let t=new X({controllersPath:e.path,prefixUrl:e.prefixUrl,port:e.preferredPort,appName:e.name,serviceTag:`${e.name}/api`,onRequestLog:({method:s,path:o,status:a,ms:l})=>{if(o==="/__health")return;let u=`${e.name}/api`,d=`${s} ${o} ${a} ${l}ms`;this.sink.write(u,d,{level:a>=500?"error":"info"});let h=this.reqTimes.get(u);h?h.push(Date.now()):this.reqTimes.set(u,[Date.now()])}}),r=await t.start(),i;try{i=await this.registerAppAlias(e.packageName,e.path,r)}catch(s){throw await t.close().catch(()=>{}),s}try{this.writeDevContextFragment(e,r,i)}catch(s){this.renderer.log(`\u26A0\uFE0F Failed to write dev-context fragment for ${e.name}: ${String(s)}`,"warn")}return this.renderer.narrate(`\u2705 ${e.name} started on port ${r}`),{server:t,boundPort:r,alias:i}}holdFailedBackendsLocal(){for(let{app:e}of this.failedApps)try{this.writeDevContextFragment(e,0,`${Se(e.packageName,e.path)}.localhost`),this.heldLocalPkgs.add(e.packageName)}catch(t){this.renderer.log(`\u26A0\uFE0F Failed to hold ${e.name}'s routes local: ${String(t)}`,"warn")}}writeDevContextFragment(e,t,r){let i={v:Rn,package:e.packageName,port:t,pid:v.pid,writtenAt:Date.now(),release:ae(e.path),alias:r,origin:`https://${r}`,env:v.env[Ce]??""},s=y.join(this.devContextDir,`${e.name}.json`),o=y.join(this.devContextDir,`${e.name}.json.${v.pid}.tmp`);w.mkdirSync(this.devContextDir,{recursive:!0}),w.writeFileSync(o,JSON.stringify(i,null,2)),w.renameSync(o,s)}removeDevContextFragment(e){w.rmSync(y.join(this.devContextDir,`${e.name}.json`),{force:!0})}scheduleRestartWork(e){let t=this.restartWorkChain.then(()=>e(),()=>e());return this.restartWorkChain=t.catch(()=>{}),t}async delayPortRelease(){await new Promise(e=>setTimeout(e,n.PORT_RELEASE_DELAY_MS))}restart(e,t=[]){return this.shuttingDown?Promise.resolve():this.scheduleRestartWork(async()=>{this.shuttingDown||(await this.runRestart(e,t),this.recoverLostSaves(this.triggeringBuilds(t)))})}resolveRestartTargets(e){return e.map(t=>({idx:this.appServers.findIndex(r=>r.app.name===t.name),app:t})).filter(t=>t.idx>=0||this.isFailedApp(t.app.name))}isFailedApp(e){return this.failedApps.some(t=>t.app.name===e)}promoteRecoveredApp(e,t){let r={app:e,...t,startedAt:Date.now(),restarts:0},i=`${e.name}/api`;this.healthEntry(i,"api"),this.appServers.push(r);let s=this.failedApps.findIndex(o=>o.app.name===e.name);return s>=0&&this.failedApps.splice(s,1),this.lastSummary&&(this.lastSummary={...this.lastSummary,endpoints:[...this.lastSummary.endpoints,{tag:i,url:_({prefixUrl:e.prefixUrl,alias:t.alias}),health:this.healthOf(i)}],failed:(this.lastSummary.failed??[]).filter(o=>o.tag!==i)}),r}async runRestart(e,t=[]){let r=this.resolveRestartTargets(e);if(r.length===0)return;let i=r.length===1?r[0].app.name:`${r.length} apps`,s=this.moduleGeneration?.budgetState()??"ok";if(s==="stop"){this.reportGenerationBudgetStop(ve(v.env.INFRA_KIT_DEV_STOP_BYTES).stopBytes),this.shutdown().catch(()=>{}).finally(()=>{this.options.onExitRequest?.(1)}).catch(()=>{});return}s==="warn"&&this.reportGenerationBudgetWarn(),this.moduleGeneration?.bump(),this.renderer.log(`\u{1F504} Restarting ${i}...`),await Promise.all(r.map(async({idx:p})=>{if(!(p<0))try{await this.appServers[p].server.close()}catch(g){this.renderer.log(` Close warning: ${String(g)}`,"debug")}})),await this.delayPortRelease();let o=await Promise.all(r.map(async({idx:p,app:g})=>{try{let m=await this.startOneApp(g);if(m){if(p<0){let S=this.promoteRecoveredApp(g,m);return this.renderer.log(`\u2705 ${g.name} recovered \u2014 its routes are served locally again`,"info"),{app:g,entry:S}}let P=this.appServers[p],c={app:g,...m,startedAt:Date.now(),restarts:(P?.restarts??0)+1};return this.appServers[p]=c,{app:g,entry:c}}return{app:g,entry:null}}catch(m){return this.renderer.log(`\u274C Failed to ${p<0?"start":"restart"} ${g.name}: ${String(m)}`,"error"),{app:g,entry:null}}})),a=await Promise.all(o.map(async({app:p,entry:g})=>{let m=`${p.name}/api`;if(g==null)return this.markDown(m,"api"),this.refreshStatus(),{label:`${p.name} \u25CF down`,healthy:!1};let P={tag:m,port:g.boundPort,kind:"api"},c=await this.healthProbe(P);this.recordProbe(P,c),this.refreshStatus();let S=c==="ok";return{label:`${p.name}:${g.boundPort} ${S?"\u25CF up":"\u25CF down"}`,healthy:S}})),l=a.every(p=>p.healthy),u=a.map(p=>p.label).join(", "),h=o.some(p=>p.entry!==null)?this.resolveStaleFiles(t,r):[];if(h.length>0){this.renderer.log(`\u26A0\uFE0F Restarted ${u} \u2014 still serving the OLD ${Gt(h)}. Re-run \`infra-kit dev\` to pick this up.`,"warn");return}let f=this.describeBuildErrors(this.triggeringBuilds(t));if(f!==void 0){this.renderer.log(`\u26A0\uFE0F Restarted ${u} \u2014 ${f}`,"warn");return}this.renderer.log(`${l?"\u2705":"\u26A0\uFE0F "} Restarted ${u}`)}triggeringBuilds(e){let t=new Map;for(let r of e){let i=this.watchedDistDirs.find(o=>r===o||r.startsWith(o+y.sep));if(i===void 0)continue;let s=y.dirname(i);t.set(s,[...t.get(s)??[],r])}return[...t].flatMap(([r,i])=>{let s=G(r);return s&&lt(s,i)?[{pkgDir:r,info:s}]:[]})}describeBuildErrors(e){let t=e.flatMap(({pkgDir:r,info:i})=>{let s=ut(i,r);return s?[dt(pe(r),s)]:[]});return t.length>0?t.join("; "):void 0}async recoverLostSaves(e){try{for(let{pkgDir:t,info:r}of e)await this.recoverLostSave(t,r)}catch(t){this.renderer.log(` Lost-save check failed: ${String(t)}`,"debug")}}async recoverLostSave(e,t){let r=de(t,e);if(r.length===0){this.nudgedStaleness.delete(e);return}let i=r.map(({file:s,hash:o})=>`${s}:${o}`).join(`
32
- `);this.nudgedStaleness.get(e)!==i&&(await new Promise(s=>{setTimeout(s,n.STALE_BUILD_GRACE_MS)}),!(this.shuttingDown||G(e)?.mtimeMs!==t.mtimeMs)&&(this.nudgedStaleness.set(e,i),await this.forceRebuild(e,r)))}async forceRebuild(e,t){let r=pe(e),i=t.map(({file:a})=>y.relative(e,a)).join(", "),s=new Date;this.renderer.log(`\u{1F440} ${r}: last build missed a save of ${i}; forcing a rebuild`,"debug");for(let{file:a}of t)try{w.utimesSync(a,s,s)}catch{}try{await this.runBuild(`pnpm exec turbo run build --filter=${r} --env-mode=loose --output-logs=errors-only --no-update-notifier --force`,a=>{this.renderer.log(a,"debug")})}catch(a){this.renderer.log(` Forced rebuild of ${r} failed: ${String(a)}`,"debug")}let o=G(e);this.shuttingDown||!o||de(o,e).length===0||this.renderer.log(`\u26A0\uFE0F ${r}: ${i} changed but a forced rebuild still did not compile it \u2014 the server may be serving an older save. Save the file again, or re-run \`infra-kit dev\`.`,"warn")}resolveStaleFiles(e,t){let r=this.moduleGeneration;return r?e.filter(i=>!r.bustedUnder(y.dirname(i))):e.filter(i=>!t.some(s=>Wt(i,s.app.path)))}async buildClosureMapSafe(e){try{return await ct(this.monorepoRoot,e,this.dryRunner)}catch(t){return this.renderer.log(`\u26A0\uFE0F Dependency-closure map unavailable (${String(t)}); package changes restart all apps`,"warn"),null}}reportEngineDeath(e,t,r){this.shuttingDown||this.renderer.log(`\u26A0\uFE0F ${e} ${r} \u2014 ${t}. Restart \`infra-kit dev\`.`,"warn")}markUiEngineDead(){if(!this.shuttingDown){for(let{tag:e,kind:t}of this.uiTargets())this.healthEntry(e,t).dead=!0;this.refreshStatus()}}markUiTaskFailed(e){if(this.shuttingDown)return;if(this.uiHealthEnabled()){if(this.healthOf(e)==="down")return;this.markDown(e,"ui")}let t=this.uiTagByPackage.size>1?"; other frontends are unaffected":"";this.renderer.log(`\u26A0\uFE0F ${e} failed to start \u2014 its \`dev\` task exited${t}`,"error"),this.refreshStatus(),this.options.reuse&&this.shutdown().then(()=>{this.options.onExitRequest?.(1)})}setupWatch(e,t){this.turboWatch=this.turboWatchFactory({depInclusive:e.map(l=>l.packageName),depClosure:t.map(l=>l.packageName),cwd:v.cwd(),logFile:this.sink.pathFor(In),onUnexpectedExit:l=>{this.reportEngineDeath("Watch engine (`turbo watch build`)","file saves no longer rebuild",l)}}),this.renderer.narrate("\u{1F440} Watch mode: started `turbo watch build` engine; watching dist output");let r=je(e),i=I(this.monorepoRoot),s=[...r,...i];if(this.watchedDistDirs=s,s.length===0){this.renderer.log("\u26A0\uFE0F No app or package dist directories found to watch (were they built?)","warn");return}let o=v.env.DEV_SERVER_CHOKIDAR_POLL==="1",a=xn.watch(s,{ignoreInitial:!0,awaitWriteFinish:{stabilityThreshold:200,pollInterval:100},ignored:l=>l.endsWith(".tsbuildinfo")||l.endsWith(".map"),...o?{usePolling:!0,interval:400}:{}});this.watcher=a,o&&this.renderer.log("\u{1F440} chokidar: usePolling enabled (DEV_SERVER_CHOKIDAR_POLL=1)","debug");for(let l of["add","change","unlink"])a.on(l,u=>{this.handleDistChange(u,e,r,i)});this.renderer.narrate(`\u{1F440} Watching ${r.length} app dist + ${i.length} package dist dir(s) for changes...`),this.renderer.narrate(this.moduleGeneration?"\u{1F440} Watch reloads the whole local graph in place \u2014 shared packages and services included.":"\u{1F440} Watch reloads serverless handler entry files only; other modules need a re-run of `infra-kit dev`.")}handleDistChange(e,t,r,i){this.renderer.log(`\u{1F440} dist change detected: ${e}`,"debug");let s=We(e,r,i);if(s.kind==="package"){this.handlePackageDistChange(e,t,s.packageDir);return}let o=t.find(a=>y.join(a.path,"dist")===s.app);o&&(this.markChanged(o.name,e),this.scheduleDebounced(o.name,()=>this.restart([o],this.takeChanged(o.name))))}handlePackageDistChange(e,t,r){let i=ht(t,this.closureMap,r);if(i===null){this.markChanged("__packages__",e),this.scheduleDebounced("__packages__",()=>this.restart(t,this.takeChanged("__packages__")));return}for(let s of i)this.markChanged(s.name,e),this.scheduleDebounced(s.name,()=>this.restart([s],this.takeChanged(s.name)))}markChanged(e,t){let r=this.pendingChanges.get(e);r?r.add(t):this.pendingChanges.set(e,new Set([t]))}installModuleGeneration(){if(v.env.INFRA_KIT_DEV_NO_GENERATION==="1"){this.renderer.log("\u{1F440} Module generation disabled (INFRA_KIT_DEV_NO_GENERATION=1)","debug");return}if(!Nt()){this.renderer.log(`\u26A0\uFE0F This Node (${v.version}) has no \`module.registerHooks\`, so only handler entry files reload in place.`,"warn");return}let e=ve(v.env.INFRA_KIT_DEV_STOP_BYTES);this.moduleGeneration=_t({root:w.realpathSync(this.monorepoRoot),selfDist:Lt(),stopBytes:e.stopBytes,stopEnabled:e.stopEnabled})}reportGenerationBudgetWarn(){let e=this.moduleGeneration?.generation()??0;this.renderer.log(`\u26A0\uFE0F Reload memory ${De(v.memoryUsage.rss())} after ${e} reload(s). Module records cannot be freed in-process, so this only grows \u2014 re-run \`infra-kit dev\` when convenient.`,"warn")}reportGenerationBudgetStop(e){let t=this.moduleGeneration?.generation()??0;this.renderer.log(`\u26D4 Reload memory ${De(v.memoryUsage.rss())} exceeds the ${De(e)} budget after ${t} reload(s) \u2014 stopping. Module records cannot be freed in-process. Re-run \`infra-kit dev\` to continue, or set INFRA_KIT_DEV_STOP_BYTES to raise the budget (or \`off\` to disable the stop).`,"error")}takeChanged(e){let t=this.pendingChanges.get(e);return this.pendingChanges.delete(e),t?[...t]:[]}scheduleDebounced(e,t){if(this.shuttingDown)return;let r=this.watchDebounceTimers.get(e);r&&clearTimeout(r);let i=setTimeout(()=>{this.watchDebounceTimers.delete(e),!this.shuttingDown&&t().catch(s=>{this.renderer.log(`Restart error (${e}): ${String(s)}`,"error")})},n.WATCH_DEBOUNCE_MS);this.watchDebounceTimers.set(e,i)}uiHealthEnabled(){return(this.options.uiHealth??!0)&&v.env.INFRA_KIT_NO_UI_HEALTH!=="1"}apiTargets(){return this.appServers.map(({app:e,boundPort:t})=>({tag:`${e.name}/api`,port:t,kind:"api"}))}uiTargets(){return this.uiHealthEnabled()?Object.entries(this.uiPortMap).map(([e,{port:t}])=>({tag:this.uiTagByPackage.get(e)??`${e}/ui`,port:t,kind:"ui"})):[]}healthEntry(e,t){let r=this.health.get(e);if(r)return r;let i={kind:t,failures:0,foreignStreak:0,everUp:!1,dead:!1,unverified:!1};return this.health.set(e,i),i}recordProbe(e,t){let r=this.healthEntry(e.tag,e.kind),i=n.LIVENESS_FAILURE_THRESHOLD;if(t==="ok"){let s=r.failures>=i;r.everUp=!0,r.failures=0,r.foreignStreak=0,r.unverified=!1,s&&this.renderer.log(`\u2705 ${e.tag} recovered`);return}if(tt(t)&&e.kind==="ui"){r.foreignStreak+=1,r.unverified=r.foreignStreak>=i,r.foreignStreak===i&&this.renderer.log(`\u26A0\uFE0F ${e.tag}: ${rt(e.port,t)}`,"warn");return}if(r.foreignStreak=0,r.failures+=1,e.kind==="ui"&&r.failures>=i&&(r.unverified=!1),r.failures===i){let s=e.kind==="api"?"/__health not responding":"not answering vite's ping";this.renderer.log(`\u26A0\uFE0F ${e.tag} unhealthy (${s})`,"warn")}}markDown(e,t){let r=this.healthEntry(e,t);r.dead=!0,r.failures=n.LIVENESS_FAILURE_THRESHOLD,r.foreignStreak=0,r.unverified=!1}healthOf(e){let t=this.health.get(e);return t==null?"unknown":t.unverified?"unverified":t.everUp?t.failures>=n.LIVENESS_FAILURE_THRESHOLD?"down":"ok":this.neverUpState(t)}neverUpState(e){return e.kind==="api"||e.dead||e.failures>=n.NEVER_UP_DOWN_THRESHOLD?"down":"starting"}startLivenessMonitor(){this.scheduleLivenessTick()}scheduleLivenessTick(){let e=this.options.livenessIntervalMs??n.LIVENESS_INTERVAL_MS;this.livenessTimer=setTimeout(()=>{this.livenessTick().finally(()=>{this.shuttingDown||this.scheduleLivenessTick()}).catch(()=>{})},e),this.livenessTimer.unref()}refreshStatus(){let e=this.lastSummary;if(e==null)return;let t=Date.now(),r=new Map(this.appServers.map(i=>[`${i.app.name}/api`,i]));this.pruneRequestWindow(t),this.renderer.refresh?.({...e,sessionUptimeMs:t-this.readyAt,degraded:this.degradedRows(),uiRefs:e.uiRefs.map(i=>({...i,errors:this.announceFirstError(i.tag)})),endpoints:e.endpoints.map(i=>{let s=r.get(i.tag);return{...i,health:this.healthOf(i.tag),uptimeMs:s?t-s.startedAt:void 0,restarts:s?.restarts,rpm:(this.reqTimes.get(i.tag)??[]).length,errors:this.announceFirstError(i.tag)}})})}announceFirstError(e){let{errors:t}=this.sink.statsFor(e);return t>0&&!this.firstErrorLogged.has(e)&&(this.firstErrorLogged.add(e),this.renderer.log(`\u26A0\uFE0F ${e} reported its first error \u2192 ${Z(this.sink.pathFor(e))}`,"warn")),t}degradedRows(){if(this.degradedRoutes.length===0)return[];let e=new Set(this.appServers.map(({app:t})=>t.packageName));return this.degradedRoutes.filter(t=>!e.has(t.packageName)).map(t=>({route:t.route,tag:`${t.uiApp}/ui`,fallback:t.fallback,target:t.cloudTarget}))}pruneRequestWindow(e){let t=e-6e4;for(let[r,i]of this.reqTimes)this.reqTimes.set(r,i.filter(s=>s>t))}async livenessTick(){this.shuttingDown||(await Promise.all([...this.apiTargets(),...this.uiTargets()].map(e=>this.probeOne(e))),this.announceServingOnce(),this.refreshStatus())}announceServingOnce(){this.servingAnnounced||this.lastSummary==null||this.failedApps.length>0||!this.lastSummary.endpoints.every(t=>{let r=this.healthOf(t.tag);return r==="ok"||r==="unknown"})||(this.servingAnnounced=!0,this.renderer.log(`${Xt} ${this.targetLabel}`))}async findServedApps(e,t){let r=Ie(),i=[...e.map(o=>({tag:`${o.name}/api`,kind:"api",port:En(o.path).info.get(o.packageName)?.port??0})),...t.map(o=>{let a=Se(o.packageName,o.path);return{tag:`${o.name}/ui`,kind:"ui",port:r.find(l=>l.name===a||l.name===`${a}.localhost`)?.port??0}})],s=await Promise.all(i.map(async o=>o.port>0&&await this.healthProbe(o)==="ok"));return i.filter((o,a)=>s[a]).map(o=>o.tag)}async endsBeforeBoot(e,t){if(e.length===0&&t.length===0)return this.renderer.log("\u26A0\uFE0F No API or UI apps to run for this preset","warn"),!0;if(!this.options.reuse)return!1;let r=await this.findServedApps(e,t);return r.length>0?(this.idleOverRunningSession(r),!0):(await this.assertCloudRoutesHaveEnv(e,t),!1)}assertNoBootFailuresWhenReusing(e){let[t]=this.failedApps;if(!(!this.options.reuse||!t))throw new Error(`infra-kit dev: ${this.failedApps.length} of ${e} app(s) failed to start. First failure (${t.app.name}): ${t.reason}`)}idleOverRunningSession(e){this.reusing=!0,this.keepAlive=setInterval(()=>{},6e4),this.renderer.log(`\u267B\uFE0F Reusing the dev server this worktree already runs (${e.join(", ")})`),this.servingAnnounced=!0,this.renderer.log(`${Xt} ${e.join(", ")}`)}async assertCloudRoutesHaveEnv(e,t){let r=new Set(e.map(s=>s.packageName)),i=[];for(let s of t){let o=await Qt(s.path),a=Object.entries(o?.dev?.proxy?.routes??{}).filter(([,l])=>!l.from.includes("local")||!r.has(l.packageName));if(a.length>0&&!Zt(o?.deployedUrlEnv).url){let l=a.map(([u])=>u);i.push(`${s.name}/ui ${l.join(", ")} (${o?.deployedUrlEnv??"no deployedUrlEnv"})`)}}if(i.length!==0)throw new se(void 0,{operation:"start the dev servers",remediation:"load an environment first (`infra-kit env-load -c dev`)",stderrExcerpt:`these routes proxy to the deployed app, whose URL is not loaded: ${i.join("; ")}`})}async probeOne(e){let t=await this.healthProbe(e);this.shuttingDown||this.recordProbe(e,t)}async printReady(e,t,r,i){let s=Date.now()-r;await Promise.all(this.apiTargets().map(p=>this.probeOne(p)));let o=this.appServers.map(({app:p,alias:g})=>({tag:`${p.name}/api`,url:_({prefixUrl:p.prefixUrl,alias:g}),health:this.healthOf(`${p.name}/api`)})),a=new Map(this.appServers.map(({app:p,alias:g})=>[p.packageName,_({prefixUrl:p.prefixUrl,alias:g})]));this.proxyRoutes=mt({uis:this.launchedUis,running:new Set(this.appServers.map(({app:p})=>p.packageName)),localOrigin:p=>a.get(p),held:this.heldLocalPkgs}),this.uiPortMap={};let l=[],u=[];for(let p of t){let g=await this.assignUiPort(p),m=`${p.name}/ui`;g!=null?l.push({tag:m,url:_({prefixUrl:"",alias:g.alias}),health:this.seedUiHealth(m),proxies:this.proxiesFor(p.name)}):u.push({tag:m,proxies:this.proxiesFor(p.name)})}let d=this.options.watch??!1,h=e.length+t.length,f=I(this.monorepoRoot).length;this.lastSummary={target:i,watch:d,release:ae(v.cwd()),elapsedMs:s,endpoints:[...o,...l],uiRefs:u,failed:this.failedApps.map(({app:p,reason:g})=>({tag:`${p.name}/api`,reason:g})),degraded:this.degradedRows(),watchSummary:`${h} app${h===1?"":"s"} \xB7 ${f} package${f===1?"":"s"}`,logPath:Z(this.sink.dir),logHref:this.sink.dir},this.readyAt=Date.now(),this.renderer.ready(this.lastSummary),this.refreshStatus()}async assignUiPort(e){if(!e.managedPort)return null;let t;try{t=await _n()}catch{return null}let r=await this.registerAppAlias(e.packageName,e.path,t);return this.uiPortMap[e.packageName]={port:t,alias:r},this.uiTagByPackage.set(e.packageName,`${e.name}/ui`),{port:t,alias:r}}seedUiHealth(e){return this.uiHealthEnabled()?(this.healthEntry(e,"ui"),this.healthOf(e)):"unknown"}printRouteDump(){if(this.appServers.length!==0){this.renderer.log("\u{1F5FA}\uFE0F Registered routes:");for(let{app:e,server:t}of this.appServers){let r=t.getRegisteredRoutes();this.renderer.log(` ${e.name} (${r.length}): ${r.length>0?r.join(", "):"(none)"}`)}}}reportFault(e){this.sink.write(K()??ee,e,{level:"error"}),!this.shuttingDown&&this.renderer.log(e,"error",{tee:!1})}fileFault(e){this.sink.write(K()??ee,e,{level:"error"})}get shutdownStage(){return this.stage}shutdown(){return this.teardown!=null?this.teardown:(this.teardown=this.doShutdown(),this.teardown.catch(()=>{}),this.teardown)}async doShutdown(){if(this.shuttingDown=!0,this.stage="starting",this.renderer.dispose(),this.intercept?.uninstall(),this.reusing){this.keepAlive&&clearInterval(this.keepAlive),this.keepAlive=null,this.renderer.log("\u267B\uFE0F Left the reused dev server running"),this.stage="done";return}this.renderer.log("\u{1F6D1} Shutting down all servers...");for(let e of this.watchDebounceTimers.values())clearTimeout(e);this.watchDebounceTimers.clear(),this.livenessTimer&&(clearTimeout(this.livenessTimer),this.livenessTimer=null),this.watcher&&(this.stage="watcher.close",await this.watcher.close(),this.watcher=null),this.stage="restartWorkChain",await this.restartWorkChain,this.stage="closureBuild",await this.closureBuild,this.stage="removeAlias",await Promise.all([...this.registeredAliases].map(e=>this.proxy.removeAlias(e))),this.registeredAliases.clear(),this.turboWatch&&(this.stage="turboWatch.kill",await this.turboWatch.kill(),this.turboWatch=null),this.uiDev&&(this.stage="uiDev.kill",await this.uiDev.kill(),this.uiDev=null);for(let{app:e,server:t}of this.appServers){this.stage=`server.close(${e.name})`;try{await t.close()}catch{}this.removeDevContextFragment(e)}for(let{app:e}of this.failedApps)this.heldLocalPkgs.has(e.packageName)&&this.removeDevContextFragment(e);this.intercept?.uninstall(),this.renderer.log(`\u2713 dev stopped \xB7 logs \u2192 ${Z(this.sink.dir)}`),this.sink.close(),te===this.sink&&(te=null),this.stage="done"}},ir=n=>(n.tty??!!v.stdout.isTTY)&&!n.json,zn=async n=>{if(!ir(n))return;let{PersistentInkDevUi:e}=await import("./persistent-ink-dev-ui-ZSOS3CF4.js"),{createSafeStream:t}=await import("./safe-stderr-ECYA6DRR.js");return new e({appendLog:nr,verbose:n.verbose??!1,stdout:t(Rt())})};async function sr(n={}){let e=await zn(n),t=new ke(n,void 0,void 0,void 0,void 0,e);try{await t.start()}catch(r){throw await t.shutdown().catch(()=>{}),r}return t}import*as Re from"node:path";import qn from"node:process";var Ae=(n,e,t,r)=>{if(n.length<=1)return;let i=Math.ceil(n.length/2),s=n.slice(0,i),o=n.slice(i);r.push({from:t,direction:e%2===0?"horizontal":"vertical",command:o[0]});let a=r.length;Ae(s,e+1,t,r),Ae(o,e+1,a,r)},or=n=>{if(n.length===0)throw new Error("buildOrcaSplitPlan: at least one command is required");let e=[];return Ae(n,0,0,e),e};var Yn=(n,e)=>n.map(({app:t,targets:r})=>`pnpm exec infra-kit dev ${r&&r.length>0?`--target=${r.join(",")}`:`--app=${t}`}${e?"":" --no-watch"}`),Xn=n=>{let e=new Map;for(let t of Object.keys(n?.apps??{})){let r=t.split("/")[0];r===void 0||r==="*"||e.set(r,[...e.get(r)??[],t])}return e},Qn=(n,e)=>{let t=M(n);return e?t.filter(r=>e.includes(r.name)):t},Zn=async(n,e)=>{let t=await Qe();if(t==="absent")return"Orca is not installed";if(t==="unreachable")return"Orca is not running";let r=Re.basename(n);return(await Ze(n)).registered?await et(n,e)?null:`this worktree is hidden in Orca's sidebar \u2014 open Orca \u2192 ${r} \u2192 "hidden worktrees" \u2192 Show`:`${r} is not registered in Orca \u2014 run \`infra-kit worktrees add\` once, it offers to register it`},ei=async(n,e)=>(await L(["terminal","split","--terminal",n[e.from],"--direction",e.direction,"--command",e.command])).split.handle,ar=async n=>{try{await L(["terminal","close","--terminal",n[0],"--tab"]);return}catch(e){T.debug({error:e},"orca: tab close refused, closing panes one by one")}for(let e of n)try{await L(["terminal","close","--terminal",e])}catch(t){T.debug({handle:e,error:t},"orca: pane already gone")}},ti=async(n,e,t)=>{let i=[(await L(["terminal","create","--worktree",`path:${n}`,"--title",e,"--command",t[0]])).terminal.handle];try{for(let s of or(t))i.push(await ei(i,s))}catch(s){throw await ar(i),s}return i},ri=(n,e,t)=>{T.info(`\u{1F9E9} Opened Orca tab "${t}" with ${n.length} pane(s):`),n.forEach((r,i)=>{T.info(` \u2022 ${r.name} (${e[i]})`)})},ni=(n,e)=>{W({onSignal:async t=>{T.info(`
33
- Received ${t}, closing Orca tab "${e}"...`),await ar(n)}})},lr=async n=>{let e=O(qn.cwd()),t=Qn(e,H(n.include));if(t.length===0)return{fallback:"no API apps to open a pane for (panes are backend-only)"};let r=await Zn(await Ue(e),e);if(r!==null)return{fallback:r};let i=Xn(n.presetDef),s=Yn(t.map(u=>({app:u.name,targets:i.get(u.name)})),n.watch??!1),o=`${Re.basename(e)} dev`,a=await ti(e,o,s);ri(t,s,o),ni(a,o);let l=setInterval(()=>{l.refresh()},2**30);return await new Promise(()=>{}),"ran"};var Ee=1,oi=2e4,ai=n=>{if(n!=null){for(let e of n)if(Ge(e)!==null)throw new Error(`infra-kit dev: invalid --target "${e}" (expected "<app>/api" or "<app>/ui").`);return{apps:Object.fromEntries(n.map(e=>[e,{}]))}}},ur=n=>{if(n==null)return null;let e=n.split(",").map(t=>t.trim()).filter(Boolean);return e.length>0?e:null},dr=n=>({watch:n.watch??!0,include:ur(n.app),preset:n.preset,presetDef:ai(ur(n.target)),orca:n.orca??!1,self:n.self??!1,verbose:n.verbose??!1,routes:n.routes??!1,uiHealth:n.uiHealth??!0,reuse:n.reuse??!1,...n.reuse?{livenessIntervalMs:1e3}:{}}),li=n=>!n.self||n.include?n:{...n,include:[He(k.cwd())]},pr=(n,e)=>{let t=setTimeout(n,e);return t.unref(),()=>{clearTimeout(t)}},ui=(n=pr)=>{let e=!1;return(t,r)=>{if(e)return()=>{};e=!0;let i=n(t,r);return()=>{e=!1,i()}}},cr=(n,e,t)=>{n(),e(t)},di=({getRunner:n,forceReap:e=N,exit:t=s=>{k.exit(s)},setTimer:r=pr,deadlineMs:i=oi})=>{let s=!1;return o=>{if(s)return;s=!0;let a=n();if(a==null){cr(e,t,Ee);return}a.fileFault(`
31
+ \`infra-kit doctor\` checks the daemon and CA trust state.`)}async registerAppAlias(e,t,r){let i=be(e,t);if(!await this.proxy.registerAlias(i,r))throw new Error(`infra-kit dev: portless refused the alias "${i}" \u2192 127.0.0.1:${r}.`);return this.registeredAliases.add(i),`${i}.localhost`}async startOneApp(e){this.renderer.narrate(`\u{1F504} Starting ${e.name}...`);let t=new X({controllersPath:e.path,prefixUrl:e.prefixUrl,port:e.preferredPort,appName:e.name,serviceTag:`${e.name}/api`,onRequestLog:({method:s,path:o,status:a,ms:l})=>{if(o==="/__health")return;let u=`${e.name}/api`,d=`${s} ${o} ${a} ${l}ms`;this.sink.write(u,d,{level:a>=500?"error":"info"});let h=this.reqTimes.get(u);h?h.push(Date.now()):this.reqTimes.set(u,[Date.now()])}}),r=await t.start(),i;try{i=await this.registerAppAlias(e.packageName,e.path,r)}catch(s){throw await t.close().catch(()=>{}),s}try{this.writeDevContextFragment(e,r,i)}catch(s){this.renderer.log(`\u26A0\uFE0F Failed to write dev-context fragment for ${e.name}: ${String(s)}`,"warn")}return this.renderer.narrate(`\u2705 ${e.name} started on port ${r}`),{server:t,boundPort:r,alias:i}}holdFailedBackendsLocal(){for(let{app:e}of this.failedApps)try{this.writeDevContextFragment(e,0,`${be(e.packageName,e.path)}.localhost`),this.heldLocalPkgs.add(e.packageName)}catch(t){this.renderer.log(`\u26A0\uFE0F Failed to hold ${e.name}'s routes local: ${String(t)}`,"warn")}}writeDevContextFragment(e,t,r){let i={v:Sn,package:e.packageName,port:t,pid:g.pid,writtenAt:Date.now(),release:oe(e.path),alias:r,origin:`https://${r}`,env:g.env[$e]??""},s=f.join(this.devContextDir,`${e.name}.json`),o=f.join(this.devContextDir,`${e.name}.json.${g.pid}.tmp`);m.mkdirSync(this.devContextDir,{recursive:!0}),m.writeFileSync(o,JSON.stringify(i,null,2)),m.renameSync(o,s)}removeDevContextFragment(e){m.rmSync(f.join(this.devContextDir,`${e.name}.json`),{force:!0})}scheduleRestartWork(e){let t=this.restartWorkChain.then(()=>e(),()=>e());return this.restartWorkChain=t.catch(()=>{}),t}async delayPortRelease(){await new Promise(e=>setTimeout(e,n.PORT_RELEASE_DELAY_MS))}restart(e,t=[]){return this.shuttingDown?Promise.resolve():this.scheduleRestartWork(async()=>{this.shuttingDown||(await this.runRestart(e,t),this.recoverLostSaves(this.triggeringBuilds(t)))})}resolveRestartTargets(e){return e.map(t=>({idx:this.appServers.findIndex(r=>r.app.name===t.name),app:t})).filter(t=>t.idx>=0||this.isFailedApp(t.app.name))}isFailedApp(e){return this.failedApps.some(t=>t.app.name===e)}promoteRecoveredApp(e,t){let r={app:e,...t,startedAt:Date.now(),restarts:0},i=`${e.name}/api`;this.healthEntry(i,"api"),this.appServers.push(r);let s=this.failedApps.findIndex(o=>o.app.name===e.name);return s>=0&&this.failedApps.splice(s,1),this.lastSummary&&(this.lastSummary={...this.lastSummary,endpoints:[...this.lastSummary.endpoints,{tag:i,url:_({prefixUrl:e.prefixUrl,alias:t.alias}),health:this.healthOf(i)}],failed:(this.lastSummary.failed??[]).filter(o=>o.tag!==i)}),r}async runRestart(e,t=[]){let r=this.resolveRestartTargets(e);if(r.length===0)return;let i=r.length===1?r[0].app.name:`${r.length} apps`,s=this.moduleGeneration?.budgetState()??"ok";if(s==="stop"){this.reportGenerationBudgetStop(me(g.env.INFRA_KIT_DEV_STOP_BYTES).stopBytes),this.shutdown().catch(()=>{}).finally(()=>{this.options.onExitRequest?.(1)}).catch(()=>{});return}s==="warn"&&this.reportGenerationBudgetWarn(),this.moduleGeneration?.bump(),this.renderer.log(`\u{1F504} Restarting ${i}...`),await Promise.all(r.map(async({idx:c})=>{if(!(c<0))try{await this.appServers[c].server.close()}catch(p){this.renderer.log(` Close warning: ${String(p)}`,"debug")}})),await this.delayPortRelease();let o=await Promise.all(r.map(async({idx:c,app:p})=>{try{let v=await this.startOneApp(p);if(v){if(c<0){let b=this.promoteRecoveredApp(p,v);return this.renderer.log(`\u2705 ${p.name} recovered \u2014 its routes are served locally again`,"info"),{app:p,entry:b}}let A=this.appServers[c],S={app:p,...v,startedAt:Date.now(),restarts:(A?.restarts??0)+1};return this.appServers[c]=S,{app:p,entry:S}}return{app:p,entry:null}}catch(v){return this.renderer.log(`\u274C Failed to ${c<0?"start":"restart"} ${p.name}: ${String(v)}`,"error"),{app:p,entry:null}}})),a=await Promise.all(o.map(async({app:c,entry:p})=>{let v=`${c.name}/api`;if(p==null)return this.markDown(v,"api"),this.refreshStatus(),{label:`${c.name} \u25CF down`,healthy:!1};let A={tag:v,port:p.boundPort,kind:"api"},S=await this.healthProbe(A);this.recordProbe(A,S),this.refreshStatus();let b=S==="ok";return{label:`${c.name}:${p.boundPort} ${b?"\u25CF up":"\u25CF down"}`,healthy:b}})),l=a.every(c=>c.healthy),u=a.map(c=>c.label).join(", "),h=o.some(c=>c.entry!==null)?this.resolveStaleFiles(t,r):[];if(h.length>0){this.renderer.log(`\u26A0\uFE0F Restarted ${u} \u2014 still serving the OLD ${Mt(h)}. Re-run \`infra-kit dev\` to pick this up.`,"warn");return}let y=this.describeBuildErrors(this.triggeringBuilds(t));if(y!==void 0){this.renderer.log(`\u26A0\uFE0F Restarted ${u} \u2014 ${y}`,"warn");return}this.renderer.log(`${l?"\u2705":"\u26A0\uFE0F "} Restarted ${u}`)}triggeringBuilds(e){let t=new Map;for(let r of e){let i=this.watchedDistDirs.find(o=>r===o||r.startsWith(o+f.sep));if(i===void 0)continue;let s=f.dirname(i);t.set(s,[...t.get(s)??[],r])}return[...t].flatMap(([r,i])=>{let s=G(r);return s&&it(s,i)?[{pkgDir:r,info:s}]:[]})}describeBuildErrors(e){let t=e.flatMap(({pkgDir:r,info:i})=>{let s=st(i,r);return s?[ot(de(r),s)]:[]});return t.length>0?t.join("; "):void 0}async recoverLostSaves(e){try{for(let{pkgDir:t,info:r}of e)await this.recoverLostSave(t,r)}catch(t){this.renderer.log(` Lost-save check failed: ${String(t)}`,"debug")}}async recoverLostSave(e,t){let r=ue(t,e);if(r.length===0){this.nudgedStaleness.delete(e);return}let i=r.map(({file:s,hash:o})=>`${s}:${o}`).join(`
32
+ `);this.nudgedStaleness.get(e)!==i&&(await new Promise(s=>{setTimeout(s,n.STALE_BUILD_GRACE_MS)}),!(this.shuttingDown||G(e)?.mtimeMs!==t.mtimeMs)&&(this.nudgedStaleness.set(e,i),await this.forceRebuild(e,r)))}async forceRebuild(e,t){let r=de(e),i=t.map(({file:a})=>f.relative(e,a)).join(", "),s=new Date;this.renderer.log(`\u{1F440} ${r}: last build missed a save of ${i}; forcing a rebuild`,"debug");for(let{file:a}of t)try{m.utimesSync(a,s,s)}catch{}try{await this.runBuild(`pnpm exec turbo run build --filter=${r} --env-mode=loose --output-logs=errors-only --no-update-notifier --force`,a=>{this.renderer.log(a,"debug")})}catch(a){this.renderer.log(` Forced rebuild of ${r} failed: ${String(a)}`,"debug")}let o=G(e);this.shuttingDown||!o||ue(o,e).length===0||this.renderer.log(`\u26A0\uFE0F ${r}: ${i} changed but a forced rebuild still did not compile it \u2014 the server may be serving an older save. Save the file again, or re-run \`infra-kit dev\`.`,"warn")}resolveStaleFiles(e,t){let r=this.moduleGeneration;return r?e.filter(i=>!r.bustedUnder(f.dirname(i))):e.filter(i=>!t.some(s=>Bt(i,s.app.path)))}async buildClosureMapSafe(e){try{return await lt(this.monorepoRoot,e,this.dryRunner)}catch(t){return this.renderer.log(`\u26A0\uFE0F Dependency-closure map unavailable (${String(t)}); package changes restart all apps`,"warn"),null}}reportEngineDeath(e,t,r){this.shuttingDown||this.renderer.log(`\u26A0\uFE0F ${e} ${r} \u2014 ${t}. Restart \`infra-kit dev\`.`,"warn")}markUiEngineDead(){if(!this.shuttingDown){for(let{tag:e,kind:t}of this.uiTargets())this.healthEntry(e,t).dead=!0;this.refreshStatus()}}markUiTaskFailed(e){if(this.shuttingDown)return;if(this.uiHealthEnabled()){if(this.healthOf(e)==="down")return;this.markDown(e,"ui")}let t=this.uiTagByPackage.size>1?"; other frontends are unaffected":"";this.renderer.log(`\u26A0\uFE0F ${e} failed to start \u2014 its \`dev\` task exited${t}`,"error"),this.refreshStatus(),this.options.reuse&&this.shutdown().then(()=>{this.options.onExitRequest?.(1)})}setupWatch(e,t){this.turboWatch=this.turboWatchFactory({depInclusive:e.map(l=>l.packageName),depClosure:t.map(l=>l.packageName),cwd:g.cwd(),logFile:this.sink.pathFor(xn),onUnexpectedExit:l=>{this.reportEngineDeath("Watch engine (`turbo watch build`)","file saves no longer rebuild",l)}}),this.renderer.narrate("\u{1F440} Watch mode: started `turbo watch build` engine; watching dist output");let r=Ye(e),i=L(this.monorepoRoot),s=[...r,...i];if(this.watchedDistDirs=s,s.length===0){this.renderer.log("\u26A0\uFE0F No app or package dist directories found to watch (were they built?)","warn");return}let o=g.env.DEV_SERVER_CHOKIDAR_POLL==="1",a=kn.watch(s,{ignoreInitial:!0,awaitWriteFinish:{stabilityThreshold:200,pollInterval:100},ignored:l=>l.endsWith(".tsbuildinfo")||l.endsWith(".map"),...o?{usePolling:!0,interval:400}:{}});this.watcher=a,o&&this.renderer.log("\u{1F440} chokidar: usePolling enabled (DEV_SERVER_CHOKIDAR_POLL=1)","debug");for(let l of["add","change","unlink"])a.on(l,u=>{this.handleDistChange(u,e,r,i)});this.renderer.narrate(`\u{1F440} Watching ${r.length} app dist + ${i.length} package dist dir(s) for changes...`),this.renderer.narrate(this.moduleGeneration?"\u{1F440} Watch reloads the whole local graph in place \u2014 shared packages and services included.":"\u{1F440} Watch reloads serverless handler entry files only; other modules need a re-run of `infra-kit dev`.")}handleDistChange(e,t,r,i){this.renderer.log(`\u{1F440} dist change detected: ${e}`,"debug");let s=Xe(e,r,i);if(s.kind==="package"){this.handlePackageDistChange(e,t,s.packageDir);return}let o=t.find(a=>f.join(a.path,"dist")===s.app);o&&(this.markChanged(o.name,e),this.scheduleDebounced(o.name,()=>this.restart([o],this.takeChanged(o.name))))}handlePackageDistChange(e,t,r){let i=ut(t,this.closureMap,r);if(i===null){this.markChanged("__packages__",e),this.scheduleDebounced("__packages__",()=>this.restart(t,this.takeChanged("__packages__")));return}for(let s of i)this.markChanged(s.name,e),this.scheduleDebounced(s.name,()=>this.restart([s],this.takeChanged(s.name)))}markChanged(e,t){let r=this.pendingChanges.get(e);r?r.add(t):this.pendingChanges.set(e,new Set([t]))}installModuleGeneration(){if(g.env.INFRA_KIT_DEV_NO_GENERATION==="1"){this.renderer.log("\u{1F440} Module generation disabled (INFRA_KIT_DEV_NO_GENERATION=1)","debug");return}if(!$t()){this.renderer.log(`\u26A0\uFE0F This Node (${g.version}) has no \`module.registerHooks\`, so only handler entry files reload in place.`,"warn");return}let e=me(g.env.INFRA_KIT_DEV_STOP_BYTES);this.moduleGeneration=Ct({root:m.realpathSync(this.monorepoRoot),selfDist:Ot(),stopBytes:e.stopBytes,stopEnabled:e.stopEnabled})}reportGenerationBudgetWarn(){let e=this.moduleGeneration?.generation()??0;this.renderer.log(`\u26A0\uFE0F Reload memory ${Pe(g.memoryUsage.rss())} after ${e} reload(s). Module records cannot be freed in-process, so this only grows \u2014 re-run \`infra-kit dev\` when convenient.`,"warn")}reportGenerationBudgetStop(e){let t=this.moduleGeneration?.generation()??0;this.renderer.log(`\u26D4 Reload memory ${Pe(g.memoryUsage.rss())} exceeds the ${Pe(e)} budget after ${t} reload(s) \u2014 stopping. Module records cannot be freed in-process. Re-run \`infra-kit dev\` to continue, or set INFRA_KIT_DEV_STOP_BYTES to raise the budget (or \`off\` to disable the stop).`,"error")}takeChanged(e){let t=this.pendingChanges.get(e);return this.pendingChanges.delete(e),t?[...t]:[]}scheduleDebounced(e,t){if(this.shuttingDown)return;let r=this.watchDebounceTimers.get(e);r&&clearTimeout(r);let i=setTimeout(()=>{this.watchDebounceTimers.delete(e),!this.shuttingDown&&t().catch(s=>{this.renderer.log(`Restart error (${e}): ${String(s)}`,"error")})},n.WATCH_DEBOUNCE_MS);this.watchDebounceTimers.set(e,i)}uiHealthEnabled(){return(this.options.uiHealth??!0)&&g.env.INFRA_KIT_NO_UI_HEALTH!=="1"}apiTargets(){return this.appServers.map(({app:e,boundPort:t})=>({tag:`${e.name}/api`,port:t,kind:"api"}))}uiTargets(){return this.uiHealthEnabled()?Object.entries(this.uiPortMap).map(([e,{port:t}])=>({tag:this.uiTagByPackage.get(e)??`${e}/ui`,port:t,kind:"ui"})):[]}healthEntry(e,t){let r=this.health.get(e);if(r)return r;let i={kind:t,failures:0,foreignStreak:0,everUp:!1,dead:!1,unverified:!1};return this.health.set(e,i),i}recordProbe(e,t){let r=this.healthEntry(e.tag,e.kind),i=n.LIVENESS_FAILURE_THRESHOLD;if(t==="ok"){let s=r.failures>=i;r.everUp=!0,r.failures=0,r.foreignStreak=0,r.unverified=!1,s&&this.renderer.log(`\u2705 ${e.tag} recovered`);return}if(We(t)&&e.kind==="ui"){r.foreignStreak+=1,r.unverified=r.foreignStreak>=i,r.foreignStreak===i&&this.renderer.log(`\u26A0\uFE0F ${e.tag}: ${Ge(e.port,t)}`,"warn");return}if(r.foreignStreak=0,r.failures+=1,e.kind==="ui"&&r.failures>=i&&(r.unverified=!1),r.failures===i){let s=e.kind==="api"?"/__health not responding":"not answering vite's ping";this.renderer.log(`\u26A0\uFE0F ${e.tag} unhealthy (${s})`,"warn")}}markDown(e,t){let r=this.healthEntry(e,t);r.dead=!0,r.failures=n.LIVENESS_FAILURE_THRESHOLD,r.foreignStreak=0,r.unverified=!1}healthOf(e){let t=this.health.get(e);return t==null?"unknown":t.unverified?"unverified":t.everUp?t.failures>=n.LIVENESS_FAILURE_THRESHOLD?"down":"ok":this.neverUpState(t)}neverUpState(e){return e.kind==="api"||e.dead||e.failures>=n.NEVER_UP_DOWN_THRESHOLD?"down":"starting"}startLivenessMonitor(){this.scheduleLivenessTick()}scheduleLivenessTick(){let e=this.options.livenessIntervalMs??n.LIVENESS_INTERVAL_MS;this.livenessTimer=setTimeout(()=>{this.livenessTick().finally(()=>{this.shuttingDown||this.scheduleLivenessTick()}).catch(()=>{})},e),this.livenessTimer.unref()}refreshStatus(){let e=this.lastSummary;if(e==null)return;let t=Date.now(),r=new Map(this.appServers.map(i=>[`${i.app.name}/api`,i]));this.pruneRequestWindow(t),this.renderer.refresh?.({...e,sessionUptimeMs:t-this.readyAt,degraded:this.degradedRows(),uiRefs:e.uiRefs.map(i=>({...i,errors:this.announceFirstError(i.tag)})),endpoints:e.endpoints.map(i=>{let s=r.get(i.tag);return{...i,health:this.healthOf(i.tag),uptimeMs:s?t-s.startedAt:void 0,restarts:s?.restarts,rpm:(this.reqTimes.get(i.tag)??[]).length,errors:this.announceFirstError(i.tag)}})})}announceFirstError(e){let{errors:t}=this.sink.statsFor(e);return t>0&&!this.firstErrorLogged.has(e)&&(this.firstErrorLogged.add(e),this.renderer.log(`\u26A0\uFE0F ${e} reported its first error \u2192 ${Z(this.sink.pathFor(e))}`,"warn")),t}degradedRows(){if(this.degradedRoutes.length===0)return[];let e=new Set(this.appServers.map(({app:t})=>t.packageName));return this.degradedRoutes.filter(t=>!e.has(t.packageName)).map(t=>({route:t.route,tag:`${t.uiApp}/ui`,fallback:t.fallback,target:t.cloudTarget}))}pruneRequestWindow(e){let t=e-6e4;for(let[r,i]of this.reqTimes)this.reqTimes.set(r,i.filter(s=>s>t))}async livenessTick(){this.shuttingDown||(await Promise.all([...this.apiTargets(),...this.uiTargets()].map(e=>this.probeOne(e))),this.announceServingOnce(),this.refreshStatus())}announceServingOnce(){this.servingAnnounced||this.lastSummary==null||this.failedApps.length>0||!this.lastSummary.endpoints.every(t=>{let r=this.healthOf(t.tag);return r==="ok"||r==="unknown"})||(this.servingAnnounced=!0,this.renderer.log(`${Jt} ${this.targetLabel}`))}async findServedApps(e,t){let r=Ce(),i=[...e.map(o=>({tag:`${o.name}/api`,kind:"api",port:Pn(o.path).info.get(o.packageName)?.port??0})),...t.map(o=>{let a=be(o.packageName,o.path);return{tag:`${o.name}/ui`,kind:"ui",port:r.find(l=>l.name===a||l.name===`${a}.localhost`)?.port??0}})],s=await Promise.all(i.map(async o=>o.port>0&&await this.healthProbe(o)==="ok"));return i.filter((o,a)=>s[a]).map(o=>o.tag)}async endsBeforeBoot(e,t){if(e.length===0&&t.length===0)return this.renderer.log("\u26A0\uFE0F No API or UI apps to run","warn"),!0;if(!this.options.reuse)return!1;let r=await this.findServedApps(e,t);return r.length>0?(this.idleOverRunningSession(r),!0):(await this.assertCloudRoutesHaveEnv(e,t),!1)}assertNoBootFailuresWhenReusing(e){let[t]=this.failedApps;if(!(!this.options.reuse||!t))throw new Error(`infra-kit dev: ${this.failedApps.length} of ${e} app(s) failed to start. First failure (${t.app.name}): ${t.reason}`)}idleOverRunningSession(e){this.reusing=!0,this.keepAlive=setInterval(()=>{},6e4),this.renderer.log(`\u267B\uFE0F Reusing the dev server this worktree already runs (${e.join(", ")})`),this.servingAnnounced=!0,this.renderer.log(`${Jt} ${e.join(", ")}`)}async assertCloudRoutesHaveEnv(e,t){let r=new Set(e.map(s=>s.packageName)),i=[];for(let s of t){let o=await zt(s.path),a=Object.entries(o?.dev?.proxy?.routes??{}).filter(([,l])=>!l.from.includes("local")||!r.has(l.packageName));if(a.length>0&&!qt(o?.deployedUrlEnv).url){let l=a.map(([u])=>u);i.push(`${s.name}/ui ${l.join(", ")} (${o?.deployedUrlEnv??"no deployedUrlEnv"})`)}}if(i.length!==0)throw new se(void 0,{operation:"start the dev servers",remediation:"load an environment first (`infra-kit env-load -c dev`)",stderrExcerpt:`these routes proxy to the deployed app, whose URL is not loaded: ${i.join("; ")}`})}async probeOne(e){let t=await this.healthProbe(e);this.shuttingDown||this.recordProbe(e,t)}async printReady(e,t,r,i){let s=Date.now()-r;await Promise.all(this.apiTargets().map(c=>this.probeOne(c)));let o=this.appServers.map(({app:c,alias:p})=>({tag:`${c.name}/api`,url:_({prefixUrl:c.prefixUrl,alias:p}),health:this.healthOf(`${c.name}/api`)})),a=new Map(this.appServers.map(({app:c,alias:p})=>[c.packageName,_({prefixUrl:c.prefixUrl,alias:p})]));this.proxyRoutes=ct({uis:this.launchedUis,running:new Set(this.appServers.map(({app:c})=>c.packageName)),localOrigin:c=>a.get(c),held:this.heldLocalPkgs}),this.uiPortMap={};let l=[],u=[];for(let c of t){let p=await this.assignUiPort(c),v=`${c.name}/ui`;p!=null?l.push({tag:v,url:_({prefixUrl:"",alias:p.alias}),health:this.seedUiHealth(v),proxies:this.proxiesFor(c.name)}):u.push({tag:v,proxies:this.proxiesFor(c.name)})}let d=this.options.watch??!1,h=e.length+t.length,y=L(this.monorepoRoot).length;this.lastSummary={target:i,watch:d,release:oe(g.cwd()),elapsedMs:s,endpoints:[...o,...l],uiRefs:u,failed:this.failedApps.map(({app:c,reason:p})=>({tag:`${c.name}/api`,reason:p})),degraded:this.degradedRows(),watchSummary:`${h} app${h===1?"":"s"} \xB7 ${y} package${y===1?"":"s"}`,logPath:Z(this.sink.dir),logHref:this.sink.dir},this.readyAt=Date.now(),this.renderer.ready(this.lastSummary),this.refreshStatus()}async assignUiPort(e){if(!e.managedPort)return null;let t;try{t=await On()}catch{return null}let r=await this.registerAppAlias(e.packageName,e.path,t);return this.uiPortMap[e.packageName]={port:t,alias:r},this.uiTagByPackage.set(e.packageName,`${e.name}/ui`),{port:t,alias:r}}seedUiHealth(e){return this.uiHealthEnabled()?(this.healthEntry(e,"ui"),this.healthOf(e)):"unknown"}printRouteDump(){if(this.appServers.length!==0){this.renderer.log("\u{1F5FA}\uFE0F Registered routes:");for(let{app:e,server:t}of this.appServers){let r=t.getRegisteredRoutes();this.renderer.log(` ${e.name} (${r.length}): ${r.length>0?r.join(", "):"(none)"}`)}}}reportFault(e){this.sink.write(K()??ee,e,{level:"error"}),!this.shuttingDown&&this.renderer.log(e,"error",{tee:!1})}fileFault(e){this.sink.write(K()??ee,e,{level:"error"})}get shutdownStage(){return this.stage}shutdown(){return this.teardown!=null?this.teardown:(this.teardown=this.doShutdown(),this.teardown.catch(()=>{}),this.teardown)}async doShutdown(){if(this.shuttingDown=!0,this.stage="starting",this.renderer.dispose(),this.intercept?.uninstall(),this.reusing){this.keepAlive&&clearInterval(this.keepAlive),this.keepAlive=null,this.renderer.log("\u267B\uFE0F Left the reused dev server running"),this.stage="done";return}this.renderer.log("\u{1F6D1} Shutting down all servers...");for(let e of this.watchDebounceTimers.values())clearTimeout(e);this.watchDebounceTimers.clear(),this.livenessTimer&&(clearTimeout(this.livenessTimer),this.livenessTimer=null),this.watcher&&(this.stage="watcher.close",await this.watcher.close(),this.watcher=null),this.stage="restartWorkChain",await this.restartWorkChain,this.stage="closureBuild",await this.closureBuild,this.stage="removeAlias",await Promise.all([...this.registeredAliases].map(e=>this.proxy.removeAlias(e))),this.registeredAliases.clear(),this.turboWatch&&(this.stage="turboWatch.kill",await this.turboWatch.kill(),this.turboWatch=null),this.uiDev&&(this.stage="uiDev.kill",await this.uiDev.kill(),this.uiDev=null);for(let{app:e,server:t}of this.appServers){this.stage=`server.close(${e.name})`;try{await t.close()}catch{}this.removeDevContextFragment(e)}for(let{app:e}of this.failedApps)this.heldLocalPkgs.has(e.packageName)&&this.removeDevContextFragment(e);this.intercept?.uninstall(),this.renderer.log(`\u2713 dev stopped \xB7 logs \u2192 ${Z(this.sink.dir)}`),this.sink.close(),te===this.sink&&(te=null),this.stage="done"}},er=n=>(n.tty??!!g.stdout.isTTY)&&!n.json,Wn=async n=>{if(!er(n))return;let{PersistentInkDevUi:e}=await import("./persistent-ink-dev-ui-ZSOS3CF4.js"),{createSafeStream:t}=await import("./safe-stderr-ECYA6DRR.js");return new e({appendLog:Zt,verbose:n.verbose??!1,stdout:t(Pt())})};async function tr(n={}){let e=await Wn(n),t=new ke(n,void 0,void 0,void 0,void 0,e);try{await t.start()}catch(r){throw await t.shutdown().catch(()=>{}),r}return t}import*as De from"node:path";import Gn from"node:process";var Ae=(n,e,t,r)=>{if(n.length<=1)return;let i=Math.ceil(n.length/2),s=n.slice(0,i),o=n.slice(i);r.push({from:t,direction:e%2===0?"horizontal":"vertical",command:o[0]});let a=r.length;Ae(s,e+1,t,r),Ae(o,e+1,a,r)},rr=n=>{if(n.length===0)throw new Error("buildOrcaSplitPlan: at least one command is required");let e=[];return Ae(n,0,0,e),e};var Vn=(n,e)=>n.map(({app:t,targets:r})=>`pnpm exec infra-kit dev ${r&&r.length>0?`--target=${r.join(",")}`:`--app=${t}`}${e?"":" --no-watch"}`),Kn=n=>{let e=new Map;for(let t of Object.keys(n?.apps??{})){let r=t.split("/")[0];r===void 0||r==="*"||e.set(r,[...e.get(r)??[],t])}return e},Jn=(n,e)=>{let t=j(n);return e?t.filter(r=>e.includes(r.name)):t},zn=async(n,e)=>{let t=await Me();if(t==="absent")return"Orca is not installed";if(t==="unreachable")return"Orca is not running";let r=De.basename(n);return(await He(n)).registered?await je(n,e)?null:`this worktree is hidden in Orca's sidebar \u2014 open Orca \u2192 ${r} \u2192 "hidden worktrees" \u2192 Show`:`${r} is not registered in Orca \u2014 run \`infra-kit worktrees add\` once, it offers to register it`},qn=async(n,e)=>(await I(["terminal","split","--terminal",n[e.from],"--direction",e.direction,"--command",e.command])).split.handle,nr=async n=>{try{await I(["terminal","close","--terminal",n[0],"--tab"]);return}catch(e){T.debug({error:e},"orca: tab close refused, closing panes one by one")}for(let e of n)try{await I(["terminal","close","--terminal",e])}catch(t){T.debug({handle:e,error:t},"orca: pane already gone")}},Yn=async(n,e,t)=>{let i=[(await I(["terminal","create","--worktree",`path:${n}`,"--title",e,"--command",t[0]])).terminal.handle];try{for(let s of rr(t))i.push(await qn(i,s))}catch(s){throw await nr(i),s}return i},Xn=(n,e,t)=>{T.info(`\u{1F9E9} Opened Orca tab "${t}" with ${n.length} pane(s):`),n.forEach((r,i)=>{T.info(` \u2022 ${r.name} (${e[i]})`)})},Qn=(n,e)=>{H({onSignal:async t=>{T.info(`
33
+ Received ${t}, closing Orca tab "${e}"...`),await nr(n)}})},ir=async n=>{let e=N(Gn.cwd()),t=Jn(e,W(n.include));if(t.length===0)return{fallback:"no API apps to open a pane for (panes are backend-only)"};let r=await zn(await _e(e),e);if(r!==null)return{fallback:r};let i=Kn(n.presetDef),s=Vn(t.map(u=>({app:u.name,targets:i.get(u.name)})),n.watch??!1),o=`${De.basename(e)} dev`,a=await Yn(e,o,s);Xn(t,s,o),Qn(a,o);let l=setInterval(()=>{l.refresh()},2**30);return await new Promise(()=>{}),"ran"};var Re=1,ti=2e4,ri=n=>{if(n!=null){for(let e of n)if(Qe(e)!==null)throw new Error(`infra-kit dev: invalid --target "${e}" (expected "<app>/api" or "<app>/ui").`);return{apps:Object.fromEntries(n.map(e=>[e,{}]))}}},sr=n=>{if(n==null)return null;let e=n.split(",").map(t=>t.trim()).filter(Boolean);return e.length>0?e:null},or=n=>({watch:n.watch??!0,include:sr(n.app),app:n.appName,presetDef:ri(sr(n.target)),orca:n.orca??!1,self:n.self??!1,verbose:n.verbose??!1,routes:n.routes??!1,uiHealth:n.uiHealth??!0,reuse:n.reuse??!1,...n.reuse?{livenessIntervalMs:1e3}:{}}),ni=n=>!n.self||n.include?n:{...n,include:[qe(k.cwd())]},ar=(n,e)=>{let t=setTimeout(n,e);return t.unref(),()=>{clearTimeout(t)}},ii=(n=ar)=>{let e=!1;return(t,r)=>{if(e)return()=>{};e=!0;let i=n(t,r);return()=>{e=!1,i()}}},lr=(n,e,t)=>{n(),e(t)},si=({getRunner:n,forceReap:e=C,exit:t=s=>{k.exit(s)},setTimer:r=ar,deadlineMs:i=ti})=>{let s=!1;return o=>{if(s)return;s=!0;let a=n();if(a==null){lr(e,t,Re);return}a.fileFault(`
34
34
  \u2717 dev-server exiting: ${o}
35
- `);let l=r(()=>{e(),t(Ee)},i);a.shutdown().finally(()=>{l(),t(Ee)}).catch(()=>{})}},pi=["SIGINT","SIGTERM"],ci=({forceReap:n=N,exit:e=i=>{k.exit(i)},register:t=(i,s)=>{k.on(i,s)},unregister:r=(i,s)=>{k.off(i,s)}}={})=>{let i=new Map;for(let s of pi){let o=()=>{cr(n,e,Xe(s))};i.set(s,o),t(s,o)}return{remove:()=>{for(let[s,o]of i)r(s,o)}}},xe=async n=>{let e=li(n);if(e.orca){let l=await lr(e);if(l==="ran")return;k.stdout.write(`Orca: ${l.fallback}; falling back to single-terminal dev
36
- `)}let t=null,r=ui(),i=di({getRunner:()=>t,setTimer:r}),s=Pt({onDeath:(l,u)=>{i(`stdio unwritable: ${l} ${u.code}`)}}),o=ci();try{t=await sr({...e,onExitRequest:l=>{k.exit(l)}})}finally{o.remove()}let a=t;ot({onFault:(l,u)=>{a.reportFault(U(l,u))},isTerminalDead:s.isDead,fileFault:(l,u)=>{a.fileFault(U(l,u,!1))},onFatal:i}),W({onSignal:async l=>{he(`
35
+ `);let l=r(()=>{e(),t(Re)},i);a.shutdown().finally(()=>{l(),t(Re)}).catch(()=>{})}},oi=["SIGINT","SIGTERM"],ai=({forceReap:n=C,exit:e=i=>{k.exit(i)},register:t=(i,s)=>{k.on(i,s)},unregister:r=(i,s)=>{k.off(i,s)}}={})=>{let i=new Map;for(let s of oi){let o=()=>{lr(n,e,Be(s))};i.set(s,o),t(s,o)}return{remove:()=>{for(let[s,o]of i)r(s,o)}}},Ee=async n=>{let e=ni(n);if(e.orca){let l=await ir(e);if(l==="ran")return;k.stdout.write(`Orca: ${l.fallback}; falling back to single-terminal dev
36
+ `)}let t=null,r=ii(),i=si({getRunner:()=>t,setTimer:r}),s=yt({onDeath:(l,u)=>{i(`stdio unwritable: ${l} ${u.code}`)}}),o=ai();try{t=await tr({...e,onExitRequest:l=>{k.exit(l)}})}finally{o.remove()}let a=t;rt({onFault:(l,u)=>{a.reportFault(U(l,u))},isTerminalDead:s.isDead,fileFault:(l,u)=>{a.fileFault(U(l,u,!1))},onFatal:i}),H({onSignal:async l=>{ce(`
37
37
  Received ${l}, shutting down dev-server...
38
- `),await a.shutdown()},describeStall:()=>a.shutdownStage,fileReport:l=>{a.fileFault(l)},setTimer:r})},hi=(n,e,t)=>!n.preset&&!n.app&&!n.self&&!n.orca&&n.watch==null&&!n.verbose&&!n.routes&&!n.reuse&&e&&!t,gi=(n,e)=>({watch:n.watch,orca:n.orca,include:n.include??null,preset:n.preset,presetDef:n.presetDef,self:!1,verbose:!1,routes:!1,uiHealth:e.uiHealth??!0}),ho=async(n,e,t)=>{if(hi(n,e,t)){let{runDevWizard:r}=await import("./dev-wizard-run-ZRBSMQIB.js"),i=Promise.all([import("./persistent-ink-dev-ui-ZSOS3CF4.js"),import("./safe-stderr-ECYA6DRR.js")]).catch(()=>{}),s;try{s=await r()}catch(o){if(it(o))return;throw o}if(s==null)return;await i,await xe({...gi(s,n),tty:e,json:t});return}await xe({...dr(n),tty:e,json:t})},fi=async n=>{let e=new ii;e.name("infra-kit-dev-server").description("Run local dev servers for the apps in a named devServersPresets preset (or all apps)").argument("[preset]","Named preset from devServersPresets (omit to run every app)").option("-w, --watch","Rebuild and restart on file save (the default)").option("--no-watch","Build once and never restart on save").option("--app <names>","Further narrow to these app folder names (comma-separated)").option("--orca","Run each app in its own Orca pane (one tab, N panes; falls back to single terminal when Orca is not running)").option("--self","Run only the app of the current directory (infer from cwd; use inside apps/<app>/\u2026)").option("-V, --verbose","Print full boot narration (default: quiet; full detail always in the session log)").option("--routes","Print each app\u2019s registered METHOD /path routes at startup (default: off)"),e.parse(n),await xe(dr({...e.opts(),preset:e.args[0]}))};import.meta.url===si(k.argv[1]??"").href&&fi(k.argv).catch(n=>{console.error(n instanceof Error?n.message:String(n)),k.exit(1)});export{di as createFatalHandler,ui as createSharedDeadlineTimer,ci as installBootSignalGuard,xe as runDevServer,ho as runDevServerCli,hi as shouldRunWizard,dr as toDevServerOptions};
38
+ `),await a.shutdown()},describeStall:()=>a.shutdownStage,fileReport:l=>{a.fileFault(l)},setTimer:r})},li=(n,e,t)=>!n.appName&&!n.app&&!n.self&&!n.orca&&n.watch==null&&!n.verbose&&!n.routes&&!n.reuse&&e&&!t,ui=(n,e)=>({watch:n.watch,orca:n.orca,include:n.include??null,presetDef:n.presetDef,self:!1,verbose:!1,routes:!1,uiHealth:e.uiHealth??!0}),oo=async(n,e,t)=>{if(li(n,e,t)){let{runDevWizard:r}=await import("./dev-wizard-run-LZYQQOXT.js"),i=Promise.all([import("./persistent-ink-dev-ui-ZSOS3CF4.js"),import("./safe-stderr-ECYA6DRR.js")]).catch(()=>{}),s;try{s=await r()}catch(o){if(Ke(o))return;throw o}if(s==null)return;await i,await Ee({...ui(s,n),tty:e,json:t});return}await Ee({...or(n),tty:e,json:t})},di=async n=>{let e=new Zn;e.name("infra-kit-dev-server").description("Run local dev servers for an app (or all apps)").argument("[app]","An app \u2014 its api/ui plus the backends its routes can run locally (omit to run every app)").option("-w, --watch","Rebuild and restart on file save (the default)").option("--no-watch","Build once and never restart on save").option("--app <names>","Further narrow to these app folder names (comma-separated)").option("--orca","Run each app in its own Orca pane (one tab, N panes; falls back to single terminal when Orca is not running)").option("--self","Run only the app of the current directory (infer from cwd; use inside apps/<app>/\u2026)").option("-V, --verbose","Print full boot narration (default: quiet; full detail always in the session log)").option("--routes","Print each app\u2019s registered METHOD /path routes at startup (default: off)"),e.parse(n),await Ee(or({...e.opts(),appName:e.args[0]}))};import.meta.url===ei(k.argv[1]??"").href&&di(k.argv).catch(n=>{console.error(n instanceof Error?n.message:String(n)),k.exit(1)});export{si as createFatalHandler,ii as createSharedDeadlineTimer,ai as installBootSignalGuard,Ee as runDevServer,oo as runDevServerCli,li as shouldRunWizard,or as toDevServerOptions};
39
39
  //# sourceMappingURL=dev-server.js.map