@aifabrix/builder 2.45.0 → 2.46.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 (352) hide show
  1. package/.cursor/rules/cli-layout.mdc +2 -0
  2. package/.cursor/rules/cli-product-neutral.mdc +43 -0
  3. package/.cursor/rules/docs-rules.mdc +3 -1
  4. package/.cursor/rules/project-rules.mdc +1 -0
  5. package/README.md +43 -17
  6. package/integration/hubspot-test/wizard-hubspot-e2e.yaml +1 -1
  7. package/jest.config.default.js +2 -1
  8. package/jest.config.isolated.js +2 -1
  9. package/jest.global-live-fabrix-hooks.js +36 -0
  10. package/jest.isolated-projects.js +130 -0
  11. package/jest.projects.js +69 -121
  12. package/lib/api/agent-metadata-validation.api.js +93 -0
  13. package/lib/api/api-response-errors.js +124 -0
  14. package/lib/api/audit-api-helpers.js +68 -0
  15. package/lib/api/audit.api.js +119 -0
  16. package/lib/api/auth-cache.api.js +44 -0
  17. package/lib/api/certificates.api.js +4 -4
  18. package/lib/api/credential.api.js +2 -2
  19. package/lib/api/dataplane-health.api.js +137 -0
  20. package/lib/api/dataplane-sync.api.js +27 -0
  21. package/lib/api/datasources-core.api.js +21 -18
  22. package/lib/api/datasources-extended.api.js +22 -22
  23. package/lib/api/dimensions.api.js +3 -0
  24. package/lib/api/external-systems.api.js +13 -13
  25. package/lib/api/governance-scenario-pack.api.js +159 -0
  26. package/lib/api/governance-scenarios.api.js +39 -0
  27. package/lib/api/groups.api.js +79 -0
  28. package/lib/api/index.js +88 -5
  29. package/lib/api/lifecycle.api.js +77 -0
  30. package/lib/api/pipeline.api.js +7 -5
  31. package/lib/api/protection.api.js +157 -0
  32. package/lib/api/records-bulk.api.js +43 -0
  33. package/lib/api/records-search-parse.js +49 -0
  34. package/lib/api/records-search.api.js +29 -0
  35. package/lib/api/types/agent-metadata-validation.types.js +67 -0
  36. package/lib/api/types/audit.types.js +72 -0
  37. package/lib/api/types/dataplane-health.types.js +27 -0
  38. package/lib/api/types/dimensions.types.js +2 -0
  39. package/lib/api/types/governance-scenarios.types.js +39 -0
  40. package/lib/api/types/protection.types.js +69 -0
  41. package/lib/api/types/records-bulk.types.js +35 -0
  42. package/lib/api/types/records-search.types.js +28 -0
  43. package/lib/api/user-groups.api.js +50 -0
  44. package/lib/api/users.api.js +95 -0
  45. package/lib/api/validation-run.api.js +3 -3
  46. package/lib/api/validation-runner.js +5 -3
  47. package/lib/api/wizard-platform.api.js +3 -3
  48. package/lib/api/wizard.api.js +21 -21
  49. package/lib/app/deploy-config.js +4 -1
  50. package/lib/app/deploy.js +34 -19
  51. package/lib/app/down.js +20 -1
  52. package/lib/app/push.js +4 -5
  53. package/lib/app/register.js +7 -4
  54. package/lib/app/run-env-compose.js +84 -30
  55. package/lib/build/index.js +5 -0
  56. package/lib/certification/sync-system-certification.js +13 -6
  57. package/lib/cli/index.js +8 -2
  58. package/lib/cli/infra-guided-auth.js +109 -0
  59. package/lib/cli/infra-guided.js +91 -69
  60. package/lib/cli/setup-app.help.js +19 -2
  61. package/lib/cli/setup-app.js +58 -23
  62. package/lib/cli/setup-app.test-commands.js +28 -21
  63. package/lib/cli/setup-auth.js +55 -3
  64. package/lib/cli/setup-certification-commands.js +147 -0
  65. package/lib/cli/setup-certification.help.js +141 -0
  66. package/lib/cli/setup-environment.js +19 -2
  67. package/lib/cli/setup-external-system-help.js +62 -0
  68. package/lib/cli/setup-external-system.js +62 -6
  69. package/lib/cli/setup-infra.js +9 -1
  70. package/lib/cli/setup-platform.js +6 -1
  71. package/lib/cli/setup-secrets.js +33 -1
  72. package/lib/cli/setup-utility-help-after.js +65 -0
  73. package/lib/cli/setup-utility-repair.js +198 -0
  74. package/lib/cli/setup-utility-resolve.js +42 -6
  75. package/lib/cli/setup-utility-validate.js +197 -0
  76. package/lib/cli/setup-utility.js +41 -258
  77. package/lib/commands/auth-status-client-tokens.js +214 -0
  78. package/lib/commands/auth-status-dataplane-version.js +269 -0
  79. package/lib/commands/auth-status-display.js +94 -0
  80. package/lib/commands/auth-status.js +111 -53
  81. package/lib/commands/credential-push.js +10 -5
  82. package/lib/commands/datasource-load-export-cli.js +178 -0
  83. package/lib/commands/datasource-test-trust-cli.js +118 -0
  84. package/lib/commands/datasource-unified-test-cli.js +31 -20
  85. package/lib/commands/datasource-unified-test-cli.options.js +22 -3
  86. package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
  87. package/lib/commands/datasource-verify-audit-cli.js +96 -0
  88. package/lib/commands/datasource.js +68 -1
  89. package/lib/commands/dev-show-display.js +8 -1
  90. package/lib/commands/dimension-value.js +43 -13
  91. package/lib/commands/dimension.js +38 -7
  92. package/lib/commands/governance-cli-shared.js +94 -0
  93. package/lib/commands/governance-command-action.js +132 -0
  94. package/lib/commands/governance-download-external.js +34 -0
  95. package/lib/commands/governance-generate-external.js +71 -0
  96. package/lib/commands/governance-upload-external.js +36 -0
  97. package/lib/commands/governance-verify-external.js +64 -0
  98. package/lib/commands/governance.js +144 -0
  99. package/lib/commands/identity-apply-sync.js +131 -0
  100. package/lib/commands/identity-group-cli.js +146 -0
  101. package/lib/commands/identity-membership-role-cache.js +175 -0
  102. package/lib/commands/identity-shared.js +94 -0
  103. package/lib/commands/identity-user-cli.js +158 -0
  104. package/lib/commands/identity.js +43 -0
  105. package/lib/commands/lifecycle-command-action.js +123 -0
  106. package/lib/commands/lifecycle-external.js +268 -0
  107. package/lib/commands/login.js +56 -17
  108. package/lib/commands/protection-cli-leaves.js +286 -0
  109. package/lib/commands/protection.js +50 -0
  110. package/lib/commands/repair-auth-apply.js +127 -0
  111. package/lib/commands/repair-changed-files.js +41 -0
  112. package/lib/commands/repair-datasource-keys.js +4 -1
  113. package/lib/commands/repair-datasource-run.js +59 -0
  114. package/lib/commands/repair-datasource-test-payload.js +259 -0
  115. package/lib/commands/repair-datasource.js +1 -51
  116. package/lib/commands/repair-env-template.js +64 -13
  117. package/lib/commands/repair-external-run.js +216 -0
  118. package/lib/commands/repair-openapi-sync.js +23 -0
  119. package/lib/commands/repair-persist.js +4 -0
  120. package/lib/commands/repair-rbac.js +52 -1
  121. package/lib/commands/repair-steps.js +145 -0
  122. package/lib/commands/repair.js +29 -286
  123. package/lib/commands/secrets-get.js +184 -0
  124. package/lib/commands/secrets-remove-all.js +3 -1
  125. package/lib/commands/setup-image-refresh.js +146 -0
  126. package/lib/commands/setup-modes.js +158 -162
  127. package/lib/commands/setup-platform-auth.js +76 -0
  128. package/lib/commands/setup-prompts-admin.js +244 -0
  129. package/lib/commands/setup-prompts-platform-mode.js +67 -0
  130. package/lib/commands/setup-prompts-validators.js +36 -0
  131. package/lib/commands/setup-prompts.js +24 -76
  132. package/lib/commands/setup-run-context.js +45 -0
  133. package/lib/commands/setup-spinners.js +39 -0
  134. package/lib/commands/setup.js +27 -5
  135. package/lib/commands/teardown.js +39 -0
  136. package/lib/commands/test-e2e-external.js +61 -14
  137. package/lib/commands/test-governance-cli-options.js +47 -0
  138. package/lib/commands/test-governance-external.js +67 -0
  139. package/lib/commands/test-trust-cli-options.js +32 -0
  140. package/lib/commands/test-trust-external.js +126 -0
  141. package/lib/commands/up-common.js +25 -13
  142. package/lib/commands/up-dataplane.js +50 -13
  143. package/lib/commands/upload-publish-context.js +75 -0
  144. package/lib/commands/upload-sidecar-followups.js +89 -0
  145. package/lib/commands/upload.js +108 -105
  146. package/lib/commands/verify-governance-combined-external.js +61 -0
  147. package/lib/commands/verify-governance-command-action.js +64 -0
  148. package/lib/commands/verify-governance-db.js +102 -0
  149. package/lib/commands/verify-governance-external.js +77 -0
  150. package/lib/commands/verify-governance-result-builders.js +71 -0
  151. package/lib/commands/verify-operations-command-action.js +68 -0
  152. package/lib/commands/verify-operations-external.js +268 -0
  153. package/lib/commands/verify-operations-persist.js +75 -0
  154. package/lib/commands/verify-operations-step-plan.js +68 -0
  155. package/lib/commands/verify-operations-steps.js +111 -0
  156. package/lib/commands/verify-trust-command-action.js +70 -0
  157. package/lib/commands/verify-trust-external.js +67 -0
  158. package/lib/commands/wizard-config-normalizer.js +43 -23
  159. package/lib/commands/wizard-core.js +5 -0
  160. package/lib/core/admin-password-generator.js +38 -0
  161. package/lib/core/admin-secrets-bundle.js +111 -0
  162. package/lib/core/admin-secrets-env-overlay.js +62 -0
  163. package/lib/core/admin-secrets.js +32 -4
  164. package/lib/core/config-attach-extensions.js +3 -0
  165. package/lib/core/config-device-dataplane.js +185 -0
  166. package/lib/core/config-platform-controller.js +44 -0
  167. package/lib/core/config-setup-installation-profile.js +42 -0
  168. package/lib/core/config-setup-platform-mode.js +46 -0
  169. package/lib/core/config.js +46 -36
  170. package/lib/core/secrets-admin-env.js +6 -2
  171. package/lib/core/secrets-ensure.js +4 -0
  172. package/lib/core/secrets-env-content.js +54 -48
  173. package/lib/core/secrets-env-write.js +10 -1
  174. package/lib/core/secrets-infra-placeholder-sync.js +2 -0
  175. package/lib/core/setup-installation-profile.js +43 -0
  176. package/lib/core/setup-platform-mode.js +55 -0
  177. package/lib/datasource/agent-trust-debug-log.js +45 -0
  178. package/lib/datasource/agent-trust-fetch.js +115 -0
  179. package/lib/datasource/agent-trust-map.js +101 -0
  180. package/lib/datasource/agent-trust-mock.js +44 -0
  181. package/lib/datasource/agent-trust-run.js +95 -0
  182. package/lib/datasource/agent-trust-timeout.js +25 -0
  183. package/lib/datasource/audit-evidence-display.js +80 -0
  184. package/lib/datasource/audit-evidence-extract-debug.js +115 -0
  185. package/lib/datasource/audit-evidence-extract.js +180 -0
  186. package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
  187. package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
  188. package/lib/datasource/audit-evidence-poll.js +109 -0
  189. package/lib/datasource/audit-evidence-run.js +177 -0
  190. package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
  191. package/lib/datasource/audit-evidence-verify-matrix.js +205 -0
  192. package/lib/datasource/audit-evidence-verify.js +108 -0
  193. package/lib/datasource/bulk-loader-run-batches.js +100 -0
  194. package/lib/datasource/bulk-loader-service-retry.js +52 -0
  195. package/lib/datasource/bulk-loader-service.js +127 -0
  196. package/lib/datasource/capability/run-capability-dimension.js +2 -1
  197. package/lib/datasource/datasource-exporter-resolve.js +82 -0
  198. package/lib/datasource/datasource-exporter-service.js +144 -0
  199. package/lib/datasource/datasource-identifier.js +148 -0
  200. package/lib/datasource/datasource-load-export-context.js +95 -0
  201. package/lib/datasource/integration-context.js +6 -4
  202. package/lib/datasource/local-data-paths.js +145 -0
  203. package/lib/datasource/log-cleaner.js +241 -0
  204. package/lib/datasource/log-viewer.js +111 -6
  205. package/lib/datasource/record-file-parser.js +85 -0
  206. package/lib/datasource/record-mapper.js +129 -0
  207. package/lib/datasource/resolve-app.js +74 -55
  208. package/lib/datasource/test-e2e.js +29 -2
  209. package/lib/datasource/unified-validation-run-body.js +3 -1
  210. package/lib/datasource/unified-validation-run-resolve.js +45 -12
  211. package/lib/datasource/unified-validation-run.js +47 -18
  212. package/lib/datasource/validate.js +37 -20
  213. package/lib/deployment/environment-poll-status.js +138 -0
  214. package/lib/deployment/environment.js +42 -34
  215. package/lib/external-system/download.js +21 -20
  216. package/lib/external-system/generator.js +5 -1
  217. package/lib/external-system/integration-auth-context.js +45 -0
  218. package/lib/external-system/test-auth.js +12 -5
  219. package/lib/external-system/test-system-level.js +25 -4
  220. package/lib/external-system/test.js +9 -7
  221. package/lib/generator/external-controller-manifest.js +15 -0
  222. package/lib/governance/governance-cli-errors.js +96 -0
  223. package/lib/governance/governance-download-display.js +38 -0
  224. package/lib/governance/governance-pack-loader.js +70 -0
  225. package/lib/governance/governance-run-display.js +309 -0
  226. package/lib/identity/identity-apply-core.js +108 -0
  227. package/lib/identity/identity-apply-phases.js +244 -0
  228. package/lib/identity/identity-apply-service.js +77 -0
  229. package/lib/identity/identity-csv-parser.js +114 -0
  230. package/lib/infrastructure/admin-secrets-apply.js +84 -0
  231. package/lib/infrastructure/admin-secrets-ensure.js +130 -0
  232. package/lib/infrastructure/admin-secrets-resolve.js +95 -0
  233. package/lib/infrastructure/admin-secrets-sync.js +91 -0
  234. package/lib/infrastructure/helpers.js +23 -129
  235. package/lib/infrastructure/index.js +5 -6
  236. package/lib/infrastructure/orphan-infra-docker-teardown.js +11 -5
  237. package/lib/internal/fs-real-sync.js +29 -1
  238. package/lib/lifecycle/certification-progress.js +231 -0
  239. package/lib/lifecycle/dataplane-ui-url.js +145 -0
  240. package/lib/lifecycle/exit-codes.js +24 -0
  241. package/lib/lifecycle/json-envelope.js +25 -0
  242. package/lib/lifecycle/lifecycle-governance-options.js +51 -0
  243. package/lib/lifecycle/logger-quiet.js +29 -0
  244. package/lib/lifecycle/open-api-published.js +59 -0
  245. package/lib/lifecycle/operations-readiness-metrics.js +101 -0
  246. package/lib/lifecycle/product-model.js +196 -0
  247. package/lib/lifecycle/report-display.js +471 -0
  248. package/lib/lifecycle/scenario-upload.js +191 -0
  249. package/lib/lifecycle/verify-step-progress.js +87 -0
  250. package/lib/parameters/infra-kv-discovery.js +27 -2
  251. package/lib/protection/auth-context.js +51 -0
  252. package/lib/protection/convert-batch.js +90 -0
  253. package/lib/protection/load.js +41 -0
  254. package/lib/protection/paths.js +158 -0
  255. package/lib/protection/preflight-datasource-ready.js +46 -0
  256. package/lib/protection/protection-create-probe-lines.js +142 -0
  257. package/lib/protection/protection-create-probes.js +95 -0
  258. package/lib/protection/protection-create-scaffold.js +281 -0
  259. package/lib/protection/protection-display-helpers.js +445 -0
  260. package/lib/protection/protection-display.js +215 -0
  261. package/lib/protection/protection-preset-registry.js +97 -0
  262. package/lib/protection/report-exit.js +62 -0
  263. package/lib/protection/resolve.js +202 -0
  264. package/lib/protection/run-commands-validate.js +130 -0
  265. package/lib/protection/run-commands.js +216 -0
  266. package/lib/protection/run-protection-create-helpers.js +134 -0
  267. package/lib/protection/run-protection-create-probes-flow.js +43 -0
  268. package/lib/protection/run-protection-create.js +191 -0
  269. package/lib/protection/scope.js +22 -0
  270. package/lib/protection/sync-after-upload.js +64 -0
  271. package/lib/protection/upload-batch.js +127 -0
  272. package/lib/protection/validate-batch.js +113 -0
  273. package/lib/protection/validate-local.js +65 -0
  274. package/lib/resolvers/dimension-file.js +29 -4
  275. package/lib/schema/application-schema.json +1 -1
  276. package/lib/schema/datasource-test-run.schema.json +89 -6
  277. package/lib/schema/external-datasource.schema.json +2636 -2588
  278. package/lib/schema/external-system.schema.json +1089 -761
  279. package/lib/schema/flag-map-validation-run.json +2 -2
  280. package/lib/schema/infra.parameter.yaml +45 -0
  281. package/lib/schema/protection.schema.json +374 -0
  282. package/lib/schema/type/credential-field-registry.json +642 -0
  283. package/lib/schema/wizard-config.schema.json +1 -1
  284. package/lib/utils/agent-trust-run-display.js +231 -0
  285. package/lib/utils/agent-trust-run-exit.js +59 -0
  286. package/lib/utils/agent-trust-wait-ui.js +94 -0
  287. package/lib/utils/aifabrix-test-runtime-guard.js +125 -0
  288. package/lib/utils/api.js +55 -31
  289. package/lib/utils/app-relative-path-env.js +112 -0
  290. package/lib/utils/cli-exit-codes.js +30 -0
  291. package/lib/utils/cli-secrets-error-format.js +83 -21
  292. package/lib/utils/cli-sync-options.js +33 -0
  293. package/lib/utils/cli-utils.js +16 -4
  294. package/lib/utils/compose-generate-docker-compose.js +4 -0
  295. package/lib/utils/compose-traefik-ingress-base.js +16 -1
  296. package/lib/utils/config-secrets-path.js +46 -0
  297. package/lib/utils/controller-url.js +88 -1
  298. package/lib/utils/credential-push-ui.js +90 -0
  299. package/lib/utils/credential-secrets-env.js +137 -9
  300. package/lib/utils/dataplane-cli-version-gate.js +202 -0
  301. package/lib/utils/dataplane-cli-version-help.js +74 -0
  302. package/lib/utils/dataplane-resolver.js +129 -2
  303. package/lib/utils/datasource-export-display.js +137 -0
  304. package/lib/utils/datasource-load-display.js +174 -0
  305. package/lib/utils/datasource-load-export-ui.js +120 -0
  306. package/lib/utils/datasource-test-run-capability-scope.js +3 -3
  307. package/lib/utils/datasource-test-run-display.js +3 -3
  308. package/lib/utils/datasource-test-run-tty-log.js +5 -1
  309. package/lib/utils/declarative-url-ports.js +3 -3
  310. package/lib/utils/deployment-auth-mode.js +41 -0
  311. package/lib/utils/developer-volume-id.js +33 -0
  312. package/lib/utils/env-copy.js +14 -9
  313. package/lib/utils/env-kv-orphan-lines.js +43 -0
  314. package/lib/utils/error-formatters/network-errors.js +13 -4
  315. package/lib/utils/external-env-template.js +1 -0
  316. package/lib/utils/external-system-readiness-core.js +10 -0
  317. package/lib/utils/external-system-readiness-display.js +80 -23
  318. package/lib/utils/health-check.js +4 -1
  319. package/lib/utils/help-builder.js +35 -26
  320. package/lib/utils/keycloak-realm-ready.js +101 -0
  321. package/lib/utils/load-cip-capacity-display-config.js +61 -15
  322. package/lib/utils/manifest-source-emit.js +24 -0
  323. package/lib/utils/pipeline-upload-auth-retry.js +82 -0
  324. package/lib/utils/platform-controller-url.js +156 -0
  325. package/lib/utils/postgres-platform-bootstrap.js +199 -0
  326. package/lib/utils/postgres-wipe.js +9 -2
  327. package/lib/utils/resolve-env-mode.js +47 -0
  328. package/lib/utils/secrets-generator.js +31 -25
  329. package/lib/utils/secrets-missing-error.js +164 -0
  330. package/lib/utils/semver-compare.js +127 -0
  331. package/lib/utils/service-url-label.js +81 -0
  332. package/lib/utils/setup-quiet-output.js +25 -0
  333. package/lib/utils/template-helpers.js +15 -0
  334. package/lib/utils/token-manager-client-credentials.js +201 -0
  335. package/lib/utils/token-manager-messages.js +8 -0
  336. package/lib/utils/token-manager-refresh.js +12 -2
  337. package/lib/utils/token-manager.js +86 -105
  338. package/lib/utils/upload-pipeline-ui.js +98 -0
  339. package/lib/utils/upload-sync-options.js +42 -0
  340. package/lib/utils/url-declarative-public-base.js +21 -3
  341. package/lib/utils/url-declarative-vdir-inactive-env.js +14 -4
  342. package/lib/utils/validation-poll-ui.js +22 -7
  343. package/lib/utils/validation-run-stage.js +45 -0
  344. package/lib/validation/wizard-config-validator.js +58 -19
  345. package/package.json +1 -1
  346. package/templates/applications/dataplane/application.yaml +2 -1
  347. package/templates/applications/dataplane/env.template +36 -1
  348. package/templates/applications/dataplane/rbac.yaml +4 -0
  349. package/templates/applications/miso-controller/env.template +16 -0
  350. package/templates/external-system/README.md.hbs +116 -11
  351. package/templates/python/docker-compose.hbs +19 -18
  352. package/templates/typescript/docker-compose.hbs +18 -17
