@flisk-sh/flisk 1.3.189 → 1.3.191

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/CHANGELOG.md +9 -9
  2. package/LICENSE +1 -1
  3. package/README.md +41 -41
  4. package/Tiltfile +2 -2
  5. package/cli/bin/{flisk.js → tdk.js} +1 -1
  6. package/cli/dist/cli.js +61 -0
  7. package/cli/dist/commands/__tests__/project-e2e-helpers.js +2 -2
  8. package/cli/dist/commands/completion.js +26 -26
  9. package/cli/dist/commands/config.js +1 -1
  10. package/cli/dist/commands/import.js +4 -4
  11. package/cli/dist/commands/maintainers.js +1 -1
  12. package/cli/dist/commands/mcp.js +1 -4
  13. package/cli/dist/commands/project.js +1 -1
  14. package/cli/dist/commands/resource.js +1 -1
  15. package/cli/dist/commands/ui.js +1 -1
  16. package/cli/dist/commands/up.js +77 -3
  17. package/cli/dist/commands/upgrade.js +39 -25
  18. package/cli/dist/generator/extension-fetch.js +3 -3
  19. package/cli/dist/generator/template-engine.js +8 -8
  20. package/cli/dist/mcp/tools.js +2 -5
  21. package/cli/dist/program.js +2 -0
  22. package/cli/dist/utils/cli-command.js +107 -0
  23. package/cli/dist/utils/cold-preflight.js +2 -1
  24. package/cli/dist/utils/constants.js +1 -1
  25. package/cli/dist/utils/doctor-fix.js +4 -5
  26. package/cli/dist/utils/doctor-runtime.js +1 -1
  27. package/cli/dist/utils/doctor-starlark.js +1 -1
  28. package/cli/dist/utils/doctor-wiring.js +2 -2
  29. package/cli/dist/utils/env-selection.js +201 -0
  30. package/cli/dist/utils/env-validator.js +6 -0
  31. package/cli/dist/utils/one-command.js +41 -0
  32. package/cli/dist/utils/paths.js +1 -1
  33. package/cli/dist/utils/platform.js +2 -6
  34. package/cli/dist/utils/project-templates.js +11 -7
  35. package/cli/dist/utils/secret-redaction.js +15 -6
  36. package/cli/dist/utils/service-manifest.js +1 -1
  37. package/cli/dist/utils/smoke.js +2 -2
  38. package/cli/dist/utils/tar.js +1 -1
  39. package/cli/dist-bundle/{chunk-BZ4H7MM4.js → chunk-42IVF3YT.js} +2 -2
  40. package/cli/dist-bundle/chunk-GOEBYLFN.js +2 -0
  41. package/cli/dist-bundle/chunk-KGKKDD4K.js +2 -0
  42. package/cli/dist-bundle/chunk-LCSYOVPP.js +438 -0
  43. package/cli/dist-bundle/chunk-R3OOKBPG.js +34 -0
  44. package/cli/dist-bundle/chunk-V6K6MWSE.js +75 -0
  45. package/cli/dist-bundle/cli.js +344 -372
  46. package/cli/dist-bundle/cold-preflight-WDQRF5G6.js +2 -0
  47. package/cli/dist-bundle/{extension-fetch-J4FFH7TJ.js → extension-fetch-6XNTDF44.js} +1 -1
  48. package/cli/dist-bundle/one-command-BSYKMVAQ.js +4 -0
  49. package/cli/dist-bundle/project-WPYN4RTL.js +2 -0
  50. package/cli/dist-bundle/project-templates-BHURA3LT.js +2 -0
  51. package/cli/dist-bundle/url-actions-D6ROTHZG.js +2 -0
  52. package/cli/package.json +2 -4
  53. package/cli/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +1 -1
  54. package/cli/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +1 -1
  55. package/cli/shared-platform-engineering/docker-templates/install-deps.sh +1 -1
  56. package/cli/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +1 -1
  57. package/cli/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +1 -1
  58. package/cli/templates/Tiltfile.hbs +1 -1
  59. package/discovery/AGENTS.md +1 -1
  60. package/discovery/TILT_DISCOVERY.star +3 -3
  61. package/discovery/Tiltfile +3 -3
  62. package/discovery/config.star +1 -1
  63. package/discovery/constants.star +1 -1
  64. package/discovery/discovery_orchestrator.star +1 -1
  65. package/discovery/identity-management-frontend/service.yaml +1 -1
  66. package/discovery/json_manifest_scanner.star +1 -1
  67. package/discovery/manifest/constants.star +1 -1
  68. package/discovery/manifest/loading.star +2 -2
  69. package/discovery/manifest/normalize.star +1 -1
  70. package/discovery/registry.star +1 -1
  71. package/discovery/resource_snapshot.py +1 -1
  72. package/discovery/resource_snapshot.star +1 -1
  73. package/discovery/test_resource_snapshot.py +2 -2
  74. package/engine/AGENTS.md +7 -7
  75. package/engine/README.md +1 -1
  76. package/engine/Tiltfile +3 -3
  77. package/engine/assets/docker/wake-gateway/decide.ts +1 -1
  78. package/engine/assets/docker/wake-gateway/server.ts +1 -1
  79. package/engine/assets/scripts/mac-m1-resource-monitor.sh +8 -8
  80. package/engine/lifecycle/orchestrator.star +6 -6
  81. package/engine/paths.star +1 -1
  82. package/engine/schemas/project-schema.json +2 -2
  83. package/engine/schemas/service-schema.json +3 -3
  84. package/engine/scripts/debug-container.sh +2 -2
  85. package/engine/spec.master +1 -1
  86. package/engine/topologies/platform/docker/compose/compose.star +1 -1
  87. package/engine/topologies/platform/docker/compose/traefik_standalone.star +1 -1
  88. package/engine/topologies/platform/docker/config/healthcheck.star +1 -1
  89. package/engine/topologies/platform/docker/constants.star +2 -2
  90. package/engine/topologies/platform/docker/dockerfile/language_dockerfile.star +3 -3
  91. package/engine/topologies/platform/docker/layers/l1_base_layers.star +1 -1
  92. package/engine/topologies/platform/docker/layers/l2_dependency_layers.star +1 -1
  93. package/engine/topologies/platform/docker/layers/l3_builder_layers.star +1 -1
  94. package/engine/topologies/platform/docker/layers/l4_runtime_layers.star +1 -1
  95. package/engine/topologies/platform/docker/layers/prisma/constants.star +1 -1
  96. package/engine/topologies/platform/docker/layers/prisma/prisma_build.star +2 -2
  97. package/engine/topologies/platform/docker/networking/api_path_constants.star +1 -1
  98. package/engine/topologies/platform/docker/networking/traefik_static_routes.star +1 -1
  99. package/engine/topologies/platform/docker/secrets/env_secrets.star +1 -1
  100. package/engine/topologies/platform/docker/secrets/infisical.star +1 -1
  101. package/engine/topologies/platform/security/secrets.star +1 -1
  102. package/engine/topologies/tilt/common/utils.star +5 -3
  103. package/engine/topologies/tilt/common/utils_debug.star +1 -1
  104. package/engine/topologies/tilt/common/utils_deterministic.star +1 -1
  105. package/engine/topologies/tilt/common/utils_env.star +23 -3
  106. package/engine/topologies/tilt/config/profiles.star +1 -1
  107. package/engine/topologies/tilt/generators/infisical/organization.star +1 -1
  108. package/engine/topologies/tilt/generators/npmrc.star +4 -4
  109. package/engine/topologies/tilt/generators/typescript/backend_tsconfig.star +1 -1
  110. package/engine/topologies/tilt/generators/vite/helpers.star +1 -1
  111. package/engine/topologies/tilt/generators/vite/templates.star +1 -1
  112. package/engine/topologies/tilt/manifest/json_to_yaml.py +2 -2
  113. package/engine/topologies/tilt/manifest/loader.star +1 -1
  114. package/engine/topologies/tilt/manifest/schema.star +2 -2
  115. package/engine/topologies/tilt/manifest/tests/test_loader_parser.star +3 -3
  116. package/engine/topologies/tilt/manifest/tests/test_validator.star +2 -2
  117. package/engine/topologies/tilt/manifest/yaml_to_json.py +1 -1
  118. package/engine/topologies/tilt/resources/databases.star +1 -1
  119. package/engine/topologies/tilt/resources/infra-loader.star +8 -6
  120. package/engine/topologies/tilt/resources/libs_paths.star +1 -1
  121. package/engine/topologies/tilt/resources/libs_resource_definition.star +1 -1
  122. package/engine/topologies/tilt/resources/libs_setup.star +1 -1
  123. package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +13 -8
  124. package/engine/topologies/tilt/resources/orchestrator/generators/frontend.star +1 -1
  125. package/engine/topologies/tilt/resources/orchestrator/generators/manifest_resource.star +1 -1
  126. package/engine/topologies/tilt/resources/watchers.star +7 -7
  127. package/engine/where-things-live.star +1 -1
  128. package/examples/tdk-example/LICENSE +1 -1
  129. package/examples/tdk-example/Tiltfile +7 -7
  130. package/examples/tdk-example/services/shop/orders-api/service.json +1 -1
  131. package/examples/tdk-example/services/shop/orders-app/index.html +1 -1
  132. package/examples/tdk-example/services/shop/orders-app/service.json +1 -1
  133. package/examples/tdk-example/services/shop/orders-worker/service.json +1 -1
  134. package/ext/Tiltfile +3 -3
  135. package/ext/ide-components/code_viewer/server.py +1 -1
  136. package/ext/ide-components/config_inspector/server.py +1 -1
  137. package/ext/ide-components/file_browser/server.py +1 -1
  138. package/ext/ide-components/shared/config_service.py +2 -2
  139. package/ext/ide-components/shared/file_utils.py +2 -2
  140. package/ext/ide-components/shared/http_utils.py +1 -1
  141. package/ext/ide-components/shared/syntax_highlighter.py +1 -1
  142. package/ext/ide-components/shared/template_engine.py +1 -1
  143. package/ext/ide-components/shared/templates/base.html +1 -1
  144. package/ext/ide-components/shared/test_config_service.py +1 -1
  145. package/ext/ide-components/shared/tilt_integration.py +1 -1
  146. package/ext/ui-enhancements/tiltfile.py +1 -1
  147. package/package.json +6 -10
  148. package/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +1 -1
  149. package/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +1 -1
  150. package/shared-platform-engineering/docker-templates/install-deps.sh +1 -1
  151. package/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +1 -1
  152. package/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +1 -1
  153. package/cli/dist-bundle/chunk-QWYXYAX6.js +0 -510
  154. package/cli/dist-bundle/cold-preflight-MM46GFQA.js +0 -2
