@tdk-landscape/tdk-cli-core 1.3.15 → 1.3.22

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 (274) hide show
  1. package/Tiltfile +144 -0
  2. package/discovery/AGENTS.md +362 -0
  3. package/discovery/TILT_DISCOVERY.star +424 -0
  4. package/discovery/Tiltfile +58 -0
  5. package/discovery/config.star +258 -0
  6. package/discovery/constants.star +127 -0
  7. package/discovery/deterministic.star +211 -0
  8. package/discovery/discovery_daemon.star +191 -0
  9. package/discovery/discovery_orchestrator.star +460 -0
  10. package/discovery/filtering.star +5 -0
  11. package/discovery/identity-management-backend/service.yaml +26 -0
  12. package/discovery/identity-management-frontend/service.yaml +29 -0
  13. package/discovery/identity-sdk/service.yaml +9 -0
  14. package/discovery/incremental_discovery.star +213 -0
  15. package/discovery/indexing.star +7 -0
  16. package/discovery/json_manifest_scanner.star +51 -0
  17. package/discovery/libraries.star +46 -0
  18. package/discovery/loading.star +23 -0
  19. package/discovery/manifest/constants.star +104 -0
  20. package/discovery/manifest/loading.star +386 -0
  21. package/discovery/manifest/normalize.star +281 -0
  22. package/discovery/manifest/validation.star +108 -0
  23. package/discovery/normalization.star +11 -0
  24. package/discovery/registry.star +544 -0
  25. package/discovery/resource_snapshot.py +382 -0
  26. package/discovery/resource_snapshot.star +143 -0
  27. package/discovery/schemas.star +1 -0
  28. package/discovery/services/platform/cdc/debezium/nats-connector/service.yaml +9 -0
  29. package/discovery/services/platform/cdc/nats-http-bridge/service.yaml +9 -0
  30. package/discovery/services/platform/orchestrator/orchestrator-glue-backend/service.yaml +26 -0
  31. package/discovery/services/platform/synthetic-monitor/service.yaml +9 -0
  32. package/discovery/test_resource_snapshot.py +251 -0
  33. package/discovery/validation.star +78 -0
  34. package/discovery/versioning.star +1 -0
  35. package/dist/commands/config.d.ts.map +1 -1
  36. package/dist/commands/config.js +11 -0
  37. package/dist/commands/config.js.map +1 -1
  38. package/dist/commands/doctor.d.ts.map +1 -1
  39. package/dist/commands/doctor.js +1 -2
  40. package/dist/commands/doctor.js.map +1 -1
  41. package/dist/commands/project.d.ts.map +1 -1
  42. package/dist/commands/project.js +36 -41
  43. package/dist/commands/project.js.map +1 -1
  44. package/dist/commands/resource.d.ts.map +1 -1
  45. package/dist/commands/resource.js +51 -55
  46. package/dist/commands/resource.js.map +1 -1
  47. package/dist/commands/stack.d.ts.map +1 -1
  48. package/dist/commands/stack.js +22 -24
  49. package/dist/commands/stack.js.map +1 -1
  50. package/dist/commands/ui.d.ts.map +1 -1
  51. package/dist/commands/ui.js +1 -2
  52. package/dist/commands/ui.js.map +1 -1
  53. package/dist/commands/up.d.ts.map +1 -1
  54. package/dist/commands/up.js +1 -2
  55. package/dist/commands/up.js.map +1 -1
  56. package/dist/commands/upgrade.d.ts +8 -0
  57. package/dist/commands/upgrade.d.ts.map +1 -1
  58. package/dist/commands/upgrade.js +59 -18
  59. package/dist/commands/upgrade.js.map +1 -1
  60. package/dist/generator/extension-fetch.d.ts +19 -0
  61. package/dist/generator/extension-fetch.d.ts.map +1 -1
  62. package/dist/generator/extension-fetch.js +71 -5
  63. package/dist/generator/extension-fetch.js.map +1 -1
  64. package/dist/generator/template-engine.d.ts +1 -0
  65. package/dist/generator/template-engine.d.ts.map +1 -1
  66. package/dist/generator/template-engine.js +50 -6
  67. package/dist/generator/template-engine.js.map +1 -1
  68. package/dist/utils/command-helpers.d.ts.map +1 -1
  69. package/dist/utils/command-helpers.js +7 -9
  70. package/dist/utils/command-helpers.js.map +1 -1
  71. package/dist/utils/constants.d.ts +1 -0
  72. package/dist/utils/constants.d.ts.map +1 -1
  73. package/dist/utils/constants.js +1 -0
  74. package/dist/utils/constants.js.map +1 -1
  75. package/dist/utils/errors.d.ts +2 -0
  76. package/dist/utils/errors.d.ts.map +1 -1
  77. package/dist/utils/errors.js +6 -1
  78. package/dist/utils/errors.js.map +1 -1
  79. package/dist/utils/project-features.d.ts.map +1 -1
  80. package/dist/utils/project-features.js +1 -1
  81. package/dist/utils/project-features.js.map +1 -1
  82. package/dist/utils/resource-features.d.ts.map +1 -1
  83. package/dist/utils/resource-features.js +2 -2
  84. package/dist/utils/resource-features.js.map +1 -1
  85. package/dist/utils/tar.js +4 -4
  86. package/dist/utils/tar.js.map +1 -1
  87. package/dist/utils/validation.d.ts +7 -0
  88. package/dist/utils/validation.d.ts.map +1 -1
  89. package/dist/utils/validation.js +9 -0
  90. package/dist/utils/validation.js.map +1 -1
  91. package/engine/AGENTS.md +705 -0
  92. package/engine/README.md +29 -0
  93. package/engine/REFACTORING_MANIFEST_SYSTEM.md +974 -0
  94. package/engine/Tiltfile +80 -0
  95. package/engine/assets/docker/golden-layers.Dockerfile +221 -0
  96. package/engine/assets/scripts/cleanup-completed-migrator-images.sh +37 -0
  97. package/engine/assets/scripts/cleanup-tilt-images.sh +120 -0
  98. package/engine/assets/scripts/mac-m1-resource-monitor.sh +121 -0
  99. package/engine/config.star +3 -0
  100. package/engine/docs/AUTODISCOVERY_TEST.md +182 -0
  101. package/engine/docs/C4-ARCHITECTURE-operations.md +396 -0
  102. package/engine/docs/C4-ARCHITECTURE-overview.md +284 -0
  103. package/engine/docs/C4-ARCHITECTURE.md +17 -0
  104. package/engine/docs/GOLDEN_IMAGE_INTEGRATION.md +349 -0
  105. package/engine/docs/MANIFEST_DRIVEN_ARCHITECTURE.md +357 -0
  106. package/engine/docs/README.md +284 -0
  107. package/engine/entrypoint.star +97 -0
  108. package/engine/lifecycle/orchestrator.star +260 -0
  109. package/engine/paths.star +717 -0
  110. package/engine/schemas/manifest-schema.json +301 -0
  111. package/engine/schemas/service-schema.json +301 -0
  112. package/engine/scripts/README.md +89 -0
  113. package/engine/scripts/build-and-publish-lib.sh +205 -0
  114. package/engine/scripts/check-lib-publish.sh +85 -0
  115. package/engine/scripts/debug-container.sh +258 -0
  116. package/engine/scripts/generate-manifest.sh +197 -0
  117. package/engine/spec.master +702 -0
  118. package/engine/spec.master.example +341 -0
  119. package/engine/topologies/README.md +34 -0
  120. package/engine/topologies/platform/docker/AGENTS.md +59 -0
  121. package/engine/topologies/platform/docker/auth/auth.star +167 -0
  122. package/engine/topologies/platform/docker/build/golden_image.star +37 -0
  123. package/engine/topologies/platform/docker/build/golden_image_build.star +82 -0
  124. package/engine/topologies/platform/docker/build/golden_image_constants.star +93 -0
  125. package/engine/topologies/platform/docker/build/golden_image_dockerfile.star +291 -0
  126. package/engine/topologies/platform/docker/compose/compose.star +636 -0
  127. package/engine/topologies/platform/docker/compose/compose_helpers.star +52 -0
  128. package/engine/topologies/platform/docker/compose/traefik_standalone.star +53 -0
  129. package/engine/topologies/platform/docker/config/bunignore.star +142 -0
  130. package/engine/topologies/platform/docker/config/dockerignore.star +150 -0
  131. package/engine/topologies/platform/docker/constants.star +170 -0
  132. package/engine/topologies/platform/docker/dockerfile/dockerfile.star +54 -0
  133. package/engine/topologies/platform/docker/generators/golden_docker_generator_v2.star +73 -0
  134. package/engine/topologies/platform/docker/index.star +27 -0
  135. package/engine/topologies/platform/docker/layers/bun_helpers.star +23 -0
  136. package/engine/topologies/platform/docker/layers/infisical/index.star +40 -0
  137. package/engine/topologies/platform/docker/layers/infisical/infisical_docker.star +262 -0
  138. package/engine/topologies/platform/docker/layers/l1_base_layers.star +87 -0
  139. package/engine/topologies/platform/docker/layers/l2_dependency_layers.star +125 -0
  140. package/engine/topologies/platform/docker/layers/l3_builder_layers.star +209 -0
  141. package/engine/topologies/platform/docker/layers/l4_runtime_layers.star +214 -0
  142. package/engine/topologies/platform/docker/layers/prisma/constants.star +135 -0
  143. package/engine/topologies/platform/docker/layers/prisma/db_readiness.star +96 -0
  144. package/engine/topologies/platform/docker/layers/prisma/filesystem_setup.star +109 -0
  145. package/engine/topologies/platform/docker/layers/prisma/migrate_script.star +98 -0
  146. package/engine/topologies/platform/docker/layers/prisma/prisma_build.star +126 -0
  147. package/engine/topologies/platform/docker/layers/prisma/prisma_runtime.star +161 -0
  148. package/engine/topologies/platform/docker/layers/prisma/secret_resolution.star +119 -0
  149. package/engine/topologies/platform/docker/networking/api_path_constants.star +230 -0
  150. package/engine/topologies/platform/docker/networking/sablier_container_cycle.star +27 -0
  151. package/engine/topologies/platform/docker/networking/traefik.star +232 -0
  152. package/engine/topologies/platform/docker/networking/traefik_constants.star +146 -0
  153. package/engine/topologies/platform/docker/networking/traefik_helpers.star +160 -0
  154. package/engine/topologies/platform/docker/secrets/infisical.star +135 -0
  155. package/engine/topologies/platform/net/proxy.star +4 -0
  156. package/engine/topologies/platform/registries/local.star +4 -0
  157. package/engine/topologies/platform/security/secrets.star +239 -0
  158. package/engine/topologies/platform/state/relational.star +4 -0
  159. package/engine/topologies/tilt/common/AGENTS.md +59 -0
  160. package/engine/topologies/tilt/common/utils.star +364 -0
  161. package/engine/topologies/tilt/common/utils_debug.star +69 -0
  162. package/engine/topologies/tilt/common/utils_deterministic.star +168 -0
  163. package/engine/topologies/tilt/common/utils_docker_networks.star +46 -0
  164. package/engine/topologies/tilt/common/utils_env.star +43 -0
  165. package/engine/topologies/tilt/common/utils_output.star +97 -0
  166. package/engine/topologies/tilt/config/AGENTS.md +68 -0
  167. package/engine/topologies/tilt/config/defaults.star +3 -0
  168. package/engine/topologies/tilt/config/feature_flags.star +10 -0
  169. package/engine/topologies/tilt/config/global.star +55 -0
  170. package/engine/topologies/tilt/config/profiles.star +382 -0
  171. package/engine/topologies/tilt/generators/agents_md.star +62 -0
  172. package/engine/topologies/tilt/generators/api_gateway.star +145 -0
  173. package/engine/topologies/tilt/generators/index.star +124 -0
  174. package/engine/topologies/tilt/generators/infisical/README.md +424 -0
  175. package/engine/topologies/tilt/generators/infisical/examples.star +312 -0
  176. package/engine/topologies/tilt/generators/infisical/index.star +57 -0
  177. package/engine/topologies/tilt/generators/infisical/machine_identity.star +420 -0
  178. package/engine/topologies/tilt/generators/infisical/organization.star +534 -0
  179. package/engine/topologies/tilt/generators/infisical/path_manager.star +476 -0
  180. package/engine/topologies/tilt/generators/infisical/secrets_generator.star +420 -0
  181. package/engine/topologies/tilt/generators/nginx_conf.star +143 -0
  182. package/engine/topologies/tilt/generators/npmrc.star +111 -0
  183. package/engine/topologies/tilt/generators/tsconfig.star +23 -0
  184. package/engine/topologies/tilt/generators/typescript/backend_library_tsconfig.star +71 -0
  185. package/engine/topologies/tilt/generators/typescript/backend_tsconfig.star +124 -0
  186. package/engine/topologies/tilt/generators/typescript/common.star +7 -0
  187. package/engine/topologies/tilt/generators/typescript/frontend_library_tsconfig.star +76 -0
  188. package/engine/topologies/tilt/generators/typescript/frontend_tsconfig.star +110 -0
  189. package/engine/topologies/tilt/generators/typescript/include.star +23 -0
  190. package/engine/topologies/tilt/generators/typescript/playwright_config.star +40 -0
  191. package/engine/topologies/tilt/generators/typescript/prisma_config.star +49 -0
  192. package/engine/topologies/tilt/generators/validators.star +853 -0
  193. package/engine/topologies/tilt/generators/vite/backend.star +78 -0
  194. package/engine/topologies/tilt/generators/vite/frontend.star +98 -0
  195. package/engine/topologies/tilt/generators/vite/generators.star +43 -0
  196. package/engine/topologies/tilt/generators/vite/helpers.star +244 -0
  197. package/engine/topologies/tilt/generators/vite/library.star +65 -0
  198. package/engine/topologies/tilt/generators/vite/sdk.star +73 -0
  199. package/engine/topologies/tilt/generators/vite/templates.star +737 -0
  200. package/engine/topologies/tilt/generators/vite_config.star +22 -0
  201. package/engine/topologies/tilt/manifest/AGENTS.md +61 -0
  202. package/engine/topologies/tilt/manifest/README.md +693 -0
  203. package/engine/topologies/tilt/manifest/__init__.star +218 -0
  204. package/engine/topologies/tilt/manifest/constants.star +252 -0
  205. package/engine/topologies/tilt/manifest/errors.star +266 -0
  206. package/engine/topologies/tilt/manifest/integration.star +339 -0
  207. package/engine/topologies/tilt/manifest/json_to_yaml.py +54 -0
  208. package/engine/topologies/tilt/manifest/loader.star +214 -0
  209. package/engine/topologies/tilt/manifest/mapper.star +227 -0
  210. package/engine/topologies/tilt/manifest/parser.star +400 -0
  211. package/engine/topologies/tilt/manifest/schema.star +462 -0
  212. package/engine/topologies/tilt/manifest/tests/test_integration.star +276 -0
  213. package/engine/topologies/tilt/manifest/tests/test_loader_parser.star +397 -0
  214. package/engine/topologies/tilt/manifest/tests/test_manifest.star +316 -0
  215. package/engine/topologies/tilt/manifest/tests/test_validator.star +470 -0
  216. package/engine/topologies/tilt/manifest/validator.star +717 -0
  217. package/engine/topologies/tilt/manifest/yaml_to_json.py +18 -0
  218. package/engine/topologies/tilt/resources/AGENTS.md +71 -0
  219. package/engine/topologies/tilt/resources/conditions.star +3 -0
  220. package/engine/topologies/tilt/resources/databases.star +244 -0
  221. package/engine/topologies/tilt/resources/declaration.star +3 -0
  222. package/engine/topologies/tilt/resources/deps.star +3 -0
  223. package/engine/topologies/tilt/resources/infra-loader.star +437 -0
  224. package/engine/topologies/tilt/resources/libs.star +30 -0
  225. package/engine/topologies/tilt/resources/libs_discovery.star +102 -0
  226. package/engine/topologies/tilt/resources/libs_paths.star +79 -0
  227. package/engine/topologies/tilt/resources/libs_resource_definition.star +151 -0
  228. package/engine/topologies/tilt/resources/libs_setup.star +164 -0
  229. package/engine/topologies/tilt/resources/libs_tsconfig.star +37 -0
  230. package/engine/topologies/tilt/resources/orchestrator/apply.star +77 -0
  231. package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +431 -0
  232. package/engine/topologies/tilt/resources/orchestrator/apply_manifest_orchestration.star +81 -0
  233. package/engine/topologies/tilt/resources/orchestrator/apply_migrator_orchestration.star +148 -0
  234. package/engine/topologies/tilt/resources/orchestrator/apply_resource_validation.star +36 -0
  235. package/engine/topologies/tilt/resources/orchestrator/apply_runtime_flags.star +44 -0
  236. package/engine/topologies/tilt/resources/orchestrator/builders/typescript.star +164 -0
  237. package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/AGENTS.md +75 -0
  238. package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/index.star +21 -0
  239. package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/resolver.star +134 -0
  240. package/engine/topologies/tilt/resources/orchestrator/generators/env.star +258 -0
  241. package/engine/topologies/tilt/resources/orchestrator/generators/frontend.star +169 -0
  242. package/engine/topologies/tilt/resources/orchestrator/generators/manifest_resource.star +524 -0
  243. package/engine/topologies/tilt/resources/orchestrator/helpers.star +33 -0
  244. package/engine/topologies/tilt/resources/orchestrator.star +54 -0
  245. package/engine/topologies/tilt/resources/ordering.star +3 -0
  246. package/engine/topologies/tilt/resources/triggers.star +3 -0
  247. package/engine/topologies/tilt/resources/watchers.star +299 -0
  248. package/engine/where-things-live.star +717 -0
  249. package/ext/Tiltfile +66 -0
  250. package/ext/ide-components/README.md +219 -0
  251. package/ext/ide-components/code_executor/server.py +268 -0
  252. package/ext/ide-components/code_viewer/server.py +206 -0
  253. package/ext/ide-components/config_inspector/server.py +1050 -0
  254. package/ext/ide-components/file_browser/server.py +285 -0
  255. package/ext/ide-components/shared/config_service.py +469 -0
  256. package/ext/ide-components/shared/file_utils.py +298 -0
  257. package/ext/ide-components/shared/http_utils.py +232 -0
  258. package/ext/ide-components/shared/static/styles.css +1334 -0
  259. package/ext/ide-components/shared/syntax_highlighter.py +140 -0
  260. package/ext/ide-components/shared/template_engine.py +123 -0
  261. package/ext/ide-components/shared/templates/base.html +121 -0
  262. package/ext/ide-components/shared/test_config_service.py +326 -0
  263. package/ext/ide-components/shared/tilt_integration.py +158 -0
  264. package/ext/ui-enhancements/cron-status-server.sh +49 -0
  265. package/ext/ui-enhancements/cron-status-tracker.sh +146 -0
  266. package/ext/ui-enhancements/extension.yaml +36 -0
  267. package/ext/ui-enhancements/tiltfile.py +99 -0
  268. package/ext/ui-enhancements/ui/cron-jobs-tab.css +396 -0
  269. package/ext/ui-enhancements/ui/help-panel.css +279 -0
  270. package/ext/ui-enhancements/ui/icons.css +125 -0
  271. package/ext/ui-enhancements/ui/tooltips.css +114 -0
  272. package/package.json +13 -9
  273. package/specs/specs/TILT_SERVICE_DEFAULTS.star +205 -0
  274. package/specs/specs/TILT_TECH_STACK.star +159 -0
