@tdk-landscape/tdk-cli-core 1.3.155 → 1.3.157
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/dist/commands/doctor.js +103 -1
- package/cli/dist/commands/up.js +27 -4
- package/cli/dist/generator/template-engine.js +23 -0
- package/cli/dist/utils/doctor-crashloop.js +161 -0
- package/cli/dist/utils/doctor-fix.js +231 -0
- package/cli/dist/utils/tilt-port-owner.js +49 -0
- package/cli/dist-bundle/chunk-O2SVXNG5.js +377 -0
- package/cli/dist-bundle/cli.js +320 -370
- package/cli/dist-bundle/{cold-preflight-3RDK76RY.js → cold-preflight-OQUN7OFG.js} +1 -1
- package/cli/package.json +1 -1
- package/discovery/manifest/loading.star +7 -1
- package/engine/topologies/platform/docker/build/golden_image_dockerfile.star +5 -4
- package/engine/topologies/platform/docker/compose/compose.star +10 -5
- package/engine/topologies/platform/docker/generators/golden_docker_generator_v2.star +1 -1
- package/engine/topologies/platform/docker/layers/l1_base_layers.star +1 -1
- package/engine/topologies/platform/docker/layers/l3_builder_layers.star +1 -1
- package/engine/topologies/platform/docker/layers/l4_runtime_layers.star +14 -1
- package/engine/topologies/tilt/manifest/constants.star +1 -1
- package/package.json +1 -1
- package/cli/dist-bundle/chunk-XTSHWDDE.js +0 -322
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import{createRequire as __tdkRequire}from'node:module';const require=__tdkRequire(import.meta.url);
|
|
2
|
+
import{c as He,e as qn}from"./chunk-42IVF3YT.js";import{a as bt}from"./chunk-6EA4DGCY.js";import{execFileSync as zo}from"node:child_process";import{readFileSync as bl}from"node:fs";import{join as xl}from"node:path";import{execFileSync as Mu,execSync as Bo}from"node:child_process";import{existsSync as U,readdirSync as Uu,readFileSync as ke}from"node:fs";import{join as A,normalize as Fn,relative as Ln}from"node:path";function zn(e,t,n){let r=e.indexOf(t);if(r===-1)return e;let s=t.length,o=0,i="";do i+=e.slice(o,r)+t+n,o=r+s,r=e.indexOf(t,o);while(r!==-1);return i+=e.slice(o),i}function Yn(e,t,n,r){let s=0,o="";do{let i=e[r-1]==="\r";o+=e.slice(s,i?r-1:r)+t+(i?`\r
|
|
3
|
+
`:`
|
|
4
|
+
`)+n,s=r+1,r=e.indexOf(`
|
|
5
|
+
`,s)}while(r!==-1);return o+=e.slice(s),o}var Xn=(e=0)=>t=>`\x1B[${t+e}m`,xt=(e=0)=>t=>`\x1B[${38+e};5;${t}m`,wt=(e=0)=>(t,n,r)=>`\x1B[${38+e};2;${t};${n};${r}m`,Xo=e=>`\x1B[58;5;${e<90?e-30:e-90+8}m`,E={modifier:{reset:[0,0],bold:[1,22],dim:[2,22],italic:[3,23],underline:[4,24],underlineDouble:["4:2",24],underlineCurly:["4:3",24],underlineDotted:["4:4",24],underlineDashed:["4:5",24],overline:[53,55],inverse:[7,27],hidden:[8,28],strikethrough:[9,29]},color:{black:[30,39],red:[31,39],green:[32,39],yellow:[33,39],blue:[34,39],magenta:[35,39],cyan:[36,39],white:[37,39],blackBright:[90,39],gray:[90,39],grey:[90,39],redBright:[91,39],greenBright:[92,39],yellowBright:[93,39],blueBright:[94,39],magentaBright:[95,39],cyanBright:[96,39],whiteBright:[97,39]},bgColor:{bgBlack:[40,49],bgRed:[41,49],bgGreen:[42,49],bgYellow:[43,49],bgBlue:[44,49],bgMagenta:[45,49],bgCyan:[46,49],bgWhite:[47,49],bgBlackBright:[100,49],bgGray:[100,49],bgGrey:[100,49],bgRedBright:[101,49],bgGreenBright:[102,49],bgYellowBright:[103,49],bgBlueBright:[104,49],bgMagentaBright:[105,49],bgCyanBright:[106,49],bgWhiteBright:[107,49]},underlineColor:{underlineBlack:["58;5;0",59],underlineRed:["58;5;1",59],underlineGreen:["58;5;2",59],underlineYellow:["58;5;3",59],underlineBlue:["58;5;4",59],underlineMagenta:["58;5;5",59],underlineCyan:["58;5;6",59],underlineWhite:["58;5;7",59],underlineBlackBright:["58;5;8",59],underlineGray:["58;5;8",59],underlineGrey:["58;5;8",59],underlineRedBright:["58;5;9",59],underlineGreenBright:["58;5;10",59],underlineYellowBright:["58;5;11",59],underlineBlueBright:["58;5;12",59],underlineMagentaBright:["58;5;13",59],underlineCyanBright:["58;5;14",59],underlineWhiteBright:["58;5;15",59]}},Rl=Object.keys(E.modifier),Qo=Object.keys(E.color),Zo=Object.keys(E.bgColor),Al=Object.keys(E.underlineColor),Dl=[...Qo,...Zo];function ei(){let e=new Map;for(let[t,n]of Object.entries(E)){for(let[r,s]of Object.entries(n))E[r]={open:`\x1B[${s[0]}m`,close:`\x1B[${s[1]}m`},n[r]=E[r],e.set(Number.parseInt(s[0],10),s[1]);Object.defineProperty(E,t,{value:n,enumerable:!1})}return Object.defineProperty(E,"codes",{value:e,enumerable:!1}),E.color.close="\x1B[39m",E.bgColor.close="\x1B[49m",E.underlineColor.close="\x1B[59m",E.color.ansi=Xn(),E.color.ansi256=xt(),E.color.ansi16m=wt(),E.bgColor.ansi=Xn(10),E.bgColor.ansi256=xt(10),E.bgColor.ansi16m=wt(10),E.underlineColor.ansi=Xo,E.underlineColor.ansi256=xt(20),E.underlineColor.ansi16m=wt(20),Object.defineProperties(E,{rgbToAnsi256:{value(t,n,r){return t===n&&n===r?t<8?16:t>248?231:Math.round((t-8)/247*24)+232:16+36*Math.round(t/255*5)+6*Math.round(n/255*5)+Math.round(r/255*5)},enumerable:!1},hexToRgb:{value(t){let n=/[\da-f]{6}|[\da-f]{3}/i.exec(t.toString(16));if(!n)return[0,0,0];let[r]=n;r.length===3&&(r=[...r].map(o=>o+o).join(""));let s=Number.parseInt(r,16);return[s>>16&255,s>>8&255,s&255]},enumerable:!1},hexToAnsi256:{value:t=>E.rgbToAnsi256(...E.hexToRgb(t)),enumerable:!1},ansi256ToAnsi:{value(t){if(t<8)return 30+t;if(t<16)return 90+(t-8);let n,r,s;if(t>=232)n=((t-232)*10+8)/255,r=n,s=n;else{t-=16;let a=t%36;n=Math.floor(t/36)/5,r=Math.floor(a/6)/5,s=a%6/5}let o=Math.max(n,r,s)*2;if(o===0)return 30;let i=30+(Math.round(s)<<2|Math.round(r)<<1|Math.round(n));return o===2&&(i+=60),i},enumerable:!1},rgbToAnsi:{value:(t,n,r)=>E.ansi256ToAnsi(E.rgbToAnsi256(t,n,r)),enumerable:!1},hexToAnsi:{value:t=>E.ansi256ToAnsi(E.hexToAnsi256(t)),enumerable:!1}}),E}var ti=ei(),V=ti;import Et from"node:process";import ni from"node:os";import Qn from"node:tty";function B(e,t=globalThis.Deno?globalThis.Deno.args:Et.argv){let n=e.startsWith("-")?"":e.length===1?"-":"--",r=t.indexOf(n+e),s=t.indexOf("--");return r!==-1&&(s===-1||r<s)}var{env:P}=Et,Ke;B("no-color")||B("no-colors")||B("color=false")||B("color=never")?Ke=0:(B("color")||B("colors")||B("color=true")||B("color=always"))&&(Ke=1);function er(){return/^\d+$/.test(P.FORCE_COLOR)}function ri(){if("FORCE_COLOR"in P){if(P.FORCE_COLOR==="false")return 0;if(P.FORCE_COLOR==="true"||P.FORCE_COLOR.length===0)return 1;if(er())return Math.min(Number.parseInt(P.FORCE_COLOR,10),3)}}function si(e){return e===0?!1:{level:e,hasBasic:!0,has256:e>=2,has16m:e>=3}}function oi(e,{streamIsTTY:t,sniffFlags:n=!0}={}){let r=ri();r!==void 0&&(Ke=r);let s=n?Ke:r;if(s===0)return 0;if(n){if(B("color=16m")||B("color=full")||B("color=truecolor"))return 3;if(B("color=256"))return 2}if(s!==void 0&&er())return s;if("TF_BUILD"in P&&"AGENT_NAME"in P)return 1;if(e&&!t&&s===void 0)return 0;let o=s||0;if(P.TERM==="dumb")return o;if(Et.platform==="win32"){let i=ni.release().split(".");return Number(i[0])>=10&&Number(i[2])>=10586?Number(i[2])>=14931?3:2:1}if("CI"in P)return["GITHUB_ACTIONS","GITEA_ACTIONS","CIRCLECI"].some(i=>i in P)?3:["TRAVIS","APPVEYOR","GITLAB_CI","BUILDKITE","DRONE"].some(i=>i in P)||P.CI_NAME==="codeship"?1:o;if("TEAMCITY_VERSION"in P)return/^(?:9\.0*[1-9]\d*\.|\d{2,}\.)/.test(P.TEAMCITY_VERSION)?1:0;if(P.COLORTERM==="truecolor"||P.TERM==="xterm-kitty"||P.TERM==="xterm-ghostty"||P.TERM==="wezterm")return 3;if("TERM_PROGRAM"in P){let i=Number.parseInt((P.TERM_PROGRAM_VERSION||"").split(".",1)[0],10);switch(P.TERM_PROGRAM){case"iTerm.app":return i>=3?3:2;case"Apple_Terminal":return 2}}return/-256(?:color)?$/i.test(P.TERM)?2:/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(P.TERM)||"COLORTERM"in P?1:o}function Zn(e,t={}){let n=oi(e,{streamIsTTY:e&&e.isTTY,...t});return si(n)}var ii={stdout:Zn({isTTY:Qn.isatty(1)}),stderr:Zn({isTTY:Qn.isatty(2)})},tr=ii;var{stdout:nr,stderr:rr}=tr,be=Symbol("GENERATOR"),xe=Symbol("STYLER"),Ve=Symbol("IS_EMPTY"),Ge=Symbol("LEVEL"),we=Object.create(null),sr=e=>{if(!Number.isSafeInteger(e)||e<0||e>3)throw new Error("The `level` should be an integer from 0 to 3")},ai={enumerable:!0,get(){return this[Ge]},set(e){sr(e),this[Ge]=e}},ci=(e,t={})=>{t.level!==void 0&&sr(t.level);let n=nr?nr.level:0;e[Ge]=t.level===void 0?n:t.level};var di=e=>{let t=(...n)=>n.join(" ");return ci(t,e),Object.setPrototypeOf(t,Ee.prototype),t};function Ee(e){return di(e)}Object.setPrototypeOf(Ee.prototype,Function.prototype);for(let[e,t]of Object.entries(V))we[e]={get(){let n=Pt(this,or(t.open,t.close,this[xe]),this[Ve]);return Object.defineProperty(this,e,{value:n}),n}};we.visible={get(){let e=Pt(this,this[xe],!0);return Object.defineProperty(this,"visible",{value:e}),e}};var ui=(e,t)=>{let n=V[t];if(e==="rgb"){let s=(i,a,c)=>n.ansi(V.rgbToAnsi(i,a,c));return[s,s,(i,a,c)=>n.ansi256(V.rgbToAnsi256(i,a,c)),n.ansi16m]}if(e==="hex"){let s=i=>n.ansi(V.hexToAnsi(i));return[s,s,i=>n.ansi256(V.hexToAnsi256(i)),i=>n.ansi16m(...V.hexToRgb(i))]}let r=s=>n.ansi(V.ansi256ToAnsi(s));return[r,r,n.ansi256,n.ansi256]},li=["rgb","hex","ansi256"];for(let e of li){let t=e[0].toUpperCase()+e.slice(1);for(let[n,r]of[[e,"color"],["bg"+t,"bgColor"],["underline"+t,"underlineColor"]]){let{close:s}=V[r],o=ui(e,r);we[n]={get(){let i=function(a,c,d){let l=o[this.level](a,c,d);return Pt(this,or(l,s,this[xe]),this[Ve])};return Object.defineProperty(this,n,{value:i}),i}}}}var pi=Object.defineProperties(()=>{},{...we,level:{enumerable:!0,get(){return this[be].level},set(e){this[be].level=e}}}),or=(e,t,n)=>{let r,s;return n===void 0?(r=e,s=t):(r=n.openAll+e,s=t+n.closeAll),{open:e,close:t,openAll:r,closeAll:s,parent:n}},Pt=(e,t,n)=>{let r=(...s)=>s.length===1?vt(r,""+s[0]):s.length===2?vt(r,`${s[0]??""} ${s[1]??""}`):vt(r,s.join(" "));return Object.setPrototypeOf(r,pi),r[be]=e[be]??e,r[xe]=t,r[Ve]=n,r},vt=(e,t)=>{if(e[be][Ge]<=0||!t)return e[Ve]?"":t;let n=e[xe];if(n===void 0)return t;let{openAll:r,closeAll:s}=n;if(t.includes("\x1B"))for(;n!==void 0;)t=zn(t,n.close,n.open),n=n.parent;let o=t.indexOf(`
|
|
6
|
+
`);return o!==-1&&(t=Yn(t,s,r,o)),r+t+s};Object.defineProperties(Ee.prototype,{...we,level:ai});var fi=Ee(),Ul=Ee({level:rr?rr.level:0});var h=fi;import{Command as Bu}from"commander";import*as S from"node:fs";import*as g from"node:path";import{fileURLToPath as ec}from"node:url";import oe from"handlebars";var M="Container Runtime",W="bring-your-own";var O="database-management",J=".tiltignore",ir="service.json",re="docker-compose.yml",D=".autogenerated",We="TILT_TECH_STACK.star",Je="TILT_RESOURCE_DEFAULTS.star",_="project.json",R="package.json",$t=[We,Je,"spec.master"],Hl=["backend","frontend","library","sdk","worker","migrator","mcp",W],_t={backend:["dev","build","start"],worker:["dev","build","start"],migrator:["dev","build","start"],mcp:["dev","build","start"],frontend:["dev","build"],library:["dev","build"],sdk:["dev","build"],[W]:[]};var Rt=["monitoring","elk","debezium","golden_image","verdaccio"],$={frontend:{base:3e3,min:3e3,max:3999,range:"3000-3999"},backend:{base:4e3,min:4e3,max:4999,range:"4000-4999"},mcp:{base:4e3,min:4e3,max:5999,range:"4000-5999"},[W]:{base:4e3,min:4e3,max:5999,range:"4000-5999"},worker:{base:6e3,min:6e3,max:6999,range:"6000-6999"},health:{base:5e3,min:5e3,max:5999,range:"5000-5999"},migrator:{base:7e3,min:7e3,max:7999,range:"7000-7999"}},ve={tiltUi:10350,traefik:8080},At="https://tdk-landscape.github.io/tdk-website/docs/quickstart/";var ar=["node_modules",".git",".tilt",".tilt-engine","dist","build",".prisma",".turbo","coverage","tmp","temp","__tests__","test","tests",".github",".vscode","docs"],cr=["Dockerfile",re,"docker-compose.yaml","Tiltfile","vite.config.ts","tsconfig.json","tsconfig.build.json"],mi={infra:"\u{1F3D7}\uFE0F",core:"\u{1F527}",api:"\u26A1",app:"\u{1F3A8}",orchestrator:"\u2699\uFE0F"};function Kl(e){let t=e.toLowerCase();for(let[n,r]of Object.entries(mi))if(t.includes(n))return r;return"\u{1F4E6}"}var hi="1.0.0",gi={runtime:"bun",bundler:"vite",language:"typescript",framework:"hono",database:"postgresql",orm:"prisma",messaging:"nats",linting:"biome",testing:"vitest"},ki={frontend:{base:$.frontend.base,range:$.frontend.range,start:$.frontend.min,end:$.frontend.max},backend:{base:$.backend.base,range:$.backend.range,start:$.backend.min,end:$.backend.max},health:{base:$.health.base,range:$.health.range,start:$.health.min,end:$.health.max},worker:{base:$.worker.base,range:$.worker.range,start:$.worker.min,end:$.worker.max},migrator:{base:$.migrator.base,range:$.migrator.range,start:$.migrator.min,end:$.migrator.max},tiltUi:ve.tiltUi,traefik:ve.traefik},yi={path:"/health",live:"/health/live",ready:"/health/ready",timeout:30,interval:5},Si={frontend:"-frontend",backend:"-backend",worker:"-worker",migrator:"-migrator",library:"-lib",sdk:"-sdk",validSeparators:["-","_"],maxLength:63,minLength:3},Ti={entrypoint:"web",network:"traefik-public",defaultHost:"localhost",appHost:"app.localhost",apiHost:"api.localhost",appSubdomainPrefix:"app",apiSubdomainPrefix:"api",defaultPort:8080,tlsEnabled:!1,entrypoints:["web"],middlewares:[],tls:{enabled:!1},healthcheckPath:"/health",healthcheckInterval:"10s",healthcheckTimeout:"5s",frontendPriorityBase:100},bi=["node_modules","dist","build",".git",".prisma",".turbo","coverage","tmp","temp","__tests__","test","tests"],xi={frontend:{suffix:"-frontend",portRange:"3000-3999"},backend:{suffix:"-backend",portRange:"4000-4999"},library:{suffix:"-library",portRange:null},sdk:{suffix:"-sdk",portRange:"3000-9999"},migrator:{suffix:"-migrator",portRange:"7000-7999"},worker:{suffix:"-worker",portRange:"6000-6999"}},wi={prisma:"Database ORM with migrations",nats:"Event streaming via NATS",redis:"Caching layer",infisical:"Secrets management",vitest:"Testing framework",traefik:"HTTP routing",websocket:"Real-time connections",graphql:"GraphQL support",grpc:"gRPC support",viteNode:"Vite Node runtime",maintenance:"Maintenance mode"},Ei={services:"services/product",sharedPlatform:"shared-platform-engineering",sharedProduct:"shared-product-engineering",sharedDdd:"shared-ddd-layers"},vi={scanIntervalSeconds:5,maxManifestsPerRoot:50,servicePatterns:["services/product/*","services/platform/*"]},Pi={dockerfile:"Dockerfile",context:"."},$i={backend:{command:"bun",args:["run","dev"],env:{NODE_ENV:"development"}},frontend:{command:"bun",args:["run","dev"],env:{NODE_ENV:"development"}}},F={version:hi,tech:gi,ports:ki,health:yi,naming:Si,traefik:Ti,filewatchIgnores:bi,serviceTypes:xi,features:wi,paths:Ei,discovery:vi,docker:Pi,runtime:$i};import{randomBytes as _i}from"node:crypto";import{closeSync as Ri,fchmodSync as Ai,fsyncSync as Di,openSync as Oi,renameSync as Ci,rmSync as Ni,statSync as ji,writeFileSync as Ii}from"node:fs";import{basename as Fi,dirname as Li,join as Mi}from"node:path";function Pe(e,t){let n=Mi(Li(e),`.${Fi(e)}.${process.pid}.${_i(4).toString("hex")}.tmp`),r;try{r=ji(e).mode&511}catch{r=void 0}try{let s=Oi(n,"wx");try{r!==void 0&&Ai(s,r),Ii(s,t,"utf-8"),Di(s)}finally{Ri(s)}Ci(n,e)}catch(s){throw Ni(n,{force:!0}),s}}import{appendFileSync as Ui,existsSync as dr,mkdirSync as Bi,readFileSync as Hi,writeFileSync as ur}from"node:fs";import{resolve as Dt}from"node:path";function ep(e,t,n){for(let[r,s]of t.entries())console.log(h.blue(`${s.emoji} ${s.description}...`)),s.type==="json"?Gi(e,s.filename,s.content):Vi(e,s.filename,String(s.content)),n&&n(s,r,t.length)}function Ki(e,t,n=2){let r=`${JSON.stringify(t,null,n)}
|
|
7
|
+
`;ur(e,r,"utf-8")}function Gi(e,t,n,r=2){let s=Dt(e,t);Ki(s,n,r)}function Ot(e,t){ur(e,t,"utf-8")}function Vi(e,t,n){let r=Dt(e,t);Ot(r,n)}function tp(e){dr(e)||Bi(e,{recursive:!0})}var Wi=[".env",".tdk/.tdk-out/",".tdk/.project-id",".tdk/smoke/","node_modules/"];function np(e){let t=Dt(e,".gitignore"),n=dr(t)?Hi(t,"utf-8"):"",r=new Set(n.split(`
|
|
8
|
+
`).map(a=>a.trim().replace(/\/$/,""))),s=Wi.filter(a=>!r.has(a.replace(/\/$/,"")));if(s.length===0)return[];let o=n&&!n.endsWith(`
|
|
9
|
+
`)?`
|
|
10
|
+
`:"";return Ui(t,`${o}${n?`
|
|
11
|
+
# TDK (generated files and secrets)
|
|
12
|
+
`:`# TDK (generated files and secrets)
|
|
13
|
+
`}${s.join(`
|
|
14
|
+
`)}
|
|
15
|
+
`),s}import{existsSync as Ji,lstatSync as qi}from"node:fs";import{isAbsolute as lr,normalize as qe,relative as zi,sep as pr}from"node:path";var ap=[`${D}/**`,".tdk/.tdk-out/**","Tiltfile","TILT_*.star",J,`${R} (create only)`,"shared-platform-engineering/docker-templates/**","services/platform/database-management/docker-compose.yml","product_autogenerated_resource_manifests/**","product_autogenerated_services_list/**"];function Yi(e){let t=qe(e).split(pr).join("/");return lr(e)||t===".."||t.startsWith("../")?!1:t.split("/").includes(D)||t.startsWith(".tdk/.tdk-out/")||t==="shared-platform-engineering/docker-templates"||t.startsWith("shared-platform-engineering/docker-templates/")||t.startsWith("product_autogenerated_resource_manifests/")||t.startsWith("product_autogenerated_services_list/")?!0:t==="Tiltfile"||/^TILT_[^/]+\.star$/.test(t)||t===J||t===R||t==="services/platform/database-management/docker-compose.yml"}function se(e,t){let n=lr(t)?qe(t):qe(`${e}/${t}`),r=zi(e,n).split(pr).join("/");if(!Yi(r))throw new Error(`Refusing to write outside TDK's generated-file allowlist: ${r}`);let s=e;for(let o of r.split("/"))if(s=qe(`${s}/${o}`),Ji(s)&&qi(s).isSymbolicLink())throw new Error(`Refusing to write through a symbolic link in generated path: ${r}`);return n}var $e=[O,"proxy"];import{existsSync as Or,readFileSync as Cr}from"node:fs";import{copyFileSync as yr,mkdirSync as aa,rmSync as Sr,writeFileSync as Tr}from"node:fs";import{join as de}from"node:path";function C(e){return e==="backend"||e==="mcp"}import{readFileSync as ra}from"node:fs";import{join as sa}from"node:path";import{existsSync as Qi}from"node:fs";import{dirname as Zi,join as ea,resolve as mr}from"node:path";import{cwd as ta}from"node:process";var fr={name:"tdk-cli-internal",private:!0,version:"1.3.157",description:"TDK CLI \u2014 start services on your machine.",keywords:["tdk","tilt","cli","local-dev","microservices"],homepage:"https://github.com/tdk-landscape/tdk-cli-core#readme",bugs:{url:"https://github.com/tdk-landscape/tdk-cli-core/issues"},repository:{type:"git",url:"git+https://github.com/tdk-landscape/tdk-cli-core.git",directory:"cli"},license:"MIT",author:"TDK Team",type:"module",main:"./dist/index.js",types:"./dist/index.d.ts",bin:{tdk:"bin/tdk.js","tdk-cli":"bin/tdk.js"},files:["dist","dist-bundle","bin","templates","README.md","Tiltfile","engine","discovery","specs","ext","!ext/ide-components/code_executor","shared-platform-engineering/docker-templates"],scripts:{build:"tsc && node scripts/bundle.mjs","build:watch":"tsc --watch",lint:"biome check .","lint:fix":"biome check . --write",typecheck:"tsc --project tsconfig.typecheck.json",clean:"rm -rf dist",dev:"bun run build:watch",test:"vitest run","test:watch":"vitest",prepack:"../scripts/stage-npm-engine.sh && npm run build"},dependencies:{commander:"^15.0.0",handlebars:"^4.7.8"},devDependencies:{"@biomejs/biome":"^2.5.13","@types/node":"^22.0.0","@types/react":"^19.3.0",ajv:"^8.17.1",chalk:"^6.0.1",esbuild:"^0.28.2",ink:"^7.1.1",knip:"^6.38.0",react:"^19.3.0","react-devtools-core":"^8.0.0",typescript:"^7.0.2",vite:"^8.3.1",vitest:"^5.0.0"},engines:{node:">=22.12.0"}};function y(e=ta()){let t=mr(e),n=mr("/");for(;t!==n;){if(Qi(ea(t,".tdk",_)))return t;let r=Zi(t);if(r===t)break;t=r}return null}var ze=null;function na(){if(ze)return ze;let e=fr;if(!e||typeof e!="object"||Array.isArray(e))throw new Error("Invalid package.json: expected object");let t=e;return ze={name:String(t.name??"@tdk-landscape/tdk-cli-core"),version:String(t.version??"0.0.0"),fullPackage:t},ze}function hr(){return na().version}function ce(){let e=y();if(e)try{let t=ra(sa(e,".tdk",_),"utf-8"),n=JSON.parse(t);if(n?.project?.name)return n.project.name}catch{}return"tdk-project"}function Ct(e){let t=ce(),n=process.env.TDK_SERVICE_BASE_URL??`http://${t}.localhost${e?`:${e}`:""}`;try{let r=new URL(n.includes("://")?n:`http://${n}`),s=r.hostname,o=r.protocol==="https:"?"443":"80",i=r.port||(e?String(e):""),a=i===o?"":i;if(s==="localhost"||/^\d+\.\d+\.\d+\.\d+$/.test(s))return{appBase:`${r.protocol}//${s}${a?`:${a}`:""}`,apiBase:`${r.protocol}//${s}${a?`:${a}`:""}`};let c=s.replace(/^(app|api)\./,"");return{appBase:`${r.protocol}//app.${c}${a?`:${a}`:""}`,apiBase:`${r.protocol}//api.${c}${a?`:${a}`:""}`}}catch{return{appBase:`http://app.${t}.localhost`,apiBase:`http://api.${t}.localhost`}}}function oa(e){return`${e.replace(/\/+$/,"")}/health`}function Nt(e){if(C(e.config?.appType)){let t=e.name.replace(/-api$/,"");return e.config?.apiPath??`/api/${t}`}return e.config?.basePath??`/${e.name}`}function jt(e,t){return kr(e,t,n=>oa(n))}function gr(e,t){return kr(e,t,n=>n)}function kr(e,t,n){let{appBase:r,apiBase:s}=Ct(t);return e.filter(o=>o.config?.appType==="frontend"||C(o.config?.appType)).map(o=>{let i=C(o.config.appType)?s:r;return{name:o.name,appType:o.config.appType,url:`${i}${n(Nt(o))}`}})}async function ia(e,t){try{let n=await fetch(e.url,{method:"GET",redirect:"manual",signal:AbortSignal.timeout(t)});return{...e,ok:n.ok,status:n.status}}catch(n){let r=n instanceof Error&&n.name==="TimeoutError"?`no response within ${t}ms`:n instanceof Error?n.message:String(n);return{...e,ok:!1,error:r}}}async function Ye(e,t){return Promise.all(e.map(n=>ia(n,t)))}var Pr=60,br=1e3,xr=1e4,wr=200,Er=64*1024,vr=new Set([404,502,503,504]),ca=new Set(["ECONNREFUSED","ENOTFOUND","EAI_AGAIN","EHOSTUNREACH","ENETUNREACH"]),da=new Set(["GET","HEAD","OPTIONS"]);function Y(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function ua(e,t){if(da.has(t))return!0;if(!Y(e))return!1;let n=Y(e.cause)?e.cause:void 0,r=e.code??n?.code;return typeof r=="string"&&ca.has(r)}function Ft(e){if(!Y(e))return['smoke: must be an object with "via" and "steps"'];let t=[];return e.via!=="proxy"&&t.push('smoke.via: only "proxy" is supported (the check must go through the public URL)'),e.timeoutSeconds!==void 0&&!(typeof e.timeoutSeconds=="number"&&e.timeoutSeconds>0)&&t.push("smoke.timeoutSeconds: must be a number greater than 0"),!Array.isArray(e.steps)||e.steps.length===0?(t.push("smoke.steps: needs at least one step"),t):(e.steps.forEach((n,r)=>{let s=`smoke.steps[${r}]`;if(!Y(n)){t.push(`${s}: must be an object`);return}let o=n.path;(typeof o!="string"||!o.startsWith("/")||o.startsWith("//"))&&t.push(`${s}.path: must start with "/" and is appended to the public URL; a full URL or relative path would skip the proxy`),n.expect!==void 0&&!(typeof n.expect=="number"&&Number.isInteger(n.expect)&&n.expect>=100&&n.expect<=599)&&t.push(`${s}.expect: must be an HTTP status between 100 and 599`),n.bodyContains!==void 0&&typeof n.bodyContains!="string"&&t.push(`${s}.bodyContains: must be a string`),n.save!==void 0&&(Y(n.save)&&Object.values(n.save).every(a=>typeof a=="string"&&a.startsWith("$."))||t.push(`${s}.save: must map names to paths such as "$.id"`))}),t)}function $r(e,t){let{appBase:n,apiBase:r}=Ct(t),s=[];for(let o of e){let i=o.config;if(!i?.smoke)continue;let a=i.appType,c=C(a)||a===W;if(!c&&a!=="frontend"||i.exposeViaProxy===!1)continue;let d=c?r:n,l=i.healthCheckPath||(c&&a!==W?"/health":void 0);s.push({name:o.name,baseUrl:`${d}${Nt(o).replace(/\/+$/,"")}`,...l?.startsWith("/")?{readyPath:l}:{},smoke:i.smoke})}return s}var la=10;function _r(e,t={}){let n=t.timeoutSeconds??la;return e.flatMap(r=>{let s=r.smoke.steps.filter(i=>(i.method??"GET").toUpperCase()==="GET"&&i.body===void 0&&!i.path.includes("{{")).map(i=>({...i,bodyContains:void 0}));if(s.length===0)return[];let o=Math.min(r.smoke.timeoutSeconds??Pr,n);return[{name:r.name,baseUrl:r.baseUrl,smoke:{via:r.smoke.via,timeoutSeconds:o,steps:s}}]})}function pa(e,t){let n=t.replace(/^\$\.?/,"").match(/[^.[\]]+/g)??[],r=e;for(let s of n){if(r==null||s==="__proto__"||s==="constructor"||s==="prototype"||!Object.hasOwn(r,s))return;let o=Object.getOwnPropertyDescriptor(r,s);if(!o||!("value"in o))return;r=o.value}return r}function Rr(e,t){return e.replace(/\{\{(\w+)\}\}/g,(n,r)=>t[r]??n)}function It(e,t){return typeof e=="string"?Rr(e,t):Array.isArray(e)?e.map(n=>It(n,t)):Y(e)?Object.fromEntries(Object.entries(e).map(([n,r])=>[n,It(r,t)])):e}function Xe(e){let t=e.replace(/\s+/g," ").trim();return t.length>wr?`${t.slice(0,wr)}...`:t}function Ar(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"step"}function fa(e,t){let n=Ar(t),r=(e.get(n)??0)+1;return e.set(n,r),r===1?n:`${n}-${r}`}function ma(e,t,n,r,s){try{let o=de(e,Ar(n.service),t);aa(o,{recursive:!0});let i=de(o,"latest.json"),a=de(o,"body.txt");return r===void 0?Sr(a,{force:!0}):Tr(a,r),Tr(i,`${JSON.stringify({...n,bodyPath:r===void 0?null:"body.txt"},null,2)}
|
|
16
|
+
`),s&&(yr(i,de(o,"last-success.json")),r===void 0?Sr(de(o,"last-success-body.txt"),{force:!0}):yr(a,de(o,"last-success-body.txt"))),i}catch{return}}function ha(e){let t=Buffer.from(e,"utf8");if(t.length<=Er)return{body:e,truncated:!1,bytes:t.length};let n=t.subarray(0,Er).toString("utf8").replace(/\uFFFD$/,"");return{body:n,truncated:!0,bytes:Buffer.byteLength(n,"utf8")}}async function ga(e,t={}){let n=t.fetch??((l,u)=>fetch(l,u)),r=t.now??Date.now,s=t.sleep??(l=>new Promise(u=>setTimeout(u,l))),o=(e.smoke.timeoutSeconds??Pr)*1e3,i=r()+o,a={},c=Ft(e.smoke);if(c.length>0)return{name:e.name,ok:!1,failure:`${e.name}: invalid smoke block: ${c.join("; ")}`};if(e.readyPath){let l=`${e.baseUrl}${e.readyPath}`;for(;;){try{let u=await n(l,{method:"GET",signal:AbortSignal.timeout(xr)});if(await u.text(),!vr.has(u.status)&&u.status<500)break}catch{}if(r()>=i)break;await s(br)}}i=r()+o;let d=new Map;for(let[l,u]of e.smoke.steps.entries()){let p=u.name??`step ${l+1}`,k=(u.method??"GET").toUpperCase(),m=`${e.baseUrl}${Rr(u.path,a)}`,T=u.expect??200,w=u.body!==void 0,f,v="",G,te;for(;;){G=void 0,te=void 0,f=void 0;let H=!1;try{let I=await n(m,{method:k,headers:w?{"content-type":"application/json"}:void 0,body:w?JSON.stringify(It(u.body,a)):void 0,signal:AbortSignal.timeout(xr)});f=I.status,v=await I.text()}catch(I){H=ua(I,k),G=I instanceof Error?I.message:String(I);let ne=Y(I)?I.code??(Y(I.cause)?I.cause.code:void 0):void 0;te=typeof ne=="string"?ne:void 0}if(!(G!==void 0&&H||f!==void 0&&f!==T&&vr.has(f))||r()>=i)break;await s(br)}let ye=`${e.name}: ${p}: ${k} ${m}`,Jn;if(t.recordDir){let H=f===void 0?void 0:ha(v),Te=G===void 0&&f===T&&(u.bodyContains===void 0||v.includes(u.bodyContains));Jn=ma(t.recordDir,fa(d,p),{service:e.name,step:p,method:k,url:m,expectedStatus:T,status:f??null,...G===void 0?{}:{error:te??G},truncated:H?.truncated??!1,bodyBytes:H?.bytes??0},H?.body,Te)}let Se=H=>({name:e.name,ok:!1,failure:H,recordPath:Jn});if(G!==void 0)return Se(`${ye} never answered (${G})`);if(f!==T)return Se(`${ye} returned ${f}, expected ${T}${v?`: ${Xe(v)}`:""}`);if(u.bodyContains!==void 0&&!v.includes(u.bodyContains))return Se(`${ye} returned ${f} but the body does not contain "${u.bodyContains}": ${Xe(v)}`);if(u.save){let H;try{H=JSON.parse(v)}catch{return Se(`${ye} returned a body that is not JSON, cannot save: ${Xe(v)}`)}for(let[Te,I]of Object.entries(u.save)){let ne=pa(H,I);if(ne==null||typeof ne=="object")return Se(`${ye} response has no ${I} to save as {{${Te}}}: ${Xe(v)}`);a[Te]=String(ne)}}}return{name:e.name,ok:!0}}function Ap(e){return`Smoke check failed: ${e.failure}${e.recordPath?` (record: ${e.recordPath})`:""}`}async function Dr(e,t={}){return Promise.all(e.map(n=>ga(n,t)))}var _e=1,Np="https://tdk-landscape.github.io/schema.service.json",ka={dependencies:"deprecated, use dependsOn (still read for now)",envVars:"deprecated, use params (still read when params is absent)",jwtSecret:"ignored. A secret must not be committed: remove it and set JWT_SECRET in the project .env"},ya=new Set(["$schema","apiBasePath","apiPath","appName","appType","backendName","basePath","build","buildContext","databaseName","dependencies","dev","dependsOn","dockerfile","enabled","exposeViaProxy","featuresEnabled","framework","healthCheckPath","image","language","name","nats","params","playwright","port","replicas","restart","runtime","sablier","schemaVersion","secrets","smoke","stack","syncs","traefik","type"]),Sa=[{path:["healthCheckPath"],pattern:/^\/[a-z0-9/-]*$/,expected:"a path such as /health"},{path:["traefik","host"],pattern:/^[A-Za-z0-9.-]+$/,expected:"a hostname such as shop.backend.myproject.local"},{path:["traefik","pathPrefix"],pattern:/^\/[a-z0-9/-]*$/,expected:"a path such as /api/v1/shop-management"},{path:["traefik","healthCheck"],pattern:/^\/[a-z0-9/-]*$/,expected:"a path such as /health"},{path:["nats","queueGroup"],pattern:/^[A-Za-z0-9_.-]+$/,expected:"letters, digits, '_', '.' or '-'"},{path:["databaseName"],pattern:/^[A-Za-z0-9_-]+$/,expected:"letters, digits, '_' or '-'"},{path:["stack"],pattern:/^[A-Za-z0-9_-]+$/,expected:"letters, digits, '_' or '-'"},{path:["image"],pattern:/^[A-Za-z0-9._/:@-]+$/,expected:"an image reference such as ghcr.io/acme/app:1.0"}];function Qe(e,t){let n=[],r=new Set;for(let{path:s,pattern:o,expected:i}of Sa){let a=e;for(let d of s)a=a&&typeof a=="object"&&!Array.isArray(a)?a[d]:void 0;if(a==null||a==="")continue;let c=`${t}.${s.join(".")}`;(typeof a!="string"||!o.test(a))&&r.add(c),typeof a!="string"?n.push(`${c}: expected a string`):o.test(a)||n.push(`${c}: must be ${i} (no spaces, line breaks or YAML syntax)`)}for(let[s,o]of Ta(e,t))r.has(s)||n.push(`${s}: ${o}`);return n}function Lt(e){for(let t=0;t<e.length;t++){let n=e.charCodeAt(t);if(n<32||n===127||n===133||n===8232||n===8233)return!0}return!1}function Ta(e,t){let n=[],r=Object.entries(e).filter(([s])=>s!=="smoke").map(([s,o])=>[o,`${t}.${s}`]);for(let s of Object.keys(e))Lt(s)&&n.push([t,"a key contains a line break"]);for(;r.length>0;){let[s,o]=r.pop();if(typeof s=="string")Lt(s)&&n.push([o,"must not contain line breaks or control characters"]);else if(Array.isArray(s))for(let[i,a]of s.entries())r.push([a,`${o}[${i}]`]);else if(s&&typeof s=="object")for(let[i,a]of Object.entries(s))Lt(i)?n.push([o,"a key contains a line break"]):r.push([a,`${o}.${i}`])}return n.sort(([s],[o])=>s.localeCompare(o))}var ba=["apiPath","basePath"];function Mt(e){if(!e||typeof e!="object"||Array.isArray(e))return[];let t=e;return ba.filter(n=>n in t&&typeof t[n]!="string")}function Ut(e,t){if(!e||typeof e!="object"||Array.isArray(e))return{errors:[`${t}: expected a JSON object`],warnings:[]};let n=e,r=[];for(let a of["appName","appType","stack","schemaVersion"])a in n||r.push(`${t}.${a}: required field is missing`);for(let a of["appName","appType","stack"])a in n&&typeof n[a]!="string"&&r.push(`${t}.${a}: expected a string`);"schemaVersion"in n&&n.schemaVersion!==_e&&r.push(`${t}.schemaVersion: unsupported version ${String(n.schemaVersion)} (supported: ${_e})`);for(let a of Mt(n))r.push(`${t}.${a}: expected a string`);let s=n.dev,o=s&&typeof s=="object"&&!Array.isArray(s)?s.liveReload:void 0;if(o!==void 0&&typeof o!="boolean"&&r.push(`${t}.dev.liveReload: expected true or false`),r.push(...Qe(n,t)),"smoke"in n)for(let a of Ft(n.smoke))r.push(`${t}.${a}`);let i=Object.keys(n).flatMap(a=>{let c=ka[a];return c?[`${t}.${a}: ${c}`]:ya.has(a)?[]:[`${t}.${a}: unknown field is preserved`]});return o===!0&&n.language!=="go"&&i.push(`${t}.dev.liveReload: only applies to Go services (language "go"); it is ignored here`),{errors:r,warnings:i,manifest:n}}function Nr(e,t){if(!Or(e))return[];try{let n=JSON.parse(Cr(e,"utf-8"));return!n||typeof n!="object"||Array.isArray(n)?[]:Qe(n,t)}catch{return[]}}function jr(e,t){if(!Or(e))return{errors:[`${t}: file is missing`],warnings:[]};try{let n=JSON.parse(Cr(e,"utf-8"));return Ut(n,t)}catch(n){let r=n instanceof Error?n.message:String(n);return{errors:[`${t}: invalid JSON (${r})`],warnings:[]}}}import{existsSync as ue,readdirSync as Qr,readFileSync as Zr,statSync as Jt}from"node:fs";import{dirname as Ga,join as Q}from"node:path";var Re=class{data;options;constructor(t){this.data=new Map,this.options=t}has(t){let n=this.data.get(t);return n?Date.now()-n.timestamp>this.options.ttlMs?(this.data.delete(t),!1):!0:!1}get(t){if(this.has(t))return this.data.get(t)?.value}set(t,n){this.data.set(t,{value:n,timestamp:Date.now()})}delete(t){this.data.delete(t)}clear(){this.data.clear()}keys(){let t=[];for(let n of this.data.keys())this.has(n)&&t.push(n);return t}size(){return this.keys().length}};function Ir(e){let t=0;return{isValid(){return Date.now()-t<e},markUpdated(){t=Date.now()},reset(){t=0}}}import{execFileSync as Pa,execSync as $a}from"node:child_process";import{existsSync as Fr}from"node:fs";import{delimiter as Lr,extname as Mr,join as Ur}from"node:path";import{delimiter as Lp,extname as xa}from"node:path";function Ae(){return process.platform==="win32"}function wa(e){return e==="linux"?"linux":e==="darwin"?"darwin":e==="win32"?"windows":null}function Ea(e){return e==="x64"?"amd64":e==="arm64"?"arm64":null}function va(e,t){let n=wa(e),r=Ea(t);if(!n||!r||n==="windows"&&r!=="amd64")return null;let s=`tdk-${n}-${r}`;return n==="windows"?`${s}.exe`:s}function Mp(){return va(process.platform,process.arch)}function Up(){return Ae()?"tdk.exe":"tdk"}function Bt(e,t=Ae()){return t?xa(e)?[e]:[e,`${e}.exe`,`${e}.cmd`,`${e}.bat`]:[e]}function N(e){let t=process.env.PATH??process.env.Path??"",n=Ae(),r=Bt(e,n);if(n&&!Mr(e))for(let s of(process.env.PATHEXT??".EXE;.CMD;.BAT;.COM").split(";")){let o=s.toLowerCase();o&&!r.includes(`${e}${o}`)&&r.push(`${e}${o}`)}for(let s of t.split(Lr))if(s)for(let o of r){let i=Ur(s,o);if(Fr(i))return i}return null}function Br(e){let t=process.env.PATH??process.env.Path??"",n=Ae(),r=Bt(e,n);if(n&&!Mr(e))for(let i of(process.env.PATHEXT??".EXE;.CMD;.BAT;.COM").split(";")){let a=i.toLowerCase();a&&!r.includes(`${e}${a}`)&&r.push(`${e}${a}`)}let s=[],o=new Set;for(let i of t.split(Lr))if(i)for(let a of r){let c=Ur(i,a);if(!Fr(c))continue;let d=n?c.toLowerCase():c;o.has(d)||(o.add(d),s.push(c))}return s}var Hr=1e4;function Kr(){let e=!1;if(process.platform==="win32")try{return Pa(N("docker")??"docker",["ps"],{stdio:"ignore",timeout:Hr,windowsHide:!0}),"running"}catch(t){return t.code==="ETIMEDOUT"?"unresponsive":"missing"}for(let t of["docker ps","colima status","podman ps"])try{return $a(t,{stdio:"ignore",timeout:Hr}),"running"}catch(n){n.code==="ETIMEDOUT"&&(e=!0)}return e?"unresponsive":"missing"}function Ze(e,t){return e[t]??0}function _a(e,t){let n=Array.from({length:t.length+1},(r,s)=>s);for(let r=1;r<=e.length;r++){let s=[r];for(let o=1;o<=t.length;o++){let i=e[r-1]===t[o-1]?0:1;s[o]=Math.min(Ze(s,o-1)+1,Ze(n,o)+1,Ze(n,o-1)+i)}n=s}return Ze(n,t.length)}function Ra(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length-1;n++)if(e[n]!==t[n]&&e[n]===t[n+1]&&e[n+1]===t[n]&&e.slice(0,n)===t.slice(0,n)&&e.slice(n+2)===t.slice(n+2))return!0;return!1}function Gr(e,t){if(e.length<2||t.length===0||t.includes(e))return;let n=e.toLowerCase(),r,s=Number.POSITIVE_INFINITY;for(let o of t){if(o.length===0)continue;let i=o.toLowerCase(),a=Ra(n,i)?1:_a(n,i),c=Math.max(1,Math.min(2,Math.floor(Math.max(n.length,i.length)/3)));a>c||(a<s||a===s&&(r===void 0||o<r))&&(r=o,s=a)}return r}import{spawn as Aa}from"node:child_process";import{join as Da}from"node:path";function Oa(e,t=[],n={}){return new Promise((r,s)=>{let o=[e,...t];n.verbose&&console.log(`Executing: tilt ${o.join(" ")}`);let i=Aa(N("tilt")??"tilt",o,{stdio:n.inheritStdio?"inherit":"pipe",shell:!1,windowsHide:process.platform==="win32",timeout:n.timeoutMs}),a="",c="";n.inheritStdio||(i.stdout?.on("data",d=>{a+=d.toString()}),i.stderr?.on("data",d=>{c+=d.toString()})),i.on("close",d=>{r({exitCode:d??1,stdout:a,stderr:c})}),i.on("error",d=>{s(new Error(`Failed to spawn tilt: ${d.message}`))})})}async function Vr(){try{return(await Oa("version",[],{inheritStdio:!1,timeoutMs:1e4})).exitCode===0}catch{return!1}}function Ca(){let e=y();if(!e)throw z.notInProject();return Da(e,".tdk",".tdk-out","Tiltfile")}function Wr(e){let t=Ca();e.push("-f",t)}function tf(e,t={}){let n=[];return Wr(n),t.focusTargets&&t.focusTargets.length>0&&n.push("--",`--focus=${t.focusTargets.join(",")}`),n.push(...e),t.verbose&&!t.quiet&&n.push("--verbose"),t.watch&&n.push("--watch"),n}function nf(e={}){let t=[];return Wr(t),e.force&&t.push("--force"),e.focusTargets&&e.focusTargets.length>0&&t.push("--",`--focus=${e.focusTargets.join(",")}`),t}function Jr(e){return e instanceof Error?e.message:String(e)}function lf(e,t){if(process.env.TDK_VERBOSE){let n=t!==void 0?`: ${Jr(t)}`:"";console.warn(h.gray(`${e}${n}`))}}var j=class extends Error{suggestions;exitCode;constructor(t,n=[],r=1){super(t),this.name="TdkError",this.suggestions=n,this.exitCode=r}display(){Ia(this.message,void 0,this.suggestions)}exit(){this.display(),process.exit(this.exitCode)}};function Ht(e,t,n=!1){let r=new Set;for(let s of e){let o=Gr(s,t);o&&r.add(n?`Did you mean "${o}" for "${s}"?`:`Did you mean "${o}"?`)}return[...r]}var z={tiltNotInstalled:()=>new j("Tilt CLI is not installed",["Install Tilt: `brew install tilt` (macOS)","Or download from: https://docs.tilt.dev/install.html","Verify with: `tilt version`",`Setup guide: ${At}`]),dockerNotAvailable:()=>new j("Docker (or a compatible container runtime) is not running",["Start Docker Desktop, then verify with: `docker ps`","Or start Colima: `colima start`","Or start the Docker service on Linux: `sudo systemctl start docker`",`Setup guide: ${At}`]),dockerNotResponding:()=>new j("Docker is running but not responding (`docker ps` hung for 10s)",["Quit and reopen Docker Desktop, or run `colima restart`","Then verify with: `docker ps`"]),stackNotFound:(e,t=[])=>new j(`Stack "${e}" not found`,[...Ht([e],t),"Run `tdk stacks` to see available stacks","Run `tdk stack` to assign resources to a stack"]),resourceNotFound:(e,t=[])=>new j(`Resource "${e}" not found`,[...Ht([e],t),"Run `tdk resources` to list all resources","Check the resource name spelling"]),unknownServices:(e,t)=>{let n=Ht(e,t,e.length>1);return new j(`Unknown service ${e.join(", ")}. Valid names: ${t.join(", ")}`,n,2)},directoryExists:e=>new j(`Directory already exists: ${e}`,["Use `--path` to specify a different location","Remove the existing directory if no longer needed"]),invalidPath:e=>new j(`Invalid path: ${e}`,["Path must be within the project directory",'Path cannot contain special characters like <>:"|?*']),notInProject:()=>new j("Could not find project root (no .tdk/project.json found)",["Run this from within a TDK project","Run `tdk project --yes` to initialize a new project"])};function pf(){let e=y();return e||z.notInProject().exit()}function Na(e){e instanceof j&&e.exit(),console.error(h.red(`Error: ${Jr(e)}`)),process.exit(1)}async function ja(e,t){try{return await e()}catch(n){return t?.verbose&&n instanceof Error&&n.stack&&console.error(h.gray(n.stack)),Na(n)}}async function ff(e,t){let n=[],r=Kr();return r==="unresponsive"?n.push(z.dockerNotResponding()):r==="missing"&&n.push(z.dockerNotAvailable()),await Vr()||n.push(z.tiltNotInstalled()),n.length>0&&(console.error(h.red(`
|
|
17
|
+
TDK can't start - missing prerequisites:
|
|
18
|
+
`)),n.forEach((s,o)=>{o>0&&console.error(""),s.display()}),console.error(h.gray("\nRun `tdk doctor` for a full environment check.")),process.exit(1)),ja(e,t)}function mf(e,t=1){console.error(h.red(`Error: ${e}`)),process.exit(t)}function Ia(e,t,n){if(console.error(h.red(`\u274C ${e}`)),t&&console.error(h.gray(` ${t}`)),n&&n.length>0){console.error(h.yellow(`
|
|
19
|
+
\u{1F4A1} Suggestions:`));for(let r of n)console.error(h.cyan(` \u2192 ${r}`))}}function hf(e,t){console.error(h.red(`
|
|
20
|
+
tilt ${e} failed with exit code ${t}`)),process.exit(t)}function Fa(e,t,n){return e===1?t:n||`${t}s`}function b(e,t,n){return`${e} ${Fa(e,t,n)}`}var La={month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"};function yf(e){return new Date(e).toLocaleString("en-US",La)}function Sf(e){return new Date(e).toLocaleDateString("en-US",{month:"short",day:"numeric"})}var Kt={success:{color:"green",icon:"\u2713",chalkFn:h.green},error:{color:"red",icon:"\u2717",chalkFn:h.red},warning:{color:"yellow",icon:"\u25CB",chalkFn:h.yellow},unknown:{color:"gray",icon:"?",chalkFn:h.gray}};function Gt(e){if(!e)return"unknown";let t=e.toLowerCase();return t==="ready"||t==="healthy"||t==="active"||t==="running"?"success":t==="error"||t==="failed"||t==="critical"||t==="stopped"?"error":t==="pending"||t==="starting"||t==="building"||t==="degraded"?"warning":"unknown"}function Tf(e){return Kt[Gt(e)].color}function bf(e){return Kt[Gt(e)].icon}function xf(e,t){return Kt[Gt(t)].chalkFn(e)}var Ma={resources:{singular:"resource",command:"tdk resource <name>",context:`
|
|
21
|
+
To create a resource:`},stacks:{singular:"stack",command:"tdk stack <stack-name>",context:`
|
|
22
|
+
To create a stack, use:`},services:{singular:"service",command:"tdk resource <name>",context:`
|
|
23
|
+
To create a service:`},"stack-services":{singular:"service",command:"tdk resource <name> --stack <stack-name>",context:`
|
|
24
|
+
To add services to this stack:`}};function wf(e,t){let n=Ma[e];console.log(t?h.yellow(`No ${n.singular}s found${t}.`):h.yellow(`No ${n.singular}s found.`)),console.log(h.gray(n.context)),console.log(h.gray(` ${n.command}`)),e==="stacks"&&(console.log(h.gray(`
|
|
25
|
+
Or create a new resource with a stack:`)),console.log(h.gray(" tdk resource <name> --stack <stack-name>")))}function Ef(e){console.log(h.yellow(e||"Cancelled."))}function vf(e){console.log(h.blue(`TDK ${e}
|
|
26
|
+
`))}function Pf(e,t){console.log(h.green(`All ${e} are ${t}!`))}var Vt=62;function $f(e="\u2500",t=Vt){return e.repeat(t)}function qr(e,t=Vt-2){let n=Math.max(0,t-e.length),r=Math.floor(n/2),s=n-r;return" ".repeat(r)+e+" ".repeat(s)}function _f(e,t){return e.length>t?`${e.slice(0,t-1)}\u2026`:e.padEnd(t)}function Rf(e,t){return e.length<=t?e:`${e.slice(0,t-3)}...`}function Af(e){console.log(h.green(`\u2713 ${e}`))}function Df(e){console.log(h.blue(e))}function Of(e,t=2){console.log(h.gray(`${" ".repeat(t)}${e}`))}function Cf(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(1)} MB`}function Nf(e,t,n=Vt){let r=n-2,s="\u2500".repeat(r);console.log(),console.log(h.cyan(`\u256D${s}\u256E`)),console.log(h.cyan("\u2502")+h.bold.white(qr(e,r))+h.cyan("\u2502")),t&&(console.log(h.cyan(`\u251C${s}\u2524`)),console.log(h.cyan("\u2502")+h.gray(qr(t,r))+h.cyan("\u2502"))),console.log(h.cyan(`\u2570${s}\u256F`))}function zr(e){if(e.length===0||e.every(n=>n==="unknown"))return"unknown";let t=e.filter(n=>n==="ready").length/e.length;return t>.9?"healthy":t>.5?"degraded":"error"}var Ua=/^[a-z0-9-]+$/;function Yr(e){return Ua.test(e)}var Ba="Use lowercase letters, numbers, and hyphens only";function Xr(e,t){return`${e} "${t}" is not valid. ${Ba}, e.g. "${e==="Resource name"?"my-service":"my-stack"}".`}function Ha(e){return e.trim()?Yr(e)?{valid:!0}:{valid:!1,error:Xr("Resource name",e)}:{valid:!1,error:"Resource name is required"}}function Ka(e){return e.trim()?Yr(e)?{valid:!0}:{valid:!1,error:Xr("Stack name",e)}:{valid:!1,error:"Stack name is required"}}function Lf(e){let t=e==="resource"?Ha:Ka;return n=>{let r=t(n);return r.valid?!0:r.error??"Invalid name"}}function Mf(e){return Wt(Rt,e)?{valid:!0}:{valid:!1,error:`Invalid service. Must be one of: ${Rt.join(", ")}`}}function Uf(e){return Number.isInteger(e)&&e>0&&e<=65535}function Bf(e){return!e.includes("\0")&&!/[<>:"|?*]/.test(e)}function Hf(e,t="_"){return e.replace(/[^a-zA-Z0-9-]/g,t).substring(0,100)}function Wt(e,t){return e.includes(t)}var Va=ir;function Xt(e,t=5,n=0){let r=[];if(n>t)return r;let s=Qr(e,{withFileTypes:!0});for(let o of s){let i=Q(e,o.name);if(o.isDirectory()){if(Wa(o.name))continue;r.push(...Xt(i,t,n+1))}else o.isFile()&&o.name===Va&&r.push(i)}return r}function Qt(e){return Xt(e)}function Wa(e){return Wt(ar,e)||e.startsWith(".")}function Ja(e){if(!e||typeof e!="object")return!1;let t=e,n=typeof t.runtime=="string"||typeof t.appType=="string"||typeof t.type=="string";return typeof t.appName=="string"&&n}var et=new Set;var X=class extends Error{reason;constructor(t){super(t),this.reason=t,this.name="InvalidServiceJsonError"}};function qt(e){return e instanceof Error?e.message:String(e)}function qa(e){let t;try{t=Zr(e,"utf-8")}catch(c){throw new X(`could not read file: ${qt(c)}`)}let n;try{n=JSON.parse(t)}catch(c){throw new X(`invalid JSON: ${qt(c)}`)}if(!Ja(n))throw new X("missing required fields (appName, runtime or appType)");let r=Mt(n);if(r.length>0)throw new X(r.map(c=>`${c}: expected a string`).join("; "));let s=Qe(n,e);if(s.length>0)throw new X(s.join("; "));let o=n;for(let c of Ut(n,e).warnings)et.has(c)||(et.add(c),console.warn(`\u26A0\uFE0F ${c}`));let i={...o,appType:o.appType||o.type},a=Ga(e);return{name:i.appName,path:a,configPath:e,config:i,stack:i.stack,port:i.port,type:i.appType}}function es(e){let t=Xt(e),n=[],r=[];for(let s of t)try{n.push(qa(s))}catch(o){r.push({path:s,reason:o instanceof X?o.reason:qt(o)})}return{resources:n,problems:r}}function za(e){for(let t of e){let n=`skipped ${t.path}: ${t.reason}`;et.has(n)||(et.add(n),console.warn(`\u26A0\uFE0F ${n}`))}}function x(e){let{resources:t,problems:n}=es(e);return za(n),t}function Oe(){let e=y();if(!e)throw z.notInProject();return x(e)}function em(){let e=y();if(!e)throw z.notInProject();let{resources:t,problems:n}=es(e);if(n.length>0)throw new Error(n.map(r=>`Invalid service.json at ${r.path}: ${r.reason}`).join(`
|
|
27
|
+
`));return t}function tm(e){let t=x(e),n=new Set;for(let r of t)r.stack&&n.add(r.stack);return Array.from(n).sort()}function nm(e){let t=e||Oe(),n=new Set;for(let r of t)r.stack&&n.add(r.stack);return Array.from(n).sort()}function rm(e=Oe()){let t=new Map;for(let r of e)if(r.stack){let s=t.get(r.stack);s?s.push(r):t.set(r.stack,[r])}let n=[];for(let[r,s]of t)n.push({name:r,description:b(s.length,"resource"),resources:s,resourceCount:s.length});return n.sort((r,s)=>r.name.localeCompare(s.name))}function sm(e,t){return(t??Oe()).some(r=>r.stack===e)}var Zt=5e3,zt=new Re({ttlMs:Zt}),Yt=new Re({ttlMs:Zt}),De=Ir(Zt);function om(){zt.clear(),Yt.clear(),De.reset()}function Ya(e,t){if(e==="Dockerfile"||e.startsWith("docker-compose"))return"docker";if(e==="Tiltfile"||e.endsWith(".star"))return"tilt";if(e==="vite.config.ts"||e==="tsconfig.json"||e===R)return"config";if(e==="schema.prisma"||e.includes("migration"))return"prisma";if(t){let n=t.split(`
|
|
28
|
+
`).slice(0,10).join(`
|
|
29
|
+
`);if(n.includes("GENERATED BY TDK")||n.includes("AUTOGENERATED"))return"generated"}return"unknown"}function Xa(e){let t=e.configPath;if(De.isValid()){let k=zt.get(t);if(k)return k}let n=e.path,r=Jt(e.configPath),s=r.mtime.toISOString(),o=r.mtime.toISOString(),i=Qa(n),a=ue(Q(n,"Dockerfile")),c=ue(Q(n,"Tiltfile")),d=ue(Q(n,re))||ue(Q(n,"docker-compose.yaml")),l=e.config?.appType,u=l||"backend";l||(e.name.includes("frontend")?u="frontend":(e.name.includes("sdk")||e.name.includes("lib")||e.name.includes("infra"))&&(u="library"));let p={name:e.name,stack:e.stack,type:u,port:e.config?.port,createdAt:s,lastModified:o,dependencies:e.config?.dependsOn??[],hasDockerfile:a,hasTiltfile:c,hasDockerCompose:d,autogeneratedFiles:i,status:"unknown"};return zt.set(t,p),De.markUpdated(),p}function Qa(e){let t=[];for(let r of cr){let s=Q(e,r);if(ue(s)){let o=Jt(s),i=Zr(s,"utf-8");t.push({name:r,path:s,type:Ya(r,i),size:o.size,lastModified:o.mtime.toISOString()})}}let n=Q(e,"prisma");if(ue(n)){let r=Qr(n);for(let s of r)if(s==="schema.prisma"||s.includes("migration")){let o=Q(n,s),i=Jt(o);t.push({name:`prisma/${s}`,path:o,type:"prisma",size:i.size,lastModified:i.mtime.toISOString()})}}return t.sort((r,s)=>r.name.localeCompare(s.name))}function im(e){let t=e.name;if(De.isValid()){let c=Yt.get(t);if(c)return c}let n=e.resources.map(c=>Xa(c)),r=n.map(c=>new Date(c.createdAt).getTime()),s=Math.min(...r),o=Math.max(...r),i=zr(n.map(c=>c.status)),a={name:e.name,resourceCount:n.length,createdAt:new Date(s).toISOString(),lastModified:new Date(o).toISOString(),resources:n,overallStatus:i};return Yt.set(t,a),De.markUpdated(),a}var Za={[O]:{name:O,description:"Generate and load the stack-level PostgreSQL docker-compose file",generatedFiles:["services/platform/database-management/docker-compose.yml"]}};function ts(e,t){return Za[t]?["pre_alpha","alpha","beta"].some(n=>e[n]?.enabledStacks?.includes(t)):!1}var tc=ec(new URL("../dist/generator/template-engine.js",import.meta.url).href),en=g.dirname(tc);function Ce(e){let t=[g.join(en,"..","..","templates"),g.join(en,"..","templates"),g.join(en,"..","..","..","templates"),g.join(process.cwd(),"node_modules","@tdk-landscape","tdk-cli-core","templates"),g.join(g.dirname(process.execPath),"tdk-cli","cli","templates")],n=[];for(let r of t){let s=g.join(r,e);n.push(s);try{return S.readFileSync(s,"utf-8")}catch{}}throw new Error(`Failed to load template ${e} from any of these paths: ${n.join(", ")}`)}var tn=null;function nc(){return tn||(tn={[J]:Ce(".tiltignore.hbs"),[Je]:Ce("TILT_RESOURCE_DEFAULTS.star.hbs"),[We]:Ce("TILT_TECH_STACK.star.hbs"),Tiltfile:Ce("Tiltfile.hbs"),"spec.master":Ce("spec.master.hbs")}),tn}var rc={proxy:"Traefik reverse proxy",verdaccio:"Private npm registry","database-management":"PostgreSQL database"},sc={monitoring:"Signoz, SkyWalking (RAM intensive)",elk:"Elasticsearch/Logstash/Kibana",debezium:"CDC with Kafka/Zookeeper",golden_image:"Golden image rebuild (one-time)",verdaccio:"Premium local npm registry"},rs=["TILT_TECH_STACK.star","TILT_RESOURCE_DEFAULTS.star","spec.master","Tiltfile",".tiltignore"],tt=class{templatesDir;constructor(t){let n=bt("node:url").fileURLToPath(new URL("../dist/generator",import.meta.url))||g.dirname(new URL(new URL("../dist/generator/template-engine.js",import.meta.url).href).pathname);this.templatesDir=t||g.join(n,"..","..","templates"),this.registerHelpers()}quoteStarlarkString(t){return`"${t.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}formatStarlarkValue(t){return t==null?"None":typeof t=="string"?this.quoteStarlarkString(t):typeof t=="boolean"?t?"True":"False":typeof t=="number"?String(t):Array.isArray(t)?`[${t.map(r=>this.formatStarlarkValue(r)).join(", ")}]`:typeof t=="object"?`{${Object.entries(t).map(([r,s])=>`${this.quoteStarlarkString(r)}: ${this.formatStarlarkValue(s)}`).join(", ")}}`:String(t)}registerHelpers(){oe.registerHelper("starlark",t=>new oe.SafeString(this.formatStarlarkValue(t))),oe.registerHelper("starlarkArray",t=>{if(!Array.isArray(t))return new oe.SafeString("[]");let n=t.map(r=>this.formatStarlarkValue(r));return new oe.SafeString(`[${n.join(", ")}]`)}),oe.registerHelper("json",t=>JSON.stringify(t))}buildContext(t){return{version:F.version,tech:F.tech,ports:F.ports,health:F.health,naming:F.naming,traefik:F.traefik,filewatchIgnores:[...F.filewatchIgnores],serviceTypes:F.serviceTypes,paths:F.paths,discovery:{scanIntervalSeconds:F.discovery.scanIntervalSeconds,maxManifestsPerRoot:F.discovery.maxManifestsPerRoot,paths:t.discovery.paths},docker:F.docker,runtime:F.runtime,project:t.project,phases:oc(t.phases),alwaysEnabledInfra:t.always_enabled_infra??[...$e],optionalInfra:t.optional_infra,serviceDescriptions:rc,infraDescriptions:sc}}loadTemplate(t){let n=g.join(this.templatesDir,`${t}.hbs`),r=S.existsSync(n)?S.readFileSync(n,"utf-8"):nc()[t];if(!r)throw new Error(`Template not found: ${n}`);return oe.compile(r)}generateTechStack(t){return this.loadTemplate(We)(t)}generateServiceDefaults(t){return this.loadTemplate(Je)(t)}generateSpecMaster(t){return this.loadTemplate("spec.master")(t)}generateTiltfile(t){return this.loadTemplate("Tiltfile")(t)}generateTiltIgnore(t){return this.loadTemplate(".tiltignore")(t)}generateAll(t){let n=this.buildContext(t);return{"TILT_TECH_STACK.star":this.generateTechStack(n),"TILT_RESOURCE_DEFAULTS.star":this.generateServiceDefaults(n),"spec.master":this.generateSpecMaster(n),Tiltfile:this.generateTiltfile(n),".tiltignore":this.generateTiltIgnore(n)}}};function oc(e){let t={...e};for(let n of Object.keys(t))t[n]={...t[n],enabledStacks:Array.from(new Set(t[n].enabledStacks))};return t}function ic(e){return e.replace(/-/g,"_")}function ac(e){let t=ic(e.project.name||"tdk_project");return`###############################################################################
|
|
30
|
+
# SYSTEM-GENERATED - DO NOT EDIT
|
|
31
|
+
# Stack feature: database-management
|
|
32
|
+
# Source: .tdk/project.json phases.*.enabledStacks includes "database-management"
|
|
33
|
+
# Regenerate with: tdk config regenerate
|
|
34
|
+
###############################################################################
|
|
35
|
+
|
|
36
|
+
services:
|
|
37
|
+
postgres:
|
|
38
|
+
image: postgres:16-alpine
|
|
39
|
+
container_name: ${t}_postgres
|
|
40
|
+
restart: unless-stopped
|
|
41
|
+
environment:
|
|
42
|
+
POSTGRES_USER: ${t}
|
|
43
|
+
POSTGRES_PASSWORD: \${DB_PASSWORD}
|
|
44
|
+
POSTGRES_DB: postgres
|
|
45
|
+
ports:
|
|
46
|
+
- "\${TDK_BIND_ADDRESS:-127.0.0.1}:\${TDK_POSTGRES_PORT:-15432}:5432"
|
|
47
|
+
volumes:
|
|
48
|
+
- ${t}_postgres_data:/var/lib/postgresql/data
|
|
49
|
+
networks:
|
|
50
|
+
- database
|
|
51
|
+
healthcheck:
|
|
52
|
+
test: ["CMD-SHELL", "pg_isready -U ${t} -d postgres"]
|
|
53
|
+
interval: 5s
|
|
54
|
+
timeout: 5s
|
|
55
|
+
retries: 20
|
|
56
|
+
|
|
57
|
+
networks:
|
|
58
|
+
database:
|
|
59
|
+
name: ${t}_database
|
|
60
|
+
external: true
|
|
61
|
+
|
|
62
|
+
volumes:
|
|
63
|
+
${t}_postgres_data:
|
|
64
|
+
`}function ns(e,t){if(!e||typeof e!="object")return!1;let n=e;for(let r of["pre_alpha","alpha","beta","out_of_scope"]){let s=n[r];if(!s||typeof s!="object"||!Array.isArray(s[t]))return!1}return!0}function cc(e){if(!e||typeof e!="object")return null;let t=e;if(typeof t.version!="string"||!t.project||typeof t.project!="object")return null;let n=t.project;if(typeof n.name!="string"||typeof n.version!="string")return null;let r=null;if(ns(t.phases,"enabledStacks"))r=t.phases;else if(ns(t.stacks,"services")){let i=t.stacks;r={pre_alpha:{name:i.pre_alpha.name,description:i.pre_alpha.description,enabledStacks:i.pre_alpha.services},alpha:{name:i.alpha.name,description:i.alpha.description,enabledStacks:i.alpha.services},beta:{name:i.beta.name,description:i.beta.description,enabledStacks:i.beta.services},out_of_scope:{name:i.out_of_scope.name,description:i.out_of_scope.description,enabledStacks:i.out_of_scope.services}}}if(!r||!t.optional_infra||typeof t.optional_infra!="object")return null;let s=t.optional_infra;if(typeof s.monitoring!="boolean"||typeof s.elk!="boolean"||typeof s.debezium!="boolean"||typeof s.golden_image!="boolean"||typeof s.verdaccio!="boolean"||!t.discovery||typeof t.discovery!="object")return null;let o=t.discovery;return Array.isArray(o.paths)?{version:t.version,project:t.project,phases:r,always_enabled_infra:t.always_enabled_infra,optional_infra:t.optional_infra,discovery:t.discovery,overrides:t.overrides}:null}var nn=class extends Error{};function nt(e){let t=g.join(e,".tdk",_);if(!S.existsSync(t))throw new nn(`Project config not found: ${t}. Run 'tdk project --yes' first.`);let n=S.readFileSync(t,"utf-8"),r=JSON.parse(n),s=cc(r);if(!s)throw new Error("Invalid project.json: missing or invalid required fields. Expected: version (string), project (object with name/version), phases (object with pre_alpha/alpha/beta/out_of_scope enabledStacks), optional_infra (object with boolean flags), discovery (object with paths array)");return s}async function dc(e){let t=x(e).filter(r=>r.config?.sablier?.enable===!0);t.length===0||await qn(e)||console.warn(`\u26A0\uFE0F Sablier (on-demand scaling) is a Premium feature - no valid license key found. ${t.length} resource${t.length===1?" has":"s have"} a \`sablier\` block, but it will have no effect without a license. Set TDK_LICENSE_KEY to a key that grants it, or remove the \`sablier\` block from the affected service.json file(s) to silence this warning.`)}function uc(e,t){let n=se(e,R);if(S.existsSync(n))return!1;let r={name:t.project.name||g.basename(e),version:t.project.version||"1.0.0",private:!0,type:"module",workspaces:t.discovery.paths};return Ot(n,`${JSON.stringify(r,null,2)}
|
|
65
|
+
`),!0}async function xm(e,t={}){let n=nt(e),r=Qt(e).flatMap(u=>Nr(u,g.relative(e,u).split(g.sep).join("/")));if(r.length>0)throw new Error(`Refusing to generate config from unsafe service.json values:
|
|
66
|
+
${r.map(u=>` ${u}`).join(`
|
|
67
|
+
`)}`);await dc(e),uc(e,n)&&!t.quiet&&console.log("\u2713 Generated: package.json (workspace root)");let s=Object.values(n.phases).some(u=>u.enabledStacks.includes("verdaccio"));(n.optional_infra.verdaccio||s)&&(await He(e)||console.warn("\u26A0\uFE0F Verdaccio is a Premium feature - no valid license key found. It's enabled in this project's config, but will have no effect without a license. Set TDK_LICENSE_KEY to a key that grants it, or run `tdk config disable-infra verdaccio` to silence this warning."));let o=g.join(e,".tdk",".tdk-out");S.existsSync(o)||S.mkdirSync(o,{recursive:!0});let a=new tt().generateAll(n);for(let u of rs){let p=a[u],k=se(e,g.join(".tdk",".tdk-out",u));Pe(k,p),t.quiet||console.log(`\u2713 Generated: .tdk/.tdk-out/${u}`)}if(ts(n.phases,O)){let u=se(e,g.join("services","platform",O,re)),p=g.dirname(u);S.mkdirSync(p,{recursive:!0}),Pe(u,ac(n)),t.quiet||console.log("\u2713 Generated: services/platform/database-management/docker-compose.yml")}let c=g.join(o,J),d=se(e,J);if(S.existsSync(c)){if(!S.existsSync(d))S.copyFileSync(c,d),console.log("\u2713 Copied: .tiltignore \u2192 project root");else if(t.discardHandEdits){let u=S.readFileSync(d,"utf-8");u.includes(is)&&u!==S.readFileSync(c,"utf-8")&&(S.copyFileSync(c,d),console.log("\u2713 Restored: .tiltignore \u2192 project root (hand edits discarded)"))}}let l=kc(e);for(let u of l)console.log(`\u2713 Copied: ${u} \u2192 project root`);await yc(e),console.log(""),console.log("\u{1F4A1} To start Tilt: tdk up")}var lc=/\.(?:test|spec)\.[cm]?[jt]sx?$/;function ss(e){return lc.test(e)}function os(e){if(S.existsSync(e))for(let t of S.readdirSync(e,{withFileTypes:!0})){let n=g.join(e,t.name);t.isDirectory()?os(n):ss(n)&&S.rmSync(n,{force:!0})}}var pc=["engine","discovery","specs","ext"],fc=[["shared-platform-engineering","docker-templates"]],mc=["install-deps.sh","bun-hoisted-symlink-fix.sh","prisma-bun-client-link-fix.sh","prisma-normalize-client.sh"];function hc(){let e=bt("node:url").fileURLToPath(new URL("../dist/generator",import.meta.url))||process.cwd();for(let t=0;t<6;t++){if(S.existsSync(g.join(e,"Tiltfile"))&&S.existsSync(g.join(e,"engine")))return e;let n=g.dirname(e);if(n===e)break;e=n}return null}function gc(){let e=bt("node:url").fileURLToPath(new URL("../dist/generator",import.meta.url))||process.cwd();for(let r=0;r<8;r++){if(S.existsSync(g.join(e,"shared-platform-engineering","docker-templates")))return e;let s=g.dirname(e);if(s===e)break;e=s}let t=g.dirname(process.execPath);return[g.join(t,"tdk-cli"),g.join(process.cwd(),"node_modules","@tdk-landscape","tdk-cli-core")].find(r=>S.existsSync(g.join(r,"shared-platform-engineering","docker-templates")))??null}function kc(e){let t=gc(),n=[];if(!t)return console.warn("\u26A0\uFE0F TDK runtime assets not found - generated Dockerfiles may fail. Reinstall or upgrade TDK, then re-run `tdk project`."),n;for(let r of fc){let s=g.join(t,...r),o=se(e,g.join(...r));S.existsSync(s)&&(S.mkdirSync(g.dirname(o),{recursive:!0}),S.cpSync(s,o,{recursive:!0}),n.push(r.join("/")))}for(let r of mc){let s=g.join(e,"shared-platform-engineering","docker-templates",r);S.existsSync(s)||console.warn(`\u26A0\uFE0F Missing runtime asset after copy: ${s}`)}return n}async function yc(e){let t=se(e,g.join(".tdk",".tdk-out","tdk-cli-ext")),n=[];process.env.TDK_EXTENSION_SOURCE&&n.push(process.env.TDK_EXTENSION_SOURCE);let r=hc();r&&n.push(r);let s=g.dirname(process.execPath);n.push(g.join(s,"tdk-cli")),n.push(g.join(e,"..","tdk-cli"));let o="";for(let i of n)if(i&&S.existsSync(g.join(i,"Tiltfile"))&&S.existsSync(g.join(i,"engine"))){o=i;break}if(!o){console.warn("\u26A0\uFE0F TDK extension not found - set TDK_EXTENSION_SOURCE to its checkout path and re-run `tdk project` (or `tdk config regenerate`) to vendor it before `tdk up`.");return}S.existsSync(t)||S.mkdirSync(t,{recursive:!0}),S.copyFileSync(g.join(o,"Tiltfile"),g.join(t,"Tiltfile"));for(let i of pc)os(g.join(t,i)),S.cpSync(g.join(o,i),g.join(t,i),{recursive:!0,filter:a=>!ss(a)});console.log("\u2713 Vendored TDK extension \u2192 .tdk/.tdk-out/tdk-cli-ext/");try{let{applyPremiumOverlay:i}=await import("./extension-fetch-6XNTDF44.js");await i(e,t)}catch(i){console.warn(`\u26A0\uFE0F Premium overlay skipped: ${i.message}`)}}var is="SYSTEM-GENERATED BY TDK CLI";function wm(e){let t=[],n=[],r=[],s=[],o=(p,k,m)=>{k!==m&&(t.push(`${k===void 0?"Missing file":"Out of sync"}: ${p} (run 'tdk config regenerate')`),r.push({file:p,diff:Sc(p,k??"",m)}))},i=nt(e),c=new tt().generateAll(i),d=g.join(e,".tdk",".tdk-out");for(let p of rs){let k=c[p],m=g.join(d,p);o(`.tdk/.tdk-out/${p}`,S.existsSync(m)?S.readFileSync(m,"utf-8"):void 0,k)}let l=g.join(e,J),u=S.existsSync(l)?S.readFileSync(l,"utf-8"):void 0;u!==void 0&&!u.includes(is)?n.push(".tiltignore: not generated by TDK, left as is"):o(J,u,c[J]);for(let p of Qt(e)){let k=g.relative(e,p).split(g.sep).join("/"),m=jr(p,k);t.push(...m.errors),n.push(...m.warnings);let T=Tc(e,p);t.push(...T.errors),s.push(...T.handEdited)}return{valid:t.length===0,errors:t,warnings:n,diffs:r,handEdited:s}}function Sc(e,t,n){let r=t.replace(/\n$/,"").split(`
|
|
68
|
+
`),s=n.replace(/\n$/,"").split(`
|
|
69
|
+
`),o=[`--- a/${e}`,`+++ b/${e}`,`@@ -1,${r.length} +1,${s.length} @@`];for(let i of r)o.push(`-${i}`);for(let i of s)o.push(`+${i}`);return o.join(`
|
|
70
|
+
`)}function Tc(e,t){let n=g.dirname(t),r=g.join(n,D);if(!S.existsSync(r))return{errors:[],handEdited:[]};let s=[],o=(l,u="")=>{for(let p of S.readdirSync(l,{withFileTypes:!0})){if(u===""&&p.name===".tdk-contract")continue;let k=g.join(u,p.name),m=g.join(l,p.name);p.isDirectory()?o(m,k):p.isFile()&&s.push({fullPath:m,relativeFile:k})}};if(o(r),s.length===0)return{errors:[],handEdited:[]};let i=g.join(r,".tdk-contract"),a=g.join(i,"service.json.source"),c=[],d=[];S.existsSync(a)?S.readFileSync(a,"utf-8")!==S.readFileSync(t,"utf-8")&&c.push(g.relative(e,t)+": service.json changed since generated outputs were written (run tdk up)"):c.push(g.relative(e,a)+": generated-file contract snapshot is missing (run tdk up to regenerate)");for(let l of s){if(l.relativeFile==="service.yaml")continue;let u=g.join(i,l.relativeFile),p=g.relative(e,l.fullPath);S.existsSync(u)?S.readFileSync(l.fullPath,"utf-8")!==S.readFileSync(u,"utf-8")&&(d.push(p.split(g.sep).join("/")),c.push(`${p}: generated file differs from its TDK snapshot (run tdk up to regenerate)`)):c.push(`${p}: generated-file snapshot is missing (run tdk up to regenerate)`)}return{errors:c,handEdited:d}}import{existsSync as bc}from"node:fs";var rt="docs/agent-hosts.md",rn=`WebContainers cannot run TDK: there is no Docker daemon or Tilt. Run \`tdk up\` on a machine with Docker. See ${rt}.`,as=`Docker is not reachable from this Dev Container. Mount the host Docker socket (-v /var/run/docker.sock:/var/run/docker.sock) or add the docker-outside-of-docker feature to devcontainer.json, then retry. Docker-in-docker is not supported. See ${rt}.`;function xc(){return{env:process.env,versions:process.versions,platform:process.platform,fileExists:bc}}function cs(e=xc()){let{env:t,versions:n,platform:r,fileExists:s}=e;return n.webcontainer?{kind:"webcontainer",canUp:!1}:t.CODESPACES==="true"?{kind:"codespaces",canUp:!0}:t.REMOTE_CONTAINERS||s("/.dockerenv")&&t.DEVCONTAINER?{kind:"devcontainer",canUp:!0}:r==="win32"?{kind:"native-windows",canUp:t.TDK_ALLOW_NATIVE_WINDOWS==="1"}:r==="linux"&&t.WSL_DISTRO_NAME?{kind:"wsl2",canUp:!0}:{kind:"local",canUp:!0}}function ds(e){return e==="devcontainer"||e==="codespaces"}function st(e){return e.some(t=>C(t.config?.appType)||t.config?.appType==="frontend"||t.config?.appType==="worker")}import{existsSync as wc,readFileSync as Ec}from"node:fs";import{join as vc,relative as us,resolve as ls,sep as Pc}from"node:path";var $c=2,ps=["services/*/*"];function fs(e){return e.split(Pc).filter(Boolean)}function _c(e,t){let n=0,r=0,s=-1,o=0;for(;r<t.length;)if(n<e.length&&e[n]!=="*"&&(e[n]==="?"||e[n]===t[r]))n++,r++;else if(n<e.length&&e[n]==="*")s=n++,o=r;else if(s!==-1)n=s+1,r=++o;else return!1;for(;n<e.length&&e[n]==="*";)n++;return n===e.length}function Rc(e,t){return e.length===t.length&&e.every((n,r)=>_c(n,t[r]))}function sn(e){let t=vc(e,".tdk",_);if(!wc(t))return ps;try{let n=JSON.parse(Ec(t,"utf-8"))?.discovery?.paths;if(Array.isArray(n)&&n.every(r=>typeof r=="string"))return n}catch{}return ps}function ot(e,t,n){let r=fs(us(e,ls(e,t)));return n.some(s=>{let o=fs(us(e,ls(e,s)));for(let i=0;i<=$c;i++){let a=r.length-i;if(a>0&&Rc(o,r.slice(0,a)))return!0}return!1})}function Om(e,t,n,r){let s=sn(e);if(ot(e,t,s))return{path:t};let o=`services/${n}/${r}`;return ot(e,o,s)?{path:o,adjustedFrom:t}:{path:t}}import{execFile as rd}from"node:child_process";import{promisify as sd}from"node:util";import{execFileSync as Ts,execSync as Ie}from"node:child_process";import{existsSync as on,readdirSync as Ac,readFileSync as an}from"node:fs";import{join as le}from"node:path";function ms(e,t){return e==="pending"||e==="in_progress"||e==="none"||t==="pending"||t==="none"}var hs="container is running, but its health check is failing";function gs(e){let t=e?.runtimeStatus??"";if(t!=="ok")return t;let n=e?.composeResourceInfo?.healthStatus;return n==="unhealthy"?"error":n==="starting"?"pending":t}var Ne=1e4,ks=3e3,Dc="http://localhost:4873";function bs(e){return`'${e.replace(/'/g,"'\\''")}'`}function je(e,t,n,r,s){return process.platform==="win32"?Ts(N(t)??t,n,{...s,windowsHide:!0}):e(r,s)}var Oc=[80,443];function cn(e){return e.replace(/-/g,"_")}function Cc(e,t){let n=new Set(t),r=[];for(let s of e.split(`
|
|
71
|
+
`)){let o=s.trim();if(!o)continue;let i=o.indexOf(" "),a=i>=0?o.slice(0,i):o,c=i>=0?o.slice(i+1):"",d=[];for(let l of n)c.split(/[\s,]+/).some(p=>p.startsWith(`${l}->`)||p.includes(`:${l}->`))&&d.push(l);d.length>0&&r.push({name:a,ports:c,publishedPorts:d})}return r}function xs(e,t){let n=e.toLowerCase(),r=t.toLowerCase();return n===`${r}_traefik`||n.startsWith(`${r}_traefik`)}function Nc(e,t){return e.filter(n=>!xs(n.name,t))}function jc(){try{return new Set(Oe().filter(e=>e.config?.sablier?.enable&&e.config?.sablier?.deferStart).map(e=>e.name))}catch{return new Set}}function Ic(e,t=new Set){let r=JSON.parse(e).items??[],s=[],o=0,i=0,a=0;for(let c of r){let d=c.metadata?.name??"unknown",l=c.status?.updateStatus??"",u=gs(c.status),p=u==="error"&&c.status?.runtimeStatus==="ok",m=(c.status?.buildHistory?.[0]?.error??"").trim()||(p?hs:"");if(l==="error"||u==="error"){s.push({name:d,updateStatus:l,runtimeStatus:u,error:m.replace(/\s+/g," ").slice(0,800)});continue}if(l==="ok"&&(u==="ok"||u==="not_applicable")){a+=1;continue}d!=="(Tiltfile)"&&ms(l,u)&&(t.has(d)?i+=1:o+=1)}return{failures:s,pendingCount:o,deferredCount:i,okCount:a,total:r.length}}var Fc=/failed to resolve source metadata for docker\.io\/library\/([A-Za-z0-9._-]+-l[1-4](?:-[a-z]+)*)(?::[^\s:]+)?:/i;function dn(e){let t=e.replace(/\s+/g," ").trim(),n=t.match(/Bind for [^ ]+:(\d+) failed: port is already allocated/i);if(n)return`host port ${n[1]} is already allocated`;if(/port is already allocated/i.test(t))return"a required host port is already allocated";let r=[...t.matchAll(/ConnectionRefused downloading package manifest (@[A-Za-z0-9._/-]+)/gi)].map(a=>a[1]);if(r.length>0){let a=[...new Set(r)],c=a.slice(0,4).join(", "),d=a.length>4?` (+${a.length-4} more)`:"";return`private registry ConnectionRefused for ${c}${d} \u2014 Verdaccio (:4873) is down or unreachable from the build`}let s=t.match(Fc);if(s)return`base image ${s[1]} was missing when this build started (the golden layers weren't built yet, or golden-layers-build failed) - Tilt does not retry a failed build by itself`;if(/install-deps\.sh/i.test(t)||/bun install(?: --production)?/i.test(t)||/npm (?:ci|install)/i.test(t))return"dependency install failed during ImageBuild \u2014 usually Verdaccio (:4873) unreachable or missing @scoped packages (ConnectionRefused downloading package manifest)";let o=t.match(/No such image:\s*([^\s]+)/i)?.[1]??t.match(/pull access denied for ([^,:\s]+)/i)?.[1]??void 0,i=t.match(/\bup\s+-d\s+--no-build\s+([A-Za-z0-9._-]+)/i)?.[1];if(o||i||/docker compose .*\bup\s+-d\s+--no-build\b/i.test(t)){let a=o??`${i??"service"}:dev`,c=i?` for ${i}`:"";return`compose run failed because image is missing (${a})${c} \u2014 wait for the ImageBuild resource to finish, then re-trigger the *-run-only resource`}return/bun run build/i.test(t)?"ImageBuild failed during `bun run build` (compile/typecheck) \u2014 open the resource logs in Tilt for the TypeScript/build error":(/ImageBuild:/i.test(t)||/^Command "/i.test(t),t.slice(0,220))}function ys(e){return/ConnectionRefused downloading package manifest|Verdaccio|private registry|dependency install failed during ImageBuild|install-deps\.sh|bun install/i.test(e)}function un(e=y()??process.cwd()){let t=le(e,".tdk",_);if(!on(t))return!1;try{let n=JSON.parse(an(t,"utf-8"));return n.optional_infra?.verdaccio===!0||Object.values(n.phases??{}).some(r=>(r.enabledStacks??[]).includes("verdaccio"))}catch{return!1}}function ws(e){for(let t of[le(e,".npmrc"),le(e,R)])if(on(t))try{let n=an(t,"utf-8");if(/:4873\b/.test(n)||/verdaccio/i.test(n))return!0}catch{}return!1}function Lc(e=y()??process.cwd()){return un(e)||ws(e)}function ln(e=Ie,t=y()??process.cwd(),n=process.env.VERDACCIO_URL??process.env.NPM_REGISTRY_URL??Dc,r=!0){if(!r&&!ws(t))return{name:"Private npm registry",didPass:!0,isSkipped:!0,message:"Verdaccio is enabled in the project config but needs a Premium license, so TDK will not start it - skipped"};if(!Lc(t))return{name:"Private npm registry",didPass:!0,isSkipped:!0,message:"Private npm registry (Verdaccio) not enabled for this project - skipped"};let s=!1;try{let a=je(e,"docker",["ps","--format","{{.Names}}"],"docker ps --format '{{.Names}}'",{stdio:"pipe",encoding:"utf-8",timeout:Ne});s=/verdaccio/i.test(a)}catch{}let o=!1,i="";try{let a=process.platform==="win32"?Ts("curl.exe",["-fsS","-o","NUL","-w","%{http_code}","--max-time","2",n],{stdio:"pipe",encoding:"utf-8",timeout:ks+1e3,windowsHide:!0}).trim():e(`curl -fsS -o /dev/null -w '%{http_code}' --max-time 2 ${bs(n)}`,{stdio:"pipe",encoding:"utf-8",timeout:ks+1e3}).trim();o=/^[23]\d\d$/.test(a),i=`HTTP ${a}`}catch(a){i=a instanceof Error?a.message.slice(0,120):"request failed"}return o?{name:"Private npm registry",didPass:!0,message:`Private npm registry reachable at ${n}${s?" (verdaccio container running)":""}`}:s?{name:"Private npm registry",didPass:!1,message:`Verdaccio container is running but ${n} is not responding (${i}). ImageBuilds that install @scoped packages will fail with ConnectionRefused downloading package manifest.`,fix:"Check `docker logs` for the verdaccio container, confirm port 4873 is published, then re-run `tdk doctor`."}:{name:"Private npm registry",didPass:!1,message:`Private npm registry (Verdaccio) is not reachable at ${n}. Docker ImageBuilds will fail with ConnectionRefused downloading package manifests for @tdk-landscape/* and project-scoped packages.`,fix:"Start the registry with `bun run verdaccio:start` (or `docker compose -f docker-compose.verdaccio.yml up -d`), publish packages with `bun run publish:all` / `scripts/build/publish-to-verdaccio.sh`, then `tilt trigger` failed resources or re-run `tdk up`."}}function pn(e=Ie,t=ce(),n=Oc){let r=cn(t),s="";try{s=je(e,"docker",["ps","--format","{{.Names}}\\t{{.Ports}}"],"docker ps --format '{{.Names}}\\t{{.Ports}}'",{stdio:"pipe",encoding:"utf-8",timeout:Ne})}catch{return{name:"Ingress Ports",didPass:!0,isSkipped:!0,message:"Docker not available - skipped ingress port check"}}let o=Cc(s,n),i=Nc(o,r);if(i.length===0){let c=o.filter(d=>xs(d.name,r));return c.length>0?{name:"Ingress Ports",didPass:!0,message:`Ingress ports ${n.join("/")} held by this project's Traefik (${c.map(d=>d.name).join(", ")})`}:{name:"Ingress Ports",didPass:!0,message:`Ingress ports ${n.join("/")} are free for Traefik`}}return{name:"Ingress Ports",didPass:!1,message:`Traefik cannot bind ingress ports because another container already owns them:
|
|
72
|
+
${i.map(c=>{let d=c.publishedPorts.join(", ");return`${c.name} (host ports ${d})`}).join(`
|
|
73
|
+
`)}`,fix:`Stop the foreign container(s), e.g. \`docker stop ${i[0]?.name}\`, or shut down the other TDK/Tilt project using that Traefik. Then re-run \`tdk up\`.`}}function Mc(e){let{status:t}=e;return t===void 0?e.error??"no response":t===404?"HTTP 404: Traefik is answering but has no route for this URL (container not started yet, or not running)":t===502||t===503||t===504?`HTTP ${t}: Traefik found the route but the container isn't answering (still starting, or unhealthy)`:`HTTP ${t}`}function fn(e){let t=e.filter(o=>o.status!==void 0),n=e.filter(o=>!o.ok);if(t.length===0)return{name:"Service Health",didPass:!0,isSkipped:!0,message:`No services responded - skipped ping of ${b(e.length,"service")} (stack may be down, or Traefik never bound :80)`,fix:"If `tdk up` is already running, check Traefik/port 80 in the Tilt UI. Otherwise start with: tdk up"};if(n.length===0)return{name:"Service Health",didPass:!0,message:`All ${b(e.length,"service")} responding on /health`};let r=n.map(o=>`${o.name} (${Mc(o)})
|
|
74
|
+
${o.url}`).join(`
|
|
75
|
+
`),s=n.some(o=>o.status===404);return{name:"Service Health",didPass:!1,message:`${b(n.length,"service")} not healthy (${e.length-n.length}/${e.length} healthy):
|
|
76
|
+
${r}`,fix:s?"If `tdk up` just started, images may still be building: re-run `tdk doctor` in a minute. Otherwise check `docker ps` and the resource in the Tilt UI, and compare `tdk networks` with the URL above.":"Check container state and routing: docker ps, then tdk networks to compare the advertised URLs against Traefik's routers"}}function mn(e){if(e.length===0)return{name:"Service Smoke",didPass:!0,isSkipped:!0,message:"No read-only smoke checks declared"};let t=e.filter(r=>!r.ok);if(t.length===0)return{name:"Service Smoke",didPass:!0,message:`All ${b(e.length,"service")} pass their smoke checks`};let n=t.map(r=>r.failure??`${r.name}: failed`).join(`
|
|
77
|
+
`);return{name:"Service Smoke",didPass:!1,message:`${b(t.length,"service")} failing a smoke check (${e.length-t.length}/${e.length} passing):
|
|
78
|
+
${n}`,fix:'Add the route the smoke step expects, or correct its "path" in the service.json "smoke" block. Then re-run tdk doctor.'}}function Es(e){let t=e.filter(s=>s.status!==void 0);if(t.length===0)return{name:"Advertised Endpoints",didPass:!0,isSkipped:!0,message:"No services responded - skipped advertised endpoint check"};let n=t.filter(s=>s.status===404);if(n.length===0)return{name:"Advertised Endpoints",didPass:!0,message:`${b(t.length,"advertised endpoint")} reachable`};let r=n.map(s=>`${s.name} (404)
|
|
79
|
+
${s.url}`).join(`
|
|
80
|
+
`);return{name:"Advertised Endpoints",didPass:!0,isWarning:!0,message:`${b(n.length,"advertised link")} ${n.length===1?"returns":"return"} 404 (the service may be healthy, but nothing answers at this exact path):
|
|
81
|
+
${r}`,fix:'Nothing handles this exact path, so opening the link shows a 404 even when /health is fine. If this API has no root route, you can leave it. Otherwise add a GET route for it (for example one that returns the service name), or declare a read-only "smoke" step on a real route in service.json. The URL tdk networks and the TDK App print stays the same until the CLI changes how it reports it.'}}function hn(e=Ie){let t="";try{t=je(e,"tilt",["get","uiresources","-o","json"],"tilt get uiresources -o json",{stdio:"pipe",encoding:"utf-8",timeout:Ne})}catch{return{name:"Tilt Resources",didPass:!0,isSkipped:!0,message:"Tilt is not running - skipped resource status check",fix:"Start them with: tdk up"}}let n;try{n=Ic(t,jc())}catch{return{name:"Tilt Resources",didPass:!0,isSkipped:!0,message:"Could not parse Tilt resource status - skipped"}}if(n.total===0)return{name:"Tilt Resources",didPass:!0,isSkipped:!0,message:"No Tilt UIResources found"};if(n.failures.length===0){let w=n.deferredCount>0?`, ${n.deferredCount} deferred`:"";return{name:"Tilt Resources",didPass:!0,message:`Tilt resources healthy (${n.okCount} ok, ${n.pendingCount} pending${w})`}}let r=Bc(n.failures),o=r.slice(0,20),i=r.length-o.length,a=o.map(w=>({failure:w,why:Kc(w,e)})),c=a.map(({failure:w,why:f})=>`${w.name}: ${f}`).join(`
|
|
82
|
+
`),d=i>0?`
|
|
83
|
+
\u2026and ${b(i,"more failed resource")}`:"",l=n.pendingCount>0?`
|
|
84
|
+
${b(n.pendingCount,"resource")} still pending/not started \u2014 often blocked by the failures above.`:"",u=n.deferredCount>0?`
|
|
85
|
+
${b(n.deferredCount,"resource")} intentionally deferred (sablier.deferStart) \u2014 not started until first request, unrelated to the failures above.`:"",p=r.some(w=>/port is already allocated/i.test(w.error)),k=r.some(w=>ys(w.error)||ys(dn(w.error))),m=a.some(({why:w})=>/Kafka broker unreachable|kafka:9092/i.test(w)),T="Open the Tilt UI URL printed by tdk up (default port "+ve.tiltUi+" when TILT_PORT is unset), inspect the red resources, fix the listed errors, then `tilt trigger <resource>` or re-run `tdk up`.";return p?T="Free the conflicting host port (usually 80/443): `docker ps --filter publish=80` then `docker stop <other-traefik>`. Re-trigger Traefik in the Tilt UI or run `tdk up` again.":k?T="Start Verdaccio (`bun run verdaccio:start`), ensure packages are published (`bun run publish:all` or `bash scripts/build/publish-to-verdaccio.sh`), confirm `curl http://localhost:4873` works, then `tilt trigger` the failed resources.":m&&(T="Kafka is not running (optional infra). Start it, point the bridge at a real broker, or disable Kafka-dependent resources until Kafka is enabled."),{name:"Tilt Resources",didPass:!1,message:`Tilt reports ${b(n.failures.length,"failed resource")}:
|
|
86
|
+
${c}${d}${l}${u}`,fix:T}}var Uc=/^(traefik|postgres|nats|infisical|verdaccio|proxy)([.-]|$)/i;function Ss(e){return Uc.test(e)}function Bc(e){return[...e].sort((t,n)=>{let r=Ss(t.name)?0:1,s=Ss(n.name)?0:1;return r!==s?r-s:t.name.localeCompare(n.name)})}function Hc(e,t=Ie,n=ce()){try{let r=je(t,"docker",["ps","-a","--format","{{.Names}}"],"docker ps -a --format '{{.Names}}'",{stdio:"pipe",encoding:"utf-8",timeout:Ne}).split(`
|
|
87
|
+
`).map(d=>d.trim()).filter(Boolean),s=`-autogenerated-${e}-1`,o=r.find(d=>d===e)??r.find(d=>d.startsWith(`${n}-`)&&d.endsWith(s))??r.find(d=>d.endsWith(s))??r.find(d=>d.includes(e));if(!o)return null;let i=je(t,"docker",["logs","--tail","120",o],`docker logs --tail 120 ${bs(o)} 2>&1`,{stdio:"pipe",encoding:"utf-8",timeout:Ne});if(/getaddrinfo ENOTFOUND/i.test(i)&&/kafka:9092/i.test(i))return"runtime crash: Kafka broker unreachable (getaddrinfo ENOTFOUND kafka:9092)";if(/ECONNREFUSED/i.test(i)&&/kafka/i.test(i))return"runtime crash: Kafka connection refused";let a=i.match(/ConnectionRefused downloading package manifest (@[A-Za-z0-9._/-]+)/i);if(a?.[1])return`runtime/build log: private registry ConnectionRefused for ${a[1]}`;if(/ZodError/i.test(i)){let d=[...i.matchAll(/"path"\s*:\s*\[\s*"([^"]+)"\s*\]/g)].map(u=>u[1]),l=[...new Set(d)].slice(0,6);return l.length>0?`runtime crash: ZodError missing required config/env: ${l.join(", ")}`:"runtime crash: ZodError during config/env validation"}let c=i.match(/error:\s+Failed to [^:\n]+[^\n]{0,120}/i)?.[0]??i.match(/Failed to (?:start|initialize) [^\n]{0,120}/i)?.[0]??i.match(/^\s*error:\s+[^\n]{0,160}/im)?.[0];if(c)return`runtime crash: ${c.replace(/\s+/g," ").trim().slice(0,180)}`}catch{return null}return null}function Kc(e,t=Ie){return e.error?dn(e.error):e.runtimeStatus==="error"?Hc(e.name,t)??"runtime error (container crashed or unhealthy \u2014 check Tilt/docker logs)":e.updateStatus||e.runtimeStatus||"unknown error"}var Gc=/\.(ts|tsx|mts|js|mjs|cjs)$/,Vc=new Set(["node_modules","dist",D,"tests","__tests__"]);function vs(e){let t=[];for(let n of Ac(e,{withFileTypes:!0}))n.isDirectory()?Vc.has(n.name)||t.push(...vs(le(e,n.name))):Gc.test(n.name)&&!/\.(test|spec)\./.test(n.name)&&t.push(le(e,n.name));return t}function Wc(e){let t=e?.healthCheckPath||"/health";return{container:t,traefik:e?.traefik?.healthCheck||t}}function Jc(e){let t=[];for(let n of e){if(!C(n.config?.appType))continue;let r=le(n.path,"src");if(!on(r))continue;let s=vs(r);if(s.length===0)continue;let o=s.map(d=>an(d,"utf-8")),{container:i,traefik:a}=Wc(n.config),c=i===a?[{path:i,usedBy:"container and Traefik healthchecks"}]:[{path:i,usedBy:"container healthcheck"},{path:a,usedBy:"Traefik healthcheck"}];for(let{path:d,usedBy:l}of c){let u=["'",'"',"`"].map(p=>`${p}${d}${p}`);o.some(p=>u.some(k=>p.includes(k)))||t.push({name:n.name,healthPath:d,usedBy:l})}}return t}function Ps(e=y()??process.cwd()){let t=[];try{t=x(e)}catch{return{name:"Health Routes",didPass:!0,isSkipped:!0,message:"Could not read services - skipped health route check"}}let n=t.filter(o=>C(o.config?.appType));if(n.length===0)return{name:"Health Routes",didPass:!0,isSkipped:!0,message:"No backend services to check"};let r=Jc(n);return r.length===0?{name:"Health Routes",didPass:!0,message:`All ${b(n.length,"backend")} define their health route`}:{name:"Health Routes",didPass:!1,message:`Backends without a health route (the container never turns healthy, so Traefik returns 404):
|
|
88
|
+
${r.map(o=>`${o.name}: no "${o.healthPath}" route in src/ (probed by the ${o.usedBy})`).join(`
|
|
89
|
+
`)}`,fix:`Add the route, e.g. for Hono: app.get("${r[0]?.healthPath}", (c) => c.json({ status: "ok" })). To use a different path, set "healthCheckPath" (container healthcheck) or "traefik.healthCheck" (Traefik) in its service.json.`}}import{existsSync as Qc,readFileSync as Zc}from"node:fs";import{join as ed}from"node:path";import{randomBytes as $s}from"node:crypto";import{existsSync as kn,readFileSync as _s,writeFileSync as Rs}from"node:fs";import{join as yn}from"node:path";var pe=[{name:"VERDACCIO_URL_DOCKER",description:"Docker-internal URL of the private npm registry (Verdaccio, a paid feature)",required:!1,default:"http://verdaccio:4873",example:"http://verdaccio:4873"},{name:"VERDACCIO_URL",description:"Local URL of the private npm registry (Verdaccio, a paid feature)",required:!1,default:"http://localhost:4873"},{name:"TILT_ENV",description:"Tilt environment name",required:!0,default:"dev",example:"dev",complete:!0},{name:"DATABASE_URL",description:"PostgreSQL connection string (uses the generated DB_PASSWORD below)",required:!1},{name:"DB_PASSWORD",description:"PostgreSQL superuser password (docker-compose POSTGRES_PASSWORD)",required:!0,example:"postgres",complete:!0},{name:"JWT_SECRET",description:"Secret that signs local-jwt tokens (generated once per project; never shared between projects)",required:!1,complete:!0}],As=()=>$s(16).toString("hex"),qc=()=>$s(32).toString("hex");function gn(e,t){return e==="DB_PASSWORD"?t:e==="JWT_SECRET"?qc():e==="DATABASE_URL"?`postgresql://postgres:${t}@postgres:5432/app_dev`:pe.find(n=>n.name===e)?.default??""}function zc(e){let t=e.trim(),n=t[0];if(n==='"'||n==="'"){let s=!1;for(let o=1;o<t.length;o+=1){let i=t[o];if(s){s=!1;continue}if(i==="\\"){s=!0;continue}if(i===n){let a=t.slice(1,o);if(n==='"'){let c={n:`
|
|
90
|
+
`,r:"\r",t:" ",'"':'"',"\\":"\\",$:"$"};return a.replace(/\\([nrt"\\$])/g,(d,l)=>c[l]??l)}return a.replace(/\\'/g,"'")}}return t}let r=e.search(/\s#/);return(r===-1?e:e.slice(0,r)).trim()}function fe(e){let t=new Map,n=e.startsWith("\uFEFF")?e.slice(1):e;for(let r of n.split(/\r?\n/)){let s=r.trim();if(!s||s.startsWith("#"))continue;let o=s.startsWith("export ")?s.slice(7).trim():s,i=o.indexOf("=");if(i<=0)continue;let a=o.slice(0,i).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(a)&&t.set(a,zc(o.slice(i+1)))}return t}function Ds(e){let t=[`# ${e.description}`];return e.required&&t.push("# Required (the value below is a working default)"),e.example&&t.push(`# Example: ${e.example}`),t}function Os(){let e=As(),t=["# \u{1F527} TDK Environment Configuration","# Generated by `tdk project`. The defaults work as-is; edit only to change them.","# Keep this file out of git: it holds a generated database password and JWT secret.","#","# After editing, run: tdk config regenerate && tdk up",""];for(let n of pe)t.push(...Ds(n)),t.push(`${n.name}=${gn(n.name,e)}`),t.push("");return t.join(`
|
|
91
|
+
`)}function Yc(e){let t=e.match(/^[a-z][a-z0-9+.-]*:\/\/[^:/@\s]+:([^@\s]*)@/i);if(!(!t||t[1].includes("$")))return t[1]}function Cs(e){let t=yn(e,".env"),n={missing:[],invalid:[],warnings:[]};if(!kn(t))return{missing:pe.filter(i=>i.required).map(i=>i.name),invalid:[],warnings:[".env file not found - will be auto-generated"]};let r=fe(_s(t,"utf-8"));for(let i of pe)i.required&&!r.has(i.name)&&n.missing.push(i.name),r.has(i.name)&&!(r.get(i.name)??"").trim()&&n.invalid.push(`${i.name} is set but empty`);let s=r.get("DATABASE_URL"),o=r.get("DB_PASSWORD");if(s&&o){let i=Yc(s);i!==void 0&&i!==o&&n.warnings.push("DATABASE_URL carries a different password than DB_PASSWORD; the database container uses DB_PASSWORD")}return n}function Xc(e,t,n){if(!fe(e).has(t))return e;let s=e.indexOf("=");if(s<0)return e;let o=e.slice(s+1),i=o.search(/\s#/),a=i<0?"":o.slice(i);return e.slice(0,s+1)+n+a}function Xm(e){let t=yn(e,".env");if(!kn(t))return Rs(t,Os(),{mode:384}),pe.map(u=>u.name);let n=_s(t,"utf-8"),r=fe(n),s=pe.filter(u=>u.complete?r.has(u.name)?!(r.get(u.name)??"").trim():!0:!1);if(s.length===0)return[];let o=s.filter(u=>!r.has(u.name)),i=s.filter(u=>r.has(u.name)),a=r.get("DB_PASSWORD"),c=a?.trim()?a:As(),d=n.split(/(\r\n|\n|\r)/);for(let u of i){let p=-1;for(let k=0;k<d.length;k+=2)fe(d[k]).has(u.name)&&(p=k);if(p<0)throw new Error(`Could not locate empty .env assignment for ${u.name}`);d[p]=Xc(d[p],u.name,gn(u.name,c))}let l=d.join("");if(o.length>0){let u=["","# Added by TDK: keys this version expects. Existing values above were not changed.",""];for(let k of o)u.push(...Ds(k)),u.push(`${k.name}=${gn(k.name,c)}`),u.push("");let p=l.endsWith(`
|
|
92
|
+
`)||l.endsWith("\r")?"":`
|
|
93
|
+
`;l+=p+u.join(`
|
|
94
|
+
`)}return Pe(t,l),s.map(u=>u.name)}function Qm(e){let t=yn(e,".env");return kn(t)?!1:(Rs(t,Os(),{mode:384}),!0)}var td="[REDACTED]",Fe=class extends Error{path;constructor(t,n){super(`Could not read ${t}: ${n}`),this.path=t,this.name="EnvUnreadableError"}},nd=8;function Sn(e){let t=ed(e,".env");if(!Qc(t))return[];let n;try{n=Zc(t,"utf-8")}catch(s){throw new Fe(t,s instanceof Error?s.message:String(s))}let r=[...fe(n).values()].filter(s=>s.length>=nd);return[...new Set(r)].sort((s,o)=>o.length-s.length)}function Le(e,t){let n=e;for(let r of t)n=n.split(r).join(td);return n}function it(e,t){return t.length===0?e:typeof e=="string"?Le(e,t):Array.isArray(e)?e.map(n=>it(n,t)):e!==null&&typeof e=="object"?Object.fromEntries(Object.entries(e).map(([n,r])=>[n,it(r,t)])):e}var od=sd(rd),id=1e4,ad=5,Ns=10*6e4,cd=3,dd=200,ud=async e=>{let{stdout:t,stderr:n}=await od("docker",e,{encoding:"utf-8",timeout:id,windowsHide:!0});return`${t}${n}`};function ld(e,t){let n=cn(t).toLowerCase(),r=e.replace(/-/g,"_").toLowerCase();return r===n||r.startsWith(`${n}_`)}function pd(e,t=Date.now()){if(e.status==="restarting")return!0;if(e.restartCount<ad)return!1;if(e.status==="running"){let r=Date.parse(e.startedAt);return Number.isNaN(r)||t-r<Ns}let n=Date.parse(e.finishedAt??"");return e.exitCode!==0&&!Number.isNaN(n)&&t-n<Ns}function fd(e,t){let n=/database "([^"]+)" does not exist/i.exec(e);return n?`database "${n[1]}" does not exist: re-run the project's database provisioning (tdk up) or create it`:/Cannot find module|MODULE_NOT_FOUND|module not found/i.test(e)?"a module is missing from the image: rebuild it (tdk up --force) after checking the resource's dependencies":/EADDRINUSE|address already in use/i.test(e)?"the port inside the container is already in use: check for two processes started by the same command":/ECONNREFUSED|connection refused|could not connect/i.test(e)?"a dependency is not reachable: check that the service it connects to is running and healthy":/\b(?:environment variable|env var)\b.*\b(?:missing|not set|required|undefined)\b/i.test(e)||/\b[A-Z][A-Z0-9_]{2,}\b (?:is )?(?:not set|required|missing)\b/.test(e)?"a required environment variable is missing: check the project's .env":t.exitCode===137?"killed (exit 137), usually out of memory: raise the container's memory limit":null}function md(e){let t=[];for(let n of e.split(/\r?\n/)){let[r,s,o,i,a,c]=n.split(" ");!r||s===void 0||t.push({name:r.replace(/^\//,""),restartCount:Number(s)||0,status:o??"",startedAt:i??"",exitCode:Number(a)||0,finishedAt:c??""})}return t}async function js(e,t=[],n=ud,r=Date.now()){let s="Crash-looping containers",o;try{o=(await n(["ps","-a","--format",'{{.ID}} {{.Label "com.docker.compose.project"}}'])).split(/\r?\n/).map(u=>u.split(" ")).filter(([u,p])=>u&&p&&ld(p,e)).map(([u])=>u)}catch{return{name:s,didPass:!0,isSkipped:!0,message:"Docker not available - skipped crash-loop check"}}if(o.length===0)return{name:s,didPass:!0,isSkipped:!0,message:"No project containers to check"};let i;try{i=md(await n(["inspect","--format","{{.Name}} {{.RestartCount}} {{.State.Status}} {{.State.StartedAt}} {{.State.ExitCode}} {{.State.FinishedAt}}",...o]))}catch{return{name:s,didPass:!0,isSkipped:!0,message:"Containers changed while checking - skipped crash-loop check"}}let a=i.filter(l=>pd(l,r));if(a.length===0)return{name:s,didPass:!0,message:"No project container is crash-looping"};let c=[],d=new Set;for(let l of a){let u="";try{u=await n(["logs","--tail",String(cd),l.name])}catch{}let p=Le(u,t).split(/\r?\n/).map(m=>m.trim().slice(0,dd)).filter(Boolean),k=fd(u,l);k&&d.add(k),c.push([`${l.name}: ${l.status}, restarted ${l.restartCount} times`,...p.map(m=>` | ${m}`)].join(`
|
|
95
|
+
`))}return{name:s,didPass:!1,message:`${a.length} container${a.length===1?" is":"s are"} crash-looping:
|
|
96
|
+
${c.join(`
|
|
97
|
+
`)}`,fix:[...d,"Read the full log: docker logs <container>"].join("; ")}}import{execFile as kd}from"node:child_process";import{realpathSync as Is}from"node:fs";import{join as yd}from"node:path";import{promisify as Sd}from"node:util";import{execFileSync as hd}from"node:child_process";var Tn=(e,t)=>hd(e,t,{encoding:"utf-8",stdio:"pipe",windowsHide:!0}),gd=["traefik-public","traefik-private","backend","database","infisical-network","verdaccio-network","proxy"];function bn(e){let t=e.replace(/-/g,"_");return gd.map(n=>`${t}_${n}`)}function uh(e,t=Tn){let n=new Set(t("docker",["network","ls","--format","{{.Name}}"]).split(/\r?\n/).map(s=>s.trim()).filter(Boolean)),r={removed:[],kept:[]};for(let s of bn(e))if(n.has(s))try{t("docker",["network","rm",s]),r.removed.push(s)}catch(o){let i=String(o.stderr??o).trim();r.kept.push({name:s,reason:i.split(/\r?\n/)[0]??""})}return r}function at(e,t=process.platform,n=Tn){if(t==="win32")return[];let r;try{r=n("ps",["-axo","pid=,command="])}catch{return[]}let s=[];for(let o of r.split(/\r?\n/)){let i=/^\s*(\d+)\s+(.*)$/.exec(o);if(!i)continue;let a=i[2]??"",c=(a.split(/\s+/)[0]??"").split("/").at(-1),d=` -f ${e}`,l=a.indexOf(d),u=a[l+d.length];c==="tilt"&&/^\S+\s+up\s/.test(a)&&l>0&&(u===void 0||u===" ")&&s.push(Number(i[1]))}return s}function lh(e,t=process.platform,n=Tn){let r=at(e,t,n);for(let s of r)try{n("kill",["-TERM",String(s)])}catch{}return r}async function ph(e,t=8e3,n=at){let r=Date.now()+t;for(;n(e).length>0;){if(Date.now()>=r)return!1;await new Promise(s=>setTimeout(s,250))}return!0}var Fs=Sd(kd),Td=15e3,bd=/^tdk_doctor_probe_(\d+)$/,xd=async e=>{let{stdout:t}=await Fs("docker",e,{encoding:"utf-8",timeout:Td,windowsHide:!0});return t};function Ls(e){return String(e.stderr??e).trim().split(/\r?\n/)[0]??""}function wd(e=xd){return{async listNetworks(){let t=(await e(["network","ls","--format","{{.Name}}"])).split(/\r?\n/).map(r=>r.trim()).filter(Boolean);return(await Promise.all(t.map(async r=>{try{let s=await e(["network","inspect","--format","{{len .Containers}}",r]);return{name:r,containers:Number(s.trim())||0}}catch(s){if(/no such network|not found/i.test(String(s)))return null;throw s}}))).filter(r=>r!==null)},async removeNetwork(t){try{await e(["network","rm",t])}catch(n){throw new Error(Ls(n))}},isProcessAlive(t){try{return process.kill(t,0),!0}catch(n){return n.code==="EPERM"}}}}var Ms=wd();async function Us(e,t=Ms,n){let r=await t.listNetworks(),s=[],o=new Set(e?bn(e):[]);for(let i of r){let a=bd.exec(i.name),c,d;if(a)c="probe",d="Orphaned probe networks";else if(o.has(i.name))c="project",d="Docker networks";else continue;let l={id:`remove-network:${i.name}`,check:d,scope:c,command:`docker network rm ${i.name}`,status:"planned",reason:"unused network"};i.containers>0?(l.status="skipped",l.reason=`${i.containers} container(s) still attached`):c==="project"&&n?(l.status="skipped",l.reason=n):a&&t.isProcessAlive(Number(a[1]))&&(l.status="skipped",l.reason=`probe of a running process (pid ${a[1]})`),s.push(l)}return s}async function Bs(e,t,n=Ms){let r=[];for(let s of e){if(s.status!=="planned"||t.dryRun){r.push(s);continue}if(!t.confirm){r.push({...s,status:"skipped",reason:"confirmation required; run in a terminal or pass --yes"});continue}let o=!1;try{o=await t.confirm(s)}catch{}if(!o){r.push({...s,status:"skipped",reason:"not confirmed"});continue}let i=s.id.slice(15);try{await n.removeNetwork(i),r.push({...s,status:"applied",reason:"removed"})}catch(a){r.push({...s,status:"failed",reason:Ls(a)})}}return r}function Hs(e){return e.some(t=>t.status==="failed")}async function Ed(e){try{let{stdout:t}=await Fs(e,["--version"],{encoding:"utf-8",timeout:5e3,windowsHide:!0});return t.trim().split(/\r?\n/)[0]||null}catch{return null}}async function vd(e=Br("tdk"),t=Ed){let n=new Set,r=[];for(let s of e){let o=s;try{o=Is(s)}catch{}n.has(o)||(n.add(o),r.push({path:s,version:await t(s)}))}return r}async function Ks(e){let t=e??await vd();if(t.length<=1)return{name:"TDK installs",didPass:!0,message:"One tdk install on PATH"};let n=t.map(s=>`${s.path} (${s.version??"version unknown"})`).join(", "),r=new Set(t.map(s=>s.version));return{name:"TDK installs",didPass:!1,isWarning:!0,message:`${t.length} tdk installs on PATH${r.size>1?" with different versions":""}: ${n}`,fix:"Keep one: remove the others, or run tdk upgrade. The first on PATH is the one your shell runs; tools that launch /usr/local/bin/tdk directly may use another"}}function Gs(e,t=at){let n=new Set([e]);try{n.add(Is(e))}catch{}let r=new Set;for(let s of n)for(let o of t(yd(s,".tdk",".tdk-out","Tiltfile")))r.add(o);return[...r]}import{statfsSync as Pd}from"node:fs";import{homedir as $d}from"node:os";var ct="Disk Space",_d=1024**3,Rd=2,Ad=10,Vs=["Free disk space, then retry: tdk doctor"," docker system prune -af # unused images and build cache (needs Docker running)"," npm cache clean --force"," bun pm cache rm"," brew cleanup -s"].join(`
|
|
98
|
+
`);function Ws(e=Pd,t=$d()){let n;try{let s=e(t);n=Number(s.bavail)*Number(s.bsize)/_d}catch{return{name:ct,didPass:!0,isSkipped:!0,message:"Could not read free disk space"}}let r=`${n.toFixed(1)} GB free`;return n<Rd?{name:ct,didPass:!1,message:`${r}: Docker cannot build or start containers`,fix:Vs}:n<Ad?{name:ct,didPass:!1,isWarning:!0,message:`${r}: image builds may fail`,fix:Vs}:{name:ct,didPass:!0,message:r}}var Dd="Quit Docker Desktop completely (whale icon > Quit Docker Desktop), reopen it, and wait until it shows Running. If it keeps getting stuck, check free disk space. Then retry: tdk doctor";function Js(e,t=process.platform){return t!=="darwin"||!e?null:{name:"Container Runtime",didPass:!1,message:"Docker Desktop is open, but its engine is not responding",fix:Dd}}async function zs(e,t){let n=[],r=[],s=o=>{o.status==="fulfilled"?n.push(o.value):r.push({code:"INTERNAL",message:o.reason instanceof Error?o.reason.message:String(o.reason)})};for(let o of await Promise.allSettled(e.map(i=>Promise.resolve().then(i))))s(o);for(let o of t)for(let i of await Promise.allSettled([Promise.resolve().then(o)]))s(i);return{checks:n,errors:r}}function qs(e){let t=e.find(n=>n.name===M);return t?t.didPass:null}function Z(e,t,n=[],r,s){return{schemaVersion:1,data:{ready:n.length===0&&e.every(o=>o.didPass||o.isSkipped||o.isWarning),inProject:t,checks:e,...s?{host:{...s,containerRuntimeReachable:qs(e),canUp:s.canUp&&qs(e)!==!1}}:{},...r!==void 0?{ports:{http:{requested:r?.requested.ingressHttp??80,chosen:r?.ingressHttp??null,explicit:r?.explicit.ingressHttp??!!process.env.TDK_HTTP_PORT,reason:r?.reason.ingressHttp??"port planning failed"},https:{requested:r?.requested.ingressHttps??443,chosen:r?.ingressHttps??null,explicit:r?.explicit.ingressHttps??!!process.env.TDK_HTTPS_PORT,reason:r?.reason.ingressHttps??"port planning failed"},postgres:{requested:r?.requested.postgres??5432,chosen:r?.postgres??null,explicit:r?.explicit.postgres??!!process.env.TDK_POSTGRES_PORT,reason:r?.reason.postgres??"port planning failed"}}}:{}},errors:n}}function Ys(e){return e.errors.length>0?2:e.data.ready?0:1}import{existsSync as wn,readFileSync as Xs}from"node:fs";import{dirname as Od,join as xn,normalize as Cd}from"node:path";function Nd(e){let t=new Set,n=/^load\s*\(([\s\S]*?)\)/gm;for(let r of e.matchAll(n)){let s=r[1]??"",o=/["']([^"']+)["']/g,i=Array.from(s.matchAll(o),a=>a[1]);for(let a of i.slice(1))a.startsWith("_")&&t.add(a)}return Array.from(t).sort()}function Qs(e,t,n){return t==="ext://tdk-cli"?xn(n,".tdk",".tdk-out","tdk-cli-ext","Tiltfile"):t.startsWith(".")||!t.includes("://")&&!t.startsWith("@")?Cd(xn(Od(e),t)):null}function jd(e,t,n){let r=[],s=/^load\s*\(([\s\S]*?)\)/gm;for(let o of t.matchAll(s)){let c=(o[1]??"").match(/["']([^"']+)["']/)?.[1];if(!c)continue;let d=Qs(e,c,n);d&&(wn(d)||r.push(`${e}: ${c} -> ${d}`))}return r}function Id(e,t,n){let r=[],s=/^load\s*\(([\s\S]*?)\)/gm;for(let o of t.matchAll(s)){let c=(o[1]??"").match(/["']([^"']+)["']/)?.[1];if(c){let d=Qs(e,c,n);d&&r.push(d)}}return r}function Fd(e,t){let n=new Set,r=[e];for(;r.length>0;){let s=r.pop();if(!s||n.has(s)||!wn(s))continue;n.add(s);let o=Xs(s,"utf-8");for(let i of Id(s,o,t))n.has(i)||r.push(i)}return Array.from(n).sort()}function Zs(){let e=y()??process.cwd(),t=xn(e,".tdk",".tdk-out","Tiltfile");if(!wn(t))return{name:"Starlark Load Exports",didPass:!0,isSkipped:!0,message:"No generated Tiltfile to check"};let n=[],r=[],s=Fd(t,e);for(let i of s){let a=Xs(i,"utf-8"),c=Nd(a);c.length>0&&n.push(`${i}: ${c.join(", ")}`);for(let d of jd(i,a,e))r.push(d)}if(n.length===0&&r.length===0)return{name:"Starlark Load Exports",didPass:!0,message:`${b(s.length,"reachable Starlark file")} ${s.length===1?"has":"have"} valid load exports and paths`};let o=[];return n.length>0&&o.push(`Private exported symbols:
|
|
99
|
+
${n.join(`
|
|
100
|
+
`)}`),r.length>0&&o.push(`Missing relative load targets:
|
|
101
|
+
${r.join(`
|
|
102
|
+
`)}`),{name:"Starlark Load Exports",didPass:!1,message:`Starlark load() issues will stop Tilt before services start:
|
|
103
|
+
${o.join(`
|
|
104
|
+
|
|
105
|
+
`)}`,fix:"Regenerate with a current TDK (`tdk config regenerate`). If the issue remains, rename loaded symbols without leading underscores and update stale load paths."}}import{execFileSync as Xd,execSync as Qd}from"node:child_process";import{existsSync as Pn,readdirSync as ho,readFileSync as Ue}from"node:fs";import{basename as Zd,dirname as eu,join as L,relative as Me}from"node:path";function Dh(e){return`import { Hono } from 'hono';
|
|
106
|
+
|
|
107
|
+
const app = new Hono();
|
|
108
|
+
|
|
109
|
+
// Health check endpoint (required by TILT_RESOURCE_DEFAULTS.star)
|
|
110
|
+
app.get('/health', (c) => {
|
|
111
|
+
return c.json({ status: 'ok', service: '${e}' });
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
app.get('/health/live', (c) => {
|
|
115
|
+
return c.json({ status: 'alive', timestamp: Date.now() });
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
app.get('/health/ready', async (c) => {
|
|
119
|
+
const dependencies: Record<string, string> = {};
|
|
120
|
+
let allReady = true;
|
|
121
|
+
|
|
122
|
+
// Add dependency checks here (database, cache, etc.)
|
|
123
|
+
// Mark allReady = false if any dependency is unhealthy
|
|
124
|
+
|
|
125
|
+
const status = allReady ? 'ready' : 'not_ready';
|
|
126
|
+
return c.json({ status, dependencies }, allReady ? 200 : 503);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
app.get('/', (c) => {
|
|
130
|
+
return c.json({
|
|
131
|
+
service: '${e}',
|
|
132
|
+
version: '1.0.0',
|
|
133
|
+
endpoints: ['/health', '/health/live', '/health/ready']
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const port = process.env.PORT || 3000;
|
|
138
|
+
console.log('\\n\u{1F680} ${e} running on http://localhost:' + port);
|
|
139
|
+
console.log('\u{1F4CA} Health check: http://localhost:' + port + '/health\\n');
|
|
140
|
+
|
|
141
|
+
export default {
|
|
142
|
+
port,
|
|
143
|
+
fetch: app.fetch,
|
|
144
|
+
};
|
|
145
|
+
`}var eo={id:"bun",label:"Bun + Hono",installHint:"bun install"};var Ld="go run .",to={id:"go",label:"Go 1.23 (net/http)",devCommand:Ld,watch:["**/*.go","go.mod","go.sum"],installHint:"go mod tidy # optional: Docker builds the image for you",createFiles(e){return[{filename:"go.mod",content:`module ${e}
|
|
146
|
+
|
|
147
|
+
go 1.23
|
|
148
|
+
`,description:"Generating go.mod",emoji:"\u{1F4E6}"},{filename:"main.go",content:`package main
|
|
149
|
+
|
|
150
|
+
import (
|
|
151
|
+
"encoding/json"
|
|
152
|
+
"log"
|
|
153
|
+
"net/http"
|
|
154
|
+
"os"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
func writeJSON(w http.ResponseWriter, status int, body any) {
|
|
158
|
+
w.Header().Set("Content-Type", "application/json")
|
|
159
|
+
w.WriteHeader(status)
|
|
160
|
+
_ = json.NewEncoder(w).Encode(body)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
func newMux() *http.ServeMux {
|
|
164
|
+
mux := http.NewServeMux()
|
|
165
|
+
|
|
166
|
+
// Health check endpoint (required by TILT_RESOURCE_DEFAULTS.star)
|
|
167
|
+
mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
|
|
168
|
+
writeJSON(w, http.StatusOK, map[string]string{"status": "ok", "service": "${e}"})
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) {
|
|
172
|
+
writeJSON(w, http.StatusOK, map[string]any{
|
|
173
|
+
"service": "${e}",
|
|
174
|
+
"version": "1.0.0",
|
|
175
|
+
"endpoints": []string{"/health"},
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
return mux
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
func main() {
|
|
183
|
+
port := os.Getenv("PORT")
|
|
184
|
+
if port == "" {
|
|
185
|
+
port = "3000"
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ":" + port listens on every interface, which Traefik needs inside the container network.
|
|
189
|
+
log.Printf("${e} listening on :%s", port)
|
|
190
|
+
log.Fatal(http.ListenAndServe(":"+port, newMux()))
|
|
191
|
+
}
|
|
192
|
+
`,description:"Generating backend source",emoji:"\u{1F4BB}"},{filename:"main_test.go",content:`package main
|
|
193
|
+
|
|
194
|
+
import (
|
|
195
|
+
"net/http"
|
|
196
|
+
"net/http/httptest"
|
|
197
|
+
"testing"
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
func TestHealth(t *testing.T) {
|
|
201
|
+
rec := httptest.NewRecorder()
|
|
202
|
+
newMux().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/health", nil))
|
|
203
|
+
|
|
204
|
+
if rec.Code != http.StatusOK {
|
|
205
|
+
t.Fatalf("GET /health = %d, want %d", rec.Code, http.StatusOK)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
`,description:"Generating smoke test",emoji:"\u{1F9EA}"}]}};var Md="uvicorn main:app --app-dir src --host 0.0.0.0 --port ${PORT:-4000} --reload --reload-dir src",no={id:"python",label:"Python 3.12 + FastAPI",devCommand:Md,watch:["src/**/*"],installHint:'pip install -e ".[dev]" # optional: Docker builds the image for you',createFiles(e){return[{filename:"pyproject.toml",content:`[project]
|
|
209
|
+
name = "${e}"
|
|
210
|
+
version = "0.0.1"
|
|
211
|
+
requires-python = ">=3.12"
|
|
212
|
+
dependencies = [
|
|
213
|
+
"fastapi==0.115.6",
|
|
214
|
+
"uvicorn[standard]==0.34.0",
|
|
215
|
+
]
|
|
216
|
+
|
|
217
|
+
[project.optional-dependencies]
|
|
218
|
+
dev = [
|
|
219
|
+
"pytest==9.0.3",
|
|
220
|
+
"httpx==0.28.1",
|
|
221
|
+
]
|
|
222
|
+
|
|
223
|
+
[build-system]
|
|
224
|
+
requires = ["setuptools>=68"]
|
|
225
|
+
build-backend = "setuptools.build_meta"
|
|
226
|
+
|
|
227
|
+
# Only the dependencies are installed into the image; the source is synced for live reload.
|
|
228
|
+
[tool.setuptools]
|
|
229
|
+
py-modules = []
|
|
230
|
+
|
|
231
|
+
[tool.pytest.ini_options]
|
|
232
|
+
pythonpath = ["src"]
|
|
233
|
+
testpaths = ["tests"]
|
|
234
|
+
`,description:"Generating pyproject.toml",emoji:"\u{1F4E6}"},{filename:"src/main.py",content:`import os
|
|
235
|
+
|
|
236
|
+
from fastapi import FastAPI
|
|
237
|
+
|
|
238
|
+
app = FastAPI(title="${e}")
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
@app.get("/health")
|
|
242
|
+
def health() -> dict[str, str]:
|
|
243
|
+
return {"status": "ok", "service": "${e}"}
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
@app.get("/")
|
|
247
|
+
def root() -> dict[str, object]:
|
|
248
|
+
return {"service": "${e}", "version": "1.0.0", "endpoints": ["/health"]}
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
if __name__ == "__main__":
|
|
252
|
+
import uvicorn
|
|
253
|
+
|
|
254
|
+
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "3000")))
|
|
255
|
+
`,description:"Generating backend source",emoji:"\u{1F4BB}"},{filename:"tests/test_health.py",content:`from fastapi.testclient import TestClient
|
|
256
|
+
|
|
257
|
+
from main import app
|
|
258
|
+
|
|
259
|
+
client = TestClient(app)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def test_health() -> None:
|
|
263
|
+
response = client.get("/health")
|
|
264
|
+
assert response.status_code == 200
|
|
265
|
+
assert response.json() == {"status": "ok", "service": "${e}"}
|
|
266
|
+
`,description:"Generating test file",emoji:"\u{1F9EA}"}]}};var Ud="cargo run",ro={id:"rust",label:"Rust (axum)",devCommand:Ud,watch:["src/**/*.rs","Cargo.toml","Cargo.lock"],installHint:"cargo build # optional: Docker builds the image for you",createFiles(e){return[{filename:"Cargo.toml",content:`[package]
|
|
267
|
+
name = "${e}"
|
|
268
|
+
version = "0.1.0"
|
|
269
|
+
edition = "2021"
|
|
270
|
+
|
|
271
|
+
# The image copies the release binary by this fixed name.
|
|
272
|
+
[[bin]]
|
|
273
|
+
name = "app"
|
|
274
|
+
path = "src/main.rs"
|
|
275
|
+
|
|
276
|
+
[dependencies]
|
|
277
|
+
axum = "0.8"
|
|
278
|
+
serde_json = "1"
|
|
279
|
+
tokio = { version = "1", features = ["macros", "rt-multi-thread", "net"] }
|
|
280
|
+
`,description:"Generating Cargo.toml",emoji:"\u{1F4E6}"},{filename:"src/main.rs",content:`use axum::{routing::get, Json, Router};
|
|
281
|
+
use serde_json::{json, Value};
|
|
282
|
+
|
|
283
|
+
fn app() -> Router {
|
|
284
|
+
Router::new()
|
|
285
|
+
.route("/", get(root))
|
|
286
|
+
// Health check endpoint (required by TILT_RESOURCE_DEFAULTS.star)
|
|
287
|
+
.route("/health", get(health))
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async fn root() -> Json<Value> {
|
|
291
|
+
Json(json!({ "service": "${e}", "version": "1.0.0", "endpoints": ["/health"] }))
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async fn health() -> Json<Value> {
|
|
295
|
+
Json(json!({ "status": "ok", "service": "${e}" }))
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
#[tokio::main]
|
|
299
|
+
async fn main() {
|
|
300
|
+
let port = std::env::var("PORT").unwrap_or_else(|_| "3000".to_string());
|
|
301
|
+
|
|
302
|
+
// 0.0.0.0 listens on every interface, which Traefik needs inside the container network.
|
|
303
|
+
let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{port}"))
|
|
304
|
+
.await
|
|
305
|
+
.expect("failed to bind the port");
|
|
306
|
+
println!("${e} listening on 0.0.0.0:{port}");
|
|
307
|
+
axum::serve(listener, app()).await.expect("server error");
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
#[cfg(test)]
|
|
311
|
+
mod tests {
|
|
312
|
+
use super::*;
|
|
313
|
+
|
|
314
|
+
#[tokio::test]
|
|
315
|
+
async fn health_reports_ok() {
|
|
316
|
+
let Json(body) = health().await;
|
|
317
|
+
assert_eq!(body["status"], "ok");
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
`,description:"Generating backend source and smoke test",emoji:"\u{1F4BB}"}]}};var so="bun",me={bun:eo,python:no,go:to,rust:ro};function Bd(e){let t=(e??so).trim().toLowerCase(),n=Object.hasOwn(me,t)?me[t]:void 0;if(!n){let r=Object.keys(me).join(", ");throw new j(`Unknown backend language "${e}". Supported languages: ${r}.`,[`Use one of: ${r}`,`Omit --language to use ${so}`])}return n}function Bh(e,t){if(e!=="backend"){if(t!==void 0)throw new j("--language can only be used with --type backend.",["Add --type backend, or drop --language for this resource type"]);return}return Bd(t)}import{exec as Hd,execFile as Kd}from"node:child_process";import{promisify as oo}from"node:util";var Gd=oo(Hd),K=async(e,t)=>{if(process.platform==="win32"){let r=e.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)??[],[s,...o]=r.map(c=>c.startsWith('"')||c.startsWith("'")?c.slice(1,-1):c);if(!s)throw new Error("Command is empty");let i=N(s)??s,{stdout:a}=await oo(Kd)(i,o,{encoding:"utf-8",timeout:t,windowsHide:!0,shell:!1});return a}let{stdout:n}=await Gd(e,{encoding:"utf-8",timeout:t});return n};function io(e){let t=e;return t?.code==="ETIMEDOUT"||t?.killed===!0}import{existsSync as Vd,readFileSync as ao}from"node:fs";import{join as ee}from"node:path";var co=["postgres",O],uo=["pre_alpha","alpha","beta","out_of_scope"];function Wd(e){return co.includes(e)}function lo(e){try{return JSON.parse(ao(ee(e,".tdk",_),"utf-8"))}catch{return}}function he(e){try{return Vd(e)?ao(e,"utf-8"):void 0}catch{return}}function po(e,t){let n=s=>{let o=s;for(;o<e.length&&/\s/.test(e[o]);)o+=1;return o},r=0;for(;;){let s=e.indexOf(t,r);if(s===-1)return;let o=n(s+t.length);if(e[o]==="="&&(o=n(o+1),e[o]==="[")){let i=e.indexOf("]",o+1);return i===-1?void 0:[...e.slice(o+1,i).matchAll(/"([^"]+)"|'([^']+)'/g)].map(a=>a[1]??a[2]??"")}r=s+t.length}}function fo(e){let t=ee(e,".tdk",".tdk-out","Tiltfile"),n=he(t);if(n)return po(n,"ALWAYS_ENABLED_INFRA")}function Jd(e){let t=ee(e,".tdk",".tdk-out","spec.master"),n=he(t);if(n!==void 0)return/"database-management"\s*:\s*True/.test(n)}function qd(e){let t=ee(e,".tdk",".tdk-out"),n=he(ee(t,"Tiltfile")),r=he(ee(t,"spec.master")),s=he(ee(t,"tdk-cli-ext","discovery","config.star")),o=he(ee(t,"tdk-cli-ext","engine","topologies","tilt","config","profiles.star"));if(!n||!r||!s||!o)return!1;let i=r.match(/PRE_ALPHA_RESOURCES\s*=\s*\{([^}]*)\}/)?.[1],a=po(s,"CORE_INFRA");return/FOCUS_MODE\s*,\s*FOCUS_ENABLED_ALL\s*,\s*FOCUS_ENABLED_RESOURCES\s*=\s*Config\.apply_focus\(cfg\)/.test(n)&&/if FOCUS_MODE and FOCUS_ENABLED_ALL\s*:/.test(n)&&/"[^"]+"\s*:\s*True|'[^']+'\s*:\s*True/.test(i??"")&&(a?.includes("postgres")??!1)&&/["']postgres["']\s*:\s*["']database-management["']/.test(s)&&/for infra in CORE_INFRA_EXPORT\s*:/.test(o)&&/needed\[INFRA_STACK_MAP_EXPORT\[infra\]\]\s*=\s*True/.test(o)}function zd(e){let t=lo(e);if(!t)return!1;let n=t.phases??{};for(let s of uo){let o=n[s]?.enabledStacks;if(Array.isArray(o)&&o.includes(O))return!0}let r=t.always_enabled_infra??$e;return Array.isArray(r)&&r.includes(O)}function Yd(e){let t=new Set;for(let s of co)t.add(s);for(let s of x(e))t.add(s.name),s.stack&&t.add(s.stack);let n=lo(e);if(n){let s=n.phases??{};for(let i of uo){let a=s[i]?.enabledStacks;if(Array.isArray(a))for(let c of a)t.add(c)}let o=n.always_enabled_infra??$e;if(Array.isArray(o))for(let i of o)t.add(i)}let r=fo(e);if(Array.isArray(r))for(let s of r)t.add(s);return t}function En(e){let t=zd(e),n=fo(e),r=Array.isArray(n)&&n.includes(O),s=Jd(e)===!0,o=t||r||s,i=!t&&qd(e),a=Yd(e),c=[],d=[];for(let p of x(e)){let k=p.config?.dependsOn??[],m=!1;for(let T of k){if(Wd(T)){m=!0;continue}a.has(T)||d.push({resource:p.name,name:T})}m&&c.push(p.name)}let l=o||c.length>0,u=o?"feature":c.length>0?"dependsOn":"none";return{featureOn:o,featureOnFromProjectJson:t,featureOnFromTiltfile:r,featureOnFromSpecMaster:s,focusWouldEnableDatabaseManagement:i,dependsOnUsers:c,willStart:l,reason:u,unknownDependsOnNames:d}}var ut=1e4,tu=/\.(?:[cm]?[jt]sx?|vue)$/,nu=/(?:\.test\.|\.spec\.|\/tests?\/|\/__tests__\/)/,ru=7,su=new Set(["node_modules",D,"dist","tests","__tests__"]);function go(e){try{let t=JSON.parse(Ue(L(e,".tdk",_),"utf-8"));return typeof t?.project?.name=="string"?t.project.name:void 0}catch{return}}function ou(e){try{let t=JSON.parse(Ue(e,"utf-8")).params;return t&&typeof t=="object"?t:{}}catch{return{}}}function vn(e){try{let t=JSON.parse(Ue(L(e,R),"utf-8"));return t&&typeof t=="object"?t:{}}catch{return{}}}function mo(e){if(typeof e!="string")return;let t=e.match(/(?:^|[^0-9])(\d+)(?:\.|$)/);return t?Number(t[1]):void 0}function iu(e){let t=pt(e);if(t===void 0)return["missing prisma/schema.prisma"];let n=t.replace(/\/\*[\s\S]*?\*\//g,"").replace(/^\s*\/\/.*$/gm,""),r=[],s=n.match(/datasource\s+\w+\s*\{([^}]*)\}/)?.[1]??"";return/provider\s*=\s*["']postgresql["']/.test(s)||r.push("schema provider is not postgresql"),/\b(?:url|directUrl)\s*=/.test(n)&&r.push("schema still declares url/directUrl"),r}function au(e){let t=L(e,D,"prisma.config.autogenerated.ts"),r=[L(e,"prisma.config.ts"),t].find(i=>Pn(i));if(!r)return["missing prisma.config.ts or generated Prisma config"];let o=(pt(r)??"").match(/datasource\s*:\s*\{([^}]*)\}/)?.[1]??"";return/url\s*:\s*(?:env\(["']DATABASE_URL["']\)|process\.env\.DATABASE_URL)\s*,?/.test(o)?[]:['Prisma config datasource.url is not env("DATABASE_URL")']}function ko(e=y()??process.cwd()){let t=x(e),n=t.filter(o=>o.config?.featuresEnabled?.includes("prisma")),r=[],s=t.filter(o=>o.config?.appType==="migrator");for(let o of t){let i=o.config?.features;Array.isArray(i)&&i.includes("prisma")&&!o.config?.featuresEnabled?.includes("prisma")&&r.push(`${o.name}: use featuresEnabled, not features, for Prisma`)}if(n.length===0)return r.length===0?{name:"Prisma 7 consistency",didPass:!0,isSkipped:!0,message:"No resources enable Prisma; Prisma consistency checks were skipped"}:{name:"Prisma 7 consistency",didPass:!1,message:`${b(r.length,"Prisma consistency error")}:
|
|
321
|
+
${r.join(`
|
|
322
|
+
`)}`,fix:"Use featuresEnabled to opt into the validated Prisma 7 shape"};for(let o of n){let i=vn(o.path).dependencies,c={...vn(o.path).devDependencies??{},...i??{}},d=mo(c.prisma),l=mo(c["@prisma/client"]);if(d===void 0||l===void 0?r.push(`${o.name}: prisma and @prisma/client are required`):(d!==l||d!==ru)&&r.push(`${o.name}: prisma packages must both be major 7`),r.push(...iu(L(o.path,"prisma","schema.prisma")).map(p=>`${o.name}: ${p}`)),r.push(...au(o.path).map(p=>`${o.name}: ${p}`)),(o.config?.appType??o.type)==="migrator")o.config?.dependsOn?.some(k=>k==="postgres"||k===O)||r.push(`${o.name}: migrator must depend on postgres or database-management`);else{o.config?.dependsOn?.some(T=>s.some(w=>w.name===T))||r.push(`${o.name}: API must depend on its Prisma migrator`);let k=vn(o.path).scripts;Object.entries(k??{}).some(([T,w])=>Po.has(T)&&typeof w=="string"&&/prisma\s+migrate/.test(w))&&r.push(`${o.name}: API start script must not run prisma migrate`);let m=L(o.path,o.config?.dockerfile??"Dockerfile");dt(m,!0)&&r.push(`${o.name}: API image/startup must not run Prisma migrations`)}}return n.length>0&&!En(e).willStart&&r.push("shared platform Postgres will not start for the Prisma resources"),r.length===0?{name:"Prisma 7 consistency",didPass:!0,message:"Prisma resources use the validated Prisma 7 shape"}:{name:"Prisma 7 consistency",didPass:!1,message:`${b(r.length,"Prisma consistency error")}:
|
|
323
|
+
${r.join(`
|
|
324
|
+
`)}`,fix:"Use Prisma 7 packages and config, put migrations in a migrator, and make the API depend on that migrator"}}function yo(e){let t=new Map;for(let n of e){let r=n.config?.port;C(n.config?.appType)&&typeof r=="number"&&t.set(n.name,r)}return t}function cu(e){let t=e.config?.language;return typeof t=="string"&&Object.hasOwn(me,t)&&me[t].createFiles!==void 0}function du(e){return(e.config?.appType??e.type)===W}function So(e=y()??process.cwd()){let t=x(e).filter(n=>!du(n)&&!cu(n)&&!Pn(L(n.path,R))).map(n=>n.name);return t.length===0?{name:"Resource package.json",didPass:!0,message:"Every resource has a package.json"}:{name:"Resource package.json",didPass:!1,message:`${b(t.length,"resource")} without a package.json: ${t.join(", ")}. The image build stops at "COPY .../package.json: not found"`,fix:"Add a package.json next to each service.json (copy one from a working resource), or delete the folder's service.json if it is not a service"}}function lt(e=y()??process.cwd()){let t=new Map;for(let s of x(e)){let o=t.get(s.name)??[];o.push(Me(e,s.configPath)||s.configPath),t.set(s.name,o)}let n=[...t].filter(([,s])=>s.length>1);if(n.length===0)return{name:"Duplicate resource names",didPass:!0,message:"Every resource has a unique appName"};let r=n.map(([s,o])=>`"${s}" in ${o.join(" and ")}`);return{name:"Duplicate resource names",didPass:!1,message:`${b(n.length,"appName")} used by more than one resource:
|
|
325
|
+
${r.join(`
|
|
326
|
+
`)}`,fix:'Give each resource its own "appName" in its service.json. The name becomes the Compose service and the Traefik router, so two resources with one name collide'}}function uu(e){let t=e.config?.appType;return C(t)?!0:t===W&&e.config?.exposeViaProxy!==!1}function To(e=y()??process.cwd()){let t=new Map;for(let s of x(e)){let o=s.config?.port;if(typeof o!="number"||!uu(s))continue;let i=t.get(o)??[];i.push(s.name),t.set(o,i)}let n=[...t].filter(([,s])=>s.length>1);if(n.length===0)return{name:"Duplicate resource ports",didPass:!0,message:"No two routed resources use the same port"};let r=n.map(([s,o])=>`port ${s}: ${o.join(", ")}`);return{name:"Duplicate resource ports",didPass:!1,isWarning:!0,message:`${b(n.length,"port")} used by more than one routed resource:
|
|
327
|
+
${r.join(`
|
|
328
|
+
`)}`,fix:'Usually a copied service.json. Each container can listen on the same port, but pick distinct "port" values (tdk resource assigns free ones) so URLs like http://<resource>:<port> and localhost checks stay unambiguous'}}var lu=/https?:\/\/([a-z][a-z0-9-]*):(\d+)/g;function bo(e=y()??process.cwd()){let t=x(e),n=yo(t),r=new Map([...n].map(([o,i])=>[i,o])),s=[];for(let o of t)for(let[i,a]of Object.entries(ou(o.configPath)))for(let[,c,d]of String(a).matchAll(lu)){let l=Number(d),u=n.get(c);u!==void 0&&u!==l?s.push(`${o.name} param ${i}: ${c}:${l}, but ${c} listens on ${u}`):(c==="localhost"||c==="127.0.0.1")&&r.has(l)&&r.get(l)!==o.name&&s.push(`${o.name} param ${i}: localhost:${l} is ${o.name} itself inside its container; ${r.get(l)} is reached as ${r.get(l)}:${l}`)}return s.length===0?{name:"Service URL ports",didPass:!0,message:"Service URLs in params point at the ports the services listen on"}:{name:"Service URL ports",didPass:!1,message:`${b(s.length,"service URL")} in params that cannot connect:
|
|
329
|
+
${s.join(`
|
|
330
|
+
`)}`,fix:'TDK runs each backend on the "port" in its service.json, and containers reach each other by resource name (http://<resource>:<port>). Use that port in the URL'}}function $n(e){let t=[],n;try{n=ho(e,{withFileTypes:!0})}catch{return t}for(let r of n){let s=L(e,r.name);r.isDirectory()?su.has(r.name)||t.push(...$n(s)):tu.test(r.name)&&!nu.test(s)&&t.push(s)}return t}var pu=/(?:localhost|127\.0\.0\.1):(\d{2,5})\b/g,xo=/^\s*(?:\/\/|\/\*|\*|<!--|#)/;function wo(e=y()??process.cwd()){let t=new Map;for(let r of x(e)){let s=r.config;if(!(C(s?.appType)||s?.appType===W&&s.exposeViaProxy!==!1))continue;let i=eu(r.path),a=s?.stack??Zd(i),c=`${i}\0${a}`,d=t.get(c)??{stack:a,resources:[]};d.resources.push(r),t.set(c,d)}let n=[];for(let{stack:r,resources:s}of t.values()){if(s.length<2)continue;let o=a=>!!(a.config?.traefik?.host||a.config?.traefik?.pathPrefix),i=s.filter(a=>!o(a)).map(a=>a.name);i.length>0&&n.push(`stack "${r}": ${i.sort().join(", ")} lose the shared /api/${r}-management route because ${s.length} backends share the stack; they are routed at /api/<name> only`);for(let a of["host","pathPrefix"]){let c=new Map;for(let d of s){let l=d.config?.traefik?.[a];l&&c.set(l,[...c.get(l)??[],d.name])}for(let[d,l]of c)l.length>1&&n.push(`stack "${r}": ${l.sort().join(" and ")} both set traefik.${a} "${d}", so Traefik cannot tell them apart`)}}return n.length===0?{name:"Stack API routes",didPass:!0,message:"No two backends in one service directory compete for one stack route"}:{name:"Stack API routes",didPass:!1,isWarning:!0,message:n.join(`
|
|
331
|
+
`),fix:'Call each backend at /api/<name>, move one backend to its own stack, or give each its own "traefik.pathPrefix" in service.json'}}function Eo(e=y()??process.cwd()){let t=x(e),n=new Map([...yo(t)].map(([o,i])=>[i,o])),r=[];for(let o of t.filter(i=>i.config?.appType==="frontend"))for(let i of $n(L(o.path,"src")))Ue(i,"utf-8").split(`
|
|
332
|
+
`).forEach((a,c)=>{if(!xo.test(a))for(let[,d]of a.matchAll(pu)){let l=n.get(Number(d));l&&r.push(`${Me(e,i)}:${c+1} calls localhost:${d} (${l})`)}});if(r.length===0)return{name:"Frontend backend URLs",didPass:!0,message:"No frontend calls a backend through a localhost port"};let s=go(e)??"<project>";return{name:"Frontend backend URLs",didPass:!1,message:`${b(r.length,"frontend call")} to a backend port that TDK does not publish on localhost:
|
|
333
|
+
${r.join(`
|
|
334
|
+
`)}`,fix:`Backends are only reachable through Traefik: http://api.${s}.localhost/api/<name without -api>/... (for example catalog-api is /api/catalog). Use that URL, or the generated .autogenerated/env.ts`}}var vo=/\b(?:prisma\s+(?:migrate\s+(?:deploy|dev|resolve)|db\s+push)|drizzle-kit\s+(?:migrate|push)|knex\s+migrate:\w+|typeorm\s+migration:run|mikro-orm\s+migration:up|sequelize(?:-cli)?\s+db:migrate|dbmate\s+(?:up|migrate)|atlas\s+migrate\s+apply|alembic\s+upgrade)\b/,Po=new Set(["start","dev","serve","prestart","predev","preserve"]),fu=/\.(?:sh|bash)$/,mu=/^\s*(?:CMD|ENTRYPOINT)\b/i;function pt(e){try{return Ue(e,"utf-8")}catch{return}}function hu(e){let t=pt(L(e,R));if(t===void 0)return[];let n={};try{n=JSON.parse(t)?.scripts??{}}catch{return[]}return Object.entries(n).filter(([r,s])=>Po.has(r)&&typeof s=="string"&&vo.test(s)).map(([r])=>`package.json "${r}" script`)}function dt(e,t){let n=pt(e);return n===void 0?!1:n.split(`
|
|
335
|
+
`).some(r=>vo.test(r.replace(/["'`,[\]]+/g," "))&&!xo.test(r)&&(!t||mu.test(r)))}function $o(e=y()??process.cwd()){let t=[];for(let n of x(e)){let r=n.config?.appType??n.type;if(r!=="backend"&&r!=="worker")continue;let s=n.path;for(let a of hu(s))t.push(`${n.name}: ${a}`);let o=L(s,n.config?.dockerfile??"Dockerfile");dt(o,!0)&&t.push(`${n.name}: ${Me(e,o)} CMD or ENTRYPOINT`);let i=[];try{i=ho(s,{withFileTypes:!0})}catch{}for(let a of i){let c=L(s,a.name);a.isFile()&&fu.test(a.name)&&dt(c,!1)&&t.push(`${n.name}: ${Me(e,c)}`)}for(let a of $n(L(s,"src")))dt(a,!1)&&t.push(`${n.name}: ${Me(e,a)}`)}return t.length===0?{name:"Migrations outside the API",didPass:!0,message:"No backend or worker runs a migration command from its own start-up"}:{name:"Migrations outside the API",didPass:!1,isWarning:!0,message:`${b(t.length,"migration command")} run from a backend or worker's own start-up:
|
|
336
|
+
${t.join(`
|
|
337
|
+
`)}`,fix:"Move the migration into a `migrator` resource (appType migrator) that the service depends on, so it runs once before the service starts instead of on every start and restart"}}function ft(e=y()??process.cwd()){let t=x(e).filter(r=>r.config?.featuresEnabled?.includes("nats")).map(r=>r.name);if(t.length===0)return{name:"NATS broker",didPass:!0,message:"No resource enables the nats feature",isSkipped:!0};if(Pn(L(e,"services","platform","messaging",re)))return{name:"NATS broker",didPass:!0,message:`${b(t.length,"resource")} enable nats and services/platform/messaging/docker-compose.yml is present`};let n=(go(e)??"project").replace(/-/g,"_");return{name:"NATS broker",didPass:!1,message:`${t.join(", ")} enable the nats feature, but no NATS server will start: TDK only starts one when services/platform/messaging/docker-compose.yml exists, and it does not generate that file`,fix:`Create it with a "nats" service (image nats with -js, container_name ${n}_nats, network ${n}_backend, external) and a "redis" service, which TDK registers alongside it`}}function _o(e=y()??process.cwd()){let t=En(e),n=" (project resource set; tdk up may select a subset and not start Postgres)",r=t.focusWouldEnableDatabaseManagement?" Warning: generated Tiltfile default focus expands CORE_INFRA to database-management while project.json has it off; default tdk up takes the feature path.":"";if(t.unknownDependsOnNames.length>0){let o=t.unknownDependsOnNames.map(({resource:a,name:c})=>`"${c}" (from ${a})`).join(", "),i=t.willStart?` Postgres will start because ${t.reason==="feature"?"database-management is enabled":`resource(s) ${t.dependsOnUsers.join(", ")} depend on postgres/database-management`}${n}`:" Postgres will not start for this project.json/generated Tiltfile state.";return{name:"Shared platform Postgres",didPass:!1,message:`${b(t.unknownDependsOnNames.length,"unknown dependsOn name")}: ${o}. These are not known services or stacks, and are not the shared platform database names (postgres, database-management).${i}${r}`,fix:'Use "postgres" or "database-management" in dependsOn for the shared platform database, or fix the name to match an existing service'}}if(!t.willStart)return{name:"Shared platform Postgres",didPass:!0,isWarning:!!r,isSkipped:!r,message:r?r.trim():"Shared platform Postgres will not start: database-management is off and no resource depends on postgres or database-management."};if(t.reason==="feature"&&t.dependsOnUsers.length===0){let o=t.featureOnFromTiltfile||t.featureOnFromSpecMaster?" (generated Tiltfile/spec.master)":"";return{name:"Shared platform Postgres",didPass:!0,isWarning:!!r,message:`Shared platform Postgres will start because the database-management feature is enabled${o} (project-wide; not a tdk up selection).${r}`}}let s=t.reason==="feature"?`the database-management feature is enabled and resource(s) ${t.dependsOnUsers.join(", ")} depend on postgres/database-management`:`resource(s) ${t.dependsOnUsers.join(", ")} depend on postgres/database-management`;return{name:"Shared platform Postgres",didPass:!0,isWarning:!!r,message:`Shared platform Postgres will start because ${s}${n}${r}`}}function gu(e){let t=[];for(let n of e.split(`
|
|
338
|
+
`)){let r=n.match(/^\s*(\d+)\s+.*\btilt up\b.*?\s-f\s+(\S+)/i);r&&t.push({pid:Number(r[1]),root:r[2].split(/[\\/]\.tdk[\\/]/i)[0]})}return t}function Ro(e=y()??process.cwd(),t=Qd){let n;try{n=process.platform==="win32"?Xd("powershell.exe",["-NoProfile","-Command","Get-CimInstance Win32_Process | Where-Object { $_.Name -eq 'tilt.exe' } | ForEach-Object { '{0} {1}' -f $_.ProcessId, $_.CommandLine }"],{encoding:"utf-8",timeout:ut,windowsHide:!0}):t("ps -Ao pid=,command=",{encoding:"utf-8",timeout:ut})}catch{return{name:"Tilt processes",didPass:!0,isSkipped:!0,message:"Could not list processes"}}let r=gu(n),s=r.filter(i=>i.root===e),o=r.filter(i=>i.root!==e);return s.length>1?{name:"Tilt processes",didPass:!1,message:`${s.length} Tilt processes are running for this project (pids ${s.map(i=>i.pid).join(", ")}). They fight over ports and containers`,fix:`Stop them all, then start one: tdk down && kill ${s.map(i=>i.pid).join(" ")} && tdk up`}:o.length>0?{name:"Tilt processes",didPass:!0,isSkipped:!0,message:`Another project's Tilt is running: ${o.map(a=>`${a.root} (pid ${a.pid})`).join(", ")}`,fix:"tdk up will use the next free Tilt UI port (10351, ...). If it is a leftover, run tdk down in that project"}:{name:"Tilt processes",didPass:!0,message:"No stray Tilt processes"}}async function Ao(e=K){let t=`tdk_doctor_probe_${process.pid}`;try{await e(`docker network create ${t}`,ut)}catch(n){let r=String(n.stderr??n);return/address pools/i.test(r)?{name:"Docker networks",didPass:!1,message:"Docker cannot create another network: all predefined address pools are in use. Each TDK project needs several",fix:"run tdk down --prune-networks in projects you are not using, or docker network prune (removes all unused networks)"}:{name:"Docker networks",didPass:!0,isSkipped:!0,message:"Could not test network creation"}}try{await e(`docker network rm ${t}`,ut)}catch{}return{name:"Docker networks",didPass:!0,message:"Docker can create networks"}}import{execFileSync as Oo}from"node:child_process";import{existsSync as xu,mkdirSync as wu,readFileSync as Co,realpathSync as Eu,writeFileSync as vu}from"node:fs";import{join as Be,resolve as Pu,sep as $u}from"node:path";import{createServer as yu}from"node:net";import{connect as ku}from"node:net";function Do(e,t=500){return new Promise(n=>{let r=ku({port:e,host:"127.0.0.1"}),s=o=>{r.destroy(),n(o)};r.setTimeout(t,()=>s(!1)),r.once("connect",()=>s(!0)),r.once("error",()=>s(!1))})}var Su={ingressHttp:{start:8080,end:8180},ingressHttps:{start:8443,end:8543},postgres:{start:15432,end:15532}},mt={ingressHttp:"TDK_HTTP_PORT",ingressHttps:"TDK_HTTPS_PORT",postgres:"TDK_POSTGRES_PORT"},_n={ingressHttp:80,ingressHttps:443,postgres:5432},Rn={ingressHttp:_n.ingressHttp,ingressHttps:_n.ingressHttps};function Tu(e,t){return e==="EACCES"&&t==="darwin"}async function An(e){return await Do(e)?!1:new Promise(t=>{let n=yu();n.once("error",r=>{t(Tu(r.code,process.platform))}),n.listen(e,"127.0.0.1",()=>n.close(()=>t(!0)))})}function bu(e,t){let n=Number(t);if(!Number.isInteger(n)||n<1||n>65535)throw new Error(`${e} must be an integer between 1 and 65535 (received ${JSON.stringify(t)})`);return n}async function ge(e={}){let t=e.env??process.env,n=e.isAvailable??An,r={ingressHttp:!1,ingressHttps:!1,postgres:!1},s={ingressHttp:"",ingressHttps:"",postgres:""},o={explicit:r},i=new Set;for(let a of["ingressHttp","ingressHttps","postgres"]){let c=t[mt[a]],d=c!==void 0&&c.trim()!=="",l=e.ranges?.[a]??Su[a],u=Rn[a],p=d?[bu(mt[a],c)]:[...u===void 0?[]:[u],...Array.from({length:l.end-l.start+1},(m,T)=>l.start+T)],k;for(let m of p)if(!i.has(m)){if(await n(m)){k=m;break}if(d)throw new Error(`${mt[a]}=${m} is already in use. Choose a free host port and retry.`)}if(k===void 0)throw new Error(`No available host port in ${l.start}-${l.end} for ${a}. Set ${mt[a]} to a free port and retry.`);i.add(k),o[a]=k,r[a]=d,s[a]=d?"explicit override":k===u?"requested port is free":`selected from fallback range ${l.start}-${l.end}`}return{...o,requested:{..._n},reason:s}}function hg(e){return`Host ports: HTTP ${e.ingressHttp}, HTTPS ${e.ingressHttps}, Postgres ${e.postgres}`}var No=Be(".tdk",".tdk-out","host-ports.json");function _u(e,t,n){return e.split(`
|
|
339
|
+
`).some(r=>{let[s="",o=""]=r.split("|"),i=s.toLowerCase(),a=t.toLowerCase();return(i.startsWith(`${a}_`)||i.startsWith(`${a}-`))&&o.includes(`:${n}->`)})}function jo(e){let t=Be(e,No);if(!xu(t))return null;try{let n=JSON.parse(Co(t,"utf-8"));if(!n||typeof n!="object")return null;let r=n;return!Number.isInteger(r.ingressHttp)||!Number.isInteger(r.ingressHttps)||!Number.isInteger(r.postgres)||!r.requested||!r.explicit||!r.reason?null:r}catch{return null}}function xg(e,t){let n=Be(e,No);wu(Be(e,".tdk",".tdk-out"),{recursive:!0}),vu(n,`${JSON.stringify(t,null,2)}
|
|
340
|
+
`,{encoding:"utf-8",mode:384})}function wg(e){process.env.TDK_HTTP_PORT=String(e.ingressHttp),process.env.TDK_HTTPS_PORT=String(e.ingressHttps),process.env.TDK_POSTGRES_PORT=String(e.postgres)}var Ru="com.docker.compose.project.working_dir";function Au(e,t,n){let r=new Set([Pu(t)]);try{r.add(Eu(t))}catch{}return e.split(`
|
|
341
|
+
`).some(s=>{let[,o="",i=""]=s.split("|");return!i||!o.includes(`:${n}->`)?!1:[...r].some(a=>i===a||i.startsWith(`${a}${$u}`))})}function Io(e,t,n=()=>Oo("docker",["ps","--format",`{{.Names}}|{{.Ports}}|{{.Label "${Ru}"}}`],{encoding:"utf-8",timeout:5e3})){let r=jo(e);if(!r||r.ingressHttp===t)return t;try{if(Au(n(),e,r.ingressHttp))return r.ingressHttp}catch{}return t}async function ht(e,t={}){let n=jo(e),r=["TDK_HTTP_PORT","TDK_HTTPS_PORT","TDK_POSTGRES_PORT"].some(d=>process.env[d]);if(t.inspectDocker===!1&&n&&!r)return n;let s="";try{let d=JSON.parse(Co(Be(e,".tdk",_),"utf-8"));s=String(d?.project?.name??"tdk-project").replace(/-/g,"_").toLowerCase()}catch{s="tdk_project"}let o="";if(t.inspectDocker!==!1)try{o=Oo("docker",["ps","--format","{{.Names}}|{{.Ports}}"],{encoding:"utf-8",timeout:5e3})}catch{}let i=d=>_u(o,s,d),a=t.isPortFree??An,c=async d=>await a(d)||i(d);if(n&&!r){for(let d of[n.ingressHttp,n.ingressHttps,n.postgres])if(!await c(d))return ge({isAvailable:c});for(let[d,l]of Object.entries(Rn)){let u=n[d];if(l!==void 0&&u!==l&&await c(l))return ge({isAvailable:c})}return n}return ge({isAvailable:c})}import{stdin as Du,stdout as Ou}from"node:process";import{createInterface as Cu}from"node:readline/promises";async function gt(e){let t=Cu({input:Du,output:Ou});try{return await new Promise((n,r)=>{t.once("close",()=>r(new Error("No input available to answer the prompt. Run this in a terminal."))),t.question(e).then(s=>n(s.trim()),r)})}finally{t.close()}}async function $g(e){let t=e.initial?` (${e.initial})`:"";for(;;){let r=await gt(`${e.message}${t} `)||e.initial||"",s=e.validate?.(r)??!0;if(s===!0)return r;console.log(s)}}async function Fo(e){let t=e.initial??!0,n=t?"Y/n":"y/N";for(;;){let r=(await gt(`${e.message} (${n}) `)).toLowerCase();if(!r)return t;if(["y","yes"].includes(r))return!0;if(["n","no"].includes(r))return!1;console.log("Please answer yes or no.")}}async function _g(e){for(console.log(e.message),e.choices.forEach((t,n)=>{console.log(` ${n+1}. ${t.title}`)});;){let t=await gt("Choose one: "),n=Number.parseInt(t,10)-1,r=e.choices[n];if(r)return r.value;console.log(`Enter a number from 1 to ${e.choices.length}.`)}}async function Rg(e){let t=e.choices.map((r,s)=>r.selected?s+1:null).filter(r=>r!==null),n=t.length>0?` [${t.join(",")}]`:"";for(console.log(e.message),e.choices.forEach((r,s)=>{let o=r.selected?"*":" ";console.log(` ${o} ${s+1}. ${r.title}`)});;){let r=await gt(`Choose one or more numbers, comma-separated${n}: `),s=r?r.split(",").map(a=>Number.parseInt(a.trim(),10)):t,o=Array.from(new Set(s)).map(a=>e.choices[a-1]).filter(a=>!!a).map(a=>a.value),i=e.validate?.(o)??!0;if(i===!0)return o;console.log(i)}}import{relative as Nu}from"node:path";function kt(e,t){return Nu(e,t.configPath)||t.configPath}function Lo(e){let t=e.config?.dependsOn;return t===void 0?[]:Array.isArray(t)&&t.every(n=>typeof n=="string")?t:null}function ju(e){let t=new Set(e.map(a=>a.name)),n=new Map(e.map(a=>[a.name,(Lo(a)??[]).filter(c=>t.has(c))])),r=new Map,s=new Map,o=[],i=a=>{s.set(a,"visiting"),o.push(a);for(let c of n.get(a)??[])if(s.get(c)==="visiting"){let d=o.slice(o.indexOf(c)),l=d.indexOf([...d].sort()[0]??c),u=[...d.slice(l),...d.slice(0,l)];r.set(u.join(">"),[...u,u[0]??c])}else s.has(c)||i(c);o.pop(),s.set(a,"done")};for(let a of[...n.keys()].sort())s.has(a)||i(a);return[...r.values()]}function Dn(e=y()??process.cwd()){let t=x(e).flatMap(n=>Lo(n)===null?[`${kt(e,n)} dependsOn ${JSON.stringify(n.config?.dependsOn)}: expected an array of service names`]:[]);return t.length===0?{name:"dependsOn",didPass:!0,message:"Every dependsOn is a list of service names"}:{name:"dependsOn",didPass:!1,message:`${b(t.length,"invalid dependsOn","invalid dependsOn")}:
|
|
342
|
+
${t.join(`
|
|
343
|
+
`)}`,fix:'Write "dependsOn" as an array of service names, for example ["postgres", "api"]'}}function On(e=y()??process.cwd()){let t=x(e),n=new Map(t.map(o=>[o.name,o])),r=ju(t);if(r.length===0)return{name:"Circular dependencies",didPass:!0,message:"No service depends on itself through dependsOn"};let s=r.map(o=>{let i=n.get(o[0]??""),a=o[1]??"";return`${i?kt(e,i):o[0]} dependsOn "${a}": circular dependency ${o.join(" -> ")}`});return{name:"Circular dependencies",didPass:!1,message:`${r.length} ${r.length===1?"circular dependency":"circular dependencies"}:
|
|
344
|
+
${s.join(`
|
|
345
|
+
`)}`,fix:'Remove one "dependsOn" entry in each cycle so the services no longer wait on each other, or move what they share into a third service'}}function Cn(e=y()??process.cwd()){let t=x(e).flatMap(n=>{let r=n.config?.port;return r===void 0?[]:typeof r=="number"&&Number.isInteger(r)&&r>=1&&r<=65535?[]:[`${kt(e,n)} port ${JSON.stringify(r)}: not an integer from 1 to 65535`]});return t.length===0?{name:"Service ports",didPass:!0,message:"Every declared port is valid"}:{name:"Service ports",didPass:!1,message:`${b(t.length,"invalid port")}:
|
|
346
|
+
${t.join(`
|
|
347
|
+
`)}`,fix:'Set "port" to the port the service listens on, an integer from 1 to 65535'}}function Nn(e=y()??process.cwd(),t=!1){let n=[],r=[];for(let o of x(e)){let i=o.config?.schemaVersion;if(i===_e)continue;let a=kt(e,o);i===void 0?n.push(`${a} schemaVersion: missing`):r.push(`${a} schemaVersion ${JSON.stringify(i)}: this tdk supports ${_e}`)}if(n.length===0&&r.length===0)return{name:"schemaVersion",didPass:!0,message:"Every service.json has a supported schemaVersion"};let s=[...n.length>0?["Run `tdk config migrate` to add the missing schemaVersion"]:[],...r.length>0?["A version this tdk does not support cannot be migrated: upgrade tdk"]:[]];return{name:"schemaVersion",didPass:!1,isWarning:!t,message:`${b(n.length+r.length,"service.json file")} without a supported schemaVersion:
|
|
348
|
+
${[...n,...r].join(`
|
|
349
|
+
`)}`,fix:s.join(". ")}}function Lg(e){let t=Nn(e);t.didPass||(console.warn(h.yellow(`\u26A0 ${t.message}`)),t.fix&&console.warn(h.gray(` ${t.fix}`)))}function Mg(e,t={},n=process.exit){let r=[lt(e),Dn(e),On(e),Cn(e)].filter(o=>!o.didPass);if(r.length===0)return;let s=r.map(o=>`${o.message}${o.fix?`
|
|
350
|
+
Fix: ${o.fix}`:""}`).join(`
|
|
351
|
+
`);t.onInvalid?.(s);for(let o of r)console.error(h.red(`\u2717 ${o.message}`)),o.fix&&console.error(h.gray(` Fix: ${o.fix}`));n(1)}import{readFileSync as Iu}from"node:fs";import{join as Fu}from"node:path";function Mo(e){let t=e.trim().match(/(\d+)\.(\d+)(?:\.(\d+))?/);return t?[Number(t[1]),Number(t[2]),Number(t[3]??0)]:null}function Lu(e){try{return JSON.parse(Iu(Fu(e,".tdk",_),"utf-8"))?.minTdkVersion}catch{return}}function Uo(e,t){let n=Lu(e);if(n===void 0)return{status:"none"};let r=typeof n=="string"?Mo(n):null;if(!r||!/^\d+\.\d+\.\d+$/.test(String(n).trim()))return{status:"malformed",message:`.tdk/project.json minTdkVersion ${JSON.stringify(n)} is not a version like "1.3.80"`,fix:'Set minTdkVersion to a MAJOR.MINOR.PATCH string, for example "1.3.80".'};let s=Mo(t),o=s!==null&&(()=>{for(let[a,c]of r.entries()){let d=s[a]??0;if(d!==c)return d>c}return!0})(),i=r.join(".");return o?{status:"ok",required:i}:{status:"too-old",required:i,message:`tdk ${t} is older than the ${i} this project requires (minTdkVersion)`,fix:"Run: tdk upgrade"}}var q=1e4,ie={dockerMissing:"See https://docs.docker.com/get-docker/",dockerDaemonDown:"Start Docker Desktop, OrbStack, or Colima, then retry: tdk doctor",tiltMissing:"curl -fsSL https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh | bash",bunMissing:"curl -fsSL https://bun.sh/install | bash",port80:"Stop the process bound to port 80, or stop local nginx/caddy. Then: tdk doctor",port5432:"Stop local Postgres or change the host port. Then: tdk doctor",notProject:"tdk project --yes"},Ho="WSL2 detected. Use Docker Desktop WSL integration. Guide: docs/wsl2.md",Ko="Native Windows landscape boot is unsupported. Use WSL2 Ubuntu with Docker Desktop integration. Guide: docs/wsl2.md",jn=[0,25,0],ae=[1,2,0];function yt(e,t){let n=Mn(e);return n?Un(n,t):!1}function Hu(e,t,n=process.platform==="linux"&&!!process.env.WSL_DISTRO_NAME){if(!n)return{name:"WSL project location",didPass:!0,isSkipped:!0,message:"Not running in WSL2 - skipped project filesystem check"};let r=e.replace(/\\/g,"/").replace(/\/+$/,"").toLowerCase();if(r!=="/mnt/c"&&!r.startsWith("/mnt/c/"))return{name:"WSL project location",didPass:!0,message:`${Ho} Project is on the WSL filesystem.`};let s=`${Ho} Project is under /mnt/c (${e}); hot reload may be broken. Move it into the WSL filesystem, such as ~/projects.`;return{name:"WSL project location",didPass:!t,isWarning:!t,message:s,fix:"Move the repository under your WSL home directory (for example, ~/projects) and retry tdk doctor."}}function Ku(e){let t=e.replace(/^\[(.*)\]$/,"$1");return t==="0.0.0.0"||/^[0:]*:0\.0\.0\.0$/.test(t)?!0:t.includes(":")&&/^[0:]+$/.test(t)}function Gu(e=process.env,t=!1){let n=(e.TDK_BIND_ADDRESS??"").trim();return Ku(n)?{name:"Dev Port Bind Address",didPass:!t,isWarning:!t,message:`TDK_BIND_ADDRESS is ${n}: Traefik and Postgres are reachable from other machines on the network.`,fix:"Unset TDK_BIND_ADDRESS to keep dev ports on 127.0.0.1. Keep it only when you test from another device on purpose."}:{name:"Dev Port Bind Address",didPass:!0,message:n?`Dev ports bind to ${n}`:"Dev ports bind to 127.0.0.1 (this machine only)"}}function Go(e){let t=e.filter(r=>!r.didPass&&!r.isSkipped&&!r.isWarning),n=e.filter(r=>r.didPass||r.isSkipped||r.isWarning);return t.sort((r,s)=>{let o=i=>i.name==="Host Ports"&&/\b5432\b/.test(i.message)?0:i.name==="Host Ports"?1:i.name==="Ingress Ports"?2:3;return o(r)-o(s)}),[...t,...n]}function Vu(e,t){return t?e?"Doctor passed. Next: tdk up":"Doctor passed. Next: tdk project example":"Doctor failed. Fix the items above, then run: tdk doctor"}function Hn(e){return io(e)}function Wu(e,t,n,r,s){return async(o=K)=>{try{return await o(t,q),{name:e,didPass:!0,message:n}}catch(i){return Hn(i)?{name:e,didPass:!1,message:`${e} did not respond within ${q/1e3}s`,fix:s}:{name:e,didPass:!1,message:r,fix:s}}}}async function In(e){try{return await K(e,q),!0}catch{return!1}}async function Kn(){if(process.platform==="win32")try{return await K("docker ps",q),{name:M,didPass:!0,message:"Docker Desktop Linux container engine is running"}}catch{return{name:M,didPass:!1,message:"Docker is not running",fix:ie.dockerDaemonDown}}let e=!!N("docker");try{return await K("docker ps",q),{name:M,didPass:!0,message:"Docker daemon is running"}}catch(n){if(Hn(n))return{name:M,didPass:!1,message:"Docker daemon is not responding",fix:ie.dockerDaemonDown}}let t=Js(await In("pgrep -f com.docker.backend"));return t||(await In("colima status")?{name:M,didPass:!0,message:"Colima (Docker runtime) is running"}:N("colima")?{name:M,didPass:!1,message:"Colima is installed but not running",fix:ie.dockerDaemonDown}:await In("podman ps")?{name:M,didPass:!0,message:"Podman is running"}:e?{name:M,didPass:!1,message:"Docker daemon is not running",fix:ie.dockerDaemonDown}:{name:M,didPass:!1,message:"Docker is not running",fix:ie.dockerMissing})}var Gn=Wu("Docker Compose","docker compose version","Docker Compose plugin available","Docker Compose plugin not found","Install Docker Compose: https://docs.docker.com/compose/install/"),Ju=[25,0,0],qu=[2,20,2];function Mn(e){let t=e.trim().match(/(\d+)\.(\d+)(?:\.(\d+))?/);return t?[Number(t[1]),Number(t[2]),Number(t[3]??0)]:null}function Un(e,t){for(let[n,r]of t.entries()){let s=e[n]??0;if(s!==r)return s>r}return!0}async function Vn(e=K){let t=async a=>String(await e(a,q)).trim(),n,r;try{[n,r]=await Promise.all([t("docker version --format '{{.Server.Version}}'"),t("docker compose version --short")])}catch{return{name:"Docker Versions",didPass:!0,isSkipped:!0,message:"Could not read Docker Engine/Compose versions - skipped version check"}}let s=Mn(n),o=Mn(r);if(!s||!o)return{name:"Docker Versions",didPass:!0,isSkipped:!0,message:`Unrecognized Docker version output (engine "${n}", compose "${r}") - skipped version check`};let i=[];return Un(s,Ju)||i.push(`Docker Engine ${n} (need 25.0+)`),Un(o,qu)||i.push(`Docker Compose ${r} (need 2.20.2+)`),i.length===0?{name:"Docker Versions",didPass:!0,message:`Docker Engine ${n} and Compose ${r} support generated healthchecks`}:{name:"Docker Versions",didPass:!1,message:`Docker is too old for generated healthchecks (they use start_interval):
|
|
352
|
+
${i.join(`
|
|
353
|
+
`)}`,fix:"Update Docker Desktop, or on Linux update docker-ce and the docker-compose-plugin package"}}async function Wn(e=K){let t;try{t=String(await e("tilt version",q)).trim()}catch{return{name:"Tilt CLI",didPass:!1,message:"Tilt CLI not found",fix:ie.tiltMissing}}return yt(t,jn)?{name:"Tilt CLI",didPass:!0,message:`Tilt ${t} is available`}:{name:"Tilt CLI",didPass:!1,message:`Tilt ${t||"version unknown"} is below the required v${jn.join(".")} floor`,fix:`Install Tilt v${jn.join(".")} or newer: https://docs.tilt.dev/install.html`}}async function zu(e=K){let t;try{t=String(await e("docker info --format '{{.OSType}}'",q)).trim().toLowerCase()}catch{return{name:"Docker OS",didPass:!0,isSkipped:!0,message:"Docker OS unavailable - daemon failure reported separately"}}return t!=="linux"?{name:"Docker OS",didPass:!1,message:`Docker is using the ${t||"unknown"} engine; TDK requires Linux containers`,fix:"Switch Docker Desktop to Linux containers, enable WSL2 integration, then retry tdk doctor."}:{name:"Docker OS",didPass:!0,message:"Docker is using Linux containers"}}async function Yu(e=K){let t=y();if(!t)return{name:"Bun",didPass:!0,isSkipped:!0,message:"Not in a project - skipping Bun check"};if(!st(x(t)))return{name:"Bun",didPass:!0,isSkipped:!0,message:"No generated JS services - skipping Bun check"};try{let n=String(await e("bun --version",q)).trim();return yt(n,ae)?{name:"Bun",didPass:!0,message:`Bun ${n} is available`}:{name:"Bun",didPass:!1,message:`Bun ${n||"version unknown"} is below the required ${ae.join(".")} floor`,fix:`Install Bun ${ae.join(".")} or newer: https://bun.sh/install`}}catch{return{name:"Bun",didPass:!1,message:"Bun is not available",fix:ie.bunMissing}}}function Xu(){let e=y()??process.cwd(),t=Cs(e);if(t.missing.length>0)return{name:"Environment Variables",didPass:!1,message:`Missing required env variables: ${t.missing.join(", ")}`,fix:U(A(e,".env"))?`Edit .env and set these values:
|
|
354
|
+
${t.missing.map(r=>`${r}=<value>`).join(`
|
|
355
|
+
`)}`:"No .env file yet. Run `tdk project` to create one with working defaults."};if(t.invalid.length>0)return{name:"Environment Variables",didPass:!1,message:`Invalid env variables: ${t.invalid.join(", ")}`,fix:"Edit .env and provide values for empty variables"};let n=t.warnings.filter(r=>!r.startsWith(".env file not found"));return{name:"Environment Variables",didPass:!0,message:n.length>0?`All required environment variables are set (warning: ${n.join("; ")})`:"All required environment variables are set"}}function Qu(){let e=y()??process.cwd(),t=A(e,".tdk",".tdk-out"),n=$t.filter(r=>!U(A(t,r)));return n.length===0?{name:"Master Configs",didPass:!0,message:`${$t.join(", ")} found`}:{name:"Master Configs",didPass:!1,message:`Master configs missing: ${n.join(", ")}`,fix:"Run: tdk project"}}function Zu(e=hr()){let t=y();if(!t)return{name:"TDK version",didPass:!0,isSkipped:!0,message:"Not in a project - skipping TDK version check"};let n=Uo(t,e);return n.status==="none"?{name:"TDK version",didPass:!0,isSkipped:!0,message:`tdk ${e} (no minTdkVersion in .tdk/project.json)`}:n.status==="malformed"||n.status==="too-old"?{name:"TDK version",didPass:!1,message:n.message,fix:n.fix}:{name:"TDK version",didPass:!0,message:`tdk ${e} meets minTdkVersion ${n.required}`}}function el(){let e=y()??process.cwd(),t=sn(e),n=x(e).filter(s=>!ot(e,s.path,t));if(n.length===0)return{name:"Resource Discovery",didPass:!0,message:`All resources are covered by discovery.paths ${JSON.stringify(t)}`};let r=n.map(s=>Fn(Ln(e,s.path))).join(", ");return{name:"Resource Discovery",didPass:!1,message:`${b(n.length,"resource")} outside discovery.paths ${JSON.stringify(t)}: ${r}. \`tdk up\` will not start them`,fix:"Move each under services/<stack>/<name>, or add a matching pattern to discovery.paths in .tdk/project.json and run: tdk config regenerate"}}var tl=["install-deps.sh","bun-hoisted-symlink-fix.sh","prisma-bun-client-link-fix.sh","prisma-normalize-client.sh","infisical-entrypoint.sh"],nl=/shared-platform-engineering\/docker-templates\/([\w.-]+)/g;function rl(e){let t=new Set;for(let n of x(e)){let r=A(n.path,D);if(U(r)){for(let s of Uu(r))if(!(!s.startsWith("Dockerfile")||s.endsWith(".dockerignore")))for(let o of ke(A(r,s),"utf-8").matchAll(nl))t.add(o[1])}}return[...t]}function sl(){let e=y()??process.cwd(),t=[];U(A(e,R))||t.push(`${R} (workspace root manifest)`);let n=A(e,"shared-platform-engineering","docker-templates"),r=[];try{r=rl(e)}catch{}for(let s of new Set([...tl,...r]))U(A(n,s))||t.push(`shared-platform-engineering/docker-templates/${s}`);return t.length===0?{name:"Generated Runtime Assets",didPass:!0,message:"Generated Docker runtime assets are present"}:{name:"Generated Runtime Assets",didPass:!1,message:`Generated Dockerfiles reference missing project runtime assets:
|
|
356
|
+
${t.join(`
|
|
357
|
+
`)}`,fix:"Run `tdk project --yes` (or `tdk config regenerate`) with an updated TDK. `tdk up` also refreshes these assets before starting Tilt."}}function ol(){let e=y()??process.cwd(),t=x(e),n=[];for(let r of t){let s=A(r.path,R);if(!U(s))continue;let o=r.config?.appType??"backend",i=_t[o]??_t.backend,a;try{a=JSON.parse(ke(s,"utf-8")).scripts??{}}catch{n.push(`${r.name} (package.json is invalid JSON)`);continue}let c=i.filter(d=>!a[d]);c.length>0&&n.push(`${r.name}: missing "${c.join('", "')}"`)}return n.length===0?{name:"Startup Scripts",didPass:!0,message:"All services have required package.json scripts"}:{name:"Startup Scripts",didPass:!1,message:`Services missing required package.json scripts:
|
|
358
|
+
${n.join(`
|
|
359
|
+
`)}`,fix:`Add the missing scripts to each service's package.json, e.g.:
|
|
360
|
+
"dev": "bun run src/index.ts",
|
|
361
|
+
"build": "tsc",
|
|
362
|
+
"start": "bun run dist/index.js"`}}function Wo(e){if(e.startsWith("@"))return[e.split("/").slice(0,2).join("/")];let t=e.split("/")[0]??e;return[`@types/${t}`,t]}function Jo(e,t,n=0){let r=e[t];if(typeof r!="string"||n>3)return!1;if(/(^|[\s;&|(])tsc(\s|$)/.test(r))return!0;for(let s of r.matchAll(/\b(?:bun|npm|pnpm|yarn)\s+run\s+([\w:.-]+)/g))if(Jo(e,s[1],n+1))return!0;return!1}function Vo(e){let t=Wo(e);return e.includes("/")||e.startsWith("@")?t[t.length-1]:t[0]}var il={"@types/bun":"^1.4.2","@types/node":"^20.0.0"};function al(e,t){return(t.includes("bun")||e.has("@types/bun"))&&!e.has("@types/node")}function cl(){let e=y()??process.cwd(),t=x(e),n=[],r=new Set;for(let o of t){let i=A(o.path,R),a=A(o.path,D,"tsconfig.docker.autogenerated.json");if(!U(i)||!U(a))continue;let l=Bn(a)?.compilerOptions?.types;if(!Array.isArray(l))continue;let u=Bn(i),p=u?.scripts??{};if(!Jo(p,"build"))continue;let k=new Set([...Object.keys(u?.devDependencies??{}),...Object.keys(u?.dependencies??{})]),m=l.filter(T=>typeof T=="string").filter(T=>!Wo(T).some(w=>k.has(w)));for(let T of m)r.add(Vo(T));if(m.length>0){let T=m.map(w=>`"${w}" (needs ${Vo(w)})`).join(", ");n.push(`${o.name}: "types" lists ${T}`)}al(k,l)&&(r.add("@types/node"),n.push(`${o.name}: uses @types/bun, which references node types internally, but @types/node is not a direct dependency`))}if(n.length===0)return{name:"TypeScript Type Dependencies",didPass:!0,message:`Every service's Docker tsconfig "types" entry has its @types package installed`};let s=[...r].map(o=>{let i=il[o];return i?`"${o}": "${i}"`:`${o} (bun add -d ${o})`});return{name:"TypeScript Type Dependencies",didPass:!1,message:`\`bun run build\` will fail with TS2688 (Cannot find type definition file) because a @types package it needs is not a direct dependency:
|
|
363
|
+
${n.join(`
|
|
364
|
+
`)}`,fix:`Add to each service's package.json devDependencies: ${s.join(", ")}`}}function Bn(e){try{return JSON.parse(ke(e,"utf-8"))}catch{return null}}function dl(e){let n=Bn(e)?.compilerOptions;return n?.allowSyntheticDefaultImports===!0||n?.esModuleInterop===!0}function ul(){let e=y()??process.cwd(),t=x(e).filter(r=>r.config?.appType==="frontend");if(t.length===0)return{name:"Frontend Docker Preflight",didPass:!0,isSkipped:!0,message:"No frontend services to check"};let n=[];for(let r of t){U(A(r.path,"public"))||n.push(`${r.name}: no public/ folder, but the Docker build copies it. Create it: mkdir -p ${Fn(Ln(e,A(r.path,"public")))} && touch ${Fn(Ln(e,A(r.path,"public",".gitkeep")))}`);let s=A(r.path,D,"Dockerfile.app.autogenerated"),o=A(r.path,D,"tsconfig.docker.autogenerated.json");if(U(s)){let i=ke(s,"utf-8"),a=/\bEXPOSE\s+80\b/.test(i),c=r.config?.stack,d=c?A(e,"services",c,D,"docker-compose.app.autogenerated.yml"):"";if(a&&d&&U(d)){let l=ke(d,"utf-8"),u=r.name.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),p=new RegExp(`traefik\\.http\\.services\\.${u}\\.loadbalancer\\.server\\.port=(\\d+)`),m=l.match(p)?.[1];m&&m!=="80"&&n.push(`${r.name}: nginx runtime exposes 80, but generated Traefik routes to ${m}`)}}if(U(o)&&!dl(o)){let i=A(r.path,"src","main.tsx");if(U(i)){let a=ke(i,"utf-8"),c=[/import\s+\w+\s+from\s+["']react["']/,/import\s+\w+\s+from\s+["']react-dom\/client["']/].filter(d=>d.test(a)).map(d=>d.source.includes("react-dom")?"react-dom/client":"react");c.length>0&&n.push(`${r.name}: Docker tsconfig does not allow synthetic default imports, but src/main.tsx default-imports ${c.join(" and ")}`)}}}return n.length===0?{name:"Frontend Docker Preflight",didPass:!0,message:`${b(t.length,"frontend")} ${t.length===1?"has":"have"} compatible Docker build/routing metadata`}:{name:"Frontend Docker Preflight",didPass:!1,message:`Frontend Docker metadata has issues that will make \`tdk up\` fail or route 404:
|
|
365
|
+
${n.join(`
|
|
366
|
+
`)}`,fix:'Regenerate with a current TDK (`tdk config regenerate`) and ensure frontend nginx runtimes route to port 80. For React entrypoints, prefer named imports: `import { StrictMode } from "react"` and `import { createRoot } from "react-dom/client"`.'}}var ll=90,pl=80;function fl(e){return e.split(`
|
|
367
|
+
`).map(t=>t.trim()).filter(Boolean).map(t=>{let[n,r,s]=t.split(",");return{name:n??"unknown",memPct:Number.parseFloat((r??"").replace(/%/g,"")),cpuPct:Number.parseFloat((s??"").replace(/%/g,""))}}).filter(t=>Number.isFinite(t.memPct)&&Number.isFinite(t.cpuPct))}function ml(){let e;try{e=Mu(N("docker")??"docker",["stats","--no-stream","--format","{{.Name}},{{.MemPerc}},{{.CPUPerc}}"],{stdio:"pipe",encoding:"utf-8",timeout:q,windowsHide:process.platform==="win32"})}catch(s){return{name:"Container Resource Health",didPass:!0,isSkipped:!0,message:Hn(s)?"Docker not responding - skipped container resource check":"Docker not running - skipped container resource check"}}let t=fl(e);if(t.length===0)return{name:"Container Resource Health",didPass:!0,isSkipped:!0,message:"No running containers to check"};let n=t.filter(s=>s.memPct>=ll&&s.cpuPct>=pl);if(n.length===0)return{name:"Container Resource Health",didPass:!0,message:`${b(t.length,"container")} within normal resource usage`};let r=n.map(s=>`${s.name} (mem ${s.memPct.toFixed(0)}%, cpu ${s.cpuPct.toFixed(0)}%)`).join(`
|
|
368
|
+
`);return{name:"Container Resource Health",didPass:!1,message:`${b(n.length,"container")} pinned near its memory limit while burning CPU -- likely stuck in a boot-time retry/fetch loop, not a normal crash:
|
|
369
|
+
${r}`,fix:"Check the container's boot logs for a network fetch loop (e.g. `bunx <pkg>` re-downloading a CLI on every start instead of using one already installed): docker logs <container>. If a Prisma auto-migration is the cause, set AUTO_MIGRATE=false in the project's .env as a temporary unblock."}}var hl=5e3;async function gl(e,t){let n=y()??process.cwd(),r=jt(x(n),t);return r.length===0?{name:"Service Health",didPass:!0,isSkipped:!0,message:"No routable services to ping"}:fn(await Ye(r,e))}async function kl(e,t){let n=y()??process.cwd(),r=x(n),s=_r($r(r,t),{timeoutSeconds:Math.ceil(e/1e3)});return s.length===0?mn([]):(await Ye(jt(r,t),e)).some(i=>i.status!==void 0)?mn(await Dr(s)):{name:"Service Smoke",didPass:!0,isSkipped:!0,message:"No services responded - skipped smoke checks"}}async function yl(e,t){let n=y()??process.cwd(),r=x(n),s=gr(r,t);return s.length===0?{name:"Advertised Endpoints",didPass:!0,isSkipped:!0,message:"No routable services to check"}:Es(await Ye(s,e))}function Sl(e,t){if(e.length===0){console.log(`${h.green("\u2713")} Nothing to repair
|
|
370
|
+
`);return}for(let n of e){let r=n.status==="planned"?`${h.cyan("would run")}`:n.status==="applied"?h.green("ran"):n.status==="failed"?h.red("failed"):h.gray("skipped"),s=n.status==="planned"?"":` (${n.reason})`;console.log(`${r}: ${n.command}${s}`)}t&&console.log(h.gray(`
|
|
371
|
+
Dry run: nothing was changed.`)),console.log("")}var Tl=new Bu("doctor").description("Check environment readiness for TDK").option("--json","Output a versioned JSON readiness report",!1).exitOverride(e=>{e.exitCode===0&&process.exit(0),Tl.opts().json&&console.log(JSON.stringify(Z([],!1,[{code:"USAGE",message:e.message}]))),process.exit(2)}).option("--no-ping","Skip pinging running services' /health endpoints").option("--strict","Fail warnings such as a WSL project under /mnt/c").option("--fix","Repair this project's unused Docker networks and leftover doctor probes",!1).option("--dry-run","With --fix: list the repairs without changing anything",!1).option("-y, --yes","With --fix: apply every repair without asking",!1).option("--ping-timeout <ms>","Per-service ping timeout in milliseconds",String(hl)).action(async e=>{let t=Number(e.pingTimeout);if(!/^\d+$/.test(e.pingTimeout)||!Number.isSafeInteger(t)||t<=0){let f="--ping-timeout must be a positive integer number of milliseconds";e.json&&console.log(JSON.stringify(Z([],!1,[{code:"USAGE",message:f}]))),console.error(f),process.exit(2);return}if((e.dryRun||e.yes)&&!e.fix){let f=`${e.dryRun?"--dry-run":"--yes"} only applies with --fix`;e.json&&console.log(JSON.stringify(Z([],!1,[{code:"USAGE",message:f}]))),console.error(f),process.exit(2);return}e.json||console.log("TDK doctor checks your local Docker + Tilt development environment; it does not check cluster deployments.");let n=cs();if(process.platform==="win32"){if(e.json){let f=null;try{f=await ge()}catch{}console.log(JSON.stringify(Z([{name:"Native Windows support",didPass:!1,message:Ko,fix:"Use WSL2 Ubuntu with Docker Desktop integration. Guide: docs/wsl2.md"}],!!y(),[],f,{...n,canUp:!1})))}console.error(Ko),process.exit(1);return}if(n.kind==="webcontainer"){let f=Z([{name:"Host",didPass:!1,message:rn,fix:`Use a machine with Docker. Guide: ${rt}`}],!!y(),[],void 0,n);e.json&&console.log(JSON.stringify(f)),console.error(rn),process.exit(1);return}e.json||(console.log(`
|
|
372
|
+
${h.bold("\u{1F50D} TDK Doctor")}
|
|
373
|
+
`),console.log(`Checking environment...
|
|
374
|
+
`));let r=y(),s;if(e.fix){try{let f;if(r)try{f=nt(r).project.name}catch{}let v=r?Gs(r):[],G=await Us(f,void 0,v.length>0?`Tilt is running for this project (pid ${v.join(", ")}); run tdk down first`:void 0);s=await Bs(G,{dryRun:e.dryRun,confirm:e.yes||!e.json&&process.stdin.isTTY?async te=>e.yes||await Fo({message:`Run \`${te.command}\` (${te.check}, ${te.reason})?`}):void 0})}catch(f){s=[{id:"plan-fixes",check:"Docker networks",scope:"project",command:"docker network ls",status:"failed",reason:`could not list Docker networks: ${(f instanceof Error?f.message:String(f)).split(/\r?\n/)[0]}`}]}e.json||Sl(s,e.dryRun)}let o,i;try{o=await ht(r??process.cwd())}catch(f){o=null,i=f instanceof Error?f.message:String(f)}let a=[()=>o?{name:"Host Ports",didPass:!0,message:`HTTP ${o.ingressHttp}, HTTPS ${o.ingressHttps}, Postgres ${o.postgres}`}:{name:"Host Ports",didPass:!1,message:i??"Could not select host ports",fix:"Set TDK_HTTP_PORT, TDK_HTTPS_PORT, or TDK_POSTGRES_PORT to available host ports."},()=>o?pn(Bo,void 0,[o.ingressHttp,o.ingressHttps]):{name:"Ingress Ports",didPass:!1,message:"Ingress port plan is unavailable"},Kn,()=>Ws(),zu,Wn,Gn,Vn,Yu,()=>Hu(y()??process.cwd(),e.strict),()=>Gu(process.env,e.strict),()=>Ao(),()=>Ks()],c=[()=>Zu(),Qu,sl,Zs,ol,()=>Ps(),cl,ul,el,()=>So(),()=>lt(),()=>Dn(),()=>On(),()=>Cn(),()=>Nn(void 0,e.strict),()=>To(),()=>bo(),()=>Eo(),()=>wo(),()=>ft(),()=>ko(),()=>_o(),()=>$o(),()=>Ro(),Xu,async()=>{let f=y()??process.cwd(),v=un(f)?await He(f):!0;return ln(Bo,f,void 0,v)},ml,()=>{let f=y()??process.cwd(),v=[];try{v=Sn(f)}catch{return{name:"Crash-looping containers",didPass:!0,isSkipped:!0,message:"Could not read .env - skipped crash-loop check"}}return js(ce(),v)},hn];if(e.ping){let f=r?Io(r,o?.ingressHttp):o?.ingressHttp;c.push(()=>gl(t,f)),c.push(()=>kl(t,f)),c.push(()=>yl(t,f))}let d=!!r,{checks:l,errors:u}=await zs(a,d?c:[]);if(ds(n.kind))for(let f of l)f.name===M&&!f.didPass&&(f.fix=as);let p=Z(Go(l),d,u,o,n);s&&(p.data.actions=s),s&&Hs(s)&&(p.data.ready=!1);let m=Ys(p),T=p.data.ready;if(e.json){let f;try{f=Sn(y()??process.cwd())}catch(v){if(!(v instanceof Fe))throw v;console.log(JSON.stringify(Z([],d,[...u,{code:"ENV_UNREADABLE",message:v.message}]))),console.error(v.message),process.exit(1)}console.log(JSON.stringify(it(p,f)));for(let v of u)console.error(Le(v.message,f));m!==0&&process.exit(m);return}for(let f of u)console.error(`${h.red("\u2717")} Doctor check failed: ${f.message}`);for(let f of Go(l))!f.didPass&&!f.isSkipped&&!f.isWarning?(console.log(`${h.red("\u2717")} ${f.message}`),f.fix&&console.log(`${h.blue("\u2139")} Fix: ${f.fix}`)):f.isWarning?(console.log(`${h.yellow("\u26A0")} ${h.yellow(f.message)}`),f.fix&&console.log(`${h.blue("\u2139")} Fix: ${f.fix}`)):f.isSkipped?(console.log(`${h.gray("\u25CB")} ${h.gray(f.message)}`),f.fix&&console.log(`${h.blue("\u2139")} ${f.fix}`)):console.log(`${h.green("\u2713")} ${f.message}`);console.log(""),T&&!d&&(console.log(`${h.gray("\u25CB")} ${h.gray("Not in a TDK project, so project checks were skipped")}`),console.log(""));let w=Vu(d,T);console.log(`${T?h.green(h.bold("\u2713")):h.red(h.bold("\u2717"))} ${w}`),m!==0&&process.exit(m)});function qo(e){return yt(e,ae)}var St=`${ae[0]}.${ae[1]}+`;function wl(e,t){return{id:e,ok:t.didPass||!!t.isSkipped,message:t.message,fix:t.fix}}function Yo(e){let[t=0,n=0]=String(e).split(".").map(r=>Number.parseInt(r,10));return!Number.isFinite(t)||!Number.isFinite(n)||t<22||t===22&&n<12}async function Tt(e,t,n){try{return wl(e,await t())}catch{return{id:e,ok:!1,message:n}}}function El(e){return e.isSkipped?{...e,didPass:!1,isSkipped:!1,message:`Could not verify Docker Engine/Compose minimum versions: ${e.message}`,fix:e.fix??"Start Docker and ensure Docker Engine 25+ and Compose 2.20.2+ are installed."}:e}function vl(e){try{return x(e).some(t=>t.config?.featuresEnabled?.includes("prisma")??!1)}catch{return!1}}async function Pl(e={}){let t=e.cwd??process.cwd(),n=y(t),r=!!n,s=[],o=process.versions.node;s.push({id:"node",ok:!Yo(o),message:o,fix:Yo(o)?`Install Node.js 22.12+ (current: ${o}). https://nodejs.org`:void 0}),s.push(await Tt("docker",Kn,"Docker daemon is not running")),s.push(await Tt("compose",async()=>{let[m,T]=await Promise.all([Gn(),Vn()]);return m.didPass?El(T):m},"Docker Engine or Compose version check failed")),s.push(await Tt("tilt",Wn,"Tilt CLI not found"));let i=n?st(x(n)):!0,a=!1,c=i?N("bun"):void 0;if(c)try{let m=zo(c,["--version"],{encoding:"utf-8",timeout:3e3}).trim();a=qo(m)}catch{}s.push(i?{id:"bun",ok:a,message:a?`Bun ${St} is available on PATH`:`Bun ${St} not found on PATH`,fix:a?void 0:"curl -fsSL https://bun.sh/install | bash"}:{id:"bun",ok:!0,message:"Bun not needed: no generated JS services"});try{let m=await ht(n??t);s.push({id:"ports",ok:!0,message:`HTTP ${m.ingressHttp}, HTTPS ${m.ingressHttps}, Postgres ${m.postgres}`})}catch(m){s.push({id:"ports",ok:!1,message:m instanceof Error?m.message:String(m),fix:"Set TDK_HTTP_PORT, TDK_HTTPS_PORT, or TDK_POSTGRES_PORT to available host ports."})}if(n){let m=await Tt("nats",()=>ft(n),"NATS broker check failed");if(m.ok||s.push(m),vl(n)){let T=!1;try{let w=JSON.parse(bl(xl(n,".tdk",_),"utf-8")),f=String(w?.project?.name??"").replace(/-/g,"_");f&&(T=zo(N("docker")??"docker",["ps","--filter",`label=com.docker.compose.project=${f}`,"--filter","label=com.docker.compose.service=postgres","--filter","health=healthy","--format","{{.Names}}"],{encoding:"utf-8",timeout:3e3}).trim().length>0)}catch{}T||s.push({id:"prisma",ok:!1,message:"A Prisma resource is configured, but the project Postgres service is not healthy",fix:"Run `tdk up` to start the bundled Postgres, then check `tdk doctor`."})}}let d=s.filter(m=>!m.ok).slice(0,8),l=d.some(m=>["node","docker","compose","tilt","bun","ports"].includes(m.id)),u=d.some(m=>m.id==="bun"),p=d.length?l?u?`Cold start blocked. Bun ${St} must be installed on PATH.
|
|
375
|
+
Prisma/NATS configuration is project-specific and generated after \`tdk project\`. Fix the machine checks below.`:"Cold start blocked. Bun/Prisma/NATS are not the first failure.\nThey are generated after `tdk project`. Fix the machine checks below.":"Machine is ready. Project wiring is not.":"",k=d.length?"":r?"Environment ready for TDK":`This machine is ready for TDK.
|
|
376
|
+
Next: tdk project --yes`;return{ok:d.length===0,inProject:r,items:d,header:p,footer:k}}function $l(e,t={}){let n=[e.header];for(let r of e.items)n.push(`FAIL ${r.id.padEnd(7)} ${r.message}`),r.fix&&n.push(` ${r.fix}`);return e.footer&&(t.includeSuccessFooter??!0)&&n.push(e.footer),n.filter(Boolean).join(`
|
|
377
|
+
`)}async function Mk(){let e=await Pl();e.items.some(n=>["node","docker","compose","tilt","bun","ports"].includes(n.id))&&(console.error($l(e)),process.exit(1))}export{h as a,fr as b,W as c,O as d,ir as e,D as f,We as g,Je as h,_ as i,R as j,$t as k,Hl as l,$ as m,ve as n,Kl as o,ep as p,Ki as q,tp as r,np as s,C as t,y as u,hr as v,Ct as w,oa as x,Nt as y,$r as z,Ap as A,Dr as B,_e as C,Np as D,Ut as E,Ir as F,Ae as G,Mp as H,Up as I,N as J,Br as K,Oa as L,Vr as M,Ca as N,tf as O,nf as P,Jr as Q,lf as R,j as S,z as T,pf as U,ja as V,ff as W,mf as X,hf as Y,b as Z,yf as _,Sf as $,Tf as aa,bf as ba,xf as ca,wf as da,Ef as ea,vf as fa,Pf as ga,Vt as ha,$f as ia,_f as ja,Rf as ka,Af as la,Df as ma,Of as na,Cf as oa,Nf as pa,zr as qa,Ha as ra,Ka as sa,Lf as ta,Mf as ua,Uf as va,Bf as wa,Hf as xa,Wt as ya,Qt as za,es as Aa,za as Ba,x as Ca,Oe as Da,em as Ea,tm as Fa,nm as Ga,rm as Ha,sm as Ia,om as Ja,Xa as Ka,im as La,Ce as Ma,tt as Na,nn as Oa,nt as Pa,xm as Qa,gc as Ra,kc as Sa,wm as Ta,$g as Ua,Fo as Va,_g as Wa,Rg as Xa,Dh as Ya,Bh as Za,co as _a,En as $a,ko as ab,gu as bb,rn as cb,as as db,cs as eb,ds as fb,sn as gb,ot as hb,Om as ib,ms as jb,gs as kb,jc as lb,Ic as mb,dn as nb,Cs as ob,Xm as pb,Qm as qb,Fe as rb,Sn as sb,Le as tb,it as ub,uh as vb,lh as wb,ph as xb,ge as yb,hg as zb,jo as Ab,xg as Bb,wg as Cb,ht as Db,Lg as Eb,Mg as Fb,Uo as Gb,Tl as Hb,Yo as Ib,El as Jb,Pl as Kb,$l as Lb,Mk as Mb};
|