@aifabrix/builder 2.46.0 → 2.47.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/.npmrc.token +1 -0
  2. package/.temp/jest-protection-convert/run-IFN2nv/.protection/hubspot-companies.json +1 -0
  3. package/.temp/jest-protection-convert/run-IvmKLO/.protection/hubspot-companies.yaml +17 -0
  4. package/.temp/jest-protection-convert/run-SdvblF/.protection/hubspot-companies.json +1 -0
  5. package/.temp/jest-protection-convert/run-b51TsW/.protection/hubspot-companies.json +1 -0
  6. package/.temp/jest-protection-convert/run-fiLmb1/.protection/hubspot-companies.json +1 -0
  7. package/.temp/jest-protection-convert/run-qYGHMU/.protection/hubspot-companies.yaml +17 -0
  8. package/.temp/jest-protection-fixtures/hubspot-Mq5re8/hubspot-companies.yaml +17 -0
  9. package/.temp/jest-protection-fixtures/hubspot-f3u4YL/hubspot-companies.yaml +17 -0
  10. package/.temp/jest-protection-fixtures/hubspot-iQtd1O/hubspot-companies.yaml +17 -0
  11. package/.temp/jest-protection-fixtures/hubspot-mdmz5M/hubspot-companies.yaml +17 -0
  12. package/.temp/jest-protection-fixtures/hubspot-qbKYxQ/hubspot-companies.yaml +17 -0
  13. package/.temp/jest-protection-fixtures/hubspot-yzlJy6/hubspot-companies.yaml +17 -0
  14. package/.temp/validation/summary.txt +5 -0
  15. package/jest.isolated-projects.js +18 -2
  16. package/jest.projects.js +24 -0
  17. package/lib/api/dataplane-health.api.js +19 -7
  18. package/lib/api/lifecycle.api.js +22 -1
  19. package/lib/api/resource-types.api.js +107 -0
  20. package/lib/api/runtime-configuration.api.js +72 -0
  21. package/lib/api/types/resource-types.types.js +45 -0
  22. package/lib/api/user-groups.api.js +9 -9
  23. package/lib/api/users.api.js +8 -8
  24. package/lib/api/wizard.api.js +10 -2
  25. package/lib/app/down.js +58 -26
  26. package/lib/app/index.js +2 -2
  27. package/lib/app/run-resolve-image.js +2 -1
  28. package/lib/app/run-stop-containers.js +113 -0
  29. package/lib/app/run.js +1 -31
  30. package/lib/app/show-display.js +35 -0
  31. package/lib/app/show.js +32 -1
  32. package/lib/build/index.js +17 -5
  33. package/lib/build/miso-controller-npm-sdk-guard.js +25 -0
  34. package/lib/channels/add-channel.js +348 -0
  35. package/lib/channels/channel-artifact-generator.js +141 -0
  36. package/lib/channels/channel-audit.js +101 -0
  37. package/lib/channels/channel-auth-context.js +37 -0
  38. package/lib/channels/channel-credential.js +200 -0
  39. package/lib/channels/channel-display-name.js +64 -0
  40. package/lib/channels/channel-external-system.js +106 -0
  41. package/lib/channels/channel-integration-generator.js +274 -0
  42. package/lib/channels/channel-probe.js +89 -0
  43. package/lib/channels/channel-runtime-resolver.js +152 -0
  44. package/lib/channels/channel-status-display.js +141 -0
  45. package/lib/channels/channel-targets.js +115 -0
  46. package/lib/cli/index.js +18 -4
  47. package/lib/cli/infra-guided-auth.js +36 -12
  48. package/lib/cli/setup-app.js +54 -3
  49. package/lib/cli/setup-infra-up-dataplane-action.js +2 -2
  50. package/lib/cli/setup-infra-up-platform-action.js +2 -1
  51. package/lib/cli/setup-npm.js +59 -0
  52. package/lib/cli/setup-platform.js +1 -1
  53. package/lib/cli/setup-secrets.js +1 -1
  54. package/lib/cli/setup-utility-help-after.js +4 -1
  55. package/lib/cli/setup-utility-resolve.js +8 -5
  56. package/lib/commands/app-logs.js +54 -26
  57. package/lib/commands/app.js +41 -0
  58. package/lib/commands/auth-status-dataplane-version.js +97 -61
  59. package/lib/commands/auth-status.js +2 -7
  60. package/lib/commands/channel.js +58 -0
  61. package/lib/commands/datasource.js +1 -1
  62. package/lib/commands/identity-apply-sync.js +49 -8
  63. package/lib/commands/identity-group-cli.js +66 -26
  64. package/lib/commands/identity-membership-role-cache.js +58 -37
  65. package/lib/commands/identity-shared.js +22 -1
  66. package/lib/commands/identity-user-cli.js +80 -34
  67. package/lib/commands/login-device.js +11 -3
  68. package/lib/commands/login.js +8 -3
  69. package/lib/commands/npm-config.js +103 -0
  70. package/lib/commands/repair-auth-apply.js +30 -4
  71. package/lib/commands/repair-auth-config.js +8 -13
  72. package/lib/commands/repair-datasource-com-v2.js +15 -0
  73. package/lib/commands/repair-datasource-service-contract.js +206 -0
  74. package/lib/commands/repair-datasource.js +53 -18
  75. package/lib/commands/repair-env-template.js +12 -12
  76. package/lib/commands/repair-openapi-sync.js +26 -4
  77. package/lib/commands/repair-steps.js +28 -13
  78. package/lib/commands/repair-system-alignment.js +7 -4
  79. package/lib/commands/resource-type.js +371 -0
  80. package/lib/commands/setup-modes.js +1 -1
  81. package/lib/commands/setup-prompts-platform-mode.js +24 -21
  82. package/lib/commands/test-trust-external.js +39 -90
  83. package/lib/commands/up-common.js +33 -1
  84. package/lib/commands/up-dataplane.js +8 -4
  85. package/lib/commands/upload.js +24 -3
  86. package/lib/commands/verify-trust-command-action.js +4 -1
  87. package/lib/commands/verify-trust-external.js +12 -20
  88. package/lib/core/config-setup-platform-mode.js +3 -3
  89. package/lib/core/setup-platform-mode.js +41 -6
  90. package/lib/datasource/capability/capability-diff-slice.js +2 -2
  91. package/lib/datasource/capability/capability-manifest.js +254 -0
  92. package/lib/datasource/capability/capability-resolve.js +3 -4
  93. package/lib/datasource/capability/copy-operations.js +5 -3
  94. package/lib/datasource/capability/remove-operations.js +2 -1
  95. package/lib/datasource/capability/run-capability-copy.js +32 -4
  96. package/lib/datasource/capability/run-capability-diff.js +3 -3
  97. package/lib/datasource/capability/run-capability-dimension.js +2 -4
  98. package/lib/datasource/capability/run-capability-edit.js +4 -7
  99. package/lib/datasource/capability/run-capability-relate.js +2 -4
  100. package/lib/datasource/capability/run-capability-remove.js +2 -4
  101. package/lib/datasource/capability/validate-capability-slice.js +2 -1
  102. package/lib/datasource/datasource-identifier.js +2 -1
  103. package/lib/datasource/datasource-validate-display.js +1 -1
  104. package/lib/datasource/field-reference-validator.js +24 -3
  105. package/lib/datasource/orphan-profiles-validator.js +98 -0
  106. package/lib/datasource/validate.js +41 -23
  107. package/lib/external-system/download-helpers.js +87 -0
  108. package/lib/external-system/download.js +61 -122
  109. package/lib/external-system/generator.js +1 -59
  110. package/lib/external-system/test.js +10 -15
  111. package/lib/generator/builders.js +11 -1
  112. package/lib/generator/external-controller-manifest.js +5 -2
  113. package/lib/generator/split.js +3 -1
  114. package/lib/generator/wizard.js +1 -28
  115. package/lib/identity/fk-reference.js +65 -0
  116. package/lib/identity/identity-apply-core.js +13 -58
  117. package/lib/identity/identity-apply-phases.js +5 -1
  118. package/lib/identity/identity-display.js +243 -0
  119. package/lib/identity/identity-response-helpers.js +52 -0
  120. package/lib/identity/identity-sync-errors.js +62 -0
  121. package/lib/identity/resolve-user-id.js +129 -0
  122. package/lib/identity/username-default.js +31 -0
  123. package/lib/lifecycle/certification-progress.js +2 -2
  124. package/lib/lifecycle/report-display-trust.js +70 -0
  125. package/lib/lifecycle/report-display.js +14 -13
  126. package/lib/resolvers/datasource-resolver.js +2 -3
  127. package/lib/resolvers/resource-type-file.js +159 -0
  128. package/lib/schema/application-schema.json +16 -0
  129. package/lib/schema/deployment-rules.yaml +2 -1
  130. package/lib/schema/external-datasource.schema.json +372 -58
  131. package/lib/schema/external-system.schema.json +331 -2
  132. package/lib/schema/type/document-storage.json +9 -5
  133. package/lib/utils/api.js +2 -1
  134. package/lib/utils/app-run-containers.js +25 -2
  135. package/lib/utils/authentication-shape.js +224 -0
  136. package/lib/utils/build-copy.js +36 -5
  137. package/lib/utils/build-resolve-image.js +66 -12
  138. package/lib/utils/celery-companion-apps.js +53 -0
  139. package/lib/utils/cli-utils.js +4 -0
  140. package/lib/utils/compose-celery-worker.js +23 -0
  141. package/lib/utils/compose-generate-docker-compose.js +9 -1
  142. package/lib/utils/configuration-env-resolver.js +36 -0
  143. package/lib/utils/controller-url.js +4 -1
  144. package/lib/utils/credential-secrets-env-kv.js +216 -0
  145. package/lib/utils/credential-secrets-env.js +52 -262
  146. package/lib/utils/dataplane-resolver.js +4 -115
  147. package/lib/utils/declarative-url-ports.js +24 -1
  148. package/lib/utils/dev-platform-tls.js +148 -0
  149. package/lib/utils/docker-build.js +1 -4
  150. package/lib/utils/external-env-template.js +10 -3
  151. package/lib/utils/external-system-validators.js +8 -2
  152. package/lib/utils/health-check-request.js +114 -0
  153. package/lib/utils/health-check-url.js +3 -1
  154. package/lib/utils/health-check.js +1 -113
  155. package/lib/utils/help-builder.js +3 -0
  156. package/lib/utils/keycloak-realm-ready.js +5 -1
  157. package/lib/utils/localhost-public-host.js +31 -0
  158. package/lib/utils/npm-user-config.js +145 -0
  159. package/lib/utils/paths.js +25 -0
  160. package/lib/utils/platform-controller-url.js +58 -4
  161. package/lib/utils/platform-kv-ref.js +57 -0
  162. package/lib/utils/runtime-configuration-download.js +189 -0
  163. package/lib/utils/runtime-configuration-values.js +119 -0
  164. package/lib/utils/schema-loader.js +19 -4
  165. package/lib/utils/url-declarative-public-base.js +33 -25
  166. package/lib/utils/worker-sidecars.js +142 -0
  167. package/lib/validation/external-manifest-validator.js +2 -0
  168. package/lib/validation/runtime-configuration-rules.js +98 -0
  169. package/lib/validation/validate-display-dimensions.js +154 -0
  170. package/lib/validation/validate-display-sections.js +276 -0
  171. package/lib/validation/validate-display-steps.js +67 -0
  172. package/lib/validation/validate-display.js +24 -402
  173. package/package.json +1 -1
  174. package/templates/applications/dataplane/application.yaml +5 -0
  175. package/templates/applications/dataplane/env.template +26 -13
  176. package/templates/applications/dataplane/rbac.yaml +108 -1
  177. package/templates/applications/miso-controller/application.yaml +2 -2
  178. package/templates/applications/miso-controller/env.template +37 -122
  179. package/templates/applications/miso-controller/rbac.yaml +28 -13
  180. package/templates/channels/chatgpt/install.template.md.hbs +49 -0
  181. package/templates/channels/chatgpt/manifest.template.json.hbs +9 -0
  182. package/templates/channels/microsoft-copilot/install.template.md.hbs +49 -0
  183. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +8 -0
  184. package/templates/channels/slack/install.template.md.hbs +49 -0
  185. package/templates/channels/slack/manifest.template.json.hbs +66 -0
  186. package/templates/channels/teams-bot/install.template.md.hbs +49 -0
  187. package/templates/channels/teams-bot/manifest.template.json.hbs +13 -0
  188. package/templates/channels/web-embed/install.template.md.hbs +49 -0
  189. package/templates/channels/web-embed/manifest.template.json.hbs +8 -0
  190. package/templates/python/docker-compose.hbs +56 -0
  191. package/templates/typescript/docker-compose.hbs +52 -0
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Worker sidecar resolution from application.yaml build settings.
3
+ *
4
+ * @fileoverview Generic worker/beat companions driven by build.worker and imageRun* fields
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { buildDefaultLocalContainerName } = require('./environment-scoped-resources');
10
+
11
+ /**
12
+ * @param {object|null|undefined} build
13
+ * @param {string} field
14
+ * @returns {string|null}
15
+ */
16
+ function readImageRunCommand(build, field) {
17
+ if (!build || typeof build !== 'object') {
18
+ return null;
19
+ }
20
+ const raw = build[field];
21
+ if (typeof raw !== 'string') {
22
+ return null;
23
+ }
24
+ const trimmed = raw.trim();
25
+ return trimmed ? trimmed : null;
26
+ }
27
+
28
+ /**
29
+ * @param {object|null|undefined} appConfig
30
+ * @returns {boolean}
31
+ */
32
+ function isWorkerStackEnabled(appConfig) {
33
+ if (!appConfig || typeof appConfig !== 'object') {
34
+ return false;
35
+ }
36
+ return appConfig.build?.worker === true;
37
+ }
38
+
39
+ /**
40
+ * @param {string} appKey
41
+ * @param {object|null|undefined} appConfig
42
+ * @returns {{
43
+ * enabled: boolean,
44
+ * worker: { key: string, command: string }|null,
45
+ * beat: { key: string, command: string }|null,
46
+ * companions: Array<{ role: string, key: string, command: string }>
47
+ * }}
48
+ */
49
+ function resolveWorkerSidecars(appKey, appConfig) {
50
+ const empty = { enabled: false, worker: null, beat: null, companions: [] };
51
+ if (!isWorkerStackEnabled(appConfig)) {
52
+ return empty;
53
+ }
54
+
55
+ const build = appConfig.build && typeof appConfig.build === 'object' ? appConfig.build : {};
56
+ const workerCommand = readImageRunCommand(build, 'imageRunWorker');
57
+ const beatCommand = readImageRunCommand(build, 'imageRunBeat');
58
+ const companions = [];
59
+
60
+ if (workerCommand) {
61
+ companions.push({ role: 'worker', key: `${appKey}-worker`, command: workerCommand });
62
+ }
63
+ if (beatCommand) {
64
+ companions.push({ role: 'beat', key: `${appKey}-beat`, command: beatCommand });
65
+ }
66
+
67
+ if (companions.length === 0) {
68
+ return empty;
69
+ }
70
+
71
+ const worker = companions.find((entry) => entry.role === 'worker') || null;
72
+ const beat = companions.find((entry) => entry.role === 'beat') || null;
73
+
74
+ return {
75
+ enabled: true,
76
+ worker,
77
+ beat,
78
+ companions
79
+ };
80
+ }
81
+
82
+ /**
83
+ * @param {string} appKey
84
+ * @param {object|null|undefined} appConfig
85
+ * @returns {string[]}
86
+ */
87
+ function getWorkerCompanionAppKeys(appKey, appConfig) {
88
+ return resolveWorkerSidecars(appKey, appConfig).companions.map((entry) => entry.key);
89
+ }
90
+
91
+ /**
92
+ * @param {string} appKey
93
+ * @param {object} appConfig
94
+ * @param {{ devId: string|number, idNum: number }} ids
95
+ * @returns {{
96
+ * enabled: boolean,
97
+ * worker?: { key: string, containerName: string, command: string },
98
+ * beat?: { key: string, containerName: string, command: string }
99
+ * }}
100
+ */
101
+ function buildWorkerSidecarComposeContext(appKey, appConfig, ids) {
102
+ const sidecars = resolveWorkerSidecars(appKey, appConfig);
103
+ if (!sidecars.enabled) {
104
+ return { enabled: false };
105
+ }
106
+
107
+ const context = { enabled: true };
108
+ if (sidecars.worker) {
109
+ context.worker = {
110
+ key: sidecars.worker.key,
111
+ containerName: buildDefaultLocalContainerName(sidecars.worker.key, ids.devId, ids.idNum),
112
+ command: sidecars.worker.command
113
+ };
114
+ }
115
+ if (sidecars.beat) {
116
+ context.beat = {
117
+ key: sidecars.beat.key,
118
+ containerName: buildDefaultLocalContainerName(sidecars.beat.key, ids.devId, ids.idNum),
119
+ command: sidecars.beat.command
120
+ };
121
+ }
122
+ return context;
123
+ }
124
+
125
+ /**
126
+ * Azure startupCommand from compose imageRun (drops leading exec).
127
+ *
128
+ * @param {string} imageRun
129
+ * @returns {string}
130
+ */
131
+ function imageRunToStartupCommand(imageRun) {
132
+ return imageRun.replace(/^exec\s+/i, '').trim();
133
+ }
134
+
135
+ module.exports = {
136
+ readImageRunCommand,
137
+ isWorkerStackEnabled,
138
+ resolveWorkerSidecars,
139
+ getWorkerCompanionAppKeys,
140
+ buildWorkerSidecarComposeContext,
141
+ imageRunToStartupCommand
142
+ };
@@ -16,6 +16,7 @@ const path = require('path');
16
16
  const { formatValidationErrors } = require('../utils/error-formatter');