@@ -0,0 +1,717 @@
1
+ # =============================================================================
2
+ # 📋 MANIFEST MODULE - VALIDATOR
3
+ # =============================================================================
4
+ # Path: .tilt/topologies/tilt/manifest/validator.star
5
+ # Purpose: Comprehensive manifest validation with schema, values, and dependencies
6
+ # Status: Phase 3 of manifest system refactoring
7
+ # =============================================================================
8
+
9
+ # === INLINED CONSTANTS for pure extension loading ===
10
+ VALID_STACKS = () # Stacks are project-specific, discovered dynamically
11
+ VALID_FEATURES = "api-client", "env-config", "api-index", "prisma", "nats", "redis", "infisical", "vitest", "traefik", "websocket", "graphql", "grpc", "vite-node", "maintenance", "sablier"
12
+ PORT_RANGES = {"frontend": {"min": 3000, "max": 5999}, "backend": {"min": 4000, "max": 5999}, "worker": {"min": 6000, "max": 6999}, "migrator": {"min": 7000, "max": 7999}, "sdk": {"min": 3000, "max": 9999}, "library": {"min": 3000, "max": 9999}}
13
+ DEFAULTS = {}
14
+
15
+ # === END INLINED CONSTANTS ===
16
+
17
+
18
+ load("./constants.star",
19
+ "VALID_APP_TYPES",
20
+ "MANIFEST_DEFAULTS",
21
+ "VALIDATION_THRESHOLDS",
22
+ "MANIFEST_FILENAME",
23
+ )
24
+
25
+ load("./schema.star", "ManifestSchema")
26
+ load("./errors.star", "ManifestErrors")
27
+ load("./parser.star", "extract_stack_from_path")
28
+
29
+ def validate(manifest, context=None, level='all'):
30
+ """
31
+ Comprehensive manifest validation at specified level.
32
+
33
+ Args:
34
+ manifest: Manifest dict to validate
35
+ context: Optional context dict:
36
+ - all_manifests: List of all manifests for cross-validation
37
+ - resource_path: Path for error context
38
+ - strict: Boolean for strict mode (treats warnings as errors)
39
+ level: Validation level
40
+ - 'schema': Structure and types only
41
+ - 'values': Value constraints
42
+ - 'cross_field': Cross-field dependencies
43
+ - 'cross_resource': Cross-resource dependencies (requires all_manifests)
44
+ - 'all': All validations (default)
45
+
46
+ Returns:
47
+ struct(
48
+ valid=True/False,
49
+ errors=[], # Critical errors
50
+ warnings=[], # Non-critical issues
51
+ stats={ # Validation statistics
52
+ 'fields_checked': N,
53
+ 'rules_passed': N,
54
+ 'rules_failed': N,
55
+ }
56
+ )
57
+ """
58
+ context = context or {}
59
+ errors = []
60
+ warnings = []
61
+ stats = {'fields_checked': 0, 'rules_passed': 0, 'rules_failed': 0}
62
+
63
+ levels = ['schema', 'values', 'cross_field', 'cross_service']
64
+ if level == 'all':
65
+ levels_to_run = levels
66
+ elif level in levels:
67
+ levels_to_run = [level]
68
+ else:
69
+ return struct(
70
+ valid=False,
71
+ errors=[ManifestErrors.new(
72
+ message="Invalid validation level: " + level,
73
+ category=ManifestErrors.CATEGORY['CONFIG'],
74
+ severity=ManifestErrors.SEVERITY['ERROR'],
75
+ )],
76
+ warnings=[],
77
+ stats=stats,
78
+ )
79
+
80
+ # Run schema validation
81
+ if 'schema' in levels_to_run:
82
+ schema_result = _validate_schema(manifest, context)
83
+ errors.extend(schema_result.errors)
84
+ warnings.extend(schema_result.warnings)
85
+ stats['fields_checked'] += schema_result.stats['fields_checked']
86
+ stats['rules_passed'] += schema_result.stats['rules_passed']
87
+ stats['rules_failed'] += schema_result.stats['rules_failed']
88
+
89
+ # Run value validation
90
+ if 'values' in levels_to_run:
91
+ values_result = _validate_values(manifest, context)
92
+ errors.extend(values_result.errors)
93
+ warnings.extend(values_result.warnings)
94
+ stats['fields_checked'] += values_result.stats['fields_checked']
95
+ stats['rules_passed'] += values_result.stats['rules_passed']
96
+ stats['rules_failed'] += values_result.stats['rules_failed']
97
+
98
+ # Run cross-field validation
99
+ if 'cross_field' in levels_to_run:
100
+ cross_field_result = _validate_cross_field(manifest, context)
101
+ errors.extend(cross_field_result.errors)
102
+ warnings.extend(cross_field_result.warnings)
103
+ stats['fields_checked'] += cross_field_result.stats['fields_checked']
104
+ stats['rules_passed'] += cross_field_result.stats['rules_passed']
105
+ stats['rules_failed'] += cross_field_result.stats['rules_failed']
106
+
107
+ # Run cross-resource validation
108
+ if 'cross_resource' in levels_to_run and context.get('all_manifests'):
109
+ cross_resource_result = _validate_cross_resource(manifest, context)
110
+ errors.extend(cross_resource_result.errors)
111
+ warnings.extend(cross_resource_result.warnings)
112
+ stats['fields_checked'] += cross_resource_result.stats['fields_checked']
113
+ stats['rules_passed'] += cross_resource_result.stats['rules_passed']
114
+ stats['rules_failed'] += cross_resource_result.stats['rules_failed']
115
+
116
+ # In strict mode, treat warnings as errors
117
+ if context.get('strict'):
118
+ errors.extend([ManifestErrors.new(
119
+ message=w.message,
120
+ category=w.category,
121
+ severity=ManifestErrors.SEVERITY['ERROR'],
122
+ context=w.context,
123
+ ) for w in warnings])
124
+ warnings = []
125
+
126
+ valid = len([e for e in errors if e.severity == ManifestErrors.SEVERITY['CRITICAL'] or
127
+ e.severity == ManifestErrors.SEVERITY['ERROR']]) == 0
128
+
129
+ return struct(
130
+ valid=valid,
131
+ errors=errors,
132
+ warnings=warnings,
133
+ stats=stats,
134
+ )
135
+
136
+ def _validate_schema(manifest, context):
137
+ """
138
+ Level 1: Schema Validation
139
+ - Check all required fields present
140
+ - Validate field types
141
+ - Check field formats (regex patterns)
142
+ - Validate enum values
143
+ """
144
+ errors = []
145
+ warnings = []
146
+ stats = {'fields_checked': 0, 'rules_passed': 0, 'rules_failed': 0}
147
+
148
+ if not manifest:
149
+ errors.append(ManifestErrors.new(
150
+ message="Manifest is empty or None",
151
+ category=ManifestErrors.CATEGORY['SCHEMA'],
152
+ severity=ManifestErrors.SEVERITY['CRITICAL'],
153
+ context={'path': context.get('resource_path', '')},
154
+ ))
155
+ return struct(errors=errors, warnings=warnings, stats=stats)
156
+
157
+ # Check required fields
158
+ required_fields = ManifestSchema.get_required_fields(manifest)
159
+ for field_name in required_fields:
160
+ stats['fields_checked'] += 1
161
+
162
+ if field_name not in manifest or manifest[field_name] == None or manifest[field_name] == '':
163
+ errors.append(ManifestErrors.new(
164
+ message="Missing required field: " + field_name,
165
+ category=ManifestErrors.CATEGORY['SCHEMA'],
166
+ severity=ManifestErrors.SEVERITY['ERROR'],
167
+ context={'field': field_name, 'path': context.get('resource_path', '')},
168
+ ))
169
+ stats['rules_failed'] += 1
170
+ else:
171
+ stats['rules_passed'] += 1
172
+
173
+ # Validate field types and constraints
174
+ for field_name, value in manifest.items():
175
+ if field_name.startswith('_'): # Skip internal fields
176
+ continue
177
+
178
+ field_schema = ManifestSchema.get_field(field_name)
179
+ if not field_schema:
180
+ # Unknown field - could be a warning
181
+ continue
182
+
183
+ stats['fields_checked'] += 1
184
+
185
+ # Check field type
186
+ field_type = field_schema.get('type')
187
+ type_valid = _check_field_type(field_name, value, field_type, field_schema)
188
+
189
+ if not type_valid:
190
+ errors.append(ManifestErrors.new(
191
+ message="Field {} has invalid type".format(field_name),
192
+ category=ManifestErrors.CATEGORY['SCHEMA'],
193
+ severity=ManifestErrors.SEVERITY['ERROR'],
194
+ context={'field': field_name, 'value': value},
195
+ ))
196
+ stats['rules_failed'] += 1
197
+ else:
198
+ # Check constraints
199
+ constraints = field_schema.get('constraints', {})
200
+ constraint_result = _check_constraints(field_name, value, constraints)
201
+
202
+ if constraint_result:
203
+ errors.append(constraint_result)
204
+ stats['rules_failed'] += 1
205
+ else:
206
+ stats['rules_passed'] += 1
207
+
208
+ return struct(errors=errors, warnings=warnings, stats=stats)
209
+
210
+ def _validate_values(manifest, context):
211
+ """
212
+ Level 2: Value Validation
213
+ - Port range validation
214
+ - Name format validation
215
+ - String length constraints
216
+ - Numeric range constraints
217
+ - Pattern matching
218
+ """
219
+ errors = []
220
+ warnings = []
221
+ stats = {'fields_checked': 0, 'rules_passed': 0, 'rules_failed': 0}
222
+
223
+ # Validate port
224
+ if 'port' in manifest:
225
+ stats['fields_checked'] += 1
226
+ port = manifest['port']
227
+ app_type = manifest.get('appType', 'backend')
228
+
229
+ # Check basic range
230
+ if port < 1024 or port > 65535:
231
+ errors.append(ManifestErrors.new(
232
+ message="Port {} is outside valid range [1024, 65535]".format(port),
233
+ category=ManifestErrors.CATEGORY['VALIDATION'],
234
+ severity=ManifestErrors.SEVERITY['ERROR'],
235
+ context={'field': 'port', 'value': port},
236
+ ))
237
+ stats['rules_failed'] += 1
238
+ else:
239
+ # Check app type specific range
240
+ port_range = PORT_RANGES.get(app_type)
241
+ if port_range:
242
+ if port < port_range['min'] or port > port_range['max']:
243
+ warnings.append(ManifestErrors.new(
244
+ message="Port {} is outside recommended range for {} [{}-{}]".format(
245
+ port, app_type, port_range['min'], port_range['max']
246
+ ),
247
+ category=ManifestErrors.CATEGORY['VALIDATION'],
248
+ severity=ManifestErrors.SEVERITY['WARNING'],
249
+ context={'field': 'port', 'value': port, 'appType': app_type},
250
+ ))
251
+ stats['rules_passed'] += 1
252
+ else:
253
+ stats['rules_passed'] += 1
254
+ else:
255
+ stats['rules_passed'] += 1
256
+
257
+ # Validate appName format
258
+ if 'appName' in manifest:
259
+ stats['fields_checked'] += 1
260
+ app_name = manifest['appName']
261
+
262
+ # Check kebab-case pattern
263
+ if not _matches_pattern(app_name, r'^[a-z][a-z0-9-]*$'):
264
+ errors.append(ManifestErrors.new(
265
+ message="Invalid appName format: {} (must be kebab-case)".format(app_name),
266
+ category=ManifestErrors.CATEGORY['VALIDATION'],
267
+ severity=ManifestErrors.SEVERITY['ERROR'],
268
+ context={'field': 'appName', 'value': app_name},
269
+ ))
270
+ stats['rules_failed'] += 1
271
+ else:
272
+ # Check length
273
+ if len(app_name) < 3:
274
+ errors.append(ManifestErrors.new(
275
+ message="appName too short (min 3 characters): {}".format(app_name),
276
+ category=ManifestErrors.CATEGORY['VALIDATION'],
277
+ severity=ManifestErrors.SEVERITY['ERROR'],
278
+ context={'field': 'appName', 'value': app_name, 'min_length': 3},
279
+ ))
280
+ stats['rules_failed'] += 1
281
+ elif len(app_name) > 50:
282
+ errors.append(ManifestErrors.new(
283
+ message="appName too long (max 50 characters): {}".format(app_name),
284
+ category=ManifestErrors.CATEGORY['VALIDATION'],
285
+ severity=ManifestErrors.SEVERITY['ERROR'],
286
+ context={'field': 'appName', 'value': app_name, 'max_length': 50},
287
+ ))
288
+ stats['rules_failed'] += 1
289
+ else:
290
+ stats['rules_passed'] += 1
291
+
292
+ # Validate replicas
293
+ if 'replicas' in manifest:
294
+ stats['fields_checked'] += 1
295
+ replicas = manifest['replicas']
296
+
297
+ if replicas < 1:
298
+ errors.append(ManifestErrors.new(
299
+ message="replicas must be at least 1: {}".format(replicas),
300
+ category=ManifestErrors.CATEGORY['VALIDATION'],
301
+ severity=ManifestErrors.SEVERITY['ERROR'],
302
+ context={'field': 'replicas', 'value': replicas},
303
+ ))
304
+ stats['rules_failed'] += 1
305
+ elif replicas > VALIDATION_THRESHOLDS['max_replicas']:
306
+ errors.append(ManifestErrors.new(
307
+ message="replicas exceeds maximum ({}): {}".format(
308
+ VALIDATION_THRESHOLDS['max_replicas'], replicas
309
+ ),
310
+ category=ManifestErrors.CATEGORY['VALIDATION'],
311
+ severity=ManifestErrors.SEVERITY['ERROR'],
312
+ context={'field': 'replicas', 'value': replicas, 'max': VALIDATION_THRESHOLDS['max_replicas']},
313
+ ))
314
+ stats['rules_failed'] += 1
315
+ else:
316
+ stats['rules_passed'] += 1
317
+
318
+ # Validate features length
319
+ if 'features' in manifest:
320
+ stats['fields_checked'] += 1
321
+ features = manifest['features']
322
+
323
+ if len(features) > VALIDATION_THRESHOLDS['max_features']:
324
+ warnings.append(ManifestErrors.new(
325
+ message="Too many features ({}), consider splitting resource".format(len(features)),
326
+ category=ManifestErrors.CATEGORY['VALIDATION'],
327
+ severity=ManifestErrors.SEVERITY['WARNING'],
328
+ context={'field': 'features', 'count': len(features), 'max': VALIDATION_THRESHOLDS['max_features']},
329
+ ))
330
+ else:
331
+ stats['rules_passed'] += 1
332
+
333
+ return struct(errors=errors, warnings=warnings, stats=stats)
334
+
335
+ def _validate_cross_field(manifest, context):
336
+ """
337
+ Level 3: Cross-Field Validation
338
+ - Frontend requires backend
339
+ - Prisma requires database
340
+ - Port conflicts with app type
341
+ - Dependencies valid
342
+ """
343
+ errors = []
344
+ warnings = []
345
+ stats = {'fields_checked': 0, 'rules_passed': 0, 'rules_failed': 0}
346
+
347
+ app_type = manifest.get('appType', '')
348
+
349
+ # Frontend requires backendName
350
+ if app_type == 'frontend':
351
+ stats['fields_checked'] += 1
352
+
353
+ if not manifest.get('backendName'):
354
+ errors.append(ManifestErrors.new(
355
+ message="Frontend resource must specify backendName",
356
+ category=ManifestErrors.CATEGORY['VALIDATION'],
357
+ severity=ManifestErrors.SEVERITY['ERROR'],
358
+ context={'field': 'backendName', 'appType': 'frontend'},
359
+ ))
360
+ stats['rules_failed'] += 1
361
+ else:
362
+ stats['rules_passed'] += 1
363
+
364
+ # Frontend should have basePath
365
+ stats['fields_checked'] += 1
366
+ if not manifest.get('basePath'):
367
+ warnings.append(ManifestErrors.new(
368
+ message="Frontend resource should specify basePath for routing",
369
+ category=ManifestErrors.CATEGORY['VALIDATION'],
370
+ severity=ManifestErrors.SEVERITY['WARNING'],
371
+ context={'field': 'basePath', 'appType': 'frontend'},
372
+ ))
373
+ else:
374
+ stats['rules_passed'] += 1
375
+
376
+ # Prisma requires databaseName
377
+ features = manifest.get('features', [])
378
+ if 'prisma' in features:
379
+ stats['fields_checked'] += 1
380
+
381
+ if not manifest.get('databaseName'):
382
+ warnings.append(ManifestErrors.new(
383
+ message="Resource with 'prisma' feature should specify databaseName",
384
+ category=ManifestErrors.CATEGORY['VALIDATION'],
385
+ severity=ManifestErrors.SEVERITY['WARNING'],
386
+ context={'field': 'databaseName', 'features': features},
387
+ ))
388
+ else:
389
+ stats['rules_passed'] += 1
390
+
391
+ # Backend should have Traefik config
392
+ if app_type == 'backend':
393
+ stats['fields_checked'] += 1
394
+
395
+ if not manifest.get('traefik'):
396
+ warnings.append(ManifestErrors.new(
397
+ message="Backend resource should have Traefik configuration for routing",
398
+ category=ManifestErrors.CATEGORY['VALIDATION'],
399
+ severity=ManifestErrors.SEVERITY['WARNING'],
400
+ context={'field': 'traefik', 'appType': 'backend'},
401
+ ))
402
+ else:
403
+ stats['rules_passed'] += 1
404
+
405
+ return struct(errors=errors, warnings=warnings, stats=stats)
406
+
407
+ def _validate_cross_resource(manifest, context):
408
+ """
409
+ Level 4: Cross-Resource Validation
410
+ - dependsOn exist as resources
411
+ - No circular dependencies
412
+ - Backend references are valid
413
+ - Port uniqueness across resources
414
+ """
415
+ errors = []
416
+ warnings = []
417
+ stats = {'fields_checked': 0, 'rules_passed': 0, 'rules_failed': 0}
418
+
419
+ all_manifests = context.get('all_manifests', [])
420
+ resource_path = context.get('resource_path', '')
421
+
422
+ if not all_manifests:
423
+ return struct(errors=errors, warnings=warnings, stats=stats)
424
+
425
+ # Build lookup maps
426
+ app_names = {}
427
+ ports = {}
428
+
429
+ for m in all_manifests:
430
+ name = m.get('appName', '')
431
+ if name:
432
+ app_names[name] = m
433
+
434
+ port = m.get('port')
435
+ if port:
436
+ if port not in ports:
437
+ ports[port] = []
438
+ ports[port].append(name)
439
+
440
+ current_name = manifest.get('appName', '')
441
+ current_port = manifest.get('port')
442
+
443
+ # Check dependsOn exist
444
+ internal_deps = manifest.get('dependsOn', [])
445
+ if internal_deps:
446
+ stats['fields_checked'] += 1
447
+
448
+ missing_deps = []
449
+ for dep in internal_deps:
450
+ if dep not in app_names:
451
+ missing_deps.append(dep)
452
+
453
+ if missing_deps:
454
+ errors.append(ManifestErrors.new(
455
+ message="Missing resource dependencies: {}".format(', '.join(missing_deps)),
456
+ category=ManifestErrors.CATEGORY['DEPENDENCY'],
457
+ severity=ManifestErrors.SEVERITY['ERROR'],
458
+ context={'field': 'dependsOn', 'missing': missing_deps},
459
+ ))
460
+ stats['rules_failed'] += 1
461
+ else:
462
+ stats['rules_passed'] += 1
463
+
464
+ # Check backendName exists (for frontends)
465
+ if manifest.get('appType') == 'frontend':
466
+ backend_name = manifest.get('backendName', '')
467
+ if backend_name:
468
+ stats['fields_checked'] += 1
469
+
470
+ if backend_name not in app_names:
471
+ errors.append(ManifestErrors.new(
472
+ message="Referenced backend '{}' does not exist".format(backend_name),
473
+ category=ManifestErrors.CATEGORY['DEPENDENCY'],
474
+ severity=ManifestErrors.SEVERITY['ERROR'],
475
+ context={'field': 'backendName', 'value': backend_name},
476
+ ))
477
+ stats['rules_failed'] += 1
478
+ else:
479
+ # Check that backend is actually a backend
480
+ backend_manifest = app_names[backend_name]
481
+ if backend_manifest.get('appType') != 'backend':
482
+ errors.append(ManifestErrors.new(
483
+ message="Referenced backend '{}' is not a backend (type: {})".format(
484
+ backend_name, backend_manifest.get('appType')
485
+ ),
486
+ category=ManifestErrors.CATEGORY['DEPENDENCY'],
487
+ severity=ManifestErrors.SEVERITY['ERROR'],
488
+ context={'field': 'backendName', 'value': backend_name},
489
+ ))
490
+ stats['rules_failed'] += 1
491
+ else:
492
+ stats['rules_passed'] += 1
493
+
494
+ # Check port uniqueness
495
+ if current_port and current_port in ports:
496
+ resources_with_port = ports[current_port]
497
+ other_resources = [r for r in resources_with_port if r != current_name]
498
+
499
+ if other_resources:
500
+ stats['fields_checked'] += 1
501
+
502
+ errors.append(ManifestErrors.new(
503
+ message="Port {} is already used by: {}".format(current_port, ', '.join(other_resources)),
504
+ category=ManifestErrors.CATEGORY['VALIDATION'],
505
+ severity=ManifestErrors.SEVERITY['ERROR'],
506
+ context={'field': 'port', 'value': current_port, 'conflicts': other_resources},
507
+ ))
508
+ stats['rules_failed'] += 1
509
+ else:
510
+ stats['rules_passed'] += 1
511
+
512
+ return struct(errors=errors, warnings=warnings, stats=stats)
513
+
514
+ def _check_field_type(field_name, value, expected_type, field_schema):
515
+ """Check if value matches expected type."""
516
+ if expected_type == 'string':
517
+ return type(value) == "string"
518
+ elif expected_type == 'integer':
519
+ return type(value) == "int"
520
+ elif expected_type == 'boolean':
521
+ return type(value) == "bool"
522
+ elif expected_type == 'list':
523
+ return type(value) == "list"
524
+ elif expected_type == 'dict':
525
+ return type(value) == "dict"
526
+ elif expected_type == 'enum':
527
+ valid_values = field_schema.get('constraints', {}).get('values', [])
528
+ return value in valid_values
529
+ return True
530
+
531
+ def _check_constraints(field_name, value, constraints):
532
+ """Check value against constraints."""
533
+ # Check pattern
534
+ if 'pattern' in constraints:
535
+ pattern = constraints['pattern']
536
+ if not _matches_pattern(value, pattern):
537
+ return ManifestErrors.new(
538
+ message="Field {} value does not match pattern: {}".format(field_name, pattern),
539
+ category=ManifestErrors.CATEGORY['VALIDATION'],
540
+ severity=ManifestErrors.SEVERITY['ERROR'],
541
+ context={'field': field_name, 'value': value, 'pattern': pattern},
542
+ )
543
+
544
+ # Check length (for strings and lists)
545
+ if 'min_length' in constraints:
546
+ if len(value) < constraints['min_length']:
547
+ return ManifestErrors.new(
548
+ message="Field {} value too short (min {}): {}".format(
549
+ field_name, constraints['min_length'], len(value)
550
+ ),
551
+ category=ManifestErrors.CATEGORY['VALIDATION'],
552
+ severity=ManifestErrors.SEVERITY['ERROR'],
553
+ context={'field': field_name, 'value': value, 'min_length': constraints['min_length']},
554
+ )
555
+
556
+ if 'max_length' in constraints:
557
+ if len(value) > constraints['max_length']:
558
+ return ManifestErrors.new(
559
+ message="Field {} value too long (max {}): {}".format(
560
+ field_name, constraints['max_length'], len(value)
561
+ ),
562
+ category=ManifestErrors.CATEGORY['VALIDATION'],
563
+ severity=ManifestErrors.SEVERITY['ERROR'],
564
+ context={'field': field_name, 'value': value, 'max_length': constraints['max_length']},
565
+ )
566
+
567
+ return None
568
+
569
+ def _matches_pattern(value, pattern):
570
+ """Simple pattern matching (Starlark doesn't have regex)."""
571
+ # For now, do basic checks
572
+ if pattern == r'^[a-z][a-z0-9-]*$':
573
+ # Kebab-case: starts with letter, then letters/digits/hyphens
574
+ if not value:
575
+ return False
576
+ if not (value[0] >= 'a' and value[0] <= 'z'):
577
+ return False
578
+ for c in value:
579
+ if not ((c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') or c == '-'):
580
+ return False
581
+ return True
582
+
583
+ if pattern == r'^[a-z][a-z0-9_]*$':
584
+ # Snake-case for database names
585
+ if not value:
586
+ return False
587
+ if not (value[0] >= 'a' and value[0] <= 'z'):
588
+ return False
589
+ for c in value:
590
+ if not ((c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') or c == '_'):
591
+ return False
592
+ return True
593
+
594
+ if pattern == r'^/[a-z][a-z0-9/-]*$':
595
+ # Path prefix: starts with /, then letters/digits/hyphens/slashes
596
+ if not value or value[0] != '/':
597
+ return False
598
+ for c in value[1:]:
599
+ if not ((c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') or c == '-' or c == '/'):
600
+ return False
601
+ return True
602
+
603
+ # Default: accept
604
+ return True
605
+
606
+ def is_valid(manifest, context=None):
607
+ """
608
+ Quick check if manifest is valid.
609
+
610
+ Args:
611
+ manifest: Manifest dict to check
612
+ context: Optional context for validation
613
+
614
+ Returns:
615
+ True if valid (no critical errors)
616
+ """
617
+ result = validate(manifest, context, level='all')
618
+ return result.valid
619
+
620
+ def get_validation_report(manifest, context=None):
621
+ """
622
+ Generate detailed validation report.
623
+
624
+ Args:
625
+ manifest: Manifest dict to validate
626
+ context: Optional context for validation
627
+
628
+ Returns:
629
+ Human-readable report string
630
+ """
631
+ result = validate(manifest, context, level='all')
632
+
633
+ lines = []
634
+ lines.append("=" * 70)
635
+ lines.append("📋 MANIFEST VALIDATION REPORT")
636
+ lines.append("=" * 70)
637
+
638
+ # Summary
639
+ status = "✅ PASSED" if result.valid else "❌ FAILED"
640
+ lines.append("Status: " + status)
641
+ lines.append("")
642
+
643
+ # Statistics
644
+ lines.append("Fields Checked: {}".format(result.stats['fields_checked']))
645
+ lines.append("Rules Passed: {}".format(result.stats['rules_passed']))
646
+ lines.append("Rules Failed: {}".format(result.stats['rules_failed']))
647
+ lines.append("")
648
+
649
+ # Errors
650
+ if result.errors:
651
+ lines.append("ERRORS ({}):".format(len(result.errors)))
652
+ for error in result.errors:
653
+ formatted = ManifestErrors.format(error)
654
+ lines.append(" - " + formatted)
655
+ lines.append("")
656
+
657
+ # Warnings
658
+ if result.warnings:
659
+ lines.append("WARNINGS ({}):".format(len(result.warnings)))
660
+ for warning in result.warnings:
661
+ formatted = ManifestErrors.format(warning)
662
+ lines.append(" - " + formatted)
663
+ lines.append("")
664
+
665
+ lines.append("=" * 70)
666
+
667
+ return "\n".join(lines)
668
+
669
+ def validate_schema(manifest):
670
+ """
671
+ Quick schema-only validation.
672
+
673
+ Args:
674
+ manifest: Manifest dict
675
+
676
+ Returns:
677
+ Validation result struct
678
+ """
679
+ return validate(manifest, level='schema')
680
+
681
+ def validate_values(manifest):
682
+ """
683
+ Quick values-only validation.
684
+
685
+ Args:
686
+ manifest: Manifest dict
687
+
688
+ Returns:
689
+ Validation result struct
690
+ """
691
+ return validate(manifest, level='values')
692
+
693
+ def validate_dependencies(manifest, all_manifests):
694
+ """
695
+ Validate dependencies against all resources.
696
+
697
+ Args:
698
+ manifest: Manifest to validate
699
+ all_manifests: List of all manifests for reference
700
+
701
+ Returns:
702
+ Validation result struct
703
+ """
704
+ context = {'all_manifests': all_manifests}
705
+ return validate(manifest, context, level='cross_resource')
706
+
707
+ # Export validator functions
708
+ ManifestValidator = struct(
709
+ validate=validate,
710
+ is_valid=is_valid,
711
+ get_report=get_validation_report,
712
+ validate_schema=validate_schema,
713
+ validate_values=validate_values,
714
+ validate_dependencies=validate_dependencies,
715
+ )
716
+
717
+ VALIDATION = {}