@tdk-landscape/tdk-cli-core 1.3.49 → 1.3.50

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 (309) hide show
  1. package/.claude/skills/layer-autoresearch/SKILL.md +48 -0
  2. package/.claude/skills/tdk-troubleshoot/SKILL.md +222 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.yml +40 -0
  4. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.yml +18 -0
  6. package/.github/dependabot.yml +33 -0
  7. package/.github/pull_request_template.md +5 -0
  8. package/.github/workflows/ci.yml +187 -0
  9. package/.github/workflows/release-binaries.yml +19 -9
  10. package/.github/zizmor.yml +9 -0
  11. package/CODE_OF_CONDUCT.md +9 -0
  12. package/CONTRIBUTING.md +42 -0
  13. package/LICENSE +21 -0
  14. package/README.md +149 -0
  15. package/SECURITY.md +17 -0
  16. package/benchmarks/results/container-scale-2026-09-25T20-54-49-736Z.json +45 -0
  17. package/benchmarks/results/container-scale-2026-09-25T21-01-20-540Z.json +150 -0
  18. package/benchmarks/results/container-scale-2026-09-25T21-15-01-441Z.json +73 -0
  19. package/benchmarks/results/container-scale-2026-09-25T22-09-01-630Z.json +77 -0
  20. package/benchmarks/results/container-scale-2026-09-25T22-18-28-371Z.json +202 -0
  21. package/cli/README.md +25 -0
  22. package/cli/biome.json +1 -1
  23. package/cli/dist/cli.d.ts +3 -0
  24. package/cli/dist/cli.d.ts.map +1 -0
  25. package/cli/dist/cli.js +64 -0
  26. package/cli/dist/cli.js.map +1 -0
  27. package/cli/dist/commands/completion.d.ts +3 -0
  28. package/cli/dist/commands/completion.d.ts.map +1 -0
  29. package/cli/dist/commands/completion.js +294 -0
  30. package/cli/dist/commands/completion.js.map +1 -0
  31. package/cli/dist/commands/config.d.ts +3 -0
  32. package/cli/dist/commands/config.d.ts.map +1 -0
  33. package/cli/dist/commands/config.js +231 -0
  34. package/cli/dist/commands/config.js.map +1 -0
  35. package/cli/dist/commands/doctor.d.ts +19 -0
  36. package/cli/dist/commands/doctor.d.ts.map +1 -0
  37. package/cli/dist/commands/doctor.js +745 -0
  38. package/cli/dist/commands/doctor.js.map +1 -0
  39. package/cli/dist/commands/down.d.ts +3 -0
  40. package/cli/dist/commands/down.d.ts.map +1 -0
  41. package/cli/dist/commands/down.js +32 -0
  42. package/cli/dist/commands/down.js.map +1 -0
  43. package/cli/dist/commands/help.d.ts +2 -0
  44. package/cli/dist/commands/help.d.ts.map +1 -0
  45. package/cli/dist/commands/help.js +138 -0
  46. package/cli/dist/commands/help.js.map +1 -0
  47. package/cli/dist/commands/networks.d.ts +3 -0
  48. package/cli/dist/commands/networks.d.ts.map +1 -0
  49. package/cli/dist/commands/networks.js +260 -0
  50. package/cli/dist/commands/networks.js.map +1 -0
  51. package/cli/dist/commands/project.d.ts +3 -0
  52. package/cli/dist/commands/project.d.ts.map +1 -0
  53. package/cli/dist/commands/project.js +370 -0
  54. package/cli/dist/commands/project.js.map +1 -0
  55. package/cli/dist/commands/projects.d.ts +3 -0
  56. package/cli/dist/commands/projects.d.ts.map +1 -0
  57. package/cli/dist/commands/projects.js +71 -0
  58. package/cli/dist/commands/projects.js.map +1 -0
  59. package/cli/dist/commands/resource.d.ts +69 -0
  60. package/cli/dist/commands/resource.d.ts.map +1 -0
  61. package/cli/dist/commands/resource.js +579 -0
  62. package/cli/dist/commands/resource.js.map +1 -0
  63. package/cli/dist/commands/resources.d.ts +3 -0
  64. package/cli/dist/commands/resources.d.ts.map +1 -0
  65. package/cli/dist/commands/resources.js +73 -0
  66. package/cli/dist/commands/resources.js.map +1 -0
  67. package/cli/dist/commands/stack.d.ts +3 -0
  68. package/cli/dist/commands/stack.d.ts.map +1 -0
  69. package/cli/dist/commands/stack.js +97 -0
  70. package/cli/dist/commands/stack.js.map +1 -0
  71. package/cli/dist/commands/stacks.d.ts +3 -0
  72. package/cli/dist/commands/stacks.d.ts.map +1 -0
  73. package/cli/dist/commands/stacks.js +43 -0
  74. package/cli/dist/commands/stacks.js.map +1 -0
  75. package/cli/dist/commands/status.d.ts +3 -0
  76. package/cli/dist/commands/status.d.ts.map +1 -0
  77. package/cli/dist/commands/status.js +71 -0
  78. package/cli/dist/commands/status.js.map +1 -0
  79. package/cli/dist/commands/ui.d.ts +3 -0
  80. package/cli/dist/commands/ui.d.ts.map +1 -0
  81. package/cli/dist/commands/ui.js +445 -0
  82. package/cli/dist/commands/ui.js.map +1 -0
  83. package/cli/dist/commands/up.d.ts +3 -0
  84. package/cli/dist/commands/up.d.ts.map +1 -0
  85. package/cli/dist/commands/up.js +124 -0
  86. package/cli/dist/commands/up.js.map +1 -0
  87. package/cli/dist/commands/upgrade.d.ts +11 -0
  88. package/cli/dist/commands/upgrade.d.ts.map +1 -0
  89. package/cli/dist/commands/upgrade.js +496 -0
  90. package/cli/dist/commands/upgrade.js.map +1 -0
  91. package/cli/dist/commands/version.d.ts +3 -0
  92. package/cli/dist/commands/version.d.ts.map +1 -0
  93. package/cli/dist/commands/version.js +9 -0
  94. package/cli/dist/commands/version.js.map +1 -0
  95. package/cli/dist/components/Accessible.d.ts +8 -0
  96. package/cli/dist/components/Accessible.d.ts.map +1 -0
  97. package/cli/dist/components/Accessible.js +10 -0
  98. package/cli/dist/components/Accessible.js.map +1 -0
  99. package/cli/dist/components/BaseTooltip.d.ts +4 -0
  100. package/cli/dist/components/BaseTooltip.d.ts.map +1 -0
  101. package/cli/dist/components/BaseTooltip.js +28 -0
  102. package/cli/dist/components/BaseTooltip.js.map +1 -0
  103. package/cli/dist/components/DetailPanel.d.ts +4 -0
  104. package/cli/dist/components/DetailPanel.d.ts.map +1 -0
  105. package/cli/dist/components/DetailPanel.js +18 -0
  106. package/cli/dist/components/DetailPanel.js.map +1 -0
  107. package/cli/dist/components/FileTree.d.ts +4 -0
  108. package/cli/dist/components/FileTree.d.ts.map +1 -0
  109. package/cli/dist/components/FileTree.js +47 -0
  110. package/cli/dist/components/FileTree.js.map +1 -0
  111. package/cli/dist/components/ResourceSelectInput.d.ts +4 -0
  112. package/cli/dist/components/ResourceSelectInput.d.ts.map +1 -0
  113. package/cli/dist/components/ResourceSelectInput.js +7 -0
  114. package/cli/dist/components/ResourceSelectInput.js.map +1 -0
  115. package/cli/dist/components/ResourceTable.d.ts +4 -0
  116. package/cli/dist/components/ResourceTable.d.ts.map +1 -0
  117. package/cli/dist/components/ResourceTable.js +18 -0
  118. package/cli/dist/components/ResourceTable.js.map +1 -0
  119. package/cli/dist/components/TabBar.d.ts +4 -0
  120. package/cli/dist/components/TabBar.d.ts.map +1 -0
  121. package/cli/dist/components/TabBar.js +17 -0
  122. package/cli/dist/components/TabBar.js.map +1 -0
  123. package/cli/dist/components/Tooltip.d.ts +12 -0
  124. package/cli/dist/components/Tooltip.d.ts.map +1 -0
  125. package/cli/dist/components/Tooltip.js +12 -0
  126. package/cli/dist/components/Tooltip.js.map +1 -0
  127. package/cli/dist/components/index.d.ts +8 -0
  128. package/cli/dist/components/index.d.ts.map +1 -0
  129. package/cli/dist/components/index.js +8 -0
  130. package/cli/dist/components/index.js.map +1 -0
  131. package/cli/dist/config/platform-standards.d.ts +159 -0
  132. package/cli/dist/config/platform-standards.d.ts.map +1 -0
  133. package/cli/dist/config/platform-standards.js +181 -0
  134. package/cli/dist/config/platform-standards.js.map +1 -0
  135. package/cli/dist/generator/extension-fetch.d.ts +25 -0
  136. package/cli/dist/generator/extension-fetch.d.ts.map +1 -0
  137. package/cli/dist/generator/extension-fetch.js +259 -0
  138. package/cli/dist/generator/extension-fetch.js.map +1 -0
  139. package/cli/dist/generator/template-engine.d.ts +62 -0
  140. package/cli/dist/generator/template-engine.d.ts.map +1 -0
  141. package/cli/dist/generator/template-engine.js +623 -0
  142. package/cli/dist/generator/template-engine.js.map +1 -0
  143. package/cli/dist/index.d.ts +11 -0
  144. package/cli/dist/index.d.ts.map +1 -0
  145. package/cli/dist/index.js +11 -0
  146. package/cli/dist/index.js.map +1 -0
  147. package/cli/dist/types/index.d.ts +257 -0
  148. package/cli/dist/types/index.d.ts.map +1 -0
  149. package/cli/dist/types/index.js +17 -0
  150. package/cli/dist/types/index.js.map +1 -0
  151. package/cli/dist/utils/cache.d.ts +19 -0
  152. package/cli/dist/utils/cache.d.ts.map +1 -0
  153. package/cli/dist/utils/cache.js +64 -0
  154. package/cli/dist/utils/cache.js.map +1 -0
  155. package/cli/dist/utils/command-helpers.d.ts +17 -0
  156. package/cli/dist/utils/command-helpers.d.ts.map +1 -0
  157. package/cli/dist/utils/command-helpers.js +41 -0
  158. package/cli/dist/utils/command-helpers.js.map +1 -0
  159. package/cli/dist/utils/constants.d.ts +53 -0
  160. package/cli/dist/utils/constants.d.ts.map +1 -0
  161. package/cli/dist/utils/constants.js +115 -0
  162. package/cli/dist/utils/constants.js.map +1 -0
  163. package/cli/dist/utils/discovery-context.d.ts +4 -0
  164. package/cli/dist/utils/discovery-context.d.ts.map +1 -0
  165. package/cli/dist/utils/discovery-context.js +39 -0
  166. package/cli/dist/utils/discovery-context.js.map +1 -0
  167. package/cli/dist/utils/docker.d.ts +4 -0
  168. package/cli/dist/utils/docker.d.ts.map +1 -0
  169. package/cli/dist/utils/docker.js +19 -0
  170. package/cli/dist/utils/docker.js.map +1 -0
  171. package/cli/dist/utils/doctor-runtime.d.ts +81 -0
  172. package/cli/dist/utils/doctor-runtime.d.ts.map +1 -0
  173. package/cli/dist/utils/doctor-runtime.js +498 -0
  174. package/cli/dist/utils/doctor-runtime.js.map +1 -0
  175. package/cli/dist/utils/env-validator.d.ts +15 -0
  176. package/cli/dist/utils/env-validator.d.ts.map +1 -0
  177. package/cli/dist/utils/env-validator.js +109 -0
  178. package/cli/dist/utils/env-validator.js.map +1 -0
  179. package/cli/dist/utils/errors.d.ts +31 -0
  180. package/cli/dist/utils/errors.d.ts.map +1 -0
  181. package/cli/dist/utils/errors.js +140 -0
  182. package/cli/dist/utils/errors.js.map +1 -0
  183. package/cli/dist/utils/file-helpers.d.ts +14 -0
  184. package/cli/dist/utils/file-helpers.d.ts.map +1 -0
  185. package/cli/dist/utils/file-helpers.js +45 -0
  186. package/cli/dist/utils/file-helpers.js.map +1 -0
  187. package/cli/dist/utils/formatting.d.ts +30 -0
  188. package/cli/dist/utils/formatting.d.ts.map +1 -0
  189. package/cli/dist/utils/formatting.js +164 -0
  190. package/cli/dist/utils/formatting.js.map +1 -0
  191. package/cli/dist/utils/paths.d.ts +3 -0
  192. package/cli/dist/utils/paths.d.ts.map +1 -0
  193. package/cli/dist/utils/paths.js +41 -0
  194. package/cli/dist/utils/paths.js.map +1 -0
  195. package/cli/dist/utils/port-assignment.d.ts +6 -0
  196. package/cli/dist/utils/port-assignment.d.ts.map +1 -0
  197. package/cli/dist/utils/port-assignment.js +81 -0
  198. package/cli/dist/utils/port-assignment.js.map +1 -0
  199. package/cli/dist/utils/project-features.d.ts +23 -0
  200. package/cli/dist/utils/project-features.d.ts.map +1 -0
  201. package/cli/dist/utils/project-features.js +98 -0
  202. package/cli/dist/utils/project-features.js.map +1 -0
  203. package/cli/dist/utils/project-templates.d.ts +7 -0
  204. package/cli/dist/utils/project-templates.d.ts.map +1 -0
  205. package/cli/dist/utils/project-templates.js +24 -0
  206. package/cli/dist/utils/project-templates.js.map +1 -0
  207. package/cli/dist/utils/prompt.d.ts +28 -0
  208. package/cli/dist/utils/prompt.d.ts.map +1 -0
  209. package/cli/dist/utils/prompt.js +79 -0
  210. package/cli/dist/utils/prompt.js.map +1 -0
  211. package/cli/dist/utils/resource-features.d.ts +29 -0
  212. package/cli/dist/utils/resource-features.d.ts.map +1 -0
  213. package/cli/dist/utils/resource-features.js +175 -0
  214. package/cli/dist/utils/resource-features.js.map +1 -0
  215. package/cli/dist/utils/service-urls.d.ts +30 -0
  216. package/cli/dist/utils/service-urls.d.ts.map +1 -0
  217. package/cli/dist/utils/service-urls.js +93 -0
  218. package/cli/dist/utils/service-urls.js.map +1 -0
  219. package/cli/dist/utils/services.d.ts +18 -0
  220. package/cli/dist/utils/services.d.ts.map +1 -0
  221. package/cli/dist/utils/services.js +292 -0
  222. package/cli/dist/utils/services.js.map +1 -0
  223. package/cli/dist/utils/stack-features.d.ts +22 -0
  224. package/cli/dist/utils/stack-features.d.ts.map +1 -0
  225. package/cli/dist/utils/stack-features.js +13 -0
  226. package/cli/dist/utils/stack-features.js.map +1 -0
  227. package/cli/dist/utils/tar.d.ts +7 -0
  228. package/cli/dist/utils/tar.d.ts.map +1 -0
  229. package/cli/dist/utils/tar.js +21 -0
  230. package/cli/dist/utils/tar.js.map +1 -0
  231. package/cli/dist/utils/tilt.d.ts +20 -0
  232. package/cli/dist/utils/tilt.d.ts.map +1 -0
  233. package/cli/dist/utils/tilt.js +95 -0
  234. package/cli/dist/utils/tilt.js.map +1 -0
  235. package/cli/dist/utils/validation.d.ts +16 -0
  236. package/cli/dist/utils/validation.d.ts.map +1 -0
  237. package/cli/dist/utils/validation.js +58 -0
  238. package/cli/dist/utils/validation.js.map +1 -0
  239. package/cli/package.json +11 -12
  240. package/cli/src/commands/__tests__/always-enabled-infra.test.ts +2 -8
  241. package/cli/src/commands/__tests__/doctor.test.ts +181 -8
  242. package/cli/src/commands/__tests__/phases-model.test.ts +2 -4
  243. package/cli/src/commands/__tests__/project-resource-features-e2e.test.ts +4 -24
  244. package/cli/src/commands/__tests__/saas-starter-integration.test.ts +8 -10
  245. package/cli/src/commands/__tests__/stack-features-e2e.test.ts +10 -4
  246. package/cli/src/commands/__tests__/upgrade.test.ts +23 -13
  247. package/cli/src/commands/doctor.ts +136 -0
  248. package/cli/src/commands/help.ts +13 -3
  249. package/cli/src/commands/project.ts +11 -14
  250. package/cli/src/commands/projects.ts +2 -2
  251. package/cli/src/commands/up.ts +13 -4
  252. package/cli/src/commands/upgrade.ts +11 -4
  253. package/cli/src/config/platform-standards.test.ts +6 -6
  254. package/cli/src/generator/__tests__/extension-fetch.test.ts +36 -14
  255. package/cli/src/generator/extension-fetch.ts +12 -5
  256. package/cli/src/generator/template-engine.ts +19 -21
  257. package/cli/src/types/index.ts +2 -0
  258. package/cli/src/utils/__tests__/tilt.test.ts +29 -0
  259. package/cli/src/utils/constants.ts +4 -0
  260. package/cli/src/utils/doctor-runtime.ts +67 -27
  261. package/cli/src/utils/env-validator.ts +2 -2
  262. package/cli/src/utils/project-templates.ts +2 -1
  263. package/cli/src/utils/prompt.ts +2 -4
  264. package/cli/src/utils/resource-features.ts +10 -4
  265. package/cli/src/utils/services.ts +2 -1
  266. package/cli/src/utils/tilt.ts +20 -0
  267. package/engine/assets/docker/wake-gateway/Dockerfile +26 -0
  268. package/engine/assets/docker/wake-gateway/decide.test.ts +87 -0
  269. package/engine/assets/docker/wake-gateway/decide.ts +75 -0
  270. package/engine/assets/docker/wake-gateway/server.ts +220 -0
  271. package/engine/topologies/platform/docker/build/golden_image_dockerfile.star +13 -23
  272. package/engine/topologies/platform/docker/compose/compose.star +7 -14
  273. package/engine/topologies/platform/docker/compose/compose_helpers.star +1 -1
  274. package/engine/topologies/platform/docker/compose/traefik_standalone.star +44 -6
  275. package/engine/topologies/platform/docker/config/healthcheck.star +56 -0
  276. package/engine/topologies/platform/docker/constants.star +10 -0
  277. package/engine/topologies/platform/docker/layers/l1_base_layers.star +1 -1
  278. package/engine/topologies/platform/docker/layers/l4_runtime_layers.star +55 -20
  279. package/engine/topologies/platform/docker/networking/traefik_static_routes.star +107 -0
  280. package/engine/topologies/tilt/generators/typescript/backend_tsconfig.star +2 -2
  281. package/engine/topologies/tilt/generators/typescript/frontend_tsconfig.star +1 -1
  282. package/engine/topologies/tilt/manifest/schema.star +6 -0
  283. package/engine/topologies/tilt/manifest/validator.star +16 -0
  284. package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +107 -8
  285. package/engine/topologies/tilt/resources/orchestrator/builders/typescript.star +12 -6
  286. package/engine/topologies/tilt/resources/orchestrator/generators/manifest_resource.star +2 -1
  287. package/knip.json +1 -1
  288. package/openspec/changes/optimize-runtime-docker-layers/.openspec.yaml +3 -0
  289. package/openspec/changes/optimize-runtime-docker-layers/design.md +81 -0
  290. package/openspec/changes/optimize-runtime-docker-layers/proposal.md +44 -0
  291. package/openspec/changes/optimize-runtime-docker-layers/specs/runtime-container-footprint/spec.md +79 -0
  292. package/openspec/changes/optimize-runtime-docker-layers/tasks.md +36 -0
  293. package/openspec/changes/prioritized-cold-start/.openspec.yaml +4 -0
  294. package/openspec/changes/prioritized-cold-start/design.md +113 -0
  295. package/openspec/changes/prioritized-cold-start/proposal.md +40 -0
  296. package/openspec/changes/prioritized-cold-start/specs/on-demand-resource-startup/spec.md +92 -0
  297. package/openspec/changes/prioritized-cold-start/tasks.md +46 -0
  298. package/openspec/config.yaml +1 -0
  299. package/package.json +35 -5
  300. package/scripts/autoresearch/layer-eval.sh +65 -0
  301. package/scripts/benchmark/README.md +25 -0
  302. package/scripts/benchmark/container-scale.ts +503 -0
  303. package/scripts/benchmark/pre-release-gate.sh +9 -0
  304. package/scripts/measure-idle-footprint.sh +58 -0
  305. package/tests/tilt-engine/test_runtime_container_footprint.py +162 -0
  306. package/tests/tilt-engine/test_sablier_defer_start.py +196 -0
  307. package/tests/tilt-engine/test_standalone_traefik.py +38 -0
  308. package/tests/tilt-engine/test_traefik_static_wake_route.py +119 -0
  309. package/bun.lock +0 -538
