@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,417 @@
1
+ /**
2
+ * @fileoverview System-level TTY renderer for DatasourceTestRun fan-out results (plan §17).
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ const chalk = require('chalk');
8
+ const logger = require('./logger');
9
+ const {
10
+ SEP,
11
+ statusGlyph
12
+ } = require('./datasource-test-run-display');
13
+ const {
14
+ sectionTitle,
15
+ headerKeyValue,
16
+ formatStatusKeyValue,
17
+ integrationFooterLine,
18
+ colorRollupPrefixedLine,
19
+ metadata: metaGray
20
+ } = require('./cli-test-layout-chalk');
21
+ const {
22
+ formatDataQualityLines,
23
+ readinessLineFromDataReadiness,
24
+ verdictLineFromEnvelope
25
+ } = require('./validation-report-tty-kit');
26
+ const {
27
+ logCapabilitiesOverview: logCapabilitiesOverviewSection,
28
+ logIntegrationHealthSection: logIntegrationHealthSectionBlock
29
+ } = require('./external-system-system-test-tty-overview');
30
+
31
+ /**
32
+ * @param {'ok'|'warn'|'fail'|'skipped'|null} st
33
+ * @returns {number}
34
+ */
35
+ function statusRank(st) {
36
+ if (st === 'fail') return 0;
37
+ if (st === 'warn') return 1;
38
+ if (st === 'ok') return 2;
39
+ if (st === 'skipped') return 3;
40
+ return 4;
41
+ }
42
+
43
+ /**
44
+ * Per-row status for system rollup and tables: CLI/transport failure overrides envelope-only OK.
45
+ * @param {{ skipped?: boolean, success?: boolean, datasourceTestRun?: { status?: string }|null }} r
46
+ * @returns {'ok'|'warn'|'fail'|'skipped'}
47
+ */
48
+ function rollupRowStatus(r) {
49
+ if (r && r.skipped) return 'skipped';
50
+ if (r && r.success === false) return 'fail';
51
+ const env = r && r.datasourceTestRun;
52
+ return env && typeof env.status === 'string' ? env.status : 'ok';
53
+ }
54
+
55
+ /**
56
+ * @param {Array<{ key: string, skipped?: boolean, datasourceTestRun?: Object|null, success?: boolean }>} rows
57
+ * @returns {'ok'|'warn'|'fail'|'skipped'}
58
+ */
59
+ function deriveSystemStatus(rows) {
60
+ if (!Array.isArray(rows) || rows.length === 0) return 'ok';
61
+ const statuses = rows.map(rollupRowStatus);
62
+ if (statuses.some(s => s === 'fail')) return 'fail';
63
+ if (statuses.some(s => s === 'warn')) return 'warn';
64
+ if (statuses.every(s => s === 'skipped')) return 'skipped';
65
+ // Mixed ok/skipped => warn per plan.
66
+ return statuses.every(s => s === 'ok') ? 'ok' : 'warn';
67
+ }
68
+
69
+ function bucketIssueSeverity(issue) {
70
+ const sev = issue && issue.severity ? String(issue.severity).toLowerCase() : '';
71
+ if (sev === 'error' || sev === 'critical' || sev === 'high' || sev === 'fatal') return 'fail';
72
+ if (sev === 'warn' || sev === 'warning' || sev === 'medium') return 'warn';
73
+ return null;
74
+ }
75
+
76
+ /**
77
+ * Minimal heuristic rollups backed by envelope fields (no engine codenames).
78
+ * @param {Object|null|undefined} env
79
+ * @returns {'ok'|'warn'|'fail'}
80
+ */
81
+ function dqFromEnvelope(env) {
82
+ const v = env && env.validation;
83
+ const st = v && typeof v.status === 'string' ? String(v.status) : null;
84
+ if (st === 'fail') return 'fail';
85
+ if (st === 'warn') return 'warn';
86
+
87
+ const issues = v && Array.isArray(v.issues) ? v.issues : [];
88
+ if (issues.some(i => bucketIssueSeverity(i) === 'fail')) return 'fail';
89
+ if (issues.some(i => bucketIssueSeverity(i) === 'warn')) return 'warn';
90
+ return 'ok';
91
+ }
92
+
93
+ /**
94
+ * @param {Array} rows
95
+ * @returns {{ schema: 'ok'|'warn'|'fail', consistency: 'ok'|'warn'|'fail', reliability: 'ok'|'warn'|'fail' }}
96
+ */
97
+ function deriveSystemDataQuality(rows) {
98
+ const envs = rows
99
+ .map(r => (r && r.datasourceTestRun && typeof r.datasourceTestRun === 'object' ? r.datasourceTestRun : null))
100
+ .filter(Boolean);
101
+ if (envs.length === 0) {
102
+ return { schema: 'warn', consistency: 'warn', reliability: 'warn' };
103
+ }
104
+
105
+ const picks = envs.map(dqFromEnvelope);
106
+ const agg = picks.some(x => x === 'fail') ? 'fail' : picks.some(x => x === 'warn') ? 'warn' : 'ok';
107
+ return { schema: agg, consistency: agg, reliability: agg };
108
+ }
109
+
110
+ /**
111
+ * @param {Array} rows
112
+ * @returns {'ready'|'partial'|'not_ready'|null}
113
+ */
114
+ function deriveSystemReadiness(rows) {
115
+ const envs = rows
116
+ .map(r => (r && r.datasourceTestRun && typeof r.datasourceTestRun === 'object' ? r.datasourceTestRun : null))
117
+ .filter(Boolean);
118
+ const drs = envs
119
+ .map(e => e.validation && e.validation.dataReadiness)
120
+ .filter(Boolean);
121
+ if (drs.length === 0) return null;
122
+ if (drs.some(x => x === 'not_ready')) return 'not_ready';
123
+ if (drs.some(x => x === 'partial')) return 'partial';
124
+ return 'ready';
125
+ }
126
+
127
+ function countByStatus(rows) {
128
+ const counts = { ok: 0, warn: 0, fail: 0, skipped: 0 };
129
+ for (const r of rows) {
130
+ const s = rollupRowStatus(r);
131
+ if (counts[s] !== undefined) counts[s] += 1;
132
+ }
133
+ return counts;
134
+ }
135
+
136
+ function pickBlockingDatasourceKey(rows) {
137
+ const keys = rows
138
+ .map(r => {
139
+ const env = r && r.datasourceTestRun;
140
+ const st = rollupRowStatus(r);
141
+ const key =
142
+ env && env.datasourceKey
143
+ ? String(env.datasourceKey)
144
+ : r && r.key
145
+ ? String(r.key)
146
+ : '';
147
+ return { key, st };
148
+ })
149
+ .filter(x => x.key);
150
+ keys.sort((a, b) => {
151
+ const d = statusRank(a.st) - statusRank(b.st);
152
+ if (d !== 0) return d;
153
+ return a.key.localeCompare(b.key);
154
+ });
155
+ return keys.length ? keys[0].key : null;
156
+ }
157
+
158
+ function issueKey(issue) {
159
+ const code = issue && issue.code ? String(issue.code) : '';
160
+ const msg = issue && issue.message ? String(issue.message) : '';
161
+ return `${code}::${msg}`.toLowerCase();
162
+ }
163
+
164
+ function issueSortKey(it) {
165
+ return `${statusRank(it.severity)}::${it.datasourceKey}::${it.message}`.toLowerCase();
166
+ }
167
+
168
+ function extractRowIssues(row) {
169
+ const env = row && row.datasourceTestRun;
170
+ const datasourceKey = (env && env.datasourceKey) || (row && row.key) || 'datasource';
171
+ const issues = env && env.validation && Array.isArray(env.validation.issues) ? env.validation.issues : [];
172
+ const envStatus = env && typeof env.status === 'string' ? env.status : null;
173
+ return { datasourceKey: String(datasourceKey), issues, envStatus };
174
+ }
175
+
176
+ function toIssueItem(datasourceKey, envStatus, iss) {
177
+ const sev =
178
+ bucketIssueSeverity(iss) ||
179
+ (envStatus === 'fail' ? 'fail' : envStatus === 'warn' ? 'warn' : 'ok');
180
+ const msg = iss && iss.message ? String(iss.message) : iss && iss.code ? String(iss.code) : 'Issue';
181
+ return { datasourceKey, message: msg, severity: sev };
182
+ }
183
+
184
+ function collectKeyIssues(rows, cap) {
185
+ /** @type {{ datasourceKey: string, message: string, severity: 'fail'|'warn'|'ok' }[]} */
186
+ const out = [];
187
+ const seen = new Set();
188
+ for (const row of rows) {
189
+ const { datasourceKey, issues, envStatus } = extractRowIssues(row);
190
+ for (const iss of issues) {
191
+ const k = `${datasourceKey}::${issueKey(iss)}`;
192
+ if (seen.has(k)) continue;
193
+ seen.add(k);
194
+ out.push(toIssueItem(datasourceKey, envStatus, iss));
195
+ }
196
+ }
197
+ out.sort((a, b) => issueSortKey(a).localeCompare(issueSortKey(b)));
198
+ return out.slice(0, Math.max(0, cap));
199
+ }
200
+
201
+ function certificateBucket(env) {
202
+ const cert = env && env.certificate;
203
+ if (!cert || typeof cert !== 'object') return { status: null, level: null };
204
+ return {
205
+ status: cert.status ? String(cert.status) : null,
206
+ level: cert.level ? String(cert.level) : null
207
+ };
208
+ }
209
+
210
+ function systemCertStatus(rows) {
211
+ const certs = rows
212
+ .map(r => (r && r.datasourceTestRun ? certificateBucket(r.datasourceTestRun) : { status: null, level: null }))
213
+ .filter(c => c.status !== null);
214
+ if (certs.length === 0) return null;
215
+ if (certs.some(c => c.status === 'not_passed')) return 'not_passed';
216
+ return 'passed';
217
+ }
218
+
219
+ function drillDownCommand(runType, datasourceKey) {
220
+ if (!datasourceKey) return null;
221
+ if (runType === 'e2e') return `aifabrix datasource test-e2e ${datasourceKey}`;
222
+ if (runType === 'integration') return `aifabrix datasource test-integration ${datasourceKey}`;
223
+ return `aifabrix datasource test ${datasourceKey}`;
224
+ }
225
+
226
+ function logSystemHeader(results, runType, systemStatus) {
227
+ logger.log('');
228
+ logger.log(sectionTitle('Server test results'));
229
+ logger.log('');
230
+ logger.log(headerKeyValue('System:', results.systemKey));
231
+ logger.log(
232
+ headerKeyValue(
233
+ 'Run:',
234
+ runType === 'e2e' ? 'test-e2e (dataplane)' : 'test-integration (dataplane)'
235
+ )
236
+ );
237
+ logger.log(formatStatusKeyValue(systemStatus, statusGlyph(systemStatus)));
238
+ logger.log('');
239
+ }
240
+
241
+ function logVerdictAndSummary(runType, systemStatus, certStatus, counts) {
242
+ logger.log(sectionTitle('Verdict:'));
243
+ logger.log(chalk.white(verdictLineFromEnvelope(systemStatus, certStatus, runType)));
244
+ logger.log('');
245
+ logger.log(sectionTitle('Summary:'));
246
+ logger.log(
247
+ chalk.white(
248
+ `${counts.ok + counts.warn + counts.fail} datasource(s): ${counts.ok} ok, ${counts.warn} warn, ${counts.fail} fail${counts.skipped ? `, ${counts.skipped} skipped` : ''}`
249
+ )
250
+ );
251
+ logger.log('');
252
+ logger.log(metaGray(SEP));
253
+ logger.log('');
254
+ }
255
+
256
+ function logDataQualityAndReadiness(rows) {
257
+ logger.log(sectionTitle('Data Quality:'));
258
+ const dq = deriveSystemDataQuality(rows);
259
+ const dqLines = formatDataQualityLines(dq, {
260
+ schema: 'structural coverage aggregated across datasources.',
261
+ consistency: 'issues aggregated across datasources.',
262
+ reliability: 'issues aggregated across datasources.'
263
+ });
264
+ dqLines.map(colorRollupPrefixedLine).forEach(l => logger.log(l));
265
+
266
+ const readiness = deriveSystemReadiness(rows);
267
+ const readinessLine = readinessLineFromDataReadiness(readiness);
268
+ if (readinessLine) {
269
+ logger.log('');
270
+ logger.log(colorRollupPrefixedLine(readinessLine));
271
+ }
272
+
273
+ logger.log('');
274
+ logger.log(metaGray(SEP));
275
+ logger.log('');
276
+ }
277
+
278
+ function logDatasourceTable(rows, counts, verbose) {
279
+ logger.log(sectionTitle('Datasources:'));
280
+ logger.log('');
281
+ if (!verbose && counts.ok > 0) {
282
+ logger.log(chalk.gray(`✔ ${counts.ok} datasource(s) fully ready`));
283
+ }
284
+
285
+ function rowStatus(r) {
286
+ return rollupRowStatus(r);
287
+ }
288
+
289
+ function readinessLabel(env, st) {
290
+ const ready = env && env.validation ? env.validation.dataReadiness : null;
291
+ if (ready === 'not_ready') return 'Not ready';
292
+ if (ready === 'partial') return 'Partial';
293
+ if (ready === 'ready') return 'Ready';
294
+ if (st === 'fail') return 'Not ready';
295
+ if (st === 'warn') return 'Partial';
296
+ return 'Ready';
297
+ }
298
+
299
+ function shouldListRow(r) {
300
+ if (verbose) return true;
301
+ const st = rowStatus(r);
302
+ return st === 'warn' || st === 'fail';
303
+ }
304
+
305
+ const listRows = rows.filter(shouldListRow);
306
+ for (const r of listRows) {
307
+ const env = r && r.datasourceTestRun;
308
+ const key = (env && env.datasourceKey) || (r && r.key) || 'datasource';
309
+ const st = rowStatus(r);
310
+ const readyLabel = readinessLabel(env, st);
311
+ logger.log(`${statusGlyph(st)} ${chalk.white(String(key).padEnd(22))} (${readyLabel})`);
312
+ }
313
+ }
314
+
315
+ function logBlockingDatasource(blocking) {
316
+ if (!blocking) return;
317
+ logger.log('');
318
+ logger.log(chalk.white(`Blocking datasource: ${blocking}`));
319
+ }
320
+
321
+ function logKeyIssuesSection(rows) {
322
+ const issues = collectKeyIssues(rows, 5);
323
+ if (issues.length === 0) return false;
324
+ logger.log('');
325
+ logger.log(metaGray(SEP));
326
+ logger.log('');
327
+ logger.log(sectionTitle('Key issues:'));
328
+ logger.log('');
329
+ let cur = null;
330
+ for (const it of issues) {
331
+ if (cur !== it.datasourceKey) {
332
+ cur = it.datasourceKey;
333
+ logger.log(chalk.white(cur));
334
+ }
335
+ logger.log(chalk.white(`- ${it.message}`));
336
+ }
337
+ return true;
338
+ }
339
+
340
+ function logCertificationSection(rows) {
341
+ const certSt = systemCertStatus(rows);
342
+ if (!certSt) return;
343
+ logger.log('');
344
+ logger.log(metaGray(SEP));
345
+ logger.log('');
346
+ logger.log(sectionTitle('Certification:'));
347
+ logger.log('');
348
+ logger.log(chalk.white(`System level: ${certSt === 'passed' ? '✔ Achieved' : '✖ Not achieved'}`));
349
+ logger.log('');
350
+ logger.log(chalk.white('Breakdown:'));
351
+ for (const r of rows) {
352
+ const env = r && r.datasourceTestRun;
353
+ if (!env) continue;
354
+ const cert = certificateBucket(env);
355
+ if (!cert.status) continue;
356
+ const g = cert.status === 'passed' ? '✔' : '✖';
357
+ const tier = cert.level ? cert.level : '(no level)';
358
+ logger.log(chalk.white(`- ${env.datasourceKey}: ${g} ${tier}`));
359
+ }
360
+ }
361
+
362
+ function logUseAndFooter(results, runType, systemStatus, blocking) {
363
+ logger.log('');
364
+ logger.log(metaGray(SEP));
365
+ logger.log('');
366
+ logger.log(sectionTitle('Use:'));
367
+ const cmd = drillDownCommand(runType, blocking);
368
+ logger.log(chalk.white(cmd || 'aifabrix datasource test <datasourceKey>'));
369
+ logger.log(
370
+ integrationFooterLine(
371
+ results.success,
372
+ systemStatus,
373
+ 'All server tests passed.',
374
+ 'Server tests completed with warnings.',
375
+ 'Some server tests failed.'
376
+ )
377
+ );
378
+ }
379
+
380
+ /**
381
+ * Render system-level aggregate (plan §17) for DatasourceTestRun wrapper results.
382
+ * @param {Object} results
383
+ * @param {Object} opts
384
+ * @param {'integration'|'e2e'} opts.runType
385
+ * @param {boolean} opts.verbose
386
+ */
387
+ function displaySystemAggregateDatasourceTestRuns(results, opts) {
388
+ const rows = Array.isArray(results.datasourceResults) ? results.datasourceResults : [];
389
+ const runType = opts.runType === 'e2e' ? 'e2e' : 'integration';
390
+ const systemStatus = deriveSystemStatus(rows);
391
+ const counts = countByStatus(rows);
392
+ const blocking = pickBlockingDatasourceKey(rows);
393
+ logSystemHeader(results, runType, systemStatus);
394
+ logVerdictAndSummary(runType, systemStatus, systemCertStatus(rows), counts);
395
+ logDataQualityAndReadiness(rows);
396
+ logDatasourceTable(rows, counts, Boolean(opts.verbose));
397
+ logBlockingDatasource(blocking);
398
+ logKeyIssuesSection(rows);
399
+ const ttyIo = { log: logger.log.bind(logger), chalk, metaGray, sectionTitle, statusGlyph, SEP };
400
+ logCapabilitiesOverviewSection(rows, ttyIo);
401
+ logIntegrationHealthSectionBlock(rows, runType, ttyIo);
402
+ logCertificationSection(rows);
403
+ logUseAndFooter(results, runType, systemStatus, blocking);
404
+ }
405
+
406
+ module.exports = {
407
+ displaySystemAggregateDatasourceTestRuns,
408
+ deriveSystemStatus,
409
+ deriveSystemDataQuality,
410
+ deriveSystemReadiness,
411
+ rollupRowStatus,
412
+ pickBlockingDatasourceKey,
413
+ collectKeyIssues,
414
+ systemCertStatus,
415
+ drillDownCommand
416
+ };
417
+
@@ -12,9 +12,32 @@
12
12
  const fs = require('fs').promises;