@@ -5,49 +5,82 @@
5
5
  * @version 2.0.0
6
6
  */
7
7
 
8
- const path = require('path');
9
8
  const fs = require('fs');
10
9
  const {
11
10
  getIntegrationPath,
12
11
  listIntegrationAppNames,
13
12
  resolveIntegrationAppKeyFromCwd
14
13
  } = require('../utils/paths');
15
- const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
16
- const { loadConfigFile } = require('../utils/config-format');
14
+ const {
15
+ appHasDatasourceIdentifier,
16
+ pickIntegrationAppForDatasourceIdentifier
17
+ } = require('./datasource-identifier');
18
+
19
+ /** @deprecated Use appHasDatasourceIdentifier — kept for tests and backward compatibility */
20
+ function appHasDatasourceKey(appKey, datasourceKey) {
21
+ return appHasDatasourceIdentifier(appKey, datasourceKey);
22
+ }
17
23
 
18
24
  /**
19
- * For one app, check if any of its datasource files has the given key
20
- * @param {string} appKey - Integration folder name (system key context)
21
- * @param {string} datasourceKey - Datasource key to match
22
- * @returns {boolean} True if this app has a datasource with that key
25
+ * @param {string} key
26
+ * @param {string} [explicitApp]
27
+ * @returns {{ appKey: string }|null}
23
28
  */