package/cli/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tdk-cli-internal",
3
- "version": "1.3.49",
3
+ "version": "1.3.50",
4
4
  "description": "TDK CLI - Tilt Development Kit command line interface",
5
5
  "keywords": [
6
6
  "tdk",
@@ -53,25 +53,24 @@
53
53
  },
54
54
  "dependencies": {
55
55
  "chalk": "^5.3.0",
56
- "commander": "^12.0.0",
56
+ "commander": "^15.0.0",
57
57
  "handlebars": "^4.7.8",
58
- "ink": "^7.0.1",
58
+ "ink": "^7.1.1",
59
59
  "ink-select-input": "^6.2.0",
60
- "ora": "^8.0.0",
61
- "react": "^19.2.5"
60
+ "ora": "^9.4.1",
61
+ "react": "^19.3.0"
62
62
  },
63
63
  "devDependencies": {
64
64
  "@biomejs/biome": "^2.5.13",
65
- "@types/react": "^19.2.14",
66
- "@types/inquirer": "^9.0.0",
67
- "@types/node": "^20.0.0",
68
- "knip": "^6.35.1",
69
- "react-devtools-core": "^6.1.1",
65
+ "@types/react": "^19.3.0",
66
+ "@types/node": "^22.0.0",
67
+ "knip": "^6.38.0",
68
+ "react-devtools-core": "^8.0.0",
70
69
  "typescript": "^7.0.2",
71
- "vite": "^6.0.0",
70
+ "vite": "^8.3.1",
72
71
  "vitest": "^5.0.0"
73
72
  },