13
13
  const path = require('path');
14
14
  const { testDatasourceViaPipeline } = require('../api/pipeline.api');
15
+ const { integrationResultFromEnvelope, firstIssueMessage } = require('./datasource-test-run-legacy-adapter');
15
16
 
16
17
  /** Pipeline test endpoints accept client credentials; do not enforce Bearer-only */
17
18
 
19
+ /**
20
+ * Parse successful pipeline test body (envelope or legacy).
21
+ * @param {Object} body
22
+ * @param {string} datasourceKey
23
+ * @returns {Object}
24
+ * @throws {Error} When body indicates failure
25
+ */
26
+ function parsePipelineTestResponseBody(body, datasourceKey) {
27
+ if (body && typeof body === 'object' && typeof body.status === 'string' && body.datasourceKey) {
28
+ if (body.status === 'fail') {
29
+ const errMsg = firstIssueMessage(body) || 'Test failed';
30
+ throw new Error(`Test endpoint failed: ${errMsg}`);
31
+ }
32
+ return integrationResultFromEnvelope(body, datasourceKey);
33
+ }
34
+ if (body.success === false) {
35
+ const errMsg = body.error || body.formattedError || 'Test failed';
36
+ throw new Error(`Test endpoint failed: ${errMsg}`);
37
+ }
38
+ return body.data || body;
39
+ }
40
+
18
41
  /**
19
42
  * Retry API call with exponential backoff
20
43
  * @async
@@ -73,13 +96,8 @@ async function callPipelineTestEndpoint({ systemKey, datasourceKey, payloadTempl
73
96
  if (!response.success || !response.data) {
74
97
  throw new Error(`Test endpoint failed: ${response.error || response.formattedError || 'Unknown error'}`);
75
98
  }
76
- // When 200 with success: false in body, pass through; caller interprets via data.success
77
- if (response.data?.success === false) {
78
- const errMsg = response.data?.error || response.data?.formattedError || 'Test failed';
79
- throw new Error(`Test endpoint failed: ${errMsg}`);
80
- }
81
99
 
82
- return response.data.data || response.data;
100
+ return parsePipelineTestResponseBody(response.data, datasourceKey);
83
101
  }
84
102
 
85
103
  /**
@@ -119,24 +119,38 @@ function checkPathExistsInPayload(fieldPath, payloadTemplate) {
119
119
  }
120
120
 
121
121
  /**
122
- * Validates dimensions object structure and content
123
- * @param {Object} dimensions - Dimensions object to validate
122
+ * Root dimensions (dimensionBinding): local bindings as dimKey → metadata path string for validateDimensions.
123
+ * @param {Object} datasource - Datasource config
124
+ * @returns {Object} Map suitable for validateDimensions
125
+ */
126
+ function getDimensionsMapForValidation(datasource) {
127
+ const root = datasource?.dimensions;
128
+ if (!root || typeof root !== 'object' || Array.isArray(root)) return {};
129
+ const out = {};
130
+ for (const [dimKey, binding] of Object.entries(root)) {
131
+ if (binding && typeof binding === 'object' && typeof binding.field === 'string') {
132
+ out[dimKey] = `metadata.${binding.field}`;
133
+ }
134
+ }
135
+ return out;
136
+ }
137
+
138
+ /**
139
+ * Validates dimensions object structure and content (path strings).
140
+ * @param {Object} dimensions - dimKey → path (e.g. metadata.country)
124
141
  * @param {Object} results - Results object to update
125
142
  */