24
- function appHasDatasourceKey(appKey, datasourceKey) {
25
- const appPath = getIntegrationPath(appKey);
26
- let config;
27
- try {
28
- const configPath = resolveApplicationConfigPath(appPath);
29
- config = loadConfigFile(configPath);
30
- } catch {
31
- return false;
29
+ function resolveFromExplicitApp(key, explicitApp) {
30
+ if (!explicitApp || typeof explicitApp !== 'string' || !explicitApp.trim()) {
31
+ return null;
32
+ }
33
+ const appKey = explicitApp.trim();
34
+ if (fs.existsSync(getIntegrationPath(appKey))) {
35
+ return { appKey };
36
+ }
37
+ return null;
38
+ }
39
+
40
+ /**
41
+ * @param {string} key
42
+ * @param {string[]} appNames
43
+ * @returns {{ appKey: string }|null}
44
+ * @throws {Error}
45
+ */
46
+ function resolveFromAppScan(key, appNames) {
47
+ const matches = appNames.filter(appName => appHasDatasourceIdentifier(appName, key));
48
+ if (matches.length === 1) {
49
+ return { appKey: matches[0] };
50
+ }
51
+ if (matches.length > 1) {
52
+ throw new Error(
53
+ `More than one app has this datasource; add --app <app>. Apps: ${matches.join(', ')}`
54
+ );
32
55
  }
33
- const schemaBasePath = config.externalIntegration?.schemaBasePath || './';
34
- const datasourceFiles = config.externalIntegration?.dataSources || [];
35
- for (const f of datasourceFiles) {
36
- if (!f || typeof f !== 'string') continue;
37
- const fullPath = path.isAbsolute(schemaBasePath)
38
- ? path.join(schemaBasePath, f)
39
- : path.join(appPath, schemaBasePath, f);
40
- if (!fs.existsSync(fullPath)) continue;
41
- try {
42
- const parsed = loadConfigFile(fullPath);
43
- if (parsed && parsed.key === datasourceKey) return true;
44
- } catch {
45
- // skip unreadable or invalid files
46
- }
56
+ return null;
57
+ }
58
+
59
+ /**
60
+ * @param {string} key
61
+ * @param {string[]} appNames
62
+ * @returns {{ appKey: string }|null}
63
+ */
64
+ function resolveFromPrefixOrSegments(key, appNames) {
65
+ const prefixApp = pickIntegrationAppForDatasourceIdentifier(key, appNames);
66
+ if (prefixApp && appHasDatasourceIdentifier(prefixApp, key)) {
67
+ return { appKey: prefixApp };
47
68
  }
48
- return false;
69
+ const segments = key.split('-');
70
+ if (segments.length < 2) {
71
+ return null;
72
+ }
73
+ const candidate = segments.slice(0, -1).join('-');
74
+ const candidatePath = getIntegrationPath(candidate);
75
+ if (fs.existsSync(candidatePath) && appHasDatasourceIdentifier(candidate, key)) {
76
+ return { appKey: candidate };
77
+ }
78
+ return null;
49
79
  }
50
80
 
81
+ const RESOLVE_APP_HINT =
82
+ 'Could not determine app context. Use --app <app>, a datasource key, a datasource JSON filename (e.g. myapp-datasource-contacts), or run from integration/<systemKey>/ directory.';
83
+
51
84
  /**
52
85
  * Resolve app key for a datasource: explicit --app, cwd, scan by key, or parse key convention.
53
86
  * @async
@@ -56,7 +89,6 @@ function appHasDatasourceKey(appKey, datasourceKey) {
56
89
  * @returns {Promise<{appKey: string}>} Resolved app key
57
90
  * @throws {Error} When app cannot be determined or multiple apps match
58
91
  */
59
- /* eslint-disable-next-line max-statements -- Resolution order: explicit, cwd, scan, parse */
60
92
  async function resolveAppKeyForDatasource(datasourceKey, explicitApp) {
61
93
  if (!datasourceKey || typeof datasourceKey !== 'string') {
62
94
  throw new Error('Datasource key is required');
@@ -66,41 +98,28 @@ async function resolveAppKeyForDatasource(datasourceKey, explicitApp) {
66
98
  throw new Error('Datasource key cannot be empty');
67
99
  }
68
100
 
69
- if (explicitApp && typeof explicitApp === 'string' && explicitApp.trim()) {
70
- const appPath = getIntegrationPath(explicitApp.trim());
71
- if (fs.existsSync(appPath)) {
72
- return { appKey: explicitApp.trim() };
73
- }
101
+ const explicit = resolveFromExplicitApp(key, explicitApp);
102
+ if (explicit) {
103
+ return explicit;
74
104
  }
75
105
 
76
106
  const fromCwd = resolveIntegrationAppKeyFromCwd();
77
- if (fromCwd && appHasDatasourceKey(fromCwd, key)) {
107
+ if (fromCwd && appHasDatasourceIdentifier(fromCwd, key)) {
78
108
  return { appKey: fromCwd };
79
109
  }
80
110
 
81
111
  const appNames = listIntegrationAppNames();
82
- const matches = appNames.filter(appName => appHasDatasourceKey(appName, key));
83
- if (matches.length === 1) {
84
- return { appKey: matches[0] };
85
- }
86
- if (matches.length > 1) {
87
- throw new Error(
88
- `More than one app has this datasource; add --app <app>. Apps: ${matches.join(', ')}`
89
- );
112
+ const scanned = resolveFromAppScan(key, appNames);
113
+ if (scanned) {
114
+ return scanned;
90
115
  }
91
116
 
92
- const segments = key.split('-');
93
- if (segments.length >= 2) {
94
- const candidate = segments.slice(0, -1).join('-');
95
- const candidatePath = getIntegrationPath(candidate);
96
- if (fs.existsSync(candidatePath) && appHasDatasourceKey(candidate, key)) {
97
- return { appKey: candidate };
98
- }
117
+ const inferred = resolveFromPrefixOrSegments(key, appNames);
118
+ if (inferred) {
119
+ return inferred;
99
120
  }
100
121
 
101
- throw new Error(
102
- 'Could not determine app context. Use --app <app> or run from integration/<systemKey>/ directory.'
103
- );
122
+ throw new Error(RESOLVE_APP_HINT);
104
123
  }
105
124
 
106
125
  module.exports = {
@@ -11,7 +11,7 @@ const chalk = require('chalk');
11
11
  const logger = require('../utils/logger');
12
12
  const { getIntegrationPath } = require('../utils/paths');
13
13
  const { resolveAppKeyForDatasource } = require('./resolve-app');
14
- const { infoLine } = require('../utils/cli-test-layout-chalk');
14
+ const { formatProgress, formatSuccessLine, infoLine } = require('../utils/cli-test-layout-chalk');
15
15
  const { runUnifiedDatasourceValidation } = require('./unified-validation-run');
16
16
  const { includeDebugForRequest } = require('../utils/validation-run-request');
17
17
  const { e2eShapeFromEnvelope } = require('../utils/datasource-test-run-legacy-adapter');
@@ -41,7 +41,12 @@ function buildUnifiedE2eRunOptions(options, timeoutMs, pk) {
41
41
  minRecordCount: options.minRecordCount,
42
42
  capabilityKey: options.capabilityKey,
43
43
  timeout: timeoutMs,
44
- sync: options.sync === true
44
+ sync: options.sync === true,
45
+ force: options.force === true,
46
+ authConfig: options.authConfig,
47
+ dataplaneUrl: options.dataplaneUrl || options.dataplane,
48
+ quietPollUi: options.quietPollUi === true,
49
+ onPollProgress: options.onPollProgress
45
50
  };
46
51
  }
47
52
 
@@ -51,6 +56,25 @@ function logE2eDatasourceBanner(datasourceKey, verbose) {
51
56
  logger.log(infoLine(`🧪 Running E2E test for datasource: ${datasourceKey}`));
52
57
  }
53
58
 
59
+ /**
60
+ * Compact per-datasource progress (default test-e2e / system rollup when not --verbose).
61
+ * @param {string} datasourceKey
62
+ * @param {boolean} verbose
63
+ */
64
+ function logE2eDatasourceProgress(datasourceKey, verbose) {
65
+ if (verbose) return;
66
+ logger.log(formatProgress(`Running E2E validation for ${datasourceKey}…`));
67
+ }
68
+
69
+ /**
70
+ * @param {string} datasourceKey
71
+ * @param {boolean} verbose
72
+ */
73
+ function logE2eDatasourceValidated(datasourceKey, verbose) {
74
+ if (verbose) return;
75
+ logger.log(formatSuccessLine(`${datasourceKey} validated`));
76
+ }
77
+
54
78
  /**
55
79
  * Resolve primaryKeyValue for request body: string as-is, or read and parse JSON from @path
56
80
  * @param {string} [value]
@@ -174,6 +198,7 @@ async function runDatasourceTestE2E(datasourceKey, options = {}) {
174
198
  const cliWallStartedAt = Date.now();
175
199
  const { appKey } = await resolveAppKeyForDatasource(datasourceKey, options.app);
176
200
  logE2eDatasourceBanner(datasourceKey, options.verbose);
201
+ logE2eDatasourceProgress(datasourceKey, options.verbose);
177
202
  const pk = await resolvePrimaryKeyValue(options.primaryKeyValue);
178
203
  const timeoutMs = resolveE2ePollTimeoutMs(options);
179
204
  const requestMeta = buildE2eRequestMeta(datasourceKey, options, pk);
@@ -184,6 +209,8 @@ async function runDatasourceTestE2E(datasourceKey, options = {}) {
184
209
 
185
210
  await throwIfUnifiedE2EBlocked(unifiedResult, appKey, options, requestMeta);
186
211
 
212
+ logE2eDatasourceValidated(datasourceKey, options.verbose);
213
+
187
214
  const display = e2eShapeFromEnvelope(unifiedResult.envelope);
188
215
  Object.assign(display, { datasourceTestRun: unifiedResult.envelope });
189
216
 
@@ -10,6 +10,7 @@ const {
10
10
  includeDebugForRequest
11
11
  } = require('../utils/validation-run-request');
12
12
  const testHelpers = require('../utils/external-system-test-helpers');
13
+ const { withStageOnValidationRunBody } = require('../utils/validation-run-stage');
13
14
 
14
15
  /**
15
16
  * @param {Object} params
@@ -52,7 +53,7 @@ async function buildUnifiedValidationBody(params) {
52
53
  })
53
54
  : undefined;
54
55
 
55
- return buildExternalDataSourceValidationRequest({
56
+ const body = buildExternalDataSourceValidationRequest({
56
57
  systemKey,
57
58
  datasourceKey,
58
59
  runType,
@@ -62,6 +63,7 @@ async function buildUnifiedValidationBody(params) {
62
63
  explain: options.verbose === true,
63
64
  e2eOptions: e2eOptions && Object.keys(e2eOptions).length > 0 ? e2eOptions : undefined
64
65
  });
66
+ return withStageOnValidationRunBody(body, runType);
65
67
  }
66
68
 
67
69
  module.exports = { buildUnifiedValidationBody };
@@ -4,40 +4,73 @@
4
4
  * @version 2.0.0
5
5
  */
6
6
 
7
- const path = require('path');
8
7
  const { getIntegrationPath } = require('../utils/paths');
9
8
  const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
10
9
  const { loadConfigFile } = require('../utils/config-format');
10
+ const {
11
+ datasourceIdentifierMatchesFile,
12
+ resolveDatasourceFilePath
13
+ } = require('./datasource-identifier');
14
+
15
+ /**
16
+ * @param {string} appPath
17
+ * @param {string} schemaBasePath
18
+ * @param {string[]} datasourceFiles
19
+ * @param {string} identifier
20
+ * @returns {string|null}
21
+ */
22
+ function findDatasourceFileByIdentifier(appPath, schemaBasePath, datasourceFiles, identifier) {
23
+ const fsSync = require('fs');
24
+ for (const fileRef of datasourceFiles) {
25
+ if (!fileRef || typeof fileRef !== 'string') {
26
+ continue;
27
+ }
28
+ const fullPath = resolveDatasourceFilePath(appPath, schemaBasePath, fileRef);
29
+ if (!fsSync.existsSync(fullPath)) {
30
+ continue;
31
+ }
32
+ try {
33
+ const parsed = loadConfigFile(fullPath);
34
+ if (datasourceIdentifierMatchesFile(identifier, fileRef, parsed)) {
35
+ return fileRef;
36
+ }
37
+ } catch {
38
+ // skip unreadable or invalid files
39
+ }
40
+ }
41
+ return null;
42
+ }
11
43
 
12
44
  /**
13
45
  * @param {string} appKey
14
46
  * @param {string} datasourceKey
15
47
  * @param {Function} findDatasourceFileByKey - From integration-context
16
- * @returns {{ datasource: Object, datasourcePath: string }}
48
+ * @returns {{ datasource: Object, datasourcePath: string, datasourceKey: string }}
17
49
  */
18
50
  function loadDatasourceForApp(appKey, datasourceKey, findDatasourceFileByKey) {
19
51
  const appPath = getIntegrationPath(appKey);
20
52
  const config = loadConfigFile(resolveApplicationConfigPath(appPath));
21
53
  const schemaBasePath = config.externalIntegration?.schemaBasePath || './';
22
54
  const datasourceFiles = config.externalIntegration?.dataSources || [];
23
- let datasourceFile = datasourceFiles.find(f => {
24
- const base = path.basename(f, path.extname(f));
25
- return base === datasourceKey || base.includes(datasourceKey);
26
- });
55
+ let datasourceFile = findDatasourceFileByIdentifier(
56
+ appPath,
57
+ schemaBasePath,
58
+ datasourceFiles,
59
+ datasourceKey
60
+ );
27
61
  if (!datasourceFile) {
28
62
  datasourceFile = findDatasourceFileByKey(appPath, schemaBasePath, datasourceFiles, datasourceKey);
29
63
  }
30
64
  if (!datasourceFile) {
31
65
  throw new Error(`Datasource '${datasourceKey}' not found in application config`);
32
66
  }
33
- const datasourcePath = path.isAbsolute(schemaBasePath)
34
- ? path.join(schemaBasePath, datasourceFile)
35
- : path.join(appPath, schemaBasePath, datasourceFile);
67
+ const datasourcePath = resolveDatasourceFilePath(appPath, schemaBasePath, datasourceFile);
36
68
  const datasource = loadConfigFile(datasourcePath);
37
- if (datasource.key !== datasourceKey) {
38
- throw new Error(`Datasource key mismatch: file has '${datasource.key}', expected '${datasourceKey}'`);
69
+ const canonicalKey = datasource.key;
70
+ if (!canonicalKey || typeof canonicalKey !== 'string') {
71
+ throw new Error(`Datasource file is missing a valid key: ${datasourcePath}`);
39
72
  }
40
- return { datasource, datasourcePath };
73
+ return { datasource, datasourcePath, datasourceKey: canonicalKey };
41
74
  }
42
75
 
43
76
  module.exports = { loadDatasourceForApp };
@@ -18,6 +18,7 @@ const { buildUnifiedValidationBody } = require('./unified-validation-run-body');
18
18
  const { postValidationRunAndOptionalPoll } = require('./unified-validation-run-post');
19
19
  const { requireBearerForDataplanePipeline } = require('../utils/token-manager');
20
20
  const logger = require('../utils/logger');
21
+ const { buildMinimalUploadSyncOptions } = require('../utils/upload-sync-options');
21
22
 
22
23
  function _resourceTypeFromDatasource(datasource) {
23
24
  return datasource && typeof datasource === 'object' && typeof datasource.resourceType === 'string'
@@ -59,7 +60,7 @@ async function maybeSyncDatasourceToDataplane({ options, authConfig, systemKey }
59
60
  // are updated before validation runs. Datasource-only publish can still fail DP-SEC-013 when the
60
61
  // dataplane system permissions[] are stale.
61
62
  const { uploadExternalSystem } = require('../commands/upload');
62
- await uploadExternalSystem(systemKey, { minimal: true });
63
+ await uploadExternalSystem(systemKey, buildMinimalUploadSyncOptions(options));
63
64
  logger.log(formatSuccessLine('Sync complete'));
64
65
  }
65
66
 
@@ -71,6 +72,40 @@ function loadDatasourceForAppExport(appKey, datasourceKey) {
71
72
  return loadDatasourceForApp(appKey, datasourceKey, findDatasourceFileByKey);
72
73
  }
73
74
 
75
+ /**
76
+ * @async
77
+ * @param {string} appKey
78
+ * @param {Object} options
79
+ * @returns {Promise<{ authConfig: Object, dataplaneUrl: string }>}
80
+ */
81
+ async function resolveValidationAuth(appKey, options) {
82
+ if (options.authConfig && (options.dataplaneUrl || options.dataplane)) {
83
+ return {
84
+ authConfig: options.authConfig,
85
+ dataplaneUrl: options.dataplaneUrl || options.dataplane
86
+ };
87
+ }
88
+ const configObj = await getConfig();
89
+ const auth = await setupIntegrationTestAuth(appKey, options, configObj);
90
+ return { authConfig: auth.authConfig, dataplaneUrl: auth.dataplaneUrl };
91
+ }
92
+
93
+ /**
94
+ * @async
95
+ * @param {string} datasourceKey
96
+ * @param {Object} options
97
+ * @returns {Promise<Object>}
98
+ */
99
+ async function prepareValidationRunContext(datasourceKey, options) {
100
+ const runType = options.runType || 'test';
101
+ const { appKey } = await resolveAppKeyForDatasource(datasourceKey, options.app);
102
+ const systemKey = await getSystemKeyFromAppKey(appKey);
103
+ const { datasource } = loadDatasourceForAppExport(appKey, datasourceKey);
104
+ const { authConfig, dataplaneUrl } = await resolveValidationAuth(appKey, options);
105
+ await maybeSyncDatasourceToDataplane({ options, authConfig, systemKey });
106
+ return { runType, systemKey, datasource, authConfig, dataplaneUrl };
107
+ }
108
+
74
109
  /**
75
110
  * Run unified validation for one persisted datasource.
76
111
  * @async
@@ -83,41 +118,35 @@ async function runUnifiedDatasourceValidation(datasourceKey, options) {
83
118
  if (!datasourceKey || typeof datasourceKey !== 'string') {
84
119
  throw new Error('Datasource key is required');
85
120
  }
86
- const runType = options.runType || 'test';
87
- const { appKey } = await resolveAppKeyForDatasource(datasourceKey, options.app);
88
- const systemKey = await getSystemKeyFromAppKey(appKey);
89
- const { datasource } = loadDatasourceForAppExport(appKey, datasourceKey);
90
-
91
- const configObj = await getConfig();
92
- const { authConfig, dataplaneUrl } = await setupIntegrationTestAuth(appKey, options, configObj);
93
-
94
- await maybeSyncDatasourceToDataplane({ options, authConfig, systemKey });
95
121
 
122
+ const ctx = await prepareValidationRunContext(datasourceKey, options);
96
123
  const useAsync = options.noAsync ? false : options.async !== false;
97
124
  const timeoutMs = parseInt(String(options.timeout || '30000'), 10) || 30000;
98
-
99
125
  const body = await buildUnifiedValidationBody({
100
- systemKey,
126
+ systemKey: ctx.systemKey,
101
127
  datasourceKey,
102
- runType,
103
- datasource,
128
+ runType: ctx.runType,
129
+ datasource: ctx.datasource,
104
130
  payloadPath: options.payload,
105
131
  useAsync,
106
132
  options
107
133
  });
108
134
 
109
135
  const result = await postValidationRunAndOptionalPoll({
110
- dataplaneUrl,
111
- authConfig,
136
+ dataplaneUrl: ctx.dataplaneUrl,
137
+ authConfig: ctx.authConfig,
112
138
  body,
113
139
  timeoutMs,
114
140
  useAsync,
115
141
  noAsync: options.noAsync === true || options.async === false,
116
- verbosePoll: options.verbose === true
142
+ verbosePoll: options.verbose === true,
143
+ quietPollUi: options.quietPollUi === true,
144
+ pollSubject: datasourceKey,
145
+ onPollProgress: options.onPollProgress
117
146
  });
118
147
 
119
148
  if (result && result.envelope) {
120
- annotateMissingAutoRbacPermissions(result.envelope, datasourceKey, datasource);
149
+ annotateMissingAutoRbacPermissions(result.envelope, datasourceKey, ctx.datasource);
121
150
  }
122
151
  return result;
123
152
  }
@@ -11,6 +11,11 @@
11
11
  const path = require('path');
12
12
  const fs = require('fs');
13
13
  const pathsUtil = require('../utils/paths');
14
+ const {
15
+ datasourceIdentifierMatchesFile,
16
+ pickIntegrationAppForDatasourceIdentifier,
17
+ collectDatasourceFileRefsMatchingIdentifier
18
+ } = require('./datasource-identifier');
14
19
  const { loadExternalDataSourceSchema } = require('../utils/schema-loader');
15
20
  const { formatValidationErrors } = require('../utils/error-formatter');
16
21
  const { validateFieldReferences } = require('./field-reference-validator');
@@ -46,16 +51,13 @@ function tryResolveAsExistingFile(identifier) {
46
51
  }
47
52
 
48
53
  /**
49
- * Pick integration app folder for a datasource `key` (longest matching prefix wins).
54
+ * Pick integration app folder for a datasource identifier (longest matching prefix wins).
50
55
  * @param {string} datasourceKey
51
56
  * @param {string[]} appNames
52
57
  * @returns {string|null}
53
58
  */
54
59
  function pickIntegrationAppForDatasourceKey(datasourceKey, appNames) {
55
- const matches = appNames.filter((app) => datasourceKey === app || datasourceKey.startsWith(`${app}-`));
56
- if (matches.length === 0) return null;
57
- matches.sort((a, b) => b.length - a.length);
58
- return matches[0];
60
+ return pickIntegrationAppForDatasourceIdentifier(datasourceKey, appNames);
59
61
  }
60
62
 
61
63
  /**
@@ -94,7 +96,7 @@ function collectJsonPathsMatchingDatasourceKey(dir, scanOrder, datasourceKey) {
94
96
  } catch {
95
97
  continue;
96
98
  }
97
- if (parsed && typeof parsed === 'object' && parsed.key === datasourceKey) {
99
+ if (datasourceIdentifierMatchesFile(datasourceKey, name, parsed)) {
98
100
  hits.push(full);
99
101
  }
100
102
  }
@@ -102,21 +104,12 @@ function collectJsonPathsMatchingDatasourceKey(dir, scanOrder, datasourceKey) {
102
104
  }
103
105
 
104
106
  /**
105
- * Find integration JSON whose parsed `key` equals datasourceKey.
107
+ * @param {string} appName
106
108
  * @param {string} datasourceKey
107
- * @returns {string} Absolute file path
109
+ * @returns {string}
108
110
  * @throws {Error}
109
111
  */
110
- function resolvePathFromIntegrationDatasourceKey(datasourceKey) {
111
- const appNames = pathsUtil.listIntegrationAppNames();
112
- const appName = pickIntegrationAppForDatasourceKey(datasourceKey, appNames);
113
- if (!appName) {
114
- throw new Error(
115
- `No integration/<app>/ folder matches datasource key "${datasourceKey}". ` +
116
- 'Pass a path to the JSON file, or use a key whose prefix matches an app under integration/ ' +
117
- '(e.g. app test-e2e-hubspot for key test-e2e-hubspot-users).'
118
- );
119
- }
112
+ function resolvePathFromIntegrationFolderScan(appName, datasourceKey) {
120
113
  const dir = pathsUtil.getIntegrationPath(appName);
121
114
  if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
122
115
  throw new Error(`Integration folder not found: ${dir}`);
@@ -128,16 +121,40 @@ function resolvePathFromIntegrationDatasourceKey(datasourceKey) {
128
121
  const hits = collectJsonPathsMatchingDatasourceKey(dir, scanOrder, datasourceKey);
129
122
  if (hits.length === 0) {
130
123
  throw new Error(
131
- `No datasource JSON with key "${datasourceKey}" under integration/${appName}/. ` +
124
+ `No datasource JSON matching "${datasourceKey}" under integration/${appName}/. ` +
132
125
  `Checked: ${scanOrder.length ? scanOrder.join(', ') : '(no JSON candidates)'}`
133
126
  );
134
127
  }
135
128
  if (hits.length > 1) {
136
- throw new Error(`Ambiguous: multiple files declare key "${datasourceKey}": ${hits.join(', ')}`);
129
+ throw new Error(`Ambiguous: multiple files match "${datasourceKey}": ${hits.join(', ')}`);
137
130
  }
138
131
  return hits[0];
139
132
  }
140
133
 
134
+ /**
135
+ * Find integration JSON whose parsed `key` equals datasourceKey.
136
+ * @param {string} datasourceKey
137
+ * @returns {string} Absolute file path
138
+ * @throws {Error}
139
+ */
140
+ function resolvePathFromIntegrationDatasourceKey(datasourceKey) {
141
+ const appNames = pathsUtil.listIntegrationAppNames();
142
+ const appName = pickIntegrationAppForDatasourceKey(datasourceKey, appNames);
143
+ if (!appName) {
144
+ throw new Error(
145
+ `No integration/<app>/ folder matches datasource identifier "${datasourceKey}". ` +
146
+ 'Pass a path to the JSON file, or use a datasource key or filename whose prefix matches an app under integration/ ' +
147
+ '(e.g. test-e2e-hubspot-companies or test-e2e-hubspot-datasource-companies for app test-e2e-hubspot).'
148
+ );
149
+ }
150
+ const manifestHits = collectDatasourceFileRefsMatchingIdentifier(appName, datasourceKey);
151
+ if (manifestHits.length === 1) {
152
+ const dir = pathsUtil.getIntegrationPath(appName);
153
+ return path.join(dir, manifestHits[0]);
154
+ }
155
+ return resolvePathFromIntegrationFolderScan(appName, datasourceKey);
156
+ }
157
+
141
158
  /**
142
159
  * @param {string} identifier - File path or datasource `key`
143
160
  * @returns {string} Absolute path to JSON file