@flisk-sh/flisk 0.0.0-stage → 1.3.190

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 (440) hide show
  1. package/CHANGELOG.md +492 -0
  2. package/LICENSE +21 -0
  3. package/README.md +479 -2
  4. package/Tiltfile +151 -0
  5. package/cli/bin/flisk.js +42 -0
  6. package/cli/dist/agent-runtimes/custom.js +335 -0
  7. package/cli/dist/agent-runtimes/registry.js +34 -0
  8. package/cli/dist/backend-frameworks/elysia.js +38 -0
  9. package/cli/dist/backend-frameworks/express.js +49 -0
  10. package/cli/dist/backend-frameworks/fastify.js +43 -0
  11. package/cli/dist/backend-frameworks/h3.js +37 -0
  12. package/cli/dist/backend-frameworks/koa.js +47 -0
  13. package/cli/dist/backend-frameworks/mcp.js +86 -0
  14. package/cli/dist/backend-frameworks/nestjs.js +55 -0
  15. package/cli/dist/backend-frameworks/registry.js +134 -0
  16. package/cli/dist/backend-frameworks/types.js +1 -0
  17. package/cli/dist/backend-languages/bun.js +76 -0
  18. package/cli/dist/backend-languages/go.js +125 -0
  19. package/cli/dist/backend-languages/node.js +122 -0
  20. package/cli/dist/backend-languages/python.js +94 -0
  21. package/cli/dist/backend-languages/registry.js +37 -0
  22. package/cli/dist/backend-languages/rust.js +82 -0
  23. package/cli/dist/backend-languages/types.js +1 -0
  24. package/cli/dist/cli.js +29 -0
  25. package/cli/dist/commands/__tests__/project-e2e-helpers.js +49 -0
  26. package/cli/dist/commands/__tests__/ui-loading-screen.test.js +16 -0
  27. package/cli/dist/commands/agent.js +182 -0
  28. package/cli/dist/commands/completion-candidates.js +116 -0
  29. package/cli/dist/commands/completion.js +843 -0
  30. package/cli/dist/commands/config.js +362 -0
  31. package/cli/dist/commands/doctor.js +1504 -0
  32. package/cli/dist/commands/down.js +114 -0
  33. package/cli/dist/commands/eject.js +108 -0
  34. package/cli/dist/commands/help.js +152 -0
  35. package/cli/dist/commands/import.js +164 -0
  36. package/cli/dist/commands/logs.js +119 -0
  37. package/cli/dist/commands/maintainers.js +102 -0
  38. package/cli/dist/commands/mcp.js +28 -0
  39. package/cli/dist/commands/networks.js +306 -0
  40. package/cli/dist/commands/project.js +447 -0
  41. package/cli/dist/commands/projects.js +103 -0
  42. package/cli/dist/commands/registry.js +53 -0
  43. package/cli/dist/commands/resource.js +992 -0
  44. package/cli/dist/commands/resources.js +127 -0
  45. package/cli/dist/commands/restart.js +89 -0
  46. package/cli/dist/commands/runtime.js +75 -0
  47. package/cli/dist/commands/stack.js +171 -0
  48. package/cli/dist/commands/stacks.js +71 -0
  49. package/cli/dist/commands/status.js +211 -0
  50. package/cli/dist/commands/ui.js +725 -0
  51. package/cli/dist/commands/up.js +609 -0
  52. package/cli/dist/commands/upgrade.js +840 -0
  53. package/cli/dist/commands/version.js +10 -0
  54. package/cli/dist/components/Accessible.js +11 -0
  55. package/cli/dist/components/BaseTooltip.js +30 -0
  56. package/cli/dist/components/DetailPanel.js +33 -0
  57. package/cli/dist/components/FileTree.js +51 -0
  58. package/cli/dist/components/ResourceSelectInput.js +42 -0
  59. package/cli/dist/components/ResourceTable.js +35 -0
  60. package/cli/dist/components/ServiceIssues.js +13 -0
  61. package/cli/dist/components/TUIHeader.js +27 -0
  62. package/cli/dist/components/TabBar.js +27 -0
  63. package/cli/dist/components/Tooltip.js +13 -0
  64. package/cli/dist/components/index.js +11 -0
  65. package/cli/dist/components/ui-theme.js +120 -0
  66. package/cli/dist/config/platform-standards.js +182 -0
  67. package/cli/dist/frontend-frameworks/lit.js +64 -0
  68. package/cli/dist/frontend-frameworks/preact.js +62 -0
  69. package/cli/dist/frontend-frameworks/qwik.js +61 -0
  70. package/cli/dist/frontend-frameworks/react.js +69 -0
  71. package/cli/dist/frontend-frameworks/registry.js +83 -0
  72. package/cli/dist/frontend-frameworks/solid.js +62 -0
  73. package/cli/dist/frontend-frameworks/svelte.js +67 -0
  74. package/cli/dist/frontend-frameworks/tanstack-router.js +101 -0
  75. package/cli/dist/frontend-frameworks/types.js +1 -0
  76. package/cli/dist/frontend-frameworks/vanilla.js +59 -0
  77. package/cli/dist/frontend-frameworks/vue.js +72 -0
  78. package/cli/dist/generator/__tests__/golden-helpers.js +43 -0
  79. package/cli/dist/generator/extension-fetch.js +291 -0
  80. package/cli/dist/generator/resource-templates.js +133 -0
  81. package/cli/dist/generator/template-engine.js +801 -0
  82. package/cli/dist/index.js +10 -0
  83. package/cli/dist/mcp/server.js +101 -0
  84. package/cli/dist/mcp/tools.js +351 -0
  85. package/cli/dist/program.js +37 -0
  86. package/cli/dist/types/index.js +38 -0
  87. package/cli/dist/utils/agent-host.js +36 -0
  88. package/cli/dist/utils/atomic-write.js +36 -0
  89. package/cli/dist/utils/bun-floor.js +9 -0
  90. package/cli/dist/utils/bun-requirement.js +10 -0
  91. package/cli/dist/utils/cache.js +63 -0
  92. package/cli/dist/utils/cold-preflight.js +257 -0
  93. package/cli/dist/utils/command-helpers.js +57 -0
  94. package/cli/dist/utils/constants.js +160 -0
  95. package/cli/dist/utils/discovery-context.js +71 -0
  96. package/cli/dist/utils/discovery-paths.js +93 -0
  97. package/cli/dist/utils/docker.js +34 -0
  98. package/cli/dist/utils/doctor-crashloop.js +161 -0
  99. package/cli/dist/utils/doctor-fix.js +391 -0
  100. package/cli/dist/utils/doctor-host.js +66 -0
  101. package/cli/dist/utils/doctor-report.js +78 -0
  102. package/cli/dist/utils/doctor-runtime.js +880 -0
  103. package/cli/dist/utils/doctor-starlark.js +132 -0
  104. package/cli/dist/utils/doctor-wiring.js +698 -0
  105. package/cli/dist/utils/down-cleanup.js +125 -0
  106. package/cli/dist/utils/env-validator.js +266 -0
  107. package/cli/dist/utils/errors.js +175 -0
  108. package/cli/dist/utils/exec-async.js +31 -0
  109. package/cli/dist/utils/file-helpers.js +104 -0
  110. package/cli/dist/utils/formatting.js +165 -0
  111. package/cli/dist/utils/generated-paths.js +51 -0
  112. package/cli/dist/utils/help-command.js +11 -0
  113. package/cli/dist/utils/host-port-config.js +140 -0
  114. package/cli/dist/utils/host-port-plan.js +100 -0
  115. package/cli/dist/utils/json-output.js +29 -0
  116. package/cli/dist/utils/machine-output.js +33 -0
  117. package/cli/dist/utils/paths.js +43 -0
  118. package/cli/dist/utils/platform.js +58 -0
  119. package/cli/dist/utils/port-assignment.js +75 -0
  120. package/cli/dist/utils/port-conflict-fix.js +25 -0
  121. package/cli/dist/utils/port-fallback-notice.js +17 -0
  122. package/cli/dist/utils/port-listener-probe.js +20 -0
  123. package/cli/dist/utils/project-config-defaults.js +17 -0
  124. package/cli/dist/utils/project-features.js +91 -0
  125. package/cli/dist/utils/project-templates.js +32 -0
  126. package/cli/dist/utils/prompt.js +97 -0
  127. package/cli/dist/utils/resource-features.js +184 -0
  128. package/cli/dist/utils/resource-kind.js +9 -0
  129. package/cli/dist/utils/schema-version-gate.js +45 -0
  130. package/cli/dist/utils/search-status.js +12 -0
  131. package/cli/dist/utils/secret-redaction.js +73 -0
  132. package/cli/dist/utils/service-config-checks.js +189 -0
  133. package/cli/dist/utils/service-manifest.js +254 -0
  134. package/cli/dist/utils/service-runtime-state.js +66 -0
  135. package/cli/dist/utils/service-urls.js +112 -0
  136. package/cli/dist/utils/services.js +360 -0
  137. package/cli/dist/utils/shared-infra.js +45 -0
  138. package/cli/dist/utils/shared-platform-postgres.js +232 -0
  139. package/cli/dist/utils/single-flight.js +21 -0
  140. package/cli/dist/utils/smoke.js +401 -0
  141. package/cli/dist/utils/spinner.js +40 -0
  142. package/cli/dist/utils/stack-features.js +15 -0
  143. package/cli/dist/utils/stack-status.js +14 -0
  144. package/cli/dist/utils/startup-report.js +78 -0
  145. package/cli/dist/utils/status-message.js +21 -0
  146. package/cli/dist/utils/status-ports.js +33 -0
  147. package/cli/dist/utils/suggestions.js +63 -0
  148. package/cli/dist/utils/tar.js +46 -0
  149. package/cli/dist/utils/tdk-version.js +51 -0
  150. package/cli/dist/utils/terminal-layout.js +19 -0
  151. package/cli/dist/utils/tilt-events.js +194 -0
  152. package/cli/dist/utils/tilt-logs.js +61 -0
  153. package/cli/dist/utils/tilt-port-owner.js +63 -0
  154. package/cli/dist/utils/tilt-process.js +54 -0
  155. package/cli/dist/utils/tilt-resource-state.js +32 -0
  156. package/cli/dist/utils/tilt-startup.js +49 -0
  157. package/cli/dist/utils/tilt-unreachable-message.js +9 -0
  158. package/cli/dist/utils/tilt.js +104 -0
  159. package/cli/dist/utils/ui-keymap.js +129 -0
  160. package/cli/dist/utils/ui-service-state.js +34 -0
  161. package/cli/dist/utils/up-only.js +28 -0
  162. package/cli/dist/utils/up-readiness.js +118 -0
  163. package/cli/dist/utils/url-actions.js +103 -0
  164. package/cli/dist/utils/validation.js +62 -0
  165. package/cli/dist/utils/which.js +56 -0
  166. package/cli/dist/utils/windows-doctor.js +176 -0
  167. package/cli/dist-bundle/chunk-6EA4DGCY.js +2 -0
  168. package/cli/dist-bundle/chunk-BZ4H7MM4.js +3 -0
  169. package/cli/dist-bundle/chunk-CLCUIDBH.js +510 -0
  170. package/cli/dist-bundle/cli.js +2017 -0
  171. package/cli/dist-bundle/cold-preflight-SY26O2IG.js +2 -0
  172. package/cli/dist-bundle/devtools-ZUHXW33W.js +5 -0
  173. package/cli/dist-bundle/extension-fetch-J4FFH7TJ.js +2 -0
  174. package/cli/package.json +83 -0
  175. package/cli/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +25 -0
  176. package/cli/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +466 -0
  177. package/cli/shared-platform-engineering/docker-templates/install-deps.sh +154 -0
  178. package/cli/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +14 -0
  179. package/cli/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +58 -0
  180. package/cli/templates/.tiltignore.hbs +72 -0
  181. package/cli/templates/TILT_RESOURCE_DEFAULTS.star.hbs +220 -0
  182. package/cli/templates/TILT_TECH_STACK.star.hbs +149 -0
  183. package/cli/templates/Tiltfile.hbs +381 -0
  184. package/cli/templates/spec.master.hbs +192 -0
  185. package/discovery/AGENTS.md +362 -0
  186. package/discovery/TILT_DISCOVERY.star +429 -0
  187. package/discovery/Tiltfile +58 -0
  188. package/discovery/config.star +265 -0
  189. package/discovery/constants.star +167 -0
  190. package/discovery/deterministic.star +213 -0
  191. package/discovery/discovery_daemon.star +193 -0
  192. package/discovery/discovery_orchestrator.star +492 -0
  193. package/discovery/filtering.star +7 -0
  194. package/discovery/identity-management-backend/service.yaml +26 -0
  195. package/discovery/identity-management-frontend/service.yaml +29 -0
  196. package/discovery/identity-sdk/service.yaml +9 -0
  197. package/discovery/incremental_discovery.star +215 -0
  198. package/discovery/indexing.star +9 -0
  199. package/discovery/json_manifest_scanner.star +57 -0
  200. package/discovery/libraries.star +48 -0
  201. package/discovery/loading.star +25 -0
  202. package/discovery/manifest/constants.star +106 -0
  203. package/discovery/manifest/loading.star +418 -0
  204. package/discovery/manifest/normalize.star +289 -0
  205. package/discovery/manifest/validation.star +110 -0
  206. package/discovery/normalization.star +13 -0
  207. package/discovery/registry.star +562 -0
  208. package/discovery/resource_snapshot.py +384 -0
  209. package/discovery/resource_snapshot.star +145 -0
  210. package/discovery/schemas.star +3 -0
  211. package/discovery/services/platform/cdc/debezium/nats-connector/service.yaml +9 -0
  212. package/discovery/services/platform/cdc/nats-http-bridge/service.yaml +9 -0
  213. package/discovery/services/platform/orchestrator/orchestrator-glue-backend/service.yaml +26 -0
  214. package/discovery/services/platform/synthetic-monitor/service.yaml +9 -0
  215. package/discovery/test_resource_snapshot.py +253 -0
  216. package/discovery/validation.star +80 -0
  217. package/discovery/versioning.star +3 -0
  218. package/engine/AGENTS.md +706 -0
  219. package/engine/README.md +31 -0
  220. package/engine/REFACTORING_MANIFEST_SYSTEM.md +974 -0
  221. package/engine/Tiltfile +86 -0
  222. package/engine/assets/docker/golden-layers.Dockerfile +204 -0
  223. package/engine/assets/docker/wake-gateway/Dockerfile +28 -0
  224. package/engine/assets/docker/wake-gateway/decide.test.ts +177 -0
  225. package/engine/assets/docker/wake-gateway/decide.ts +135 -0
  226. package/engine/assets/docker/wake-gateway/server.ts +363 -0
  227. package/engine/assets/scripts/cleanup-completed-migrator-images.sh +39 -0
  228. package/engine/assets/scripts/cleanup-tilt-images.sh +122 -0
  229. package/engine/assets/scripts/mac-m1-resource-monitor.sh +123 -0
  230. package/engine/config.star +5 -0
  231. package/engine/entrypoint.star +101 -0
  232. package/engine/lifecycle/orchestrator.star +263 -0
  233. package/engine/paths.star +719 -0
  234. package/engine/schemas/project-schema.json +147 -0
  235. package/engine/schemas/service-schema.json +537 -0
  236. package/engine/scripts/README.md +89 -0
  237. package/engine/scripts/build-and-publish-lib.sh +207 -0
  238. package/engine/scripts/check-lib-publish.sh +87 -0
  239. package/engine/scripts/debug-container.sh +265 -0
  240. package/engine/scripts/generate-manifest.sh +200 -0
  241. package/engine/spec.master +702 -0
  242. package/engine/spec.master.example +341 -0
  243. package/engine/topologies/README.md +33 -0
  244. package/engine/topologies/platform/docker/AGENTS.md +59 -0
  245. package/engine/topologies/platform/docker/auth/auth.star +207 -0
  246. package/engine/topologies/platform/docker/build/frontend_build_plan.star +65 -0
  247. package/engine/topologies/platform/docker/build/golden_image.star +37 -0
  248. package/engine/topologies/platform/docker/build/golden_image_build.star +144 -0
  249. package/engine/topologies/platform/docker/build/golden_image_constants.star +100 -0
  250. package/engine/topologies/platform/docker/build/golden_image_dockerfile.star +263 -0
  251. package/engine/topologies/platform/docker/compose/compose.star +637 -0
  252. package/engine/topologies/platform/docker/compose/compose_helpers.star +54 -0
  253. package/engine/topologies/platform/docker/compose/traefik_standalone.star +171 -0
  254. package/engine/topologies/platform/docker/config/bunignore.star +144 -0
  255. package/engine/topologies/platform/docker/config/dockerignore.star +152 -0
  256. package/engine/topologies/platform/docker/config/healthcheck.star +58 -0
  257. package/engine/topologies/platform/docker/constants.star +211 -0
  258. package/engine/topologies/platform/docker/dockerfile/dockerfile.star +56 -0
  259. package/engine/topologies/platform/docker/dockerfile/language_dockerfile.star +264 -0
  260. package/engine/topologies/platform/docker/generators/golden_docker_generator_v2.star +76 -0
  261. package/engine/topologies/platform/docker/index.star +40 -0
  262. package/engine/topologies/platform/docker/layers/bun_helpers.star +25 -0
  263. package/engine/topologies/platform/docker/layers/infisical/index.star +42 -0
  264. package/engine/topologies/platform/docker/layers/infisical/infisical_docker.star +264 -0
  265. package/engine/topologies/platform/docker/layers/l1_base_layers.star +89 -0
  266. package/engine/topologies/platform/docker/layers/l2_dependency_layers.star +147 -0
  267. package/engine/topologies/platform/docker/layers/l3_builder_layers.star +229 -0
  268. package/engine/topologies/platform/docker/layers/l4_runtime_layers.star +282 -0
  269. package/engine/topologies/platform/docker/layers/prisma/constants.star +138 -0
  270. package/engine/topologies/platform/docker/layers/prisma/db_readiness.star +98 -0
  271. package/engine/topologies/platform/docker/layers/prisma/filesystem_setup.star +131 -0
  272. package/engine/topologies/platform/docker/layers/prisma/migrate_script.star +100 -0
  273. package/engine/topologies/platform/docker/layers/prisma/prisma_build.star +128 -0
  274. package/engine/topologies/platform/docker/layers/prisma/prisma_runtime.star +205 -0
  275. package/engine/topologies/platform/docker/layers/prisma/secret_resolution.star +121 -0
  276. package/engine/topologies/platform/docker/networking/api_path_constants.star +206 -0
  277. package/engine/topologies/platform/docker/networking/sablier_container_cycle.star +31 -0
  278. package/engine/topologies/platform/docker/networking/traefik.star +266 -0
  279. package/engine/topologies/platform/docker/networking/traefik_constants.star +148 -0
  280. package/engine/topologies/platform/docker/networking/traefik_helpers.star +162 -0
  281. package/engine/topologies/platform/docker/networking/traefik_static_routes.star +165 -0
  282. package/engine/topologies/platform/docker/secrets/env_secrets.star +74 -0
  283. package/engine/topologies/platform/docker/secrets/infisical.star +109 -0
  284. package/engine/topologies/platform/net/AGENTS.md +64 -0
  285. package/engine/topologies/platform/net/proxy.star +6 -0
  286. package/engine/topologies/platform/registries/local.star +6 -0
  287. package/engine/topologies/platform/registries/verdaccio_loader.star +20 -0
  288. package/engine/topologies/platform/security/AGENTS.md +49 -0
  289. package/engine/topologies/platform/security/secrets.star +241 -0
  290. package/engine/topologies/platform/state/AGENTS.md +64 -0
  291. package/engine/topologies/platform/state/relational.star +6 -0
  292. package/engine/topologies/tilt/common/AGENTS.md +59 -0
  293. package/engine/topologies/tilt/common/utils.star +409 -0
  294. package/engine/topologies/tilt/common/utils_debug.star +71 -0
  295. package/engine/topologies/tilt/common/utils_deterministic.star +170 -0
  296. package/engine/topologies/tilt/common/utils_docker_networks.star +54 -0
  297. package/engine/topologies/tilt/common/utils_env.star +92 -0
  298. package/engine/topologies/tilt/common/utils_output.star +99 -0
  299. package/engine/topologies/tilt/config/AGENTS.md +68 -0
  300. package/engine/topologies/tilt/config/defaults.star +5 -0
  301. package/engine/topologies/tilt/config/feature_flags.star +12 -0
  302. package/engine/topologies/tilt/config/global.star +57 -0
  303. package/engine/topologies/tilt/config/profiles.star +398 -0
  304. package/engine/topologies/tilt/generators/agents_md.star +64 -0
  305. package/engine/topologies/tilt/generators/api_gateway.star +147 -0
  306. package/engine/topologies/tilt/generators/index.star +126 -0
  307. package/engine/topologies/tilt/generators/infisical/README.md +424 -0
  308. package/engine/topologies/tilt/generators/infisical/examples.star +314 -0
  309. package/engine/topologies/tilt/generators/infisical/index.star +59 -0
  310. package/engine/topologies/tilt/generators/infisical/machine_identity.star +422 -0
  311. package/engine/topologies/tilt/generators/infisical/organization.star +536 -0
  312. package/engine/topologies/tilt/generators/infisical/path_manager.star +478 -0
  313. package/engine/topologies/tilt/generators/infisical/secrets_generator.star +422 -0
  314. package/engine/topologies/tilt/generators/nginx_conf.star +145 -0
  315. package/engine/topologies/tilt/generators/npmrc.star +132 -0
  316. package/engine/topologies/tilt/generators/tsconfig.star +25 -0
  317. package/engine/topologies/tilt/generators/typescript/backend_library_tsconfig.star +73 -0
  318. package/engine/topologies/tilt/generators/typescript/backend_tsconfig.star +137 -0
  319. package/engine/topologies/tilt/generators/typescript/common.star +9 -0
  320. package/engine/topologies/tilt/generators/typescript/frontend_library_tsconfig.star +78 -0
  321. package/engine/topologies/tilt/generators/typescript/frontend_tsconfig.star +140 -0
  322. package/engine/topologies/tilt/generators/typescript/include.star +25 -0
  323. package/engine/topologies/tilt/generators/typescript/playwright_config.star +42 -0
  324. package/engine/topologies/tilt/generators/typescript/prisma_config.star +51 -0
  325. package/engine/topologies/tilt/generators/validators.star +868 -0
  326. package/engine/topologies/tilt/generators/vite/backend.star +80 -0
  327. package/engine/topologies/tilt/generators/vite/frameworks/lit.star +92 -0
  328. package/engine/topologies/tilt/generators/vite/frameworks/preact.star +94 -0
  329. package/engine/topologies/tilt/generators/vite/frameworks/qwik.star +94 -0
  330. package/engine/topologies/tilt/generators/vite/frameworks/react.star +128 -0
  331. package/engine/topologies/tilt/generators/vite/frameworks/solid.star +94 -0
  332. package/engine/topologies/tilt/generators/vite/frameworks/svelte.star +94 -0
  333. package/engine/topologies/tilt/generators/vite/frameworks/vanilla.star +92 -0
  334. package/engine/topologies/tilt/generators/vite/frameworks/vue.star +94 -0
  335. package/engine/topologies/tilt/generators/vite/frontend.star +153 -0
  336. package/engine/topologies/tilt/generators/vite/generators.star +45 -0
  337. package/engine/topologies/tilt/generators/vite/helpers.star +256 -0
  338. package/engine/topologies/tilt/generators/vite/library.star +67 -0
  339. package/engine/topologies/tilt/generators/vite/sdk.star +75 -0
  340. package/engine/topologies/tilt/generators/vite/templates.star +460 -0
  341. package/engine/topologies/tilt/generators/vite_config.star +24 -0
  342. package/engine/topologies/tilt/manifest/AGENTS.md +61 -0
  343. package/engine/topologies/tilt/manifest/README.md +679 -0
  344. package/engine/topologies/tilt/manifest/__init__.star +220 -0
  345. package/engine/topologies/tilt/manifest/constants.star +262 -0
  346. package/engine/topologies/tilt/manifest/errors.star +268 -0
  347. package/engine/topologies/tilt/manifest/integration.star +348 -0
  348. package/engine/topologies/tilt/manifest/json_to_yaml.py +61 -0
  349. package/engine/topologies/tilt/manifest/loader.star +239 -0
  350. package/engine/topologies/tilt/manifest/mapper.star +229 -0
  351. package/engine/topologies/tilt/manifest/parser.star +490 -0
  352. package/engine/topologies/tilt/manifest/schema.star +502 -0
  353. package/engine/topologies/tilt/manifest/tests/test_integration.star +278 -0
  354. package/engine/topologies/tilt/manifest/tests/test_loader_parser.star +475 -0
  355. package/engine/topologies/tilt/manifest/tests/test_manifest.star +319 -0
  356. package/engine/topologies/tilt/manifest/tests/test_validator.star +472 -0
  357. package/engine/topologies/tilt/manifest/validator.star +744 -0
  358. package/engine/topologies/tilt/manifest/yaml_to_json.py +20 -0
  359. package/engine/topologies/tilt/resources/AGENTS.md +71 -0
  360. package/engine/topologies/tilt/resources/build_concurrency.star +56 -0
  361. package/engine/topologies/tilt/resources/conditions.star +5 -0
  362. package/engine/topologies/tilt/resources/databases.star +246 -0
  363. package/engine/topologies/tilt/resources/declaration.star +5 -0
  364. package/engine/topologies/tilt/resources/deps.star +5 -0
  365. package/engine/topologies/tilt/resources/infra-loader.star +504 -0
  366. package/engine/topologies/tilt/resources/libs.star +32 -0
  367. package/engine/topologies/tilt/resources/libs_discovery.star +104 -0
  368. package/engine/topologies/tilt/resources/libs_paths.star +81 -0
  369. package/engine/topologies/tilt/resources/libs_resource_definition.star +153 -0
  370. package/engine/topologies/tilt/resources/libs_setup.star +166 -0
  371. package/engine/topologies/tilt/resources/libs_tsconfig.star +39 -0
  372. package/engine/topologies/tilt/resources/orchestrator/apply.star +79 -0
  373. package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +864 -0
  374. package/engine/topologies/tilt/resources/orchestrator/apply_manifest_orchestration.star +90 -0
  375. package/engine/topologies/tilt/resources/orchestrator/apply_migrator_orchestration.star +150 -0
  376. package/engine/topologies/tilt/resources/orchestrator/apply_resource_validation.star +38 -0
  377. package/engine/topologies/tilt/resources/orchestrator/apply_runtime_flags.star +46 -0
  378. package/engine/topologies/tilt/resources/orchestrator/builders/typescript.star +193 -0
  379. package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/AGENTS.md +75 -0
  380. package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/index.star +23 -0
  381. package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/resolver.star +136 -0
  382. package/engine/topologies/tilt/resources/orchestrator/generators/env.star +219 -0
  383. package/engine/topologies/tilt/resources/orchestrator/generators/frontend.star +172 -0
  384. package/engine/topologies/tilt/resources/orchestrator/generators/manifest_resource.star +577 -0
  385. package/engine/topologies/tilt/resources/orchestrator/helpers.star +35 -0
  386. package/engine/topologies/tilt/resources/orchestrator.star +56 -0
  387. package/engine/topologies/tilt/resources/ordering.star +5 -0
  388. package/engine/topologies/tilt/resources/shared-platform-postgres.star +30 -0
  389. package/engine/topologies/tilt/resources/triggers.star +5 -0
  390. package/engine/topologies/tilt/resources/watchers.star +304 -0
  391. package/engine/where-things-live.star +719 -0
  392. package/examples/tdk-example/LICENSE +21 -0
  393. package/examples/tdk-example/README.md +23 -0
  394. package/examples/tdk-example/TILT_SERVICE_DEFAULTS.star +42 -0
  395. package/examples/tdk-example/TILT_TECH_STACK.star +67 -0
  396. package/examples/tdk-example/Tiltfile +25 -0
  397. package/examples/tdk-example/services/platform/messaging/docker-compose.yml +26 -0
  398. package/examples/tdk-example/services/shop/orders-api/package.json +20 -0
  399. package/examples/tdk-example/services/shop/orders-api/service.json +14 -0
  400. package/examples/tdk-example/services/shop/orders-api/src/index.ts +81 -0
  401. package/examples/tdk-example/services/shop/orders-app/index.html +12 -0
  402. package/examples/tdk-example/services/shop/orders-app/package.json +23 -0
  403. package/examples/tdk-example/services/shop/orders-app/public/.gitkeep +0 -0
  404. package/examples/tdk-example/services/shop/orders-app/service.json +9 -0
  405. package/examples/tdk-example/services/shop/orders-app/src/main.tsx +9 -0
  406. package/examples/tdk-example/services/shop/orders-app/src/orders.tsx +67 -0
  407. package/examples/tdk-example/services/shop/orders-app/src/style.css +15 -0
  408. package/examples/tdk-example/services/shop/orders-worker/package.json +18 -0
  409. package/examples/tdk-example/services/shop/orders-worker/service.json +13 -0
  410. package/examples/tdk-example/services/shop/orders-worker/src/index.ts +33 -0
  411. package/ext/Tiltfile +66 -0
  412. package/ext/ide-components/README.md +221 -0
  413. package/ext/ide-components/code_viewer/server.py +208 -0
  414. package/ext/ide-components/config_inspector/server.py +1052 -0
  415. package/ext/ide-components/file_browser/server.py +287 -0
  416. package/ext/ide-components/shared/config_service.py +471 -0
  417. package/ext/ide-components/shared/file_utils.py +300 -0
  418. package/ext/ide-components/shared/http_utils.py +234 -0
  419. package/ext/ide-components/shared/static/styles.css +1334 -0
  420. package/ext/ide-components/shared/syntax_highlighter.py +142 -0
  421. package/ext/ide-components/shared/template_engine.py +125 -0
  422. package/ext/ide-components/shared/templates/base.html +121 -0
  423. package/ext/ide-components/shared/test_config_service.py +328 -0
  424. package/ext/ide-components/shared/tilt_integration.py +160 -0
  425. package/ext/ui-enhancements/cron-status-server.sh +51 -0
  426. package/ext/ui-enhancements/cron-status-tracker.sh +148 -0
  427. package/ext/ui-enhancements/extension.yaml +36 -0
  428. package/ext/ui-enhancements/tiltfile.py +101 -0
  429. package/ext/ui-enhancements/ui/cron-jobs-tab.css +396 -0
  430. package/ext/ui-enhancements/ui/help-panel.css +279 -0
  431. package/ext/ui-enhancements/ui/icons.css +125 -0
  432. package/ext/ui-enhancements/ui/tooltips.css +114 -0
  433. package/package.json +94 -4
  434. package/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +25 -0
  435. package/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +466 -0
  436. package/shared-platform-engineering/docker-templates/install-deps.sh +154 -0
  437. package/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +14 -0
  438. package/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +58 -0
  439. package/specs/specs/TILT_SERVICE_DEFAULTS.star +207 -0
  440. package/specs/specs/TILT_TECH_STACK.star +161 -0
