@aifabrix/builder 2.45.0 → 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 (180) hide show
  1. package/.cursor/rules/docs-rules.mdc +3 -1
  2. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
  3. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
  4. package/integration/hubspot-test/wizard-hubspot-e2e.yaml +1 -1
  5. package/jest.projects.js +44 -1
  6. package/lib/api/agent-metadata-validation.api.js +93 -0
  7. package/lib/api/audit-api-helpers.js +68 -0
  8. package/lib/api/audit.api.js +119 -0
  9. package/lib/api/auth-cache.api.js +44 -0
  10. package/lib/api/certificates.api.js +4 -4
  11. package/lib/api/credential.api.js +2 -2
  12. package/lib/api/dataplane-health.api.js +137 -0
  13. package/lib/api/dataplane-sync.api.js +27 -0
  14. package/lib/api/datasources-core.api.js +21 -18
  15. package/lib/api/datasources-extended.api.js +22 -22
  16. package/lib/api/dimensions.api.js +3 -0
  17. package/lib/api/external-systems.api.js +13 -13
  18. package/lib/api/governance-scenarios.api.js +39 -0
  19. package/lib/api/groups.api.js +79 -0
  20. package/lib/api/index.js +45 -2
  21. package/lib/api/pipeline.api.js +4 -4
  22. package/lib/api/protection.api.js +157 -0
  23. package/lib/api/records-bulk.api.js +30 -0
  24. package/lib/api/records-search-parse.js +49 -0
  25. package/lib/api/records-search.api.js +29 -0
  26. package/lib/api/types/agent-metadata-validation.types.js +67 -0
  27. package/lib/api/types/audit.types.js +72 -0
  28. package/lib/api/types/dataplane-health.types.js +27 -0
  29. package/lib/api/types/dimensions.types.js +2 -0
  30. package/lib/api/types/governance-scenarios.types.js +39 -0
  31. package/lib/api/types/protection.types.js +69 -0
  32. package/lib/api/types/records-bulk.types.js +35 -0
  33. package/lib/api/types/records-search.types.js +28 -0
  34. package/lib/api/user-groups.api.js +50 -0
  35. package/lib/api/users.api.js +95 -0
  36. package/lib/api/validation-run.api.js +3 -3
  37. package/lib/api/validation-runner.js +3 -3
  38. package/lib/api/wizard-platform.api.js +3 -3
  39. package/lib/api/wizard.api.js +21 -21
  40. package/lib/app/deploy.js +34 -19
  41. package/lib/cli/index.js +4 -2
  42. package/lib/cli/infra-guided.js +22 -3
  43. package/lib/cli/setup-app.help.js +50 -2
  44. package/lib/cli/setup-app.js +33 -24
  45. package/lib/cli/setup-app.test-commands.js +80 -21
  46. package/lib/cli/setup-auth.js +30 -3
  47. package/lib/cli/setup-external-system.js +37 -3
  48. package/lib/cli/setup-utility-help-after.js +45 -0
  49. package/lib/cli/setup-utility-resolve.js +2 -2
  50. package/lib/cli/setup-utility.js +99 -81
  51. package/lib/commands/auth-status-dataplane-version.js +231 -0
  52. package/lib/commands/auth-status-display.js +94 -0
  53. package/lib/commands/auth-status.js +70 -6
  54. package/lib/commands/credential-push.js +10 -5
  55. package/lib/commands/datasource-load-export-cli.js +178 -0
  56. package/lib/commands/datasource-test-trust-cli.js +114 -0
  57. package/lib/commands/datasource-unified-test-cli.js +25 -17
  58. package/lib/commands/datasource-unified-test-cli.options.js +18 -3
  59. package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
  60. package/lib/commands/datasource-verify-audit-cli.js +96 -0
  61. package/lib/commands/datasource.js +68 -1
  62. package/lib/commands/dimension-value.js +43 -13
  63. package/lib/commands/dimension.js +38 -7
  64. package/lib/commands/identity-apply-sync.js +131 -0
  65. package/lib/commands/identity-group-cli.js +146 -0
  66. package/lib/commands/identity-membership-role-cache.js +175 -0
  67. package/lib/commands/identity-shared.js +94 -0
  68. package/lib/commands/identity-user-cli.js +158 -0
  69. package/lib/commands/identity.js +43 -0
  70. package/lib/commands/login.js +46 -16
  71. package/lib/commands/protection-cli-leaves.js +286 -0
  72. package/lib/commands/protection.js +50 -0
  73. package/lib/commands/repair.js +13 -3
  74. package/lib/commands/test-e2e-external.js +40 -9
  75. package/lib/commands/test-governance-cli-options.js +47 -0
  76. package/lib/commands/test-governance-command-action.js +55 -0
  77. package/lib/commands/test-governance-external.js +88 -0
  78. package/lib/commands/test-trust-cli-options.js +32 -0
  79. package/lib/commands/test-trust-command-action.js +118 -0
  80. package/lib/commands/test-trust-external.js +127 -0
  81. package/lib/commands/upload.js +34 -13
  82. package/lib/commands/wizard-core.js +3 -0
  83. package/lib/core/config-device-dataplane.js +185 -0
  84. package/lib/datasource/agent-trust-debug-log.js +45 -0
  85. package/lib/datasource/agent-trust-fetch.js +115 -0
  86. package/lib/datasource/agent-trust-map.js +101 -0
  87. package/lib/datasource/agent-trust-mock.js +44 -0
  88. package/lib/datasource/agent-trust-run.js +93 -0
  89. package/lib/datasource/agent-trust-timeout.js +25 -0
  90. package/lib/datasource/audit-evidence-display.js +80 -0
  91. package/lib/datasource/audit-evidence-extract-debug.js +103 -0
  92. package/lib/datasource/audit-evidence-extract.js +177 -0
  93. package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
  94. package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
  95. package/lib/datasource/audit-evidence-poll.js +109 -0
  96. package/lib/datasource/audit-evidence-run.js +177 -0
  97. package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
  98. package/lib/datasource/audit-evidence-verify-matrix.js +177 -0
  99. package/lib/datasource/audit-evidence-verify.js +108 -0
  100. package/lib/datasource/bulk-loader-run-batches.js +88 -0
  101. package/lib/datasource/bulk-loader-service-retry.js +52 -0
  102. package/lib/datasource/bulk-loader-service.js +127 -0
  103. package/lib/datasource/capability/run-capability-dimension.js +2 -1
  104. package/lib/datasource/datasource-exporter-resolve.js +82 -0
  105. package/lib/datasource/datasource-exporter-service.js +144 -0
  106. package/lib/datasource/datasource-identifier.js +148 -0
  107. package/lib/datasource/datasource-load-export-context.js +92 -0
  108. package/lib/datasource/integration-context.js +6 -4
  109. package/lib/datasource/local-data-paths.js +145 -0
  110. package/lib/datasource/log-cleaner.js +241 -0
  111. package/lib/datasource/log-viewer.js +111 -6
  112. package/lib/datasource/record-file-parser.js +85 -0
  113. package/lib/datasource/record-mapper.js +129 -0
  114. package/lib/datasource/resolve-app.js +74 -55
  115. package/lib/datasource/test-e2e.js +26 -2
  116. package/lib/datasource/unified-validation-run-resolve.js +45 -12
  117. package/lib/datasource/unified-validation-run.js +43 -17
  118. package/lib/datasource/validate.js +37 -20
  119. package/lib/external-system/test-system-level.js +25 -4
  120. package/lib/external-system/test.js +3 -1
  121. package/lib/governance/governance-pack-loader.js +70 -0
  122. package/lib/governance/governance-run-display.js +119 -0
  123. package/lib/identity/identity-apply-core.js +108 -0
  124. package/lib/identity/identity-apply-phases.js +244 -0
  125. package/lib/identity/identity-apply-service.js +77 -0
  126. package/lib/identity/identity-csv-parser.js +114 -0
  127. package/lib/internal/fs-real-sync.js +29 -1
  128. package/lib/protection/auth-context.js +45 -0
  129. package/lib/protection/convert-batch.js +90 -0
  130. package/lib/protection/load.js +41 -0
  131. package/lib/protection/paths.js +158 -0
  132. package/lib/protection/preflight-datasource-ready.js +46 -0
  133. package/lib/protection/protection-create-probe-lines.js +142 -0
  134. package/lib/protection/protection-create-probes.js +95 -0
  135. package/lib/protection/protection-create-scaffold.js +281 -0
  136. package/lib/protection/protection-display-helpers.js +445 -0
  137. package/lib/protection/protection-display.js +215 -0
  138. package/lib/protection/protection-preset-registry.js +97 -0
  139. package/lib/protection/report-exit.js +62 -0
  140. package/lib/protection/resolve.js +202 -0
  141. package/lib/protection/run-commands-validate.js +130 -0
  142. package/lib/protection/run-commands.js +216 -0
  143. package/lib/protection/run-protection-create-helpers.js +134 -0
  144. package/lib/protection/run-protection-create-probes-flow.js +43 -0
  145. package/lib/protection/run-protection-create.js +191 -0
  146. package/lib/protection/scope.js +22 -0
  147. package/lib/protection/sync-after-upload.js +64 -0
  148. package/lib/protection/upload-batch.js +127 -0
  149. package/lib/protection/validate-batch.js +113 -0
  150. package/lib/protection/validate-local.js +65 -0
  151. package/lib/resolvers/dimension-file.js +29 -4
  152. package/lib/schema/external-datasource.schema.json +47 -13
  153. package/lib/schema/flag-map-validation-run.json +2 -2
  154. package/lib/schema/protection.schema.json +374 -0
  155. package/lib/schema/type/credential-field-registry.json +636 -0
  156. package/lib/schema/wizard-config.schema.json +1 -1
  157. package/lib/utils/agent-trust-run-display.js +231 -0
  158. package/lib/utils/agent-trust-run-exit.js +59 -0
  159. package/lib/utils/agent-trust-wait-ui.js +94 -0
  160. package/lib/utils/cli-exit-codes.js +30 -0
  161. package/lib/utils/cli-sync-options.js +33 -0
  162. package/lib/utils/credential-push-ui.js +90 -0
  163. package/lib/utils/dataplane-cli-version-gate.js +202 -0
  164. package/lib/utils/dataplane-cli-version-help.js +74 -0
  165. package/lib/utils/datasource-export-display.js +137 -0
  166. package/lib/utils/datasource-load-display.js +174 -0
  167. package/lib/utils/datasource-load-export-ui.js +120 -0
  168. package/lib/utils/datasource-test-run-capability-scope.js +3 -3
  169. package/lib/utils/datasource-test-run-display.js +3 -3
  170. package/lib/utils/datasource-test-run-tty-log.js +5 -1
  171. package/lib/utils/help-builder.js +30 -24
  172. package/lib/utils/manifest-source-emit.js +22 -0
  173. package/lib/utils/semver-compare.js +127 -0
  174. package/lib/utils/upload-pipeline-ui.js +91 -0
  175. package/lib/utils/validation-poll-ui.js +14 -7
  176. package/lib/validation/wizard-config-validator.js +58 -19
  177. package/package.json +1 -1
  178. package/templates/applications/dataplane/env.template +13 -1
  179. package/templates/applications/dataplane/rbac.yaml +4 -0
  180. package/templates/external-system/README.md.hbs +56 -8
