@aifabrix/builder 2.43.0 → 2.44.1

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 (371) hide show
  1. package/.cursor/rules/anchor-docs.mdc +15 -0
  2. package/.cursor/rules/cli-layout.mdc +75 -0
  3. package/.cursor/rules/project-rules.mdc +8 -0
  4. package/.npmrc.token +1 -0
  5. package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +1 -0
  6. package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +1 -0
  7. package/.nyc_output/processinfo/index.json +1 -0
  8. package/README.md +1 -1
  9. package/anchor-docs/README.md +10 -0
  10. package/anchor-docs/_TEMPLATE +24 -0
  11. package/bin/aifabrix.js +13 -4
  12. package/integration/hubspot-test/README.md +31 -0
  13. package/integration/hubspot-test/create-hubspot.js +5 -5
  14. package/integration/hubspot-test/hubspot-test-datasource-company.json +58 -462
  15. package/integration/hubspot-test/hubspot-test-datasource-contact.json +61 -555
  16. package/integration/hubspot-test/hubspot-test-datasource-deal.json +63 -506
  17. package/integration/hubspot-test/hubspot-test-datasource-users.json +42 -83
  18. package/integration/hubspot-test/hubspot-test-deploy.json +3 -3
  19. package/integration/hubspot-test/test-dataplane-down-tests.js +1 -7
  20. package/integration/hubspot-test/test-dataplane-down.js +3 -3
  21. package/integration/hubspot-test/test.js +35 -43
  22. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +23 -0
  23. package/integration/roundtrip-test-local/README.md +144 -0
  24. package/integration/roundtrip-test-local/application.yaml +13 -0
  25. package/integration/roundtrip-test-local/env.template +15 -0
  26. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +14 -0
  27. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +61 -0
  28. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +25 -0
  29. package/integration/roundtrip-test-local2/README.md +144 -0
  30. package/integration/roundtrip-test-local2/application.yaml +13 -0
  31. package/integration/roundtrip-test-local2/env.template +15 -0
  32. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +31 -0
  33. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +86 -0
  34. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +25 -0
  35. package/integration/test/wizard.yaml +8 -0
  36. package/jest.config.default.js +10 -0
  37. package/jest.config.integration.fixtures.js +22 -0
  38. package/jest.config.integration.js +21 -18
  39. package/jest.config.isolated.js +10 -0
  40. package/jest.projects.js +301 -0
  41. package/lib/api/certificates.api.js +62 -0
  42. package/lib/api/datasources-core.api.js +3 -3
  43. package/lib/api/dev-mtls-request.js +110 -0
  44. package/lib/api/dev-server-https.js +145 -0
  45. package/lib/api/dev.api.js +133 -144
  46. package/lib/api/index.js +11 -3
  47. package/lib/api/pipeline.api.js +67 -20
  48. package/lib/api/types/certificates.types.js +48 -0
  49. package/lib/api/types/dev.types.js +4 -3
  50. package/lib/api/types/pipeline.types.js +8 -5
  51. package/lib/api/types/validation-run.types.js +56 -0
  52. package/lib/api/validation-run.api.js +111 -0
  53. package/lib/api/validation-runner.js +109 -0
  54. package/lib/app/certification-show-enrich.js +129 -0
  55. package/lib/app/certification-verify-rows.js +60 -0
  56. package/lib/app/config.js +1 -1
  57. package/lib/app/deploy-status-display.js +2 -2
  58. package/lib/app/deploy.js +7 -6
  59. package/lib/app/display.js +2 -1
  60. package/lib/app/dockerfile.js +3 -2
  61. package/lib/app/down.js +2 -1
  62. package/lib/app/helpers.js +6 -5
  63. package/lib/app/index.js +27 -8
  64. package/lib/app/list.js +7 -6
  65. package/lib/app/push.js +4 -3
  66. package/lib/app/register.js +16 -7
  67. package/lib/app/rotate-secret.js +14 -13
  68. package/lib/app/run-container-start.js +184 -0
  69. package/lib/app/run-docker-fallback.js +108 -0
  70. package/lib/app/run-env-compose.js +30 -42
  71. package/lib/app/run-helpers.js +49 -126
  72. package/lib/app/run-infra-requirements.js +30 -0
  73. package/lib/app/run-resolve-image.js +21 -0
  74. package/lib/app/run.js +74 -21
  75. package/lib/app/show-display.js +44 -1
  76. package/lib/app/show.js +93 -9
  77. package/lib/build/index.js +13 -10
  78. package/lib/certification/cli-cert-sync-skip.js +21 -0
  79. package/lib/certification/merge-certification-from-artifact.js +185 -0
  80. package/lib/certification/post-unified-cert-sync.js +33 -0
  81. package/lib/certification/sync-after-external-command.js +52 -0
  82. package/lib/certification/sync-system-certification.js +197 -0
  83. package/lib/cli/index.js +2 -0
  84. package/lib/cli/setup-app.help.js +67 -0
  85. package/lib/cli/setup-app.js +61 -121
  86. package/lib/cli/setup-app.test-commands.js +195 -0
  87. package/lib/cli/setup-auth.js +19 -5
  88. package/lib/cli/setup-credential-deployment.js +22 -8
  89. package/lib/cli/setup-dev-path-commands.js +124 -0
  90. package/lib/cli/setup-dev.js +170 -113
  91. package/lib/cli/setup-environment.js +7 -1
  92. package/lib/cli/setup-external-system.js +84 -23
  93. package/lib/cli/setup-infra.js +126 -47
  94. package/lib/cli/setup-parameters.js +32 -0
  95. package/lib/cli/setup-secrets.js +137 -18
  96. package/lib/cli/setup-service-user.js +1 -1
  97. package/lib/cli/setup-utility.js +54 -22
  98. package/lib/commands/app-down.js +5 -7
  99. package/lib/commands/app-install.js +14 -7
  100. package/lib/commands/app-logs.js +13 -10
  101. package/lib/commands/app-shell.js +4 -1
  102. package/lib/commands/app-test.js +25 -19
  103. package/lib/commands/app.js +32 -11
  104. package/lib/commands/auth-config.js +6 -6
  105. package/lib/commands/auth-status.js +4 -3
  106. package/lib/commands/credential-env.js +4 -3
  107. package/lib/commands/credential-list.js +5 -4
  108. package/lib/commands/credential-push.js +4 -3
  109. package/lib/commands/datasource-unified-test-cli.js +428 -0
  110. package/lib/commands/datasource-unified-test-cli.options.js +191 -0
  111. package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +106 -0
  112. package/lib/commands/datasource-validation-cli.js +143 -0
  113. package/lib/commands/datasource.js +125 -95
  114. package/lib/commands/deployment-list.js +6 -5
  115. package/lib/commands/dev-cli-handlers.js +122 -18
  116. package/lib/commands/dev-down.js +4 -3
  117. package/lib/commands/dev-init.js +231 -116
  118. package/lib/commands/dev-show-display.js +473 -0
  119. package/lib/commands/login-credentials.js +3 -2
  120. package/lib/commands/login-device.js +4 -3
  121. package/lib/commands/login.js +5 -4
  122. package/lib/commands/logout.js +8 -7
  123. package/lib/commands/parameters-validate.js +54 -0
  124. package/lib/commands/repair-datasource.js +314 -68
  125. package/lib/commands/repair-env-template.js +2 -2
  126. package/lib/commands/repair.js +21 -3
  127. package/lib/commands/secrets-list.js +23 -12
  128. package/lib/commands/secrets-remove-all.js +220 -0
  129. package/lib/commands/secrets-remove.js +21 -12
  130. package/lib/commands/secrets-set.js +21 -12
  131. package/lib/commands/secrets-validate.js +4 -4
  132. package/lib/commands/secure.js +10 -9
  133. package/lib/commands/service-user.js +26 -25
  134. package/lib/commands/test-e2e-external.js +27 -1
  135. package/lib/commands/up-common.js +3 -2
  136. package/lib/commands/up-dataplane.js +29 -16
  137. package/lib/commands/up-miso.js +19 -29
  138. package/lib/commands/upload.js +149 -39
  139. package/lib/commands/wizard-core-helpers.js +1 -1
  140. package/lib/commands/wizard-dataplane.js +4 -3
  141. package/lib/commands/wizard-helpers.js +3 -3
  142. package/lib/commands/wizard.js +2 -2
  143. package/lib/core/admin-secrets.js +14 -5
  144. package/lib/core/audit-logger.js +12 -4
  145. package/lib/core/config-attach-extensions.js +46 -0
  146. package/lib/core/config-runtime-paths.js +29 -0
  147. package/lib/core/config.js +55 -56
  148. package/lib/core/diff.js +3 -2
  149. package/lib/core/ensure-encryption-key.js +1 -1
  150. package/lib/core/secrets-ensure-infra.js +77 -0
  151. package/lib/core/secrets-ensure.js +120 -64
  152. package/lib/core/secrets-env-write.js +35 -7
  153. package/lib/core/secrets-infra-placeholder-sync.js +61 -0
  154. package/lib/core/secrets.js +200 -37
  155. package/lib/core/templates-env.js +4 -3
  156. package/lib/datasource/abac-validator.js +1 -10
  157. package/lib/datasource/deploy.js +75 -53
  158. package/lib/datasource/field-reference-validator.js +9 -6
  159. package/lib/datasource/integration-context.js +63 -0
  160. package/lib/datasource/list.js +8 -7
  161. package/lib/datasource/log-viewer.js +189 -67
  162. package/lib/datasource/resolve-app.js +4 -4
  163. package/lib/datasource/test-e2e.js +113 -146
  164. package/lib/datasource/test-integration.js +114 -122
  165. package/lib/datasource/unified-validation-run-body.js +68 -0
  166. package/lib/datasource/unified-validation-run-post.js +23 -0
  167. package/lib/datasource/unified-validation-run-resolve.js +43 -0
  168. package/lib/datasource/unified-validation-run.js +93 -0
  169. package/lib/datasource/validate.js +157 -13
  170. package/lib/deployment/deployer.js +4 -3
  171. package/lib/deployment/environment.js +7 -6
  172. package/lib/deployment/push.js +17 -8
  173. package/lib/external-system/delete.js +4 -3
  174. package/lib/external-system/deploy.js +166 -53
  175. package/lib/external-system/download-helpers.js +1 -1
  176. package/lib/external-system/download.js +7 -6
  177. package/lib/external-system/generator.js +92 -6
  178. package/lib/external-system/integration-test-dispatch.js +26 -0
  179. package/lib/external-system/test-execution.js +5 -1
  180. package/lib/external-system/test-helpers.js +0 -4
  181. package/lib/external-system/test-system-level-helpers.js +110 -0
  182. package/lib/external-system/test-system-level.js +83 -44
  183. package/lib/external-system/test.js +59 -8
  184. package/lib/generator/builders.js +23 -11
  185. package/lib/generator/deploy-manifest-azure-kv.js +81 -0
  186. package/lib/generator/external.js +16 -4
  187. package/lib/generator/helpers.js +58 -3
  188. package/lib/generator/index.js +4 -0
  189. package/lib/generator/split-readme.js +12 -7
  190. package/lib/generator/split-variables.js +2 -1
  191. package/lib/generator/split.js +1 -1
  192. package/lib/generator/wizard-readme.js +3 -3
  193. package/lib/generator/wizard.js +8 -8
  194. package/lib/infrastructure/compose.js +70 -7
  195. package/lib/infrastructure/helpers-docker-check.js +67 -0
  196. package/lib/infrastructure/helpers.js +203 -42
  197. package/lib/infrastructure/index.js +31 -18
  198. package/lib/infrastructure/services.js +21 -67
  199. package/lib/internal/fs-real-sync.js +104 -0
  200. package/lib/internal/node-fs.js +98 -0
  201. package/lib/parameters/database-secret-values.js +173 -0
  202. package/lib/parameters/infra-kv-discovery.js +121 -0
  203. package/lib/parameters/infra-parameter-catalog.js +458 -0
  204. package/lib/parameters/infra-parameter-validate.js +64 -0
  205. package/lib/schema/application-schema.json +37 -17
  206. package/lib/schema/datasource-test-run.schema.json +493 -0
  207. package/lib/schema/deployment-rules.yaml +102 -63
  208. package/lib/schema/external-datasource.schema.json +1200 -442
  209. package/lib/schema/external-system.schema.json +203 -5
  210. package/lib/schema/flag-map-validation-run.json +31 -0
  211. package/lib/schema/infra-parameter.schema.json +106 -0
  212. package/lib/schema/infra.parameter.yaml +421 -0
  213. package/lib/schema/type/credential-auth-templates.json +40 -0
  214. package/lib/schema/type/document-storage.json +226 -0
  215. package/lib/schema/type/message-service.json +123 -0
  216. package/lib/schema/type/vector-store.json +88 -0
  217. package/lib/utils/aifabrix-runtime-config-dir.js +132 -0
  218. package/lib/utils/api-error-handler.js +2 -2
  219. package/lib/utils/api.js +77 -17
  220. package/lib/utils/app-register-api.js +3 -2
  221. package/lib/utils/app-register-auth.js +1 -1
  222. package/lib/utils/app-register-config.js +4 -4
  223. package/lib/utils/app-register-display.js +3 -2
  224. package/lib/utils/app-register-validator.js +3 -2
  225. package/lib/utils/app-run-containers.js +26 -22
  226. package/lib/utils/app-scoped-config.js +31 -0
  227. package/lib/utils/app-service-env-from-builder.js +164 -0
  228. package/lib/utils/build-copy.js +1 -1
  229. package/lib/utils/build-helpers.js +20 -20
  230. package/lib/utils/build-resolve-image.js +165 -0
  231. package/lib/utils/cli-layout-chalk.js +8 -0
  232. package/lib/utils/cli-test-layout-chalk.js +267 -0
  233. package/lib/utils/cli-utils.js +88 -11
  234. package/lib/utils/compose-db-passwords.js +138 -0
  235. package/lib/utils/compose-generate-docker-compose.js +216 -0
  236. package/lib/utils/compose-generator.js +197 -291
  237. package/lib/utils/compose-miso-env.js +18 -0
  238. package/lib/utils/compose-traefik-ingress-base.js +158 -0
  239. package/lib/utils/config-paths.js +166 -7
  240. package/lib/utils/config-scoped-resources-preference.js +41 -0
  241. package/lib/utils/configuration-env-resolver.js +11 -8
  242. package/lib/utils/controller-deployment-outcome.js +68 -0
  243. package/lib/utils/credential-display.js +2 -2
  244. package/lib/utils/credential-secrets-env.js +5 -5
  245. package/lib/utils/dataplane-pipeline-warning.js +4 -3
  246. package/lib/utils/datasource-test-run-capability-scope.js +43 -0
  247. package/lib/utils/datasource-test-run-certificate-tty.js +82 -0
  248. package/lib/utils/datasource-test-run-debug-display.js +137 -0
  249. package/lib/utils/datasource-test-run-debug-slice.js +93 -0
  250. package/lib/utils/datasource-test-run-display.js +459 -0
  251. package/lib/utils/datasource-test-run-exit.js +83 -0
  252. package/lib/utils/datasource-test-run-legacy-adapter.js +93 -0
  253. package/lib/utils/datasource-test-run-report-version.js +51 -0
  254. package/lib/utils/datasource-test-run-schema-sync.js +59 -0
  255. package/lib/utils/datasource-test-run-tty-log.js +81 -0
  256. package/lib/utils/datasource-validation-watch.js +266 -0
  257. package/lib/utils/declarative-url-ports.js +47 -0
  258. package/lib/utils/derive-env-key-from-client-id.js +41 -0
  259. package/lib/utils/dev-ca-install.js +185 -23
  260. package/lib/utils/dev-cert-helper.js +266 -17
  261. package/lib/utils/dev-hosts-helper.js +307 -0
  262. package/lib/utils/dev-init-cert-hints.js +37 -0
  263. package/lib/utils/dev-init-health-messages.js +52 -0
  264. package/lib/utils/dev-init-resolve.js +86 -0
  265. package/lib/utils/dev-init-ssh-merge.js +65 -0
  266. package/lib/utils/dev-ssh-config-helper.js +196 -0
  267. package/lib/utils/dev-user-groups.js +93 -0
  268. package/lib/utils/docker-build.js +42 -17
  269. package/lib/utils/docker-exec.js +28 -0
  270. package/lib/utils/docker-manifest-public-port.js +116 -0
  271. package/lib/utils/docker-not-running-hint.js +52 -0
  272. package/lib/utils/docker.js +98 -11
  273. package/lib/utils/ensure-dev-certs-for-remote-docker.js +192 -0
  274. package/lib/utils/env-config-loader.js +10 -91
  275. package/lib/utils/env-copy.js +19 -10
  276. package/lib/utils/env-map.js +35 -8
  277. package/lib/utils/env-template.js +2 -2
  278. package/lib/utils/environment-scoped-resources.js +144 -0
  279. package/lib/utils/error-formatter.js +92 -13
  280. package/lib/utils/error-formatters/http-status-errors.js +6 -5
  281. package/lib/utils/error-formatters/network-errors.js +2 -1
  282. package/lib/utils/error-formatters/permission-errors.js +2 -1
  283. package/lib/utils/error-formatters/validation-errors.js +2 -1
  284. package/lib/utils/external-readme.js +8 -1
  285. package/lib/utils/external-system-display.js +242 -136
  286. package/lib/utils/external-system-local-test-tty.js +389 -0
  287. package/lib/utils/external-system-readiness-core.js +377 -0
  288. package/lib/utils/external-system-readiness-deploy-display.js +270 -0
  289. package/lib/utils/external-system-readiness-display-internals.js +150 -0
  290. package/lib/utils/external-system-readiness-display.js +186 -0
  291. package/lib/utils/external-system-system-test-tty-overview.js +120 -0
  292. package/lib/utils/external-system-system-test-tty.js +417 -0
  293. package/lib/utils/external-system-test-helpers.js +24 -6
  294. package/lib/utils/external-system-validators.js +30 -12
  295. package/lib/utils/health-check-url.js +119 -0
  296. package/lib/utils/health-check.js +59 -25
  297. package/lib/utils/help-builder.js +11 -8
  298. package/lib/utils/image-version.js +4 -8
  299. package/lib/utils/infra-containers.js +4 -7
  300. package/lib/utils/infra-env-defaults.js +162 -0
  301. package/lib/utils/infra-status-display.js +167 -0
  302. package/lib/utils/infra-status.js +16 -8
  303. package/lib/utils/local-secrets.js +3 -4
  304. package/lib/utils/paths.js +148 -47
  305. package/lib/utils/port-resolver.js +10 -23
  306. package/lib/utils/redis-env-scope.js +62 -0
  307. package/lib/utils/register-aifabrix-shell-env.js +204 -0
  308. package/lib/utils/remote-builder-validation.js +99 -0
  309. package/lib/utils/remote-dev-auth.js +117 -21
  310. package/lib/utils/remote-docker-env.js +67 -15
  311. package/lib/utils/remote-secrets-loader.js +13 -4
  312. package/lib/utils/resolve-docker-image-ref.js +124 -0
  313. package/lib/utils/schema-loader.js +22 -9
  314. package/lib/utils/secrets-bash-kv.js +25 -0
  315. package/lib/utils/secrets-generator.js +169 -49
  316. package/lib/utils/secrets-helpers.js +70 -59
  317. package/lib/utils/secrets-kv-scope.js +60 -0
  318. package/lib/utils/secrets-utils.js +32 -38
  319. package/lib/utils/secrets-validation.js +3 -1
  320. package/lib/utils/secrets-yaml-preserve.js +109 -0
  321. package/lib/utils/ssh-key-helper.js +4 -2
  322. package/lib/utils/template-helpers.js +2 -2
  323. package/lib/utils/test-log-writer.js +3 -3
  324. package/lib/utils/token-manager.js +1 -2
  325. package/lib/utils/url-declarative-public-base.js +188 -0
  326. package/lib/utils/url-declarative-resolve-build.js +493 -0
  327. package/lib/utils/url-declarative-resolve-load-doc.js +51 -0
  328. package/lib/utils/url-declarative-resolve.js +220 -0
  329. package/lib/utils/url-declarative-token-parse.js +74 -0
  330. package/lib/utils/url-declarative-url-flags.js +50 -0
  331. package/lib/utils/url-declarative-vdir-inactive-env.js +99 -0
  332. package/lib/utils/url-public-path-prefix.js +34 -0
  333. package/lib/utils/urls-local-registry.js +220 -0
  334. package/lib/utils/validation-report-tty-kit.js +77 -0
  335. package/lib/utils/validation-run-poll.js +112 -0
  336. package/lib/utils/validation-run-post-retry.js +85 -0
  337. package/lib/utils/validation-run-request.js +116 -0
  338. package/lib/utils/variable-transformer.js +21 -4
  339. package/lib/utils/yaml-preserve.js +33 -14
  340. package/lib/validation/datasource-warnings.js +56 -0
  341. package/lib/validation/env-template-auth.js +1 -1
  342. package/lib/validation/external-manifest-validator.js +27 -7
  343. package/lib/validation/validate-display.js +37 -31
  344. package/lib/validation/validate-external-cert-sync.js +23 -0
  345. package/lib/validation/validate.js +8 -14
  346. package/lib/validation/validator-unresolved-placeholders.js +98 -0
  347. package/lib/validation/validator.js +22 -65
  348. package/lib/validation/wizard-config-validator.js +2 -1
  349. package/package.json +9 -4
  350. package/scripts/check-datasource-test-run-schema-sync.js +34 -0
  351. package/scripts/diagnose-cli.js +150 -0
  352. package/scripts/install-local.js +307 -55
  353. package/scripts/pnpm-global-remove.js +48 -0
  354. package/templates/README.md +15 -2
  355. package/templates/applications/dataplane/application.yaml +52 -2
  356. package/templates/applications/dataplane/env.template +79 -17
  357. package/templates/applications/dataplane/rbac.yaml +8 -0
  358. package/templates/applications/keycloak/application.yaml +9 -1
  359. package/templates/applications/keycloak/env.template +15 -6
  360. package/templates/applications/miso-controller/application.yaml +10 -2
  361. package/templates/applications/miso-controller/env.template +42 -12
  362. package/templates/applications/miso-controller/rbac.yaml +5 -0
  363. package/templates/external-system/README.md.hbs +20 -7
  364. package/templates/external-system/deploy.js.hbs +5 -5
  365. package/templates/external-system/external-datasource.yaml.hbs +197 -118
  366. package/templates/infra/compose.yaml.hbs +33 -16
  367. package/templates/infra/servers.json.hbs +3 -1
  368. package/templates/python/docker-compose.hbs +16 -0
  369. package/templates/typescript/docker-compose.hbs +16 -0
  370. package/lib/api/external-test.api.js +0 -111
  371. package/lib/schema/env-config.yaml +0 -60
