@aifabrix/builder 2.44.6 → 2.45.5

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 (257) hide show
  1. package/.cursor/rules/cli-layout.mdc +7 -3
  2. package/.cursor/rules/docs-rules.mdc +3 -1
  3. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
  4. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
  5. package/integration/hubspot-test/wizard-hubspot-e2e.yaml +1 -1
  6. package/jest.projects.js +100 -1
  7. package/lib/api/agent-metadata-validation.api.js +93 -0
  8. package/lib/api/audit-api-helpers.js +68 -0
  9. package/lib/api/audit.api.js +119 -0
  10. package/lib/api/auth-cache.api.js +44 -0
  11. package/lib/api/certificates.api.js +4 -4
  12. package/lib/api/credential.api.js +2 -2
  13. package/lib/api/dataplane-health.api.js +137 -0
  14. package/lib/api/dataplane-sync.api.js +27 -0
  15. package/lib/api/datasources-core.api.js +21 -18
  16. package/lib/api/datasources-extended.api.js +22 -22
  17. package/lib/api/dimensions.api.js +3 -0
  18. package/lib/api/external-systems.api.js +13 -13
  19. package/lib/api/governance-scenarios.api.js +39 -0
  20. package/lib/api/groups.api.js +79 -0
  21. package/lib/api/index.js +45 -2
  22. package/lib/api/pipeline.api.js +4 -4
  23. package/lib/api/protection.api.js +157 -0
  24. package/lib/api/records-bulk.api.js +30 -0
  25. package/lib/api/records-search-parse.js +49 -0
  26. package/lib/api/records-search.api.js +29 -0
  27. package/lib/api/types/agent-metadata-validation.types.js +67 -0
  28. package/lib/api/types/audit.types.js +72 -0
  29. package/lib/api/types/dataplane-health.types.js +27 -0
  30. package/lib/api/types/dimensions.types.js +2 -0
  31. package/lib/api/types/governance-scenarios.types.js +39 -0
  32. package/lib/api/types/protection.types.js +69 -0
  33. package/lib/api/types/records-bulk.types.js +35 -0
  34. package/lib/api/types/records-search.types.js +28 -0
  35. package/lib/api/user-groups.api.js +50 -0
  36. package/lib/api/users.api.js +95 -0
  37. package/lib/api/validation-run.api.js +3 -3
  38. package/lib/api/validation-runner.js +3 -3
  39. package/lib/api/wizard-platform.api.js +3 -3
  40. package/lib/api/wizard.api.js +21 -21
  41. package/lib/app/deploy.js +34 -19
  42. package/lib/app/helpers.js +3 -3
  43. package/lib/app/index.js +3 -3
  44. package/lib/app/register.js +7 -6
  45. package/lib/app/restart-display.js +52 -21
  46. package/lib/app/rotate-secret.js +7 -6
  47. package/lib/app/run-helpers.js +15 -8
  48. package/lib/app/run.js +57 -9
  49. package/lib/app/show-display.js +7 -0
  50. package/lib/app/show.js +87 -5
  51. package/lib/build/index.js +9 -5
  52. package/lib/cli/index.js +4 -2
  53. package/lib/cli/infra-guided.js +62 -28
  54. package/lib/cli/installation-log-command.js +73 -0
  55. package/lib/cli/setup-app.help.js +50 -2
  56. package/lib/cli/setup-app.js +44 -25
  57. package/lib/cli/setup-app.test-commands.js +80 -21
  58. package/lib/cli/setup-auth.js +124 -52
  59. package/lib/cli/setup-external-system.js +37 -3
  60. package/lib/cli/setup-infra-up-dataplane-action.js +111 -0
  61. package/lib/cli/setup-infra-up-platform-action.js +131 -0
  62. package/lib/cli/setup-infra.js +60 -119
  63. package/lib/cli/setup-platform.js +1 -1
  64. package/lib/cli/setup-utility-help-after.js +45 -0
  65. package/lib/cli/setup-utility-resolve.js +132 -0
  66. package/lib/cli/setup-utility.js +148 -116
  67. package/lib/commands/app-logs.js +81 -33
  68. package/lib/commands/auth-config.js +116 -18
  69. package/lib/commands/auth-status-dataplane-version.js +231 -0
  70. package/lib/commands/auth-status-display.js +94 -0
  71. package/lib/commands/auth-status.js +70 -6
  72. package/lib/commands/credential-push.js +10 -5
  73. package/lib/commands/datasource-load-export-cli.js +178 -0
  74. package/lib/commands/datasource-test-trust-cli.js +114 -0
  75. package/lib/commands/datasource-unified-test-cli.js +25 -17
  76. package/lib/commands/datasource-unified-test-cli.options.js +18 -3
  77. package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
  78. package/lib/commands/datasource-verify-audit-cli.js +96 -0
  79. package/lib/commands/datasource.js +68 -1
  80. package/lib/commands/dimension-value.js +43 -13
  81. package/lib/commands/dimension.js +38 -7
  82. package/lib/commands/identity-apply-sync.js +131 -0
  83. package/lib/commands/identity-group-cli.js +146 -0
  84. package/lib/commands/identity-membership-role-cache.js +175 -0
  85. package/lib/commands/identity-shared.js +94 -0
  86. package/lib/commands/identity-user-cli.js +158 -0
  87. package/lib/commands/identity.js +43 -0
  88. package/lib/commands/login.js +46 -16
  89. package/lib/commands/protection-cli-leaves.js +286 -0
  90. package/lib/commands/protection.js +50 -0
  91. package/lib/commands/repair.js +13 -3
  92. package/lib/commands/setup-modes.js +19 -6
  93. package/lib/commands/setup-prompts.js +41 -8
  94. package/lib/commands/setup.js +114 -9
  95. package/lib/commands/teardown.js +54 -5
  96. package/lib/commands/test-e2e-external.js +40 -9
  97. package/lib/commands/test-governance-cli-options.js +47 -0
  98. package/lib/commands/test-governance-command-action.js +55 -0
  99. package/lib/commands/test-governance-external.js +88 -0
  100. package/lib/commands/test-trust-cli-options.js +32 -0
  101. package/lib/commands/test-trust-command-action.js +118 -0
  102. package/lib/commands/test-trust-external.js +127 -0
  103. package/lib/commands/up-common.js +48 -14
  104. package/lib/commands/up-dataplane.js +21 -18
  105. package/lib/commands/up-miso.js +12 -8
  106. package/lib/commands/upload.js +38 -15
  107. package/lib/commands/wizard-core.js +3 -0
  108. package/lib/core/audit-logger.js +1 -34
  109. package/lib/core/config-admin-email.js +56 -0
  110. package/lib/core/config-device-dataplane.js +185 -0
  111. package/lib/core/config-normalize.js +60 -0
  112. package/lib/core/config-registered-controller-urls.js +54 -0
  113. package/lib/core/config.js +33 -50
  114. package/lib/core/secrets-ensure-infra.js +1 -1
  115. package/lib/core/secrets-env-content.js +86 -90
  116. package/lib/core/secrets-env-declarative-expand.js +170 -0
  117. package/lib/core/secrets-env-write.js +2 -0
  118. package/lib/core/secrets-load.js +106 -102
  119. package/lib/datasource/agent-trust-debug-log.js +45 -0
  120. package/lib/datasource/agent-trust-fetch.js +115 -0
  121. package/lib/datasource/agent-trust-map.js +101 -0
  122. package/lib/datasource/agent-trust-mock.js +44 -0
  123. package/lib/datasource/agent-trust-run.js +93 -0
  124. package/lib/datasource/agent-trust-timeout.js +25 -0
  125. package/lib/datasource/audit-evidence-display.js +80 -0
  126. package/lib/datasource/audit-evidence-extract-debug.js +103 -0
  127. package/lib/datasource/audit-evidence-extract.js +177 -0
  128. package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
  129. package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
  130. package/lib/datasource/audit-evidence-poll.js +109 -0
  131. package/lib/datasource/audit-evidence-run.js +177 -0
  132. package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
  133. package/lib/datasource/audit-evidence-verify-matrix.js +177 -0
  134. package/lib/datasource/audit-evidence-verify.js +108 -0
  135. package/lib/datasource/bulk-loader-run-batches.js +88 -0
  136. package/lib/datasource/bulk-loader-service-retry.js +52 -0
  137. package/lib/datasource/bulk-loader-service.js +127 -0
  138. package/lib/datasource/capability/run-capability-dimension.js +2 -1
  139. package/lib/datasource/datasource-exporter-resolve.js +82 -0
  140. package/lib/datasource/datasource-exporter-service.js +144 -0
  141. package/lib/datasource/datasource-identifier.js +148 -0
  142. package/lib/datasource/datasource-load-export-context.js +92 -0
  143. package/lib/datasource/integration-context.js +6 -4
  144. package/lib/datasource/local-data-paths.js +145 -0
  145. package/lib/datasource/log-cleaner.js +241 -0
  146. package/lib/datasource/log-viewer.js +111 -6
  147. package/lib/datasource/record-file-parser.js +85 -0
  148. package/lib/datasource/record-mapper.js +129 -0
  149. package/lib/datasource/resolve-app.js +74 -55
  150. package/lib/datasource/test-e2e.js +26 -2
  151. package/lib/datasource/unified-validation-run-resolve.js +45 -12
  152. package/lib/datasource/unified-validation-run.js +43 -17
  153. package/lib/datasource/validate.js +37 -20
  154. package/lib/external-system/deploy.js +5 -1
  155. package/lib/external-system/test-system-level.js +25 -4
  156. package/lib/external-system/test.js +3 -1
  157. package/lib/governance/governance-pack-loader.js +70 -0
  158. package/lib/governance/governance-run-display.js +119 -0
  159. package/lib/identity/identity-apply-core.js +108 -0
  160. package/lib/identity/identity-apply-phases.js +244 -0
  161. package/lib/identity/identity-apply-service.js +77 -0
  162. package/lib/identity/identity-csv-parser.js +114 -0
  163. package/lib/internal/fs-real-sync.js +29 -1
  164. package/lib/internal/node-fs.js +2 -0
  165. package/lib/protection/auth-context.js +45 -0
  166. package/lib/protection/convert-batch.js +90 -0
  167. package/lib/protection/load.js +41 -0
  168. package/lib/protection/paths.js +158 -0
  169. package/lib/protection/preflight-datasource-ready.js +46 -0
  170. package/lib/protection/protection-create-probe-lines.js +142 -0
  171. package/lib/protection/protection-create-probes.js +95 -0
  172. package/lib/protection/protection-create-scaffold.js +281 -0
  173. package/lib/protection/protection-display-helpers.js +445 -0
  174. package/lib/protection/protection-display.js +215 -0
  175. package/lib/protection/protection-preset-registry.js +97 -0
  176. package/lib/protection/report-exit.js +62 -0
  177. package/lib/protection/resolve.js +202 -0
  178. package/lib/protection/run-commands-validate.js +130 -0
  179. package/lib/protection/run-commands.js +216 -0
  180. package/lib/protection/run-protection-create-helpers.js +134 -0
  181. package/lib/protection/run-protection-create-probes-flow.js +43 -0
  182. package/lib/protection/run-protection-create.js +191 -0
  183. package/lib/protection/scope.js +22 -0
  184. package/lib/protection/sync-after-upload.js +64 -0
  185. package/lib/protection/upload-batch.js +127 -0
  186. package/lib/protection/validate-batch.js +113 -0
  187. package/lib/protection/validate-local.js +65 -0
  188. package/lib/resolvers/dimension-file.js +29 -4
  189. package/lib/schema/application-schema.json +4 -0
  190. package/lib/schema/external-datasource.schema.json +47 -13
  191. package/lib/schema/flag-map-validation-run.json +2 -2
  192. package/lib/schema/infra.parameter.yaml +10 -0
  193. package/lib/schema/protection.schema.json +374 -0
  194. package/lib/schema/type/credential-field-registry.json +636 -0
  195. package/lib/schema/wizard-config.schema.json +1 -1
  196. package/lib/utils/agent-trust-run-display.js +231 -0
  197. package/lib/utils/agent-trust-run-exit.js +59 -0
  198. package/lib/utils/agent-trust-wait-ui.js +94 -0
  199. package/lib/utils/app-config-resolver.js +24 -1
  200. package/lib/utils/applications-config-defaults.js +206 -0
  201. package/lib/utils/auth-config-validator.js +2 -12
  202. package/lib/utils/bash-secret-env.js +1 -1
  203. package/lib/utils/cli-exit-codes.js +30 -0
  204. package/lib/utils/cli-sync-options.js +33 -0
  205. package/lib/utils/compose-generate-docker-compose.js +111 -6
  206. package/lib/utils/compose-generator.js +17 -8
  207. package/lib/utils/controller-url.js +50 -7
  208. package/lib/utils/credential-push-ui.js +90 -0
  209. package/lib/utils/dataplane-cli-version-gate.js +202 -0
  210. package/lib/utils/dataplane-cli-version-help.js +74 -0
  211. package/lib/utils/datasource-export-display.js +137 -0
  212. package/lib/utils/datasource-load-display.js +174 -0
  213. package/lib/utils/datasource-load-export-ui.js +120 -0
  214. package/lib/utils/datasource-test-run-capability-scope.js +3 -3
  215. package/lib/utils/datasource-test-run-display.js +3 -3
  216. package/lib/utils/datasource-test-run-tty-log.js +5 -1
  217. package/lib/utils/env-copy.js +99 -14
  218. package/lib/utils/env-template.js +5 -1
  219. package/lib/utils/health-check-url.js +18 -15
  220. package/lib/utils/health-check.js +7 -5
  221. package/lib/utils/help-builder.js +30 -24
  222. package/lib/utils/infra-optional-service-flags.js +69 -0
  223. package/lib/utils/installation-log-core.js +282 -0
  224. package/lib/utils/installation-log-record.js +237 -0
  225. package/lib/utils/installation-log.js +123 -0
  226. package/lib/utils/log-redaction.js +105 -0
  227. package/lib/utils/manifest-location.js +164 -0
  228. package/lib/utils/manifest-source-emit.js +184 -0
  229. package/lib/utils/paths.js +238 -89
  230. package/lib/utils/remote-secrets-loader.js +7 -1
  231. package/lib/utils/run-cli-flags.js +29 -0
  232. package/lib/utils/secrets-canonical.js +10 -3
  233. package/lib/utils/secrets-path.js +3 -4
  234. package/lib/utils/secrets-utils.js +20 -10
  235. package/lib/utils/semver-compare.js +127 -0
  236. package/lib/utils/system-builder-root.js +10 -2
  237. package/lib/utils/upload-pipeline-ui.js +91 -0
  238. package/lib/utils/url-declarative-public-base.js +80 -12
  239. package/lib/utils/url-declarative-resolve-build-urls.js +238 -0
  240. package/lib/utils/url-declarative-resolve-build.js +24 -393
  241. package/lib/utils/url-declarative-resolve-expand-token.js +189 -0
  242. package/lib/utils/url-declarative-resolve-load-doc.js +12 -3
  243. package/lib/utils/url-declarative-resolve-surface-state.js +102 -0
  244. package/lib/utils/url-declarative-resolve.js +47 -7
  245. package/lib/utils/url-declarative-runtime-base-path.js +21 -1
  246. package/lib/utils/urls-local-registry-scan.js +103 -0
  247. package/lib/utils/urls-local-registry.js +161 -90
  248. package/lib/utils/validation-poll-ui.js +14 -7
  249. package/lib/validation/wizard-config-validator.js +58 -19
  250. package/package.json +3 -1
  251. package/templates/applications/dataplane/application.yaml +4 -0
  252. package/templates/applications/dataplane/env.template +13 -1
  253. package/templates/applications/dataplane/rbac.yaml +4 -0
  254. package/templates/applications/miso-controller/application.yaml +2 -0
  255. package/templates/applications/miso-controller/env.template +27 -29
  256. package/templates/external-system/README.md.hbs +56 -8
  257. package/.npmrc.token +0 -1