74
73
  "engines": {
75
- "node": ">=20.0.0"
74
+ "node": ">=22.12.0"
76
75
  }
77
76
  }
@@ -45,10 +45,7 @@ describe("always_enabled_infra", () => {
45
45
  enabledStacks: [],
46
46
  },
47
47
  },
48
- always_enabled_infra: [
49
- "database-management",
50
- "proxy",
51
- ],
48
+ always_enabled_infra: ["database-management", "proxy"],
52
49
  };
53
50
 
54
51
  expect(projectConfig.always_enabled_infra).toContain("database-management");
@@ -85,10 +82,7 @@ describe("always_enabled_infra", () => {
85
82
  };
86
83
 
87
84
  // TemplateEngine uses ?? operator to default when undefined/null
88
- const defaults = projectConfig.always_enabled_infra ?? [
89
- "database-management",
90
- "proxy",
91
- ];
85
+ const defaults = projectConfig.always_enabled_infra ?? ["database-management", "proxy"];
92
86
 
93
87
  expect(defaults).toContain("database-management");
94
88
  expect(defaults).toContain("proxy");
@@ -2,11 +2,6 @@ import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
- import {
6
- checkFrontendDockerPreflight,
7
- checkGeneratedProjectRuntimeAssets,
8
- checkStarlarkLoadExports,
9
- } from "../doctor.js";
10
5
  import {
11
6
  checkIngressPorts,
12
7
  checkPrivateNpmRegistry,
@@ -18,6 +13,13 @@ import {
18
13
  probeContainerRuntimeError,
19
14
  summarizeTiltBuildError,
20
15
  } from "../../utils/doctor-runtime.js";
16
+ import {
17
+ checkDockerVersions,
18
+ checkFrontendDockerPreflight,
19
+ checkGeneratedProjectRuntimeAssets,
20
+ checkStarlarkLoadExports,
21
+ checkTypeScriptTypeDependencies,
22
+ } from "../doctor.js";
21
23
 
22
24
  describe("doctor frontend Docker preflight", () => {
23
25
  let testDir: string;
@@ -240,6 +242,9 @@ describe("doctor generated runtime asset preflight", () => {
240
242
  "bun-hoisted-symlink-fix.sh",
241
243
  "prisma-bun-client-link-fix.sh",
242
244
  "prisma-normalize-client.sh",
245
+ "prisma-runtime-relink.sh",
246
+ "infisical-entrypoint.sh",
247
+ "migrate.sh",
243
248
  ]) {
244
249
  writeFileSync(join(templateDir, file), "#!/bin/sh\n");
245
250
  }
@@ -268,6 +273,75 @@ describe("doctor generated runtime asset preflight", () => {
268
273
  });
269
274
  });
270
275
 
276
+ describe("doctor TypeScript type dependency check", () => {
277
+ let testDir: string;
278
+ let originalCwd: string;
279
+
280
+ beforeEach(() => {
281
+ originalCwd = process.cwd();
282
+ testDir = join(
283
+ tmpdir(),
284
+ `tdk-doctor-types-bun-${Date.now()}-${Math.random().toString(16).slice(2)}`,
285
+ );
286
+ mkdirSync(testDir, { recursive: true });
287
+ process.chdir(testDir);
288
+ });
289
+
290
+ afterEach(() => {
291
+ process.chdir(originalCwd);
292
+ if (testDir && existsSync(testDir)) {
293
+ rmSync(testDir, { recursive: true, force: true });
294
+ }
295
+ });
296
+
297
+ function writeBackendResource(devDependencies: Record<string, string>) {
298
+ const appDir = join(testDir, "services", "finance", "cash-management-api");
299
+ const generatedDir = join(appDir, ".autogenerated");
300
+ mkdirSync(generatedDir, { recursive: true });
301
+
302
+ writeFileSync(
303
+ join(appDir, "service.json"),
304
+ JSON.stringify(
305
+ {
306
+ appName: "cash-management-api",
307
+ appType: "backend",
308
+ stack: "finance",
309
+ port: 4000,
310
+ },
311
+ null,
312
+ 2,
313
+ ),
314
+ );
315
+ writeFileSync(
316
+ join(appDir, "package.json"),
317
+ JSON.stringify({ name: "cash-management-api", devDependencies }, null, 2),
318
+ );
319
+ writeFileSync(
320
+ join(generatedDir, "tsconfig.docker.autogenerated.json"),
321
+ JSON.stringify({ compilerOptions: { types: ["bun"] } }, null, 2),
322
+ );
323
+ }
324
+
325
+ it('fails when a service\'s tsconfig sets "types": ["bun"] but @types/bun is not a devDependency', () => {
326
+ writeBackendResource({ typescript: "^5.0.0" });
327
+
328
+ const result = checkTypeScriptTypeDependencies();
329
+
330
+ expect(result.didPass).toBe(false);
331
+ expect(result.message).toContain("cash-management-api");
332
+ expect(result.message).toContain("@types/bun");
333
+ expect(result.fix).toContain('"@types/bun"');
334
+ });
335
+
336
+ it('passes when @types/bun is a devDependency alongside "types": ["bun"]', () => {
337
+ writeBackendResource({ typescript: "^5.0.0", "@types/bun": "^1.4.2" });
338
+
339
+ const result = checkTypeScriptTypeDependencies();
340
+
341
+ expect(result.didPass).toBe(true);
342
+ });
343
+ });
344
+
271
345
  describe("doctor ingress + tilt runtime checks", () => {
272
346
  it("detects a foreign Traefik holding host port 80", () => {
273
347
  const dockerPs = [
@@ -290,8 +364,7 @@ describe("doctor ingress + tilt runtime checks", () => {
290
364
  });
291
365
 
292
366
  it("passes when this project's Traefik owns the ingress ports", () => {
293
- const dockerPs =
294
- "beauty_crm_traefik\t0.0.0.0:80->80/tcp,:::80->80/tcp,0.0.0.0:443->443/tcp";
367
+ const dockerPs = "beauty_crm_traefik\t0.0.0.0:80->80/tcp,:::80->80/tcp,0.0.0.0:443->443/tcp";
295
368
  const result = checkIngressPorts((() => dockerPs) as never, "beauty-crm");
296
369
  expect(result.didPass).toBe(true);
297
370
  expect(result.message).toContain("beauty_crm_traefik");
@@ -308,7 +381,7 @@ describe("doctor ingress + tilt runtime checks", () => {
308
381
  buildHistory: [
309
382
  {
310
383
  error:
311
- 'Bind for 0.0.0.0:80 failed: port is already allocated\nerror: exit status 1',
384
+ "Bind for 0.0.0.0:80 failed: port is already allocated\nerror: exit status 1",
312
385
  },
313
386
  ],
314
387
  },
@@ -337,6 +410,66 @@ describe("doctor ingress + tilt runtime checks", () => {
337
410
  expect(result.fix).toMatch(/docker ps --filter publish=80|Free the conflicting/);
338
411
  });
339
412
 
413
+ it("separates sablier.deferStart resources from genuinely pending ones", () => {
414
+ const payload = {
415
+ items: [
416
+ {
417
+ metadata: { name: "data-governance-api" },
418
+ status: { updateStatus: "none", runtimeStatus: "none" },
419
+ },
420
+ {
421
+ metadata: { name: "some-other-stuck-resource" },
422
+ status: { updateStatus: "pending", runtimeStatus: "pending" },
423
+ },
424
+ {
425
+ metadata: { name: "postgres" },
426
+ status: { updateStatus: "ok", runtimeStatus: "ok" },
427
+ },
428
+ ],
429
+ };
430
+
431
+ const parsed = parseTiltResourceFailures(
432
+ JSON.stringify(payload),
433
+ new Set(["data-governance-api"]),
434
+ );
435
+ expect(parsed.failures).toHaveLength(0);
436
+ expect(parsed.deferredCount).toBe(1);
437
+ expect(parsed.pendingCount).toBe(1);
438
+
439
+ const result = checkTiltResourceHealth((() => JSON.stringify(payload)) as never);
440
+ expect(result.didPass).toBe(true);
441
+ // Without an injected deferredNames set (checkTiltResourceHealth computes
442
+ // its own via discoverResources(), best-effort empty outside a project),
443
+ // this specific resource isn't classified as deferred here -- covered by
444
+ // the direct parseTiltResourceFailures assertions above instead.
445
+ });
446
+
447
+ it("does not describe deferred resources as blocked by unrelated failures", () => {
448
+ const payload = {
449
+ items: [
450
+ {
451
+ metadata: { name: "traefik" },
452
+ status: {
453
+ updateStatus: "error",
454
+ runtimeStatus: "unknown",
455
+ buildHistory: [{ error: "Bind for 0.0.0.0:80 failed: port is already allocated" }],
456
+ },
457
+ },
458
+ {
459
+ metadata: { name: "data-governance-api" },
460
+ status: { updateStatus: "none", runtimeStatus: "none" },
461
+ },
462
+ ],
463
+ };
464
+
465
+ const parsed = parseTiltResourceFailures(
466
+ JSON.stringify(payload),
467
+ new Set(["data-governance-api"]),
468
+ );
469
+ expect(parsed.deferredCount).toBe(1);
470
+ expect(parsed.pendingCount).toBe(0);
471
+ });
472
+
340
473
  it("skips tilt resource check when tilt is not running", () => {
341
474
  const result = checkTiltResourceHealth((() => {
342
475
  throw new Error("tilt not running");
@@ -555,3 +688,43 @@ describe("doctor ingress + tilt runtime checks", () => {
555
688
  rmSync(projectRoot, { recursive: true, force: true });
556
689
  });
557
690
  });
691
+
692
+ describe("doctor Docker version check", () => {
693
+ function fakeExec(engine: string, compose: string) {
694
+ return ((command: string) =>
695
+ command.startsWith("docker compose") ? `${compose}\n` : `${engine}\n`) as never;
696
+ }
697
+
698
+ it("passes on Docker Engine 25+ and Compose 2.20.2+", () => {
699
+ const result = checkDockerVersions(fakeExec("29.8.0", "2.39.1"));
700
+
701
+ expect(result.didPass).toBe(true);
702
+ expect(result.isSkipped).toBeFalsy();
703
+ expect(result.message).toContain("29.8.0");
704
+ });
705
+
706
+ it("fails with an upgrade hint when the engine is older than 25", () => {
707
+ const result = checkDockerVersions(fakeExec("24.0.7", "2.39.1"));
708
+
709
+ expect(result.didPass).toBe(false);
710
+ expect(result.message).toContain("Docker Engine 24.0.7");
711
+ expect(result.message).toContain("start_interval");
712
+ expect(result.fix).toContain("Update Docker");
713
+ });
714
+
715
+ it("fails when Compose is older than 2.20.2", () => {
716
+ const result = checkDockerVersions(fakeExec("27.1.1", "v2.20.1"));
717
+
718
+ expect(result.didPass).toBe(false);
719
+ expect(result.message).toContain("Docker Compose v2.20.1");
720
+ });
721
+
722
+ it("skips instead of failing when versions cannot be read", () => {
723
+ const result = checkDockerVersions((() => {
724
+ throw new Error("docker not found");
725
+ }) as never);
726
+
727
+ expect(result.didPass).toBe(true);
728
+ expect(result.isSkipped).toBe(true);
729
+ });
730
+ });
@@ -75,9 +75,7 @@ describe("Project → Phase → Stack → Resource model", () => {
75
75
  });
76
76
 
77
77
  it("deduplicates enabledStacks per phase", () => {
78
- const enabledStacks = Array.from(
79
- new Set(["proxy", "billing", "checkout-app", "billing"]),
80
- );
78
+ const enabledStacks = Array.from(new Set(["proxy", "billing", "checkout-app", "billing"]));
81
79
 
82
80
  expect(enabledStacks).toEqual(["proxy", "billing", "checkout-app"]);
83
81
  expect(enabledStacks.filter((s) => s === "billing")).toHaveLength(1);
@@ -142,7 +140,7 @@ describe("Project → Phase → Stack → Resource model", () => {
142
140
  const resourceEntries = enabledStacks.map((stack) => `"${stack}": True`);
143
141
  const specMaster = `PRE_ALPHA_RESOURCES = {\n${resourceEntries.join(",\n")}\n}`;
144
142
 
145
- expect(specMaster).toContain('PRE_ALPHA_RESOURCES = {');
143
+ expect(specMaster).toContain("PRE_ALPHA_RESOURCES = {");
146
144
  expect(specMaster).toContain('"billing": True');
147
145
  expect(specMaster).toContain('"checkout-app": True');
148
146
  expect(specMaster).not.toContain('"services"');
@@ -34,7 +34,7 @@ describe("project and resource feature E2E", () => {
34
34
  let projectRoot = "";
35
35
 
36
36
  afterEach(() => {
37
- if (projectRoot && projectRoot.startsWith(tmpdir())) {
37
+ if (projectRoot?.startsWith(tmpdir())) {
38
38
  rmSync(projectRoot, { recursive: true, force: true });
39
39
  }
40
40
  });
@@ -113,9 +113,7 @@ describe("project and resource feature E2E", () => {
113
113
  "prisma-normalize-client.sh",
114
114
  ]) {
115
115
  expect(
116
- existsSync(
117
- join(projectRoot, "shared-platform-engineering", "docker-templates", file),
118
- ),
116
+ existsSync(join(projectRoot, "shared-platform-engineering", "docker-templates", file)),
119
117
  ).toBe(true);
120
118
  }
121
119
  }, 10000);
@@ -166,30 +164,12 @@ describe("project and resource feature E2E", () => {
166
164
  runTdk(["project", "--yes"], projectRoot);
167
165
 
168
166
  runTdk(
169
- [
170
- "resource",
171
- "api",
172
- "--type",
173
- "backend",
174
- "--stack",
175
- "alpha",
176
- "--path",
177
- "services/alpha/api",
178
- ],
167
+ ["resource", "api", "--type", "backend", "--stack", "alpha", "--path", "services/alpha/api"],
179
168
  projectRoot,
180
169
  "\n",
181
170
  );
182
171
  runTdk(
183
- [
184
- "resource",
185
- "web",
186
- "--type",
187
- "frontend",
188
- "--stack",
189
- "alpha",
190
- "--path",
191
- "services/alpha/web",
192
- ],
172
+ ["resource", "web", "--type", "frontend", "--stack", "alpha", "--path", "services/alpha/web"],
193
173
  projectRoot,
194
174
  "\n",
195
175
  );
@@ -1,10 +1,9 @@
1
- import { describe, expect, it, beforeEach, afterEach } from "vitest";
2
1
  import { execSync } from "node:child_process";
3
2
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
4
- import { join } from "node:path";
5
3
  import { tmpdir } from "node:os";
4
+ import { dirname, join } from "node:path";
6
5
  import { fileURLToPath } from "node:url";
7
- import { dirname } from "node:path";
6
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
8
7
 
9
8
  // Get the CLI bin path dynamically
10
9
  const __filename = fileURLToPath(import.meta.url);
@@ -48,8 +47,7 @@ describe.skipIf(!EXTENSION_SOURCE_AVAILABLE)("saas-starter cloning and discovery
48
47
  try {
49
48
  // Safety: only delete if path is under tmpdir and contains our test marker
50
49
  if (
51
- testDir &&
52
- testDir.startsWith(tmpdir()) &&
50
+ testDir?.startsWith(tmpdir()) &&
53
51
  testDir.includes("tdk-saas-test-") &&
54
52
  testDir.length > 20
55
53
  ) {
@@ -117,7 +115,7 @@ describe.skipIf(!EXTENSION_SOURCE_AVAILABLE)("saas-starter cloning and discovery
117
115
  // Generate project config
118
116
  execSync(
119
117
  `cd "${starterRoot}" && TDK_EXTENSION_SOURCE="${EXTENSION_SOURCE}" bun "${CLI_BIN_PATH}" project --yes`,
120
- { stdio: "pipe" }
118
+ { stdio: "pipe" },
121
119
  );
122
120
 
123
121
  // Verify project.json has services populated
@@ -148,7 +146,7 @@ describe.skipIf(!EXTENSION_SOURCE_AVAILABLE)("saas-starter cloning and discovery
148
146
 
149
147
  execSync(
150
148
  `cd "${starterRoot}" && TDK_EXTENSION_SOURCE="${EXTENSION_SOURCE}" bun ${CLI_BIN_PATH} project --yes`,
151
- { stdio: "pipe" }
149
+ { stdio: "pipe" },
152
150
  );
153
151
 
154
152
  const specPath = join(starterRoot, ".tdk", ".tdk-out", "spec.master");
@@ -158,10 +156,10 @@ describe.skipIf(!EXTENSION_SOURCE_AVAILABLE)("saas-starter cloning and discovery
158
156
 
159
157
  // Before fix: PRE_ALPHA_RESOURCES = { }
160
158
  // After fix: PRE_ALPHA_RESOURCES = { "app": True, "billing": True, }
161
- expect(specContent).toContain('PRE_ALPHA_RESOURCES = {');
159
+ expect(specContent).toContain("PRE_ALPHA_RESOURCES = {");
162
160
  expect(specContent).toMatch(/"app":\s*True/);
163
161
  expect(specContent).toMatch(/"billing":\s*True/);
164
- expect(specContent).toContain('FOCUS_PRE_ALPHA = list(PRE_ALPHA_RESOURCES.keys())');
162
+ expect(specContent).toContain("FOCUS_PRE_ALPHA = list(PRE_ALPHA_RESOURCES.keys())");
165
163
  });
166
164
 
167
165
  /**
@@ -181,7 +179,7 @@ describe.skipIf(!EXTENSION_SOURCE_AVAILABLE)("saas-starter cloning and discovery
181
179
 
182
180
  execSync(
183
181
  `cd "${starterRoot}" && TDK_EXTENSION_SOURCE="${EXTENSION_SOURCE}" bun ${CLI_BIN_PATH} project --yes`,
184
- { stdio: "pipe" }
182
+ { stdio: "pipe" },
185
183
  );
186
184
 
187
185
  // Doctor should pass (all master configs present)
@@ -25,7 +25,7 @@ describe("stack feature E2E", () => {
25
25
  let projectRoot = "";
26
26
 
27
27
  afterEach(() => {
28
- if (projectRoot && projectRoot.startsWith(tmpdir())) {
28
+ if (projectRoot?.startsWith(tmpdir())) {
29
29
  rmSync(projectRoot, { recursive: true, force: true });
30
30
  }
31
31
  });
@@ -36,7 +36,9 @@ describe("stack feature E2E", () => {
36
36
  const output = runTdk(["project", "--yes"], projectRoot);
37
37
  expect(output).toContain("Generated: services/platform/database-management/docker-compose.yml");
38
38
 
39
- const projectJson = JSON.parse(readFileSync(join(projectRoot, ".tdk", "project.json"), "utf-8"));
39
+ const projectJson = JSON.parse(
40
+ readFileSync(join(projectRoot, ".tdk", "project.json"), "utf-8"),
41
+ );
40
42
  expect(projectJson.stack_features).toBeUndefined();
41
43
  expect(projectJson.phases.pre_alpha.enabledStacks).toContain("database-management");
42
44
 
@@ -51,7 +53,9 @@ describe("stack feature E2E", () => {
51
53
 
52
54
  const compose = readFileSync(composePath, "utf-8");
53
55
  expect(compose).toContain("Stack feature: database-management");
54
- expect(compose).toContain('Source: .tdk/project.json phases.*.enabledStacks includes "database-management"');
56
+ expect(compose).toContain(
57
+ 'Source: .tdk/project.json phases.*.enabledStacks includes "database-management"',
58
+ );
55
59
  expect(compose).toContain("container_name: tdk_stack_feature_");
56
60
  expect(compose).toContain("_postgres");
57
61
  expect(compose).toContain("_database");
@@ -120,7 +124,9 @@ describe("stack feature E2E", () => {
120
124
 
121
125
  runTdk(["project", "--yes"], projectRoot);
122
126
 
123
- const projectJson = JSON.parse(readFileSync(join(projectRoot, ".tdk", "project.json"), "utf-8"));
127
+ const projectJson = JSON.parse(
128
+ readFileSync(join(projectRoot, ".tdk", "project.json"), "utf-8"),
129
+ );
124
130
  expect(projectJson.phases.pre_alpha.enabledStacks).toContain("billing");
125
131
 
126
132
  const spec = readFileSync(join(projectRoot, ".tdk", ".tdk-out", "spec.master"), "utf-8");
@@ -9,6 +9,7 @@ vi.mock("node:child_process", () => ({
9
9
  }));
10
10
 
11
11
  const { upgradeViaBinary, isWritable } = await import("../upgrade.js");
12
+
12
13
  import type { BinaryRelease } from "../upgrade.js";
13
14
 
14
15
  // Regression coverage for: `tdk upgrade` used to only swap the `tdk` binary
@@ -56,7 +57,8 @@ describe("upgradeViaBinary", () => {
56
57
  expect(commands.some((c) => c.startsWith("mkdir -p") && c.includes(engineDir))).toBe(true);
57
58
  expect(
58
59
  commands.some(
59
- (c) => c.includes("tar -xzf") && c.includes(engineDir) && c.includes("--strip-components=1"),
60
+ (c) =>
61
+ c.includes("tar -xzf") && c.includes(engineDir) && c.includes("--strip-components=1"),
60
62
  ),
61
63
  ).toBe(true);
62
64
  });
@@ -77,19 +79,27 @@ describe("upgradeViaBinary", () => {
77
79
  expect(tarIndex).toBeGreaterThan(engineCurlIndex);
78
80
  });
79
81
 
80
- it("never shells out to sudo, and fails fast without touching the network when the install dir isn't writable", async () => {
81
- chmodSync(installDir, 0o555);
82
- try {
83
- const ok = await upgradeViaBinary(tdkPath, release);
84
- expect(ok).toBe(false);
85
- expect(execSyncMock).not.toHaveBeenCalled();
86
- expect(commandsRun().some((c) => c.includes("sudo"))).toBe(false);
87
- } finally {
88
- chmodSync(installDir, 0o755);
89
- }
90
- });
82
+ // root ignores the write-permission bit (accessSync(W_OK) legitimately
83
+ // returns true even on a 0o555 dir), so these checks only hold for
84
+ // non-root users.
85
+ const isRoot = process.getuid?.() === 0;
86
+
87
+ it.skipIf(isRoot)(
88
+ "never shells out to sudo, and fails fast without touching the network when the install dir isn't writable",
89
+ async () => {
90
+ chmodSync(installDir, 0o555);
91
+ try {
92
+ const ok = await upgradeViaBinary(tdkPath, release);
93
+ expect(ok).toBe(false);
94
+ expect(execSyncMock).not.toHaveBeenCalled();
95
+ expect(commandsRun().some((c) => c.includes("sudo"))).toBe(false);
96
+ } finally {
97
+ chmodSync(installDir, 0o755);
98
+ }
99
+ },
100
+ );
91
101
 
92
- it("isWritable reflects actual filesystem permissions", () => {
102
+ it.skipIf(isRoot)("isWritable reflects actual filesystem permissions", () => {
93
103
  expect(isWritable(installDir)).toBe(true);
94
104
  chmodSync(installDir, 0o555);
95
105
  try {