@aifabrix/builder 2.42.1 → 2.44.0

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 (392) hide show
  1. package/.cursor/rules/anchor-docs.mdc +15 -0
  2. package/README.md +2 -2
  3. package/anchor-docs/README.md +10 -0
  4. package/anchor-docs/_TEMPLATE +24 -0
  5. package/bin/aifabrix.js +13 -4
  6. package/integration/hubspot-test/README.md +157 -0
  7. package/integration/{hubspot → hubspot-test}/application.json +6 -6
  8. package/integration/{hubspot → hubspot-test}/create-hubspot.js +10 -10
  9. package/integration/hubspot-test/env.template +4 -0
  10. package/integration/hubspot-test/hubspot-test-datasource-company.json +138 -0
  11. package/integration/hubspot-test/hubspot-test-datasource-contact.json +146 -0
  12. package/integration/hubspot-test/hubspot-test-datasource-deal.json +146 -0
  13. package/integration/hubspot-test/hubspot-test-datasource-users.json +76 -0
  14. package/integration/{hubspot/hubspot-deploy.json → hubspot-test/hubspot-test-deploy.json} +201 -24
  15. package/integration/{hubspot/hubspot-system.json → hubspot-test/hubspot-test-system.json} +8 -7
  16. package/integration/hubspot-test/rbac.json +166 -0
  17. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-credential-real.yaml +3 -3
  18. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-env-vars.yaml +2 -2
  19. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-add-datasource.yaml +1 -1
  20. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-create.yaml +1 -1
  21. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-select.yaml +1 -1
  22. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-known-platform.yaml +1 -1
  23. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +2 -0
  24. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-mode.yaml +1 -1
  25. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-file.yaml +1 -1
  26. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-url.yaml +1 -1
  27. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-source.yaml +1 -1
  28. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-array-test.yaml +1 -1
  29. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +5 -0
  30. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +5 -0
  31. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-test.yaml +1 -1
  32. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-test.yaml +1 -1
  33. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +1 -1
  34. package/integration/{hubspot → hubspot-test}/test-dataplane-down-tests.js +1 -7
  35. package/integration/{hubspot → hubspot-test}/test-dataplane-down.js +3 -3
  36. package/integration/{hubspot → hubspot-test}/test.js +137 -102
  37. package/integration/{hubspot → hubspot-test}/wizard-hubspot-e2e.yaml +2 -2
  38. package/integration/{hubspot → hubspot-test}/wizard-hubspot-platform.yaml +1 -1
  39. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +23 -0
  40. package/integration/roundtrip-test-local/README.md +144 -0
  41. package/integration/roundtrip-test-local/application.yaml +13 -0
  42. package/integration/roundtrip-test-local/env.template +15 -0
  43. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +14 -0
  44. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +61 -0
  45. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +25 -0
  46. package/integration/roundtrip-test-local2/README.md +144 -0
  47. package/integration/roundtrip-test-local2/application.yaml +13 -0
  48. package/integration/roundtrip-test-local2/env.template +15 -0
  49. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +31 -0
  50. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +86 -0
  51. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +25 -0
  52. package/integration/test/wizard.yaml +8 -0
  53. package/jest.config.default.js +10 -0
  54. package/jest.config.integration.fixtures.js +22 -0
  55. package/jest.config.integration.js +21 -18
  56. package/jest.config.isolated.js +10 -0
  57. package/jest.projects.js +288 -0
  58. package/lib/api/datasources-core.api.js +3 -3
  59. package/lib/api/dev-mtls-request.js +110 -0
  60. package/lib/api/dev-server-https.js +145 -0
  61. package/lib/api/dev.api.js +133 -144
  62. package/lib/api/index.js +0 -1
  63. package/lib/api/pipeline.api.js +67 -20
  64. package/lib/api/service-users.api.js +111 -2
  65. package/lib/api/types/dev.types.js +4 -3
  66. package/lib/api/types/pipeline.types.js +8 -5
  67. package/lib/api/types/service-users.types.js +41 -0
  68. package/lib/api/types/validation-run.types.js +56 -0
  69. package/lib/api/validation-run.api.js +99 -0
  70. package/lib/api/validation-runner.js +99 -0
  71. package/lib/app/config.js +1 -1
  72. package/lib/app/deploy-status-display.js +2 -2
  73. package/lib/app/deploy.js +7 -6
  74. package/lib/app/display.js +2 -1
  75. package/lib/app/dockerfile.js +3 -2
  76. package/lib/app/down.js +2 -1
  77. package/lib/app/helpers.js +6 -5
  78. package/lib/app/index.js +27 -8
  79. package/lib/app/list.js +7 -6
  80. package/lib/app/push.js +4 -3
  81. package/lib/app/register.js +19 -8
  82. package/lib/app/rotate-secret.js +17 -13
  83. package/lib/app/run-container-start.js +184 -0
  84. package/lib/app/run-docker-fallback.js +108 -0
  85. package/lib/app/run-env-compose.js +30 -42
  86. package/lib/app/run-helpers.js +49 -126
  87. package/lib/app/run-infra-requirements.js +30 -0
  88. package/lib/app/run-resolve-image.js +21 -0
  89. package/lib/app/run.js +74 -21
  90. package/lib/app/show-display.js +1 -1
  91. package/lib/app/show.js +1 -1
  92. package/lib/build/index.js +13 -10
  93. package/lib/cli/index.js +2 -0
  94. package/lib/cli/setup-app.help.js +67 -0
  95. package/lib/cli/setup-app.js +59 -123
  96. package/lib/cli/setup-app.test-commands.js +179 -0
  97. package/lib/cli/setup-auth.js +36 -14
  98. package/lib/cli/setup-credential-deployment.js +22 -8
  99. package/lib/cli/setup-dev-path-commands.js +124 -0
  100. package/lib/cli/setup-dev.js +190 -103
  101. package/lib/cli/setup-environment.js +11 -20
  102. package/lib/cli/setup-external-system.js +62 -22
  103. package/lib/cli/setup-infra.js +139 -47
  104. package/lib/cli/setup-parameters.js +32 -0
  105. package/lib/cli/setup-secrets.js +147 -10
  106. package/lib/cli/setup-service-user.js +146 -20
  107. package/lib/cli/setup-utility.js +47 -19
  108. package/lib/commands/app-down.js +5 -7
  109. package/lib/commands/app-install.js +14 -7
  110. package/lib/commands/app-logs.js +13 -10
  111. package/lib/commands/app-shell.js +4 -1
  112. package/lib/commands/app-test.js +25 -19
  113. package/lib/commands/app.js +22 -10
  114. package/lib/commands/auth-config.js +10 -14
  115. package/lib/commands/auth-status.js +4 -3
  116. package/lib/commands/credential-env.js +4 -3
  117. package/lib/commands/credential-list.js +5 -4
  118. package/lib/commands/credential-push.js +4 -3
  119. package/lib/commands/datasource-unified-test-cli.js +495 -0
  120. package/lib/commands/datasource-unified-test-cli.options.js +149 -0
  121. package/lib/commands/datasource-validation-cli.js +129 -0
  122. package/lib/commands/datasource.js +123 -71
  123. package/lib/commands/deployment-list.js +6 -5
  124. package/lib/commands/dev-cli-handlers.js +122 -18
  125. package/lib/commands/dev-down.js +4 -3
  126. package/lib/commands/dev-init.js +231 -116
  127. package/lib/commands/dev-show-display.js +473 -0
  128. package/lib/commands/login-credentials.js +3 -2
  129. package/lib/commands/login-device.js +4 -3
  130. package/lib/commands/login.js +5 -4
  131. package/lib/commands/logout.js +8 -7
  132. package/lib/commands/parameters-validate.js +54 -0
  133. package/lib/commands/repair-datasource.js +314 -68
  134. package/lib/commands/repair-env-template.js +16 -10
  135. package/lib/commands/repair-rbac.js +25 -19
  136. package/lib/commands/repair.js +116 -32
  137. package/lib/commands/secrets-list.js +23 -12
  138. package/lib/commands/secrets-remove-all.js +220 -0
  139. package/lib/commands/secrets-remove.js +22 -13
  140. package/lib/commands/secrets-set.js +21 -12
  141. package/lib/commands/secrets-validate.js +20 -7
  142. package/lib/commands/secure.js +10 -9
  143. package/lib/commands/service-user.js +243 -13
  144. package/lib/commands/test-e2e-external.js +27 -1
  145. package/lib/commands/up-common.js +28 -2
  146. package/lib/commands/up-dataplane.js +31 -18
  147. package/lib/commands/up-miso.js +19 -29
  148. package/lib/commands/upload.js +138 -39
  149. package/lib/commands/wizard-core-helpers.js +1 -1
  150. package/lib/commands/wizard-dataplane.js +4 -3
  151. package/lib/commands/wizard-helpers.js +3 -3
  152. package/lib/commands/wizard.js +2 -2
  153. package/lib/core/admin-secrets.js +16 -5
  154. package/lib/core/audit-logger.js +12 -4
  155. package/lib/core/config-attach-extensions.js +46 -0
  156. package/lib/core/config-runtime-paths.js +29 -0
  157. package/lib/core/config.js +59 -58
  158. package/lib/core/diff.js +3 -2
  159. package/lib/core/ensure-encryption-key.js +2 -4
  160. package/lib/core/secrets-ensure-infra.js +77 -0
  161. package/lib/core/secrets-ensure.js +120 -64
  162. package/lib/core/secrets-env-write.js +35 -7
  163. package/lib/core/secrets-infra-placeholder-sync.js +61 -0
  164. package/lib/core/secrets.js +228 -42
  165. package/lib/core/templates-env.js +4 -3
  166. package/lib/core/templates.js +1 -1
  167. package/lib/datasource/abac-validator.js +148 -0
  168. package/lib/datasource/deploy.js +75 -53
  169. package/lib/datasource/field-reference-validator.js +77 -36
  170. package/lib/datasource/integration-context.js +63 -0
  171. package/lib/datasource/list.js +8 -7
  172. package/lib/datasource/log-viewer.js +252 -0
  173. package/lib/datasource/resolve-app.js +109 -0
  174. package/lib/datasource/test-e2e.js +95 -155
  175. package/lib/datasource/test-integration.js +121 -109
  176. package/lib/datasource/unified-validation-run-body.js +65 -0
  177. package/lib/datasource/unified-validation-run-post.js +23 -0
  178. package/lib/datasource/unified-validation-run-resolve.js +43 -0
  179. package/lib/datasource/unified-validation-run.js +92 -0
  180. package/lib/datasource/validate.js +162 -15
  181. package/lib/deployment/deployer.js +4 -3
  182. package/lib/deployment/environment.js +7 -6
  183. package/lib/deployment/push.js +17 -8
  184. package/lib/external-system/delete.js +4 -3
  185. package/lib/external-system/deploy.js +131 -53
  186. package/lib/external-system/download-helpers.js +1 -1
  187. package/lib/external-system/download.js +7 -6
  188. package/lib/external-system/generator.js +104 -14
  189. package/lib/external-system/integration-test-dispatch.js +26 -0
  190. package/lib/external-system/test-execution.js +5 -1
  191. package/lib/external-system/test-helpers.js +0 -4
  192. package/lib/external-system/test-system-level-helpers.js +110 -0
  193. package/lib/external-system/test-system-level.js +83 -44
  194. package/lib/external-system/test.js +59 -8
  195. package/lib/generator/builders.js +23 -11
  196. package/lib/generator/deploy-manifest-azure-kv.js +81 -0
  197. package/lib/generator/external-controller-manifest.js +3 -3
  198. package/lib/generator/external.js +23 -11
  199. package/lib/generator/helpers.js +71 -12
  200. package/lib/generator/index.js +8 -4
  201. package/lib/generator/split-readme.js +12 -7
  202. package/lib/generator/split-variables.js +2 -1
  203. package/lib/generator/split.js +46 -11
  204. package/lib/generator/wizard-readme.js +3 -3
  205. package/lib/generator/wizard.js +16 -13
  206. package/lib/infrastructure/compose.js +60 -6
  207. package/lib/infrastructure/helpers.js +238 -51
  208. package/lib/infrastructure/index.js +64 -37
  209. package/lib/infrastructure/services.js +21 -15
  210. package/lib/internal/fs-real-sync.js +104 -0
  211. package/lib/internal/node-fs.js +98 -0
  212. package/lib/parameters/database-secret-values.js +173 -0
  213. package/lib/parameters/infra-kv-discovery.js +121 -0
  214. package/lib/parameters/infra-parameter-catalog.js +458 -0
  215. package/lib/parameters/infra-parameter-validate.js +64 -0
  216. package/lib/schema/application-schema.json +37 -17
  217. package/lib/schema/datasource-test-run.schema.json +493 -0
  218. package/lib/schema/deployment-rules.yaml +102 -63
  219. package/lib/schema/external-datasource.schema.json +1201 -433
  220. package/lib/schema/external-system.schema.json +181 -5
  221. package/lib/schema/flag-map-validation-run.json +31 -0
  222. package/lib/schema/infra-parameter.schema.json +106 -0
  223. package/lib/schema/infra.parameter.yaml +421 -0
  224. package/lib/schema/type/credential-auth-templates.json +40 -0
  225. package/lib/schema/type/document-storage.json +213 -0
  226. package/lib/schema/type/message-service.json +123 -0
  227. package/lib/schema/type/vector-store.json +88 -0
  228. package/lib/utils/aifabrix-runtime-config-dir.js +132 -0
  229. package/lib/utils/api-error-handler.js +2 -2
  230. package/lib/utils/api.js +49 -14
  231. package/lib/utils/app-config-resolver.js +23 -1
  232. package/lib/utils/app-register-api.js +3 -2
  233. package/lib/utils/app-register-auth.js +1 -1
  234. package/lib/utils/app-register-config.js +4 -4
  235. package/lib/utils/app-register-display.js +3 -2
  236. package/lib/utils/app-register-validator.js +3 -2
  237. package/lib/utils/app-run-containers.js +26 -22
  238. package/lib/utils/app-scoped-config.js +31 -0
  239. package/lib/utils/app-service-env-from-builder.js +164 -0
  240. package/lib/utils/build-copy.js +1 -1
  241. package/lib/utils/build-helpers.js +20 -20
  242. package/lib/utils/build-resolve-image.js +165 -0
  243. package/lib/utils/cli-layout-chalk.js +8 -0
  244. package/lib/utils/cli-test-layout-chalk.js +267 -0
  245. package/lib/utils/cli-utils.js +88 -11
  246. package/lib/utils/compose-db-passwords.js +138 -0
  247. package/lib/utils/compose-generate-docker-compose.js +216 -0
  248. package/lib/utils/compose-generator.js +197 -291
  249. package/lib/utils/compose-miso-env.js +18 -0
  250. package/lib/utils/compose-traefik-ingress-base.js +158 -0
  251. package/lib/utils/config-paths.js +209 -6
  252. package/lib/utils/config-scoped-resources-preference.js +41 -0
  253. package/lib/utils/controller-deployment-outcome.js +68 -0
  254. package/lib/utils/credential-display.js +2 -2
  255. package/lib/utils/credential-secrets-env.js +16 -1
  256. package/lib/utils/dataplane-pipeline-warning.js +4 -3
  257. package/lib/utils/datasource-test-run-capability-scope.js +43 -0
  258. package/lib/utils/datasource-test-run-debug-display.js +137 -0
  259. package/lib/utils/datasource-test-run-debug-slice.js +93 -0
  260. package/lib/utils/datasource-test-run-display.js +442 -0
  261. package/lib/utils/datasource-test-run-exit.js +58 -0
  262. package/lib/utils/datasource-test-run-legacy-adapter.js +93 -0
  263. package/lib/utils/datasource-test-run-report-version.js +51 -0
  264. package/lib/utils/datasource-test-run-schema-sync.js +59 -0
  265. package/lib/utils/datasource-test-run-tty-log.js +81 -0
  266. package/lib/utils/datasource-validation-watch.js +266 -0
  267. package/lib/utils/declarative-url-ports.js +47 -0
  268. package/lib/utils/derive-env-key-from-client-id.js +41 -0
  269. package/lib/utils/dev-ca-install.js +185 -23
  270. package/lib/utils/dev-cert-helper.js +266 -17
  271. package/lib/utils/dev-hosts-helper.js +307 -0
  272. package/lib/utils/dev-init-cert-hints.js +37 -0
  273. package/lib/utils/dev-init-health-messages.js +52 -0
  274. package/lib/utils/dev-init-resolve.js +86 -0
  275. package/lib/utils/dev-init-ssh-merge.js +65 -0
  276. package/lib/utils/dev-ssh-config-helper.js +196 -0
  277. package/lib/utils/dev-user-groups.js +93 -0
  278. package/lib/utils/docker-build.js +42 -17
  279. package/lib/utils/docker-exec.js +28 -0
  280. package/lib/utils/docker-manifest-public-port.js +116 -0
  281. package/lib/utils/docker-not-running-hint.js +52 -0
  282. package/lib/utils/docker.js +98 -11
  283. package/lib/utils/ensure-dev-certs-for-remote-docker.js +192 -0
  284. package/lib/utils/env-config-loader.js +10 -91
  285. package/lib/utils/env-copy.js +19 -10
  286. package/lib/utils/env-map.js +42 -11
  287. package/lib/utils/env-template.js +2 -2
  288. package/lib/utils/environment-scoped-resources.js +144 -0
  289. package/lib/utils/error-formatter.js +125 -9
  290. package/lib/utils/error-formatters/http-status-errors.js +6 -5
  291. package/lib/utils/error-formatters/network-errors.js +2 -1
  292. package/lib/utils/error-formatters/permission-errors.js +2 -1
  293. package/lib/utils/error-formatters/validation-errors.js +2 -1
  294. package/lib/utils/external-env-template.js +180 -0
  295. package/lib/utils/external-readme.js +8 -1
  296. package/lib/utils/external-system-display.js +277 -136
  297. package/lib/utils/external-system-local-test-tty.js +389 -0
  298. package/lib/utils/external-system-readiness-core.js +377 -0
  299. package/lib/utils/external-system-readiness-deploy-display.js +270 -0
  300. package/lib/utils/external-system-readiness-display-internals.js +150 -0
  301. package/lib/utils/external-system-readiness-display.js +186 -0
  302. package/lib/utils/external-system-test-helpers.js +24 -6
  303. package/lib/utils/external-system-validators.js +32 -14
  304. package/lib/utils/health-check-url.js +119 -0
  305. package/lib/utils/health-check.js +59 -25
  306. package/lib/utils/help-builder.js +14 -13
  307. package/lib/utils/image-version.js +4 -8
  308. package/lib/utils/infra-containers.js +4 -7
  309. package/lib/utils/infra-env-defaults.js +162 -0
  310. package/lib/utils/infra-status-display.js +167 -0
  311. package/lib/utils/infra-status.js +16 -8
  312. package/lib/utils/local-secrets.js +29 -7
  313. package/lib/utils/paths.js +136 -48
  314. package/lib/utils/port-resolver.js +10 -23
  315. package/lib/utils/redis-env-scope.js +62 -0
  316. package/lib/utils/register-aifabrix-shell-env.js +204 -0
  317. package/lib/utils/remote-builder-validation.js +99 -0
  318. package/lib/utils/remote-dev-auth.js +117 -21
  319. package/lib/utils/remote-docker-env.js +67 -15
  320. package/lib/utils/remote-secrets-loader.js +13 -4
  321. package/lib/utils/resolve-docker-image-ref.js +124 -0
  322. package/lib/utils/schema-loader.js +22 -9
  323. package/lib/utils/secrets-bash-kv.js +25 -0
  324. package/lib/utils/secrets-generator.js +171 -51
  325. package/lib/utils/secrets-helpers.js +70 -59
  326. package/lib/utils/secrets-kv-scope.js +60 -0
  327. package/lib/utils/secrets-utils.js +35 -37
  328. package/lib/utils/secrets-validation.js +3 -1
  329. package/lib/utils/secrets-yaml-preserve.js +109 -0
  330. package/lib/utils/secure-file-permissions.js +91 -0
  331. package/lib/utils/ssh-key-helper.js +4 -2
  332. package/lib/utils/template-helpers.js +2 -2
  333. package/lib/utils/test-log-writer.js +3 -3
  334. package/lib/utils/token-manager.js +37 -5
  335. package/lib/utils/url-declarative-public-base.js +188 -0
  336. package/lib/utils/url-declarative-resolve-build.js +493 -0
  337. package/lib/utils/url-declarative-resolve-load-doc.js +51 -0
  338. package/lib/utils/url-declarative-resolve.js +220 -0
  339. package/lib/utils/url-declarative-token-parse.js +74 -0
  340. package/lib/utils/url-declarative-url-flags.js +50 -0
  341. package/lib/utils/url-declarative-vdir-inactive-env.js +99 -0
  342. package/lib/utils/url-public-path-prefix.js +34 -0
  343. package/lib/utils/urls-local-registry.js +220 -0
  344. package/lib/utils/validation-report-tty-kit.js +77 -0
  345. package/lib/utils/validation-run-poll.js +89 -0
  346. package/lib/utils/validation-run-post-retry.js +73 -0
  347. package/lib/utils/validation-run-request.js +98 -0
  348. package/lib/utils/variable-transformer.js +21 -4
  349. package/lib/utils/yaml-preserve.js +78 -1
  350. package/lib/validation/datasource-warnings.js +56 -0
  351. package/lib/validation/env-template-auth.js +50 -2
  352. package/lib/validation/external-manifest-validator.js +35 -7
  353. package/lib/validation/validate-display.js +37 -31
  354. package/lib/validation/validate.js +9 -10
  355. package/lib/validation/validator-unresolved-placeholders.js +98 -0
  356. package/lib/validation/validator.js +32 -78
  357. package/lib/validation/wizard-config-validator.js +2 -1
  358. package/package.json +11 -3
  359. package/scripts/check-datasource-test-run-schema-sync.js +34 -0
  360. package/scripts/diagnose-cli.js +150 -0
  361. package/scripts/install-local.js +304 -55
  362. package/templates/README.md +15 -2
  363. package/templates/applications/dataplane/application.yaml +52 -2
  364. package/templates/applications/dataplane/env.template +80 -18
  365. package/templates/applications/dataplane/rbac.yaml +8 -0
  366. package/templates/applications/keycloak/application.yaml +9 -1
  367. package/templates/applications/keycloak/env.template +15 -6
  368. package/templates/applications/miso-controller/application.yaml +10 -2
  369. package/templates/applications/miso-controller/env.template +55 -14
  370. package/templates/applications/miso-controller/rbac.yaml +5 -0
  371. package/templates/external-system/README.md.hbs +20 -7
  372. package/templates/external-system/deploy.js.hbs +5 -5
  373. package/templates/external-system/env.template.hbs +22 -0
  374. package/templates/external-system/external-datasource.yaml.hbs +197 -118
  375. package/templates/infra/compose.yaml.hbs +20 -4
  376. package/templates/python/docker-compose.hbs +16 -0
  377. package/templates/typescript/docker-compose.hbs +16 -0
  378. package/integration/hubspot/README.md +0 -102
  379. package/integration/hubspot/env.template +0 -4
  380. package/integration/hubspot/hubspot-datasource-company.json +0 -541
  381. package/integration/hubspot/hubspot-datasource-contact.json +0 -639
  382. package/integration/hubspot/hubspot-datasource-deal.json +0 -588
  383. package/integration/hubspot/hubspot-datasource-users.json +0 -116
  384. package/integration/hubspot/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
  385. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
  386. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
  387. package/lib/api/external-test.api.js +0 -111
  388. package/lib/schema/env-config.yaml +0 -43
  389. /package/integration/{hubspot → hubspot-test}/companies.json +0 -0
  390. /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-app-name.yaml +0 -0
  391. /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-missing-app.yaml +0 -0
  392. /package/integration/{hubspot → hubspot-test}/test-dataplane-down-helpers.js +0 -0
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Shared chalk helpers for external system readiness CLI blocks.
3
+ *
4
+ * @fileoverview Internals for upload and deploy readiness display
5
+ * @author AI Fabrix Team
6
+ * @version 2.0.0
7
+ */
8
+
9
+ const chalk = require('chalk');
10
+ const { failureGlyph, successGlyph } = require('./cli-test-layout-chalk');
11
+ const logger = require('./logger');
12
+ const { extractIdentitySummary, resolveCredentialTestEndpointDisplay } = require('./external-system-readiness-core');
13
+
14
+ const SEP = chalk.gray('────────────────────────────────');
15
+
16
+ /**
17
+ * @param {string} title
18
+ */
19
+ function logSectionTitle(title) {
20
+ logger.log('');
21
+ logger.log(chalk.bold(title));
22
+ }
23
+
24
+ function logSeparator() {
25
+ logger.log('');
26
+ logger.log(SEP);
27
+ }
28
+
29
+ /**
30
+ * @param {'ready'|'partial'|'failed'} tier
31
+ * @returns {string}
32
+ */
33
+ function tierGlyph(tier) {
34
+ if (tier === 'ready') return successGlyph();
35
+ if (tier === 'failed') return failureGlyph();
36
+ return chalk.yellow('⚠');
37
+ }
38
+
39
+ /**
40
+ * @param {'READY'|'PARTIAL'|'FAILED'} v
41
+ * @returns {string}
42
+ */
43
+ function verdictLine(v) {
44
+ const label = chalk.bold('System Readiness: ');
45
+ if (v === 'READY') return label + chalk.green('READY');
46
+ if (v === 'FAILED') return label + chalk.red('FAILED');
47
+ return label + chalk.yellow('PARTIAL');
48
+ }
49
+
50
+ /**
51
+ * @param {Array<{ key: string, tier: string }>} rows
52
+ * @param {{ ready: number, partial: number, failed: number }} counts
53
+ */
54
+ function logDatasourceTable(rows, counts) {
55
+ logSectionTitle('Datasources:');
56
+ for (const r of rows) {
57
+ const statusLabel = r.tier === 'ready' ? 'Ready' : r.tier === 'failed' ? 'Failed' : 'Partial';
58
+ logger.log(
59
+ `${tierGlyph(r.tier)} ${r.key.padEnd(14, ' ')} ${chalk.gray('(' + statusLabel + ')')}`
60
+ );
61
+ }
62
+ logger.log('');
63
+ logger.log(
64
+ chalk.gray('Summary:') +
65
+ ` ${chalk.green('Ready: ' + counts.ready)} · ${chalk.yellow('Partial: ' + counts.partial)} · ${chalk.red('Failed: ' + counts.failed)}`
66
+ );
67
+ }
68
+
69
+ /**
70
+ * @param {Object} system - manifest.system
71
+ */
72
+ function logIdentityBlock(system) {
73
+ const { mode, attribution, tokenBroker } = extractIdentitySummary(system || {});
74
+ logSectionTitle('Identity:');
75
+ logger.log(`${chalk.gray('Mode:')} ${mode}`);
76
+ const attrColor = attribution === 'enabled' ? chalk.white : chalk.yellow;
77
+ logger.log(`${chalk.gray('Attribution:')} ${attrColor(attribution)}`);
78
+ logger.log(`${chalk.gray('Token broker:')} ${tokenBroker === 'configured' ? chalk.white(tokenBroker) : chalk.gray(tokenBroker)}`);
79
+ }
80
+
81
+ /**
82
+ * @param {Object} system - manifest.system
83
+ * @param {boolean} probed
84
+ * @param {boolean} [willProbe] - True when caller will run --probe immediately after this block
85
+ */
86
+ function logCredentialIntentBlock(system, probed, willProbe = false) {
87
+ const url = resolveCredentialTestEndpointDisplay(system || {});
88
+ logSectionTitle('Credential (intent):');
89
+ if (url) {
90
+ logger.log(chalk.gray('Test endpoint:'));
91
+ logger.log(chalk.cyan(`GET ${url}`));
92
+ } else {
93
+ logger.log(chalk.gray('Test endpoint: (not configured — defaults may apply on server)'));
94
+ }
95
+ if (!probed) {
96
+ if (willProbe) {
97
+ logger.log(chalk.gray('⏳ Connectivity will be tested next (--probe)'));
98
+ } else {
99
+ logger.log(chalk.yellow('⚠ Connectivity not tested (use --probe)'));
100
+ }
101
+ }
102
+ }
103
+
104
+ /**
105
+ * @param {string[]} actions
106
+ * @param {string} [extraLine]
107
+ */
108
+ function logNextActions(actions, extraLine) {
109
+ logSectionTitle('Next actions:');
110
+ for (const a of actions) {
111
+ if (a.startsWith('Run:')) {
112
+ logger.log(chalk.cyan('- ') + chalk.white(a));
113
+ } else {
114
+ logger.log(chalk.cyan('- ') + a);
115
+ }
116
+ }
117
+ if (extraLine) {
118
+ logger.log(chalk.cyan('- ') + chalk.white(extraLine));
119
+ }
120
+ }
121
+
122
+ /**
123
+ * @param {Object} sys - ExternalSystemResponse
124
+ */
125
+ function logDocsBlock(sys) {
126
+ if (!sys) return;
127
+ const urls = [];
128
+ if (sys.openApiDocsPageUrl) urls.push({ label: 'OpenAPI Docs Page', url: sys.openApiDocsPageUrl });
129
+ if (sys.apiDocumentUrl) urls.push({ label: 'API Docs', url: sys.apiDocumentUrl });
130
+ if (sys.mcpServerUrl) urls.push({ label: 'MCP Server', url: sys.mcpServerUrl });
131
+ if (urls.length === 0) return;
132
+ logSeparator();
133
+ logSectionTitle('Docs:');
134
+ for (const { label, url } of urls) {
135
+ logger.log(`${chalk.gray(label + ':')} ${chalk.cyan(url)}`);
136
+ }
137
+ }
138
+
139
+ module.exports = {
140
+ SEP,
141
+ logSeparator,
142
+ logSectionTitle,
143
+ tierGlyph,
144
+ verdictLine,
145
+ logDatasourceTable,
146
+ logIdentityBlock,
147
+ logCredentialIntentBlock,
148
+ logNextActions,
149
+ logDocsBlock
150
+ };
@@ -0,0 +1,186 @@
1
+ const { formatBlockingError, formatSuccessLine } = require('./cli-test-layout-chalk');
2
+ /**
3
+ * Chalk-based CLI output for external system upload readiness (+ upload-time probe).
4
+ * Deploy summary lives in external-system-readiness-deploy-display.js.
5
+ *
6
+ * @fileoverview Readiness display for upload and optional probe after upload
7
+ * @author AI Fabrix Team
8
+ * @version 2.0.0
9
+ */
10
+
11
+ const chalk = require('chalk');
12
+ const logger = require('./logger');
13
+ const {
14
+ summarizeDatasourceTiersA,
15
+ summarizeProbeResults,
16
+ buildNextActionsTierA,
17
+ coerceProbeRunToResultRows
18
+ } = require('./external-system-readiness-core');
19
+ const {
20
+ logSeparator,
21
+ logSectionTitle,
22
+ logDatasourceTable,
23
+ logIdentityBlock,
24
+ logCredentialIntentBlock,
25
+ logNextActions,
26
+ logDocsBlock,
27
+ verdictLine,
28
+ SEP
29
+ } = require('./external-system-readiness-display-internals');
30
+
31
+ /**
32
+ * @param {Object} publication - PublicationResult
33
+ */
34
+ function logPublishResultBlock(publication) {
35
+ const genMcp = publication.generateMcpContract === true;
36
+ logSectionTitle('Publish Result:');
37
+ logger.log(formatSuccessLine('System registered (controller + dataplane)'));
38
+ if (publication.uploadId) {
39
+ logger.log(`${chalk.gray('Upload ID:')} ${chalk.cyan(String(publication.uploadId))}`);
40
+ }
41
+ const sys = publication.system || {};
42
+ if (sys.status) {
43
+ logger.log(`${chalk.gray('System status:')} ${sys.status}`);
44
+ }
45
+ logSeparator();
46
+ logSectionTitle('MCP Contract:');
47
+ logger.log(genMcp ? formatSuccessLine('Generated') : chalk.gray('○ Not requested (generateMcpContract false)'));
48
+ }
49
+
50
+ /**
51
+ * @param {string} systemKey
52
+ * @param {{ ready: number, partial: number, failed: number }} summary
53
+ * @param {boolean} willProbe
54
+ */
55
+ function logUploadReadinessSummaryMinimal(systemKey, summary, willProbe) {
56
+ logger.log(chalk.green(`\nUpload complete: ${systemKey}`));
57
+ logger.log(
58
+ chalk.gray('Summary:') +
59
+ ` ${chalk.green('Ready: ' + summary.ready)} · ${chalk.yellow('Partial: ' + summary.partial)} · ${chalk.red('Failed: ' + summary.failed)}`
60
+ );
61
+ if (!willProbe) logger.log(chalk.gray('Use --probe for runtime verification'));
62
+ }
63
+
64
+ /**
65
+ * @param {string} systemKey
66
+ * @param {Object} publication
67
+ * @param {Object} manifest
68
+ * @param {{ rows: Array<{ key: string, tier: string }>, ready: number, partial: number, failed: number }} summary
69
+ * @param {boolean} genMcp
70
+ * @param {boolean} willProbe
71
+ */
72
+ function logUploadReadinessSummaryExpanded(systemKey, publication, manifest, summary, genMcp, willProbe) {
73
+ const systemCfg = manifest.system || {};
74
+ logSeparator();
75
+ logPublishResultBlock(publication);
76
+ logSeparator();
77
+ logDatasourceTable(summary.rows, summary);
78
+ logSeparator();
79
+ logIdentityBlock(systemCfg);
80
+ logSeparator();
81
+ logCredentialIntentBlock(systemCfg, false, !!willProbe);
82
+ logSeparator();
83
+ let hints = buildNextActionsTierA(systemKey, summary, genMcp);
84
+ if (willProbe) {
85
+ hints = hints.filter(h => !h.includes(`Run: aifabrix upload ${systemKey} --probe`));
86
+ }
87
+ logNextActions(hints);
88
+ }
89
+
90
+ /**
91
+ * @param {Object} ctx
92
+ * @param {string} ctx.systemKey
93
+ * @param {Object} ctx.publication - PublicationResult
94
+ * @param {Object} ctx.manifest
95
+ * @param {boolean} [ctx.minimal] - One-line summary + probe hint only
96
+ * @param {boolean} [ctx.willProbe] - True when caller will run --probe immediately after this summary
97
+ */
98
+ function logUploadReadinessSummary(ctx) {
99
+ const { systemKey, publication, manifest, minimal, willProbe } = ctx;
100
+ const genMcp = publication.generateMcpContract === true;
101
+ const summary = summarizeDatasourceTiersA(publication.datasources || [], genMcp);
102
+ if (minimal) {
103
+ logUploadReadinessSummaryMinimal(systemKey, summary, willProbe);
104
+ return;
105
+ }
106
+ logUploadReadinessSummaryExpanded(systemKey, publication, manifest, summary, genMcp, willProbe);
107
+ }
108
+
109
+ /**
110
+ * @param {Object} validationBody - Unwrapped pipeline/validate response
111
+ */
112
+ function logServerValidationWarnings(validationBody) {
113
+ const warnings = validationBody?.warnings;
114
+ if (!Array.isArray(warnings) || warnings.length === 0) return;
115
+ logSeparator();
116
+ logSectionTitle('Server validation:');
117
+ for (const w of warnings) {
118
+ logger.log(chalk.yellow(`⚠ Warning: ${typeof w === 'string' ? w : JSON.stringify(w)}`));
119
+ }
120
+ }
121
+
122
+ /**
123
+ * @param {Object} results - Probe result rows
124
+ * @param {Object} summary - summarizeProbeResults output
125
+ */
126
+ function logProbeCredentialLine(results, summary) {
127
+ logSeparator();
128
+ logSectionTitle('Credential Test:');
129
+ const anyEndpointFail = results.some(row => row?.endpointTestResults?.success === false);
130
+ if (anyEndpointFail) {
131
+ logger.log(formatBlockingError('Failed (see Key Issues / endpoint test)'));
132
+ } else if (summary.failed > 0) {
133
+ logger.log(formatBlockingError('Some datasource checks failed'));
134
+ } else if (summary.partial > 0) {
135
+ logger.log(chalk.yellow('⚠ Completed with warnings'));
136
+ } else {
137
+ logger.log(formatSuccessLine('Passed'));
138
+ }
139
+ }
140
+
141
+ /**
142
+ * @param {Object} probeRaw - Unwrapped POST /validation/run body
143
+ * @param {string} systemKey
144
+ */
145
+ function logProbeRuntimeBlock(probeRaw, systemKey) {
146
+ const results = coerceProbeRunToResultRows(probeRaw);
147
+ const summary = summarizeProbeResults(results);
148
+ logSeparator();
149
+ logger.log(chalk.bold('Runtime Readiness:'));
150
+ if (results.length === 0) {
151
+ logger.log(chalk.yellow('⚠ No runtime datasource results were returned by the dataplane probe.'));
152
+ logger.log(chalk.gray(' Endpoint credential test may still pass even if datasource checks were not reported.'));
153
+ }
154
+ logDatasourceTable(summary.rows, summary);
155
+ if (summary.issues.length > 0) {
156
+ logSeparator();
157
+ logSectionTitle('Key Issues:');
158
+ for (const { key, lines } of summary.issues) {
159
+ logger.log(chalk.white(key));
160
+ for (const line of lines) {
161
+ logger.log(chalk.red(`- ${line}`));
162
+ }
163
+ }
164
+ }
165
+ logProbeCredentialLine(results, summary);
166
+ logSeparator();
167
+ logNextActions(
168
+ ['Fix API credentials or permissions if endpoint tests failed'],
169
+ `Run: aifabrix datasource test-e2e <datasourceKey> --app ${systemKey}`
170
+ );
171
+ }
172
+
173
+ module.exports = {
174
+ logUploadReadinessSummary,
175
+ logServerValidationWarnings,
176
+ logProbeRuntimeBlock,
177
+ SEP,
178
+ logSeparator,
179
+ logSectionTitle,
180
+ logDatasourceTable,
181
+ logIdentityBlock,
182
+ logCredentialIntentBlock,
183
+ logNextActions,
184
+ logDocsBlock,
185
+ verdictLine
186
+ };
@@ -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,31 +119,45 @@ 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
- results.errors.push(`Invalid dimension key '${dimensionKey}': must match pattern ^[a-zA-Z0-9_]+$`);
156
+ results.errors.push(`Invalid dimension key '${dimensionKey}': dimension key must contain only letters, numbers, and underscores`);
143
157
  results.valid = false;
144
158
  }
145
159
  if (typeof attributePath !== 'string' || !/^[a-zA-Z0-9_.]+$/.test(attributePath)) {
146
- results.errors.push(`Invalid attribute path '${attributePath}' for dimension '${dimensionKey}': must match pattern ^[a-zA-Z0-9_.]+$`);
160
+ results.errors.push(`Invalid attribute path '${attributePath}' for dimension '${dimensionKey}': attribute path must contain only letters, numbers, underscores, and dots`);
147
161
  results.valid = false;
148
162
  }
149
163
  }
@@ -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
+