@@ -0,0 +1,220 @@
1
+ /**
2
+ * urls.local.yaml beside effective config.yaml (same directory as secrets.local.yaml).
3
+ * When AIFABRIX_HOME is POSIX $HOME but config lives in $HOME/.aifabrix/, the registry
4
+ * is $HOME/.aifabrix/urls.local.yaml (not $HOME/urls.local.yaml).
5
+ *
6
+ * @fileoverview Read/write registry; scan each builder app folder for application.yaml
7
+ * @author AI Fabrix Team
8
+ * @version 1.0.0
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const fsRealSync = require('../internal/fs-real-sync');
14
+ const path = require('path');
15
+ const yaml = require('js-yaml');
16
+ const { DECLARATIVE_URL_INFRA_DEFAULTS } = require('./infra-env-defaults');
17
+ const pathsUtil = require('./paths');
18
+
19
+ /**
20
+ * @returns {string} Absolute path to urls.local.yaml (primary; beside config.yaml)
21
+ */
22
+ function getUrlsLocalYamlPath() {
23
+ return path.join(pathsUtil.getConfigDirForPaths(), 'urls.local.yaml');
24
+ }
25
+
26
+ /** @returns {string} Legacy path when registry was stored under getAifabrixHome() only */
27
+ function getLegacyUrlsLocalYamlPath() {
28
+ return path.join(pathsUtil.getAifabrixHome(), 'urls.local.yaml');
29
+ }
30
+
31
+ function loadRegistryYamlFile(filePath) {
32
+ try {
33
+ const doc = yaml.load(fsRealSync.readFileSync(filePath, 'utf8'));
34
+ return doc && typeof doc === 'object' ? doc : {};
35
+ } catch {
36
+ return {};
37
+ }
38
+ }
39
+
40
+ /**
41
+ * @returns {Record<string, unknown>}
42
+ */
43
+ function readUrlsLocalRegistrySync() {
44
+ const primary = getUrlsLocalYamlPath();
45
+ if (fsRealSync.existsSync(primary)) {
46
+ return loadRegistryYamlFile(primary);
47
+ }
48
+ const legacy = getLegacyUrlsLocalYamlPath();
49
+ if (legacy !== primary && fsRealSync.existsSync(legacy)) {
50
+ return loadRegistryYamlFile(legacy);
51
+ }
52
+ return {};
53
+ }
54
+
55
+ /**
56
+ * @param {Object} data - Full registry object to write
57
+ */
58
+ function writeUrlsLocalRegistrySync(data) {
59
+ const p = getUrlsLocalYamlPath();
60
+ const dir = path.dirname(p);
61
+ if (!fsRealSync.existsSync(dir)) {
62
+ fsRealSync.mkdirSync(dir, { recursive: true, mode: 0o700 });
63
+ }
64
+ const body = `${yaml.dump(data, { lineWidth: 120, noRefs: true }).trim()}\n`;
65
+ fsRealSync.writeFileSync(p, body, { mode: 0o600 });
66
+ }
67
+
68
+ /**
69
+ * Normalize front-door pattern for URLs (/data/* → /data).
70
+ * @param {string} pattern
71
+ * @returns {string}
72
+ */
73
+ function normalizePatternForUrl(pattern) {
74
+ if (!pattern || typeof pattern !== 'string') {
75
+ return '/';
76
+ }
77
+ let p = pattern.trim();
78
+ if (!p.startsWith('/')) {
79
+ p = `/${p}`;
80
+ }
81
+ p = p.replace(/\*+$/, '').replace(/\/+$/, '') || '/';
82
+ return p;
83
+ }
84
+
85
+ /**
86
+ * @param {Object} merged
87
+ * @returns {Object} same object after persist
88
+ */
89
+ function writeMergedRegistry(merged) {
90
+ writeUrlsLocalRegistrySync(merged);
91
+ return merged;
92
+ }
93
+
94
+ /**
95
+ * @param {string} cfgPath
96
+ * @returns {object|null}
97
+ */
98
+ function tryLoadApplicationYaml(cfgPath) {
99
+ if (!fsRealSync.existsSync(cfgPath)) {
100
+ return null;
101
+ }
102
+ try {
103
+ const doc = yaml.load(fsRealSync.readFileSync(cfgPath, 'utf8'));
104
+ return doc && typeof doc === 'object' ? doc : null;
105
+ } catch {
106
+ return null;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * @param {object} doc
112
+ * @returns {number|null}
113
+ */
114
+ function readExplicitContainerPortFromDoc(doc) {
115
+ const cpRaw = doc.build && doc.build.containerPort;
116
+ if (typeof cpRaw === 'number' && cpRaw > 0) {
117
+ return cpRaw;
118
+ }
119
+ if (typeof cpRaw === 'string' && /^\d+$/.test(cpRaw.trim())) {
120
+ return parseInt(cpRaw.trim(), 10);
121
+ }
122
+ return null;
123
+ }
124
+
125
+ /**
126
+ * @param {Object} merged
127
+ * @param {object} doc
128
+ * @param {string} folderName - builder/<folderName>
129
+ */
130
+ function mergeDocIntoRegistry(merged, doc, folderName) {
131
+ const appKey = (doc.app && doc.app.key) || folderName;
132
+ let port = null;
133
+ if (typeof doc.port === 'number' && doc.port > 0) {
134
+ port = doc.port;
135
+ } else if (typeof doc.port === 'string' && /^\d+$/.test(doc.port.trim())) {
136
+ port = parseInt(doc.port.trim(), 10);
137
+ }
138
+ if (port === null || port <= 0) {
139
+ return;
140
+ }
141
+ const rawPattern = doc.frontDoorRouting && doc.frontDoorRouting.pattern;
142
+ const pattern =
143
+ typeof rawPattern === 'string'
144
+ ? rawPattern
145
+ : DECLARATIVE_URL_INFRA_DEFAULTS.frontDoorPatternWhenUnspecified;
146
+ merged[`${appKey}-port`] = port;
147
+ merged[`${appKey}-pattern`] = pattern;
148
+
149
+ const explicitC = readExplicitContainerPortFromDoc(doc);
150
+ const ckey = `${appKey}-containerPort`;
151
+ if (explicitC !== null) {
152
+ merged[ckey] = explicitC;
153
+ } else {
154
+ delete merged[ckey];
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Merge scan results into registry (does not remove stale keys).
160
+ * @param {string|null} projectRoot - getProjectRoot() or null
161
+ * @returns {Object} Updated registry
162
+ */
163
+ function refreshUrlsLocalRegistryFromBuilder(projectRoot) {
164
+ const root = projectRoot || pathsUtil.getProjectRoot();
165
+ const merged = { ...readUrlsLocalRegistrySync() };
166
+ if (!root) {
167
+ return writeMergedRegistry(merged);
168
+ }
169
+ const builderDir = path.join(root, 'builder');
170
+ if (!fsRealSync.existsSync(builderDir) || !fsRealSync.statSync(builderDir).isDirectory()) {
171
+ return writeMergedRegistry(merged);
172
+ }
173
+ for (const ent of fsRealSync.readdirSync(builderDir, { withFileTypes: true })) {
174
+ if (!ent.isDirectory()) {
175
+ continue;
176
+ }
177
+ const doc = tryLoadApplicationYaml(path.join(builderDir, ent.name, 'application.yaml'));
178
+ if (!doc) {
179
+ continue;
180
+ }
181
+ mergeDocIntoRegistry(merged, doc, ent.name);
182
+ }
183
+ return writeMergedRegistry(merged);
184
+ }
185
+
186
+ /**
187
+ * @param {string} appKey
188
+ * @param {Object} registry
189
+ * @returns {{ port: number, containerPort: number|null, pattern: string }|null}
190
+ */
191
+ function getRegistryEntryForApp(appKey, registry) {
192
+ const r = registry || {};
193
+ const portKey = `${appKey}-port`;
194
+ const patKey = `${appKey}-pattern`;
195
+ const cportKey = `${appKey}-containerPort`;
196
+ const port = r[portKey];
197
+ const pattern = r[patKey];
198
+ const cport = r[cportKey];
199
+ if (typeof port !== 'number' || port <= 0) {
200
+ return null;
201
+ }
202
+ const containerPort = typeof cport === 'number' && cport > 0 ? cport : null;
203
+ return {
204
+ port,
205
+ containerPort,
206
+ pattern:
207
+ typeof pattern === 'string'
208
+ ? pattern
209
+ : DECLARATIVE_URL_INFRA_DEFAULTS.frontDoorPatternWhenUnspecified
210
+ };
211
+ }
212
+
213
+ module.exports = {
214
+ getUrlsLocalYamlPath,
215
+ readUrlsLocalRegistrySync,
216
+ writeUrlsLocalRegistrySync,
217
+ refreshUrlsLocalRegistryFromBuilder,
218
+ normalizePatternForUrl,
219
+ getRegistryEntryForApp
220
+ };
@@ -0,0 +1,77 @@
1
+ /**
2
+ * @fileoverview Reusable TTY helpers for validation/test reports (verdict, readiness, data-quality lines).
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ const { SEP, statusGlyph } = require('./datasource-test-run-display');
8
+
9
+ const TRUST_LINE_LABELS = Object.freeze({
10
+ schema: 'Schema coverage',
11
+ consistency: 'Data consistency',
12
+ reliability: 'Data reliability'
13
+ });
14
+
15
+ function rollupGlyph(r) {
16
+ return statusGlyph(r === 'fail' ? 'fail' : r === 'warn' ? 'warn' : 'ok');
17
+ }
18
+
19
+ function verdictLineFromEnvelope(rootStatus, certStatus, runType) {
20
+ if (rootStatus === 'skipped') return '⏭ Skipped';
21
+ if (rootStatus === 'warn') return '⚠ Limited production use';
22
+ if (rootStatus === 'fail') {
23
+ if (runType === 'integration') return '✖ Pipeline not working';
24
+ if (runType === 'e2e') return '✖ Not usable';
25
+ return '✖ Configuration invalid';
26
+ }
27
+ if (certStatus === 'not_passed') return '✔ Functional with certification gaps';
28
+ return '✔ Suitable for production use';
29
+ }
30
+
31
+ function verdictLineLocalExternalTest(status) {
32
+ if (status === 'ok') return '✔ Suitable for continued setup (local manifest check passed).';
33
+ if (status === 'warn') return '⚠ Limited production use';
34
+ return '✖ Configuration invalid';
35
+ }
36
+
37
+ function readinessLineFromAggregateStatus(status) {
38
+ if (status === 'fail') return `Readiness: ${statusGlyph('fail')} Not ready`;
39
+ if (status === 'warn') return `Readiness: ${statusGlyph('warn')} Partial`;
40
+ return `Readiness: ${statusGlyph('ok')} Ready`;
41
+ }
42
+
43
+ function readinessLineFromDataReadiness(dataReadiness) {
44
+ if (!dataReadiness) return null;
45
+ if (dataReadiness === 'not_ready') return `Readiness: ${statusGlyph('fail')} Not ready`;
46
+ if (dataReadiness === 'partial') return `Readiness: ${statusGlyph('warn')} Partial`;
47
+ if (dataReadiness === 'ready') return `Readiness: ${statusGlyph('ok')} Ready`;
48
+ return null;
49
+ }
50
+
51
+ function formatDataQualityLines(rollups, descriptions) {
52
+ const d = descriptions || {};
53
+ return [
54
+ `${rollupGlyph(rollups.schema)} ${TRUST_LINE_LABELS.schema}${d.schema ? ` — ${d.schema}` : ''}`,
55
+ `${rollupGlyph(rollups.consistency)} ${TRUST_LINE_LABELS.consistency}${d.consistency ? ` — ${d.consistency}` : ''}`,
56
+ `${rollupGlyph(rollups.reliability)} ${TRUST_LINE_LABELS.reliability}${d.reliability ? ` — ${d.reliability}` : ''}`
57
+ ];
58
+ }
59
+
60
+ function pushSeparatorBlock(lines) {
61
+ lines.push('');
62
+ lines.push(SEP);
63
+ lines.push('');
64
+ }
65
+
66
+ module.exports = {
67
+ SEP,
68
+ statusGlyph,
69
+ TRUST_LINE_LABELS,
70
+ rollupGlyph,
71
+ verdictLineFromEnvelope,
72
+ verdictLineLocalExternalTest,
73
+ readinessLineFromAggregateStatus,
74
+ readinessLineFromDataReadiness,
75
+ formatDataQualityLines,
76
+ pushSeparatorBlock
77
+ };
@@ -0,0 +1,112 @@
1
+ /**
2
+ * @fileoverview Poll GET /api/v1/validation/run/{testRunId} until reportCompleteness is full (plan §3.5–3.6).
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ const chalk = require('chalk');
8
+ const logger = require('./logger');
9
+ const { getValidationRunWithTransportRetry } = require('./validation-run-post-retry');
10
+
11
+ const INITIAL_INTERVAL_MS = 2000;
12
+ const MAX_INTERVAL_MS = 15000;
13
+
14
+ /**
15
+ * Delay between polls after attempt `n` (0-based): 2s, 4s, 8s, … cap 15s.
16
+ * @param {number} attemptIndex - Zero-based poll index after initial POST
17
+ * @returns {number}
18
+ */
19
+ function nextPollDelayMs(attemptIndex) {
20
+ const raw = INITIAL_INTERVAL_MS * 2 ** Math.max(0, attemptIndex);
21
+ return Math.min(raw, MAX_INTERVAL_MS);
22
+ }
23
+
24
+ function sleep(ms) {
25
+ return new Promise(resolve => setTimeout(resolve, ms));
26
+ }
27
+
28
+ function maybeLogPollProgress(envelope, verbosePoll, lastProgressLogAtRef) {
29
+ if (!verbosePoll || !envelope || typeof envelope !== 'object') return;
30
+ const now = Date.now();
31
+ if (now - lastProgressLogAtRef[0] < 5000) return;
32
+ lastProgressLogAtRef[0] = now;
33
+ const st = envelope.status !== undefined && envelope.status !== null ? String(envelope.status) : '?';
34
+ const c =
35
+ envelope.reportCompleteness !== undefined && envelope.reportCompleteness !== null
36
+ ? String(envelope.reportCompleteness)
37
+ : '?';
38
+ logger.log(chalk.gray(` Polling validation run… completeness=${c} status=${st}`));
39
+ }
40
+
41
+ /**
42
+ * Whether polling should stop on this envelope.
43
+ * @param {Object} envelope - DatasourceTestRun-like
44
+ * @returns {boolean}
45
+ */
46
+ function isTerminalReportCompleteness(envelope) {
47
+ if (!envelope || typeof envelope !== 'object') return false;
48
+ return envelope.reportCompleteness === 'full';
49
+ }
50
+
51
+ /**
52
+ * Poll until reportCompleteness === 'full' or budget exhausted.
53
+ * @async
54
+ * @param {Object} opts
55
+ * @param {string} opts.dataplaneUrl
56
+ * @param {Object} opts.authConfig
57
+ * @param {string} opts.testRunId
58
+ * @param {number} opts.budgetMs - Remaining wall-clock budget for polls only (POST excluded)
59
+ * @param {typeof getValidationRunWithTransportRetry} [opts.fetchRun] - Inject for tests (default: GET with transport retry)
60
+ * @param {boolean} [opts.verbosePoll] - Log throttled progress (plan §3.13)
61
+ * @param {number} [opts.pollRequestTimeoutMs] - Per-GET HTTP timeout (match validation aggregate budget)
62
+ * @returns {Promise<{ envelope: Object|null, timedOut: boolean, lastApiResult: Object|null }>}
63
+ */
64
+ async function pollValidationRunUntilComplete(opts) {
65
+ const {
66
+ dataplaneUrl,
67
+ authConfig,
68
+ testRunId,
69
+ budgetMs,
70
+ fetchRun = getValidationRunWithTransportRetry,
71
+ verbosePoll = false,
72
+ pollRequestTimeoutMs
73
+ } = opts;
74
+ const pollTransportOpts =
75
+ Number.isFinite(pollRequestTimeoutMs) && pollRequestTimeoutMs > 0
76
+ ? { timeoutMs: pollRequestTimeoutMs }
77
+ : {};
78
+ const deadline = Date.now() + Math.max(0, budgetMs);
79
+ let attempt = 0;
80
+ let lastApiResult = null;
81
+ let envelope = null;
82
+ const lastProgressLogAtRef = [0];
83
+
84
+ while (Date.now() < deadline) {
85
+ lastApiResult = await fetchRun(dataplaneUrl, authConfig, testRunId, pollTransportOpts);
86
+ if (!lastApiResult.success) {
87
+ return { envelope: null, timedOut: false, lastApiResult };
88
+ }
89
+ envelope = lastApiResult.data;
90
+ if (isTerminalReportCompleteness(envelope)) {
91
+ return { envelope, timedOut: false, lastApiResult };
92
+ }
93
+
94
+ maybeLogPollProgress(envelope, verbosePoll, lastProgressLogAtRef);
95
+
96
+ const delay = Math.min(nextPollDelayMs(attempt), Math.max(0, deadline - Date.now()));
97
+ attempt += 1;
98
+ if (delay > 0) {
99
+ await sleep(delay);
100
+ }
101
+ }
102
+
103
+ return { envelope, timedOut: true, lastApiResult };
104
+ }
105
+
106
+ module.exports = {
107
+ INITIAL_INTERVAL_MS,
108
+ MAX_INTERVAL_MS,
109
+ nextPollDelayMs,
110
+ pollValidationRunUntilComplete,
111
+ isTerminalReportCompleteness
112
+ };
@@ -0,0 +1,85 @@
1
+ /**
2
+ * @fileoverview Transient transport retries for POST validation/run and GET poll.
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ const { postValidationRun, getValidationRun } = require('../api/validation-run.api');
8
+
9
+ const RETRYABLE_CODES = new Set(['ECONNRESET', 'ETIMEDOUT', 'ECONNABORTED']);
10
+
11
+ /**
12
+ * @param {Object} res - ApiClient-style result
13
+ * @returns {boolean}
14
+ */
15
+ function isRetryablePostFailure(res) {
16
+ if (!res || res.success) return false;
17
+ if (!res.network) return false;
18
+ const err = res.originalError;
19
+ if (!err) return false;
20
+ const code = err.code || (err.cause && err.cause.code);
21
+ if (code && RETRYABLE_CODES.has(code)) return true;
22
+ if (err.name === 'AbortError') return true;
23
+ return false;
24
+ }
25
+
26
+ function sleep(ms) {
27
+ return new Promise(resolve => setTimeout(resolve, ms));
28
+ }
29
+
30
+ /**
31
+ * POST validation run with up to 2 retries on transient socket/timeout errors (1s / 2s backoff).
32
+ * Does not retry HTTP 4xx/5xx.
33
+ * @param {string} dataplaneUrl
34
+ * @param {Object} authConfig
35
+ * @param {Object} body
36
+ * @param {Object} [transportOpts] - forwarded to ``postValidationRun`` (e.g. ``timeoutMs``)
37
+ * @returns {Promise<Object>}
38
+ */
39
+ async function postValidationRunWithTransportRetry(
40
+ dataplaneUrl,
41
+ authConfig,
42
+ body,
43
+ transportOpts = {}
44
+ ) {
45
+ let last = await postValidationRun(dataplaneUrl, authConfig, body, transportOpts);
46
+ if (last.success || !isRetryablePostFailure(last)) return last;
47
+
48
+ await sleep(1000);
49
+ last = await postValidationRun(dataplaneUrl, authConfig, body, transportOpts);
50
+ if (last.success || !isRetryablePostFailure(last)) return last;
51
+
52
+ await sleep(2000);
53
+ return postValidationRun(dataplaneUrl, authConfig, body, transportOpts);
54
+ }
55
+
56
+ /**
57
+ * GET validation run poll with up to 2 retries on transient socket/timeout errors (1s / 2s backoff).
58
+ * @param {string} dataplaneUrl
59
+ * @param {Object} authConfig
60
+ * @param {string} testRunId
61
+ * @param {Object} [transportOpts] - forwarded to ``getValidationRun`` (e.g. ``timeoutMs``)
62
+ * @returns {Promise<Object>}
63
+ */
64
+ async function getValidationRunWithTransportRetry(
65
+ dataplaneUrl,
66
+ authConfig,
67
+ testRunId,
68
+ transportOpts = {}
69
+ ) {
70
+ let last = await getValidationRun(dataplaneUrl, authConfig, testRunId, transportOpts);
71
+ if (last.success || !isRetryablePostFailure(last)) return last;
72
+
73
+ await sleep(1000);
74
+ last = await getValidationRun(dataplaneUrl, authConfig, testRunId, transportOpts);
75
+ if (last.success || !isRetryablePostFailure(last)) return last;
76
+
77
+ await sleep(2000);
78
+ return getValidationRun(dataplaneUrl, authConfig, testRunId, transportOpts);
79
+ }
80
+
81
+ module.exports = {
82
+ isRetryablePostFailure,
83
+ postValidationRunWithTransportRetry,
84
+ getValidationRunWithTransportRetry
85
+ };
@@ -0,0 +1,116 @@
1
+ /**
2
+ * @fileoverview Build ValidationRunRequest bodies for datasource-scoped CLI commands.
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ /**
8
+ * Whether the unified validation request should set includeDebug (any `--debug` or `--debug <level>`).
9
+ * @param {*} debugOpt - Commander `options.debug`
10
+ * @returns {boolean}
11
+ */
12
+ function includeDebugForRequest(debugOpt) {
13
+ if (debugOpt === undefined || debugOpt === false || debugOpt === null || debugOpt === '') {
14
+ return false;
15
+ }
16
+ return true;
17
+ }
18
+
19
+ /**
20
+ * @param {Object} e2e
21
+ * @param {string} key
22
+ * @param {*} raw
23
+ */
24
+ function assignOptionalNonNegativeInt(e2e, key, raw) {
25
+ if (raw === undefined || raw === null || raw === '') {
26
+ return;
27
+ }
28
+ const n = Number(raw);
29
+ if (Number.isFinite(n) && n >= 0) {
30
+ e2e[key] = n;
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Merge E2E options from CLI flags into e2eOptions for ExternalDataSourceE2ETestRequest (dataplane).
36
+ * @param {Object} options - CLI-derived options
37
+ * @param {boolean} [options.debug]
38
+ * @param {boolean} [options.verbose]
39
+ * @param {boolean} [options.testCrud]
40
+ * @param {string} [options.recordId]
41
+ * @param {boolean} [options.cleanup]
42
+ * @param {string|Object} [options.primaryKeyValue]
43
+ * @param {Object} [options.e2eOptionsExtra] - Shallow-merged last (e.g. server-specific drill-down fields)
44
+ * @returns {Object}
45
+ */
46
+ function buildE2eOptionsFromCli(options = {}) {
47
+ const e2e = {};
48
+ if (options.debug) e2e.includeDebug = true;
49
+ if (options.verbose) e2e.audit = true;
50
+ if (options.testCrud === true) e2e.testCrud = true;
51
+ if (options.recordId !== undefined && options.recordId !== null && options.recordId !== '') {
52
+ e2e.recordId = String(options.recordId);
53
+ }
54
+ if (options.cleanup === false) e2e.cleanup = false;
55
+ else if (options.cleanup === true) e2e.cleanup = true;
56
+ if (options.primaryKeyValue !== undefined && options.primaryKeyValue !== null) {
57
+ e2e.primaryKeyValue = options.primaryKeyValue;
58
+ }
59
+ assignOptionalNonNegativeInt(e2e, 'minVectorHits', options.minVectorHits);
60
+ assignOptionalNonNegativeInt(e2e, 'minProcessed', options.minProcessed);
61
+ assignOptionalNonNegativeInt(e2e, 'minRecordCount', options.minRecordCount);
62
+ if (options.e2eOptionsExtra && typeof options.e2eOptionsExtra === 'object') {
63
+ Object.assign(e2e, options.e2eOptionsExtra);
64
+ }
65
+ return e2e;
66
+ }
67
+
68
+ /**
69
+ * Build request body for validationScope=externalDataSource (single datasource in DB).
70
+ * @param {Object} params
71
+ * @param {string} params.systemKey - External system key
72
+ * @param {string} params.datasourceKey - Datasource key
73
+ * @param {'test'|'integration'|'e2e'} params.runType
74
+ * @param {Object} [params.payloadTemplate] - Required for integration-style payload tests when runType=test
75
+ * @param {boolean} [params.asyncRun]
76
+ * @param {boolean} [params.includeDebug]
77
+ * @param {boolean} [params.explain]
78
+ * @param {Object} [params.e2eOptions] - Merged with buildE2eOptionsFromCli when both used by caller
79
+ * @returns {import('../api/types/validation-run.types').ValidationRunRequestBody}
80
+ */
81
+ function buildExternalDataSourceValidationRequest(params) {
82
+ const {
83
+ systemKey,
84
+ datasourceKey,
85
+ runType,
86
+ payloadTemplate,
87
+ asyncRun,
88
+ includeDebug,
89
+ explain,
90
+ e2eOptions
91
+ } = params;
92
+ if (!systemKey || !datasourceKey || !runType) {
93
+ throw new Error('systemKey, datasourceKey, and runType are required');
94
+ }
95
+ /** @type {import('../api/types/validation-run.types').ValidationRunRequestBody} */
96
+ const body = {
97
+ validationScope: 'externalDataSource',
98
+ systemIdOrKey: systemKey,
99
+ datasourceKey,
100
+ runType
101
+ };
102
+ if (payloadTemplate !== undefined) body.payloadTemplate = payloadTemplate;
103
+ if (asyncRun === true) body.asyncRun = true;
104
+ if (includeDebug === true) body.includeDebug = true;
105
+ if (explain === true) body.explain = true;
106
+ if (e2eOptions && typeof e2eOptions === 'object' && Object.keys(e2eOptions).length > 0) {
107
+ body.e2eOptions = e2eOptions;
108
+ }
109
+ return body;
110
+ }
111
+
112
+ module.exports = {
113
+ includeDebugForRequest,
114
+ buildE2eOptionsFromCli,
115
+ buildExternalDataSourceValidationRequest
116
+ };
@@ -105,6 +105,9 @@ function handlePartialAuthentication(authentication) {
105
105
  */
106
106
  function transformFlatStructure(variables, appName) {
107
107
  const result = buildBaseResult(variables, appName);
108
+ if (result.frontDoorRouting) {
109
+ result.frontDoorRouting = normalizeFrontDoorRoutingForValidation(result.frontDoorRouting);
110
+ }
108
111
 
109
112
  // Sanitize authentication if present
110
113
  if (result.authentication) {
@@ -190,9 +193,6 @@ function validateBuildConfig(build) {
190
193
  if (build.dockerfile && build.dockerfile.trim() !== '') {
191
194
  buildConfig.dockerfile = build.dockerfile;
192
195
  }
193
- if (build.localPort !== undefined && build.localPort !== null) {
194
- buildConfig.localPort = build.localPort;
195
- }
196
196
 
197
197
  return Object.keys(buildConfig).length > 0 ? buildConfig : null;
198
198
  }
@@ -256,6 +256,23 @@ function transformConfigSections(variables, transformed) {
256
256
  }
257
257
  }
258
258
 
259
+ /**
260
+ * Clone frontDoorRouting for schema validation: JSON Schema expects `tls` as a string
261
+ * (e.g. "${TLS_ENABLED}", "true") but YAML often uses booleans (`tls: false`).
262
+ * @param {Object} fd - frontDoorRouting block from application.yaml
263
+ * @returns {Object}
264
+ */
265
+ function normalizeFrontDoorRoutingForValidation(fd) {
266
+ if (!fd || typeof fd !== 'object') {
267
+ return fd;
268
+ }
269
+ const out = { ...fd };
270
+ if (typeof out.tls === 'boolean') {
271
+ out.tls = out.tls ? 'true' : 'false';
272
+ }
273
+ return out;
274
+ }
275
+
259
276
  /**
260
277
  * Transforms simple optional fields
261
278
  * @function transformSimpleOptionalFields
@@ -273,7 +290,7 @@ function transformSimpleOptionalFields(variables, transformed) {
273
290
  transformed.scaling = variables.scaling;
274
291
  }
275
292
  if (variables.frontDoorRouting) {
276
- transformed.frontDoorRouting = variables.frontDoorRouting;
293
+ transformed.frontDoorRouting = normalizeFrontDoorRoutingForValidation(variables.frontDoorRouting);
277
294
  }
278
295
  if (variables.roles) {
279
296
  transformed.roles = variables.roles;