17
17
  const { validateFieldReferences } = require('../datasource/field-reference-validator');
18
18
  const { validateAbac } = require('../datasource/abac-validator');
19
+ const { validateManifestRuntimeConfiguration } = require('./runtime-configuration-rules');
19
20
 
20
21
  /**
21
22
  * Load and normalize external-system + external-datasource schema objects for manifest AJV.
@@ -247,6 +248,7 @@ async function validateControllerManifest(manifest) {
247
248
  validateInlineSystem(manifest, ajv, externalSystemSchema, errors);
248
249
  validateDatasources(manifest, ajv, externalDatasourceSchema, errors, warnings);
249
250
  validateDatasourceSystemKeyAlignment(manifest, errors);
251
+ errors.push(...validateManifestRuntimeConfiguration(manifest));
250
252
  validateConditionalRequirements(manifest, errors, warnings);
251
253
  validateRequiredFields(manifest, errors);
252
254
 
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Runtime configuration validation for external system manifests.
3
+ *
4
+ * @fileoverview Datasource override rules (v1: no keyvault overrides)
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ /**
10
+ * @param {Array<{ name?: string, location?: string }>|undefined} systemConfiguration
11
+ * @returns {Map<string, { name?: string, location?: string }>}
12
+ */
13
+ function systemConfigDefsByName(systemConfiguration) {
14
+ const defsByName = new Map();
15
+ for (const item of systemConfiguration || []) {
16
+ if (item && item.name) {
17
+ defsByName.set(String(item.name).trim(), item);
18
+ }
19
+ }
20
+ return defsByName;
21
+ }
22
+
23
+ /**
24
+ * @param {string} prefix
25
+ * @param {Map<string, { name?: string, location?: string }>} defsByName
26
+ * @param {{ name?: string, value?: string }} override
27
+ * @returns {string[]}
28
+ */
29
+ function validateOverrideEntry(prefix, defsByName, override) {
30
+ if (!override || !override.name) {
31
+ return [];
32
+ }
33
+ const name = String(override.name).trim();
34
+ const def = defsByName.get(name);
35
+ if (!def) {
36
+ return [`${prefix}configuration override '${name}' is not declared on the parent system.`];
37
+ }
38
+ const errors = [];
39
+ if (String(def.location || 'variable').toLowerCase() === 'keyvault') {
40
+ errors.push(
41
+ `${prefix}configuration override '${name}' targets a keyvault system definition (not allowed in v1).`
42
+ );
43
+ }
44
+ const extraKeys = Object.keys(override).filter((key) => key !== 'name' && key !== 'value');
45
+ if (extraKeys.length > 0) {
46
+ errors.push(
47
+ `${prefix}configuration override '${name}' must contain only name and value (found: ${extraKeys.join(', ')}).`
48
+ );
49
+ }
50
+ return errors;
51
+ }
52
+
53
+ /**
54
+ * Validates datasource configuration[] overrides against parent system definitions.
55
+ *
56
+ * @param {Array<{ name?: string, location?: string }>|undefined} systemConfiguration
57
+ * @param {Array<{ name?: string, value?: string }>|undefined} datasourceConfiguration
58
+ * @param {string} [datasourceKey] - For error prefixes
59
+ * @returns {string[]} Validation errors
60
+ */
61
+ function validateDatasourceConfigurationOverrides(
62
+ systemConfiguration,
63
+ datasourceConfiguration,
64
+ datasourceKey
65
+ ) {
66
+ if (!Array.isArray(datasourceConfiguration) || datasourceConfiguration.length === 0) {
67
+ return [];
68
+ }
69
+ const prefix = datasourceKey ? `Datasource '${datasourceKey}': ` : '';
70
+ const defsByName = systemConfigDefsByName(systemConfiguration);
71
+ return datasourceConfiguration.flatMap((override) =>
72
+ validateOverrideEntry(prefix, defsByName, override)
73
+ );
74
+ }
75
+
76
+ /**
77
+ * @param {{ system?: { configuration?: unknown[] }, dataSources?: Array<{ key?: string, configuration?: unknown[] }> }} manifest
78
+ * @returns {string[]}
79
+ */
80
+ function validateManifestRuntimeConfiguration(manifest) {
81
+ const errors = [];
82
+ const systemConfiguration = manifest?.system?.configuration;
83
+ for (const ds of manifest?.dataSources || []) {
84
+ errors.push(
85
+ ...validateDatasourceConfigurationOverrides(
86
+ systemConfiguration,
87
+ ds?.configuration,
88
+ ds?.key
89
+ )
90
+ );
91
+ }
92
+ return errors;
93
+ }
94
+
95
+ module.exports = {
96
+ validateDatasourceConfigurationOverrides,
97
+ validateManifestRuntimeConfiguration
98
+ };
@@ -0,0 +1,154 @@
1
+ /**
2
+ * @fileoverview Dimensions (ABAC) display helpers for validate-display.
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { loadConfigFile } = require('../utils/config-format');
10
+ const { flattenRootDimensionsForDisplay } = require('./dimension-display-helpers');
11
+ const { formatDatasourceListRow } = require('../utils/cli-test-layout-chalk');
12
+ const logger = require('../utils/logger');
13
+ const {
14
+ logSectionTitle,
15
+ logWarnRow,
16
+ logDimLine
17
+ } = require('./validate-display-log-helpers');
18
+
19
+ /**
20
+ * True when datasource is orchestration-only (no ABAC dimensions contract).
21
+ * @param {string} filePath - Path to datasource JSON
22
+ * @returns {boolean}
23
+ */
24
+ function isOrchestrationOnlyDatasource(filePath) {
25
+ try {
26
+ const parsed = loadConfigFile(filePath);
27
+ return String(parsed?.entityType || '').trim() === 'none';
28
+ } catch {
29
+ return false;
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Extracts dimensions from a datasource file for CLI display.
35
+ * Local bindings become `metadata.<field>`; FK bindings become `fk:<fk>→<dimension>...` plus optional `(actor: ...)`.
36
+ * @function extractDimensionsFromDatasource
37
+ * @param {string} filePath - Path to datasource file
38
+ * @returns {Object} Dimensions info { dimensions: Object, dimensionKeys: string[], hasDimensions: boolean }
39
+ */
40
+ function extractDimensionsFromDatasource(filePath) {
41
+ try {
42
+ const parsed = loadConfigFile(filePath);
43
+
44
+ const rootFlat = flattenRootDimensionsForDisplay(parsed.dimensions);
45
+ const abacDimensions = parsed.abac?.dimensions || {};
46
+ const allDimensions = { ...rootFlat, ...abacDimensions };
47
+ const dimensionKeys = Object.keys(allDimensions);
48
+
49
+ return {
50
+ dimensions: allDimensions,
51
+ dimensionKeys,
52
+ hasDimensions: dimensionKeys.length > 0
53
+ };
54
+ } catch {
55
+ return { dimensions: {}, dimensionKeys: [], hasDimensions: false };
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Displays Dimensions (ABAC) validation results for datasources
61
+ * @function displayDimensionsValidation
62
+ * @param {Array} externalFiles - External files validation results
63
+ * @returns {Array} Warnings to add to aggregated warnings
64
+ */
65
+ function displayDimensionsValidation(externalFiles) {
66
+ if (!externalFiles || externalFiles.length === 0) {
67
+ return [];
68
+ }
69
+
70
+ const datasourceFiles = externalFiles.filter(f =>
71
+ f.type === 'datasource' || f.type === 'external-datasource'
72
+ );
73
+
74
+ if (datasourceFiles.length === 0) {
75
+ return [];
76
+ }
77
+
78
+ logSectionTitle('Dimensions (ABAC)');
79
+
80
+ const warnings = [];
81
+ let anyDatasourceHasDimensions = false;
82
+
83
+ datasourceFiles.forEach(file => {
84
+ if (file.path && isOrchestrationOnlyDatasource(file.path)) {
85
+ logger.log(formatDatasourceListRow('ok', file.file, file.type));
86
+ logDimLine('orchestration-only (entityType=none) — dimensions N/A');
87
+ return;
88
+ }
89
+ const dimensionsInfo = extractDimensionsFromDatasource(file.path);
90
+
91
+ if (dimensionsInfo.hasDimensions) {
92
+ anyDatasourceHasDimensions = true;
93
+ logger.log(formatDatasourceListRow('ok', file.file, file.type));
94
+ dimensionsInfo.dimensionKeys.forEach(key => {
95
+ const mapping = dimensionsInfo.dimensions[key];
96
+ logDimLine(`${key} → ${mapping}`);
97
+ });
98
+ } else {
99
+ logWarnRow(`${file.file} — no dimensions configured`);
100
+ warnings.push(`${file.file} - no dimensions configured, ABAC filtering disabled`);
101
+ }
102
+ });
103
+
104
+ if (!anyDatasourceHasDimensions && datasourceFiles.some(
105
+ (file) => !(file.path && isOrchestrationOnlyDatasource(file.path))
106
+ )) {
107
+ logWarnRow('No dimensions configured — ABAC filtering disabled for all datasources');
108
+ }
109
+
110
+ return warnings;
111
+ }
112
+
113
+ /**
114
+ * Displays dimensions validation for datasources
115
+ * @function displayDimensionsStep
116
+ * @param {Array} datasourceFiles - Datasource files
117
+ * @returns {void}
118
+ */
119
+ function displayDimensionsStep(datasourceFiles) {
120
+ if (datasourceFiles.length === 0) {
121
+ return;
122
+ }
123
+
124
+ logSectionTitle('Dimensions (ABAC)');
125
+ datasourceFiles.forEach(file => {
126
+ try {
127
+ const dsPath = file.path || file.file;
128
+ if (dsPath && isOrchestrationOnlyDatasource(dsPath)) {
129
+ logger.log(formatDatasourceListRow('ok', file.file, file.type));
130
+ logDimLine('orchestration-only (entityType=none) — dimensions N/A');
131
+ return;
132
+ }
133
+ const dimensionsInfo = extractDimensionsFromDatasource(dsPath);
134
+ if (dimensionsInfo.hasDimensions) {
135
+ logger.log(formatDatasourceListRow('ok', file.file, file.type));
136
+ dimensionsInfo.dimensionKeys.forEach(key => {
137
+ const mapping = dimensionsInfo.dimensions[key];
138
+ logDimLine(`${key} → ${mapping}`);
139
+ });
140
+ } else {
141
+ logWarnRow(`${file.file} — no dimensions configured`);
142
+ }
143
+ } catch {
144
+ // Skip if file path not available
145
+ }
146
+ });
147
+ }
148
+
149
+ module.exports = {
150
+ isOrchestrationOnlyDatasource,
151
+ extractDimensionsFromDatasource,
152
+ displayDimensionsValidation,
153
+ displayDimensionsStep
154
+ };
@@ -0,0 +1,276 @@
1
+ /**
2
+ * @fileoverview Section renderers for validate-display (application, files, RBAC, manifest).
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const chalk = require('chalk');
10
+ const logger = require('../utils/logger');
11
+ const {
12
+ headerKeyValue,
13
+ formatDatasourceListRow,
14
+ formatBlockingError,
15
+ successGlyph,
16
+ failureGlyph
17
+ } = require('../utils/cli-test-layout-chalk');
18
+ const {
19
+ logSectionTitle,
20
+ logOkRowRest,
21
+ logErrorDetail
22
+ } = require('./validate-display-log-helpers');
23
+
24
+ /**
25
+ * Displays application validation results
26
+ * @function displayApplicationValidation
27
+ * @param {Object} application - Application validation result
28
+ */
29
+ function displayApplicationValidation(application) {
30
+ if (!application) {
31
+ return;
32
+ }
33
+
34
+ logSectionTitle('Application');
35
+ if (application.valid) {
36
+ logOkRowRest('Application configuration is valid');
37
+ } else {
38
+ logger.log(` ${failureGlyph()} ${chalk.red('Application configuration has errors:')}`);
39
+ if (application.errors && application.errors.length > 0) {
40
+ application.errors.forEach(error => {
41
+ logErrorDetail(error);
42
+ });
43
+ }
44
+ }
45
+ if (application.warnings && application.warnings.length > 0) {
46
+ application.warnings.forEach(warning => {
47
+ logger.log(` ${chalk.yellow('⚠')} ${chalk.white(warning)}`);
48
+ });
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Displays external files validation results
54
+ * @function displayExternalFilesValidation
55
+ * @param {Array} externalFiles - External files validation results
56
+ */
57
+ function displayExternalFilesValidation(externalFiles) {
58
+ if (!externalFiles || externalFiles.length === 0) {
59
+ return;
60
+ }
61
+
62
+ logSectionTitle('External integration files');
63
+ externalFiles.forEach(file => {
64
+ if (file.valid) {
65
+ logger.log(formatDatasourceListRow('ok', file.file, file.type));
66
+ } else {
67
+ logger.log(formatDatasourceListRow('fail', file.file, file.type));
68
+ file.errors.forEach(error => {
69
+ logErrorDetail(error);
70
+ });
71
+ }
72
+ if (file.warnings && file.warnings.length > 0) {
73
+ file.warnings.forEach(warning => {
74
+ logger.log(` ${chalk.yellow('⚠')} ${chalk.white(warning)}`);
75
+ });
76
+ }
77
+ });
78
+ }
79
+
80
+ /**
81
+ * Displays RBAC validation results
82
+ * @function displayRbacValidation
83
+ * @param {Object} rbac - RBAC validation result
84
+ */
85
+ function displayRbacValidation(rbac) {
86
+ if (!rbac) {
87
+ return;
88
+ }
89
+
90
+ logSectionTitle('RBAC configuration');
91
+ if (rbac.valid) {
92
+ logOkRowRest('RBAC configuration is valid');
93
+ } else {
94
+ logger.log(` ${failureGlyph()} ${chalk.red('RBAC configuration has errors:')}`);
95
+ rbac.errors.forEach(error => {
96
+ logErrorDetail(error);
97
+ });
98
+ }
99
+ if (rbac.warnings && rbac.warnings.length > 0) {
100
+ rbac.warnings.forEach(warning => {
101
+ logger.log(` ${chalk.yellow('⚠')} ${chalk.white(warning)}`);
102
+ });
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Displays file validation results (for direct file validation)
108
+ * @function displayFileValidation
109
+ * @param {Object} result - Validation result
110
+ */
111
+ function displayFileValidation(result) {
112
+ if (!result.file) {
113
+ return;
114
+ }
115
+
116
+ logSectionTitle('File');
117
+ logger.log(` ${headerKeyValue('Path:', result.file)}`);
118
+ logger.log(` ${headerKeyValue('Type:', String(result.type))}`);
119
+ if (result.valid) {
120
+ logOkRowRest('File is valid');
121
+ } else {
122
+ logger.log(` ${failureGlyph()} ${chalk.red('File has errors:')}`);
123
+ result.errors.forEach(error => {
124
+ logErrorDetail(error);
125
+ });
126
+ }
127
+ if (result.warnings && result.warnings.length > 0) {
128
+ result.warnings.forEach(warning => {
129
+ logger.log(` ${chalk.yellow('⚠')} ${chalk.white(warning)}`);
130
+ });
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Displays aggregated warnings
136
+ * @function displayAggregatedWarnings
137
+ * @param {Array} warnings - Array of warning messages
138
+ */
139
+ function displayAggregatedWarnings(warnings) {
140
+ if (!warnings || warnings.length === 0) {
141
+ return;
142
+ }
143
+
144
+ logSectionTitle('Warnings');
145
+ warnings.forEach(warning => {
146
+ logger.log(` ${chalk.yellow('⚠')} ${chalk.white(warning)}`);
147
+ });
148
+ }
149
+
150
+ /**
151
+ * Displays application validation step
152
+ * @function displayApplicationStep
153
+ * @param {Object} application - Application validation result
154
+ * @returns {void}
155
+ */
156
+ function displayApplicationStep(application) {
157
+ logSectionTitle('Application');
158
+ if (application.valid) {
159
+ logOkRowRest('Application configuration is valid');
160
+ } else {
161
+ logger.log(` ${failureGlyph()} ${chalk.red('Application configuration has errors:')}`);
162
+ application.errors.forEach(error => {
163
+ logErrorDetail(error);
164
+ });
165
+ }
166
+ if (application.warnings && application.warnings.length > 0) {
167
+ application.warnings.forEach(warning => {
168
+ logger.log(` ${chalk.yellow('⚠')} ${chalk.white(warning)}`);
169
+ });
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Displays external integration files step
175
+ * @function displayComponentsStep
176
+ * @param {Object} components - Components validation result
177
+ * @returns {void}
178
+ */
179
+ function displayComponentsStep(components) {
180
+ const hasFiles = components.files && components.files.length > 0;
181
+ const hasErrors = components.errors && components.errors.length > 0;
182
+ if (!hasFiles && !hasErrors) {
183
+ return;
184
+ }
185
+
186
+ logSectionTitle('External integration files');
187
+ if (hasFiles) {
188
+ components.files.forEach(file => {
189
+ if (file.valid) {
190
+ logger.log(formatDatasourceListRow('ok', file.file, file.type));
191
+ } else {
192
+ logger.log(formatDatasourceListRow('fail', file.file, file.type));
193
+ }
194
+ });
195
+ }
196
+ if (hasErrors) {
197
+ components.errors.forEach(error => {
198
+ logger.log(formatBlockingError(error));
199
+ });
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Displays deployment manifest step
205
+ * @function displayManifestStep
206
+ * @param {Object} manifest - Manifest validation result
207
+ * @param {Array} componentFiles - Component files
208
+ * @returns {void}
209
+ */
210
+ function displayManifestStep(manifest, componentFiles) {
211
+ logSectionTitle('Deployment manifest');
212
+ if (manifest.skipped) {
213
+ logger.log(` ${chalk.gray('⏭')} ${chalk.white('Skipped (fix errors above first)')}`);
214
+ } else if (manifest.valid) {
215
+ logOkRowRest('Full deployment manifest is valid');
216
+ if (componentFiles) {
217
+ const datasourceFiles = componentFiles.filter(f => f.type === 'datasource' || f.type === 'external-datasource');
218
+ logger.log(` ${successGlyph()} ${chalk.white('System configuration valid')}`);
219
+ logger.log(` ${successGlyph()} ${chalk.white(`${datasourceFiles.length} datasource(s) valid`)}`);
220
+ logger.log(` ${successGlyph()} ${chalk.white('Schema validation passed')}`);
221
+ }
222
+ } else {
223
+ logger.log(` ${failureGlyph()} ${chalk.red('Full deployment manifest validation failed:')}`);
224
+ const errs = manifest.errors && manifest.errors.length > 0 ? manifest.errors : [];
225
+ if (errs.length > 0) {
226
+ errs.forEach(error => {
227
+ const msg = typeof error === 'string' ? error : String(error);
228
+ logErrorDetail(msg);
229
+ });
230
+ } else {
231
+ logErrorDetail('No error details available (check schema and manifest structure).');
232
+ }
233
+ }
234
+ if (manifest.warnings && manifest.warnings.length > 0) {
235
+ manifest.warnings.forEach(warning => {
236
+ logger.log(` ${chalk.yellow('⚠')} ${chalk.white(warning)}`);
237
+ });
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Displays overall validation status
243
+ * @param {Object} result - Validation result
244
+ */
245
+ function displayOverallStatus(result) {
246
+ let hasErrors = result.errors && result.errors.length > 0;
247
+ if (!hasErrors && result.steps) {
248
+ const stepErrors = [result.steps.application, result.steps.components, result.steps.manifest]
249
+ .filter(Boolean)
250
+ .some(s => s.errors && s.errors.length > 0);
251
+ if (stepErrors) {
252
+ hasErrors = true;
253
+ }
254
+ }
255
+ const hasWarnings = result.warnings && result.warnings.length > 0;
256
+ logger.log('');
257
+ if (hasErrors) {
258
+ logger.log(headerKeyValue('Overall:', chalk.red('Failed')));
259
+ } else if (hasWarnings) {
260
+ logger.log(headerKeyValue('Overall:', chalk.yellow('Passed with warnings')));
261
+ } else {
262
+ logger.log(headerKeyValue('Overall:', chalk.green('Passed')));
263
+ }
264
+ }
265
+
266
+ module.exports = {
267
+ displayApplicationValidation,
268
+ displayExternalFilesValidation,
269
+ displayRbacValidation,
270
+ displayFileValidation,
271
+ displayAggregatedWarnings,
272
+ displayApplicationStep,
273
+ displayComponentsStep,
274
+ displayManifestStep,
275
+ displayOverallStatus
276
+ };