@@ -0,0 +1,148 @@
1
+ /**
2
+ * @fileoverview Match datasource CLI identifiers to integration files (key or filename stem).
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const path = require('path');
10
+ const { getIntegrationPath } = require('../utils/paths');
11
+ const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
12
+ const { loadConfigFile } = require('../utils/config-format');
13
+
14
+ /**
15
+ * @param {string} fileRef
16
+ * @returns {string}
17
+ */
18
+ function datasourceFileStem(fileRef) {
19
+ return path.basename(String(fileRef || ''), path.extname(String(fileRef || '')));
20
+ }
21
+
22
+ /**
23
+ * True when identifier matches parsed `key` or the datasource JSON filename (with or without .json).
24
+ * @param {string} identifier
25
+ * @param {string} fileRef - Manifest-relative datasource path
26
+ * @param {Object|null} parsed
27
+ * @returns {boolean}
28
+ */
29
+ function datasourceIdentifierMatchesFile(identifier, fileRef, parsed) {
30
+ const id = String(identifier || '').trim();
31
+ if (!id || !parsed || typeof parsed !== 'object') {
32
+ return false;
33
+ }
34
+ if (parsed.key === id) {
35
+ return true;
36
+ }
37
+ const stem = datasourceFileStem(fileRef);
38
+ if (stem === id) {
39
+ return true;
40
+ }
41
+ if (`${stem}.json` === id.toLowerCase()) {
42
+ return true;
43
+ }
44
+ return false;
45
+ }
46
+
47
+ /**
48
+ * Pick integration app folder for a datasource identifier (longest matching prefix wins).
49
+ * @param {string} identifier
50
+ * @param {string[]} appNames
51
+ * @returns {string|null}
52
+ */
53
+ function pickIntegrationAppForDatasourceIdentifier(identifier, appNames) {
54
+ const id = String(identifier || '').trim();
55
+ if (!id) {
56
+ return null;
57
+ }
58
+ const matches = appNames.filter(app => id === app || id.startsWith(`${app}-`));
59
+ if (matches.length === 0) {
60
+ return null;
61
+ }
62
+ matches.sort((a, b) => b.length - a.length);
63
+ return matches[0];
64
+ }
65
+
66
+ /**
67
+ * List manifest datasource files for an integration app.
68
+ * @param {string} appKey
69
+ * @returns {{ appPath: string, schemaBasePath: string, datasourceFiles: string[] }|null}
70
+ */
71
+ function listDatasourceFilesForApp(appKey) {
72
+ const appPath = getIntegrationPath(appKey);
73
+ let config;
74
+ try {
75
+ const configPath = resolveApplicationConfigPath(appPath);
76
+ config = loadConfigFile(configPath);
77
+ } catch {
78
+ return null;
79
+ }
80
+ const schemaBasePath = config.externalIntegration?.schemaBasePath || './';
81
+ const datasourceFiles = config.externalIntegration?.dataSources || [];
82
+ return { appPath, schemaBasePath, datasourceFiles };
83
+ }
84
+
85
+ /**
86
+ * @param {string} appPath
87
+ * @param {string} schemaBasePath
88
+ * @param {string} fileRef
89
+ * @returns {string}
90
+ */
91
+ function resolveDatasourceFilePath(appPath, schemaBasePath, fileRef) {
92
+ if (path.isAbsolute(schemaBasePath)) {
93
+ return path.join(schemaBasePath, fileRef);
94
+ }
95
+ return path.join(appPath, schemaBasePath, fileRef);
96
+ }
97
+
98
+ /**
99
+ * Collect datasource file refs in one app that match the identifier.
100
+ * @param {string} appKey
101
+ * @param {string} identifier
102
+ * @returns {string[]}
103
+ */
104
+ function collectDatasourceFileRefsMatchingIdentifier(appKey, identifier) {
105
+ const listed = listDatasourceFilesForApp(appKey);
106
+ if (!listed) {
107
+ return [];
108
+ }
109
+ const fsSync = require('fs');
110
+ const hits = [];
111
+ for (const fileRef of listed.datasourceFiles) {
112
+ if (!fileRef || typeof fileRef !== 'string') {
113
+ continue;
114
+ }
115
+ const fullPath = resolveDatasourceFilePath(listed.appPath, listed.schemaBasePath, fileRef);
116
+ if (!fsSync.existsSync(fullPath)) {
117
+ continue;
118
+ }
119
+ try {
120
+ const parsed = loadConfigFile(fullPath);
121
+ if (datasourceIdentifierMatchesFile(identifier, fileRef, parsed)) {
122
+ hits.push(fileRef);
123
+ }
124
+ } catch {
125
+ // skip unreadable or invalid files
126
+ }
127
+ }
128
+ return hits;
129
+ }
130
+
131
+ /**
132
+ * @param {string} appKey
133
+ * @param {string} identifier
134
+ * @returns {boolean}
135
+ */
136
+ function appHasDatasourceIdentifier(appKey, identifier) {
137
+ return collectDatasourceFileRefsMatchingIdentifier(appKey, identifier).length > 0;
138
+ }
139
+
140
+ module.exports = {
141
+ datasourceFileStem,
142
+ datasourceIdentifierMatchesFile,
143
+ pickIntegrationAppForDatasourceIdentifier,
144
+ listDatasourceFilesForApp,
145
+ resolveDatasourceFilePath,
146
+ collectDatasourceFileRefsMatchingIdentifier,
147
+ appHasDatasourceIdentifier
148
+ };
@@ -0,0 +1,92 @@
1
+ /**
2
+ * @fileoverview Shared context resolution for datasource load/export (plan 144)
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { getConfig } = require('../core/config');
10
+ const { setupIntegrationTestAuth } = require('../external-system/test-auth');
11
+ const { resolveAppKeyForDatasource } = require('./resolve-app');
12
+ const { getSystemKeyFromAppKey } = require('./integration-context');
13
+ const { loadDatasourceForApp } = require('./unified-validation-run-resolve');
14
+ const { findDatasourceFileByKey } = require('./integration-context');
15
+ const { getIntegrationPath } = require('../utils/paths');
16
+ const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
17
+ const { computeEntitySuffix } = require('./local-data-paths');
18
+
19
+ /**
20
+ * @param {string} appKey
21
+ * @throws {Error}
22
+ */
23
+ async function assertExternalIntegrationApp(appKey) {
24
+ const pathsUtil = require('../utils/paths');
25
+ const appType = await pathsUtil.detectAppType(appKey).catch(() => null);
26
+ if (!appType || appType.baseDir !== 'integration') {
27
+ throw new Error(
28
+ 'datasource load/export apply to external integration folders only (integration/<systemKey>/).'
29
+ );
30
+ }
31
+ }
32
+
33
+ /**
34
+ * @async
35
+ * @param {string} datasourceKey
36
+ * @param {Object} options
37
+ * @returns {Promise<Object>}
38
+ */
39
+ async function resolveLoadExportContext(datasourceKey, options = {}) {
40
+ const { appKey } = await resolveAppKeyForDatasource(datasourceKey, options.app);
41
+ await assertExternalIntegrationApp(appKey);
42
+ const systemKey = await getSystemKeyFromAppKey(appKey);
43
+ const {
44
+ datasource,
45
+ datasourcePath,
46
+ datasourceKey: canonicalDatasourceKey
47
+ } = loadDatasourceForApp(appKey, datasourceKey, findDatasourceFileByKey);
48
+ const appPath = getIntegrationPath(appKey);
49
+ const manifestPath = resolveApplicationConfigPath(appPath);
50
+ const entitySuffix = computeEntitySuffix(systemKey, canonicalDatasourceKey);
51
+
52
+ let authConfig = options.authConfig;
53
+ let dataplaneUrl = options.dataplaneUrl || options.dataplane;
54
+ if (!authConfig || !dataplaneUrl) {
55
+ const configObj = await getConfig();
56
+ const auth = await setupIntegrationTestAuth(
57
+ appKey,
58
+ { environment: options.environment || options.env, dataplane: options.dataplane },
59
+ configObj
60
+ );
61
+ authConfig = auth.authConfig;
62
+ dataplaneUrl = auth.dataplaneUrl;
63
+ }
64
+
65
+ return {
66
+ appKey,
67
+ systemKey,
68
+ datasourceKey: canonicalDatasourceKey,
69
+ datasource,
70
+ datasourcePath,
71
+ manifestPath,
72
+ entitySuffix,
73
+ authConfig,
74
+ dataplaneUrl,
75
+ environment: options.environment || options.env
76
+ };
77
+ }
78
+
79
+ /**
80
+ * Gray manifest label segment for TTY (integration/<appKey>).
81
+ * @param {string} appKey
82
+ * @returns {string}
83
+ */
84
+ function integrationManifestLabel(appKey) {
85
+ return `integration/${appKey}`;
86
+ }
87
+
88
+ module.exports = {
89
+ assertExternalIntegrationApp,
90
+ resolveLoadExportContext,
91
+ integrationManifestLabel
92
+ };
@@ -9,6 +9,10 @@ const fs = require('fs').promises;
9
9
  const { getIntegrationPath } = require('../utils/paths');