126
143
  function validateDimensions(dimensions, results) {
127
- if (!dimensions) {
128
- results.errors.push('fieldMappings.dimensions is required (dimensions-first model)');
129
- results.valid = false;
144
+ if (!dimensions || typeof dimensions !== 'object' || Array.isArray(dimensions)) {
145
+ results.warnings.push('No dimensions configured (schema v2.4: use root dimensions; optional but recommended for ABAC)');
130
146
  return;
131
147
  }
132
148
 
133
- if (typeof dimensions !== 'object' || Array.isArray(dimensions)) {
134
- results.errors.push('fieldMappings.dimensions must be an object mapping dimension keys to attribute paths');
135
- results.valid = false;
149
+ if (Object.keys(dimensions).length === 0) {
150
+ results.warnings.push('Dimensions object is empty');
136
151
  return;
137
152
  }
138
153
 
139
- // Validate dimension keys and values
140
154
  for (const [dimensionKey, attributePath] of Object.entries(dimensions)) {
141
155
  if (!/^[a-zA-Z0-9_]+$/.test(dimensionKey)) {
142
156
  results.errors.push(`Invalid dimension key '${dimensionKey}': dimension key must contain only letters, numbers, and underscores`);
@@ -180,7 +194,7 @@ function validateAttributesStructure(attributes, results) {
180
194
  */
181
195
  function validateSingleAttribute(attributeName, attributeConfig, payloadTemplate, results) {
182
196
  if (!attributeConfig || typeof attributeConfig !== 'object') {
183
- results.errors.push(`Attribute '${attributeName}' must be an object with expression and type`);
197
+ results.errors.push(`Attribute '${attributeName}' must be an object with expression`);
184
198
  results.valid = false;
185
199
  return;
186
200
  }
@@ -224,8 +238,7 @@ function validateFieldMappings(datasource, testPayload) {
224
238
  return results;
225
239
  }
226
240
 
227
- // Validate dimensions (required in new schema)
228
- validateDimensions(datasource.fieldMappings.dimensions, results);
241
+ validateDimensions(getDimensionsMapForValidation(datasource), results);
229
242
 
230
243
  // Validate attributes structure (required in new schema)
231
244
  const attributes = validateAttributesStructure(datasource.fieldMappings.attributes, results);
@@ -322,6 +335,11 @@ function validateAgainstSchema(data, schema) {
322
335
  validateSchema: false
323
336
  });
324
337
  addFormats(ajv);
338
+ // Some schemas (e.g. external-datasource.schema.json) reference these by $id (aifabrix://schema/type/*).
339
+ // Adding them up-front keeps validation offline-safe and avoids unresolved $ref failures.
340
+ ajv.addSchema(require('../schema/type/document-storage.json'));
341
+ ajv.addSchema(require('../schema/type/message-service.json'));
342
+ ajv.addSchema(require('../schema/type/vector-store.json'));
325
343
  // Remove $schema for draft-2020-12 to avoid AJV issues
326
344
  const schemaCopy = { ...schema };
327
345
  if (schemaCopy.$schema && schemaCopy.$schema.includes('2020-12')) {
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Health check URL resolution helpers.
3
+ *
4
+ * @fileoverview Compute health check URL using declarative public URL logic (Traefik/frontDoorRouting)
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { computePublicUrlBaseString } = require('./url-declarative-public-base');
10
+ const { parseDeveloperIdNum } = require('./declarative-url-ports');
11
+
12
+ /**
13
+ * Join URL path segments with exactly one slash between them.
14
+ * @param {string} a
15
+ * @param {string} b
16
+ * @returns {string}
17
+ */
18
+ function joinUrlPath(a, b) {
19
+ const left = String(a || '').replace(/\/+$/, '');
20
+ const right = String(b || '').replace(/^\/+/, '');
21
+ if (!left) return `/${right}`;
22
+ if (!right) return left || '/';
23
+ return `${left}/${right}`;
24
+ }
25
+
26
+ /**
27
+ * Convert a frontDoorRouting.pattern (Traefik/Front Door wildcard route) into a concrete mount path.
28
+ * Example: "/auth/*" -> "/auth"
29
+ *
30
+ * @param {string} pattern
31
+ * @returns {string}
32
+ */
33
+ function normalizeFrontDoorPatternForHealth(pattern) {
34
+ let p = String(pattern || '').trim();
35
+ if (!p) return '/';
36
+ if (!p.startsWith('/')) p = `/${p}`;
37
+ // Drop wildcard suffixes used for routing.
38
+ p = p.replace(/\/\*+$/, '');
39
+ p = p.replace(/\*+$/, '');
40
+ // Drop trailing path params like "/foo/{bar}" if present (health is mounted at the base).
41
+ p = p.replace(/\/\{[^}]+\}$/, '');
42
+ // Normalize slashes and trailing slash.
43
+ p = p.replace(/\/{2,}/g, '/');
44
+ p = p.replace(/\/+$/, '');
45
+ return p || '/';
46
+ }
47
+
48
+ /**
49
+ * @param {Object|null} appConfig
50
+ * @returns {boolean}
51
+ */
52
+ function frontDoorEnabled(appConfig) {
53
+ return Boolean(appConfig && appConfig.frontDoorRouting && appConfig.frontDoorRouting.enabled === true);
54
+ }
55
+
56
+ /**
57
+ * @param {Object|null} appConfig
58
+ * @returns {string|null}
59
+ */
60
+ function frontDoorPattern(appConfig) {
61
+ const p = appConfig && appConfig.frontDoorRouting ? appConfig.frontDoorRouting.pattern : null;
62
+ return (typeof p === 'string' && p.trim()) ? p.trim() : null;
63
+ }
64
+
65
+ /**
66
+ * Compute the Traefik front-door health check URL when applicable.
67
+ *
68
+ * Returns null when Traefik/frontDoorRouting isn't active or cannot be resolved.
69
+ *
70
+ * @async
71
+ * @param {string} appName
72
+ * @param {number} healthCheckPort
73
+ * @param {Object|null} appConfig
74
+ * @returns {Promise<string|null>}
75
+ */
76
+ async function computeTraefikHealthCheckUrl(appName, healthCheckPort, appConfig) {
77
+ if (!frontDoorEnabled(appConfig)) return null;
78
+ const pattern = frontDoorPattern(appConfig);
79
+ if (!pattern) return null;
80
+
81
+ const coreConfig = require('../core/config');
82
+ const userCfg = await coreConfig.getConfig();
83
+ if (!(userCfg && userCfg.traefik)) return null;
84
+
85
+ const infraTlsEnabled = Boolean(userCfg && userCfg.tlsEnabled);
86
+ const remoteServer = await coreConfig.getRemoteServer();
87
+ const developerIdRaw = await coreConfig.getDeveloperId();
88
+ const developerIdNum = parseDeveloperIdNum(developerIdRaw);
89
+
90
+ // Health checks originate from the CLI on the host, not from inside a container.
91
+ const profile = 'local';
92
+ const fd = appConfig.frontDoorRouting;
93
+ const listenPort = Number(appConfig?.port || 3000);
94
+
95
+ const publicBase = computePublicUrlBaseString({
96
+ traefik: true,
97
+ pathActive: true,
98
+ hostTemplate: fd.host,
99
+ tls: fd.tls,
100
+ developerIdRaw,
101
+ remoteServer,
102
+ profile,
103
+ listenPort,
104
+ developerIdNum,
105
+ infraTlsEnabled
106
+ });
107
+
108
+ const healthCheckPath = appConfig?.healthCheck?.path || '/health';
109
+ const mountPath = normalizeFrontDoorPatternForHealth(pattern);
110
+ const baseWithFrontDoor = joinUrlPath(publicBase, mountPath);
111
+ return joinUrlPath(baseWithFrontDoor, healthCheckPath);
112
+ }
113
+
114
+ module.exports = {
115
+ joinUrlPath,
116
+ normalizeFrontDoorPatternForHealth,
117
+ computeTraefikHealthCheckUrl
118
+ };
119
+