@@ -1,510 +0,0 @@
1
- import{createRequire as __tdkRequire}from'node:module';const require=__tdkRequire(import.meta.url);
2
- import{c as et,e as yn}from"./chunk-BZ4H7MM4.js";import{a as Dt}from"./chunk-6EA4DGCY.js";import{execFileSync as vi}from"node:child_process";import{readFileSync as gp}from"node:fs";import{join as kp}from"node:path";import{execFileSync as Od,execSync as gi}from"node:child_process";import{existsSync as V,readdirSync as Nd,readFileSync as Ae}from"node:fs";import{join as C,normalize as cn,relative as ln}from"node:path";function Sn(e,t,r){let n=e.indexOf(t);if(n===-1)return e;let s=t.length,o=0,i="";do i+=e.slice(o,n)+t+r,o=n+s,n=e.indexOf(t,o);while(n!==-1);return i+=e.slice(o),i}function bn(e,t,r,n){let s=0,o="";do{let i=e[n-1]==="\r";o+=e.slice(s,i?n-1:n)+t+(i?`\r
3
- `:`
4
- `)+r,s=n+1,n=e.indexOf(`
5
- `,s)}while(n!==-1);return o+=e.slice(s),o}var Tn=(e=0)=>t=>`\x1B[${t+e}m`,jt=(e=0)=>t=>`\x1B[${38+e};5;${t}m`,It=(e=0)=>(t,r,n)=>`\x1B[${38+e};2;${t};${r};${n}m`,Ri=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]}},xn=Object.keys(E.modifier),Ft=Object.keys(E.color),Lt=Object.keys(E.bgColor),Ai=Object.keys(E.underlineColor),wn=[...Ft,...Lt];function Oi(){let e=new Map;for(let[t,r]of Object.entries(E)){for(let[n,s]of Object.entries(r))E[n]={open:`\x1B[${s[0]}m`,close:`\x1B[${s[1]}m`},r[n]=E[n],e.set(Number.parseInt(s[0],10),s[1]);Object.defineProperty(E,t,{value:r,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=Tn(),E.color.ansi256=jt(),E.color.ansi16m=It(),E.bgColor.ansi=Tn(10),E.bgColor.ansi256=jt(10),E.bgColor.ansi16m=It(10),E.underlineColor.ansi=Ri,E.underlineColor.ansi256=jt(20),E.underlineColor.ansi16m=It(20),Object.defineProperties(E,{rgbToAnsi256:{value(t,r,n){return t===r&&r===n?t<8?16:t>248?231:Math.round((t-8)/247*24)+232:16+36*Math.round(t/255*5)+6*Math.round(r/255*5)+Math.round(n/255*5)},enumerable:!1},hexToRgb:{value(t){let r=/[\da-f]{6}|[\da-f]{3}/i.exec(t.toString(16));if(!r)return[0,0,0];let[n]=r;n.length===3&&(n=[...n].map(o=>o+o).join(""));let s=Number.parseInt(n,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 r,n,s;if(t>=232)r=((t-232)*10+8)/255,n=r,s=r;else{t-=16;let a=t%36;r=Math.floor(t/36)/5,n=Math.floor(a/6)/5,s=a%6/5}let o=Math.max(r,n,s)*2;if(o===0)return 30;let i=30+(Math.round(s)<<2|Math.round(n)<<1|Math.round(r));return o===2&&(i+=60),i},enumerable:!1},rgbToAnsi:{value:(t,r,n)=>E.ansi256ToAnsi(E.rgbToAnsi256(t,r,n)),enumerable:!1},hexToAnsi:{value:t=>E.ansi256ToAnsi(E.hexToAnsi256(t)),enumerable:!1}}),E}var Ni=Oi(),z=Ni;import Mt from"node:process";import Ci from"node:os";import En from"node:tty";function K(e,t=globalThis.Deno?globalThis.Deno.args:Mt.argv){let r=e.startsWith("-")?"":e.length===1?"-":"--",n=t.indexOf(r+e),s=t.indexOf("--");return n!==-1&&(s===-1||n<s)}var{env:v}=Mt,tt;K("no-color")||K("no-colors")||K("color=false")||K("color=never")?tt=0:(K("color")||K("colors")||K("color=true")||K("color=always"))&&(tt=1);function Pn(){return/^\d+$/.test(v.FORCE_COLOR)}function Di(){if("FORCE_COLOR"in v){if(v.FORCE_COLOR==="false")return 0;if(v.FORCE_COLOR==="true"||v.FORCE_COLOR.length===0)return 1;if(Pn())return Math.min(Number.parseInt(v.FORCE_COLOR,10),3)}}function ji(e){return e===0?!1:{level:e,hasBasic:!0,has256:e>=2,has16m:e>=3}}function Ii(e,{streamIsTTY:t,sniffFlags:r=!0}={}){let n=Di();n!==void 0&&(tt=n);let s=r?tt:n;if(s===0)return 0;if(r){if(K("color=16m")||K("color=full")||K("color=truecolor"))return 3;if(K("color=256"))return 2}if(s!==void 0&&Pn())return s;if("TF_BUILD"in v&&"AGENT_NAME"in v)return 1;if(e&&!t&&s===void 0)return 0;let o=s||0;if(v.TERM==="dumb")return o;if(Mt.platform==="win32"){let i=Ci.release().split(".");return Number(i[0])>=10&&Number(i[2])>=10586?Number(i[2])>=14931?3:2:1}if("CI"in v)return["GITHUB_ACTIONS","GITEA_ACTIONS","CIRCLECI"].some(i=>i in v)?3:["TRAVIS","APPVEYOR","GITLAB_CI","BUILDKITE","DRONE"].some(i=>i in v)||v.CI_NAME==="codeship"?1:o;if("TEAMCITY_VERSION"in v)return/^(?:9\.0*[1-9]\d*\.|\d{2,}\.)/.test(v.TEAMCITY_VERSION)?1:0;if(v.COLORTERM==="truecolor"||v.TERM==="xterm-kitty"||v.TERM==="xterm-ghostty"||v.TERM==="wezterm")return 3;if("TERM_PROGRAM"in v){let i=Number.parseInt((v.TERM_PROGRAM_VERSION||"").split(".",1)[0],10);switch(v.TERM_PROGRAM){case"iTerm.app":return i>=3?3:2;case"Apple_Terminal":return 2}}return/-256(?:color)?$/i.test(v.TERM)?2:/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(v.TERM)||"COLORTERM"in v?1:o}function vn(e,t={}){let r=Ii(e,{streamIsTTY:e&&e.isTTY,...t});return ji(r)}var Fi={stdout:vn({isTTY:En.isatty(1)}),stderr:vn({isTTY:En.isatty(2)})},$n=Fi;var{stdout:_n,stderr:Rn}=$n,Oe=Symbol("GENERATOR"),Ne=Symbol("STYLER"),nt=Symbol("IS_EMPTY"),rt=Symbol("LEVEL"),Ce=Object.create(null),An=e=>{if(!Number.isSafeInteger(e)||e<0||e>3)throw new Error("The `level` should be an integer from 0 to 3")},Li={enumerable:!0,get(){return this[rt]},set(e){An(e),this[rt]=e}},Mi=(e,t={})=>{t.level!==void 0&&An(t.level);let r=_n?_n.level:0;e[rt]=t.level===void 0?r:t.level};var Ui=e=>{let t=(...r)=>r.join(" ");return Mi(t,e),Object.setPrototypeOf(t,De.prototype),t};function De(e){return Ui(e)}Object.setPrototypeOf(De.prototype,Function.prototype);for(let[e,t]of Object.entries(z))Ce[e]={get(){let r=Bt(this,On(t.open,t.close,this[Ne]),this[nt]);return Object.defineProperty(this,e,{value:r}),r}};Ce.visible={get(){let e=Bt(this,this[Ne],!0);return Object.defineProperty(this,"visible",{value:e}),e}};var Bi=(e,t)=>{let r=z[t];if(e==="rgb"){let s=(i,a,c)=>r.ansi(z.rgbToAnsi(i,a,c));return[s,s,(i,a,c)=>r.ansi256(z.rgbToAnsi256(i,a,c)),r.ansi16m]}if(e==="hex"){let s=i=>r.ansi(z.hexToAnsi(i));return[s,s,i=>r.ansi256(z.hexToAnsi256(i)),i=>r.ansi16m(...z.hexToRgb(i))]}let n=s=>r.ansi(z.ansi256ToAnsi(s));return[n,n,r.ansi256,r.ansi256]},Hi=["rgb","hex","ansi256"];for(let e of Hi){let t=e[0].toUpperCase()+e.slice(1);for(let[r,n]of[[e,"color"],["bg"+t,"bgColor"],["underline"+t,"underlineColor"]]){let{close:s}=z[n],o=Bi(e,n);Ce[r]={get(){let i=function(a,c,l){let u=o[this.level](a,c,l);return Bt(this,On(u,s,this[Ne]),this[nt])};return Object.defineProperty(this,r,{value:i}),i}}}}var Gi=Object.defineProperties(()=>{},{...Ce,level:{enumerable:!0,get(){return this[Oe].level},set(e){this[Oe].level=e}}}),On=(e,t,r)=>{let n,s;return r===void 0?(n=e,s=t):(n=r.openAll+e,s=t+r.closeAll),{open:e,close:t,openAll:n,closeAll:s,parent:r}},Bt=(e,t,r)=>{let n=(...s)=>s.length===1?Ut(n,""+s[0]):s.length===2?Ut(n,`${s[0]??""} ${s[1]??""}`):Ut(n,s.join(" "));return Object.setPrototypeOf(n,Gi),n[Oe]=e[Oe]??e,n[Ne]=t,n[nt]=r,n},Ut=(e,t)=>{if(e[Oe][rt]<=0||!t)return e[nt]?"":t;let r=e[Ne];if(r===void 0)return t;let{openAll:n,closeAll:s}=r;if(t.includes("\x1B"))for(;r!==void 0;)t=Sn(t,r.close,r.open),r=r.parent;let o=t.indexOf(`
6
- `);return o!==-1&&(t=bn(t,s,n,o)),n+t+s};Object.defineProperties(De.prototype,{...Ce,level:Li});var Vi=De(),Cp=De({level:Rn?Rn.level:0});var f=Vi;import{Command as Cd}from"commander";import*as T from"node:fs";import*as h from"node:path";import{fileURLToPath as Wc}from"node:url";import fe from"handlebars";var F="Container Runtime",Y="bring-your-own";var L="database-management",X=".tiltignore",Nn="service.json",de="docker-compose.yml",I=".autogenerated",st="TILT_TECH_STACK.star",ot="TILT_RESOURCE_DEFAULTS.star",A="project.json",O="package.json",Ht=[st,ot,"spec.master"],Ip=["backend","frontend","library","sdk","worker","migrator","mcp","agent",Y],Gt={backend:["dev","build","start"],worker:["dev","build","start"],migrator:["dev","build","start"],mcp:["dev","build","start"],agent:["dev","build","start"],frontend:["dev","build"],library:["dev","build"],sdk:["dev","build"],[Y]:[]};var Vt=["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"},agent:{base:4e3,min:4e3,max:5999,range:"4000-5999"},[Y]:{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"}},je={tiltUi:10350,traefik:8080},Wt="https://tdk-landscape.github.io/tdk-website/docs/quickstart/";var Cn=["node_modules",".git",".tilt",".tilt-engine","dist","build",".prisma",".turbo","coverage","tmp","temp","__tests__","test","tests",".github",".vscode","docs"],Dn=["Dockerfile",de,"docker-compose.yaml","Tiltfile","vite.config.ts","tsconfig.json","tsconfig.build.json"],Wi={infra:"\u{1F3D7}\uFE0F",core:"\u{1F527}",api:"\u26A1",app:"\u{1F3A8}",orchestrator:"\u2699\uFE0F"};function Fp(e){let t=e.toLowerCase();for(let[r,n]of Object.entries(Wi))if(t.includes(r))return n;return"\u{1F4E6}"}var Ki="1.0.0",Ji={runtime:"bun",bundler:"vite",language:"typescript",framework:"hono",database:"postgresql",orm:"prisma",messaging:"nats",linting:"biome",testing:"vitest"},qi={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:je.tiltUi,traefik:je.traefik},zi={path:"/health",live:"/health/live",ready:"/health/ready",timeout:30,interval:5},Yi={frontend:"-frontend",backend:"-backend",worker:"-worker",migrator:"-migrator",library:"-lib",sdk:"-sdk",validSeparators:["-","_"],maxLength:63,minLength:3},Xi={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},Qi=["node_modules","dist","build",".git",".prisma",".turbo","coverage","tmp","temp","__tests__","test","tests"],Zi={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"}},ea={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"},ta={services:"services/product",sharedPlatform:"shared-platform-engineering",sharedProduct:"shared-product-engineering",sharedDdd:"shared-ddd-layers"},ra={scanIntervalSeconds:5,maxManifestsPerRoot:50,servicePatterns:["services/product/*","services/platform/*"]},na={dockerfile:"Dockerfile",context:"."},sa={backend:{command:"bun",args:["run","dev"],env:{NODE_ENV:"development"}},frontend:{command:"bun",args:["run","dev"],env:{NODE_ENV:"development"}}},H={version:Ki,tech:Ji,ports:qi,health:zi,naming:Yi,traefik:Xi,filewatchIgnores:Qi,serviceTypes:Zi,features:ea,paths:ta,discovery:ra,docker:na,runtime:sa};import{randomBytes as oa}from"node:crypto";import{closeSync as ia,fchmodSync as aa,fsyncSync as ca,openSync as la,renameSync as ua,rmSync as da,statSync as pa,writeFileSync as fa}from"node:fs";import{basename as ma,dirname as ha,join as ga}from"node:path";function Ie(e,t){let r=ga(ha(e),`.${ma(e)}.${process.pid}.${oa(4).toString("hex")}.tmp`),n;try{n=pa(e).mode&511}catch{n=void 0}try{let s=la(r,"wx");try{n!==void 0&&aa(s,n),fa(s,t,"utf-8"),ca(s)}finally{ia(s)}ua(r,e)}catch(s){throw da(r,{force:!0}),s}}import{appendFileSync as ka,existsSync as Kt,mkdirSync as ya,readFileSync as Sa,writeFileSync as jn}from"node:fs";import{dirname as ba,resolve as ke}from"node:path";function qp(e,t,r){for(let[n,s]of t.entries())console.log(f.blue(`${s.emoji} ${s.description}...`)),s.type==="json"?xa(e,s.filename,s.content):wa(e,s.filename,String(s.content)),r&&r(s,n,t.length)}function Ta(e,t,r=2){let n=`${JSON.stringify(t,null,r)}
7
- `;jn(e,n,"utf-8")}function xa(e,t,r,n=2){let s=ke(e,t);Ta(s,r,n)}function Jt(e,t){jn(e,t,"utf-8")}function wa(e,t,r){let n=ke(e,t);Jt(n,r)}function zp(e){Kt(e)||ya(e,{recursive:!0})}var Ea=[".env",".tdk/.tdk-out/",".tdk/.project-id",".tdk/smoke/","node_modules/"];function va(e){let t=ke(e,".gitignore"),r=Kt(t)?Sa(t,"utf-8"):"",n=new Set(r.split(`
8
- `).map(a=>a.trim().replace(/\/$/,""))),s=Ea.filter(a=>!n.has(a.replace(/\/$/,"")));if(s.length===0)return[];let o=r&&!r.endsWith(`
9
- `)?`
10
- `:"";return ka(t,`${o}${r?`
11
- # Flisk (generated files and secrets)
12
- `:`# Flisk (generated files and secrets)
13
- `}${s.join(`
14
- `)}
15
- `),s}function Pa(e){let t=ke(e);for(;;){if(Kt(ke(t,".git")))return!0;let r=ba(t);if(r===t)return!1;t=r}}function Yp(e){if(Pa(e))try{va(e);return}catch(t){let r=t instanceof Error?t.message:String(t);return`Could not add .tdk/smoke/ to ${ke(e,".gitignore")}: ${r}. Git may track the smoke response records.`}}import{existsSync as $a,lstatSync as _a}from"node:fs";import{isAbsolute as In,normalize as it,relative as Ra,sep as Fn}from"node:path";var tf=[`${I}/**`,".tdk/.tdk-out/**","Tiltfile","TILT_*.star",X,`${O} (create only)`,"shared-platform-engineering/docker-templates/**","services/platform/database-management/docker-compose.yml","product_autogenerated_resource_manifests/**","product_autogenerated_services_list/**"];function Aa(e){let t=it(e).split(Fn).join("/");return In(e)||t===".."||t.startsWith("../")?!1:t.split("/").includes(I)||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===X||t===O||t==="services/platform/database-management/docker-compose.yml"}function pe(e,t){let r=In(t)?it(t):it(`${e}/${t}`),n=Ra(e,r).split(Fn).join("/");if(!Aa(n))throw new Error(`Refusing to write outside Flisk's generated-file allowlist: ${n}`);let s=e;for(let o of n.split("/"))if(s=it(`${s}/${o}`),$a(s)&&_a(s).isSymbolicLink())throw new Error(`Refusing to write through a symbolic link in generated path: ${n}`);return r}var Fe=[L,"proxy"];import{existsSync as ps,readFileSync as fs}from"node:fs";import{copyFileSync as Xn,mkdirSync as Ya,rmSync as Qn,writeFileSync as Zn}from"node:fs";import{join as xe}from"node:path";function M(e){return e==="backend"||e==="mcp"||e==="agent"}import{existsSync as ja,readFileSync as Ia}from"node:fs";import{join as Fa}from"node:path";import{randomBytes as Ln}from"node:crypto";import{existsSync as zt,readFileSync as Mn,writeFileSync as Un}from"node:fs";import{join as Yt}from"node:path";var ye=[{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}],Bn=()=>Ln(16).toString("hex"),Oa=()=>Ln(32).toString("hex");function qt(e,t){return e==="DB_PASSWORD"?t:e==="JWT_SECRET"?Oa():e==="DATABASE_URL"?`postgresql://postgres:${t}@postgres:5432/app_dev`:ye.find(r=>r.name===e)?.default??""}function Na(e){let t=e.trim(),r=t[0];if(r==='"'||r==="'"){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===r){let a=t.slice(1,o);if(r==='"'){let c={n:`
16
- `,r:"\r",t:" ",'"':'"',"\\":"\\",$:"$"};return a.replace(/\\([nrt"\\$])/g,(l,u)=>c[u]??u)}return a.replace(/\\'/g,"'")}}return t}let n=e.search(/\s#/);return(n===-1?e:e.slice(0,n)).trim()}function Se(e){let t=new Map,r=e.startsWith("\uFEFF")?e.slice(1):e;for(let n of r.split(/\r?\n/)){let s=n.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,Na(o.slice(i+1)))}return t}function Hn(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 Gn(){let e=Bn(),t=["# \u{1F527} Flisk Environment Configuration","# Generated by `flisk 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: flisk config regenerate && flisk up",""];for(let r of ye)t.push(...Hn(r)),t.push(`${r.name}=${qt(r.name,e)}`),t.push("");return t.join(`
17
- `)}function Ca(e){let t=e.match(/^[a-z][a-z0-9+.-]*:\/\/[^:/@\s]+:([^@\s]*)@/i);if(!(!t||t[1].includes("$")))return t[1]}function Vn(e){let t=Yt(e,".env"),r={missing:[],invalid:[],warnings:[]};if(!zt(t))return{missing:ye.filter(i=>i.required).map(i=>i.name),invalid:[],warnings:[".env file not found - will be auto-generated"]};let n=Se(Mn(t,"utf-8"));for(let i of ye)i.required&&!n.has(i.name)&&r.missing.push(i.name),n.has(i.name)&&!(n.get(i.name)??"").trim()&&r.invalid.push(`${i.name} is set but empty`);let s=n.get("DATABASE_URL"),o=n.get("DB_PASSWORD");if(s&&o){let i=Ca(s);i!==void 0&&i!==o&&r.warnings.push("DATABASE_URL carries a different password than DB_PASSWORD; the database container uses DB_PASSWORD")}return r}function Da(e,t,r){if(!Se(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)+r+a}function df(e){let t=Yt(e,".env");if(!zt(t))return Un(t,Gn(),{mode:384}),ye.map(d=>d.name);let r=Mn(t,"utf-8"),n=Se(r),s=ye.filter(d=>d.complete?n.has(d.name)?!(n.get(d.name)??"").trim():!0:!1);if(s.length===0)return[];let o=s.filter(d=>!n.has(d.name)),i=s.filter(d=>n.has(d.name)),a=n.get("DB_PASSWORD"),c=a?.trim()?a:Bn(),l=r.split(/(\r\n|\n|\r)/);for(let d of i){let p=-1;for(let g=0;g<l.length;g+=2)Se(l[g]).has(d.name)&&(p=g);if(p<0)throw new Error(`Could not locate empty .env assignment for ${d.name}`);l[p]=Da(l[p],d.name,qt(d.name,c))}let u=l.join("");if(o.length>0){let d=["","# Added by Flisk: keys this version expects. Existing values above were not changed.",""];for(let g of o)d.push(...Hn(g)),d.push(`${g.name}=${qt(g.name,c)}`),d.push("");let p=u.endsWith(`
18
- `)||u.endsWith("\r")?"":`
19
- `;u+=p+d.join(`
20
- `)}return Ie(t,u),s.map(d=>d.name)}function pf(e){let t=Yt(e,".env");return zt(t)?!1:(Un(t,Gn(),{mode:384}),!0)}var La="[REDACTED]",Le=class extends Error{path;constructor(t,r){super(`Could not read ${t}: ${r}`),this.path=t,this.name="EnvUnreadableError"}},Ma=8;function ct(e){let t=Fa(e,".env");if(!ja(t))return[];let r;try{r=Ia(t,"utf-8")}catch(s){throw new Le(t,s instanceof Error?s.message:String(s))}let n=[...Se(r).values()].filter(s=>s.length>=Ma);return[...new Set(n)].sort((s,o)=>o.length-s.length)}function be(e,t){let r=e;for(let n of t)r=r.split(n).join(La);return r}function at(e,t){return t.length===0?e:typeof e=="string"?be(e,t):Array.isArray(e)?e.map(r=>at(r,t)):e!==null&&typeof e=="object"?Object.fromEntries(Object.entries(e).map(([r,n])=>[r,at(n,t)])):e}function Wn(e){let t;try{t=ct(e)}catch(r){return()=>{throw r}}return r=>be(r,t)}import{readFileSync as Ka}from"node:fs";import{join as Ja}from"node:path";import{existsSync as Ba}from"node:fs";import{dirname as Ha,join as Ga,resolve as Jn}from"node:path";import{cwd as Va}from"node:process";var Kn={name:"tdk-cli-internal",private:!0,version:"1.3.189",description:"Flisk CLI \u2014 start services on your machine.",keywords:["flisk","tilt","cli","local-dev","microservices"],homepage:"https://github.com/flisk-sh/flisk#readme",bugs:{url:"https://github.com/flisk-sh/flisk/issues"},repository:{type:"git",url:"git+https://github.com/flisk-sh/flisk.git",directory:"cli"},license:"MIT",author:"Flisk Team",type:"module",main:"./dist/index.js",types:"./dist/index.d.ts",bin:{flisk:"bin/flisk.js",tdk:"bin/flisk.js","tdk-cli":"bin/flisk.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","check:dist":"npm run build && ../scripts/check-cli-dist.sh",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","test:coverage":"vitest run --coverage --coverage.reportOnFailure --coverage.reporter=text --coverage.reporter=html --coverage.reporter=json-summary --coverage.reporter=json --coverage.reportsDirectory=coverage --reporter=default --reporter=json --outputFile.json=test-results/vitest.json",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","@vitest/coverage-v8":"^5.0.2",ajv:"^8.17.1",chalk:"^6.0.1",esbuild:"^0.28.2",ink:"^8.0.0",knip:"^6.38.0",react:"^19.3.0","react-devtools-core":"^8.0.0",typescript:"^7.0.2",vite:"^8.3.3",vitest:"^5.0.0"},engines:{node:">=22.12.0"}};function k(e=Va()){let t=Jn(e),r=Jn("/");for(;t!==r;){if(Ba(Ga(t,".tdk",A)))return t;let n=Ha(t);if(n===t)break;t=n}return null}var lt=null;function Wa(){if(lt)return lt;let e=Kn;if(!e||typeof e!="object"||Array.isArray(e))throw new Error("Invalid package.json: expected object");let t=e;return lt={name:String(t.name??"flisk"),version:String(t.version??"0.0.0"),fullPackage:t},lt}function qn(){return Wa().version}function Te(){let e=k();if(e)try{let t=Ka(Ja(e,".tdk",A),"utf-8"),r=JSON.parse(t);if(r?.project?.name)return r.project.name}catch{}return"tdk-project"}function Xt(e){let t=Te(),r=process.env.TDK_SERVICE_BASE_URL??`http://${t}.localhost${e?`:${e}`:""}`;try{let n=new URL(r.includes("://")?r:`http://${r}`),s=n.hostname,o=n.protocol==="https:"?"443":"80",i=n.port||(e?String(e):""),a=i===o?"":i;if(s==="localhost"||/^\d+\.\d+\.\d+\.\d+$/.test(s))return{appBase:`${n.protocol}//${s}${a?`:${a}`:""}`,apiBase:`${n.protocol}//${s}${a?`:${a}`:""}`};let c=s.replace(/^(app|api)\./,"");return{appBase:`${n.protocol}//app.${c}${a?`:${a}`:""}`,apiBase:`${n.protocol}//api.${c}${a?`:${a}`:""}`}}catch{return{appBase:`http://app.${t}.localhost`,apiBase:`http://api.${t}.localhost`}}}function qa(e){return`${e.replace(/\/+$/,"")}/health`}function Qt(e){if(M(e.config?.appType)){let t=e.name.replace(/-api$/,"");return e.config?.apiPath??`/api/${t}`}return e.config?.basePath??`/${e.name}`}function Zt(e,t){return Yn(e,t,r=>qa(r))}function zn(e,t){return Yn(e,t,r=>r)}function Yn(e,t,r){let{appBase:n,apiBase:s}=Xt(t);return e.filter(o=>o.config?.appType==="frontend"||M(o.config?.appType)).map(o=>{let i=M(o.config.appType)?s:n;return{name:o.name,appType:o.config.appType,url:`${i}${r(Qt(o))}`}})}async function za(e,t){try{let r=await fetch(e.url,{method:"GET",redirect:"manual",signal:AbortSignal.timeout(t)});return{...e,ok:r.ok,status:r.status}}catch(r){let n=r instanceof Error&&r.name==="TimeoutError"?`no response within ${t}ms`:r instanceof Error?r.message:String(r);return{...e,ok:!1,error:n}}}async function ut(e,t){return Promise.all(e.map(r=>za(r,t)))}var os=60,es=1e3,ts=1e4,rs=200,ns=64*1024,ss=new Set([404,502,503,504]),Xa=new Set(["ECONNREFUSED","ENOTFOUND","EAI_AGAIN","EHOSTUNREACH","ENETUNREACH"]),Qa=new Set(["GET","HEAD","OPTIONS"]);function se(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Za(e,t){if(Qa.has(t))return!0;if(!se(e))return!1;let r=se(e.cause)?e.cause:void 0,n=e.code??r?.code;return typeof n=="string"&&Xa.has(n)}function tr(e){if(!se(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((r,n)=>{let s=`smoke.steps[${n}]`;if(!se(r)){t.push(`${s}: must be an object`);return}let o=r.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`),r.expect!==void 0&&!(typeof r.expect=="number"&&Number.isInteger(r.expect)&&r.expect>=100&&r.expect<=599)&&t.push(`${s}.expect: must be an HTTP status between 100 and 599`),r.bodyContains!==void 0&&typeof r.bodyContains!="string"&&t.push(`${s}.bodyContains: must be a string`),r.save!==void 0&&(se(r.save)&&Object.values(r.save).every(a=>typeof a=="string"&&a.startsWith("$."))||t.push(`${s}.save: must map names to paths such as "$.id"`))}),t)}function is(e,t){let{appBase:r,apiBase:n}=Xt(t),s=[];for(let o of e){let i=o.config;if(!i?.smoke)continue;let a=i.appType,c=M(a)||a===Y;if(!c&&a!=="frontend"||i.exposeViaProxy===!1)continue;let l=c?n:r,u=i.healthCheckPath||(c&&a!==Y?"/health":void 0);s.push({name:o.name,baseUrl:`${l}${Qt(o).replace(/\/+$/,"")}`,...u?.startsWith("/")?{readyPath:u}:{},smoke:i.smoke})}return s}var ec=10;function as(e,t={}){let r=t.timeoutSeconds??ec;return e.flatMap(n=>{let s=n.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(n.smoke.timeoutSeconds??os,r);return[{name:n.name,baseUrl:n.baseUrl,smoke:{via:n.smoke.via,timeoutSeconds:o,steps:s}}]})}function tc(e,t){let r=t.replace(/^\$\.?/,"").match(/[^.[\]]+/g)??[],n=e;for(let s of r){if(n==null||s==="__proto__"||s==="constructor"||s==="prototype"||!Object.hasOwn(n,s))return;let o=Object.getOwnPropertyDescriptor(n,s);if(!o||!("value"in o))return;n=o.value}return n}function cs(e,t){return e.replace(/\{\{(\w+)\}\}/g,(r,n)=>t[n]??r)}function er(e,t){return typeof e=="string"?cs(e,t):Array.isArray(e)?e.map(r=>er(r,t)):se(e)?Object.fromEntries(Object.entries(e).map(([r,n])=>[r,er(n,t)])):e}function dt(e){let t=e.replace(/\s+/g," ").trim();return t.length>rs?`${t.slice(0,rs)}...`:t}function ls(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"step"}function rc(e,t){let r=ls(t),n=(e.get(r)??0)+1;return e.set(r,n),n===1?r:`${r}-${n}`}function nc(e,t,r,n,s){try{let o=xe(e,ls(r.service),t);Ya(o,{recursive:!0});let i=xe(o,"latest.json"),a=xe(o,"body.txt");return n===void 0?Qn(a,{force:!0}):Zn(a,n),Zn(i,`${JSON.stringify({...r,bodyPath:n===void 0?null:"body.txt"},null,2)}
21
- `),s&&(Xn(i,xe(o,"last-success.json")),n===void 0?Qn(xe(o,"last-success-body.txt"),{force:!0}):Xn(a,xe(o,"last-success-body.txt"))),i}catch{return}}function sc(e){let t=Buffer.from(e,"utf8");if(t.length<=ns)return{body:e,truncated:!1,bytes:t.length};let r=t.subarray(0,ns).toString("utf8").replace(/\uFFFD$/,"");return{body:r,truncated:!0,bytes:Buffer.byteLength(r,"utf8")}}async function oc(e,t={}){let r=t.fetch??((u,d)=>fetch(u,d)),n=t.now??Date.now,s=t.sleep??(u=>new Promise(d=>setTimeout(d,u))),o=(e.smoke.timeoutSeconds??os)*1e3,i=n()+o,a={},c=tr(e.smoke);if(c.length>0)return{name:e.name,ok:!1,failure:`${e.name}: invalid smoke block: ${c.join("; ")}`};if(e.readyPath){let u=`${e.baseUrl}${e.readyPath}`;for(;;){try{let d=await r(u,{method:"GET",signal:AbortSignal.timeout(ts)});if(await d.text(),!ss.has(d.status)&&d.status<500)break}catch{}if(n()>=i)break;await s(es)}}i=n()+o;let l=new Map;for(let[u,d]of e.smoke.steps.entries()){let p=d.name??`step ${u+1}`,g=(d.method??"GET").toUpperCase(),y=`${e.baseUrl}${cs(d.path,a)}`,S=d.expect??200,m=d.body!==void 0,$,W="",U,he;for(;;){U=void 0,he=void 0,$=void 0;let B=!1;try{let j=await r(y,{method:g,headers:m?{"content-type":"application/json"}:void 0,body:m?JSON.stringify(er(d.body,a)):void 0,signal:AbortSignal.timeout(ts)});$=j.status,W=await j.text()}catch(j){B=Za(j,g),U=j instanceof Error?j.message:String(j);let ue=se(j)?j.code??(se(j.cause)?j.cause.code:void 0):void 0;he=typeof ue=="string"?ue:void 0}if(!(U!==void 0&&B||$!==void 0&&$!==S&&ss.has($))||n()>=i)break;await s(es)}let b=!1,R=B=>{if(!b){if(!t.redact)return B;try{return t.redact(B)}catch(re){b=!0;let j=re instanceof Error?re.message:String(re);t.warn?.(`${e.name}: ${p}: the response text was neither shown nor stored: ${j}`);return}}},le=R(y),P=$===void 0?void 0:R(W),Qe=U===void 0?void 0:R(U),Ze=U===void 0?void 0:he??Qe,q=`${e.name}: ${p}: ${g} ${le??"(url withheld)"}`,ge;if(t.recordDir){let B=P===void 0?void 0:sc(P),re=U===void 0&&$===S&&(d.bodyContains===void 0||W.includes(d.bodyContains));ge=nc(t.recordDir,rc(l,p),{service:e.name,step:p,method:g,...le===void 0?{}:{url:le},expectedStatus:S,status:$??null,...Ze===void 0?{}:{error:Ze},truncated:B?.truncated??!1,bodyBytes:B?.bytes??0},B?.body,re)}let te=B=>({name:e.name,ok:!1,failure:B,recordPath:ge});if(U!==void 0)return te(`${q} never answered (${Qe??"the error was withheld"})`);if($!==S)return te(`${q} returned ${$}, expected ${S}${P?`: ${dt(P)}`:""}`);if(d.bodyContains!==void 0&&!W.includes(d.bodyContains))return te(`${q} returned ${$} but the body does not contain "${d.bodyContains}"${P?`: ${dt(P)}`:""}`);if(d.save){let B;try{B=JSON.parse(W)}catch{return te(`${q} returned a body that is not JSON, cannot save${P?`: ${dt(P)}`:""}`)}for(let[re,j]of Object.entries(d.save)){let ue=tc(B,j);if(ue==null||typeof ue=="object")return te(`${q} response has no ${j} to save as {{${re}}}${P?`: ${dt(P)}`:""}`);a[re]=String(ue)}}}return{name:e.name,ok:!0}}function Ff(e){return`Smoke check failed: ${e.failure}${e.recordPath?` (record: ${e.recordPath})`:""}`}function us(e,t){return{redact:Wn(e),...t?{warn:t}:{}}}async function ds(e,t={}){return Promise.all(e.map(r=>oc(r,t)))}var Me=1,Bf="https://tdk-landscape.github.io/schema.service.json",ic={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"},ac=new Set(["$schema","agent","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"]),cc=[{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 pt(e,t){let r=[],n=new Set;for(let{path:s,pattern:o,expected:i}of cc){let a=e;for(let l of s)a=a&&typeof a=="object"&&!Array.isArray(a)?a[l]:void 0;if(a==null||a==="")continue;let c=`${t}.${s.join(".")}`;(typeof a!="string"||!o.test(a))&&n.add(c),typeof a!="string"?r.push(`${c}: expected a string`):o.test(a)||r.push(`${c}: must be ${i} (no spaces, line breaks or YAML syntax)`)}for(let[s,o]of lc(e,t))n.has(s)||r.push(`${s}: ${o}`);return r}function rr(e){for(let t=0;t<e.length;t++){let r=e.charCodeAt(t);if(r<32||r===127||r===133||r===8232||r===8233)return!0}return!1}function lc(e,t){let r=[],n=Object.entries(e).filter(([s])=>s!=="smoke").map(([s,o])=>[o,`${t}.${s}`]);for(let s of Object.keys(e))rr(s)&&r.push([t,"a key contains a line break"]);for(;n.length>0;){let[s,o]=n.pop();if(typeof s=="string")rr(s)&&r.push([o,"must not contain line breaks or control characters"]);else if(Array.isArray(s))for(let[i,a]of s.entries())n.push([a,`${o}[${i}]`]);else if(s&&typeof s=="object")for(let[i,a]of Object.entries(s))rr(i)?r.push([o,"a key contains a line break"]):n.push([a,`${o}.${i}`])}return r.sort(([s],[o])=>s.localeCompare(o))}var uc=["apiPath","basePath"];function nr(e){if(!e||typeof e!="object"||Array.isArray(e))return[];let t=e;return uc.filter(r=>r in t&&typeof t[r]!="string")}function sr(e,t){if(!e||typeof e!="object"||Array.isArray(e))return{errors:[`${t}: expected a JSON object`],warnings:[]};let r=e,n=[];for(let a of["appName","appType","stack","schemaVersion"])a in r||n.push(`${t}.${a}: required field is missing`);for(let a of["appName","appType","stack"])a in r&&typeof r[a]!="string"&&n.push(`${t}.${a}: expected a string`);"schemaVersion"in r&&r.schemaVersion!==Me&&n.push(`${t}.schemaVersion: unsupported version ${String(r.schemaVersion)} (supported: ${Me})`);for(let a of nr(r))n.push(`${t}.${a}: expected a string`);let s=r.dev,o=s&&typeof s=="object"&&!Array.isArray(s)?s.liveReload:void 0;if(o!==void 0&&typeof o!="boolean"&&n.push(`${t}.dev.liveReload: expected true or false`),n.push(...pt(r,t)),"smoke"in r)for(let a of tr(r.smoke))n.push(`${t}.${a}`);let i=Object.keys(r).flatMap(a=>{let c=ic[a];return c?[`${t}.${a}: ${c}`]:ac.has(a)?[]:[`${t}.${a}: unknown field is preserved`]});return o===!0&&r.language!=="go"&&i.push(`${t}.dev.liveReload: only applies to Go services (language "go"); it is ignored here`),{errors:n,warnings:i,manifest:r}}function ms(e,t){if(!ps(e))return[];try{let r=JSON.parse(fs(e,"utf-8"));return!r||typeof r!="object"||Array.isArray(r)?[]:pt(r,t)}catch{return[]}}function hs(e,t){if(!ps(e))return{errors:[`${t}: file is missing`],warnings:[]};try{let r=JSON.parse(fs(e,"utf-8"));return sr(r,t)}catch(r){let n=r instanceof Error?r.message:String(r);return{errors:[`${t}: invalid JSON (${n})`],warnings:[]}}}import Cs,{existsSync as Ee,readdirSync as Nc,readFileSync as Cc,statSync as mr}from"node:fs";import{dirname as Dc,join as ie}from"node:path";var Ue=class{data;options;constructor(t){this.data=new Map,this.options=t}has(t){let r=this.data.get(t);return r?Date.now()-r.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,r){this.data.set(t,{value:r,timestamp:Date.now()})}delete(t){this.data.delete(t)}clear(){this.data.clear()}keys(){let t=[];for(let r of this.data.keys())this.has(r)&&t.push(r);return t}size(){return this.keys().length}};function gs(e){let t=0;return{isValid(){return Date.now()-t<e},markUpdated(){t=Date.now()},reset(){t=0}}}import{execFileSync as hc,execSync as gc}from"node:child_process";import{existsSync as Ss}from"node:fs";import{delimiter as bs,extname as Ts,join as xs}from"node:path";import{delimiter as Wf,extname as dc}from"node:path";function we(){return process.platform==="win32"}function pc(e){return e==="linux"?"linux":e==="darwin"?"darwin":e==="win32"?"windows":null}function fc(e){return e==="x64"?"amd64":e==="arm64"?"arm64":null}function mc(e,t){let r=pc(e),n=fc(t);if(!r||!n||r==="windows"&&n!=="amd64")return null;let s=`flisk-${r}-${n}`;return r==="windows"?`${s}.exe`:s}function Kf(){return mc(process.platform,process.arch)}function ks(){return we()?"flisk.exe":"flisk"}function ys(){return we()?"tdk.exe":"tdk"}function or(e,t=we()){return t?dc(e)?[e]:[e,`${e}.exe`,`${e}.cmd`,`${e}.bat`]:[e]}function N(e){let t=process.env.PATH??process.env.Path??"",r=we(),n=or(e,r);if(r&&!Ts(e))for(let s of(process.env.PATHEXT??".EXE;.CMD;.BAT;.COM").split(";")){let o=s.toLowerCase();o&&!n.includes(`${e}${o}`)&&n.push(`${e}${o}`)}for(let s of t.split(bs))if(s)for(let o of n){let i=xs(s,o);if(Ss(i))return i}return null}function ir(e){let t=process.env.PATH??process.env.Path??"",r=we(),n=or(e,r);if(r&&!Ts(e))for(let i of(process.env.PATHEXT??".EXE;.CMD;.BAT;.COM").split(";")){let a=i.toLowerCase();a&&!n.includes(`${e}${a}`)&&n.push(`${e}${a}`)}let s=[],o=new Set;for(let i of t.split(bs))if(i)for(let a of n){let c=xs(i,a);if(!Ss(c))continue;let l=r?c.toLowerCase():c;o.has(l)||(o.add(l),s.push(c))}return s}var ws=1e4;function Es(){let e=!1;if(process.platform==="win32")try{return hc(N("docker")??"docker",["ps"],{stdio:"ignore",timeout:ws,windowsHide:!0}),"running"}catch(t){return t.code==="ETIMEDOUT"?"unresponsive":"missing"}for(let t of["docker ps","colima status","podman ps"])try{return gc(t,{stdio:"ignore",timeout:ws}),"running"}catch(r){r.code==="ETIMEDOUT"&&(e=!0)}return e?"unresponsive":"missing"}function ft(e,t){return e[t]??0}function kc(e,t){let r=Array.from({length:t.length+1},(n,s)=>s);for(let n=1;n<=e.length;n++){let s=[n];for(let o=1;o<=t.length;o++){let i=e[n-1]===t[o-1]?0:1;s[o]=Math.min(ft(s,o-1)+1,ft(r,o)+1,ft(r,o-1)+i)}r=s}return ft(r,t.length)}function yc(e,t){if(e.length!==t.length)return!1;for(let r=0;r<e.length-1;r++)if(e[r]!==t[r]&&e[r]===t[r+1]&&e[r+1]===t[r]&&e.slice(0,r)===t.slice(0,r)&&e.slice(r+2)===t.slice(r+2))return!0;return!1}function vs(e,t){if(e.length<2||t.length===0||t.includes(e))return;let r=e.toLowerCase(),n,s=Number.POSITIVE_INFINITY;for(let o of t){if(o.length===0)continue;let i=o.toLowerCase(),a=yc(r,i)?1:kc(r,i),c=Math.max(1,Math.min(2,Math.floor(Math.max(r.length,i.length)/3)));a>c||(a<s||a===s&&(n===void 0||o<n))&&(n=o,s=a)}return n}import{spawn as Sc}from"node:child_process";import{join as bc}from"node:path";function Tc(e,t=[],r={}){return new Promise((n,s)=>{let o=[e,...t];r.verbose&&console.log(`Executing: tilt ${o.join(" ")}`);let i=Sc(N("tilt")??"tilt",o,{stdio:r.inheritStdio?"inherit":"pipe",shell:!1,windowsHide:process.platform==="win32",timeout:r.timeoutMs}),a="",c="";r.inheritStdio||(i.stdout?.on("data",l=>{a+=l.toString()}),i.stderr?.on("data",l=>{c+=l.toString()})),i.on("close",l=>{n({exitCode:l??1,stdout:a,stderr:c})}),i.on("error",l=>{s(new Error(`Failed to spawn tilt: ${l.message}`))})})}async function Ps(){try{return(await Tc("version",[],{inheritStdio:!1,timeoutMs:1e4})).exitCode===0}catch{return!1}}function xc(){let e=k();if(!e)throw ne.notInProject();return bc(e,".tdk",".tdk-out","Tiltfile")}function $s(e){let t=xc();e.push("-f",t)}function am(e,t={}){let r=[];return $s(r),t.focusTargets&&t.focusTargets.length>0&&r.push("--",`--focus=${t.focusTargets.join(",")}`),r.push(...e),t.verbose&&!t.quiet&&r.push("--verbose"),t.watch&&r.push("--watch"),r}function cm(e={}){let t=[];return $s(t),e.focusTargets&&e.focusTargets.length>0&&t.push("--",`--focus=${e.focusTargets.join(",")}`),t}function _s(e){return e instanceof Error?e.message:String(e)}function gm(e,t){if(process.env.TDK_VERBOSE){let r=t!==void 0?`: ${_s(t)}`:"";console.warn(f.gray(`${e}${r}`))}}var D=class extends Error{suggestions;exitCode;constructor(t,r=[],n=1){super(t),this.name="TdkError",this.suggestions=r,this.exitCode=n}display(){lr(this.message,void 0,this.suggestions)}exit(){this.display(),process.exit(this.exitCode)}},cr=class extends Error{code;constructor(t=1){super(`process.exit(${t})`),this.name="ProcessExitError",this.code=t}};function ar(e,t,r=!1){let n=new Set;for(let s of e){let o=vs(s,t);o&&n.add(r?`Did you mean "${o}" for "${s}"?`:`Did you mean "${o}"?`)}return[...n]}var ne={tiltNotInstalled:()=>new D("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: ${Wt}`]),dockerNotAvailable:()=>new D("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: ${Wt}`]),dockerNotResponding:()=>new D("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 D(`Stack "${e}" not found`,[...ar([e],t),"Run `flisk stacks` to see available stacks","Run `flisk stack` to assign resources to a stack"]),resourceNotFound:(e,t=[])=>new D(`Resource "${e}" not found`,[...ar([e],t),"Run `flisk resources` to list all resources","Check the resource name spelling"]),unknownServices:(e,t)=>{let r=ar(e,t,e.length>1);return new D(`Unknown service ${e.join(", ")}. Valid names: ${t.join(", ")}`,r,2)},directoryExists:e=>new D(`Directory already exists: ${e}`,["Use `--path` to specify a different location","Remove the existing directory if no longer needed"]),invalidPath:e=>new D(`Invalid path: ${e}`,["Path must be within the project directory",'Path cannot contain special characters like <>:"|?*']),notInProject:()=>new D("Could not find project root (no .tdk/project.json found)",["Run this from within a Flisk project","Run `flisk project --yes` to initialize a new project"])};function km(){let e=k();return e||ne.notInProject().exit()}function wc(e){e instanceof D&&e.exit(),lr(_s(e)),process.exit(1)}async function Ec(e,t){try{return await e()}catch(r){if(r instanceof cr)throw r;return t?.verbose&&r instanceof Error&&r.stack&&console.error(f.gray(r.stack)),wc(r)}}async function ym(e,t){let r=[],n=Es();return n==="unresponsive"?r.push(ne.dockerNotResponding()):n==="missing"&&r.push(ne.dockerNotAvailable()),await Ps()||r.push(ne.tiltNotInstalled()),r.length>0&&(console.error(f.red(`
22
- Flisk can't start - missing prerequisites:
23
- `)),r.forEach((s,o)=>{o>0&&console.error(""),s.display()}),console.error(f.gray("\nRun `flisk doctor` for a full environment check.")),process.exit(1)),Ec(e,t)}function Sm(e,t=1,r){lr(e,void 0,r),process.exit(t)}function lr(e,t,r){if(console.error(f.red(`\u274C ${e}`)),t&&console.error(f.gray(` ${t}`)),r&&r.length>0){console.error(f.yellow(`
24
- \u{1F4A1} Suggestions:`));for(let n of r)console.error(f.cyan(` \u2192 ${n}`))}}function bm(e,t){console.error(f.red(`
25
- tilt ${e} failed with exit code ${t}`)),process.exit(t)}function vc(e,t,r){return e===1?t:r||`${t}s`}function x(e,t,r){return`${e} ${vc(e,t,r)}`}var Pc={month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"};function wm(e){return new Date(e).toLocaleString("en-US",Pc)}function Em(e){return new Date(e).toLocaleDateString("en-US",{month:"short",day:"numeric"})}var ur={success:{color:"green",icon:"\u2713",chalkFn:f.green},error:{color:"red",icon:"\u2717",chalkFn:f.red},warning:{color:"yellow",icon:"\u25CB",chalkFn:f.yellow},unknown:{color:"gray",icon:"?",chalkFn:f.gray}};function dr(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 vm(e){return ur[dr(e)].color}function Pm(e){return ur[dr(e)].icon}function $m(e,t){return ur[dr(t)].chalkFn(e)}var $c={resources:{singular:"resource",command:"flisk resource <name>",context:`
26
- To create a resource:`},stacks:{singular:"stack",command:"flisk stack <stack-name>",context:`
27
- To create a stack, use:`},services:{singular:"service",command:"flisk resource <name>",context:`
28
- To create a service:`},"stack-services":{singular:"service",command:"flisk resource <name> --stack <stack-name>",context:`
29
- To add services to this stack:`}};function _m(e,t){let r=$c[e];console.log(t?f.yellow(`No ${r.singular}s found${t}.`):f.yellow(`No ${r.singular}s found.`)),console.log(f.gray(r.context)),console.log(f.gray(` ${r.command}`)),e==="stacks"&&(console.log(f.gray(`
30
- Or create a new resource with a stack:`)),console.log(f.gray(" flisk resource <name> --stack <stack-name>")))}function Rm(e){console.log(f.yellow(e||"Cancelled."))}function Am(e){console.log(f.blue(`Flisk ${e}
31
- `))}function Om(e,t){console.log(f.green(`All ${e} are ${t}!`))}var pr=62;function Nm(e="\u2500",t=pr){return e.repeat(t)}function Rs(e,t=pr-2){let r=Math.max(0,t-e.length),n=Math.floor(r/2),s=r-n;return" ".repeat(n)+e+" ".repeat(s)}function Cm(e,t){return e.length>t?`${e.slice(0,t-1)}\u2026`:e.padEnd(t)}function Dm(e,t){return e.length<=t?e:`${e.slice(0,t-3)}...`}function jm(e){console.log(f.green(`\u2713 ${e}`))}function Im(e){console.log(f.blue(e))}function Fm(e,t=2){console.log(f.gray(`${" ".repeat(t)}${e}`))}function Lm(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(1)} MB`}function Mm(e,t,r=pr){let n=r-2,s="\u2500".repeat(n);console.log(),console.log(f.cyan(`\u256D${s}\u256E`)),console.log(f.cyan("\u2502")+f.bold.white(Rs(e,n))+f.cyan("\u2502")),t&&(console.log(f.cyan(`\u251C${s}\u2524`)),console.log(f.cyan("\u2502")+f.gray(Rs(t,n))+f.cyan("\u2502"))),console.log(f.cyan(`\u2570${s}\u256F`))}function As(e){if(e.length===0||e.every(r=>r==="unknown"))return"unknown";let t=e.filter(r=>r==="ready").length/e.length;return t>.9?"healthy":t>.5?"degraded":"error"}var _c=/^[a-z0-9-]+$/;function Os(e){return _c.test(e)}var Rc="Use lowercase letters, numbers, and hyphens only";function Ns(e,t){return`${e} "${t}" is not valid. ${Rc}, e.g. "${e==="Resource name"?"my-service":"my-stack"}".`}function Ac(e){return e.trim()?Os(e)?{valid:!0}:{valid:!1,error:Ns("Resource name",e)}:{valid:!1,error:"Resource name is required"}}function Oc(e){return e.trim()?Os(e)?{valid:!0}:{valid:!1,error:Ns("Stack name",e)}:{valid:!1,error:"Stack name is required"}}function Gm(e){let t=e==="resource"?Ac:Oc;return r=>{let n=t(r);return n.valid?!0:n.error??"Invalid name"}}function Vm(e){return fr(Vt,e)?{valid:!0}:{valid:!1,error:`Invalid service. Must be one of: ${Vt.join(", ")}`}}function Wm(e){return Number.isInteger(e)&&e>0&&e<=65535}function Km(e){return!e.includes("\0")&&!/[<>:"|?*]/.test(e)}function Jm(e,t="_"){return e.replace(/[^a-zA-Z0-9-]/g,t).substring(0,100)}function fr(e,t){return e.includes(t)}var jc=Nn;function yr(e,t=5,r=0){let n=[];if(r>t)return n;let s=Cs.readdirSync(e,{withFileTypes:!0});for(let o of s){let i=ie(e,o.name);if(o.isDirectory()){if(Ic(o.name))continue;n.push(...yr(i,t,r+1))}else o.isFile()&&o.name===jc&&n.push(i)}return n}function Sr(e){return yr(e)}function Ic(e){return fr(Cn,e)||e.startsWith(".")}function Fc(e){if(!e||typeof e!="object")return!1;let t=e,r=typeof t.runtime=="string"||typeof t.appType=="string"||typeof t.type=="string";return typeof t.appName=="string"&&r}var mt=new Set;var oe=class extends Error{reason;constructor(t){super(t),this.reason=t,this.name="InvalidServiceJsonError"}};function hr(e){return e instanceof Error?e.message:String(e)}function Lc(e){let t;try{t=Cs.readFileSync(e,"utf-8")}catch(c){throw new oe(`could not read file: ${hr(c)}`)}let r;try{r=JSON.parse(t)}catch(c){throw new oe(`invalid JSON: ${hr(c)}`)}if(!Fc(r))throw new oe("missing required fields (appName, runtime or appType)");let n=nr(r);if(n.length>0)throw new oe(n.map(c=>`${c}: expected a string`).join("; "));let s=pt(r,e);if(s.length>0)throw new oe(s.join("; "));let o=r;for(let c of sr(r,e).warnings)mt.has(c)||(mt.add(c),console.warn(`\u26A0\uFE0F ${c}`));let i={...o,appType:o.appType||o.type},a=Dc(e);return{name:i.appName,path:a,configPath:e,config:i,stack:i.stack,port:i.port,type:i.appType}}function Ds(e){let t=yr(e),r=[],n=[];for(let s of t)try{r.push(Lc(s))}catch(o){n.push({path:s,reason:o instanceof oe?o.reason:hr(o)})}return{resources:r,problems:n}}function Mc(e){for(let t of e){let r=`skipped ${t.path}: ${t.reason}`;mt.has(r)||(mt.add(r),console.warn(`\u26A0\uFE0F ${r}`))}}function w(e){let{resources:t,problems:r}=Ds(e);return Mc(r),t}function ve(){let e=k();if(!e)throw ne.notInProject();return w(e)}function oh(e){let t=e;if(!t){let r=k();if(!r)throw ne.notInProject();t=Ds(r)}if(t.problems.length>0)throw new Error(t.problems.map(r=>`Invalid service.json at ${r.path}: ${r.reason}`).join(`
32
- `));return t.resources}function ih(e,t){let r=t??w(e);return Uc(r)}function Uc(e){let t=e||ve(),r=new Set;for(let n of t)n.stack&&r.add(n.stack);return Array.from(r).sort()}function ah(e=ve()){let t=new Map;for(let n of e)if(n.stack){let s=t.get(n.stack);s?s.push(n):t.set(n.stack,[n])}let r=[];for(let[n,s]of t)r.push({name:n,description:x(s.length,"resource"),resources:s,resourceCount:s.length});return r.sort((n,s)=>n.name.localeCompare(s.name))}function ch(e,t){return(t??ve()).filter(n=>n.stack===e)}function lh(e,t){return(t??ve()).some(n=>n.stack===e)}var br=5e3,gr=new Ue({ttlMs:br}),kr=new Ue({ttlMs:br}),Be=gs(br);function uh(){gr.clear(),kr.clear(),Be.reset()}function Bc(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===O)return"config";if(e==="schema.prisma"||e.includes("migration"))return"prisma";if(t){let r=t.split(`
33
- `).slice(0,10).join(`
34
- `);if(r.includes("GENERATED BY Flisk")||r.includes("AUTOGENERATED"))return"generated"}return"unknown"}function Hc(e){let t=e.configPath;if(Be.isValid()){let g=gr.get(t);if(g)return g}let r=e.path,n=mr(e.configPath),s=n.mtime.toISOString(),o=n.mtime.toISOString(),i=Gc(r),a=Ee(ie(r,"Dockerfile")),c=Ee(ie(r,"Tiltfile")),l=Ee(ie(r,de))||Ee(ie(r,"docker-compose.yaml")),u=e.config?.appType,d=u||"backend";u||(e.name.includes("frontend")?d="frontend":(e.name.includes("sdk")||e.name.includes("lib")||e.name.includes("infra"))&&(d="library"));let p={name:e.name,stack:e.stack,type:d,port:e.config?.port,createdAt:s,lastModified:o,dependencies:e.config?.dependsOn??[],hasDockerfile:a,hasTiltfile:c,hasDockerCompose:l,autogeneratedFiles:i,status:"unknown"};return gr.set(t,p),Be.markUpdated(),p}function Gc(e){let t=[];for(let n of Dn){let s=ie(e,n);if(Ee(s)){let o=mr(s),i=Cc(s,"utf-8");t.push({name:n,path:s,type:Bc(n,i),size:o.size,lastModified:o.mtime.toISOString()})}}let r=ie(e,"prisma");if(Ee(r)){let n=Nc(r);for(let s of n)if(s==="schema.prisma"||s.includes("migration")){let o=ie(r,s),i=mr(o);t.push({name:`prisma/${s}`,path:o,type:"prisma",size:i.size,lastModified:i.mtime.toISOString()})}}return t.sort((n,s)=>n.name.localeCompare(s.name))}function dh(e){let t=e.name;if(Be.isValid()){let c=kr.get(t);if(c)return c}let r=e.resources.map(c=>Hc(c)),n=r.map(c=>new Date(c.createdAt).getTime()),s=Math.min(...n),o=Math.max(...n),i=As(r.map(c=>c.status)),a={name:e.name,resourceCount:r.length,createdAt:new Date(s).toISOString(),lastModified:new Date(o).toISOString(),resources:r,overallStatus:i};return kr.set(t,a),Be.markUpdated(),a}var Vc={[L]:{name:L,description:"Generate and load the stack-level PostgreSQL docker-compose file",generatedFiles:["services/platform/database-management/docker-compose.yml"]}};function js(e,t){return Vc[t]?["pre_alpha","alpha","beta"].some(r=>e[r]?.enabledStacks?.includes(t)):!1}var Kc=Wc(new URL("../dist/generator/template-engine.js",import.meta.url).href),Tr=h.dirname(Kc);function He(e){let t=[h.join(Tr,"..","..","templates"),h.join(Tr,"..","templates"),h.join(Tr,"..","..","..","templates"),h.join(process.cwd(),"node_modules","flisk","templates"),h.join(h.dirname(process.execPath),"flisk","cli","templates")],r=[];for(let n of t){let s=h.join(n,e);r.push(s);try{return T.readFileSync(s,"utf-8")}catch{}}throw new Error(`Failed to load template ${e} from any of these paths: ${r.join(", ")}`)}var xr=null;function Jc(){return xr||(xr={[X]:He(".tiltignore.hbs"),[ot]:He("TILT_RESOURCE_DEFAULTS.star.hbs"),[st]:He("TILT_TECH_STACK.star.hbs"),Tiltfile:He("Tiltfile.hbs"),"spec.master":He("spec.master.hbs")}),xr}var qc={proxy:"Traefik reverse proxy",verdaccio:"Private npm registry","database-management":"PostgreSQL database"},zc={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"},Fs=["TILT_TECH_STACK.star","TILT_RESOURCE_DEFAULTS.star","spec.master","Tiltfile",".tiltignore"],ht=class{templatesDir;constructor(t){let r=Dt("node:url").fileURLToPath(new URL("../dist/generator",import.meta.url))||h.dirname(new URL(new URL("../dist/generator/template-engine.js",import.meta.url).href).pathname);this.templatesDir=t||h.join(r,"..","..","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(n=>this.formatStarlarkValue(n)).join(", ")}]`:typeof t=="object"?`{${Object.entries(t).map(([n,s])=>`${this.quoteStarlarkString(n)}: ${this.formatStarlarkValue(s)}`).join(", ")}}`:String(t)}registerHelpers(){fe.registerHelper("starlark",t=>new fe.SafeString(this.formatStarlarkValue(t))),fe.registerHelper("starlarkArray",t=>{if(!Array.isArray(t))return new fe.SafeString("[]");let r=t.map(n=>this.formatStarlarkValue(n));return new fe.SafeString(`[${r.join(", ")}]`)}),fe.registerHelper("json",t=>JSON.stringify(t))}buildContext(t){return{version:H.version,tech:H.tech,ports:H.ports,health:H.health,naming:H.naming,traefik:H.traefik,filewatchIgnores:[...H.filewatchIgnores],serviceTypes:H.serviceTypes,paths:H.paths,discovery:{scanIntervalSeconds:H.discovery.scanIntervalSeconds,maxManifestsPerRoot:H.discovery.maxManifestsPerRoot,paths:t.discovery.paths},docker:H.docker,runtime:H.runtime,project:t.project,phases:Yc(t.phases),alwaysEnabledInfra:t.always_enabled_infra??[...Fe],optionalInfra:t.optional_infra,serviceDescriptions:qc,infraDescriptions:zc}}loadTemplate(t){let r=h.join(this.templatesDir,`${t}.hbs`),n=T.existsSync(r)?T.readFileSync(r,"utf-8"):Jc()[t];if(!n)throw new Error(`Template not found: ${r}`);return fe.compile(n)}generateTechStack(t){return this.loadTemplate(st)(t)}generateServiceDefaults(t){return this.loadTemplate(ot)(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 r=this.buildContext(t);return{"TILT_TECH_STACK.star":this.generateTechStack(r),"TILT_RESOURCE_DEFAULTS.star":this.generateServiceDefaults(r),"spec.master":this.generateSpecMaster(r),Tiltfile:this.generateTiltfile(r),".tiltignore":this.generateTiltIgnore(r)}}};function Yc(e){let t={...e};for(let r of Object.keys(t))t[r]={...t[r],enabledStacks:Array.from(new Set(t[r].enabledStacks))};return t}function Xc(e){return e.replace(/-/g,"_")}function Qc(e){let t=Xc(e.project.name||"tdk_project");return`###############################################################################
35
- # SYSTEM-GENERATED - DO NOT EDIT
36
- # Stack feature: database-management
37
- # Source: .tdk/project.json phases.*.enabledStacks includes "database-management"
38
- # Regenerate with: flisk config regenerate
39
- ###############################################################################
40
-
41
- services:
42
- postgres:
43
- image: postgres:16-alpine
44
- container_name: ${t}_postgres
45
- restart: unless-stopped
46
- environment:
47
- POSTGRES_USER: ${t}
48
- POSTGRES_PASSWORD: \${DB_PASSWORD}
49
- POSTGRES_DB: postgres
50
- ports:
51
- - "\${TDK_BIND_ADDRESS:-127.0.0.1}:\${TDK_POSTGRES_PORT:-15432}:5432"
52
- volumes:
53
- - ${t}_postgres_data:/var/lib/postgresql/data
54
- networks:
55
- - database
56
- healthcheck:
57
- test: ["CMD-SHELL", "pg_isready -U ${t} -d postgres"]
58
- interval: 5s
59
- timeout: 5s
60
- retries: 20
61
-
62
- networks:
63
- database:
64
- name: ${t}_database
65
- external: true
66
-
67
- volumes:
68
- ${t}_postgres_data:
69
- `}function Is(e,t){if(!e||typeof e!="object")return!1;let r=e;for(let n of["pre_alpha","alpha","beta","out_of_scope"]){let s=r[n];if(!s||typeof s!="object"||!Array.isArray(s[t]))return!1}return!0}function Zc(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 r=t.project;if(typeof r.name!="string"||typeof r.version!="string")return null;let n=null;if(Is(t.phases,"enabledStacks"))n=t.phases;else if(Is(t.stacks,"services")){let i=t.stacks;n={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(!n||!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:n,always_enabled_infra:t.always_enabled_infra,optional_infra:t.optional_infra,discovery:t.discovery,overrides:t.overrides}:null}var wr=class extends Error{};function gt(e){let t=h.join(e,".tdk",A),r;try{r=T.readFileSync(t,"utf-8")}catch(o){if(o?.code==="ENOENT")throw new wr(`Project config not found: ${t}. Run 'flisk project --yes' first.`);let i=o instanceof Error?o.message:String(o);throw new Error(`Could not read project.json at ${t}: ${i}`)}let n;try{n=JSON.parse(r)}catch(o){let i=o instanceof Error?o.message:String(o);throw new Error(`Could not parse project.json at ${t}: ${i}`)}let s=Zc(n);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 el(e){let t=w(e).filter(n=>n.config?.sablier?.enable===!0);t.length===0||await yn(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 tl(e,t){let r=pe(e,O);if(T.existsSync(r))return!1;let n={name:t.project.name||h.basename(e),version:t.project.version||"1.0.0",private:!0,type:"module",workspaces:t.discovery.paths};return Jt(r,`${JSON.stringify(n,null,2)}
70
- `),!0}async function $h(e,t={}){let r=gt(e),n=Sr(e).flatMap(d=>ms(d,h.relative(e,d).split(h.sep).join("/")));if(n.length>0)throw new Error(`Refusing to generate config from unsafe service.json values:
71
- ${n.map(d=>` ${d}`).join(`
72
- `)}`);await el(e),tl(e,r)&&!t.quiet&&console.log("\u2713 Generated: package.json (workspace root)");let s=Object.values(r.phases).some(d=>d.enabledStacks.includes("verdaccio"));(r.optional_infra.verdaccio||s)&&(await et(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 `flisk config disable-infra verdaccio` to silence this warning."));let o=h.join(e,".tdk",".tdk-out");T.existsSync(o)||T.mkdirSync(o,{recursive:!0});let a=new ht().generateAll(r);for(let d of Fs){let p=a[d],g=pe(e,h.join(".tdk",".tdk-out",d));Ie(g,p),t.quiet||console.log(`\u2713 Generated: .tdk/.tdk-out/${d}`)}if(js(r.phases,L)){let d=pe(e,h.join("services","platform",L,de)),p=h.dirname(d);T.mkdirSync(p,{recursive:!0}),Ie(d,Qc(r)),t.quiet||console.log("\u2713 Generated: services/platform/database-management/docker-compose.yml")}let c=h.join(o,X),l=pe(e,X);if(T.existsSync(c)){if(!T.existsSync(l))T.copyFileSync(c,l),console.log("\u2713 Copied: .tiltignore \u2192 project root");else if(t.discardHandEdits){let d=T.readFileSync(l,"utf-8");d.includes(Us)&&d!==T.readFileSync(c,"utf-8")&&(T.copyFileSync(c,l),console.log("\u2713 Restored: .tiltignore \u2192 project root (hand edits discarded)"))}}let u=cl(e);for(let d of u)console.log(`\u2713 Copied: ${d} \u2192 project root`);await ll(e),console.log(""),console.log("\u{1F4A1} To start Tilt: flisk up")}var rl=/\.(?:test|spec)\.[cm]?[jt]sx?$/;function Ls(e){return rl.test(e)}function Ms(e){if(T.existsSync(e))for(let t of T.readdirSync(e,{withFileTypes:!0})){let r=h.join(e,t.name);t.isDirectory()?Ms(r):Ls(r)&&T.rmSync(r,{force:!0})}}var nl=["engine","discovery","specs","ext"],sl=[["shared-platform-engineering","docker-templates"]],ol=["install-deps.sh","bun-hoisted-symlink-fix.sh","prisma-bun-client-link-fix.sh","prisma-normalize-client.sh"];function il(){let e=Dt("node:url").fileURLToPath(new URL("../dist/generator",import.meta.url))||process.cwd();for(let t=0;t<6;t++){if(T.existsSync(h.join(e,"Tiltfile"))&&T.existsSync(h.join(e,"engine")))return e;let r=h.dirname(e);if(r===e)break;e=r}return null}function al(){let e=Dt("node:url").fileURLToPath(new URL("../dist/generator",import.meta.url))||process.cwd();for(let n=0;n<8;n++){if(T.existsSync(h.join(e,"shared-platform-engineering","docker-templates")))return e;let s=h.dirname(e);if(s===e)break;e=s}let t=h.dirname(process.execPath);return[h.join(t,"flisk"),h.join(process.cwd(),"node_modules","flisk")].find(n=>T.existsSync(h.join(n,"shared-platform-engineering","docker-templates")))??null}function cl(e){let t=al(),r=[];if(!t)return console.warn("\u26A0\uFE0F Flisk runtime assets not found - generated Dockerfiles may fail. Reinstall or upgrade Flisk, then re-run `flisk project`."),r;for(let n of sl){let s=h.join(t,...n),o=pe(e,h.join(...n));T.existsSync(s)&&(T.mkdirSync(h.dirname(o),{recursive:!0}),T.cpSync(s,o,{recursive:!0}),r.push(n.join("/")))}for(let n of ol){let s=h.join(e,"shared-platform-engineering","docker-templates",n);T.existsSync(s)||console.warn(`\u26A0\uFE0F Missing runtime asset after copy: ${s}`)}return r}async function ll(e){let t=pe(e,h.join(".tdk",".tdk-out","tdk-cli-ext")),r=[];process.env.TDK_EXTENSION_SOURCE&&r.push(process.env.TDK_EXTENSION_SOURCE);let n=il();n&&r.push(n);let s=h.dirname(process.execPath);r.push(h.join(s,"flisk")),r.push(h.join(e,"..","flisk"));let o="";for(let i of r)if(i&&T.existsSync(h.join(i,"Tiltfile"))&&T.existsSync(h.join(i,"engine"))){o=i;break}if(!o){console.warn("\u26A0\uFE0F Flisk extension not found - set TDK_EXTENSION_SOURCE to its checkout path and re-run `flisk project` (or `flisk config regenerate`) to vendor it before `flisk up`.");return}T.existsSync(t)||T.mkdirSync(t,{recursive:!0}),T.copyFileSync(h.join(o,"Tiltfile"),h.join(t,"Tiltfile"));for(let i of nl)Ms(h.join(t,i)),T.cpSync(h.join(o,i),h.join(t,i),{recursive:!0,filter:a=>!Ls(a)});console.log("\u2713 Vendored Flisk extension \u2192 .tdk/.tdk-out/tdk-cli-ext/");try{let{applyPremiumOverlay:i}=await import("./extension-fetch-J4FFH7TJ.js");await i(e,t)}catch(i){console.warn(`\u26A0\uFE0F Premium overlay skipped: ${i.message}`)}}var Us="SYSTEM-GENERATED BY Flisk CLI";function _h(e,t){let r=[],n=[],s=[],o=[],i=(y,S,m)=>{S!==m&&(r.push(`${S===void 0?"Missing file":"Out of sync"}: ${y} (run 'flisk config regenerate')`),s.push({file:y,diff:ul(y,S??"",m)}))},a=gt(e),l=new ht().generateAll(a),u=h.join(e,".tdk",".tdk-out");for(let y of Fs){let S=l[y],m=h.join(u,y);i(`.tdk/.tdk-out/${y}`,T.existsSync(m)?T.readFileSync(m,"utf-8"):void 0,S)}let d=h.join(e,X),p=T.existsSync(d)?T.readFileSync(d,"utf-8"):void 0;p!==void 0&&!p.includes(Us)?n.push(".tiltignore: not generated by Flisk, left as is"):i(X,p,l[X]);let g=t?.manifestPaths??Sr(e);for(let y of g){let S=h.relative(e,y).split(h.sep).join("/");if(!t?.skipManifestValidation){let $=hs(y,S);r.push(...$.errors),n.push(...$.warnings)}let m=dl(e,y);r.push(...m.errors),o.push(...m.handEdited)}return{valid:r.length===0,errors:r,warnings:n,diffs:s,handEdited:o}}function ul(e,t,r){let n=t.replace(/\n$/,"").split(`
73
- `),s=r.replace(/\n$/,"").split(`
74
- `),o=[`--- a/${e}`,`+++ b/${e}`,`@@ -1,${n.length} +1,${s.length} @@`];for(let i of n)o.push(`-${i}`);for(let i of s)o.push(`+${i}`);return o.join(`
75
- `)}function dl(e,t){let r=h.dirname(t),n=h.join(r,I);if(!T.existsSync(n))return{errors:[],handEdited:[]};let s=[],o=(u,d="")=>{for(let p of T.readdirSync(u,{withFileTypes:!0})){if(d===""&&p.name===".tdk-contract")continue;let g=h.join(d,p.name),y=h.join(u,p.name);p.isDirectory()?o(y,g):p.isFile()&&s.push({fullPath:y,relativeFile:g})}};if(o(n),s.length===0)return{errors:[],handEdited:[]};let i=h.join(n,".tdk-contract"),a=h.join(i,"service.json.source"),c=[],l=[];T.existsSync(a)?T.readFileSync(a,"utf-8")!==T.readFileSync(t,"utf-8")&&c.push(h.relative(e,t)+": service.json changed since generated outputs were written (run flisk up)"):c.push(h.relative(e,a)+": generated-file contract snapshot is missing (run flisk up to regenerate)");for(let u of s){if(u.relativeFile==="service.yaml")continue;let d=h.join(i,u.relativeFile),p=h.relative(e,u.fullPath);T.existsSync(d)?T.readFileSync(u.fullPath,"utf-8")!==T.readFileSync(d,"utf-8")&&(l.push(p.split(h.sep).join("/")),c.push(`${p}: generated file differs from its Flisk snapshot (run flisk up to regenerate)`)):c.push(`${p}: generated-file snapshot is missing (run flisk up to regenerate)`)}return{errors:c,handEdited:l}}import{existsSync as pl}from"node:fs";var kt="docs/agent-hosts.md",Er=`WebContainers cannot run Flisk: there is no Docker daemon or Tilt. Run \`flisk up\` on a machine with Docker. See ${kt}.`,Bs=`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 ${kt}.`;function fl(){return{env:process.env,versions:process.versions,platform:process.platform,fileExists:pl}}function Hs(e=fl()){let{env:t,versions:r,platform:n,fileExists:s}=e;return r.webcontainer?{kind:"webcontainer",canUp:!1}:t.CODESPACES==="true"?{kind:"codespaces",canUp:!0}:t.REMOTE_CONTAINERS||s("/.dockerenv")&&t.DEVCONTAINER?{kind:"devcontainer",canUp:!0}:n==="win32"?{kind:"native-windows",canUp:t.TDK_ALLOW_NATIVE_WINDOWS==="1"}:n==="linux"&&t.WSL_DISTRO_NAME?{kind:"wsl2",canUp:!0}:{kind:"local",canUp:!0}}function Gs(e){return e==="devcontainer"||e==="codespaces"}function yt(e){return e.some(t=>M(t.config?.appType)||t.config?.appType==="frontend"||t.config?.appType==="worker")}import{existsSync as ml,readFileSync as hl}from"node:fs";import{join as gl,relative as Vs,resolve as Ws,sep as kl}from"node:path";var yl=2,Ks=["services/*/*"];function Js(e){return e.split(kl).filter(Boolean)}function Sl(e,t){let r=0,n=0,s=-1,o=0;for(;n<t.length;)if(r<e.length&&e[r]!=="*"&&(e[r]==="?"||e[r]===t[n]))r++,n++;else if(r<e.length&&e[r]==="*")s=r++,o=n;else if(s!==-1)r=s+1,n=++o;else return!1;for(;r<e.length&&e[r]==="*";)r++;return r===e.length}function bl(e,t){return e.length===t.length&&e.every((r,n)=>Sl(r,t[n]))}function vr(e){let t=gl(e,".tdk",A);if(!ml(t))return Ks;try{let r=JSON.parse(hl(t,"utf-8"))?.discovery?.paths;if(Array.isArray(r)&&r.every(n=>typeof n=="string"))return r}catch{}return Ks}function St(e,t,r){let n=Js(Vs(e,Ws(e,t)));return r.some(s=>{let o=Js(Vs(e,Ws(e,s)));for(let i=0;i<=yl;i++){let a=n.length-i;if(a>0&&bl(o,n.slice(0,a)))return!0}return!1})}function Fh(e,t,r,n){let s=vr(e);if(St(e,t,s))return{path:t};let o=`services/${r}/${n}`;return St(e,o,s)?{path:o,adjustedFrom:t}:{path:t}}import{execFile as Fl}from"node:child_process";import{promisify as Ll}from"node:util";import{execFileSync as eo,execSync as We}from"node:child_process";import{existsSync as Pr,readdirSync as Tl,readFileSync as $r}from"node:fs";import{join as Pe}from"node:path";function qs(e,t){return e==="pending"||e==="in_progress"||e==="none"||t==="pending"||t==="none"}var zs="container is running, but its health check is failing";function Ys(e){let t=e?.runtimeStatus??"";if(t!=="ok")return t;let r=e?.composeResourceInfo?.healthStatus;return r==="unhealthy"?"error":r==="starting"?"pending":t}var Ge=1e4,Xs=3e3,xl="http://localhost:4873";function to(e){return`'${e.replace(/'/g,"'\\''")}'`}function Ve(e,t,r,n,s){return process.platform==="win32"?eo(N(t)??t,r,{...s,windowsHide:!0}):e(n,s)}var wl=[80,443];function _r(e){return e.replace(/-/g,"_")}function El(e,t){let r=new Set(t),n=[];for(let s of e.split(`
76
- `)){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):"",l=[];for(let u of r)c.split(/[\s,]+/).some(p=>p.startsWith(`${u}->`)||p.includes(`:${u}->`))&&l.push(u);l.length>0&&n.push({name:a,ports:c,publishedPorts:l})}return n}function ro(e,t){let r=e.toLowerCase(),n=t.toLowerCase();return r===`${n}_traefik`||r.startsWith(`${n}_traefik`)}function vl(e,t){return e.filter(r=>!ro(r.name,t))}function Rr(e){try{let t=e??ve();return new Set(t.filter(r=>r.config?.sablier?.enable&&r.config?.sablier?.deferStart).map(r=>r.name))}catch{return new Set}}function Ar(e,t=new Set){let n=JSON.parse(e).items??[],s=[],o=0,i=0,a=0;for(let c of n){let l=c.metadata?.name??"unknown",u=c.status?.updateStatus??"",d=Ys(c.status),p=d==="error"&&c.status?.runtimeStatus==="ok",y=(c.status?.buildHistory?.[0]?.error??"").trim()||(p?zs:"");if(u==="error"||d==="error"){s.push({name:l,updateStatus:u,runtimeStatus:d,error:y.replace(/\s+/g," ").slice(0,800)});continue}if(u==="ok"&&(d==="ok"||d==="not_applicable")){a+=1;continue}l!=="(Tiltfile)"&&qs(u,d)&&(t.has(l)?i+=1:o+=1)}return{failures:s,pendingCount:o,deferredCount:i,okCount:a,total:n.length}}var Pl=/failed to resolve source metadata for docker\.io\/library\/([A-Za-z0-9._-]+-l[1-4](?:-[a-z]+)*)(?::[^\s:]+)?:/i;function Or(e){let t=e.replace(/\s+/g," ").trim(),r=t.match(/Bind for [^ ]+:(\d+) failed: port is already allocated/i);if(r)return`host port ${r[1]} is already allocated`;if(/port is already allocated/i.test(t))return"a required host port is already allocated";let n=[...t.matchAll(/ConnectionRefused downloading package manifest (@[A-Za-z0-9._/-]+)/gi)].map(a=>a[1]);if(n.length>0){let a=[...new Set(n)],c=a.slice(0,4).join(", "),l=a.length>4?` (+${a.length-4} more)`:"";return`private registry ConnectionRefused for ${c}${l} \u2014 Verdaccio (:4873) is down or unreachable from the build`}let s=t.match(Pl);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 Qs(e){return/ConnectionRefused downloading package manifest|Verdaccio|private registry|dependency install failed during ImageBuild|install-deps\.sh|bun install/i.test(e)}function Nr(e=k()??process.cwd()){let t=Pe(e,".tdk",A);if(!Pr(t))return!1;try{let r=JSON.parse($r(t,"utf-8"));return r.optional_infra?.verdaccio===!0||Object.values(r.phases??{}).some(n=>(n.enabledStacks??[]).includes("verdaccio"))}catch{return!1}}function no(e){for(let t of[Pe(e,".npmrc"),Pe(e,O)])if(Pr(t))try{let r=$r(t,"utf-8");if(/:4873\b/.test(r)||/verdaccio/i.test(r))return!0}catch{}return!1}function $l(e=k()??process.cwd()){return Nr(e)||no(e)}function Cr(e=We,t=k()??process.cwd(),r=process.env.VERDACCIO_URL??process.env.NPM_REGISTRY_URL??xl,n=!0){if(!n&&!no(t))return{name:"Private npm registry",didPass:!0,isSkipped:!0,message:"Verdaccio is enabled in the project config but needs a Premium license, so Flisk will not start it - skipped"};if(!$l(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=Ve(e,"docker",["ps","--format","{{.Names}}"],"docker ps --format '{{.Names}}'",{stdio:"pipe",encoding:"utf-8",timeout:Ge});s=/verdaccio/i.test(a)}catch{}let o=!1,i="";try{let a=process.platform==="win32"?eo("curl.exe",["-fsS","-o","NUL","-w","%{http_code}","--max-time","2",r],{stdio:"pipe",encoding:"utf-8",timeout:Xs+1e3,windowsHide:!0}).trim():e(`curl -fsS -o /dev/null -w '%{http_code}' --max-time 2 ${to(r)}`,{stdio:"pipe",encoding:"utf-8",timeout:Xs+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 ${r}${s?" (verdaccio container running)":""}`}:s?{name:"Private npm registry",didPass:!1,message:`Verdaccio container is running but ${r} 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 `flisk doctor`."}:{name:"Private npm registry",didPass:!1,message:`Private npm registry (Verdaccio) is not reachable at ${r}. 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 `flisk up`."}}function Dr(e=We,t=Te(),r=wl){let n=_r(t),s="";try{s=Ve(e,"docker",["ps","--format","{{.Names}}\\t{{.Ports}}"],"docker ps --format '{{.Names}}\\t{{.Ports}}'",{stdio:"pipe",encoding:"utf-8",timeout:Ge})}catch{return{name:"Ingress Ports",didPass:!0,isSkipped:!0,message:"Docker not available - skipped ingress port check"}}let o=El(s,r),i=vl(o,n);if(i.length===0){let c=o.filter(l=>ro(l.name,n));return c.length>0?{name:"Ingress Ports",didPass:!0,message:`Ingress ports ${r.join("/")} held by this project's Traefik (${c.map(l=>l.name).join(", ")})`}:{name:"Ingress Ports",didPass:!0,message:`Ingress ports ${r.join("/")} are free for Traefik`}}return{name:"Ingress Ports",didPass:!1,message:`Traefik cannot bind ingress ports because another container already owns them:
77
- ${i.map(c=>{let l=c.publishedPorts.join(", ");return`${c.name} (host ports ${l})`}).join(`
78
- `)}`,fix:`Stop the foreign container(s), e.g. \`docker stop ${i[0]?.name}\`, or shut down the other Flisk/Tilt project using that Traefik. Then re-run \`flisk up\`.`}}function _l(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 jr(e){let t=e.filter(o=>o.status!==void 0),r=e.filter(o=>!o.ok);if(t.length===0)return{name:"Service Health",didPass:!0,isSkipped:!0,message:`No services responded - skipped ping of ${x(e.length,"service")} (stack may be down, or Traefik never bound :80)`,fix:"If `flisk up` is already running, check Traefik/port 80 in the Tilt UI. Otherwise start with: flisk up"};if(r.length===0)return{name:"Service Health",didPass:!0,message:`All ${x(e.length,"service")} responding on /health`};let n=r.map(o=>`${o.name} (${_l(o)})
79
- ${o.url}`).join(`
80
- `),s=r.some(o=>o.status===404);return{name:"Service Health",didPass:!1,message:`${x(r.length,"service")} not healthy (${e.length-r.length}/${e.length} healthy):
81
- ${n}`,fix:s?"If `flisk up` just started, images may still be building: re-run `flisk doctor` in a minute. Otherwise check `docker ps` and the resource in the Tilt UI, and compare `flisk networks` with the URL above.":"Check container state and routing: docker ps, then flisk networks to compare the advertised URLs against Traefik's routers"}}function Ir(e){if(e.length===0)return{name:"Service Smoke",didPass:!0,isSkipped:!0,message:"No read-only smoke checks declared"};let t=e.filter(n=>!n.ok);if(t.length===0)return{name:"Service Smoke",didPass:!0,message:`All ${x(e.length,"service")} pass their smoke checks`};let r=t.map(n=>n.failure??`${n.name}: failed`).join(`
82
- `);return{name:"Service Smoke",didPass:!1,message:`${x(t.length,"service")} failing a smoke check (${e.length-t.length}/${e.length} passing):
83
- ${r}`,fix:'Add the route the smoke step expects, or correct its "path" in the service.json "smoke" block. Then re-run flisk doctor.'}}function so(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 r=t.filter(s=>s.status===404);if(r.length===0)return{name:"Advertised Endpoints",didPass:!0,message:`${x(t.length,"advertised endpoint")} reachable`};let n=r.map(s=>`${s.name} (404)
84
- ${s.url}`).join(`
85
- `);return{name:"Advertised Endpoints",didPass:!0,isWarning:!0,message:`${x(r.length,"advertised link")} ${r.length===1?"returns":"return"} 404 (the service may be healthy, but nothing answers at this exact path):
86
- ${n}`,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 flisk networks and the Flisk App print stays the same until the CLI changes how it reports it.'}}function Fr(e=We){let t="";try{t=Ve(e,"tilt",["get","uiresources","-o","json"],"tilt get uiresources -o json",{stdio:"pipe",encoding:"utf-8",timeout:Ge})}catch{return{name:"Tilt Resources",didPass:!0,isSkipped:!0,message:"Tilt is not running - skipped resource status check",fix:"Start them with: flisk up"}}let r;try{r=Ar(t,Rr())}catch{return{name:"Tilt Resources",didPass:!0,isSkipped:!0,message:"Could not parse Tilt resource status - skipped"}}if(r.total===0)return{name:"Tilt Resources",didPass:!0,isSkipped:!0,message:"No Tilt UIResources found"};if(r.failures.length===0){let m=r.deferredCount>0?`, ${r.deferredCount} deferred`:"";return{name:"Tilt Resources",didPass:!0,message:`Tilt resources healthy (${r.okCount} ok, ${r.pendingCount} pending${m})`}}let n=Al(r.failures),o=n.slice(0,20),i=n.length-o.length,a=o.map(m=>({failure:m,why:Nl(m,e)})),c=a.map(({failure:m,why:$})=>`${m.name}: ${$}`).join(`
87
- `),l=i>0?`
88
- \u2026and ${x(i,"more failed resource")}`:"",u=r.pendingCount>0?`
89
- ${x(r.pendingCount,"resource")} still pending/not started \u2014 often blocked by the failures above.`:"",d=r.deferredCount>0?`
90
- ${x(r.deferredCount,"resource")} intentionally deferred (sablier.deferStart) \u2014 not started until first request, unrelated to the failures above.`:"",p=n.some(m=>/port is already allocated/i.test(m.error)),g=n.some(m=>Qs(m.error)||Qs(Or(m.error))),y=a.some(({why:m})=>/Kafka broker unreachable|kafka:9092/i.test(m)),S="Open the Tilt UI URL printed by flisk up (default port "+je.tiltUi+" when TILT_PORT is unset), inspect the red resources, fix the listed errors, then `tilt trigger <resource>` or re-run `flisk up`.";return p?S="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 `flisk up` again.":g?S="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.":y&&(S="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 ${x(r.failures.length,"failed resource")}:
91
- ${c}${l}${u}${d}`,fix:S}}var Rl=/^(traefik|postgres|nats|infisical|verdaccio|proxy)([.-]|$)/i;function Zs(e){return Rl.test(e)}function Al(e){return[...e].sort((t,r)=>{let n=Zs(t.name)?0:1,s=Zs(r.name)?0:1;return n!==s?n-s:t.name.localeCompare(r.name)})}function Ol(e,t=We,r=Te()){try{let n=Ve(t,"docker",["ps","-a","--format","{{.Names}}"],"docker ps -a --format '{{.Names}}'",{stdio:"pipe",encoding:"utf-8",timeout:Ge}).split(`
92
- `).map(l=>l.trim()).filter(Boolean),s=`-autogenerated-${e}-1`,o=n.find(l=>l===e)??n.find(l=>l.startsWith(`${r}-`)&&l.endsWith(s))??n.find(l=>l.endsWith(s))??n.find(l=>l.includes(e));if(!o)return null;let i=Ve(t,"docker",["logs","--tail","120",o],`docker logs --tail 120 ${to(o)} 2>&1`,{stdio:"pipe",encoding:"utf-8",timeout:Ge});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 l=[...i.matchAll(/"path"\s*:\s*\[\s*"([^"]+)"\s*\]/g)].map(d=>d[1]),u=[...new Set(l)].slice(0,6);return u.length>0?`runtime crash: ZodError missing required config/env: ${u.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 Nl(e,t=We){return e.error?Or(e.error):e.runtimeStatus==="error"?Ol(e.name,t)??"runtime error (container crashed or unhealthy \u2014 check Tilt/docker logs)":e.updateStatus||e.runtimeStatus||"unknown error"}var Cl=/\.(ts|tsx|mts|js|mjs|cjs)$/,Dl=new Set(["node_modules","dist",I,"tests","__tests__"]);function oo(e){let t=[];for(let r of Tl(e,{withFileTypes:!0}))r.isDirectory()?Dl.has(r.name)||t.push(...oo(Pe(e,r.name))):Cl.test(r.name)&&!/\.(test|spec)\./.test(r.name)&&t.push(Pe(e,r.name));return t}function jl(e){let t=e?.healthCheckPath||"/health";return{container:t,traefik:e?.traefik?.healthCheck||t}}function Il(e){let t=[];for(let r of e){if(!M(r.config?.appType))continue;let n=Pe(r.path,"src");if(!Pr(n))continue;let s=oo(n);if(s.length===0)continue;let o=s.map(l=>$r(l,"utf-8")),{container:i,traefik:a}=jl(r.config),c=i===a?[{path:i,usedBy:"container and Traefik healthchecks"}]:[{path:i,usedBy:"container healthcheck"},{path:a,usedBy:"Traefik healthcheck"}];for(let{path:l,usedBy:u}of c){let d=["'",'"',"`"].map(p=>`${p}${l}${p}`);o.some(p=>d.some(g=>p.includes(g)))||t.push({name:r.name,healthPath:l,usedBy:u})}}return t}function io(e=k()??process.cwd()){let t=[];try{t=w(e)}catch{return{name:"Health Routes",didPass:!0,isSkipped:!0,message:"Could not read services - skipped health route check"}}let r=t.filter(o=>M(o.config?.appType));if(r.length===0)return{name:"Health Routes",didPass:!0,isSkipped:!0,message:"No backend services to check"};let n=Il(r);return n.length===0?{name:"Health Routes",didPass:!0,message:`All ${x(r.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):
93
- ${n.map(o=>`${o.name}: no "${o.healthPath}" route in src/ (probed by the ${o.usedBy})`).join(`
94
- `)}`,fix:`Add the route, e.g. for Hono: app.get("${n[0]?.healthPath}", (c) => c.json({ status: "ok" })). To use a different path, set "healthCheckPath" (container healthcheck) or "traefik.healthCheck" (Traefik) in its service.json.`}}var Ml=Ll(Fl),Ul=1e4,Bl=5,ao=10*6e4,Hl=3,Gl=200,Vl=async e=>{let{stdout:t,stderr:r}=await Ml("docker",e,{encoding:"utf-8",timeout:Ul,windowsHide:!0});return`${t}${r}`};function Wl(e,t){let r=_r(t).toLowerCase(),n=e.replace(/-/g,"_").toLowerCase();return n===r||n.startsWith(`${r}_`)}function Kl(e,t=Date.now()){if(e.status==="restarting")return!0;if(e.restartCount<Bl)return!1;if(e.status==="running"){let n=Date.parse(e.startedAt);return Number.isNaN(n)||t-n<ao}let r=Date.parse(e.finishedAt??"");return e.exitCode!==0&&!Number.isNaN(r)&&t-r<ao}function Jl(e,t){let r=/database "([^"]+)" does not exist/i.exec(e);return r?`database "${r[1]}" does not exist: re-run the project's database provisioning (flisk 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 (flisk 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 ql(e){let t=[];for(let r of e.split(/\r?\n/)){let[n,s,o,i,a,c]=r.split(" ");!n||s===void 0||t.push({name:n.replace(/^\//,""),restartCount:Number(s)||0,status:o??"",startedAt:i??"",exitCode:Number(a)||0,finishedAt:c??""})}return t}async function co(e,t=[],r=Vl,n=Date.now()){let s="Crash-looping containers",o;try{o=(await r(["ps","-a","--format",'{{.ID}} {{.Label "com.docker.compose.project"}}'])).split(/\r?\n/).map(d=>d.split(" ")).filter(([d,p])=>d&&p&&Wl(p,e)).map(([d])=>d)}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=ql(await r(["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(u=>Kl(u,n));if(a.length===0)return{name:s,didPass:!0,message:"No project container is crash-looping"};let c=[],l=new Set;for(let u of a){let d="";try{d=await r(["logs","--tail",String(Hl),u.name])}catch{}let p=be(d,t).split(/\r?\n/).map(y=>y.trim().slice(0,Gl)).filter(Boolean),g=Jl(d,u);g&&l.add(g),c.push([`${u.name}: ${u.status}, restarted ${u.restartCount} times`,...p.map(y=>` | ${y}`)].join(`
95
- `))}return{name:s,didPass:!1,message:`${a.length} container${a.length===1?" is":"s are"} crash-looping:
96
- ${c.join(`
97
- `)}`,fix:[...l,"Read the full log: docker logs <container>"].join("; ")}}import{execFile as iu}from"node:child_process";import{realpathSync as ho,statfsSync as au}from"node:fs";import{homedir as cu}from"node:os";import{join as lu}from"node:path";import{promisify as uu}from"node:util";import{statfsSync as zl}from"node:fs";import{homedir as Yl}from"node:os";var bt="Disk Space",Xl=1024**3,Ql=2,Lr=10,lo=["Free disk space, then retry: flisk 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 uo(e=zl,t=Yl()){let r;try{let s=e(t);r=Number(s.bavail)*Number(s.bsize)/Xl}catch{return{name:bt,didPass:!0,isSkipped:!0,message:"Could not read free disk space"}}let n=`${r.toFixed(1)} GB free`;return r<Ql?{name:bt,didPass:!1,message:`${n}: Docker cannot build or start containers`,fix:lo}:r<Lr?{name:bt,didPass:!1,isWarning:!0,message:`${n}: image builds may fail`,fix:lo}:{name:bt,didPass:!0,message:n}}var Zl="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: flisk doctor";function po(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:Zl}}import{execFileSync as eu}from"node:child_process";var Mr=(e,t)=>eu(e,t,{encoding:"utf-8",stdio:"pipe",windowsHide:!0}),fo=["traefik-public","traefik-private","backend","database","infisical-network","verdaccio-network","proxy"];function Ur(e){let t=e.replace(/-/g,"_");return fo.map(r=>`${t}_${r}`)}function mo(e){let t=new Set;for(let r of e)for(let n of["traefik-public","traefik-private"])r.endsWith(`_${n}`)&&t.add(r.slice(0,-(n.length+1)));return t}function Br(e,t){for(let r of fo)if(e.endsWith(`_${r}`)){let n=e.slice(0,-(r.length+1));if(t.has(n))return n}return null}function ag(e,t=Mr){let r=new Set(t("docker",["network","ls","--format","{{.Name}}"]).split(/\r?\n/).map(s=>s.trim()).filter(Boolean)),n={removed:[],kept:[]};for(let s of Ur(e))if(r.has(s))try{t("docker",["network","rm",s]),n.removed.push(s)}catch(o){let i=String(o.stderr??o).trim();n.kept.push({name:s,reason:i.split(/\r?\n/)[0]??""})}return n}function Tt(e,t=process.platform,r=Mr){if(t==="win32")return[];let n;try{n=r("ps",["-axo","pid=,command="])}catch{return[]}let s=[];for(let o of n.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),l=` -f ${e}`,u=a.indexOf(l),d=a[u+l.length];c==="tilt"&&/^\S+\s+up\s/.test(a)&&u>0&&(d===void 0||d===" ")&&s.push(Number(i[1]))}return s}function cg(e,t=process.platform,r=Mr){let n=Tt(e,t,r);for(let s of n)try{r("kill",["-TERM",String(s)])}catch{}return n}async function lg(e,t=8e3,r=Tt){let n=Date.now()+t;for(;r(e).length>0;){if(Date.now()>=n)return!1;await new Promise(s=>setTimeout(s,250))}return!0}import{execFile as tu}from"node:child_process";function Hr(e){return new Promise(t=>{tu(N("tilt")??"tilt",["get","uiresources","-o","json","--port",String(e)],{timeout:15e3,maxBuffer:16*1024*1024},(r,n)=>t(r?null:n))})}function ru(e){let r=(JSON.parse(e).items??[]).filter(n=>n.status?.disableStatus?.state!=="Disabled").map(n=>n.status?.updateStatus==="not_applicable"?{...n,status:{...n.status,updateStatus:"ok"}}:n);return JSON.stringify({items:r})}function nu(e){return(JSON.parse(e).items??[]).filter(t=>t.status?.disableStatus?.state!=="Disabled").flatMap(t=>t.metadata?.name?[t.metadata.name]:[])}function su(e,t){let r=JSON.parse(e).items??[],n=new Map(r.map(s=>[s.metadata?.name,s]));return{missing:t.filter(s=>!n.has(s)),disabled:t.filter(s=>n.get(s)?.status?.disableStatus?.state==="Disabled")}}function ou(e,t,r){let n=Ar(ru(e),t),s={ready:n.failures.length===0&&n.pendingCount===0&&n.okCount>0,failures:n.failures.map(o=>({name:o.name,message:o.error||`${o.updateStatus}/${o.runtimeStatus}`})),pending:n.pendingCount,timedOut:!1,enabled:nu(e)};if(r&&r.length>0){let{missing:o,disabled:i}=su(e,r);if(o.length>0&&(s={...s,ready:!1,pending:s.pending+o.length}),i.length>0)return{settled:!0,result:{...s,ready:!1,failures:[...s.failures,...i.map(a=>({name:a,message:"disabled in Tilt"}))]}}}return{result:s,settled:s.ready||s.failures.length>0&&s.pending===0}}async function mg(e,t={}){let r=t.timeoutMs??Number(process.env.TDK_UP_READY_TIMEOUT_MS??9e5),n=t.intervalMs??2e3,s=t.fetchJson??Hr,o=t.deferred??Rr(),i=Date.now()+r,a={ready:!1,failures:[],pending:0,timedOut:!1,enabled:[]};for(;;){let c=await s(e);if(c)try{let l=ou(c,o,t.expected);if(a=l.result,l.settled||a.failures.length>0&&t.isStalled?.(c))return a}catch{}if(Date.now()>=i)return{...a,ready:!1,timedOut:!0};await new Promise(l=>setTimeout(l,n))}}var xt=uu(iu),du=15e3,pu=/^flisk_doctor_probe_(\d+)$/,fu=async e=>{let{stdout:t}=await xt("docker",e,{encoding:"utf-8",timeout:du,windowsHide:!0});return t};function Gr(e){return String(e.stderr??e).trim().split(/\r?\n/)[0]??""}var mu=120;function hu(e){let t=/^\s*(?:(?:(\d+)-)?(\d+):)?(\d+):(\d+)\s*$/.exec(e);if(!t)return null;let[,r="0",n="0",s="0",o="0"]=t;return((Number(r)*24+Number(n))*60+Number(s))*60+Number(o)}async function gu(e,t=Hr,r=2,n=s=>new Promise(o=>setTimeout(o,s))){for(let s=0;s<r;s++){s>0&&await n(1e3);for(let o of e)if(await t(o)!==null)return!0}return!1}async function ku(e){if(process.platform==="win32")return"unknown";try{let{stdout:t}=await xt("ps",["-p",String(e),"-o","etime="],{encoding:"utf-8",timeout:5e3}),r=hu(t);if(r===null||r<mu)return"unknown";let{stdout:n}=await xt("lsof",["-nP","-a","-p",String(e),"-iTCP","-sTCP:LISTEN","-Fn"],{encoding:"utf-8",timeout:5e3}),s=[...n.matchAll(/^n.*:(\d+)$/gm)].map(o=>Number(o[1]));return await gu(s)?"healthy":"stale"}catch{return"unknown"}}function yu(e=fu){return{async listNetworks(){let t=(await e(["network","ls","--format","{{.Name}}"])).split(/\r?\n/).map(n=>n.trim()).filter(Boolean);return(await Promise.all(t.map(async n=>{try{let s=await e(["network","inspect","--format","{{len .Containers}}",n]);return{name:n,containers:Number(s.trim())||0}}catch(s){if(/no such network|not found/i.test(String(s)))return null;throw s}}))).filter(n=>n!==null)},async removeNetwork(t){try{await e(["network","rm",t])}catch(r){throw new Error(Gr(r))}},freeDiskGib(){try{let t=au(cu());return Number(t.bavail)*Number(t.bsize)/1024**3}catch{return null}},async reclaimable(){let t=null;try{let n=await e(["system","df","--format","{{.Type}} {{.Reclaimable}}"]);for(let s of n.split(/\r?\n/)){let[o,i]=s.split(" ");o==="Build Cache"&&i&&(t=i.replace(/\s*\(.*\)/,"").trim())}}catch{}let r=0;try{r=(await e(["images","-q","-f","dangling=true"])).split(/\r?\n/).filter(Boolean).length}catch{}return{buildCache:t,danglingImages:r}},async prune(t){try{await e(t==="builder"?["builder","prune","-f"]:["image","prune","-f"])}catch(r){throw new Error(Gr(r))}},async tiltStatus(t){return ku(t)},stopProcess(t){process.kill(t,"SIGTERM")},isProcessAlive(t){try{return process.kill(t,0),!0}catch(r){return r.code==="EPERM"}}}}var go=yu();async function ko(e,t=go,r={}){let n=[],s=[];for(let u of r.tiltPids??[])await t.tiltStatus(u)==="stale"?n.push({id:`stop-tilt:${u}`,check:"Tilt processes",scope:"project",command:`kill -TERM ${u}`,status:"planned",reason:"this project's Tilt is running but its UI does not answer"}):s.push(u);let o=s.length>0?`Tilt is running for this project (pid ${s.join(", ")}); run flisk down first`:void 0,i=await t.listNetworks(),a=t.freeDiskGib();if(a!==null&&a<Lr){let{buildCache:u,danglingImages:d}=await t.reclaimable(),p=`${a.toFixed(1)} GB free`;u&&!/^0(\.0+)?\s*B$/i.test(u)&&n.push({id:"prune:builder",check:"Disk Space",scope:"host",command:"docker builder prune -f",status:"planned",reason:`${p}; ${u} of build cache reclaimable`}),d>0&&n.push({id:"prune:images",check:"Disk Space",scope:"host",command:"docker image prune -f",status:"planned",reason:`${p}; ${d} untagged image${d===1?"":"s"}`})}let c=new Set(e?Ur(e):[]),l=mo(i.map(u=>u.name));for(let u of i){let d=pu.exec(u.name),p,g,y="unused network";if(d)p="probe",g="Orphaned probe networks";else if(c.has(u.name))p="project",g="Docker networks";else if(r.machine&&Br(u.name,l))p="machine",g="Docker networks",y=`unused network of project "${Br(u.name,l)}"`;else continue;let S={id:`remove-network:${u.name}`,check:g,scope:p,command:`docker network rm ${u.name}`,status:"planned",reason:y};u.containers>0?(S.status="skipped",S.reason=`${u.containers} container(s) still attached`):p==="project"&&o?(S.status="skipped",S.reason=o):d&&t.isProcessAlive(Number(d[1]))&&(S.status="skipped",S.reason=`probe of a running process (pid ${d[1]})`),n.push(S)}return n}async function yo(e,t,r=go){let n=[];for(let s of e){if(s.status!=="planned"||t.dryRun){n.push(s);continue}if(!t.confirm){n.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){n.push({...s,status:"skipped",reason:"not confirmed"});continue}try{if(s.id.startsWith("prune:")){await r.prune(s.id==="prune:builder"?"builder":"images"),n.push({...s,status:"applied",reason:"pruned"});continue}if(s.id.startsWith("stop-tilt:")){r.stopProcess(Number(s.id.slice(10))),n.push({...s,status:"applied",reason:"stopped"});continue}await r.removeNetwork(s.id.slice(15)),n.push({...s,status:"applied",reason:"removed"})}catch(i){n.push({...s,status:"failed",reason:Gr(i)})}}return n}function So(e){return e.some(t=>t.status==="failed")}async function Su(e){try{let{stdout:t}=await xt(e,["--version"],{encoding:"utf-8",timeout:5e3,windowsHide:!0});return t.trim().split(/\r?\n/)[0]||null}catch{return null}}async function bu(e=[...ir(ks()),...ir(ys())],t=Su){let r=new Set,n=[];for(let s of e){let o=s;try{o=ho(s)}catch{}r.has(o)||(r.add(o),n.push({path:s,version:await t(s)}))}return n}async function bo(e){let t=e??await bu();if(t.length<=1)return{name:"Flisk installs",didPass:!0,message:"One flisk install on PATH"};let r=t.map(s=>`${s.path} (${s.version??"version unknown"})`).join(", "),n=new Set(t.map(s=>s.version));return{name:"Flisk installs",didPass:!1,isWarning:!0,message:`${t.length} flisk installs on PATH${n.size>1?" with different versions":""}: ${r}`,fix:"Keep one: remove the others, or run flisk upgrade. The first on PATH is the one your shell runs; tools that launch /usr/local/bin/flisk directly may use another"}}function To(e,t=Tt){let r=new Set([e]);try{r.add(ho(e))}catch{}let n=new Set;for(let s of r)for(let o of t(lu(s,".tdk",".tdk-out","Tiltfile")))n.add(o);return[...n]}var Vr=1;function Tu(e,t=[]){return{schemaVersion:Vr,data:e,errors:t}}function xu(e){return e instanceof D?{error:{code:"COMMAND_FAILED",message:e.message,...e.suggestions.length>0?{suggestions:e.suggestions}:{}},exitCode:e.exitCode}:{error:{code:"INTERNAL",message:e instanceof Error?e.message:String(e)},exitCode:2}}function _g(e){let t=xu(e);console.log(JSON.stringify(Tu(null,[t.error]))),console.error(t.error.message),process.exit(t.exitCode)}async function wo(e,t){let r=[],n=[],s=o=>{o.status==="fulfilled"?r.push(o.value):n.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:r,errors:n}}function xo(e){let t=e.find(r=>r.name===F);return t?t.didPass:null}function ae(e,t,r=[],n,s){return{schemaVersion:Vr,data:{ready:r.length===0&&e.every(o=>o.didPass||o.isSkipped||o.isWarning),inProject:t,checks:e,...s?{host:{...s,containerRuntimeReachable:xo(e),canUp:s.canUp&&xo(e)!==!1}}:{},...n!==void 0?{ports:{http:{requested:n?.requested.ingressHttp??80,chosen:n?.ingressHttp??null,explicit:n?.explicit.ingressHttp??!!process.env.TDK_HTTP_PORT,reason:n?.reason.ingressHttp??"port planning failed"},https:{requested:n?.requested.ingressHttps??443,chosen:n?.ingressHttps??null,explicit:n?.explicit.ingressHttps??!!process.env.TDK_HTTPS_PORT,reason:n?.reason.ingressHttps??"port planning failed"},postgres:{requested:n?.requested.postgres??5432,chosen:n?.postgres??null,explicit:n?.explicit.postgres??!!process.env.TDK_POSTGRES_PORT,reason:n?.reason.postgres??"port planning failed"}}}:{}},errors:r}}function Eo(e){return e.errors.length>0?2:e.data.ready?0:1}import{existsSync as Kr,readFileSync as vo}from"node:fs";import{dirname as wu,join as Wr,normalize as Eu}from"node:path";function vu(e){let t=new Set,r=/^load\s*\(([\s\S]*?)\)/gm;for(let n of e.matchAll(r)){let s=n[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 Po(e,t,r){return t==="ext://flisk"||t==="ext://tdk-cli"?Wr(r,".tdk",".tdk-out","tdk-cli-ext","Tiltfile"):t.startsWith(".")||!t.includes("://")&&!t.startsWith("@")?Eu(Wr(wu(e),t)):null}function Pu(e,t,r){let n=[],s=/^load\s*\(([\s\S]*?)\)/gm;for(let o of t.matchAll(s)){let c=(o[1]??"").match(/["']([^"']+)["']/)?.[1];if(!c)continue;let l=Po(e,c,r);l&&(Kr(l)||n.push(`${e}: ${c} -> ${l}`))}return n}function $u(e,t,r){let n=[],s=/^load\s*\(([\s\S]*?)\)/gm;for(let o of t.matchAll(s)){let c=(o[1]??"").match(/["']([^"']+)["']/)?.[1];if(c){let l=Po(e,c,r);l&&n.push(l)}}return n}function _u(e,t){let r=new Set,n=[e];for(;n.length>0;){let s=n.pop();if(!s||r.has(s)||!Kr(s))continue;r.add(s);let o=vo(s,"utf-8");for(let i of $u(s,o,t))r.has(i)||n.push(i)}return Array.from(r).sort()}function $o(){let e=k()??process.cwd(),t=Wr(e,".tdk",".tdk-out","Tiltfile");if(!Kr(t))return{name:"Starlark Load Exports",didPass:!0,isSkipped:!0,message:"No generated Tiltfile to check"};let r=[],n=[],s=_u(t,e);for(let i of s){let a=vo(i,"utf-8"),c=vu(a);c.length>0&&r.push(`${i}: ${c.join(", ")}`);for(let l of Pu(i,a,e))n.push(l)}if(r.length===0&&n.length===0)return{name:"Starlark Load Exports",didPass:!0,message:`${x(s.length,"reachable Starlark file")} ${s.length===1?"has":"have"} valid load exports and paths`};let o=[];return r.length>0&&o.push(`Private exported symbols:
99
- ${r.join(`
100
- `)}`),n.length>0&&o.push(`Missing relative load targets:
101
- ${n.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 Flisk (`flisk config regenerate`). If the issue remains, rename loaded symbols without leading underscores and update stale load paths."}}import{execFileSync as Gu,execSync as Vu}from"node:child_process";import{existsSync as zr,readdirSync as Vo,readFileSync as Je}from"node:fs";import{basename as Wu,dirname as Ku,join as G,relative as Ke}from"node:path";function Lg(e){return _o(e,!1)}function Mg(e){return _o(e,!0)}function _o(e,t){return`import { Hono } from 'hono';
106
- ${t?`import { sql } from 'drizzle-orm';
107
- import { db } from './db.js';
108
- `:""}
109
- const app = new Hono();
110
-
111
- // Health check endpoint (required by TILT_RESOURCE_DEFAULTS.star)
112
- app.get('/health', (c) => {
113
- return c.json({ status: 'ok', service: '${e}' });
114
- });
115
-
116
- app.get('/health/live', (c) => {
117
- return c.json({ status: 'alive', timestamp: Date.now() });
118
- });
119
-
120
- app.get('/health/ready', async (c) => {
121
- const dependencies: Record<string, string> = {};
122
- let allReady = true;
123
-
124
- // Add dependency checks here (database, cache, etc.)
125
- // Mark allReady = false if any dependency is unhealthy
126
-
127
- const status = allReady ? 'ready' : 'not_ready';
128
- return c.json({ status, dependencies }, allReady ? 200 : 503);
129
- });
130
-
131
- ${t?`// Database check: runs a trivial query through the Drizzle client (src/db.ts).
132
- app.get('/health/db', async (c) => {
133
- try {
134
- await db.execute(sql\`select 1\`);
135
- return c.json({ status: 'ok', database: 'up' });
136
- } catch (error) {
137
- console.error('Database health check failed:', error);
138
- return c.json({ status: 'error', database: 'down' }, 503);
139
- }
140
- });
141
-
142
- `:""}app.get('/', (c) => {
143
- return c.json({
144
- service: '${e}',
145
- version: '1.0.0',
146
- endpoints: ['/health', '/health/live', '/health/ready'${t?", '/health/db'":""}]
147
- });
148
- });
149
-
150
- const port = process.env.PORT || 3000;
151
- console.log('\\n\u{1F680} ${e} running on http://localhost:' + port);
152
- console.log('\u{1F4CA} Health check: http://localhost:' + port + '/health\\n');
153
-
154
- export default {
155
- port,
156
- fetch: app.fetch,
157
- };
158
- `}var Ro={id:"bun",label:"Bun + Hono",installHint:"bun install"};var Ru="go run .",Ao={id:"go",label:"Go 1.23 (net/http)",devCommand:Ru,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}
159
-
160
- go 1.23
161
- `,description:"Generating go.mod",emoji:"\u{1F4E6}"},{filename:"main.go",content:`package main
162
-
163
- import (
164
- "encoding/json"
165
- "log"
166
- "net/http"
167
- "os"
168
- )
169
-
170
- func writeJSON(w http.ResponseWriter, status int, body any) {
171
- w.Header().Set("Content-Type", "application/json")
172
- w.WriteHeader(status)
173
- _ = json.NewEncoder(w).Encode(body)
174
- }
175
-
176
- func newMux() *http.ServeMux {
177
- mux := http.NewServeMux()
178
-
179
- // Health check endpoint (required by TILT_RESOURCE_DEFAULTS.star)
180
- mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
181
- writeJSON(w, http.StatusOK, map[string]string{"status": "ok", "service": "${e}"})
182
- })
183
-
184
- mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) {
185
- writeJSON(w, http.StatusOK, map[string]any{
186
- "service": "${e}",
187
- "version": "1.0.0",
188
- "endpoints": []string{"/health"},
189
- })
190
- })
191
-
192
- return mux
193
- }
194
-
195
- func main() {
196
- port := os.Getenv("PORT")
197
- if port == "" {
198
- port = "3000"
199
- }
200
-
201
- // ":" + port listens on every interface, which Traefik needs inside the container network.
202
- log.Printf("${e} listening on :%s", port)
203
- log.Fatal(http.ListenAndServe(":"+port, newMux()))
204
- }
205
- `,description:"Generating backend source",emoji:"\u{1F4BB}"},{filename:"main_test.go",content:`package main
206
-
207
- import (
208
- "encoding/json"
209
- "net/http"
210
- "net/http/httptest"
211
- "testing"
212
- )
213
-
214
- func TestHealth(t *testing.T) {
215
- rec := httptest.NewRecorder()
216
- newMux().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/health", nil))
217
-
218
- if rec.Code != http.StatusOK {
219
- t.Fatalf("GET /health = %d, want %d", rec.Code, http.StatusOK)
220
- }
221
- }
222
-
223
- func TestRoot(t *testing.T) {
224
- rec := httptest.NewRecorder()
225
- newMux().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/", nil))
226
-
227
- if rec.Code != http.StatusOK {
228
- t.Fatalf("GET / = %d, want %d", rec.Code, http.StatusOK)
229
- }
230
- if got := rec.Header().Get("Content-Type"); got != "application/json" {
231
- t.Fatalf("GET / Content-Type = %q, want %q", got, "application/json")
232
- }
233
-
234
- var body map[string]any
235
- if err := json.NewDecoder(rec.Body).Decode(&body); err != nil {
236
- t.Fatalf("GET / body is not JSON: %v", err)
237
- }
238
- if body["service"] != "${e}" {
239
- t.Errorf("service = %v, want %q", body["service"], "${e}")
240
- }
241
- if body["version"] != "1.0.0" {
242
- t.Errorf("version = %v, want %q", body["version"], "1.0.0")
243
- }
244
- endpoints, ok := body["endpoints"].([]any)
245
- if !ok || len(endpoints) != 1 || endpoints[0] != "/health" {
246
- t.Errorf("endpoints = %v, want [/health]", body["endpoints"])
247
- }
248
- }
249
- `,description:"Generating smoke test",emoji:"\u{1F9EA}"}]}};var Au="node --watch src/index.js",Oo={id:"node",label:"Node.js 22",devCommand:Au,watch:["src/**/*"],installHint:"npm install # optional: Docker builds the image for you",createFiles(e){return[{filename:"package.json",content:`{
250
- "name": "${e}",
251
- "version": "1.0.0",
252
- "type": "module",
253
- "scripts": {
254
- "dev": "node --watch src/index.js",
255
- "start": "node src/index.js",
256
- "test": "node --test"
257
- }
258
- }
259
- `,description:"Generating package.json",emoji:"\u{1F4E6}"},{filename:"package-lock.json",content:`{
260
- "name": "${e}",
261
- "version": "1.0.0",
262
- "lockfileVersion": 3,
263
- "requires": true,
264
- "packages": {
265
- "": {
266
- "name": "${e}",
267
- "version": "1.0.0"
268
- }
269
- }
270
- }
271
- `,description:"Generating package-lock.json",emoji:"\u{1F512}"},{filename:"src/index.js",content:`import http from "node:http";
272
-
273
- const name = "${e}";
274
- const port = Number(process.env.PORT) || 3000;
275
-
276
- function sendJson(res, status, data) {
277
- res.writeHead(status, { "Content-Type": "application/json" });
278
- res.end(JSON.stringify(data));
279
- }
280
-
281
- export const server = http.createServer((req, res) => {
282
- const url = new URL(req.url, \`http://\${req.headers.host || "localhost"}\`);
283
-
284
- // Health check endpoint (required by TILT_RESOURCE_DEFAULTS.star)
285
- if (req.method === "GET" && url.pathname === "/health") {
286
- return sendJson(res, 200, { status: "ok", service: name });
287
- }
288
-
289
- if (req.method === "GET" && url.pathname === "/") {
290
- return sendJson(res, 200, {
291
- service: name,
292
- version: "1.0.0",
293
- endpoints: ["/health"],
294
- });
295
- }
296
-
297
- return sendJson(res, 404, { error: "Not Found" });
298
- });
299
-
300
- if (process.env.NODE_ENV !== "test") {
301
- // "0.0.0.0" listens on every interface, which Traefik needs inside the container network.
302
- server.listen(port, "0.0.0.0", () => {
303
- console.log(\`\\n\u{1F680} \${name} running on http://localhost:\${port}\`);
304
- console.log(\`\u{1F4CA} Health check: http://localhost:\${port}/health\\n\`);
305
- });
306
- }
307
- `,description:"Generating backend source",emoji:"\u{1F4BB}"},{filename:"tests/health.test.js",content:`import assert from "node:assert/strict";
308
- import { after, before, describe, it } from "node:test";
309
- import { server } from "../src/index.js";
310
-
311
- describe("health check", () => {
312
- let baseUrl;
313
-
314
- before((_, done) => {
315
- server.listen(0, "127.0.0.1", () => {
316
- const address = server.address();
317
- baseUrl = \`http://127.0.0.1:\${address.port}\`;
318
- done();
319
- });
320
- });
321
-
322
- after((_, done) => {
323
- server.close(done);
324
- });
325
-
326
- it("GET /health returns 200 with ok status", async () => {
327
- const res = await fetch(\`\${baseUrl}/health\`);
328
- assert.equal(res.status, 200);
329
- const body = await res.json();
330
- assert.deepEqual(body, { status: "ok", service: "${e}" });
331
- });
332
- });
333
- `,description:"Generating smoke test",emoji:"\u{1F9EA}"}]}};var Ou="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:Ou,watch:["src/**/*"],installHint:'pip install -e ".[dev]" # optional: Docker builds the image for you',createFiles(e){return[{filename:"pyproject.toml",content:`[project]
334
- name = "${e}"
335
- version = "0.0.1"
336
- requires-python = ">=3.12"
337
- dependencies = [
338
- "fastapi==0.115.6",
339
- "uvicorn[standard]==0.34.0",
340
- ]
341
-
342
- [project.optional-dependencies]
343
- dev = [
344
- "pytest==9.0.3",
345
- "httpx==0.28.1",
346
- ]
347
-
348
- [build-system]
349
- requires = ["setuptools>=68"]
350
- build-backend = "setuptools.build_meta"
351
-
352
- # Only the dependencies are installed into the image; the source is synced for live reload.
353
- [tool.setuptools]
354
- py-modules = []
355
-
356
- [tool.pytest.ini_options]
357
- pythonpath = ["src"]
358
- testpaths = ["tests"]
359
- `,description:"Generating pyproject.toml",emoji:"\u{1F4E6}"},{filename:"src/main.py",content:`import os
360
-
361
- from fastapi import FastAPI
362
-
363
- app = FastAPI(title="${e}")
364
-
365
-
366
- @app.get("/health")
367
- def health() -> dict[str, str]:
368
- return {"status": "ok", "service": "${e}"}
369
-
370
-
371
- @app.get("/")
372
- def root() -> dict[str, object]:
373
- return {"service": "${e}", "version": "1.0.0", "endpoints": ["/health"]}
374
-
375
-
376
- if __name__ == "__main__":
377
- import uvicorn
378
-
379
- uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "3000")))
380
- `,description:"Generating backend source",emoji:"\u{1F4BB}"},{filename:"tests/test_health.py",content:`from fastapi.testclient import TestClient
381
-
382
- from main import app
383
-
384
- client = TestClient(app)
385
-
386
-
387
- def test_health() -> None:
388
- response = client.get("/health")
389
- assert response.status_code == 200
390
- assert response.json() == {"status": "ok", "service": "${e}"}
391
-
392
-
393
- def test_root() -> None:
394
- response = client.get("/")
395
- assert response.status_code == 200
396
- assert response.json() == {"service": "${e}", "version": "1.0.0", "endpoints": ["/health"]}
397
- `,description:"Generating test file",emoji:"\u{1F9EA}"}]}};var Nu="cargo run",Co={id:"rust",label:"Rust (axum)",devCommand:Nu,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]
398
- name = "${e}"
399
- version = "0.1.0"
400
- edition = "2021"
401
-
402
- # The image copies the release binary by this fixed name.
403
- [[bin]]
404
- name = "app"
405
- path = "src/main.rs"
406
-
407
- [dependencies]
408
- axum = "0.8"
409
- serde_json = "1"
410
- tokio = { version = "1", features = ["macros", "rt-multi-thread", "net"] }
411
- `,description:"Generating Cargo.toml",emoji:"\u{1F4E6}"},{filename:"src/main.rs",content:`use axum::{routing::get, Json, Router};
412
- use serde_json::{json, Value};
413
-
414
- fn app() -> Router {
415
- Router::new()
416
- .route("/", get(root))
417
- // Health check endpoint (required by TILT_RESOURCE_DEFAULTS.star)
418
- .route("/health", get(health))
419
- }
420
-
421
- async fn root() -> Json<Value> {
422
- Json(json!({ "service": "${e}", "version": "1.0.0", "endpoints": ["/health"] }))
423
- }
424
-
425
- async fn health() -> Json<Value> {
426
- Json(json!({ "status": "ok", "service": "${e}" }))
427
- }
428
-
429
- #[tokio::main]
430
- async fn main() {
431
- let port = std::env::var("PORT").unwrap_or_else(|_| "3000".to_string());
432
-
433
- // 0.0.0.0 listens on every interface, which Traefik needs inside the container network.
434
- let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{port}"))
435
- .await
436
- .expect("failed to bind the port");
437
- println!("${e} listening on 0.0.0.0:{port}");
438
- axum::serve(listener, app()).await.expect("server error");
439
- }
440
-
441
- #[cfg(test)]
442
- mod tests {
443
- use super::*;
444
-
445
- #[tokio::test]
446
- async fn health_reports_ok() {
447
- let Json(body) = health().await;
448
- assert_eq!(body["status"], "ok");
449
- }
450
- }
451
- `,description:"Generating backend source and smoke test",emoji:"\u{1F4BB}"}]}};var Do="bun",$e={bun:Ro,python:No,go:Ao,rust:Co,node:Oo};function Cu(e){let t=(e??Do).trim().toLowerCase(),r=Object.hasOwn($e,t)?$e[t]:void 0;if(!r){let n=Object.keys($e).join(", ");throw new D(`Unknown backend language "${e}". Supported languages: ${n}.`,[`Use one of: ${n}`,`Omit --language to use ${Do}`])}return r}function Xg(e,t){if(e!=="backend"){if(t!==void 0)throw new D("--language can only be used with --type backend.",["Add --type backend, or drop --language for this resource type"]);return}return Cu(t)}import{exec as Du,execFile as ju}from"node:child_process";import{promisify as jo}from"node:util";var Iu=jo(Du),J=async(e,t)=>{if(process.platform==="win32"){let n=e.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)??[],[s,...o]=n.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 jo(ju)(i,o,{encoding:"utf-8",timeout:t,windowsHide:!0,shell:!1});return a}let{stdout:r}=await Iu(e,{encoding:"utf-8",timeout:t});return r};function Io(e){let t=e;return t?.code==="ETIMEDOUT"||t?.killed===!0}import{existsSync as Fu,readFileSync as Fo}from"node:fs";import{join as ce}from"node:path";var Lo=["postgres",L],Mo=["pre_alpha","alpha","beta","out_of_scope"];function Lu(e){return Lo.includes(e)}function Uo(e){try{return JSON.parse(Fo(ce(e,".tdk",A),"utf-8"))}catch{return}}function _e(e){try{return Fu(e)?Fo(e,"utf-8"):void 0}catch{return}}function Bo(e,t){let r=s=>{let o=s;for(;o<e.length&&/\s/.test(e[o]);)o+=1;return o},n=0;for(;;){let s=e.indexOf(t,n);if(s===-1)return;let o=r(s+t.length);if(e[o]==="="&&(o=r(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]??"")}n=s+t.length}}function Ho(e){let t=ce(e,".tdk",".tdk-out","Tiltfile"),r=_e(t);if(r)return Bo(r,"ALWAYS_ENABLED_INFRA")}function Mu(e){let t=ce(e,".tdk",".tdk-out","spec.master"),r=_e(t);if(r!==void 0)return/"database-management"\s*:\s*True/.test(r)}function Uu(e){let t=ce(e,".tdk",".tdk-out"),r=_e(ce(t,"Tiltfile")),n=_e(ce(t,"spec.master")),s=_e(ce(t,"tdk-cli-ext","discovery","config.star")),o=_e(ce(t,"tdk-cli-ext","engine","topologies","tilt","config","profiles.star"));if(!r||!n||!s||!o)return!1;let i=n.match(/PRE_ALPHA_RESOURCES\s*=\s*\{([^}]*)\}/)?.[1],a=Bo(s,"CORE_INFRA");return/FOCUS_MODE\s*,\s*FOCUS_ENABLED_ALL\s*,\s*FOCUS_ENABLED_RESOURCES\s*=\s*Config\.apply_focus\(cfg\)/.test(r)&&/if FOCUS_MODE and FOCUS_ENABLED_ALL\s*:/.test(r)&&/"[^"]+"\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 Bu(e){let t=Uo(e);if(!t)return!1;let r=t.phases??{};for(let s of Mo){let o=r[s]?.enabledStacks;if(Array.isArray(o)&&o.includes(L))return!0}let n=t.always_enabled_infra??Fe;return Array.isArray(n)&&n.includes(L)}function Hu(e,t){let r=new Set;for(let o of Lo)r.add(o);for(let o of t)r.add(o.name),o.stack&&r.add(o.stack);let n=Uo(e);if(n){let o=n.phases??{};for(let a of Mo){let c=o[a]?.enabledStacks;if(Array.isArray(c))for(let l of c)r.add(l)}let i=n.always_enabled_infra??Fe;if(Array.isArray(i))for(let a of i)r.add(a)}let s=Ho(e);if(Array.isArray(s))for(let o of s)r.add(o);return r}function Jr(e,t=w(e)){let r=Bu(e),n=Ho(e),s=Array.isArray(n)&&n.includes(L),o=Mu(e)===!0,i=r||s||o,a=!r&&Uu(e),c=Hu(e,t),l=[],u=[];for(let g of t){let y=g.config?.dependsOn??[],S=!1;for(let m of y){if(Lu(m)){S=!0;continue}c.has(m)||u.push({resource:g.name,name:m})}S&&l.push(g.name)}let d=i||l.length>0,p=i?"feature":l.length>0?"dependsOn":"none";return{featureOn:i,featureOnFromProjectJson:r,featureOnFromTiltfile:s,featureOnFromSpecMaster:o,focusWouldEnableDatabaseManagement:a,dependsOnUsers:l,willStart:d,reason:p,unknownDependsOnNames:u}}var Et=1e4,Ju=/\.(?:[cm]?[jt]sx?|vue)$/,qu=/(?:\.test\.|\.spec\.|\/tests?\/|\/__tests__\/)/,zu=7,Yu=new Set(["node_modules",I,"dist","tests","__tests__"]);function Wo(e){try{let t=JSON.parse(Je(G(e,".tdk",A),"utf-8"));return typeof t?.project?.name=="string"?t.project.name:void 0}catch{return}}function Xu(e){try{let t=JSON.parse(Je(e,"utf-8")).params;return t&&typeof t=="object"?t:{}}catch{return{}}}function qr(e){try{let t=JSON.parse(Je(G(e,O),"utf-8"));return t&&typeof t=="object"?t:{}}catch{return{}}}function Go(e){if(typeof e!="string")return;let t=e.match(/(?:^|[^0-9])(\d+)(?:\.|$)/);return t?Number(t[1]):void 0}function Qu(e){let t=Pt(e);if(t===void 0)return["missing prisma/schema.prisma"];let r=t.replace(/\/\*[\s\S]*?\*\//g,"").replace(/^\s*\/\/.*$/gm,""),n=[],s=r.match(/datasource\s+\w+\s*\{([^}]*)\}/)?.[1]??"";return/provider\s*=\s*["']postgresql["']/.test(s)||n.push("schema provider is not postgresql"),/\b(?:url|directUrl)\s*=/.test(r)&&n.push("schema still declares url/directUrl"),n}function Zu(e){let t=G(e,I,"prisma.config.autogenerated.ts"),n=[G(e,"prisma.config.ts"),t].find(i=>zr(i));if(!n)return["missing prisma.config.ts or generated Prisma config"];let o=(Pt(n)??"").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=k()??process.cwd(),t=w(e)){let r=t.filter(o=>o.config?.featuresEnabled?.includes("prisma")),n=[],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")&&n.push(`${o.name}: use featuresEnabled, not features, for Prisma`)}if(r.length===0)return n.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:`${x(n.length,"Prisma consistency error")}:
452
- ${n.join(`
453
- `)}`,fix:"Use featuresEnabled to opt into the validated Prisma 7 shape"};for(let o of r){let i=qr(o.path).dependencies,c={...qr(o.path).devDependencies??{},...i??{}},l=Go(c.prisma),u=Go(c["@prisma/client"]);if(l===void 0||u===void 0?n.push(`${o.name}: prisma and @prisma/client are required`):(l!==u||l!==zu)&&n.push(`${o.name}: prisma packages must both be major 7`),n.push(...Qu(G(o.path,"prisma","schema.prisma")).map(p=>`${o.name}: ${p}`)),n.push(...Zu(o.path).map(p=>`${o.name}: ${p}`)),(o.config?.appType??o.type)==="migrator")o.config?.dependsOn?.some(g=>g==="postgres"||g===L)||n.push(`${o.name}: migrator must depend on postgres or database-management`);else{o.config?.dependsOn?.some(S=>s.some(m=>m.name===S))||n.push(`${o.name}: API must depend on its Prisma migrator, which depends on postgres or database-management`);let g=qr(o.path).scripts;Object.entries(g??{}).some(([S,m])=>ti.has(S)&&typeof m=="string"&&/prisma\s+migrate/.test(m))&&n.push(`${o.name}: API start script must not run prisma migrate`);let y=G(o.path,o.config?.dockerfile??"Dockerfile");wt(y,!0)&&n.push(`${o.name}: API image/startup must not run Prisma migrations`)}}return r.length>0&&!Jr(e,t).willStart&&n.push("shared platform Postgres will not start for the Prisma resources"),n.length===0?{name:"Prisma 7 consistency",didPass:!0,message:"Prisma resources use the validated Prisma 7 shape"}:{name:"Prisma 7 consistency",didPass:!1,message:`${x(n.length,"Prisma consistency error")}:
454
- ${n.join(`
455
- `)}`,fix:"Use Prisma 7 packages and config, put migrations in a migrator, and make the API depend on that migrator"}}function Jo(e){let t=new Map;for(let r of e){let n=r.config?.port;M(r.config?.appType)&&typeof n=="number"&&t.set(r.name,n)}return t}function ed(e){let t=e.config?.language;return typeof t=="string"&&Object.hasOwn($e,t)&&$e[t].createFiles!==void 0}function td(e){return(e.config?.appType??e.type)===Y}function qo(e=k()??process.cwd()){let t=w(e).filter(r=>!td(r)&&!ed(r)&&!zr(G(r.path,O))).map(r=>r.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:`${x(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 vt(e=k()??process.cwd(),t=w(e)){let r=new Map;for(let o of t){let i=r.get(o.name)??[];i.push(Ke(e,o.configPath)||o.configPath),r.set(o.name,i)}let n=[...r].filter(([,o])=>o.length>1);if(n.length===0)return{name:"Duplicate resource names",didPass:!0,message:"Every resource has a unique appName"};let s=n.map(([o,i])=>`"${o}" in ${i.join(" and ")}`);return{name:"Duplicate resource names",didPass:!1,message:`${x(n.length,"appName")} used by more than one resource:
456
- ${s.join(`
457
- `)}`,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 rd(e){let t=e.config?.appType;return M(t)?!0:t===Y&&e.config?.exposeViaProxy!==!1}function zo(e=k()??process.cwd()){let t=new Map;for(let s of w(e)){let o=s.config?.port;if(typeof o!="number"||!rd(s))continue;let i=t.get(o)??[];i.push(s.name),t.set(o,i)}let r=[...t].filter(([,s])=>s.length>1);if(r.length===0)return{name:"Duplicate resource ports",didPass:!0,message:"No two routed resources use the same port"};let n=r.map(([s,o])=>`port ${s}: ${o.join(", ")}`);return{name:"Duplicate resource ports",didPass:!1,isWarning:!0,message:`${x(r.length,"port")} used by more than one routed resource:
458
- ${n.join(`
459
- `)}`,fix:'Usually a copied service.json. Each container can listen on the same port, but pick distinct "port" values (flisk resource assigns free ones) so URLs like http://<resource>:<port> and localhost checks stay unambiguous'}}var nd=/https?:\/\/([a-z][a-z0-9-]*):(\d+)/g;function Yo(e=k()??process.cwd()){let t=w(e),r=Jo(t),n=new Map([...r].map(([o,i])=>[i,o])),s=[];for(let o of t)for(let[i,a]of Object.entries(Xu(o.configPath)))for(let[,c,l]of String(a).matchAll(nd)){let u=Number(l),d=r.get(c);d!==void 0&&d!==u?s.push(`${o.name} param ${i}: ${c}:${u}, but ${c} listens on ${d}`):(c==="localhost"||c==="127.0.0.1")&&n.has(u)&&n.get(u)!==o.name&&s.push(`${o.name} param ${i}: localhost:${u} is ${o.name} itself inside its container; ${n.get(u)} is reached as ${n.get(u)}:${u}`)}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:`${x(s.length,"service URL")} in params that cannot connect:
460
- ${s.join(`
461
- `)}`,fix:'Flisk 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 Yr(e){let t=[],r;try{r=Vo(e,{withFileTypes:!0})}catch{return t}for(let n of r){let s=G(e,n.name);n.isDirectory()?Yu.has(n.name)||t.push(...Yr(s)):Ju.test(n.name)&&!qu.test(s)&&t.push(s)}return t}var sd=/(?:localhost|127\.0\.0\.1):(\d{2,5})\b/g,Xo=/^\s*(?:\/\/|\/\*|\*|<!--|#)/;function Qo(e=k()??process.cwd()){let t=new Map;for(let n of w(e)){let s=n.config;if(!(M(s?.appType)||s?.appType===Y&&s.exposeViaProxy!==!1))continue;let i=Ku(n.path),a=s?.stack??Wu(i),c=`${i}\0${a}`,l=t.get(c)??{stack:a,resources:[]};l.resources.push(n),t.set(c,l)}let r=[];for(let{stack:n,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&&r.push(`stack "${n}": ${i.sort().join(", ")} lose the shared /api/${n}-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 l of s){let u=l.config?.traefik?.[a];u&&c.set(u,[...c.get(u)??[],l.name])}for(let[l,u]of c)u.length>1&&r.push(`stack "${n}": ${u.sort().join(" and ")} both set traefik.${a} "${l}", so Traefik cannot tell them apart`)}}return r.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:r.join(`
462
- `),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 Zo(e=k()??process.cwd()){let t=w(e),r=new Map([...Jo(t)].map(([o,i])=>[i,o])),n=[];for(let o of t.filter(i=>i.config?.appType==="frontend"))for(let i of Yr(G(o.path,"src")))Je(i,"utf-8").split(`
463
- `).forEach((a,c)=>{if(!Xo.test(a))for(let[,l]of a.matchAll(sd)){let u=r.get(Number(l));u&&n.push(`${Ke(e,i)}:${c+1} calls localhost:${l} (${u})`)}});if(n.length===0)return{name:"Frontend backend URLs",didPass:!0,message:"No frontend calls a backend through a localhost port"};let s=Wo(e)??"<project>";return{name:"Frontend backend URLs",didPass:!1,message:`${x(n.length,"frontend call")} to a backend port that Flisk does not publish on localhost:
464
- ${n.join(`
465
- `)}`,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 ei=/\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/,ti=new Set(["start","dev","serve","prestart","predev","preserve"]),od=/\.(?:sh|bash)$/,id=/^\s*(?:CMD|ENTRYPOINT)\b/i;function Pt(e){try{return Je(e,"utf-8")}catch{return}}function ad(e){let t=Pt(G(e,O));if(t===void 0)return[];let r={};try{r=JSON.parse(t)?.scripts??{}}catch{return[]}return Object.entries(r).filter(([n,s])=>ti.has(n)&&typeof s=="string"&&ei.test(s)).map(([n])=>`package.json "${n}" script`)}function wt(e,t){let r=Pt(e);return r===void 0?!1:r.split(`
466
- `).some(n=>ei.test(n.replace(/["'`,[\]]+/g," "))&&!Xo.test(n)&&(!t||id.test(n)))}function ri(e=k()??process.cwd()){let t=[];for(let r of w(e)){let n=r.config?.appType??r.type;if(n!=="backend"&&n!=="worker")continue;let s=r.path;for(let a of ad(s))t.push(`${r.name}: ${a}`);let o=G(s,r.config?.dockerfile??"Dockerfile");wt(o,!0)&&t.push(`${r.name}: ${Ke(e,o)} CMD or ENTRYPOINT`);let i=[];try{i=Vo(s,{withFileTypes:!0})}catch{}for(let a of i){let c=G(s,a.name);a.isFile()&&od.test(a.name)&&wt(c,!1)&&t.push(`${r.name}: ${Ke(e,c)}`)}for(let a of Yr(G(s,"src")))wt(a,!1)&&t.push(`${r.name}: ${Ke(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:`${x(t.length,"migration command")} run from a backend or worker's own start-up:
467
- ${t.join(`
468
- `)}`,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 $t(e=k()??process.cwd()){let t=w(e).filter(n=>n.config?.featuresEnabled?.includes("nats")).map(n=>n.name);if(t.length===0)return{name:"NATS broker",didPass:!0,message:"No resource enables the nats feature",isSkipped:!0};if(zr(G(e,"services","platform","messaging",de)))return{name:"NATS broker",didPass:!0,message:`${x(t.length,"resource")} enable nats and services/platform/messaging/docker-compose.yml is present`};let r=(Wo(e)??"project").replace(/-/g,"_");return{name:"NATS broker",didPass:!1,message:`${t.join(", ")} enable the nats feature, but no NATS server will start: Flisk 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 ${r}_nats, network ${r}_backend, external) and a "redis" service, which Flisk registers alongside it`}}function ni(e=k()??process.cwd()){let t=Jr(e),r=" (project resource set; flisk up may select a subset and not start Postgres)",n=t.focusWouldEnableDatabaseManagement?" Warning: generated Tiltfile default focus expands CORE_INFRA to database-management while project.json has it off; default flisk 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`}${r}`:" Postgres will not start for this project.json/generated Tiltfile state.";return{name:"Shared platform Postgres",didPass:!1,message:`${x(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}${n}`,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:!!n,isSkipped:!n,message:n?n.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:!!n,message:`Shared platform Postgres will start because the database-management feature is enabled${o} (project-wide; not a flisk up selection).${n}`}}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:!!n,message:`Shared platform Postgres will start because ${s}${r}${n}`}}function cd(e){let t=[];for(let r of e.split(`
469
- `)){let n=r.match(/^\s*(\d+)\s+.*\btilt up\b.*?\s-f\s+(\S+)/i);n&&t.push({pid:Number(n[1]),root:n[2].split(/[\\/]\.tdk[\\/]/i)[0]})}return t}function si(e=k()??process.cwd(),t=Vu){let r;try{r=process.platform==="win32"?Gu("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:Et,windowsHide:!0}):t("ps -Ao pid=,command=",{encoding:"utf-8",timeout:Et})}catch{return{name:"Tilt processes",didPass:!0,isSkipped:!0,message:"Could not list processes"}}let n=cd(r),s=n.filter(i=>i.root===e),o=n.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: flisk down && kill ${s.map(i=>i.pid).join(" ")} && flisk 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:"flisk up will use the next free Tilt UI port (10351, ...). If it is a leftover, run flisk down in that project"}:{name:"Tilt processes",didPass:!0,message:"No stray Tilt processes"}}async function oi(e=J){let t=`flisk_doctor_probe_${process.pid}`;try{await e(`docker network create ${t}`,Et)}catch(r){let n=String(r.stderr??r);return/address pools/i.test(n)?{name:"Docker networks",didPass:!1,message:"Docker cannot create another network: all predefined address pools are in use. Each Flisk project needs several",fix:"run flisk 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}`,Et)}catch{}return{name:"Docker networks",didPass:!0,message:"Docker can create networks"}}import{execFileSync as ai}from"node:child_process";import{existsSync as md,mkdirSync as hd,readFileSync as ci,realpathSync as gd,writeFileSync as kd}from"node:fs";import{join as qe,resolve as yd,sep as Sd}from"node:path";import{createServer as ud}from"node:net";import{connect as ld}from"node:net";function ii(e,t=500){return new Promise(r=>{let n=ld({port:e,host:"127.0.0.1"}),s=o=>{n.destroy(),r(o)};n.setTimeout(t,()=>s(!1)),n.once("connect",()=>s(!0)),n.once("error",()=>s(!1))})}var dd={ingressHttp:{start:8080,end:8180},ingressHttps:{start:8443,end:8543},postgres:{start:15432,end:15532}},_t={ingressHttp:"TDK_HTTP_PORT",ingressHttps:"TDK_HTTPS_PORT",postgres:"TDK_POSTGRES_PORT"},Xr={ingressHttp:80,ingressHttps:443,postgres:5432},Qr={ingressHttp:Xr.ingressHttp,ingressHttps:Xr.ingressHttps};function pd(e,t){return e==="EACCES"&&t==="darwin"}async function Zr(e){return await ii(e)?!1:new Promise(t=>{let r=ud();r.once("error",n=>{t(pd(n.code,process.platform))}),r.listen(e,"127.0.0.1",()=>r.close(()=>t(!0)))})}function fd(e,t){let r=Number(t);if(!Number.isInteger(r)||r<1||r>65535)throw new Error(`${e} must be an integer between 1 and 65535 (received ${JSON.stringify(t)})`);return r}async function Re(e={}){let t=e.env??process.env,r=e.isAvailable??Zr,n={ingressHttp:!1,ingressHttps:!1,postgres:!1},s={ingressHttp:"",ingressHttps:"",postgres:""},o={explicit:n},i=new Set;for(let a of["ingressHttp","ingressHttps","postgres"]){let c=t[_t[a]],l=c!==void 0&&c.trim()!=="",u=e.ranges?.[a]??dd[a],d=Qr[a],p=l?[fd(_t[a],c)]:[...d===void 0?[]:[d],...Array.from({length:u.end-u.start+1},(y,S)=>u.start+S)],g;for(let y of p)if(!i.has(y)){if(await r(y)){g=y;break}if(l)throw new Error(`${_t[a]}=${y} is already in use. Choose a free host port and retry.`)}if(g===void 0)throw new Error(`No available host port in ${u.start}-${u.end} for ${a}. Set ${_t[a]} to a free port and retry.`);i.add(g),o[a]=g,n[a]=l,s[a]=l?"explicit override":g===d?"requested port is free":`selected from fallback range ${u.start}-${u.end}`}return{...o,requested:{...Xr},reason:s}}function vk(e){return`Host ports: HTTP ${e.ingressHttp}, HTTPS ${e.ingressHttps}, Postgres ${e.postgres}`}var li=qe(".tdk",".tdk-out","host-ports.json");function bd(e,t,r){return e.split(`
470
- `).some(n=>{let[s="",o=""]=n.split("|"),i=s.toLowerCase(),a=t.toLowerCase();return(i.startsWith(`${a}_`)||i.startsWith(`${a}-`))&&o.includes(`:${r}->`)})}function ui(e){let t=qe(e,li);if(!md(t))return null;try{let r=JSON.parse(ci(t,"utf-8"));if(!r||typeof r!="object")return null;let n=r;return!Number.isInteger(n.ingressHttp)||!Number.isInteger(n.ingressHttps)||!Number.isInteger(n.postgres)||!n.requested||!n.explicit||!n.reason?null:n}catch{return null}}function Nk(e,t){let r=qe(e,li);hd(qe(e,".tdk",".tdk-out"),{recursive:!0}),kd(r,`${JSON.stringify(t,null,2)}
471
- `,{encoding:"utf-8",mode:384})}function Ck(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 Td="com.docker.compose.project.working_dir";function xd(e,t,r){let n=new Set([yd(t)]);try{n.add(gd(t))}catch{}return e.split(`
472
- `).some(s=>{let[,o="",i=""]=s.split("|");return!i||!o.includes(`:${r}->`)?!1:[...n].some(a=>i===a||i.startsWith(`${a}${Sd}`))})}function di(e,t,r=()=>ai("docker",["ps","--format",`{{.Names}}|{{.Ports}}|{{.Label "${Td}"}}`],{encoding:"utf-8",timeout:5e3})){let n=ui(e);if(!n||n.ingressHttp===t)return t;try{if(xd(r(),e,n.ingressHttp))return n.ingressHttp}catch{}return t}async function Rt(e,t={}){let r=ui(e),n=["TDK_HTTP_PORT","TDK_HTTPS_PORT","TDK_POSTGRES_PORT"].some(l=>process.env[l]);if(t.inspectDocker===!1&&r&&!n)return r;let s="";try{let l=JSON.parse(ci(qe(e,".tdk",A),"utf-8"));s=String(l?.project?.name??"tdk-project").replace(/-/g,"_").toLowerCase()}catch{s="tdk_project"}let o="";if(t.inspectDocker!==!1)try{o=ai("docker",["ps","--format","{{.Names}}|{{.Ports}}"],{encoding:"utf-8",timeout:5e3})}catch{}let i=l=>bd(o,s,l),a=t.isPortFree??Zr,c=async l=>await a(l)||i(l);if(r&&!n){for(let l of[r.ingressHttp,r.ingressHttps,r.postgres])if(!await c(l))return Re({isAvailable:c});for(let[l,u]of Object.entries(Qr)){let d=r[l];if(u!==void 0&&d!==u&&await c(u))return Re({isAvailable:c})}return r}return Re({isAvailable:c})}import{stdin as wd,stdout as Ed}from"node:process";import{createInterface as vd}from"node:readline/promises";async function At(e){let t=vd({input:wd,output:Ed});try{return await new Promise((r,n)=>{t.once("close",()=>n(new Error("No input available to answer the prompt. Run this in a terminal."))),t.question(e).then(s=>r(s.trim()),n)})}finally{t.close()}}async function Fk(e){let t=e.initial?` (${e.initial})`:"";for(;;){let n=await At(`${e.message}${t} `)||e.initial||"",s=e.validate?.(n)??!0;if(s===!0)return n;console.log(s)}}async function pi(e){let t=e.initial??!0,r=t?"Y/n":"y/N";for(;;){let n=(await At(`${e.message} (${r}) `)).toLowerCase();if(!n)return t;if(["y","yes"].includes(n))return!0;if(["n","no"].includes(n))return!1;console.log("Please answer yes or no.")}}async function Lk(e){for(console.log(e.message),e.choices.forEach((t,r)=>{console.log(` ${r+1}. ${t.title}`)});;){let t=await At("Choose one: "),r=Number.parseInt(t,10)-1,n=e.choices[r];if(n)return n.value;console.log(`Enter a number from 1 to ${e.choices.length}.`)}}async function Mk(e){let t=e.choices.map((n,s)=>n.selected?s+1:null).filter(n=>n!==null),r=t.length>0?` [${t.join(",")}]`:"";for(console.log(e.message),e.choices.forEach((n,s)=>{let o=n.selected?"*":" ";console.log(` ${o} ${s+1}. ${n.title}`)});;){let n=await At(`Choose one or more numbers, comma-separated${r}: `),s=t;if(n){let a=n.split(",").map(l=>l.trim()),c=a.find(l=>{if(!/^\d+$/.test(l))return!0;let u=Number.parseInt(l,10);return!e.choices[u-1]});if(c!==void 0){console.log(`"${c}" is not a valid choice (1-${e.choices.length})`);continue}s=a.map(l=>Number.parseInt(l,10))}let 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 Pd}from"node:path";function Ot(e,t){return Pd(e,t.configPath)||t.configPath}function fi(e){let t=e.config?.dependsOn;return t===void 0?[]:Array.isArray(t)&&t.every(r=>typeof r=="string")?t:null}function $d(e){let t=new Set(e.map(a=>a.name)),r=new Map(e.map(a=>[a.name,(fi(a)??[]).filter(c=>t.has(c))])),n=new Map,s=new Map,o=[],i=a=>{s.set(a,"visiting"),o.push(a);for(let c of r.get(a)??[])if(s.get(c)==="visiting"){let l=o.slice(o.indexOf(c)),u=l.indexOf([...l].sort()[0]??c),d=[...l.slice(u),...l.slice(0,u)];n.set(d.join(">"),[...d,d[0]??c])}else s.has(c)||i(c);o.pop(),s.set(a,"done")};for(let a of[...r.keys()].sort())s.has(a)||i(a);return[...n.values()]}function en(e=k()??process.cwd(),t=w(e)){let r=t.flatMap(n=>fi(n)===null?[`${Ot(e,n)} dependsOn ${JSON.stringify(n.config?.dependsOn)}: expected an array of service names`]:[]);return r.length===0?{name:"dependsOn",didPass:!0,message:"Every dependsOn is a list of service names"}:{name:"dependsOn",didPass:!1,message:`${x(r.length,"invalid dependsOn","invalid dependsOn")}:
473
- ${r.join(`
474
- `)}`,fix:'Write "dependsOn" as an array of service names, for example ["postgres", "api"]'}}function tn(e=k()??process.cwd(),t=w(e)){let r=new Map(t.map(o=>[o.name,o])),n=$d(t);if(n.length===0)return{name:"Circular dependencies",didPass:!0,message:"No service depends on itself through dependsOn"};let s=n.map(o=>{let i=r.get(o[0]??""),a=o[1]??"";return`${i?Ot(e,i):o[0]} dependsOn "${a}": circular dependency ${o.join(" -> ")}`});return{name:"Circular dependencies",didPass:!1,message:`${n.length} ${n.length===1?"circular dependency":"circular dependencies"}:
475
- ${s.join(`
476
- `)}`,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 rn(e=k()??process.cwd(),t=w(e)){let r=t.flatMap(n=>{let s=n.config?.port;return s===void 0?[]:typeof s=="number"&&Number.isInteger(s)&&s>=1&&s<=65535?[]:[`${Ot(e,n)} port ${JSON.stringify(s)}: not an integer from 1 to 65535`]});return r.length===0?{name:"Service ports",didPass:!0,message:"Every declared port is valid"}:{name:"Service ports",didPass:!1,message:`${x(r.length,"invalid port")}:
477
- ${r.join(`
478
- `)}`,fix:'Set "port" to the port the service listens on, an integer from 1 to 65535'}}function nn(e=k()??process.cwd(),t=!1,r=w(e)){let n=[],s=[];for(let i of r){let a=i.config?.schemaVersion;if(a===Me)continue;let c=Ot(e,i);a===void 0?n.push(`${c} schemaVersion: missing`):s.push(`${c} schemaVersion ${JSON.stringify(a)}: this flisk supports ${Me}`)}if(n.length===0&&s.length===0)return{name:"schemaVersion",didPass:!0,message:"Every service.json has a supported schemaVersion"};let o=[...n.length>0?["Run `flisk config migrate` to add the missing schemaVersion"]:[],...s.length>0?["A version this flisk does not support cannot be migrated: upgrade flisk"]:[]];return{name:"schemaVersion",didPass:!1,isWarning:!t,message:`${x(n.length+s.length,"service.json file")} without a supported schemaVersion:
479
- ${[...n,...s].join(`
480
- `)}`,fix:o.join(". ")}}function qk(e,t){let r=nn(e,!1,t);r.didPass||(console.warn(f.yellow(`\u26A0 ${r.message}`)),r.fix&&console.warn(f.gray(` ${r.fix}`)))}function zk(e,t={},r=process.exit){let n=t.resources??w(e),s=[vt(e,n),en(e,n),tn(e,n),rn(e,n)].filter(i=>!i.didPass);if(s.length===0)return;let o=s.map(i=>`${i.message}${i.fix?`
481
- Fix: ${i.fix}`:""}`).join(`
482
- `);t.onInvalid?.(o);for(let i of s)console.error(f.red(`\u2717 ${i.message}`)),i.fix&&console.error(f.gray(` Fix: ${i.fix}`));r(1)}import{readFileSync as _d}from"node:fs";import{join as Rd}from"node:path";function mi(e){let t=e.trim().match(/(\d+)\.(\d+)(?:\.(\d+))?/);return t?[Number(t[1]),Number(t[2]),Number(t[3]??0)]:null}function Ad(e){try{return JSON.parse(_d(Rd(e,".tdk",A),"utf-8"))?.minTdkVersion}catch{return}}function hi(e,t){let r=Ad(e);if(r===void 0)return{status:"none"};let n=typeof r=="string"?mi(r):null;if(!n||!/^\d+\.\d+\.\d+$/.test(String(r).trim()))return{status:"malformed",message:`.tdk/project.json minTdkVersion ${JSON.stringify(r)} is not a version like "1.3.80"`,fix:'Set minTdkVersion to a MAJOR.MINOR.PATCH string, for example "1.3.80".'};let s=mi(t),o=s!==null&&(()=>{for(let[a,c]of n.entries()){let l=s[a]??0;if(l!==c)return l>c}return!0})(),i=n.join(".");return o?{status:"ok",required:i}:{status:"too-old",required:i,message:`flisk ${t} is older than the ${i} this project requires (minTdkVersion)`,fix:"Run: flisk upgrade"}}var ee=1e4,Z={dockerMissing:"See https://docs.docker.com/get-docker/",dockerDaemonDown:"Start Docker Desktop, OrbStack, or Colima, then retry: flisk 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: flisk doctor",port5432:"Stop local Postgres or change the host port. Then: flisk doctor",notProject:"flisk project --yes"},ki="WSL2 detected. Use Docker Desktop WSL integration. Guide: docs/wsl2.md",yi="Native Windows landscape boot is unsupported. Use WSL2 Ubuntu with Docker Desktop integration. Guide: docs/wsl2.md",sn=[0,25,0],me=[1,2,0];function Nt(e,t){let r=dn(e);return r?pn(r,t):!1}function Dd(e,t,r=process.platform==="linux"&&!!process.env.WSL_DISTRO_NAME){if(!r)return{name:"WSL project location",didPass:!0,isSkipped:!0,message:"Not running in WSL2 - skipped project filesystem check"};let n=e.replace(/\\/g,"/").replace(/\/+$/,"").toLowerCase();if(n!=="/mnt/c"&&!n.startsWith("/mnt/c/"))return{name:"WSL project location",didPass:!0,message:`${ki} Project is on the WSL filesystem.`};let s=`${ki} 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 flisk doctor."}}function jd(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 Id(e=process.env,t=!1){let r=(e.TDK_BIND_ADDRESS??"").trim();return jd(r)?{name:"Dev Port Bind Address",didPass:!t,isWarning:!t,message:`TDK_BIND_ADDRESS is ${r}: 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:r?`Dev ports bind to ${r}`:"Dev ports bind to 127.0.0.1 (this machine only)"}}function Ti(e){let t=e.filter(n=>!n.didPass&&!n.isSkipped&&!n.isWarning),r=e.filter(n=>n.didPass||n.isSkipped||n.isWarning);return t.sort((n,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(n)-o(s)}),[...t,...r]}function Fd(e,t){return t?e?"Doctor passed. Next: flisk up":"Doctor passed. Next: flisk project example":"Doctor failed. Fix the items above, then run: flisk doctor"}var un={checkDockerOperatingSystem:"Docker OS",checkDockerCompose:"Docker Compose",checkDockerVersions:"Docker Versions",checkDockerNetworkCapacity:"Docker Network Pool",checkContainerResourceHealth:"Container Resource Health",checkCrashLoopingContainers:"Crash-looping containers",checkTiltResourceHealth:"Tilt Resources",checkServiceHealth:"Service Health",checkServiceSmoke:"Service Smoke",checkAdvertisedEndpoints:"Advertised Endpoints"},Si=new Set(["Docker OS","Docker Compose","Docker Versions","Docker Network Pool","Container Resource Health","Crash-looping containers","Tilt Resources","Service Health","Service Smoke","Advertised Endpoints",...Object.keys(un)]);function Ld(e){return!!(e.requiresContainerRuntime||e.checkName&&Si.has(e.checkName)||e.name&&Si.has(e.name))}function Q(e,t){let r=()=>t();return r.requiresContainerRuntime=!0,r.checkName=e,r}async function Md(e,t={}){let r=[],n=!!t.containerRuntimeFailed;for(let s of e){if(n&&Ld(s)){let i=s.checkName??(s.name&&un[s.name]?un[s.name]:s.name&&s.name!=="check"&&s.name!=="fn"?s.name:"Container check");r.push({name:i,didPass:!0,isSkipped:!0,message:`Docker not running - skipped ${i} check`});continue}let o=await s();if(r.push(o),o.name===F&&!o.didPass&&(n=!0),!t.all&&!o.didPass&&!o.isSkipped&&!o.isWarning)break}return r}function Ud(e){let t=0,r=0,n=0,s=0;for(let o of e)o.isSkipped?n++:o.isWarning?s++:o.didPass?t++:r++;return s>0?`${t} passed, ${s} warned, ${r} failed, ${n} skipped`:`${t} passed, ${r} failed, ${n} skipped`}function mn(e){return Io(e)}function Bd(e,t,r,n,s){return async(o=J)=>{try{return await o(t,ee),{name:e,didPass:!0,message:r}}catch(i){return mn(i)?{name:e,didPass:!1,message:`${e} did not respond within ${ee/1e3}s`,fix:s}:{name:e,didPass:!1,message:n,fix:s}}}}async function on(e){try{return await J(e,ee),!0}catch{return!1}}async function hn(){if(process.platform==="win32")try{return await J("docker ps",ee),{name:F,didPass:!0,message:"Docker Desktop Linux container engine is running"}}catch{return{name:F,didPass:!1,message:"Docker is not running",fix:Z.dockerDaemonDown}}let e=!!N("docker");try{return await J("docker ps",ee),{name:F,didPass:!0,message:"Docker daemon is running"}}catch(r){if(mn(r))return{name:F,didPass:!1,message:"Docker daemon is not responding",fix:Z.dockerDaemonDown}}let t=po(await on("pgrep -f com.docker.backend"));return t||(await on("colima status")?{name:F,didPass:!0,message:"Colima (Docker runtime) is running"}:N("colima")?{name:F,didPass:!1,message:"Colima is installed but not running",fix:Z.dockerDaemonDown}:await on("podman ps")?{name:F,didPass:!0,message:"Podman is running"}:e?{name:F,didPass:!1,message:"Docker daemon is not running",fix:Z.dockerDaemonDown}:{name:F,didPass:!1,message:"Docker is not running",fix:Z.dockerMissing})}var ze=Bd("Docker Compose","docker compose version","Docker Compose plugin available","Docker Compose plugin not found","Install Docker Compose: https://docs.docker.com/compose/install/");ze.requiresContainerRuntime=!0;ze.checkName="Docker Compose";var Hd=[25,0,0],Gd=[2,20,2];function dn(e){let t=e.trim().match(/(\d+)\.(\d+)(?:\.(\d+))?/);return t?[Number(t[1]),Number(t[2]),Number(t[3]??0)]:null}function pn(e,t){for(let[r,n]of t.entries()){let s=e[r]??0;if(s!==n)return s>n}return!0}async function Ye(e=J){let t=async a=>String(await e(a,ee)).trim(),r,n;try{[r,n]=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=dn(r),o=dn(n);if(!s||!o)return{name:"Docker Versions",didPass:!0,isSkipped:!0,message:`Unrecognized Docker version output (engine "${r}", compose "${n}") - skipped version check`};let i=[];return pn(s,Hd)||i.push(`Docker Engine ${r} (need 25.0+)`),pn(o,Gd)||i.push(`Docker Compose ${n} (need 2.20.2+)`),i.length===0?{name:"Docker Versions",didPass:!0,message:`Docker Engine ${r} and Compose ${n} support generated healthchecks`}:{name:"Docker Versions",didPass:!1,message:`Docker is too old for generated healthchecks (they use start_interval):
483
- ${i.join(`
484
- `)}`,fix:"Update Docker Desktop, or on Linux update docker-ce and the docker-compose-plugin package"}}Ye.requiresContainerRuntime=!0;Ye.checkName="Docker Versions";async function gn(e=J){let t;try{t=String(await e("tilt version",ee)).trim()}catch{return{name:"Tilt CLI",didPass:!1,message:"Tilt CLI not found",fix:Z.tiltMissing}}return Nt(t,sn)?{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${sn.join(".")} floor`,fix:`Install Tilt v${sn.join(".")} or newer: https://docs.tilt.dev/install.html`}}async function kn(e=J){let t;try{t=String(await e("docker info --format '{{.OSType}}'",ee)).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; Flisk requires Linux containers`,fix:"Switch Docker Desktop to Linux containers, enable WSL2 integration, then retry flisk doctor."}:{name:"Docker OS",didPass:!0,message:"Docker is using Linux containers"}}kn.requiresContainerRuntime=!0;kn.checkName="Docker OS";async function Vd(e=J){let t=k();if(!t)return{name:"Bun",didPass:!0,isSkipped:!0,message:"Not in a project - skipping Bun check"};if(!yt(w(t)))return{name:"Bun",didPass:!0,isSkipped:!0,message:"No generated JS services - skipping Bun check"};try{let r=String(await e("bun --version",ee)).trim();return Nt(r,me)?{name:"Bun",didPass:!0,message:`Bun ${r} is available`}:{name:"Bun",didPass:!1,message:`Bun ${r||"version unknown"} is below the required ${me.join(".")} floor`,fix:`Install Bun ${me.join(".")} or newer: https://bun.sh/install`}}catch{return{name:"Bun",didPass:!1,message:"Bun is not available",fix:Z.bunMissing}}}function Wd(){let e=k()??process.cwd(),t=Vn(e);if(t.missing.length>0)return{name:"Environment Variables",didPass:!1,message:`Missing required env variables: ${t.missing.join(", ")}`,fix:V(C(e,".env"))?`Edit .env and set these values:
485
- ${t.missing.map(n=>`${n}=<value>`).join(`
486
- `)}`:"No .env file yet. Run `flisk 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 r=t.warnings.filter(n=>!n.startsWith(".env file not found"));return{name:"Environment Variables",didPass:!0,message:r.length>0?`All required environment variables are set (warning: ${r.join("; ")})`:"All required environment variables are set"}}function Kd(){let e=k()??process.cwd(),t=C(e,".tdk",".tdk-out"),r=Ht.filter(n=>!V(C(t,n)));return r.length===0?{name:"Master Configs",didPass:!0,message:`${Ht.join(", ")} found`}:{name:"Master Configs",didPass:!1,message:`Master configs missing: ${r.join(", ")}`,fix:"Run: flisk project"}}function Jd(e=qn()){let t=k();if(!t)return{name:"Flisk version",didPass:!0,isSkipped:!0,message:"Not in a project - skipping Flisk version check"};let r=hi(t,e);return r.status==="none"?{name:"Flisk version",didPass:!0,isSkipped:!0,message:`flisk ${e} (no minTdkVersion in .tdk/project.json)`}:r.status==="malformed"||r.status==="too-old"?{name:"Flisk version",didPass:!1,message:r.message,fix:r.fix}:{name:"Flisk version",didPass:!0,message:`flisk ${e} meets minTdkVersion ${r.required}`}}function qd(){let e=k()??process.cwd(),t=vr(e),r=w(e).filter(s=>!St(e,s.path,t));if(r.length===0)return{name:"Resource Discovery",didPass:!0,message:`All resources are covered by discovery.paths ${JSON.stringify(t)}`};let n=r.map(s=>cn(ln(e,s.path))).join(", ");return{name:"Resource Discovery",didPass:!1,message:`${x(r.length,"resource")} outside discovery.paths ${JSON.stringify(t)}: ${n}. \`flisk 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: flisk config regenerate"}}var zd=["install-deps.sh","bun-hoisted-symlink-fix.sh","prisma-bun-client-link-fix.sh","prisma-normalize-client.sh","infisical-entrypoint.sh"],Yd=/shared-platform-engineering\/docker-templates\/([\w.-]+)/g;function Xd(e){let t=new Set;for(let r of w(e)){let n=C(r.path,I);if(V(n)){for(let s of Nd(n))if(!(!s.startsWith("Dockerfile")||s.endsWith(".dockerignore")))for(let o of Ae(C(n,s),"utf-8").matchAll(Yd))t.add(o[1])}}return[...t]}function Qd(){let e=k()??process.cwd(),t=[];V(C(e,O))||t.push(`${O} (workspace root manifest)`);let r=C(e,"shared-platform-engineering","docker-templates"),n=[];try{n=Xd(e)}catch{}for(let s of new Set([...zd,...n]))V(C(r,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:
487
- ${t.join(`
488
- `)}`,fix:"Run `flisk project --yes` (or `flisk config regenerate`) with an updated Flisk. `flisk up` also refreshes these assets before starting Tilt."}}function Zd(){let e=k()??process.cwd(),t=w(e),r=[];for(let n of t){let s=C(n.path,O);if(!V(s))continue;let o=n.config?.appType??"backend",i=Gt[o]??Gt.backend,a;try{a=JSON.parse(Ae(s,"utf-8")).scripts??{}}catch{r.push(`${n.name} (package.json is invalid JSON)`);continue}let c=i.filter(l=>!a[l]);c.length>0&&r.push(`${n.name}: missing "${c.join('", "')}"`)}return r.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:
489
- ${r.join(`
490
- `)}`,fix:`Add the missing scripts to each service's package.json, e.g.:
491
- "dev": "bun run src/index.ts",
492
- "build": "tsc",
493
- "start": "bun run dist/index.js"`}}function xi(e){if(e.startsWith("@"))return[e.split("/").slice(0,2).join("/")];let t=e.split("/")[0]??e;return[`@types/${t}`,t]}function wi(e,t,r=0){let n=e[t];if(typeof n!="string"||r>3)return!1;if(/(^|[\s;&|(])tsc(\s|$)/.test(n))return!0;for(let s of n.matchAll(/\b(?:bun|npm|pnpm|yarn)\s+run\s+([\w:.-]+)/g))if(wi(e,s[1],r+1))return!0;return!1}function bi(e){let t=xi(e);return e.includes("/")||e.startsWith("@")?t[t.length-1]:t[0]}var ep={"@types/bun":"^1.4.2","@types/node":"^20.0.0"};function tp(e,t){return(t.includes("bun")||e.has("@types/bun"))&&!e.has("@types/node")}function rp(){let e=k()??process.cwd(),t=w(e),r=[],n=new Set;for(let o of t){let i=C(o.path,O),a=C(o.path,I,"tsconfig.docker.autogenerated.json");if(!V(i)||!V(a))continue;let u=fn(a)?.compilerOptions?.types;if(!Array.isArray(u))continue;let d=fn(i),p=d?.scripts??{};if(!wi(p,"build"))continue;let g=new Set([...Object.keys(d?.devDependencies??{}),...Object.keys(d?.dependencies??{})]),y=u.filter(S=>typeof S=="string").filter(S=>!xi(S).some(m=>g.has(m)));for(let S of y)n.add(bi(S));if(y.length>0){let S=y.map(m=>`"${m}" (needs ${bi(m)})`).join(", ");r.push(`${o.name}: "types" lists ${S}`)}tp(g,u)&&(n.add("@types/node"),r.push(`${o.name}: uses @types/bun, which references node types internally, but @types/node is not a direct dependency`))}if(r.length===0)return{name:"TypeScript Type Dependencies",didPass:!0,message:`Every service's Docker tsconfig "types" entry has its @types package installed`};let s=[...n].map(o=>{let i=ep[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:
494
- ${r.join(`
495
- `)}`,fix:`Add to each service's package.json devDependencies: ${s.join(", ")}`}}function fn(e){try{return JSON.parse(Ae(e,"utf-8"))}catch{return null}}function np(e){let r=fn(e)?.compilerOptions;return r?.allowSyntheticDefaultImports===!0||r?.esModuleInterop===!0}function sp(){let e=k()??process.cwd(),t=w(e).filter(n=>n.config?.appType==="frontend");if(t.length===0)return{name:"Frontend Docker Preflight",didPass:!0,isSkipped:!0,message:"No frontend services to check"};let r=[];for(let n of t){V(C(n.path,"public"))||r.push(`${n.name}: no public/ folder, but the Docker build copies it. Create it: mkdir -p ${cn(ln(e,C(n.path,"public")))} && touch ${cn(ln(e,C(n.path,"public",".gitkeep")))}`);let s=C(n.path,I,"Dockerfile.app.autogenerated"),o=C(n.path,I,"tsconfig.docker.autogenerated.json");if(V(s)){let i=Ae(s,"utf-8"),a=/\bEXPOSE\s+80\b/.test(i),c=n.config?.stack,l=c?C(e,"services",c,I,"docker-compose.app.autogenerated.yml"):"";if(a&&l&&V(l)){let u=Ae(l,"utf-8"),d=n.name.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),p=new RegExp(`traefik\\.http\\.services\\.${d}\\.loadbalancer\\.server\\.port=(\\d+)`),y=u.match(p)?.[1];y&&y!=="80"&&r.push(`${n.name}: nginx runtime exposes 80, but generated Traefik routes to ${y}`)}}if(V(o)&&!np(o)){let i=C(n.path,"src","main.tsx");if(V(i)){let a=Ae(i,"utf-8"),c=[/import\s+\w+\s+from\s+["']react["']/,/import\s+\w+\s+from\s+["']react-dom\/client["']/].filter(l=>l.test(a)).map(l=>l.source.includes("react-dom")?"react-dom/client":"react");c.length>0&&r.push(`${n.name}: Docker tsconfig does not allow synthetic default imports, but src/main.tsx default-imports ${c.join(" and ")}`)}}}return r.length===0?{name:"Frontend Docker Preflight",didPass:!0,message:`${x(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 \`flisk up\` fail or route 404:
496
- ${r.join(`
497
- `)}`,fix:'Regenerate with a current Flisk (`flisk 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 op=90,ip=80;function ap(e){return e.split(`
498
- `).map(t=>t.trim()).filter(Boolean).map(t=>{let[r,n,s]=t.split(",");return{name:r??"unknown",memPct:Number.parseFloat((n??"").replace(/%/g,"")),cpuPct:Number.parseFloat((s??"").replace(/%/g,""))}}).filter(t=>Number.isFinite(t.memPct)&&Number.isFinite(t.cpuPct))}var cp=Object.assign(function(){let t;try{t=Od(N("docker")??"docker",["stats","--no-stream","--format","{{.Name}},{{.MemPerc}},{{.CPUPerc}}"],{stdio:"pipe",encoding:"utf-8",timeout:ee,windowsHide:process.platform==="win32"})}catch(o){return{name:"Container Resource Health",didPass:!0,isSkipped:!0,message:mn(o)?"Docker not responding - skipped container resource check":"Docker not running - skipped container resource check"}}let r=ap(t);if(r.length===0)return{name:"Container Resource Health",didPass:!0,isSkipped:!0,message:"No running containers to check"};let n=r.filter(o=>o.memPct>=op&&o.cpuPct>=ip);if(n.length===0)return{name:"Container Resource Health",didPass:!0,message:`${x(r.length,"container")} within normal resource usage`};let s=n.map(o=>`${o.name} (mem ${o.memPct.toFixed(0)}%, cpu ${o.cpuPct.toFixed(0)}%)`).join(`
499
- `);return{name:"Container Resource Health",didPass:!1,message:`${x(n.length,"container")} pinned near its memory limit while burning CPU -- likely stuck in a boot-time retry/fetch loop, not a normal crash:
500
- ${s}`,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."}},{requiresContainerRuntime:!0,checkName:"Container Resource Health"}),lp=5e3;async function up(e,t){let r=k()??process.cwd(),n=Zt(w(r),t);return n.length===0?{name:"Service Health",didPass:!0,isSkipped:!0,message:"No routable services to ping"}:jr(await ut(n,e))}async function dp(e,t){let r=k()??process.cwd(),n=w(r),s=as(is(n,t),{timeoutSeconds:Math.ceil(e/1e3)});return s.length===0?Ir([]):(await ut(Zt(n,t),e)).some(i=>i.status!==void 0)?Ir(await ds(s,us(r))):{name:"Service Smoke",didPass:!0,isSkipped:!0,message:"No services responded - skipped smoke checks"}}async function pp(e,t){let r=k()??process.cwd(),n=w(r),s=zn(n,t);return s.length===0?{name:"Advertised Endpoints",didPass:!0,isSkipped:!0,message:"No routable services to check"}:so(await ut(s,e))}function fp(e,t){if(e.length===0){console.log(`${f.green("\u2713")} Nothing to repair
501
- `);return}for(let r of e){let n=r.status==="planned"?`${f.cyan("would run")}`:r.status==="applied"?f.green("ran"):r.status==="failed"?f.red("failed"):f.gray("skipped"),s=r.status==="planned"?"":` (${r.reason})`;console.log(`${n}: ${r.command}${s}`)}t&&console.log(f.gray(`
502
- Dry run: nothing was changed.`)),console.log("")}function an(e){for(let t of Ti(e))!t.didPass&&!t.isSkipped&&!t.isWarning?(console.log(`${f.red("\u2717")} ${t.message}`),t.fix&&console.log(`${f.blue("\u2139")} Fix: ${t.fix}`)):t.isWarning?(console.log(`${f.yellow("\u26A0")} ${f.yellow(t.message)}`),t.fix&&console.log(`${f.blue("\u2139")} Fix: ${t.fix}`)):t.isSkipped?(console.log(`${f.gray("\u25CB")} ${f.gray(t.message)}`),t.fix&&console.log(`${f.blue("\u2139")} ${t.fix}`)):console.log(`${f.green("\u2713")} ${t.message}`)}function mp(e,t,r,n={},s){let o=s??[...e,...t];n.all?(console.log(f.bold("Machine")),an(e),console.log(`
503
- ${f.bold("Project")}`),r?t.length>0&&an(t):console.log(`${f.gray("\u25CB")} ${f.gray("Not in a Flisk project, so project checks were skipped")}`),console.log(""),console.log(Ud(o))):(an(o),console.log(""),o.every(a=>a.didPass||a.isSkipped||a.isWarning)&&!r&&(console.log(`${f.gray("\u25CB")} ${f.gray("Not in a Flisk project, so project checks were skipped")}`),console.log("")))}var hp=new Cd("doctor").description("Check environment readiness for Flisk").option("--all","Run every check and show Machine and Project sections with a summary",!1).option("--json","Output a versioned JSON readiness report",!1).exitOverride(e=>{e.exitCode===0&&process.exit(0),hp.opts().json&&console.log(JSON.stringify(ae([],!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("--scope <scope>","With --fix: project (default) or machine, which also removes unused networks of other Flisk projects","project").option("-y, --yes","With --fix: apply every repair without asking",!1).option("--ping-timeout <ms>","Per-service ping timeout in milliseconds",String(lp)).action(async e=>{let t=Number(e.pingTimeout);if(!/^\d+$/.test(e.pingTimeout)||!Number.isSafeInteger(t)||t<=0){let b="--ping-timeout must be a positive integer number of milliseconds";e.json&&console.log(JSON.stringify(ae([],!1,[{code:"USAGE",message:b}]))),console.error(b),process.exit(2);return}let r=!["project","machine"].includes(e.scope),n=e.scope!=="project"&&!e.fix;if(r||n||(e.dryRun||e.yes)&&!e.fix){let b=r?`--scope must be project or machine, got "${e.scope}"`:n?"--scope only applies with --fix":`${e.dryRun?"--dry-run":"--yes"} only applies with --fix`;e.json&&console.log(JSON.stringify(ae([],!1,[{code:"USAGE",message:b}]))),console.error(b),process.exit(2);return}e.json||console.log("Flisk doctor checks your local Docker + Tilt development environment; it does not check cluster deployments.");let s=Hs();if(process.platform==="win32"){if(e.json){let b=null;try{b=await Re()}catch{}console.log(JSON.stringify(ae([{name:"Native Windows support",didPass:!1,message:yi,fix:"Use WSL2 Ubuntu with Docker Desktop integration. Guide: docs/wsl2.md"}],!!k(),[],b,{...s,canUp:!1})))}console.error(yi),process.exit(1);return}if(s.kind==="webcontainer"){let b=ae([{name:"Host",didPass:!1,message:Er,fix:`Use a machine with Docker. Guide: ${kt}`}],!!k(),[],void 0,s);e.json&&console.log(JSON.stringify(b)),console.error(Er),process.exit(1);return}e.json||(console.log(`
504
- ${f.bold("\u{1F50D} Flisk Doctor")}
505
- `),console.log(`Checking environment...
506
- `));let o=k(),i;if(e.fix){try{let b;if(o)try{b=gt(o).project.name}catch{}let R=o?To(o):[],le=await ko(b,void 0,{tiltPids:R,machine:e.scope==="machine"});i=await yo(le,{dryRun:e.dryRun,confirm:e.yes||!e.json&&process.stdin.isTTY?async P=>e.yes||await pi({message:`Run \`${P.command}\` (${P.check}, ${P.reason})?`}):void 0})}catch(b){i=[{id:"plan-fixes",check:"Docker networks",scope:"project",command:"docker network ls",status:"failed",reason:`could not list Docker networks: ${(b instanceof Error?b.message:String(b)).split(/\r?\n/)[0]}`}]}e.json||fp(i,e.dryRun)}let a,c;try{a=await Rt(o??process.cwd())}catch(b){a=null,c=b instanceof Error?b.message:String(b)}let l=[()=>a?{name:"Host Ports",didPass:!0,message:`HTTP ${a.ingressHttp}, HTTPS ${a.ingressHttps}, Postgres ${a.postgres}`}:{name:"Host Ports",didPass:!1,message:c??"Could not select host ports",fix:"Set TDK_HTTP_PORT, TDK_HTTPS_PORT, or TDK_POSTGRES_PORT to available host ports."},()=>a?Dr(gi,void 0,[a.ingressHttp,a.ingressHttps]):{name:"Ingress Ports",didPass:!1,message:"Ingress port plan is unavailable"},hn,()=>uo(),Q("Docker OS",kn),gn,Q("Docker Compose",ze),Q("Docker Versions",Ye),Vd,()=>Dd(k()??process.cwd(),e.strict),()=>Id(process.env,e.strict),Q("Docker Network Pool",()=>oi()),()=>bo()],u=[()=>Jd(),Kd,Qd,$o,Zd,()=>io(),rp,sp,qd,()=>qo(),()=>vt(),()=>en(),()=>tn(),()=>rn(),()=>nn(void 0,e.strict),()=>zo(),()=>Yo(),()=>Zo(),()=>Qo(),()=>$t(),()=>Ko(),()=>ni(),()=>ri(),()=>si(),Wd,async()=>{let b=k()??process.cwd(),R=Nr(b)?await et(b):!0;return Cr(gi,b,void 0,R)},Q("Container Resource Health",cp),Q("Crash-looping containers",()=>{let b=k()??process.cwd(),R=[];try{R=ct(b)}catch{return{name:"Crash-looping containers",didPass:!0,isSkipped:!0,message:"Could not read .env - skipped crash-loop check"}}return co(Te(),R)}),Q("Tilt Resources",Fr)];if(e.ping){let b=o?di(o,a?.ingressHttp):a?.ingressHttp;u.push(Q("Service Health",()=>up(t,b))),u.push(Q("Service Smoke",()=>dp(t,b))),u.push(Q("Advertised Endpoints",()=>pp(t,b)))}let d=!!o,p=[],g=[],y,S=[];if(e.json||!e.all){let b=await wo(l,d?u:[]);y=b.checks,S=b.errors}else{let b=async(P,Qe=!1)=>{let Ze=P.map(q=>{let ge=async()=>{try{return await q()}catch(te){return S.push({code:"INTERNAL",message:te instanceof Error?te.message:String(te)}),{name:q.checkName??"Doctor check",didPass:!1,isSkipped:!1,message:"Check failed with an internal error"}}};return ge.requiresContainerRuntime=q.requiresContainerRuntime,ge.checkName=q.checkName,ge});return Md(Ze,{all:!!e.all,containerRuntimeFailed:Qe})};p=await b(l);let R=p.some(P=>!P.didPass&&!P.isSkipped&&!P.isWarning),le=p.some(P=>P.name===F&&!P.didPass);d&&(e.all||!R)&&(g=await b(u,le)),y=[...p,...g]}if(Gs(s.kind))for(let b of y)b.name===F&&!b.didPass&&(b.fix=Bs);let m=ae(Ti(y),d,S,a,s);i&&(m.data.actions=i),i&&So(i)&&(m.data.ready=!1);let W=Eo(m),U=m.data.ready;if(e.json){let b;try{b=ct(k()??process.cwd())}catch(R){if(!(R instanceof Le))throw R;console.log(JSON.stringify(ae([],d,[...S,{code:"ENV_UNREADABLE",message:R.message}]))),console.error(R.message),process.exit(1)}console.log(JSON.stringify(at(m,b)));for(let R of S)console.error(be(R.message,b));W!==0&&process.exit(W);return}for(let b of S)console.error(`${f.red("\u2717")} Doctor check failed: ${b.message}`);mp(p,g,d,e,y);let he=Fd(d,U);console.log(`${U?f.green(f.bold("\u2713")):f.red(f.bold("\u2717"))} ${he}`),W!==0&&process.exit(W)});function Ei(e){return Nt(e,me)}var Xe=`${me[0]}.${me[1]}+`;var yp=["docker","compose","tilt"],$i=["node","bun","ports"],_i=["node",...yp,"bun","ports"];function Sp(e,t){return{id:e,ok:t.didPass||!!t.isSkipped,message:t.message,fix:t.fix}}function Pi(e){let[t=0,r=0]=String(e).split(".").map(n=>Number.parseInt(n,10));return!Number.isFinite(t)||!Number.isFinite(r)||t<22||t===22&&r<12}async function Ct(e,t,r){try{return Sp(e,await t())}catch{return{id:e,ok:!1,message:r}}}function bp(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 Tp(e){try{return w(e).some(t=>t.config?.featuresEnabled?.includes("prisma")??!1)}catch{return!1}}async function xp(e={}){let t=e.cwd??process.cwd(),r=e.scope??"runtime",n=k(t),s=!!n,o=[],i=process.versions.node;o.push({id:"node",ok:!Pi(i),message:i,fix:Pi(i)?`Install Node.js 22.12+ (current: ${i}). https://nodejs.org`:void 0}),r==="runtime"&&(o.push(await Ct("docker",hn,"Docker daemon is not running")),o.push(await Ct("compose",async()=>{let[m,$]=await Promise.all([ze(),Ye()]);return m.didPass?bp($):m},"Docker Engine or Compose version check failed")),o.push(await Ct("tilt",gn,"Tilt CLI not found")));let a=n?yt(w(n)):!0,c=!1,l=a?N("bun"):void 0;if(l)try{let m=vi(l,["--version"],{encoding:"utf-8",timeout:3e3}).trim();c=Ei(m)}catch{}o.push(a?{id:"bun",ok:c,message:c?`Bun ${Xe} is available on PATH`:`Bun ${Xe} not found on PATH`,fix:c?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 Rt(n??t,r==="scaffold"?{inspectDocker:!1}:{});o.push({id:"ports",ok:!0,message:`HTTP ${m.ingressHttp}, HTTPS ${m.ingressHttps}, Postgres ${m.postgres}`})}catch(m){o.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&&r==="runtime"){let m=await Ct("nats",()=>$t(n),"NATS broker check failed");if(m.ok||o.push(m),Tp(n)){let $=!1;try{let W=JSON.parse(gp(kp(n,".tdk",A),"utf-8")),U=String(W?.project?.name??"").replace(/-/g,"_");U&&($=vi(N("docker")??"docker",["ps","--filter",`label=com.docker.compose.project=${U}`,"--filter","label=com.docker.compose.service=postgres","--filter","health=healthy","--format","{{.Names}}"],{encoding:"utf-8",timeout:3e3}).trim().length>0)}catch{}$||o.push({id:"prisma",ok:!1,message:"A Prisma resource is configured, but the project Postgres service is not healthy",fix:"Run `flisk up` to start the bundled Postgres, then check `flisk doctor`."})}}let u=o.filter(m=>!m.ok).slice(0,8),d=r==="scaffold"?$i:_i,p=u.some(m=>d.includes(m.id)),g=u.some(m=>m.id==="bun"),y=u.length?p?r==="scaffold"?g?`Project setup blocked. Bun ${Xe} must be installed on PATH.
507
- Fix the machine checks below.`:"Project setup blocked. Fix the machine checks below.":g?`Cold start blocked. Bun ${Xe} must be installed on PATH.
508
- Prisma/NATS configuration is project-specific and generated after \`flisk project\`. Fix the machine checks below.`:"Cold start blocked. Bun/Prisma/NATS are not the first failure.\nThey are generated after `flisk project`. Fix the machine checks below.":"Machine is ready. Project wiring is not.":"",S=u.length?"":s?"Environment ready for Flisk":`This machine is ready for Flisk.
509
- Next: flisk project --yes`;return{ok:u.length===0,inProject:s,items:u,header:y,footer:S}}function wp(e,t={}){let r=[e.header];for(let n of e.items)r.push(`FAIL ${n.id.padEnd(7)} ${n.message}`),n.fix&&r.push(` ${n.fix}`);return e.footer&&(t.includeSuccessFooter??!0)&&r.push(e.footer),r.filter(Boolean).join(`
510
- `)}async function zy(e={}){let t=e.scope??"runtime",r=await xp({scope:t}),n=t==="scaffold"?$i:_i;r.items.some(o=>n.includes(o.id))&&(console.error(wp(r)),process.exit(1))}function Yy(e=N){return{hasDockerCli:!!e("docker"),hasTiltCli:!!e("tilt")}}function Xy(e){let t=[];return e.hasDockerCli||t.push(`Docker is not installed. ${Z.dockerMissing}`),e.hasTiltCli||t.push(`Tilt is not installed. ${Z.tiltMissing}`),t.push("Docker and Tilt are only needed to start the stack: start them, then run `flisk doctor` (readiness) and `flisk up`."),t}export{Ft as a,f as b,Kn as c,Y as d,L as e,Nn as f,I as g,st as h,ot as i,A as j,O as k,Ht as l,Ip as m,_ as n,je as o,Fp as p,k as q,qn as r,we as s,Kf as t,ks as u,ys as v,N as w,ir as x,Tc as y,Ps as z,xc as A,am as B,cm as C,_s as D,gm as E,D as F,ne as G,km as H,Ec as I,ym as J,Sm as K,lr as L,bm as M,x as N,wm as O,Em as P,vm as Q,Pm as R,$m as S,_m as T,Rm as U,Am as V,Om as W,pr as X,Nm as Y,Cm as Z,Dm as _,jm as $,Im as aa,Fm as ba,Lm as ca,Mm as da,M as ea,Vn as fa,df as ga,pf as ha,Le as ia,ct as ja,be as ka,at as la,Xt as ma,qa as na,Qt as oa,zn as pa,is as qa,Ff as ra,us as sa,ds as ta,Me as ua,Bf as va,sr as wa,As as xa,Ac as ya,Oc as za,Gm as Aa,Vm as Ba,Wm as Ca,Km as Da,Jm as Ea,fr as Fa,Sr as Ga,Ds as Ha,Mc as Ia,w as Ja,ve as Ka,oh as La,ih as Ma,Uc as Na,ah as Oa,ch as Pa,lh as Qa,uh as Ra,Hc as Sa,dh as Ta,Re as Ua,vk as Va,ui as Wa,Nk as Xa,Ck as Ya,Rt as Za,Tu as _a,xu as $a,_g as ab,Lg as bb,Mg as cb,$e as db,Cu as eb,Xg as fb,qp as gb,Ta as hb,zp as ib,va as jb,Yp as kb,He as lb,ht as mb,wr as nb,gt as ob,$h as pb,al as qb,cl as rb,_h as sb,Fk as tb,pi as ub,Lk as vb,Mk as wb,Lo as xb,Jr as yb,Ko as zb,cd as Ab,Er as Bb,Bs as Cb,Hs as Db,Gs as Eb,vr as Fb,St as Gb,Fh as Hb,qs as Ib,Ys as Jb,Rr as Kb,Ar as Lb,Or as Mb,ag as Nb,cg as Ob,lg as Pb,Hr as Qb,ru as Rb,ou as Sb,mg as Tb,qk as Ub,zk as Vb,hi as Wb,hp as Xb,yp as Yb,$i as Zb,_i as _b,Pi as $b,bp as ac,xp as bc,wp as cc,zy as dc,Yy as ec,Xy as fc};