@@ -0,0 +1,167 @@
1
+ /**
2
+ * @fileoverview Helpers for audit evidence matrix verification (sub-resource validators, rows 8–9)
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { checkPerExecutionRow } = require('./audit-evidence-matrix-rows');
10
+ const {
11
+ normalizeAuditOperation,
12
+ executionsCoverExpectedOperations
13
+ } = require('./audit-evidence-operation-aliases');
14
+
15
+ /**
16
+ * @param {Object} input
17
+ * @returns {import('../api/types/audit.types').AuditEvidenceMatrixRow}
18
+ */
19
+ function checkRow8CorrelationGroup(input) {
20
+ const row = { row: 8, status: 'failed', detail: '', code: 'correlationGroup' };
21
+ const items = input.executions || [];
22
+ if (!items.length) {
23
+ return {
24
+ ...row,
25
+ detail: input.correlationId
26
+ ? 'no executions for correlation or datasource filter'
27
+ : 'no correlationId and no executions',
28
+ code: 'correlationEmpty'
29
+ };
30
+ }
31
+ const expected = (input.expectedOperations || [])
32
+ .map(normalizeAuditOperation)
33
+ .filter(Boolean);
34
+ if (expected.length && !executionsCoverExpectedOperations(items, expected)) {
35
+ const ops = new Set(
36
+ items.map(e => normalizeAuditOperation(e && e.operation)).filter(Boolean)
37
+ );
38
+ const missing = expected.filter(op => !ops.has(op));
39
+ return {
40
+ ...row,
41
+ detail: `missing ops: ${missing.join(', ')}`,
42
+ code: 'correlationOpsMissing'
43
+ };
44
+ }
45
+ const opCount = new Set(
46
+ items.map(e => normalizeAuditOperation(e && e.operation)).filter(Boolean)
47
+ ).size;
48
+ return {
49
+ ...row,
50
+ status: 'passed',
51
+ detail: `correlation ops=${opCount || items.length}`,
52
+ code: 'correlationGroup'
53
+ };
54
+ }
55
+
56
+ /**
57
+ * Prefer executions tied to this run (by id), then correlation query, then datasource list.
58
+ * @param {Object} ctx
59
+ * @param {Array} executions
60
+ * @param {Array} corrQueryRows
61
+ * @returns {Array}
62
+ */
63
+ function resolveRow8Executions(ctx, executions, corrQueryRows) {
64
+ const idSet = new Set(
65
+ (ctx.executionIds || []).map(id => String(id).trim()).filter(Boolean)
66
+ );
67
+ let byId = [];
68
+ if (idSet.size && executions && executions.length) {
69
+ byId = executions.filter(e => e && e.id && idSet.has(String(e.id)));
70
+ }
71
+ const expected = ctx.expectedOperations || [];
72
+ if (byId.length && executionsCoverExpectedOperations(byId, expected)) {
73
+ return byId;
74
+ }
75
+ if (corrQueryRows && corrQueryRows.length && executionsCoverExpectedOperations(corrQueryRows, expected)) {
76
+ return corrQueryRows;
77
+ }
78
+ if (byId.length) return byId;
79
+ if (corrQueryRows && corrQueryRows.length) return corrQueryRows;
80
+ return executions || [];
81
+ }
82
+
83
+ /**
84
+ * @param {Set<string>} traceIds
85
+ * @param {string[]} execIds
86
+ * @returns {import('../api/types/audit.types').AuditEvidenceMatrixRow}
87
+ */
88
+ function checkRow9Traces(traceIds, execIds) {
89
+ const row = { row: 9, status: 'failed', detail: '', code: 'traceMismatch' };
90
+ if (!execIds.length) {
91
+ return { ...row, detail: 'no execution ids', code: 'auditIdsMissing' };
92
+ }
93
+ if (traceIds.size < 1) {
94
+ return { ...row, detail: 'no trace payloads resolved', code: 'traceEmpty' };
95
+ }
96
+ if (traceIds.size < execIds.length) {
97
+ return {
98
+ ...row,
99
+ detail: `distinct traces=${traceIds.size} executionIds=${execIds.length}`,
100
+ code: 'traceCountLow'
101
+ };
102
+ }
103
+ return { ...row, status: 'passed', detail: `traces=${traceIds.size} ids=${execIds.length}` };
104
+ }
105
+
106
+ function isRbacSubResourceValid(body) {
107
+ return !!(body && typeof body === 'object' && (body.id || body.decision || body.summary));
108
+ }
109
+
110
+ function isAbacSubResourceValid(body) {
111
+ return !!(body && typeof body === 'object' && (body.id || body.decision || body.traceId));
112
+ }
113
+
114
+ function isErrorsValid(body) {
115
+ if (Array.isArray(body)) return true;
116
+ if (body && typeof body === 'object' && Array.isArray(body.data)) return true;
117
+ return body && typeof body === 'object';
118
+ }
119
+
120
+ function isStepsValid(body) {
121
+ if (Array.isArray(body) && body.length > 0) return true;
122
+ if (body && Array.isArray(body.steps) && body.steps.length > 0) return true;
123
+ return body && typeof body === 'object' && Object.keys(body).length > 0;
124
+ }
125
+
126
+ /**
127
+ * @param {Array} executions
128
+ * @param {string[]} fallbackIds
129
+ * @returns {string[]}
130
+ */
131
+ function resolveExecutionIdsForChecks(executions, fallbackIds) {
132
+ const fromRows = (executions || [])
133
+ .map(e => (e && e.id ? String(e.id).trim() : ''))
134
+ .filter(Boolean);
135
+ return [...new Set(fromRows.concat(fallbackIds || []))];
136
+ }
137
+
138
+ /**
139
+ * @param {Object} opts
140
+ * @returns {Promise<import('../api/types/audit.types').AuditEvidenceMatrixRow>}
141
+ */
142
+ async function checkPerExecutionSub(opts) {
143
+ const { dataplaneUrl, authConfig, executionId, rowNum, label, fetcher, isValid } = opts;
144
+ try {
145
+ const body = await fetcher(dataplaneUrl, authConfig, executionId);
146
+ return checkPerExecutionRow(rowNum, `${label}Null`, label, body, isValid);
147
+ } catch (err) {
148
+ return {
149
+ row: rowNum,
150
+ status: 'failed',
151
+ detail: `${label}: ${err.message}`,
152
+ code: `${label}Error`
153
+ };
154
+ }
155
+ }
156
+
157
+ module.exports = {
158
+ checkRow8CorrelationGroup,
159
+ resolveRow8Executions,
160
+ checkRow9Traces,
161
+ isRbacSubResourceValid,
162
+ isAbacSubResourceValid,
163
+ isErrorsValid,
164
+ isStepsValid,
165
+ resolveExecutionIdsForChecks,
166
+ checkPerExecutionSub
167
+ };
@@ -0,0 +1,177 @@
1
+ /**
2
+ * @fileoverview Build matrix rows 1–9 for audit evidence verification
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const {
10
+ queryExecutions,
11
+ queryRbacDecisions,
12
+ queryAbacTraces,
13
+ getExecutionRbac,
14
+ getExecutionAbac,
15
+ getExecutionErrors,
16
+ getExecutionSteps,
17
+ getExecutionTrace
18
+ } = require('../api/audit.api');
19
+ const {
20
+ checkRow1ExecutionsByDatasource,
21
+ checkRow2RbacDecisionsList,
22
+ checkRow3AbacTracesList
23
+ } = require('./audit-evidence-matrix-rows');
24
+ const {
25
+ operationQueryVariants,
26
+ executionsCoverExpectedOperations
27
+ } = require('./audit-evidence-operation-aliases');
28
+ const {
29
+ checkRow8CorrelationGroup,
30
+ resolveRow8Executions,
31
+ checkRow9Traces,
32
+ isRbacSubResourceValid,
33
+ isAbacSubResourceValid,
34
+ isErrorsValid,
35
+ isStepsValid,
36
+ resolveExecutionIdsForChecks,
37
+ checkPerExecutionSub
38
+ } = require('./audit-evidence-verify-helpers');
39
+
40
+ /**
41
+ * @param {string} dataplaneUrl
42
+ * @param {Object} authConfig
43
+ * @param {string} correlationId
44
+ * @param {string[]} expectedOperations
45
+ * @returns {Promise<Array>}
46
+ */
47
+ async function fetchCorrelationExecutionsByOperation(
48
+ dataplaneUrl,
49
+ authConfig,
50
+ correlationId,
51
+ expectedOperations
52
+ ) {
53
+ const deduped = new Map();
54
+ for (const rawOp of expectedOperations) {
55
+ const variants = operationQueryVariants(rawOp);
56
+ for (const op of variants) {
57
+ const res = await queryExecutions(dataplaneUrl, authConfig, {
58
+ correlationId,
59
+ operation: op,
60
+ pageSize: 20
61
+ });
62
+ for (const row of res.data || []) {
63
+ if (row && row.id) deduped.set(String(row.id), row);
64
+ }
65
+ }
66
+ }
67
+ const merged = [...deduped.values()];
68
+ return executionsCoverExpectedOperations(merged, expectedOperations) ? merged : [];
69
+ }
70
+
71
+ /**
72
+ * @param {Object} ctx
73
+ * @param {Object} polled
74
+ * @param {number} minExpected
75
+ * @returns {Promise<import('../api/types/audit.types').AuditEvidenceMatrixRow[]>}
76
+ */
77
+ async function buildListMatrixRows(ctx, polled, minExpected) {
78
+ const executions = polled.data || [];
79
+ const { dataplaneUrl, authConfig } = ctx;
80
+ const totalItems =
81
+ polled.meta && polled.meta.totalItems !== undefined
82
+ ? polled.meta.totalItems
83
+ : executions.length;
84
+ const rows = [
85
+ checkRow1ExecutionsByDatasource({
86
+ executions,
87
+ totalItems,
88
+ minExpected
89
+ })
90
+ ];
91
+ const rbacList = await queryRbacDecisions(dataplaneUrl, authConfig, {
92
+ targetDatasourceKey: ctx.datasourceKey,
93
+ pageSize: 100
94
+ });
95
+ rows.push(
96
+ checkRow2RbacDecisionsList({
97
+ rbacItems: rbacList.data,
98
+ totalItems: rbacList.meta && rbacList.meta.totalItems
99
+ })
100
+ );
101
+ const abacList = await queryAbacTraces(dataplaneUrl, authConfig, {
102
+ datasourceKey: ctx.datasourceKey,
103
+ pageSize: 100
104
+ });
105
+ rows.push(
106
+ checkRow3AbacTracesList({
107
+ abacItems: abacList.data,
108
+ totalItems: abacList.meta && abacList.meta.totalItems
109
+ })
110
+ );
111
+ return rows;
112
+ }
113
+
114
+ /**
115
+ * @param {Object} ctx
116
+ * @param {Array} executions
117
+ * @param {string[]} executionIds
118
+ * @returns {Promise<{ rows: import('../api/types/audit.types').AuditEvidenceMatrixRow[], execIds: string[] }>}
119
+ */
120
+ async function buildPerExecutionAndTailRows(ctx, executions, executionIds) {
121
+ const { dataplaneUrl, authConfig } = ctx;
122
+ const execIds = resolveExecutionIdsForChecks(executions, executionIds);
123
+ const sampleId = execIds[0];
124
+ const base = { dataplaneUrl, authConfig, executionId: sampleId };
125
+ const rows = await Promise.all([
126
+ checkPerExecutionSub({ ...base, rowNum: 4, label: 'rbac', fetcher: getExecutionRbac, isValid: isRbacSubResourceValid }),
127
+ checkPerExecutionSub({ ...base, rowNum: 5, label: 'abac', fetcher: getExecutionAbac, isValid: isAbacSubResourceValid }),
128
+ checkPerExecutionSub({ ...base, rowNum: 6, label: 'errors', fetcher: getExecutionErrors, isValid: isErrorsValid }),
129
+ checkPerExecutionSub({ ...base, rowNum: 7, label: 'steps', fetcher: getExecutionSteps, isValid: isStepsValid })
130
+ ]);
131
+
132
+ let corrQueryRows = [];
133
+ if (ctx.correlationId) {
134
+ const corr = await queryExecutions(dataplaneUrl, authConfig, {
135
+ correlationId: ctx.correlationId,
136
+ pageSize: 100
137
+ });
138
+ corrQueryRows = corr.data || [];
139
+ }
140
+ let row8Pool = corrQueryRows.length ? corrQueryRows : executions;
141
+ const expected = ctx.expectedOperations || [];
142
+ if (expected.length && ctx.correlationId) {
143
+ const byOp = await fetchCorrelationExecutionsByOperation(
144
+ dataplaneUrl,
145
+ authConfig,
146
+ ctx.correlationId,
147
+ expected
148
+ );
149
+ if (byOp.length) row8Pool = byOp;
150
+ }
151
+ const row8Executions = resolveRow8Executions(ctx, executions, row8Pool);
152
+ rows.push(
153
+ checkRow8CorrelationGroup({
154
+ correlationId: ctx.correlationId,
155
+ executions: row8Executions,
156
+ expectedOperations: ctx.expectedOperations
157
+ })
158
+ );
159
+
160
+ const traceIds = new Set();
161
+ for (const id of execIds.slice(0, Math.min(execIds.length, 12))) {
162
+ try {
163
+ const tr = await getExecutionTrace(dataplaneUrl, authConfig, id);
164
+ if (tr && tr.executionId) traceIds.add(String(tr.executionId));
165
+ else if (tr && tr.id) traceIds.add(String(tr.id));
166
+ } catch {
167
+ /* row 9 may fail */
168
+ }
169
+ }
170
+ rows.push(checkRow9Traces(traceIds, execIds));
171
+ return { rows, execIds };
172
+ }
173
+
174
+ module.exports = {
175
+ buildListMatrixRows,
176
+ buildPerExecutionAndTailRows
177
+ };
@@ -0,0 +1,108 @@
1
+ /**
2
+ * @fileoverview Run Plan 407.1 nine-row audit evidence matrix against dataplane APIs
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { queryExecutions } = require('../api/audit.api');
10
+ const { pollExecutionsUntilReady } = require('./audit-evidence-poll');
11
+ const {
12
+ buildListMatrixRows,
13
+ buildPerExecutionAndTailRows
14
+ } = require('./audit-evidence-verify-matrix');
15
+ const { checkRow8CorrelationGroup, checkRow9Traces } = require('./audit-evidence-verify-helpers');
16
+
17
+ /**
18
+ * @param {Object} ctx
19
+ * @returns {number}
20
+ */
21
+ /**
22
+ * E2E runs tag executions with testRunId as correlationId; prefer that for list poll.
23
+ * @param {Object} ctx
24
+ * @returns {Object}
25
+ */
26
+ function buildExecutionsPollQuery(ctx) {
27
+ const base = { pageSize: 100, sort: '-startedAt' };
28
+ if (ctx.correlationId) {
29
+ return { ...base, correlationId: ctx.correlationId };
30
+ }
31
+ return { ...base, datasourceKey: ctx.datasourceKey };
32
+ }
33
+
34
+ function resolveMinExpected(ctx) {
35
+ const fromOps = ctx.expectedOperations && ctx.expectedOperations.length;
36
+ const fromIds = ctx.executionIds && ctx.executionIds.length;
37
+ if (fromIds && fromOps) {
38
+ return Math.max(1, Math.min(fromOps, fromIds));
39
+ }
40
+ return Math.max(1, fromOps || fromIds || 1);
41
+ }
42
+
43
+ /**
44
+ * @param {string} datasourceKey
45
+ * @param {Object} ctx
46
+ * @returns {import('../api/types/audit.types').AuditEvidenceVerification}
47
+ */
48
+ function failedMissingIds(datasourceKey, ctx) {
49
+ return {
50
+ status: 'failed',
51
+ datasourceKey,
52
+ correlationId: ctx.correlationId || null,
53
+ executionIds: [],
54
+ matrix: [
55
+ {
56
+ row: 0,
57
+ status: 'failed',
58
+ detail: 'missing audit.executionIds in envelope',
59
+ code: 'auditIdsMissing'
60
+ }
61
+ ]
62
+ };
63
+ }
64
+
65
+ /**
66
+ * @param {Object} ctx
67
+ * @returns {Promise<import('../api/types/audit.types').AuditEvidenceVerification>}
68
+ */
69
+ async function verifyAuditEvidenceMatrix(ctx) {
70
+ const datasourceKey = ctx.datasourceKey || '';
71
+ const executionIds = Array.isArray(ctx.executionIds) ? ctx.executionIds : [];
72
+ if (!executionIds.length && !ctx.correlationId) {
73
+ return failedMissingIds(datasourceKey, ctx);
74
+ }
75
+
76
+ const { dataplaneUrl, authConfig } = ctx;
77
+ const minExpected = resolveMinExpected(ctx);
78
+ const execQuery = buildExecutionsPollQuery(ctx);
79
+ const polled = await pollExecutionsUntilReady({
80
+ fetchExecutions: () => queryExecutions(dataplaneUrl, authConfig, execQuery),
81
+ minExpected,
82
+ executionIds,
83
+ maxWaitMs: ctx.maxWaitMs,
84
+ intervalMs: ctx.intervalMs
85
+ });
86
+
87
+ const executions = polled.data || [];
88
+ const matrix = await buildListMatrixRows(ctx, polled, minExpected);
89
+ const tail = await buildPerExecutionAndTailRows(ctx, executions, executionIds);
90
+ matrix.push(...tail.rows);
91
+
92
+ const passed = matrix.every(r => r.status === 'passed');
93
+ return {
94
+ status: passed ? 'passed' : 'failed',
95
+ datasourceKey,
96
+ correlationId: ctx.correlationId || null,
97
+ executionIds: tail.execIds,
98
+ matrix
99
+ };
100
+ }
101
+
102
+ module.exports = {
103
+ verifyAuditEvidenceMatrix,
104
+ buildExecutionsPollQuery,
105
+ resolveMinExpected,
106
+ checkRow8CorrelationGroup,
107
+ checkRow9Traces
108
+ };
@@ -0,0 +1,88 @@
1
+ /**
2
+ * @fileoverview Batch upload loop for datasource load (plan 144)
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { bulkSyncRecords } = require('../api/records-bulk.api');
10
+ const { withTransientRetry, errorStatusCode } = require('./bulk-loader-service-retry');
11
+
12
+ /**
13
+ * @param {Object} state
14
+ * @param {number} index
15
+ * @param {Error} err
16
+ * @param {Object[]} batchRecords
17
+ */
18
+ function recordBatchFailure(state, index, err, batchRecords) {
19
+ state.batches.push({
20
+ index: index + 1,
21
+ total: state.chunkCount,
22
+ error: err.message || String(err),
23
+ statusCode: errorStatusCode(err),
24
+ recordCount: batchRecords.length,
25
+ failed: batchRecords.map(r => ({ key: r.key, message: err.message || String(err) }))
26
+ });
27
+ state.totals.failedCount += batchRecords.length;
28
+ }
29
+
30
+ /**
31
+ * @param {Object} state
32
+ * @param {number} index
33
+ * @param {Object} response
34
+ * @param {Object[]} batchRecords
35
+ */
36
+ function recordBatchSuccess(state, index, response, batchRecords) {
37
+ const inserted = response.insertedCount || 0;
38
+ const updated = response.updatedCount || 0;
39
+ const processed = response.totalProcessed || batchRecords.length;
40
+ const failedInBatch = Math.max(0, batchRecords.length - processed);
41
+
42
+ state.batches.push({
43
+ index: index + 1,
44
+ total: state.chunkCount,
45
+ insertedCount: inserted,
46
+ updatedCount: updated,
47
+ totalProcessed: processed,
48
+ failed: failedInBatch > 0 ? [{ message: 'Some records were not processed' }] : []
49
+ });
50
+ state.totals.insertedCount += inserted;
51
+ state.totals.updatedCount += updated;
52
+ state.totals.failedCount += failedInBatch;
53
+ }
54
+
55
+ /**
56
+ * @async
57
+ * @param {Object} params
58
+ * @returns {Promise<{ batches: Object[], totals: Object }>}
59
+ */
60
+ async function uploadRecordBatches(params) {
61
+ const { chunks, syncType, dataplaneUrl, datasourceKey, authConfig } = params;
62
+ const state = {
63
+ batches: [],
64
+ totals: { insertedCount: 0, updatedCount: 0, failedCount: 0 },
65
+ chunkCount: chunks.length
66
+ };
67
+
68
+ for (let i = 0; i < chunks.length; i += 1) {
69
+ const batchRecords = chunks[i];
70
+ const body = { syncType, sync: false, records: batchRecords };
71
+ try {
72
+ const response = await withTransientRetry(() =>
73
+ bulkSyncRecords(dataplaneUrl, datasourceKey, authConfig, body)
74
+ );
75
+ recordBatchSuccess(state, i, response, batchRecords);
76
+ } catch (err) {
77
+ recordBatchFailure(state, i, err, batchRecords);
78
+ }
79
+ }
80
+
81
+ return { batches: state.batches, totals: state.totals };
82
+ }
83
+
84
+ module.exports = {
85
+ uploadRecordBatches,
86
+ recordBatchFailure,
87
+ recordBatchSuccess
88
+ };
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @fileoverview Retry helpers for bulk loader (plan 144)
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const MAX_RETRIES = 3;
10
+ const RETRY_STATUS = new Set([429, 500, 502, 503, 504]);
11
+
12
+ /**
13
+ * @param {Error} err
14
+ * @returns {number|null}
15
+ */
16
+ function errorStatusCode(err) {
17
+ if (!err || typeof err !== 'object') return null;
18
+ if (typeof err.statusCode === 'number') return err.statusCode;
19
+ if (err.response && typeof err.response.status === 'number') return err.response.status;
20
+ return null;
21
+ }
22
+
23
+ function sleep(ms) {
24
+ return new Promise(resolve => setTimeout(resolve, ms));
25
+ }
26
+
27
+ /**
28
+ * @async
29
+ * @param {Function} fn
30
+ * @returns {Promise<*>}
31
+ */
32
+ async function withTransientRetry(fn) {
33
+ let lastErr;
34
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt += 1) {
35
+ try {
36
+ return await fn();
37
+ } catch (err) {
38
+ lastErr = err;
39
+ const code = errorStatusCode(err);
40
+ if (!code || !RETRY_STATUS.has(code) || attempt >= MAX_RETRIES - 1) {
41
+ throw err;
42
+ }
43
+ await sleep(500 * (attempt + 1));
44
+ }
45
+ }
46
+ throw lastErr;
47
+ }
48
+
49
+ module.exports = {
50
+ withTransientRetry,
51
+ errorStatusCode
52
+ };