@@ -0,0 +1,45 @@
1
+ // Copyright (c) 2026 Flisk Landscape contributors
2
+ // SPDX-License-Identifier: MIT
3
+ import { relative } from "node:path";
4
+ import chalk from "chalk";
5
+ import { formatCount } from "./formatting.js";
6
+ import { SERVICE_MANIFEST_SCHEMA_VERSION } from "./service-manifest.js";
7
+ import { discoverResourcesFromRoot } from "./services.js";
8
+ /**
9
+ * A service.json that declares a schemaVersion this flisk does not know is refused before anything starts: a newer format
10
+ * read as if it were the old one could start the wrong thing. A missing schemaVersion is not refused (see
11
+ * warnSchemaVersions), because every project began without one.
12
+ */
13
+ export function enforceSchemaVersionGate(projectRoot, options = {}, exit = process.exit) {
14
+ // A root that cannot be read is reported by the checks that run next; this gate only refuses what it can read.
15
+ let resources;
16
+ if (options.resources) {
17
+ resources = options.resources;
18
+ }
19
+ else {
20
+ try {
21
+ resources = discoverResourcesFromRoot(projectRoot);
22
+ }
23
+ catch {
24
+ return;
25
+ }
26
+ }
27
+ const unsupported = resources.flatMap((resource) => {
28
+ const version = resource.config?.schemaVersion;
29
+ if (version === undefined || version === SERVICE_MANIFEST_SCHEMA_VERSION)
30
+ return [];
31
+ const file = relative(projectRoot, resource.configPath) || resource.configPath;
32
+ return [
33
+ `${file} schemaVersion ${JSON.stringify(version)}: this flisk supports ${SERVICE_MANIFEST_SCHEMA_VERSION}`,
34
+ ];
35
+ });
36
+ if (unsupported.length === 0)
37
+ return;
38
+ const verb = unsupported.length === 1 ? "declares" : "declare";
39
+ const message = `${formatCount(unsupported.length, "service.json file")} ${verb} a schemaVersion this flisk does not support:\n ${unsupported.join("\n ")}`;
40
+ const fix = "Upgrade flisk to a version that supports these files. Changing schemaVersion by hand is not a fix";
41
+ options.onInvalid?.(`${message}\nFix: ${fix}`);
42
+ console.error(chalk.red(`✗ ${message}`));
43
+ console.error(chalk.gray(` Fix: ${fix}`));
44
+ exit(1);
45
+ }
@@ -0,0 +1,12 @@
1
+ /** Describes a `/` search result for the `flisk ui` header; null when there is no query. */
2
+ export function describeSearch(query, matches, total) {
3
+ if (!query)
4
+ return null;
5
+ if (matches === 0) {
6
+ return {
7
+ summary: "0 matches",
8
+ emptyMessage: `No resources match "${query}". Press Esc to clear.`,
9
+ };
10
+ }
11
+ return { summary: `${matches} of ${total}` };
12
+ }
@@ -0,0 +1,73 @@
1
+ // Copyright (c) 2026 Flisk Landscape contributors
2
+ // SPDX-License-Identifier: MIT
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { parseEnv } from "./env-validator.js";
6
+ export const REDACTED = "[REDACTED]";
7
+ export class EnvUnreadableError extends Error {
8
+ path;
9
+ constructor(path, reason) {
10
+ super(`Could not read ${path}: ${reason}`);
11
+ this.path = path;
12
+ this.name = "EnvUnreadableError";
13
+ }
14
+ }
15
+ /**
16
+ * Shorter values are left alone: masking a two-letter value would hit ordinary words in every log line. The cost is
17
+ * that a short secret in .env is not masked, so keep real secrets at least this long.
18
+ */
19
+ export const MIN_REDACTED_VALUE_LENGTH = 8;
20
+ /**
21
+ * Values of the project's own .env file, longest first so a value that contains another is masked whole. Throws when the
22
+ * file exists but cannot be read: continuing without the values would print secrets unmasked.
23
+ */
24
+ export function envSecretValues(projectRoot) {
25
+ const envPath = join(projectRoot, ".env");
26
+ if (!existsSync(envPath))
27
+ return [];
28
+ let content;
29
+ try {
30
+ content = readFileSync(envPath, "utf-8");
31
+ }
32
+ catch (error) {
33
+ throw new EnvUnreadableError(envPath, error instanceof Error ? error.message : String(error));
34
+ }
35
+ const values = [...parseEnv(content).values()].filter((value) => value.length >= MIN_REDACTED_VALUE_LENGTH);
36
+ return [...new Set(values)].sort((a, b) => b.length - a.length);
37
+ }
38
+ export function redactSecrets(text, secrets) {
39
+ let result = text;
40
+ for (const secret of secrets)
41
+ result = result.split(secret).join(REDACTED);
42
+ return result;
43
+ }
44
+ /** Redacts every string inside a JSON-shaped value, so the output stays valid JSON. */
45
+ export function redactValue(value, secrets) {
46
+ if (secrets.length === 0)
47
+ return value;
48
+ if (typeof value === "string")
49
+ return redactSecrets(value, secrets);
50
+ if (Array.isArray(value))
51
+ return value.map((item) => redactValue(item, secrets));
52
+ if (value !== null && typeof value === "object") {
53
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, redactValue(item, secrets)]));
54
+ }
55
+ return value;
56
+ }
57
+ /**
58
+ * A redactor for text that may hold the project's own secrets. When the project's .env exists but cannot be read, the values
59
+ * are unknown, so the redactor throws instead of guessing: the caller then shows and stores nothing rather than raw text. The
60
+ * original error is kept, so a caller can still tell an unreadable .env from another failure.
61
+ */
62
+ export function createEnvRedactor(projectRoot) {
63
+ let secrets;
64
+ try {
65
+ secrets = envSecretValues(projectRoot);
66
+ }
67
+ catch (error) {
68
+ return () => {
69
+ throw error;
70
+ };
71
+ }
72
+ return (text) => redactSecrets(text, secrets);
73
+ }
@@ -0,0 +1,189 @@
1
+ // Copyright (c) 2026 Flisk Landscape contributors
2
+ // SPDX-License-Identifier: MIT
3
+ import { relative } from "node:path";
4
+ import chalk from "chalk";
5
+ import { checkDuplicateResourceNames } from "./doctor-wiring.js";
6
+ import { formatCount } from "./formatting.js";
7
+ import { findProjectRoot } from "./paths.js";
8
+ import { SERVICE_MANIFEST_SCHEMA_VERSION } from "./service-manifest.js";
9
+ import { discoverResourcesFromRoot } from "./services.js";
10
+ function filePath(projectRoot, resource) {
11
+ return relative(projectRoot, resource.configPath) || resource.configPath;
12
+ }
13
+ /** The dependsOn names a service declares: none when absent, null when it is not an array of strings. */
14
+ function declaredDependsOn(resource) {
15
+ const value = resource.config?.dependsOn;
16
+ if (value === undefined)
17
+ return [];
18
+ if (Array.isArray(value) && value.every((entry) => typeof entry === "string"))
19
+ return value;
20
+ return null;
21
+ }
22
+ /** Each cycle once, as service names starting from the alphabetically first, ending where it started. */
23
+ function findDependencyCycles(resources) {
24
+ const known = new Set(resources.map((r) => r.name));
25
+ const edges = new Map(resources.map((r) => [r.name, (declaredDependsOn(r) ?? []).filter((dep) => known.has(dep))]));
26
+ const cycles = new Map();
27
+ const state = new Map();
28
+ const stack = [];
29
+ const visit = (name) => {
30
+ state.set(name, "visiting");
31
+ stack.push(name);
32
+ for (const dep of edges.get(name) ?? []) {
33
+ if (state.get(dep) === "visiting") {
34
+ const loop = stack.slice(stack.indexOf(dep));
35
+ const start = loop.indexOf([...loop].sort()[0] ?? dep);
36
+ const ordered = [...loop.slice(start), ...loop.slice(0, start)];
37
+ cycles.set(ordered.join(">"), [...ordered, ordered[0] ?? dep]);
38
+ }
39
+ else if (!state.has(dep)) {
40
+ visit(dep);
41
+ }
42
+ }
43
+ stack.pop();
44
+ state.set(name, "done");
45
+ };
46
+ for (const name of [...edges.keys()].sort()) {
47
+ if (!state.has(name))
48
+ visit(name);
49
+ }
50
+ return [...cycles.values()];
51
+ }
52
+ /** A dependsOn that is not a list of service names cannot be followed, so say so instead of failing later. */
53
+ export function checkDependsOnShape(projectRoot = findProjectRoot() ?? process.cwd(), resources = discoverResourcesFromRoot(projectRoot)) {
54
+ const problems = resources.flatMap((resource) => declaredDependsOn(resource) === null
55
+ ? [
56
+ `${filePath(projectRoot, resource)} dependsOn ${JSON.stringify(resource.config?.dependsOn)}: expected an array of service names`,
57
+ ]
58
+ : []);
59
+ if (problems.length === 0) {
60
+ return {
61
+ name: "dependsOn",
62
+ didPass: true,
63
+ message: "Every dependsOn is a list of service names",
64
+ };
65
+ }
66
+ return {
67
+ name: "dependsOn",
68
+ didPass: false,
69
+ message: `${formatCount(problems.length, "invalid dependsOn", "invalid dependsOn")}:\n ${problems.join("\n ")}`,
70
+ fix: 'Write "dependsOn" as an array of service names, for example ["postgres", "api"]',
71
+ };
72
+ }
73
+ /** Services that wait on each other can never both start. A dependsOn name that is not a service is left to the unknown-name check. */
74
+ export function checkCircularDependencies(projectRoot = findProjectRoot() ?? process.cwd(), resources = discoverResourcesFromRoot(projectRoot)) {
75
+ const byName = new Map(resources.map((r) => [r.name, r]));
76
+ const cycles = findDependencyCycles(resources);
77
+ if (cycles.length === 0) {
78
+ return {
79
+ name: "Circular dependencies",
80
+ didPass: true,
81
+ message: "No service depends on itself through dependsOn",
82
+ };
83
+ }
84
+ const lines = cycles.map((cycle) => {
85
+ const first = byName.get(cycle[0] ?? "");
86
+ const next = cycle[1] ?? "";
87
+ return `${first ? filePath(projectRoot, first) : cycle[0]} dependsOn "${next}": circular dependency ${cycle.join(" -> ")}`;
88
+ });
89
+ return {
90
+ name: "Circular dependencies",
91
+ didPass: false,
92
+ message: `${cycles.length} ${cycles.length === 1 ? "circular dependency" : "circular dependencies"}:\n ${lines.join("\n ")}`,
93
+ 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',
94
+ };
95
+ }
96
+ /** A port outside 1-65535 can never be bound, and `flisk up` would otherwise find out only when Docker fails. */
97
+ export function checkServicePorts(projectRoot = findProjectRoot() ?? process.cwd(), resources = discoverResourcesFromRoot(projectRoot)) {
98
+ const problems = resources.flatMap((resource) => {
99
+ const port = resource.config?.port;
100
+ if (port === undefined)
101
+ return [];
102
+ if (typeof port === "number" && Number.isInteger(port) && port >= 1 && port <= 65535)
103
+ return [];
104
+ return [
105
+ `${filePath(projectRoot, resource)} port ${JSON.stringify(port)}: not an integer from 1 to 65535`,
106
+ ];
107
+ });
108
+ if (problems.length === 0) {
109
+ return { name: "Service ports", didPass: true, message: "Every declared port is valid" };
110
+ }
111
+ return {
112
+ name: "Service ports",
113
+ didPass: false,
114
+ message: `${formatCount(problems.length, "invalid port")}:\n ${problems.join("\n ")}`,
115
+ fix: 'Set "port" to the port the service listens on, an integer from 1 to 65535',
116
+ };
117
+ }
118
+ /**
119
+ * A service.json with no schemaVersion is how every project began, so this is a warning: it keeps working, and only
120
+ * `flisk config migrate` writes the field. A version this flisk does not know is reported the same way, with a different fix.
121
+ */
122
+ export function checkSchemaVersions(projectRoot = findProjectRoot() ?? process.cwd(), strict = false, resources = discoverResourcesFromRoot(projectRoot)) {
123
+ const missing = [];
124
+ const unsupported = [];
125
+ for (const resource of resources) {
126
+ const version = resource.config?.schemaVersion;
127
+ if (version === SERVICE_MANIFEST_SCHEMA_VERSION)
128
+ continue;
129
+ const file = filePath(projectRoot, resource);
130
+ if (version === undefined)
131
+ missing.push(`${file} schemaVersion: missing`);
132
+ else
133
+ unsupported.push(`${file} schemaVersion ${JSON.stringify(version)}: this flisk supports ${SERVICE_MANIFEST_SCHEMA_VERSION}`);
134
+ }
135
+ if (missing.length === 0 && unsupported.length === 0) {
136
+ return {
137
+ name: "schemaVersion",
138
+ didPass: true,
139
+ message: "Every service.json has a supported schemaVersion",
140
+ };
141
+ }
142
+ const fixes = [
143
+ ...(missing.length > 0 ? ["Run `flisk config migrate` to add the missing schemaVersion"] : []),
144
+ ...(unsupported.length > 0
145
+ ? ["A version this flisk does not support cannot be migrated: upgrade flisk"]
146
+ : []),
147
+ ];
148
+ return {
149
+ name: "schemaVersion",
150
+ didPass: false,
151
+ isWarning: !strict,
152
+ message: `${formatCount(missing.length + unsupported.length, "service.json file")} without a supported schemaVersion:\n ${[...missing, ...unsupported].join("\n ")}`,
153
+ fix: fixes.join(". "),
154
+ };
155
+ }
156
+ /** Prints the schemaVersion warning once. It never stops anything: the project keeps working as it is. */
157
+ export function warnSchemaVersions(projectRoot, resources) {
158
+ const check = checkSchemaVersions(projectRoot, false, resources);
159
+ if (check.didPass)
160
+ return;
161
+ console.warn(chalk.yellow(`⚠ ${check.message}`));
162
+ if (check.fix)
163
+ console.warn(chalk.gray(` ${check.fix}`));
164
+ }
165
+ /**
166
+ * Runs before anything starts (also under --dry-run, like the drift gate): duplicate service names, circular
167
+ * dependsOn and invalid ports exit with 1 and say which file, which field, and how to fix it.
168
+ */
169
+ export function enforceServiceConfigGate(projectRoot, options = {}, exit = process.exit) {
170
+ const resources = options.resources ?? discoverResourcesFromRoot(projectRoot);
171
+ const failed = [
172
+ checkDuplicateResourceNames(projectRoot, resources),
173
+ checkDependsOnShape(projectRoot, resources),
174
+ checkCircularDependencies(projectRoot, resources),
175
+ checkServicePorts(projectRoot, resources),
176
+ ].filter((check) => !check.didPass);
177
+ if (failed.length === 0)
178
+ return;
179
+ const text = failed
180
+ .map((check) => `${check.message}${check.fix ? `\nFix: ${check.fix}` : ""}`)
181
+ .join("\n");
182
+ options.onInvalid?.(text);
183
+ for (const check of failed) {
184
+ console.error(chalk.red(`✗ ${check.message}`));
185
+ if (check.fix)
186
+ console.error(chalk.gray(` Fix: ${check.fix}`));
187
+ }
188
+ exit(1);
189
+ }
@@ -0,0 +1,254 @@
1
+ // Copyright (c) 2026 Flisk Landscape contributors
2
+ // SPDX-License-Identifier: MIT
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ import { validateSmoke } from "./smoke.js";
5
+ export const SERVICE_MANIFEST_SCHEMA_VERSION = 1;
6
+ export const SERVICE_MANIFEST_SCHEMA_URL = "https://tdk-landscape.github.io/schema.service.json";
7
+ /**
8
+ * Fields that are still read, or still tolerated, but should not be used. The warning says what replaces each one. `jwtSecret` is
9
+ * ignored outright: a secret must not be committed (see docs/environment.md).
10
+ */
11
+ const DEPRECATED_FIELDS = {
12
+ dependencies: "deprecated, use dependsOn (still read for now)",
13
+ envVars: "deprecated, use params (still read when params is absent)",
14
+ jwtSecret: "ignored. A secret must not be committed: remove it and set JWT_SECRET in the project .env",
15
+ };
16
+ const KNOWN_SERVICE_FIELDS = new Set([
17
+ "$schema",
18
+ "agent",
19
+ "apiBasePath",
20
+ "apiPath",
21
+ "appName",
22
+ "appType",
23
+ "backendName",
24
+ "basePath",
25
+ "build",
26
+ "buildContext",
27
+ "databaseName",
28
+ "dependencies",
29
+ "dev",
30
+ "dependsOn",
31
+ "dockerfile",
32
+ "enabled",
33
+ "exposeViaProxy",
34
+ "featuresEnabled",
35
+ "framework",
36
+ "healthCheckPath",
37
+ "image",
38
+ "language",
39
+ "name",
40
+ "nats",
41
+ "params",
42
+ "playwright",
43
+ "port",
44
+ "replicas",
45
+ "restart",
46
+ "runtime",
47
+ "sablier",
48
+ "schemaVersion",
49
+ "secrets",
50
+ "smoke",
51
+ "stack",
52
+ "syncs",
53
+ "traefik",
54
+ "type",
55
+ ]);
56
+ /**
57
+ * String fields written into the generated Docker Compose, Traefik and NATS config as they are. A newline or YAML syntax in one of
58
+ * them adds keys to the generated service (GHSA-phgf-pww4-7jxc), so each must match its pattern. Empty values are treated as unset.
59
+ * `tilt up` reads service.json without this CLI, so engine/topologies/tilt/manifest/parser.star enforces the same rules.
60
+ */
61
+ const GENERATED_STRING_FIELDS = [
62
+ { path: ["healthCheckPath"], pattern: /^\/[a-z0-9/-]*$/, expected: "a path such as /health" },
63
+ {
64
+ path: ["traefik", "host"],
65
+ pattern: /^[A-Za-z0-9.-]+$/,
66
+ expected: "a hostname such as shop.backend.myproject.local",
67
+ },
68
+ {
69
+ path: ["traefik", "pathPrefix"],
70
+ pattern: /^\/[a-z0-9/-]*$/,
71
+ expected: "a path such as /api/v1/shop-management",
72
+ },
73
+ {
74
+ path: ["traefik", "healthCheck"],
75
+ pattern: /^\/[a-z0-9/-]*$/,
76
+ expected: "a path such as /health",
77
+ },
78
+ {
79
+ path: ["nats", "queueGroup"],
80
+ pattern: /^[A-Za-z0-9_.-]+$/,
81
+ expected: "letters, digits, '_', '.' or '-'",
82
+ },
83
+ { path: ["databaseName"], pattern: /^[A-Za-z0-9_-]+$/, expected: "letters, digits, '_' or '-'" },
84
+ { path: ["stack"], pattern: /^[A-Za-z0-9_-]+$/, expected: "letters, digits, '_' or '-'" },
85
+ {
86
+ path: ["image"],
87
+ pattern: /^[A-Za-z0-9._/:@-]+$/,
88
+ expected: "an image reference such as ghcr.io/acme/app:1.0",
89
+ },
90
+ ];
91
+ /**
92
+ * Errors for generated-output fields whose values could change the generated YAML. The value itself is not echoed, because it may
93
+ * contain the newline that makes it dangerous.
94
+ */
95
+ export function validateGeneratedStringFields(manifest, displayPath) {
96
+ const errors = [];
97
+ const reportedFields = new Set();
98
+ for (const { path, pattern, expected } of GENERATED_STRING_FIELDS) {
99
+ let value = manifest;
100
+ for (const key of path) {
101
+ value =
102
+ value && typeof value === "object" && !Array.isArray(value)
103
+ ? value[key]
104
+ : undefined;
105
+ }
106
+ if (value === undefined || value === null || value === "")
107
+ continue;
108
+ const field = `${displayPath}.${path.join(".")}`;
109
+ if (typeof value !== "string" || !pattern.test(value))
110
+ reportedFields.add(field);
111
+ if (typeof value !== "string") {
112
+ errors.push(`${field}: expected a string`);
113
+ }
114
+ else if (!pattern.test(value)) {
115
+ errors.push(`${field}: must be ${expected} (no spaces, line breaks or YAML syntax)`);
116
+ }
117
+ }
118
+ for (const [field, message] of lineBreakErrors(manifest, displayPath)) {
119
+ if (!reportedFields.has(field))
120
+ errors.push(`${field}: ${message}`);
121
+ }
122
+ return errors;
123
+ }
124
+ /** Line breaks (including the Unicode ones YAML 1.1 honours) and other control characters. */
125
+ function hasLineBreakOrControl(text) {
126
+ for (let index = 0; index < text.length; index++) {
127
+ const code = text.charCodeAt(index);
128
+ if (code < 0x20 || code === 0x7f || code === 0x85 || code === 0x2028 || code === 0x2029) {
129
+ return true;
130
+ }
131
+ }
132
+ return false;
133
+ }
134
+ /**
135
+ * Many more service.json values than GENERATED_STRING_FIELDS reach the generated YAML (port, appName, dockerfile, buildContext,
136
+ * basePath, envVars, ...). A key can only be added through a line break, so no string key or value may contain one. `smoke` is
137
+ * skipped: the CLI sends it over HTTP and it is never written into generated config.
138
+ */
139
+ function lineBreakErrors(manifest, displayPath) {
140
+ const errors = [];
141
+ const pending = Object.entries(manifest)
142
+ .filter(([key]) => key !== "smoke")
143
+ .map(([key, value]) => [value, `${displayPath}.${key}`]);
144
+ for (const key of Object.keys(manifest)) {
145
+ if (hasLineBreakOrControl(key))
146
+ errors.push([displayPath, "a key contains a line break"]);
147
+ }
148
+ while (pending.length > 0) {
149
+ const [value, field] = pending.pop();
150
+ if (typeof value === "string") {
151
+ if (hasLineBreakOrControl(value)) {
152
+ errors.push([field, "must not contain line breaks or control characters"]);
153
+ }
154
+ }
155
+ else if (Array.isArray(value)) {
156
+ for (const [index, item] of value.entries())
157
+ pending.push([item, `${field}[${index}]`]);
158
+ }
159
+ else if (value && typeof value === "object") {
160
+ for (const [key, item] of Object.entries(value)) {
161
+ if (hasLineBreakOrControl(key)) {
162
+ errors.push([field, "a key contains a line break"]);
163
+ }
164
+ else {
165
+ pending.push([item, `${field}.${key}`]);
166
+ }
167
+ }
168
+ }
169
+ }
170
+ return errors.sort(([a], [b]) => a.localeCompare(b));
171
+ }
172
+ const SERVICE_ROUTE_PATH_FIELDS = ["apiPath", "basePath"];
173
+ export function getInvalidServiceRoutePathFields(value) {
174
+ if (!value || typeof value !== "object" || Array.isArray(value))
175
+ return [];
176
+ const manifest = value;
177
+ return SERVICE_ROUTE_PATH_FIELDS.filter((field) => field in manifest && typeof manifest[field] !== "string");
178
+ }
179
+ export function validateServiceManifest(value, displayPath) {
180
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
181
+ return { errors: [`${displayPath}: expected a JSON object`], warnings: [] };
182
+ }
183
+ const manifest = value;
184
+ const errors = [];
185
+ for (const field of ["appName", "appType", "stack", "schemaVersion"]) {
186
+ if (!(field in manifest))
187
+ errors.push(`${displayPath}.${field}: required field is missing`);
188
+ }
189
+ for (const field of ["appName", "appType", "stack"]) {
190
+ if (field in manifest && typeof manifest[field] !== "string") {
191
+ errors.push(`${displayPath}.${field}: expected a string`);
192
+ }
193
+ }
194
+ if ("schemaVersion" in manifest && manifest.schemaVersion !== SERVICE_MANIFEST_SCHEMA_VERSION) {
195
+ errors.push(`${displayPath}.schemaVersion: unsupported version ${String(manifest.schemaVersion)} (supported: ${SERVICE_MANIFEST_SCHEMA_VERSION})`);
196
+ }
197
+ for (const field of getInvalidServiceRoutePathFields(manifest)) {
198
+ errors.push(`${displayPath}.${field}: expected a string`);
199
+ }
200
+ const dev = manifest.dev;
201
+ const liveReload = dev && typeof dev === "object" && !Array.isArray(dev)
202
+ ? dev.liveReload
203
+ : undefined;
204
+ if (liveReload !== undefined && typeof liveReload !== "boolean") {
205
+ errors.push(`${displayPath}.dev.liveReload: expected true or false`);
206
+ }
207
+ errors.push(...validateGeneratedStringFields(manifest, displayPath));
208
+ if ("smoke" in manifest) {
209
+ for (const message of validateSmoke(manifest.smoke))
210
+ errors.push(`${displayPath}.${message}`);
211
+ }
212
+ const warnings = Object.keys(manifest).flatMap((field) => {
213
+ const deprecation = DEPRECATED_FIELDS[field];
214
+ if (deprecation)
215
+ return [`${displayPath}.${field}: ${deprecation}`];
216
+ if (!KNOWN_SERVICE_FIELDS.has(field))
217
+ return [`${displayPath}.${field}: unknown field is preserved`];
218
+ return [];
219
+ });
220
+ if (liveReload === true && manifest.language !== "go") {
221
+ warnings.push(`${displayPath}.dev.liveReload: only applies to Go services (language "go"); it is ignored here`);
222
+ }
223
+ return { errors, warnings, manifest };
224
+ }
225
+ /**
226
+ * Generated-output errors for one service.json. A file that cannot be read or parsed returns none here, because
227
+ * validateServiceManifestFile reports that case.
228
+ */
229
+ export function generatedFieldErrorsForFile(filePath, displayPath) {
230
+ if (!existsSync(filePath))
231
+ return [];
232
+ try {
233
+ const parsed = JSON.parse(readFileSync(filePath, "utf-8"));
234
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
235
+ return [];
236
+ return validateGeneratedStringFields(parsed, displayPath);
237
+ }
238
+ catch {
239
+ return [];
240
+ }
241
+ }
242
+ export function validateServiceManifestFile(filePath, displayPath) {
243
+ if (!existsSync(filePath)) {
244
+ return { errors: [`${displayPath}: file is missing`], warnings: [] };
245
+ }
246
+ try {
247
+ const parsed = JSON.parse(readFileSync(filePath, "utf-8"));
248
+ return validateServiceManifest(parsed, displayPath);
249
+ }
250
+ catch (error) {
251
+ const message = error instanceof Error ? error.message : String(error);
252
+ return { errors: [`${displayPath}: invalid JSON (${message})`], warnings: [] };
253
+ }
254
+ }
@@ -0,0 +1,66 @@
1
+ import { buildStartupReport, transitiveDependencies } from "./startup-report.js";
2
+ import { effectiveRuntimeStatus, isTiltResourcePending } from "./tilt-resource-state.js";
3
+ import { onlyEnabledResources } from "./up-readiness.js";
4
+ /**
5
+ * The one answer to "is this service ready?" for `flisk status` and `flisk ui`, from Tilt's resources and the `dependsOn`
6
+ * in service.json. A service whose own process is up is still not ready while something it depends on has failed or
7
+ * is still starting. Services Tilt does not list are absent: the caller shows them as unknown.
8
+ */
9
+ export function deriveServiceStates(jsonText, dependsOn, deferred = new Set()) {
10
+ const report = buildStartupReport(jsonText, dependsOn, deferred);
11
+ const failed = new Map(report.failed.map((entry) => [entry.name, entry.message]));
12
+ const blocked = new Map(report.blocked.map((entry) => [entry.name, entry.because]));
13
+ const starting = new Set(report.starting);
14
+ const items = JSON.parse(onlyEnabledResources(jsonText)).items ?? [];
15
+ const direct = new Map();
16
+ for (const item of items) {
17
+ const name = item.metadata?.name;
18
+ if (!name || name === "(Tiltfile)")
19
+ continue;
20
+ if (failed.has(name)) {
21
+ direct.set(name, { status: "error", reason: failed.get(name) });
22
+ }
23
+ else if (blocked.has(name)) {
24
+ const because = blocked.get(name) ?? [];
25
+ direct.set(name, {
26
+ status: "error",
27
+ reason: `${because.join(", ")} failed`,
28
+ blockedBy: because,
29
+ });
30
+ }
31
+ else if (starting.has(name)) {
32
+ direct.set(name, { status: "pending" });
33
+ }
34
+ else if (deferred.has(name) &&
35
+ isTiltResourcePending(item.status?.updateStatus ?? "", effectiveRuntimeStatus(item.status))) {
36
+ direct.set(name, { status: "unknown", reason: "starts on its first request" });
37
+ }
38
+ else {
39
+ direct.set(name, { status: "ready" });
40
+ }
41
+ }
42
+ const result = {};
43
+ for (const [name, state] of direct) {
44
+ if (state.status !== "ready") {
45
+ result[name] = state;
46
+ continue;
47
+ }
48
+ const deps = [...transitiveDependencies(name, dependsOn)].filter((dep) => direct.has(dep));
49
+ const failedDeps = deps.filter((dep) => direct.get(dep)?.status === "error").sort();
50
+ const waitingDeps = deps.filter((dep) => direct.get(dep)?.status === "pending").sort();
51
+ if (failedDeps.length > 0) {
52
+ result[name] = {
53
+ status: "error",
54
+ reason: `${failedDeps.join(", ")} failed`,
55
+ blockedBy: failedDeps,
56
+ };
57
+ }
58
+ else if (waitingDeps.length > 0) {
59
+ result[name] = { status: "pending", reason: `waiting for ${waitingDeps.join(", ")}` };
60
+ }
61
+ else {
62
+ result[name] = state;
63
+ }
64
+ }
65
+ return result;
66
+ }