@aifabrix/builder 2.44.5 → 2.45.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 (249) hide show
  1. package/.cursor/rules/cli-layout.mdc +8 -4
  2. package/.cursor/rules/project-rules.mdc +1 -1
  3. package/README.md +15 -23
  4. package/integration/hubspot-test/README.md +2 -0
  5. package/integration/hubspot-test/test.js +5 -3
  6. package/jest.projects.js +104 -2
  7. package/lib/api/controller-health.api.js +49 -0
  8. package/lib/api/dimension-values.api.js +82 -0
  9. package/lib/api/dimensions.api.js +114 -0
  10. package/lib/api/external-systems.api.js +1 -0
  11. package/lib/api/integration-clients.api.js +168 -0
  12. package/lib/api/types/dimension-values.types.js +28 -0
  13. package/lib/api/types/dimensions.types.js +31 -0
  14. package/lib/api/types/integration-clients.types.js +45 -0
  15. package/lib/api/validation-runner.js +46 -25
  16. package/lib/app/deploy-config.js +11 -1
  17. package/lib/app/deploy-status-display.js +3 -3
  18. package/lib/app/deploy.js +36 -14
  19. package/lib/app/display.js +15 -11
  20. package/lib/app/helpers.js +3 -3
  21. package/lib/app/index.js +3 -3
  22. package/lib/app/push.js +46 -23
  23. package/lib/app/register.js +7 -6
  24. package/lib/app/restart-display.js +126 -0
  25. package/lib/app/rotate-secret.js +7 -6
  26. package/lib/app/run-container-start.js +12 -6
  27. package/lib/app/run-env-compose.js +30 -1
  28. package/lib/app/run-helpers.js +58 -19
  29. package/lib/app/run-reload-sync.js +148 -0
  30. package/lib/app/run-resolve-image.js +51 -1
  31. package/lib/app/run.js +148 -74
  32. package/lib/app/show-display.js +7 -0
  33. package/lib/app/show.js +87 -5
  34. package/lib/build/index.js +83 -49
  35. package/lib/cli/doctor-check.js +117 -0
  36. package/lib/cli/index.js +8 -2
  37. package/lib/cli/infra-guided.js +460 -0
  38. package/lib/cli/installation-log-command.js +73 -0
  39. package/lib/cli/setup-app.js +31 -3
  40. package/lib/cli/setup-auth.js +98 -27
  41. package/lib/cli/setup-dev-path-commands.js +50 -3
  42. package/lib/cli/setup-infra-up-dataplane-action.js +111 -0
  43. package/lib/cli/setup-infra-up-platform-action.js +131 -0
  44. package/lib/cli/setup-infra.js +132 -118
  45. package/lib/cli/setup-integration-client.js +182 -0
  46. package/lib/cli/setup-parameters.js +21 -2
  47. package/lib/cli/setup-platform.js +102 -0
  48. package/lib/cli/setup-secrets.js +18 -6
  49. package/lib/cli/setup-utility-resolve.js +132 -0
  50. package/lib/cli/setup-utility.js +143 -84
  51. package/lib/commands/app-logs.js +81 -33
  52. package/lib/commands/auth-config.js +116 -18
  53. package/lib/commands/datasource-capability-dimension-cli.js +128 -0
  54. package/lib/commands/datasource-capability-output.js +29 -0
  55. package/lib/commands/datasource-capability-relate-cli.js +140 -0
  56. package/lib/commands/datasource-capability.js +411 -0
  57. package/lib/commands/datasource-unified-test-cli.options.js +1 -1
  58. package/lib/commands/datasource.js +53 -13
  59. package/lib/commands/dev-down.js +3 -3
  60. package/lib/commands/dev-infra-gate.js +32 -0
  61. package/lib/commands/dev-init.js +13 -7
  62. package/lib/commands/dimension-value.js +179 -0
  63. package/lib/commands/dimension.js +330 -0
  64. package/lib/commands/integration-client.js +430 -0
  65. package/lib/commands/login-device.js +65 -30
  66. package/lib/commands/login.js +21 -10
  67. package/lib/commands/parameters-validate.js +78 -13
  68. package/lib/commands/repair-datasource-auto-rbac.js +166 -0
  69. package/lib/commands/repair-datasource-keys.js +10 -5
  70. package/lib/commands/repair-datasource.js +19 -7
  71. package/lib/commands/repair-env-template.js +4 -1
  72. package/lib/commands/repair-openapi-sync.js +172 -0
  73. package/lib/commands/repair-persist.js +102 -0
  74. package/lib/commands/repair-rbac-extract.js +27 -0
  75. package/lib/commands/repair-rbac-migrate.js +186 -0
  76. package/lib/commands/repair-rbac.js +214 -31
  77. package/lib/commands/repair-system-alignment.js +246 -0
  78. package/lib/commands/repair-system-permissions.js +168 -0
  79. package/lib/commands/repair.js +120 -338
  80. package/lib/commands/secure.js +1 -1
  81. package/lib/commands/setup-modes.js +468 -0
  82. package/lib/commands/setup-prompts.js +421 -0
  83. package/lib/commands/setup.js +254 -0
  84. package/lib/commands/teardown.js +277 -0
  85. package/lib/commands/up-common.js +113 -19
  86. package/lib/commands/up-dataplane.js +44 -19
  87. package/lib/commands/up-miso.js +18 -18
  88. package/lib/commands/upload.js +111 -23
  89. package/lib/commands/wizard-core-helpers.js +14 -11
  90. package/lib/commands/wizard-core.js +6 -5
  91. package/lib/commands/wizard-dataplane.js +2 -2
  92. package/lib/commands/wizard-entity-selection.js +4 -3
  93. package/lib/commands/wizard-headless.js +2 -1
  94. package/lib/commands/wizard.js +2 -1
  95. package/lib/constants/infra-compose-service-names.js +40 -0
  96. package/lib/core/audit-logger.js +1 -34
  97. package/lib/core/config-admin-email.js +56 -0
  98. package/lib/core/config-normalize.js +60 -0
  99. package/lib/core/config-registered-controller-urls.js +54 -0
  100. package/lib/core/config.js +33 -50
  101. package/lib/core/env-reader.js +16 -3
  102. package/lib/core/secrets-admin-env.js +101 -0
  103. package/lib/core/secrets-ensure-infra.js +34 -1
  104. package/lib/core/secrets-ensure.js +88 -66
  105. package/lib/core/secrets-env-content.js +428 -0
  106. package/lib/core/secrets-env-declarative-expand.js +170 -0
  107. package/lib/core/secrets-env-write.js +29 -1
  108. package/lib/core/secrets-load.js +252 -0
  109. package/lib/core/secrets-names.js +32 -0
  110. package/lib/core/secrets.js +17 -757
  111. package/lib/datasource/capability/basic-exposure.js +76 -0
  112. package/lib/datasource/capability/capability-diff-slice.js +41 -0
  113. package/lib/datasource/capability/capability-key.js +34 -0
  114. package/lib/datasource/capability/capability-resolve.js +172 -0
  115. package/lib/datasource/capability/capability-storage-keys.js +22 -0
  116. package/lib/datasource/capability/copy-operations.js +348 -0
  117. package/lib/datasource/capability/copy-test-payload.js +139 -0
  118. package/lib/datasource/capability/create-operations.js +235 -0
  119. package/lib/datasource/capability/dimension-operations.js +151 -0
  120. package/lib/datasource/capability/dimension-validate.js +219 -0
  121. package/lib/datasource/capability/json-pointer.js +31 -0
  122. package/lib/datasource/capability/reference-rewrite.js +51 -0
  123. package/lib/datasource/capability/relate-operations.js +325 -0
  124. package/lib/datasource/capability/relate-validate.js +219 -0
  125. package/lib/datasource/capability/remove-operations.js +275 -0
  126. package/lib/datasource/capability/run-capability-copy.js +152 -0
  127. package/lib/datasource/capability/run-capability-diff.js +135 -0
  128. package/lib/datasource/capability/run-capability-dimension.js +291 -0
  129. package/lib/datasource/capability/run-capability-edit.js +377 -0
  130. package/lib/datasource/capability/run-capability-relate.js +193 -0
  131. package/lib/datasource/capability/run-capability-remove.js +105 -0
  132. package/lib/datasource/capability/templates/minimal-fetch.json +18 -0
  133. package/lib/datasource/capability/validate-capability-slice.js +35 -0
  134. package/lib/datasource/list.js +136 -23
  135. package/lib/datasource/log-viewer.js +2 -4
  136. package/lib/datasource/unified-validation-run.js +51 -16
  137. package/lib/datasource/validate.js +53 -1
  138. package/lib/deployment/deploy-poll-ui.js +60 -0
  139. package/lib/deployment/deployer-status.js +29 -3
  140. package/lib/deployment/deployer.js +48 -30
  141. package/lib/deployment/environment.js +7 -2
  142. package/lib/deployment/poll-interval.js +72 -0
  143. package/lib/deployment/push.js +11 -9
  144. package/lib/external-system/deploy.js +9 -2
  145. package/lib/external-system/download.js +61 -32
  146. package/lib/external-system/sync-deploy-manifest.js +33 -0
  147. package/lib/infrastructure/index.js +49 -19
  148. package/lib/infrastructure/orphan-infra-docker-teardown.js +177 -0
  149. package/lib/internal/node-fs.js +2 -0
  150. package/lib/parameters/infra-kv-discovery.js +29 -4
  151. package/lib/parameters/infra-parameter-catalog.js +6 -3
  152. package/lib/parameters/infra-parameter-validate.js +67 -19
  153. package/lib/resolvers/datasource-resolver.js +53 -0
  154. package/lib/resolvers/dimension-file.js +52 -0
  155. package/lib/resolvers/manifest-resolver.js +133 -0
  156. package/lib/schema/application-schema.json +4 -0
  157. package/lib/schema/external-datasource.schema.json +183 -53
  158. package/lib/schema/external-system.schema.json +23 -10
  159. package/lib/schema/infra.parameter.yaml +26 -1
  160. package/lib/schema/wizard-config.schema.json +1 -1
  161. package/lib/utils/aifabrix-config-dir-walk.js +40 -0
  162. package/lib/utils/aifabrix-runtime-config-dir.js +26 -3
  163. package/lib/utils/app-config-resolver.js +24 -1
  164. package/lib/utils/app-run-containers.js +2 -2
  165. package/lib/utils/applications-config-defaults.js +206 -0
  166. package/lib/utils/auth-config-validator.js +2 -12
  167. package/lib/utils/bash-secret-env.js +59 -0
  168. package/lib/utils/cli-secrets-error-format.js +78 -0
  169. package/lib/utils/cli-test-layout-chalk.js +31 -9
  170. package/lib/utils/cli-utils.js +4 -36
  171. package/lib/utils/compose-generate-docker-compose.js +111 -6
  172. package/lib/utils/compose-generator.js +17 -8
  173. package/lib/utils/controller-url.js +50 -7
  174. package/lib/utils/datasource-test-run-display.js +8 -0
  175. package/lib/utils/dev-hosts-helper.js +3 -2
  176. package/lib/utils/dev-init-ssh-merge.js +2 -1
  177. package/lib/utils/docker-build.js +17 -9
  178. package/lib/utils/docker-reload-mount.js +127 -0
  179. package/lib/utils/env-copy.js +99 -14
  180. package/lib/utils/env-template.js +5 -1
  181. package/lib/utils/external-readme.js +71 -2
  182. package/lib/utils/external-system-local-test-tty.js +3 -2
  183. package/lib/utils/external-system-readiness-core.js +45 -12
  184. package/lib/utils/external-system-readiness-deploy-display.js +3 -3
  185. package/lib/utils/external-system-readiness-display-internals.js +33 -3
  186. package/lib/utils/external-system-readiness-display.js +10 -1
  187. package/lib/utils/file-upload.js +40 -3
  188. package/lib/utils/health-check-db-init.js +107 -0
  189. package/lib/utils/health-check-public-warn.js +69 -0
  190. package/lib/utils/health-check-url.js +28 -10
  191. package/lib/utils/health-check.js +139 -107
  192. package/lib/utils/help-builder.js +5 -1
  193. package/lib/utils/image-name.js +34 -7
  194. package/lib/utils/infra-optional-service-flags.js +69 -0
  195. package/lib/utils/installation-log-core.js +282 -0
  196. package/lib/utils/installation-log-record.js +237 -0
  197. package/lib/utils/installation-log.js +123 -0
  198. package/lib/utils/integration-file-backup.js +74 -0
  199. package/lib/utils/log-redaction.js +105 -0
  200. package/lib/utils/manifest-location.js +164 -0
  201. package/lib/utils/manifest-source-emit.js +162 -0
  202. package/lib/utils/mutagen-install.js +30 -3
  203. package/lib/utils/paths.js +308 -76
  204. package/lib/utils/postgres-wipe.js +212 -0
  205. package/lib/utils/register-aifabrix-shell-env.js +15 -0
  206. package/lib/utils/remote-dev-auth.js +21 -5
  207. package/lib/utils/remote-docker-env.js +9 -1
  208. package/lib/utils/remote-secrets-loader.js +49 -4
  209. package/lib/utils/resolve-docker-image-ref.js +9 -3
  210. package/lib/utils/run-cli-flags.js +29 -0
  211. package/lib/utils/secrets-ancestor-paths.js +47 -0
  212. package/lib/utils/secrets-canonical.js +10 -3
  213. package/lib/utils/secrets-helpers.js +17 -10
  214. package/lib/utils/secrets-kv-refs.js +42 -0
  215. package/lib/utils/secrets-kv-scope.js +19 -2
  216. package/lib/utils/secrets-materialize-local.js +134 -0
  217. package/lib/utils/secrets-path.js +26 -13
  218. package/lib/utils/secrets-utils.js +20 -10
  219. package/lib/utils/system-builder-root.js +42 -0
  220. package/lib/utils/url-declarative-public-base.js +80 -12
  221. package/lib/utils/url-declarative-resolve-build-urls.js +238 -0
  222. package/lib/utils/url-declarative-resolve-build.js +24 -388
  223. package/lib/utils/url-declarative-resolve-expand-token.js +189 -0
  224. package/lib/utils/url-declarative-resolve-load-doc.js +12 -3
  225. package/lib/utils/url-declarative-resolve-surface-state.js +102 -0
  226. package/lib/utils/url-declarative-resolve.js +47 -7
  227. package/lib/utils/url-declarative-runtime-base-path.js +52 -0
  228. package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
  229. package/lib/utils/urls-local-registry-scan.js +103 -0
  230. package/lib/utils/urls-local-registry.js +158 -76
  231. package/lib/utils/validation-poll-ui.js +81 -0
  232. package/lib/utils/validation-run-poll.js +29 -5
  233. package/lib/utils/with-muted-logger.js +53 -0
  234. package/package.json +3 -1
  235. package/templates/applications/dataplane/application.yaml +5 -1
  236. package/templates/applications/dataplane/rbac.yaml +10 -10
  237. package/templates/applications/keycloak/env.template +8 -6
  238. package/templates/applications/miso-controller/application.yaml +9 -0
  239. package/templates/applications/miso-controller/env.template +27 -29
  240. package/templates/applications/miso-controller/rbac.yaml +9 -9
  241. package/templates/external-system/README.md.hbs +83 -123
  242. package/.npmrc.token +0 -1
  243. package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
  244. package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
  245. package/.nyc_output/processinfo/index.json +0 -1
  246. package/lib/api/service-users.api.js +0 -150
  247. package/lib/api/types/service-users.types.js +0 -65
  248. package/lib/cli/setup-service-user.js +0 -187
  249. package/lib/commands/service-user.js +0 -429
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Surface expansion + {@link expandResolvedUrlToken} for declarative url:// tokens.
3
+ *
4
+ * @fileoverview Split from url-declarative-resolve-build.js for ESLint max-lines
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const {
12
+ buildPublicUrlString,
13
+ buildPublicHostOriginString,
14
+ buildInternalHostOriginString,
15
+ buildInternalUrlString
16
+ } = require('./url-declarative-resolve-build-urls');
17
+ const { normalizeRuntimeBasePath } = require('./url-declarative-runtime-base-path');
18
+
19
+ /**
20
+ * Published/browser origin port basis for url:// public surfaces (manifest `port`), not container listen.
21
+ * @param {Object} r
22
+ * @returns {number}
23
+ */
24
+ function publishedOriginPortBasis(r) {
25
+ const b = r.publicPortBasis;
26
+ if (b !== undefined && b !== null && Number.isFinite(Number(b))) {
27
+ return Number(b);
28
+ }
29
+ return r.listenPort;
30
+ }
31
+
32
+ /**
33
+ * @param {Object} r - resolved app + patternPath + pathPrefix + remoteServer + profile + devNum + derivedEnvKey
34
+ * @returns {string}
35
+ */
36
+ function expandHostSurfacePublic(r) {
37
+ return buildPublicHostOriginString({
38
+ profile: r.profile,
39
+ listenPort: publishedOriginPortBasis(r),
40
+ developerIdNum: r.devNum,
41
+ remoteServer: r.remoteServer,
42
+ traefik: r.traefik,
43
+ hostTemplate: r.hostTemplate,
44
+ tls: r.tls,
45
+ developerIdRaw: r.developerIdRaw,
46
+ infraTlsEnabled: r.infraTlsEnabled,
47
+ frontDoorIngressActive: r.frontDoorIngressActive,
48
+ declarativeTargetAppKey: r.appKey,
49
+ declarativeCurrentAppKey: r.currentAppKey,
50
+ declarativePublicUrlsUseLocalhost: r.declarativePublicUrlsUseLocalhost
51
+ });
52
+ }
53
+
54
+ /**
55
+ * Local workstation `.env` (no remote-server): browser-reachable URLs only — internal tokens match public.
56
+ * **Docker** profile must keep Docker DNS / service hostnames so resolved `.env` works **inside** containers.
57
+ *
58
+ * @param {Object} r
59
+ * @returns {boolean}
60
+ */
61
+ function isLocalProfileWithoutRemoteServer(r) {
62
+ return r.profile === 'local' && !String(r.remoteServer || '').trim();
63
+ }
64
+
65
+ /**
66
+ * @param {Object} r
67
+ * @returns {string}
68
+ */
69
+ function expandHostSurfaceInternal(r) {
70
+ if (isLocalProfileWithoutRemoteServer(r)) {
71
+ return expandHostSurfacePublic(r);
72
+ }
73
+ return buildInternalHostOriginString({
74
+ profile: r.profile,
75
+ listenPort: r.listenPort,
76
+ targetAppKey: r.appKey,
77
+ remoteServer: r.remoteServer,
78
+ pathPrefix: r.pathPrefix,
79
+ patternPath: r.patternPath,
80
+ developerIdNum: r.devNum,
81
+ derivedEnvKey: r.derivedEnvKey,
82
+ traefik: r.traefik,
83
+ hostTemplate: r.hostTemplate,
84
+ tls: r.tls,
85
+ developerIdRaw: r.developerIdRaw,
86
+ infraTlsEnabled: r.infraTlsEnabled,
87
+ frontDoorIngressActive: r.frontDoorIngressActive,
88
+ declarativeTargetAppKey: r.appKey,
89
+ declarativeCurrentAppKey: r.currentAppKey,
90
+ declarativePublicUrlsUseLocalhost: r.declarativePublicUrlsUseLocalhost
91
+ });
92
+ }
93
+
94
+ /**
95
+ * @param {Object} r
96
+ * @returns {string}
97
+ */
98
+ function expandFullSurfacePublic(r) {
99
+ return buildPublicUrlString({
100
+ profile: r.profile,
101
+ listenPort: publishedOriginPortBasis(r),
102
+ developerIdNum: r.devNum,
103
+ remoteServer: r.remoteServer,
104
+ pathPrefix: r.pathPrefix,
105
+ patternPath: r.patternPath,
106
+ traefik: r.traefik,
107
+ hostTemplate: r.hostTemplate,
108
+ tls: r.tls,
109
+ developerIdRaw: r.developerIdRaw,
110
+ infraTlsEnabled: r.infraTlsEnabled,
111
+ frontDoorIngressActive: r.frontDoorIngressActive,
112
+ declarativeTargetAppKey: r.appKey,
113
+ declarativeCurrentAppKey: r.currentAppKey,
114
+ declarativePublicUrlsUseLocalhost: r.declarativePublicUrlsUseLocalhost
115
+ });
116
+ }
117
+
118
+ /**
119
+ * @param {Object} r
120
+ * @returns {string}
121
+ */
122
+ function expandFullSurfaceInternal(r) {
123
+ if (isLocalProfileWithoutRemoteServer(r)) {
124
+ return expandFullSurfacePublic(r);
125
+ }
126
+ const useOriginOnly =
127
+ r.profile === 'docker' && Boolean(r.internalDockerUseOriginOnly);
128
+ const runtimePath = useOriginOnly
129
+ ? ''
130
+ : normalizeRuntimeBasePath(r.patternPath, r);
131
+ return buildInternalUrlString({
132
+ profile: r.profile,
133
+ listenPort: r.listenPort,
134
+ targetAppKey: r.appKey,
135
+ runtimeBasePath: runtimePath,
136
+ remoteServer: r.remoteServer,
137
+ pathPrefix: r.pathPrefix,
138
+ patternPath: r.patternPath,
139
+ developerIdNum: r.devNum,
140
+ derivedEnvKey: r.derivedEnvKey,
141
+ traefik: r.traefik,
142
+ hostTemplate: r.hostTemplate,
143
+ tls: r.tls,
144
+ developerIdRaw: r.developerIdRaw,
145
+ infraTlsEnabled: r.infraTlsEnabled,
146
+ frontDoorIngressActive: r.frontDoorIngressActive,
147
+ declarativeTargetAppKey: r.appKey,
148
+ declarativeCurrentAppKey: r.currentAppKey,
149
+ declarativePublicUrlsUseLocalhost: r.declarativePublicUrlsUseLocalhost
150
+ });
151
+ }
152
+
153
+ /**
154
+ * @param {{ kind: string, surface: string }} parsed
155
+ * @param {Object} r
156
+ * @returns {string}
157
+ */
158
+ function expandResolvedUrlToken(parsed, r) {
159
+ if (parsed.surface === 'vdir') {
160
+ if (!r.frontDoorIngressActive) {
161
+ return '';
162
+ }
163
+ // Plan 124: docker PRIVATEVDIR is always empty; public vdir still carries pattern
164
+ if (parsed.kind === 'internal' && r.profile === 'docker') {
165
+ return '';
166
+ }
167
+ const prefix = String(r.pathPrefix || '');
168
+ const pat = r.patternPath || '/';
169
+ if (!pat || pat === '/') {
170
+ return prefix || '';
171
+ }
172
+ const joined = `${prefix}${pat}`.replace(/\/{2,}/g, '/');
173
+ return joined.startsWith('/') ? joined : `/${joined}`;
174
+ }
175
+ if (parsed.surface === 'host') {
176
+ return parsed.kind === 'public' ? expandHostSurfacePublic(r) : expandHostSurfaceInternal(r);
177
+ }
178
+ return parsed.kind === 'public' ? expandFullSurfacePublic(r) : expandFullSurfaceInternal(r);
179
+ }
180
+
181
+ module.exports = {
182
+ publishedOriginPortBasis,
183
+ isLocalProfileWithoutRemoteServer,
184
+ expandHostSurfacePublic,
185
+ expandHostSurfaceInternal,
186
+ expandFullSurfacePublic,
187
+ expandFullSurfaceInternal,
188
+ expandResolvedUrlToken
189
+ };
@@ -29,11 +29,16 @@ function tryReadApplicationYamlAt(cfgPath) {
29
29
  * Prefer projectRoot/builder (fixtures, tests); then {@link pathsUtil.getBuilderPath} (global npm).
30
30
  * @param {string} appKey
31
31
  * @param {object} pathsUtil
32
+ * @param {string|null|undefined} [projectRootOverride] - When set (e.g. `ctx.projectRoot`), used instead of {@link pathsUtil.getProjectRoot} for the first candidate path (avoids flaky cross-test `paths` mocks).
32
33
  * @returns {string[]}
33
34
  */
34
- function collectApplicationYamlPathsForUrlResolve(appKey, pathsUtil) {
35
+ function collectApplicationYamlPathsForUrlResolve(appKey, pathsUtil, projectRootOverride) {
35
36
  const list = [];
36
- const root = pathsUtil.getProjectRoot();
37
+ const trimmed =
38
+ projectRootOverride !== undefined && projectRootOverride !== null
39
+ ? String(projectRootOverride).trim()
40
+ : '';
41
+ const root = trimmed || pathsUtil.getProjectRoot();
37
42
  if (root) {
38
43
  list.push(path.resolve(path.join(root, 'builder', appKey, 'application.yaml')));
39
44
  }
@@ -64,7 +69,11 @@ function loadApplicationYamlDocForUrlResolve(appKey, ctx, pathsUtil) {
64
69
  return fromVars;
65
70
  }
66
71
  }
67
- for (const cfgPath of collectApplicationYamlPathsForUrlResolve(appKey, pathsUtil)) {
72
+ const pr =
73
+ ctx && ctx.projectRoot !== undefined && ctx.projectRoot !== null
74
+ ? String(ctx.projectRoot).trim()
75
+ : '';
76
+ for (const cfgPath of collectApplicationYamlPathsForUrlResolve(appKey, pathsUtil, pr || undefined)) {
68
77
  const doc = tryReadApplicationYamlAt(cfgPath);
69
78
  if (doc) {
70
79
  return doc;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Declarative url:// **surface state machine** (plan 124 + per-app `applications.<app>.proxy`).
3
+ *
4
+ * FSM inputs (per token target): `perTokenTraefik`, `frontDoorRoutingEnabled` (strict true in yaml).
5
+ * Output phase is the product space (see `PHASE_BY_TRAEFIK_AND_FRONT_DOOR`); `pathActive` matches
6
+ * {@link computePathActive}(perTokenTraefik, frontDoorRoutingEnabled).
7
+ *
8
+ * @fileoverview Single source for `perTokenTraefik` × `frontDoorRouting.enabled` → phase + `pathActive`
9
+ * @author AI Fabrix Team
10
+ * @version 1.0.0
11
+ */
12
+
13
+ 'use strict';
14
+
15
+ const { isDeclarativeTraefikUrlsEnabled } = require('./applications-config-defaults');
16
+ const { computePathActive } = require('./url-declarative-url-flags');
17
+
18
+ /**
19
+ * Public URL surface phase for one url:// token target app.
20
+ * @readonly
21
+ * @enum {string}
22
+ */
23
+ const DeclarativeUrlSurfacePhase = {
24
+ /** No Traefik-style hints for this token (`perTokenTraefik` false). */
25
+ DIRECT: 'DIRECT',
26
+ /** Traefik on for Plan 117 path prefix, but `frontDoorRouting.enabled` is not true — no ingress path. */
27
+ TRAEFIK_SCOPED: 'TRAEFIK_SCOPED',
28
+ /** Traefik + front door enabled — `pathActive` / pattern + vdir-public behave as ingress. */
29
+ FRONT_DOOR: 'FRONT_DOOR'
30
+ };
31
+
32
+ /**
33
+ * @typedef {Object} DeclarativeUrlSurfaceInputs
34
+ * @property {Object|null|undefined} userCfg - `~/.aifabrix/config.yaml` root when resolve passes it
35
+ * @property {string} appKey - Target app for the url:// token
36
+ * @property {boolean|undefined} ctxTraefik - Legacy global flag when `userCfg` is absent
37
+ * @property {boolean} frontDoorRoutingEnabled - From `application.yaml` `frontDoorRouting.enabled === true`
38
+ */
39
+
40
+ /**
41
+ * Bit-pair → phase lookup (explicit FSM output table).
42
+ * Keys: `${perTokenTraefik}:${frontDoorRoutingEnabled}` (booleans coerced to strings).
43
+ *
44
+ * @type {Object.<string, keyof typeof DeclarativeUrlSurfacePhase>}
45
+ */
46
+ const PHASE_BY_TRAEFIK_AND_FRONT_DOOR = {
47
+ 'false:false': DeclarativeUrlSurfacePhase.DIRECT,
48
+ 'false:true': DeclarativeUrlSurfacePhase.DIRECT,
49
+ 'true:false': DeclarativeUrlSurfacePhase.TRAEFIK_SCOPED,
50
+ 'true:true': DeclarativeUrlSurfacePhase.FRONT_DOOR
51
+ };
52
+
53
+ /**
54
+ * @param {boolean} perTokenTraefik
55
+ * @param {boolean} frontDoorRoutingEnabled
56
+ * @returns {string} {@link DeclarativeUrlSurfacePhase}
57
+ */
58
+ function phaseFromTraefikAndFrontDoor(perTokenTraefik, frontDoorRoutingEnabled) {
59
+ const k = `${Boolean(perTokenTraefik)}:${Boolean(frontDoorRoutingEnabled)}`;
60
+ const phase = PHASE_BY_TRAEFIK_AND_FRONT_DOOR[k];
61
+ return phase || DeclarativeUrlSurfacePhase.DIRECT;
62
+ }
63
+
64
+ /**
65
+ * Whether Traefik-style URL hints apply for this token (infra + per-app proxy, or legacy `ctx.traefik`).
66
+ *
67
+ * @param {DeclarativeUrlSurfaceInputs} input
68
+ * @returns {boolean}
69
+ */
70
+ function resolvePerTokenTraefik(input) {
71
+ const { userCfg, appKey, ctxTraefik } = input;
72
+ if (userCfg && appKey) {
73
+ return isDeclarativeTraefikUrlsEnabled(userCfg, appKey);
74
+ }
75
+ return Boolean(ctxTraefik);
76
+ }
77
+
78
+ /**
79
+ * Resolve surface phase and derived flags for one declarative url:// target.
80
+ *
81
+ * @param {DeclarativeUrlSurfaceInputs} input
82
+ * @returns {{
83
+ * phase: string,
84
+ * perTokenTraefik: boolean,
85
+ * frontDoorIngressActive: boolean
86
+ * }}
87
+ */
88
+ function resolveDeclarativeUrlSurfaceState(input) {
89
+ const frontDoorRoutingEnabled = Boolean(input.frontDoorRoutingEnabled);
90
+ const perTokenTraefik = resolvePerTokenTraefik(input);
91
+ const phase = phaseFromTraefikAndFrontDoor(perTokenTraefik, frontDoorRoutingEnabled);
92
+ const frontDoorIngressActive = computePathActive(perTokenTraefik, frontDoorRoutingEnabled);
93
+ return { phase, perTokenTraefik, frontDoorIngressActive };
94
+ }
95
+
96
+ module.exports = {
97
+ DeclarativeUrlSurfacePhase,
98
+ PHASE_BY_TRAEFIK_AND_FRONT_DOOR,
99
+ phaseFromTraefikAndFrontDoor,
100
+ resolvePerTokenTraefik,
101
+ resolveDeclarativeUrlSurfaceState
102
+ };
@@ -17,6 +17,9 @@ const { computeDeclarativePathPrefix } = require('./url-declarative-url-flags');
17
17
  const { refreshUrlsLocalRegistryFromBuilder, normalizePatternForUrl } = require('./urls-local-registry');
18
18
  const pathsUtil = require('./paths');
19
19
  const { parseUrlToken } = require('./url-declarative-token-parse');
20
+ const {
21
+ getApplicationsRunProxyHint
22
+ } = require('./applications-config-defaults');
20
23
  const {
21
24
  applyTstRemoteDeveloperHost,
22
25
  applyTstRemoteDeveloperHostToOrigin,
@@ -100,6 +103,27 @@ function readTargetAppScopedFlag(targetAppKey) {
100
103
  }
101
104
  }
102
105
 
106
+ /**
107
+ * When global `traefik` is **false** in user config, public bases never use `remote-server`.
108
+ * Otherwise follows per-app `proxy` or legacy `ctx.declarativePublicUrlsUseLocalhost`.
109
+ *
110
+ * @param {Object} ctx
111
+ * @param {string} [appKey]
112
+ * @returns {boolean|undefined}
113
+ */
114
+ function resolveDeclarativePublicLocalhostFlag(ctx, appKey) {
115
+ if (ctx.userCfg && ctx.userCfg.traefik === false) {
116
+ return true;
117
+ }
118
+ if (ctx.userCfg && appKey) {
119
+ return !getApplicationsRunProxyHint(ctx.userCfg, appKey);
120
+ }
121
+ if (ctx.declarativePublicUrlsUseLocalhost === undefined) {
122
+ return undefined;
123
+ }
124
+ return Boolean(ctx.declarativePublicUrlsUseLocalhost);
125
+ }
126
+
103
127
  /**
104
128
  * Resolve one url:// token to a concrete URL string.
105
129
  * @param {string} token
@@ -123,14 +147,18 @@ function replaceUrlRefToken(token, ctx, profile, devNum, derivedEnvKey, registry
123
147
  patternStr,
124
148
  hostTemplate,
125
149
  tls,
126
- frontDoorIngressActive
150
+ frontDoorIngressActive,
151
+ perTokenTraefik,
152
+ internalDockerUseOriginOnly
127
153
  } = resolved;
154
+ const traefikForToken = perTokenTraefik;
155
+ const declarativePublicUrlsUseLocalhostForToken = resolveDeclarativePublicLocalhostFlag(ctx, appKey);
128
156
  const patternPath = normalizePatternForUrl(patternStr);
129
157
  const appScoped = parsed.targetKey
130
158
  ? readTargetAppScopedFlag(parsed.targetKey)
131
159
  : Boolean(ctx.appEnvironmentScopedResources);
132
160
  const pathPrefix = computeDeclarativePathPrefix(
133
- Boolean(ctx.traefik),
161
+ traefikForToken,
134
162
  ctx.useEnvironmentScopedResources,
135
163
  appScoped,
136
164
  derivedEnvKey
@@ -146,12 +174,14 @@ function replaceUrlRefToken(token, ctx, profile, devNum, derivedEnvKey, registry
146
174
  remoteServer: ctx.remoteServer,
147
175
  devNum,
148
176
  derivedEnvKey,
149
- traefik: Boolean(ctx.traefik),
177
+ traefik: traefikForToken,
150
178
  hostTemplate,
151
179
  tls,
152
180
  frontDoorIngressActive,
153
181
  developerIdRaw: ctx.developerIdRaw,
154
- infraTlsEnabled: Boolean(ctx.infraTlsEnabled)
182
+ infraTlsEnabled: Boolean(ctx.infraTlsEnabled),
183
+ internalDockerUseOriginOnly,
184
+ declarativePublicUrlsUseLocalhost: declarativePublicUrlsUseLocalhostForToken
155
185
  });
156
186
  }
157
187
 
@@ -168,8 +198,12 @@ function replaceUrlRefToken(token, ctx, profile, devNum, derivedEnvKey, registry
168
198
  * @param {boolean} ctx.appEnvironmentScopedResources
169
199
  * @param {string|null|undefined} ctx.remoteServer
170
200
  * @param {string|number|null|undefined} ctx.developerIdRaw
171
- * @param {boolean} [ctx.traefik] - Infra Traefik proxy on. Plan 117 `/dev`/`/tst` applies only when this is true. Traefik **host** authority (`frontDoorRouting.host`) applies only when this is true **and** the target app has `frontDoorRouting.enabled: true` (plan 124 `pathActive`).
172
- * @param {boolean} [ctx.infraTlsEnabled] - When true (`tlsEnabled` in config / `up-infra --tls`), Traefik front-door `url://public` uses https even if application.yaml has `frontDoorRouting.tls: false`
201
+ * @param {boolean} [ctx.traefik] - When `ctx.userCfg` is absent: global Traefik-style URL flag. When `ctx.userCfg` is set: `ctx.traefik` is ignored for per-token Traefik; see {@link resolveDeclarativeUrlSurfaceState}.
202
+ * @param {boolean|undefined} [ctx.declarativePublicUrlsUseLocalhost] - When `ctx.userCfg` is absent: **`undefined`** (default) skips `remote-server` when Traefik is off; **`true`** forces localhost; **`false`** allows remote authority without Traefik. When `ctx.userCfg` is set and root **`traefik` is not** `false`: ignored for public bases; derived per target app from `applications.<target>.proxy`. When **`ctx.userCfg.traefik === false`**, public bases always use localhost (per-app `proxy` does not opt into `remote-server` without infra Traefik).
203
+ * @param {Object} [ctx.userCfg] - `~/.aifabrix/config.yaml` root; when set, `applications.<app>.proxy` and `traefik` apply per **target** app for each `url://` token (so e.g. Keycloak URLs follow `applications.keycloak`, not only the app being resolved), except public authority stays on localhost when root `traefik` is **`false`**.
204
+ * @param {string|null|undefined} [ctx.projectRoot] - Optional absolute project root for `builder/<app>/application.yaml` discovery and `urls.local.yaml` refresh; when set, avoids relying on `paths.getProjectRoot()` (stable under cross-test `paths` mocks).
205
+ * @param {boolean} [ctx.excludeCwdBuilderScan] - When **true**, {@link refreshUrlsLocalRegistryFromBuilder} omits the cwd checkout **`builder/`** directory from the canonical scan list (tests / isolated fixtures that must not merge the ambient repo `builder/` into `urls.local.yaml`). Omit or **false** for normal CLI and `.env` expansion.
206
+ * @param {boolean} [ctx.infraTlsEnabled] - When true (`tlsEnabled` in config / `up-infra --tls`), Traefik front-door `url://public` uses **https**; when **false**, front-door bases use **http** (regardless of `frontDoorRouting.tls`). Loopback hosts stay **http**.
173
207
  * @returns {Promise<string>}
174
208
  */
175
209
  async function expandDeclarativeUrlsInEnvContent(content, ctx) {
@@ -182,7 +216,13 @@ async function expandDeclarativeUrlsInEnvContent(content, ctx) {
182
216
  const clientId = envMap.MISO_CLIENTID;
183
217
  const pipelineOverride = envMap.MISO_PIPELINE_ENV_KEY;
184
218
  const derivedEnvKey = deriveEnvKeyFromClientId(clientId, pipelineOverride);
185
- const registry = refreshUrlsLocalRegistryFromBuilder(pathsUtil.getProjectRoot());
219
+ const pr =
220
+ ctx.projectRoot !== undefined && ctx.projectRoot !== null ? String(ctx.projectRoot).trim() : '';
221
+ const registryRoot = pr || pathsUtil.getProjectRoot();
222
+ const registry =
223
+ ctx.excludeCwdBuilderScan === true
224
+ ? refreshUrlsLocalRegistryFromBuilder(registryRoot, { excludeCwdBuilderScan: true })
225
+ : refreshUrlsLocalRegistryFromBuilder(registryRoot);
186
226
 
187
227
  const lines = content.split('\n');
188
228
  const outLines = lines.map((line) => {
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Runtime base path helpers for declarative url:// resolution.
3
+ *
4
+ * @fileoverview Keeps path-aware internal URLs out of the main resolver file.
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ /**
12
+ * Runtime base path is derived from the existing frontDoorRouting path only
13
+ * when that path is active for the target app.
14
+ * @param {string|null|undefined} patternPath
15
+ * @param {Object} r
16
+ * @returns {string}
17
+ */
18
+ function normalizeRuntimeBasePath(patternPath, r) {
19
+ if (!r.frontDoorIngressActive) {
20
+ return '';
21
+ }
22
+ const value = String(patternPath || '').trim();
23
+ if (!value || value === '/') {
24
+ return '';
25
+ }
26
+ const pathValue = value.startsWith('/') ? value : `/${value}`;
27
+ return pathValue.replace(/\/{2,}/g, '/').replace(/\/+$/, '');
28
+ }
29
+
30
+ /**
31
+ * Read front-door host/TLS and optional internal-Docker origin-only flag from application.yaml.
32
+ * @param {object|null|undefined} doc
33
+ * @returns {{ hostTemplate: string|null, tls: boolean, internalDockerUseOriginOnly: boolean }}
34
+ */
35
+ function readFrontDoorHostTlsFromDoc(doc) {
36
+ if (!doc || !doc.frontDoorRouting) {
37
+ return { hostTemplate: null, tls: true, internalDockerUseOriginOnly: false };
38
+ }
39
+ const fd = doc.frontDoorRouting;
40
+ const hostTemplate =
41
+ typeof fd.host === 'string' && fd.host.trim() ? fd.host.trim() : null;
42
+ return {
43
+ hostTemplate,
44
+ tls: fd.tls !== false,
45
+ internalDockerUseOriginOnly: fd.internalDockerUseOriginOnly === true
46
+ };
47
+ }
48
+
49
+ module.exports = {
50
+ normalizeRuntimeBasePath,
51
+ readFrontDoorHostTlsFromDoc
52
+ };
@@ -95,5 +95,6 @@ module.exports = {
95
95
  URL_DECLARATIVE_VDIR_PUBLIC_TOKEN,
96
96
  INACTIVE_VDIR_PUBLIC_ENV_FALLBACK,
97
97
  applyInactiveVdirPublicTokenRewrite,
98
- rewriteInactiveDeclarativeVdirPublicContent
98
+ rewriteInactiveDeclarativeVdirPublicContent,
99
+ isFrontDoorRoutingEnabledInDoc
99
100
  };
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Builder-directory scan for {@link module:lib/utils/urls-local-registry} refresh.
3
+ *
4
+ * @fileoverview Collect application config per app key across builder roots (plan 141 P3: scan order
5
+ * defines precedence; later directories override earlier ones for the same `app.key`).
6
+ * @author AI Fabrix Team
7
+ * @version 1.0.0
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ const path = require('path');
13
+ const fsRealSync = require('../internal/fs-real-sync');
14
+ const { resolveApplicationConfigPath } = require('./app-config-resolver');
15
+ const { loadConfigFile } = require('./config-format');
16
+
17
+ /**
18
+ * @param {string} appDir - Absolute path to a builder app folder (directory containing application config)
19
+ * @returns {{ doc: object }|null}
20
+ */
21
+ function tryGetApplicationDirConfigForRegistry(appDir) {
22
+ let cfgPath;
23
+ try {
24
+ cfgPath = resolveApplicationConfigPath(appDir);
25
+ } catch {
26
+ return null;
27
+ }
28
+ if (!fsRealSync.existsSync(cfgPath)) {
29
+ return null;
30
+ }
31
+ let doc;
32
+ try {
33
+ doc = loadConfigFile(cfgPath);
34
+ } catch {
35
+ return null;
36
+ }
37
+ if (!doc || typeof doc !== 'object') {
38
+ return null;
39
+ }
40
+ return { doc };
41
+ }
42
+
43
+ /**
44
+ * @param {string} builderDir
45
+ * @param {import('fs').Dirent} ent
46
+ * @param {number} dirIndex
47
+ * @param {Map<string, { folderName: string, doc: object, dirIndex: number }>} best
48
+ */
49
+ function maybeUpdateBestFromAppFolder(builderDir, ent, dirIndex, best) {
50
+ const appDir = path.join(builderDir, ent.name);
51
+ const entry = tryGetApplicationDirConfigForRegistry(appDir);
52
+ if (!entry) {
53
+ return;
54
+ }
55
+ const { doc } = entry;
56
+ const appKey = (doc.app && doc.app.key) || ent.name;
57
+ const cur = best.get(appKey);
58
+ if (!cur || dirIndex > cur.dirIndex) {
59
+ best.set(appKey, { folderName: ent.name, doc, dirIndex });
60
+ }
61
+ }
62
+
63
+ /**
64
+ * @param {string} builderDir
65
+ * @param {number} dirIndex
66
+ * @param {Map<string, { folderName: string, doc: object, dirIndex: number }>} best
67
+ */
68
+ function mergeBuilderDirScanIntoBestMap(builderDir, dirIndex, best) {
69
+ if (!builderDir || typeof builderDir !== 'string') {
70
+ return;
71
+ }
72
+ if (!fsRealSync.existsSync(builderDir) || !fsRealSync.statSync(builderDir).isDirectory()) {
73
+ return;
74
+ }
75
+ for (const ent of fsRealSync.readdirSync(builderDir, { withFileTypes: true })) {
76
+ if (!ent.isDirectory()) {
77
+ continue;
78
+ }
79
+ maybeUpdateBestFromAppFolder(builderDir, ent, dirIndex, best);
80
+ }
81
+ }
82
+
83
+ /**
84
+ * When the same app exists under multiple builder roots, the **last** directory in `scanDirs`
85
+ * wins (highest `dirIndex`). Matches plan 141 canonical precedence (checkout / cwd after system materialization).
86
+ *
87
+ * @param {string[]} scanDirs - Builder or `packages` directories, low → high priority
88
+ * @returns {Array<{ folderName: string, doc: object }>}
89
+ */
90
+ function collectLatestApplicationYamlEntriesPerApp(scanDirs) {
91
+ /** @type {Map<string, { folderName: string, doc: object, dirIndex: number }>} */
92
+ const best = new Map();
93
+ scanDirs.forEach((builderDir, dirIndex) => {
94
+ mergeBuilderDirScanIntoBestMap(builderDir, dirIndex, best);
95
+ });
96
+ return [...best.entries()]
97
+ .sort((a, b) => a[0].localeCompare(b[0]))
98
+ .map(([, v]) => ({ folderName: v.folderName, doc: v.doc }));
99
+ }
100
+
101
+ module.exports = {
102
+ collectLatestApplicationYamlEntriesPerApp
103
+ };