10
10
  const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
11
11
  const { loadConfigFile } = require('../utils/config-format');
12
+ const {
13
+ datasourceIdentifierMatchesFile,
14
+ resolveDatasourceFilePath
15
+ } = require('./datasource-identifier');
12
16
 
13
17
  /**
14
18
  * @param {string} appKey - Integration app key
@@ -43,13 +47,11 @@ function findDatasourceFileByKey(appPath, schemaBasePath, datasourceFiles, datas
43
47
  const fsSync = require('fs');
44
48
  for (const f of datasourceFiles) {
45
49
  if (!f || typeof f !== 'string') continue;
46
- const fullPath = path.isAbsolute(schemaBasePath)
47
- ? path.join(schemaBasePath, f)
48
- : path.join(appPath, schemaBasePath, f);
50
+ const fullPath = resolveDatasourceFilePath(appPath, schemaBasePath, f);
49
51
  if (!fsSync.existsSync(fullPath)) continue;
50
52
  try {
51
53
  const parsed = loadConfigFile(fullPath);
52
- if (parsed && parsed.key === datasourceKey) return f;
54
+ if (datasourceIdentifierMatchesFile(datasourceKey, f, parsed)) return f;
53
55
  } catch {
54
56
  // skip unreadable or invalid files
55
57
  }
@@ -0,0 +1,145 @@
1
+ /**
2
+ * @fileoverview Resolve integration/.data/ paths for datasource load/export (plan 144)
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const path = require('path');
10
+ const fs = require('fs');
11
+ const { getCwdIntegrationRoot, getIntegrationRoot } = require('../utils/paths');
12
+
13
+ const DATA_DIR_NAME = '.data';
14
+
15
+ /**
16
+ * Absolute path to integration/.data (cwd integration root preferred).
17
+ * @returns {string}
18
+ */
19
+ function getIntegrationDataDir() {
20
+ const cwdRoot = getCwdIntegrationRoot();
21
+ const base = cwdRoot || getIntegrationRoot();
22
+ return path.join(base, DATA_DIR_NAME);
23
+ }
24
+
25
+ /**
26
+ * Entity suffix from datasource key and system key.
27
+ * @param {string} systemKey
28
+ * @param {string} datasourceKey
29
+ * @returns {string}
30
+ */
31
+ function computeEntitySuffix(systemKey, datasourceKey) {
32
+ const sys = String(systemKey || '').trim();
33
+ const key = String(datasourceKey || '').trim();
34
+ const prefix = `${sys}-`;
35
+ if (sys && key.startsWith(prefix)) {
36
+ return key.slice(prefix.length) || key;
37
+ }
38
+ const lastHyphen = key.lastIndexOf('-');
39
+ if (lastHyphen > 0) {
40
+ return key.slice(lastHyphen + 1);
41
+ }
42
+ return key;
43
+ }
44
+
45
+ /**
46
+ * Default fixture basename (without extension).
47
+ * @param {string} systemKey
48
+ * @param {string} entitySuffix
49
+ * @returns {string}
50
+ */
51
+ function defaultDataBasename(systemKey, entitySuffix) {
52
+ return `${systemKey}-data-${entitySuffix}`;
53
+ }
54
+
55
+ /**
56
+ * @param {string} dir
57
+ * @param {string} basename
58
+ * @param {'json'|'ndjson'} format
59
+ * @returns {string}
60
+ */
61
+ function dataFilePath(dir, basename, format) {
62
+ const ext = format === 'ndjson' ? '.ndjson' : '.json';
63
+ return path.join(dir, `${basename}${ext}`);
64
+ }
65
+
66
+ /**
67
+ * Infer format from file extension.
68
+ * @param {string} filePath
69
+ * @returns {'json'|'ndjson'}
70
+ */
71
+ function inferFormatFromPath(filePath) {
72
+ const ext = path.extname(filePath).toLowerCase();
73
+ if (ext === '.ndjson' || ext === '.jsonl') {
74
+ return 'ndjson';
75
+ }
76
+ return 'json';
77
+ }
78
+
79
+ /**
80
+ * Default integration/.data output path (file need not exist).
81
+ * @param {Object} opts
82
+ * @param {string} opts.systemKey
83
+ * @param {string} opts.entitySuffix
84
+ * @param {'json'|'ndjson'} [opts.format]
85
+ * @returns {{ filePath: string, format: 'json'|'ndjson' }}
86
+ */
87
+ function resolveDefaultDataOutputPath(opts) {
88
+ const dir = getIntegrationDataDir();
89
+ const basename = defaultDataBasename(opts.systemKey, opts.entitySuffix);
90
+ const format = opts.format === 'ndjson' ? 'ndjson' : 'json';
91
+ return { filePath: dataFilePath(dir, basename, format), format };
92
+ }
93
+
94
+ /**
95
+ * Resolve data file: --file override or default integration/.data paths.
96
+ * @param {Object} opts
97
+ * @param {string} opts.systemKey
98
+ * @param {string} opts.entitySuffix
99
+ * @param {string} [opts.file] - CLI --file
100
+ * @param {'json'|'ndjson'} [opts.format] - explicit format
101
+ * @returns {{ filePath: string, format: 'json'|'ndjson' }}
102
+ */
103
+ function resolveDataFilePath(opts) {
104
+ if (opts.file && String(opts.file).trim()) {
105
+ const filePath = path.resolve(String(opts.file).trim());
106
+ const format = opts.format || inferFormatFromPath(filePath);
107
+ return { filePath, format };
108
+ }
109
+
110
+ const dir = getIntegrationDataDir();
111
+ const basename = defaultDataBasename(opts.systemKey, opts.entitySuffix);
112
+ const jsonPath = dataFilePath(dir, basename, 'json');
113
+ const ndjsonPath = dataFilePath(dir, basename, 'ndjson');
114
+
115
+ if (opts.format === 'json' && fs.existsSync(jsonPath)) {
116
+ return { filePath: jsonPath, format: 'json' };
117
+ }
118
+ if (opts.format === 'ndjson' && fs.existsSync(ndjsonPath)) {
119
+ return { filePath: ndjsonPath, format: 'ndjson' };
120
+ }
121
+ if (!opts.format || opts.format === 'json') {
122
+ if (fs.existsSync(jsonPath)) {
123
+ return { filePath: jsonPath, format: 'json' };
124
+ }
125
+ }
126
+ if (!opts.format || opts.format === 'ndjson') {
127
+ if (fs.existsSync(ndjsonPath)) {
128
+ return { filePath: ndjsonPath, format: 'ndjson' };
129
+ }
130
+ }
131
+
132
+ throw new Error(
133
+ `No local data file found. Expected one of:\n ${jsonPath}\n ${ndjsonPath}\n` +
134
+ 'Use --file <path> to override.'
135
+ );
136
+ }
137
+
138
+ module.exports = {
139
+ getIntegrationDataDir,
140
+ computeEntitySuffix,
141
+ defaultDataBasename,
142
+ inferFormatFromPath,
143
+ resolveDefaultDataOutputPath,
144
+ resolveDataFilePath
145
+ };
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Remove saved datasource test debug JSON under integration/<app>/logs/.
3
+ * @fileoverview Local cleanup for test / test-integration / test-e2e / test-trust logs
4
+ * @author AI Fabrix Team
5
+ * @version 1.0.0
6
+ */
7
+
8
+ const path = require('path');
9
+ const fsp = require('node:fs').promises;
10
+ const chalk = require('chalk');
11
+ const logger = require('../utils/logger');
12
+ const { getIntegrationPath, listIntegrationAppNames } = require('../utils/paths');
13
+ const { isStructuralTestLogFileName } = require('./log-viewer');
14
+ const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
15
+
16
+ /** @type {readonly string[]} */
17
+ const LOG_CLEAN_TYPES = ['test', 'integration', 'e2e', 'trust', 'all'];
18
+
19
+ /**
20
+ * @param {string} [type]
21
+ * @returns {'test'|'integration'|'e2e'|'trust'|'all'}
22
+ */
23
+ function normalizeLogCleanType(type) {
24
+ const raw = (type === undefined || type === null ? 'all' : String(type)).trim().toLowerCase();
25
+ if (raw === '' || raw === 'all') return 'all';
26
+ if (LOG_CLEAN_TYPES.includes(raw)) return raw;
27
+ throw new Error(
28
+ `Invalid --type "${type}". Use: ${LOG_CLEAN_TYPES.join(', ')}`
29
+ );
30
+ }
31
+
32
+ /**
33
+ * @param {string} fileName
34
+ * @param {'test'|'integration'|'e2e'|'trust'|'all'} type
35
+ * @returns {boolean}
36
+ */
37
+ function matchesLogCleanType(fileName, type) {
38
+ if (!fileName || !fileName.endsWith('.json')) return false;
39
+ if (type === 'all') return true;
40
+ if (type === 'test') return isStructuralTestLogFileName(fileName);
41
+ if (type === 'integration') {
42
+ return fileName.startsWith('test-integration-');
43
+ }
44
+ if (type === 'e2e') return fileName.startsWith('test-e2e-');
45
+ if (type === 'trust') return fileName.startsWith('test-trust-');
46
+ return false;
47
+ }
48
+
49
+ /**
50
+ * @param {string} logsDir
51
+ * @param {'test'|'integration'|'e2e'|'trust'|'all'} type
52
+ * @returns {Promise<string[]>}
53
+ */
54
+ async function listMatchingLogFiles(logsDir, type) {
55
+ let entries;
56
+ try {
57
+ entries = await fsp.readdir(logsDir, { withFileTypes: true });
58
+ } catch (err) {
59
+ if (err.code === 'ENOENT') return [];
60
+ throw err;
61
+ }
62
+ return entries
63
+ .filter(e => e.isFile() && matchesLogCleanType(e.name, type))
64
+ .map(e => path.join(logsDir, e.name))
65
+ .sort((a, b) => a.localeCompare(b));
66
+ }
67
+
68
+ /**
69
+ * @param {string} appKey
70
+ * @returns {Promise<void>}
71
+ */
72
+ async function assertIntegrationAppExists(appKey) {
73
+ const appPath = getIntegrationPath(appKey);
74
+ try {
75
+ const st = await fsp.stat(appPath);
76
+ if (!st.isDirectory()) {
77
+ throw new Error(`Integration folder is not a directory: ${appPath}`);
78
+ }
79
+ } catch (err) {
80
+ if (err.code === 'ENOENT') {
81
+ throw new Error(`Integration folder not found: ${appPath}`);
82
+ }
83
+ throw err;
84
+ }
85
+ }
86
+
87
+ /**
88
+ * @param {Object} options
89
+ * @param {boolean} [options.all]
90
+ * @param {string} [options.app]
91
+ * @returns {Promise<string[]>}
92
+ */
93
+ async function resolveTargetAppKeys(options) {
94
+ const useAll = Boolean(options.all);
95
+ const app = options.app && typeof options.app === 'string' ? options.app.trim() : '';
96
+ if (useAll && app) {
97
+ throw new Error('Use either --app <systemKey> or --all, not both');
98
+ }
99
+ if (!useAll && !app) {
100
+ throw new Error('Specify --app <systemKey> or --all');
101
+ }
102
+ if (useAll) {
103
+ return listIntegrationAppNames();
104
+ }
105
+ await assertIntegrationAppExists(app);
106
+ return [app];
107
+ }
108
+
109
+ /**
110
+ * @param {string[]} appKeys
111
+ * @param {'test'|'integration'|'e2e'|'trust'|'all'} type
112
+ * @returns {Promise<Array<{ appKey: string, logsDir: string, files: string[] }>>}
113
+ */
114
+ async function collectLogFilesByApp(appKeys, type) {
115
+ const groups = [];
116
+ for (const appKey of appKeys) {
117
+ const logsDir = path.join(getIntegrationPath(appKey), 'logs');
118
+ const files = await listMatchingLogFiles(logsDir, type);
119
+ if (files.length > 0) {
120
+ groups.push({ appKey, logsDir, files });
121
+ }
122
+ }
123
+ return groups;
124
+ }
125
+
126
+ /**
127
+ * @param {Array<{ appKey: string, files: string[] }>} groups
128
+ * @param {boolean} dryRun
129
+ * @returns {Promise<{ removedCount: number, paths: string[] }>}
130
+ */
131
+ async function removeCollectedFiles(groups, dryRun) {
132
+ const paths = groups.flatMap(g => g.files);
133
+ if (dryRun || paths.length === 0) {
134
+ return { removedCount: 0, paths };
135
+ }
136
+ await Promise.all(paths.map(filePath => fsp.unlink(filePath)));
137
+ return { removedCount: paths.length, paths };
138
+ }
139
+
140
+ /**
141
+ * @param {Object} options
142
+ * @param {boolean} options.dryRun
143
+ * @param {'test'|'integration'|'e2e'|'trust'|'all'} options.type
144
+ * @param {Array<{ appKey: string, files: string[] }>} groups
145
+ * @param {number} removedCount
146
+ */
147
+ function printCleanLogsSummary(options, groups, removedCount) {
148
+ const totalFiles = groups.reduce((n, g) => n + g.files.length, 0);
149
+ const appCount = groups.length;
150
+ if (options.dryRun) {
151
+ if (totalFiles === 0) {
152
+ logger.log(chalk.gray('No matching log files found.'));
153
+ return;
154
+ }
155
+ logger.log('');
156
+ logger.log(
157
+ chalk.gray(
158
+ `Would remove ${totalFiles} file(s) from ${appCount} integration folder(s) (type: ${options.type}).`
159
+ )
160
+ );
161
+ return;
162
+ }
163
+ if (totalFiles === 0) {
164
+ logger.log(chalk.gray('No matching log files to remove.'));
165
+ return;
166
+ }
167
+ logger.log('');
168
+ logger.log(formatSuccessLine(`Removed ${removedCount} log file(s) from ${appCount} integration folder(s).`));
169
+ }
170
+
171
+ /**
172
+ * @param {boolean} dryRun
173
+ * @param {Array<{ appKey: string, logsDir: string, files: string[] }>} groups
174
+ */
175
+ function printCleanLogsFileList(dryRun, groups) {
176
+ const prefix = dryRun ? 'Would remove' : 'Removing';
177
+ for (const { appKey, files } of groups) {
178
+ logger.log('');
179
+ logger.log(chalk.bold.white(`${appKey}/logs/`));
180
+ for (const filePath of files) {
181
+ logger.log(chalk.gray(` ${prefix}: ${filePath}`));
182
+ }
183
+ }
184
+ }
185
+
186
+ /**
187
+ * @param {Object} result
188
+ * @param {boolean} result.dryRun
189
+ * @param {string} result.type
190
+ * @param {string[]} result.appKeys
191
+ * @param {string[]} result.paths
192
+ * @param {number} result.removedCount
193
+ */
194
+ function printCleanLogsJson(result) {
195
+ const payload = {
196
+ dryRun: result.dryRun,
197
+ type: result.type,
198
+ appKeys: result.appKeys,
199
+ fileCount: result.paths.length,
200
+ paths: result.paths,
201
+ removedCount: result.removedCount
202
+ };
203
+ logger.log(JSON.stringify(payload, null, 2));
204
+ }
205
+
206
+ /**
207
+ * @async
208
+ * @param {Object} options
209
+ * @param {boolean} [options.all]
210
+ * @param {string} [options.app]
211
+ * @param {string} [options.type]
212
+ * @param {boolean} [options.dryRun]
213
+ * @param {boolean} [options.json]
214
+ * @returns {Promise<{ dryRun: boolean, type: string, appKeys: string[], paths: string[], removedCount: number }>}
215
+ */
216
+ async function runCleanLogs(options = {}) {
217
+ const dryRun = Boolean(options.dryRun);
218
+ const json = Boolean(options.json);
219
+ const type = normalizeLogCleanType(options.type);
220
+ const appKeys = await resolveTargetAppKeys(options);
221
+ const groups = await collectLogFilesByApp(appKeys, type);
222
+ const { removedCount, paths } = await removeCollectedFiles(groups, dryRun);
223
+ const result = { dryRun, type, appKeys, paths, removedCount };
224
+ if (json) {
225
+ printCleanLogsJson(result);
226
+ } else {
227
+ printCleanLogsFileList(dryRun, groups);
228
+ printCleanLogsSummary({ dryRun, type }, groups, removedCount);
229
+ }
230
+ return result;
231
+ }
232
+
233
+ module.exports = {
234
+ LOG_CLEAN_TYPES,
235
+ normalizeLogCleanType,
236
+ matchesLogCleanType,
237
+ listMatchingLogFiles,
238
+ resolveTargetAppKeys,
239
+ collectLogFilesByApp,
240
+ runCleanLogs
241
+ };