@veraxhq/verax 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -6
- package/bin/verax.js +11 -11
- package/package.json +29 -8
- package/src/cli/commands/baseline.js +103 -0
- package/src/cli/commands/default.js +51 -6
- package/src/cli/commands/doctor.js +29 -0
- package/src/cli/commands/ga.js +246 -0
- package/src/cli/commands/gates.js +95 -0
- package/src/cli/commands/inspect.js +4 -2
- package/src/cli/commands/release-check.js +215 -0
- package/src/cli/commands/run.js +45 -6
- package/src/cli/commands/security-check.js +212 -0
- package/src/cli/commands/truth.js +113 -0
- package/src/cli/entry.js +30 -20
- package/src/cli/util/angular-component-extractor.js +179 -0
- package/src/cli/util/angular-navigation-detector.js +141 -0
- package/src/cli/util/angular-network-detector.js +161 -0
- package/src/cli/util/angular-state-detector.js +162 -0
- package/src/cli/util/ast-interactive-detector.js +544 -0
- package/src/cli/util/ast-network-detector.js +603 -0
- package/src/cli/util/ast-promise-extractor.js +581 -0
- package/src/cli/util/ast-usestate-detector.js +602 -0
- package/src/cli/util/atomic-write.js +12 -1
- package/src/cli/util/bootstrap-guard.js +86 -0
- package/src/cli/util/console-reporter.js +72 -0
- package/src/cli/util/detection-engine.js +105 -41
- package/src/cli/util/determinism-runner.js +124 -0
- package/src/cli/util/determinism-writer.js +129 -0
- package/src/cli/util/digest-engine.js +359 -0
- package/src/cli/util/dom-diff.js +226 -0
- package/src/cli/util/evidence-engine.js +287 -0
- package/src/cli/util/expectation-extractor.js +151 -5
- package/src/cli/util/findings-writer.js +3 -0
- package/src/cli/util/framework-detector.js +572 -0
- package/src/cli/util/idgen.js +1 -1
- package/src/cli/util/interaction-planner.js +529 -0
- package/src/cli/util/learn-writer.js +2 -0
- package/src/cli/util/ledger-writer.js +110 -0
- package/src/cli/util/monorepo-resolver.js +162 -0
- package/src/cli/util/observation-engine.js +127 -278
- package/src/cli/util/observe-writer.js +2 -0
- package/src/cli/util/project-discovery.js +284 -0
- package/src/cli/util/project-writer.js +2 -0
- package/src/cli/util/run-id.js +23 -27
- package/src/cli/util/run-resolver.js +64 -0
- package/src/cli/util/run-result.js +778 -0
- package/src/cli/util/selector-resolver.js +235 -0
- package/src/cli/util/source-requirement.js +55 -0
- package/src/cli/util/summary-writer.js +2 -0
- package/src/cli/util/svelte-navigation-detector.js +163 -0
- package/src/cli/util/svelte-network-detector.js +80 -0
- package/src/cli/util/svelte-sfc-extractor.js +146 -0
- package/src/cli/util/svelte-state-detector.js +242 -0
- package/src/cli/util/trust-activation-integration.js +496 -0
- package/src/cli/util/trust-activation-wrapper.js +85 -0
- package/src/cli/util/trust-integration-hooks.js +164 -0
- package/src/cli/util/types.js +153 -0
- package/src/cli/util/url-validation.js +40 -0
- package/src/cli/util/vue-navigation-detector.js +178 -0
- package/src/cli/util/vue-sfc-extractor.js +161 -0
- package/src/cli/util/vue-state-detector.js +215 -0
- package/src/types/fs-augment.d.ts +23 -0
- package/src/types/global.d.ts +137 -0
- package/src/types/internal-types.d.ts +35 -0
- package/src/verax/cli/init.js +4 -18
- package/src/verax/core/action-classifier.js +4 -3
- package/src/verax/core/artifacts/registry.js +139 -0
- package/src/verax/core/artifacts/verifier.js +990 -0
- package/src/verax/core/baseline/baseline.enforcer.js +137 -0
- package/src/verax/core/baseline/baseline.snapshot.js +233 -0
- package/src/verax/core/capabilities/gates.js +505 -0
- package/src/verax/core/capabilities/registry.js +475 -0
- package/src/verax/core/confidence/confidence-compute.js +144 -0
- package/src/verax/core/confidence/confidence-invariants.js +234 -0
- package/src/verax/core/confidence/confidence-report-writer.js +112 -0
- package/src/verax/core/confidence/confidence-weights.js +44 -0
- package/src/verax/core/confidence/confidence.defaults.js +65 -0
- package/src/verax/core/confidence/confidence.loader.js +80 -0
- package/src/verax/core/confidence/confidence.schema.js +94 -0
- package/src/verax/core/confidence-engine-refactor.js +489 -0
- package/src/verax/core/confidence-engine.js +625 -0
- package/src/verax/core/contracts/index.js +29 -0
- package/src/verax/core/contracts/types.js +186 -0
- package/src/verax/core/contracts/validators.js +456 -0
- package/src/verax/core/decisions/decision.trace.js +278 -0
- package/src/verax/core/determinism/contract-writer.js +89 -0
- package/src/verax/core/determinism/contract.js +139 -0
- package/src/verax/core/determinism/diff.js +405 -0
- package/src/verax/core/determinism/engine.js +222 -0
- package/src/verax/core/determinism/finding-identity.js +149 -0
- package/src/verax/core/determinism/normalize.js +466 -0
- package/src/verax/core/determinism/report-writer.js +93 -0
- package/src/verax/core/determinism/run-fingerprint.js +123 -0
- package/src/verax/core/dynamic-route-intelligence.js +529 -0
- package/src/verax/core/evidence/evidence-capture-service.js +308 -0
- package/src/verax/core/evidence/evidence-intent-ledger.js +166 -0
- package/src/verax/core/evidence-builder.js +487 -0
- package/src/verax/core/execution-mode-context.js +77 -0
- package/src/verax/core/execution-mode-detector.js +192 -0
- package/src/verax/core/failures/exit-codes.js +88 -0
- package/src/verax/core/failures/failure-summary.js +76 -0
- package/src/verax/core/failures/failure.factory.js +225 -0
- package/src/verax/core/failures/failure.ledger.js +133 -0
- package/src/verax/core/failures/failure.types.js +196 -0
- package/src/verax/core/failures/index.js +10 -0
- package/src/verax/core/ga/ga-report-writer.js +43 -0
- package/src/verax/core/ga/ga.artifact.js +49 -0
- package/src/verax/core/ga/ga.contract.js +435 -0
- package/src/verax/core/ga/ga.enforcer.js +87 -0
- package/src/verax/core/guardrails/guardrails-report-writer.js +109 -0
- package/src/verax/core/guardrails/policy.defaults.js +210 -0
- package/src/verax/core/guardrails/policy.loader.js +84 -0
- package/src/verax/core/guardrails/policy.schema.js +110 -0
- package/src/verax/core/guardrails/truth-reconciliation.js +136 -0
- package/src/verax/core/guardrails-engine.js +505 -0
- package/src/verax/core/incremental-store.js +1 -0
- package/src/verax/core/integrity/budget.js +138 -0
- package/src/verax/core/integrity/determinism.js +342 -0
- package/src/verax/core/integrity/integrity.js +208 -0
- package/src/verax/core/integrity/poisoning.js +108 -0
- package/src/verax/core/integrity/transaction.js +140 -0
- package/src/verax/core/observe/run-timeline.js +318 -0
- package/src/verax/core/perf/perf.contract.js +186 -0
- package/src/verax/core/perf/perf.display.js +65 -0
- package/src/verax/core/perf/perf.enforcer.js +91 -0
- package/src/verax/core/perf/perf.monitor.js +209 -0
- package/src/verax/core/perf/perf.report.js +200 -0
- package/src/verax/core/pipeline-tracker.js +243 -0
- package/src/verax/core/product-definition.js +127 -0
- package/src/verax/core/release/provenance.builder.js +130 -0
- package/src/verax/core/release/release-report-writer.js +40 -0
- package/src/verax/core/release/release.enforcer.js +164 -0
- package/src/verax/core/release/reproducibility.check.js +222 -0
- package/src/verax/core/release/sbom.builder.js +292 -0
- package/src/verax/core/replay-validator.js +2 -0
- package/src/verax/core/replay.js +4 -0
- package/src/verax/core/report/cross-index.js +195 -0
- package/src/verax/core/report/human-summary.js +362 -0
- package/src/verax/core/route-intelligence.js +420 -0
- package/src/verax/core/run-id.js +6 -3
- package/src/verax/core/run-manifest.js +4 -3
- package/src/verax/core/security/secrets.scan.js +329 -0
- package/src/verax/core/security/security-report.js +50 -0
- package/src/verax/core/security/security.enforcer.js +128 -0
- package/src/verax/core/security/supplychain.defaults.json +38 -0
- package/src/verax/core/security/supplychain.policy.js +334 -0
- package/src/verax/core/security/vuln.scan.js +265 -0
- package/src/verax/core/truth/truth.certificate.js +252 -0
- package/src/verax/core/ui-feedback-intelligence.js +481 -0
- package/src/verax/detect/conditional-ui-silent-failure.js +84 -0
- package/src/verax/detect/confidence-engine.js +62 -34
- package/src/verax/detect/confidence-helper.js +34 -0
- package/src/verax/detect/dynamic-route-findings.js +338 -0
- package/src/verax/detect/expectation-chain-detector.js +417 -0
- package/src/verax/detect/expectation-model.js +2 -2
- package/src/verax/detect/failure-cause-inference.js +293 -0
- package/src/verax/detect/findings-writer.js +131 -35
- package/src/verax/detect/flow-detector.js +2 -2
- package/src/verax/detect/form-silent-failure.js +98 -0
- package/src/verax/detect/index.js +46 -5
- package/src/verax/detect/invariants-enforcer.js +147 -0
- package/src/verax/detect/journey-stall-detector.js +558 -0
- package/src/verax/detect/navigation-silent-failure.js +82 -0
- package/src/verax/detect/problem-aggregator.js +361 -0
- package/src/verax/detect/route-findings.js +219 -0
- package/src/verax/detect/summary-writer.js +477 -0
- package/src/verax/detect/test-failure-cause-inference.js +314 -0
- package/src/verax/detect/ui-feedback-findings.js +207 -0
- package/src/verax/detect/view-switch-correlator.js +242 -0
- package/src/verax/flow/flow-engine.js +2 -1
- package/src/verax/flow/flow-spec.js +0 -6
- package/src/verax/index.js +4 -0
- package/src/verax/intel/ts-program.js +1 -0
- package/src/verax/intel/vue-navigation-extractor.js +3 -0
- package/src/verax/learn/action-contract-extractor.js +3 -0
- package/src/verax/learn/ast-contract-extractor.js +1 -1
- package/src/verax/learn/flow-extractor.js +1 -0
- package/src/verax/learn/project-detector.js +5 -0
- package/src/verax/learn/react-router-extractor.js +2 -0
- package/src/verax/learn/source-instrumenter.js +1 -0
- package/src/verax/learn/state-extractor.js +2 -1
- package/src/verax/learn/static-extractor.js +1 -0
- package/src/verax/observe/coverage-gaps.js +132 -0
- package/src/verax/observe/expectation-handler.js +126 -0
- package/src/verax/observe/incremental-skip.js +46 -0
- package/src/verax/observe/index.js +51 -155
- package/src/verax/observe/interaction-executor.js +192 -0
- package/src/verax/observe/interaction-runner.js +782 -513
- package/src/verax/observe/network-firewall.js +86 -0
- package/src/verax/observe/observation-builder.js +169 -0
- package/src/verax/observe/observe-context.js +205 -0
- package/src/verax/observe/observe-helpers.js +192 -0
- package/src/verax/observe/observe-runner.js +230 -0
- package/src/verax/observe/observers/budget-observer.js +185 -0
- package/src/verax/observe/observers/console-observer.js +102 -0
- package/src/verax/observe/observers/coverage-observer.js +107 -0
- package/src/verax/observe/observers/interaction-observer.js +471 -0
- package/src/verax/observe/observers/navigation-observer.js +132 -0
- package/src/verax/observe/observers/network-observer.js +87 -0
- package/src/verax/observe/observers/safety-observer.js +82 -0
- package/src/verax/observe/observers/ui-feedback-observer.js +99 -0
- package/src/verax/observe/page-traversal.js +138 -0
- package/src/verax/observe/snapshot-ops.js +94 -0
- package/src/verax/observe/ui-feedback-detector.js +742 -0
- package/src/verax/scan-summary-writer.js +2 -0
- package/src/verax/shared/artifact-manager.js +25 -5
- package/src/verax/shared/caching.js +1 -0
- package/src/verax/shared/css-spinner-rules.js +204 -0
- package/src/verax/shared/expectation-tracker.js +1 -0
- package/src/verax/shared/view-switch-rules.js +208 -0
- package/src/verax/shared/zip-artifacts.js +6 -0
- package/src/verax/shared/config-loader.js +0 -169
- /package/src/verax/shared/{expectation-proof.js → expectation-validation.js} +0 -0
|
@@ -25,7 +25,9 @@ export async function detect(manifestPath, tracesPath, validation = null, _expec
|
|
|
25
25
|
const manifestContent = readFileSync(manifestPath, 'utf-8');
|
|
26
26
|
const tracesContent = readFileSync(tracesPath, 'utf-8');
|
|
27
27
|
|
|
28
|
+
// @ts-expect-error - readFileSync with encoding returns string
|
|
28
29
|
const manifest = JSON.parse(manifestContent);
|
|
30
|
+
// @ts-expect-error - readFileSync with encoding returns string
|
|
29
31
|
const observation = JSON.parse(tracesContent);
|
|
30
32
|
|
|
31
33
|
const projectDir = manifest.projectDir;
|
|
@@ -98,20 +100,20 @@ export async function detect(manifestPath, tracesPath, validation = null, _expec
|
|
|
98
100
|
selectorHint.includes(interactionSelector) ||
|
|
99
101
|
interactionSelector.includes(normalizedSelectorHint) ||
|
|
100
102
|
normalizedSelectorHint === normalizedInteractionSelector) {
|
|
101
|
-
if (expectation.type === 'navigation' && (interaction.type === 'link' || interaction.type === 'button')) {
|
|
103
|
+
if ((expectation.type === 'navigation' || expectation.type === 'spa_navigation') && (interaction.type === 'link' || interaction.type === 'button')) {
|
|
102
104
|
matchingExpectations.push(expectation);
|
|
103
105
|
} else if (expectation.type === 'form_submission' && interaction.type === 'form') {
|
|
104
106
|
matchingExpectations.push(expectation);
|
|
105
107
|
}
|
|
106
108
|
} else {
|
|
107
|
-
if (expectation.type === 'navigation' && (interaction.type === 'link' || interaction.type === 'button')) {
|
|
109
|
+
if ((expectation.type === 'navigation' || expectation.type === 'spa_navigation') && (interaction.type === 'link' || interaction.type === 'button')) {
|
|
108
110
|
selectorMismatch = true;
|
|
109
111
|
} else if (expectation.type === 'form_submission' && interaction.type === 'form') {
|
|
110
112
|
selectorMismatch = true;
|
|
111
113
|
}
|
|
112
114
|
}
|
|
113
115
|
} else if (!selectorHint && !interactionSelector) {
|
|
114
|
-
if (expectation.type === 'navigation' && (interaction.type === 'link' || interaction.type === 'button')) {
|
|
116
|
+
if ((expectation.type === 'navigation' || expectation.type === 'spa_navigation') && (interaction.type === 'link' || interaction.type === 'button')) {
|
|
115
117
|
matchingExpectations.push(expectation);
|
|
116
118
|
} else if (expectation.type === 'form_submission' && interaction.type === 'form') {
|
|
117
119
|
matchingExpectations.push(expectation);
|
|
@@ -122,9 +124,24 @@ export async function detect(manifestPath, tracesPath, validation = null, _expec
|
|
|
122
124
|
|
|
123
125
|
if (matchingExpectations.length > 1) {
|
|
124
126
|
multipleMatches = true;
|
|
127
|
+
// VISION TRANSPARENCY: Record ambiguity explicitly (not silent skip)
|
|
128
|
+
if (_silenceTracker) {
|
|
129
|
+
_silenceTracker.record({
|
|
130
|
+
scope: 'expectation',
|
|
131
|
+
reason: 'ambiguous_promise',
|
|
132
|
+
description: `Multiple expectations match interaction "${interaction.label}" (${matchingExpectations.length} candidates). Cannot determine intent without guessing.`,
|
|
133
|
+
context: {
|
|
134
|
+
interaction: { type: interaction.type, selector: interaction.selector, label: interaction.label },
|
|
135
|
+
candidateCount: matchingExpectations.length,
|
|
136
|
+
candidates: matchingExpectations.map(e => e.targetPath)
|
|
137
|
+
},
|
|
138
|
+
impact: 'interaction_not_evaluated',
|
|
139
|
+
outcome: 'UNPROVEN_INTERACTION'
|
|
140
|
+
});
|
|
141
|
+
}
|
|
125
142
|
} else if (matchingExpectations.length === 1) {
|
|
126
143
|
expectedTargetPath = matchingExpectations[0].targetPath;
|
|
127
|
-
expectationType = matchingExpectations[0].type;
|
|
144
|
+
expectationType = matchingExpectations[0].type === 'spa_navigation' ? 'navigation' : matchingExpectations[0].type;
|
|
128
145
|
} else if (selectorMismatch) {
|
|
129
146
|
skips.push({
|
|
130
147
|
code: 'SELECTOR_MISMATCH',
|
|
@@ -169,6 +186,21 @@ export async function detect(manifestPath, tracesPath, validation = null, _expec
|
|
|
169
186
|
}
|
|
170
187
|
|
|
171
188
|
if (matchingRoutes.length > 1) {
|
|
189
|
+
// VISION TRANSPARENCY: Record ambiguity explicitly
|
|
190
|
+
if (_silenceTracker) {
|
|
191
|
+
_silenceTracker.record({
|
|
192
|
+
scope: 'expectation',
|
|
193
|
+
reason: 'ambiguous_promise',
|
|
194
|
+
description: `Multiple routes match interaction "${interaction.label}" (${matchingRoutes.length} candidates). Conservative approach requires single clear match.`,
|
|
195
|
+
context: {
|
|
196
|
+
interaction: { type: interaction.type, selector: interaction.selector, label: interaction.label },
|
|
197
|
+
candidateCount: matchingRoutes.length,
|
|
198
|
+
candidates: matchingRoutes
|
|
199
|
+
},
|
|
200
|
+
impact: 'interaction_not_evaluated',
|
|
201
|
+
outcome: 'UNPROVEN_INTERACTION'
|
|
202
|
+
});
|
|
203
|
+
}
|
|
172
204
|
skips.push({
|
|
173
205
|
code: 'AMBIGUOUS_MATCH',
|
|
174
206
|
message: 'Multiple expectations could match; conservative approach requires single clear match',
|
|
@@ -187,6 +219,7 @@ export async function detect(manifestPath, tracesPath, validation = null, _expec
|
|
|
187
219
|
}
|
|
188
220
|
|
|
189
221
|
if (multipleMatches) {
|
|
222
|
+
// VISION TRANSPARENCY: Ambiguity already recorded in silence tracker above
|
|
190
223
|
skips.push({
|
|
191
224
|
code: 'AMBIGUOUS_MATCH',
|
|
192
225
|
message: 'Multiple expectations could match; conservative approach requires single clear match',
|
|
@@ -243,13 +276,17 @@ export async function detect(manifestPath, tracesPath, validation = null, _expec
|
|
|
243
276
|
|
|
244
277
|
if (!hasEffect) {
|
|
245
278
|
findings.push({
|
|
246
|
-
type: '
|
|
279
|
+
type: 'navigation_silent_failure',
|
|
247
280
|
interaction: {
|
|
248
281
|
type: interaction.type,
|
|
249
282
|
selector: interaction.selector,
|
|
250
283
|
label: interaction.label
|
|
251
284
|
},
|
|
252
285
|
reason: 'Expected user-visible outcome did not occur',
|
|
286
|
+
what_happened: 'Navigation attempt produced no visible effect',
|
|
287
|
+
what_was_expected: `Navigate to ${normalizedTarget || 'target page'}`,
|
|
288
|
+
what_was_observed: 'URL, DOM, and visuals remained unchanged',
|
|
289
|
+
why_it_matters: 'Users cannot reach the intended destination despite interacting',
|
|
253
290
|
evidence: {
|
|
254
291
|
before: beforeScreenshot,
|
|
255
292
|
after: afterScreenshot,
|
|
@@ -269,6 +306,10 @@ export async function detect(manifestPath, tracesPath, validation = null, _expec
|
|
|
269
306
|
label: interaction.label
|
|
270
307
|
},
|
|
271
308
|
reason: 'Expected user-visible outcome did not occur',
|
|
309
|
+
what_happened: 'User action produced no visible effect',
|
|
310
|
+
what_was_expected: 'Some user-visible change after interaction',
|
|
311
|
+
what_was_observed: 'URL, DOM, and visuals remained unchanged',
|
|
312
|
+
why_it_matters: 'Users cannot complete the intended action',
|
|
272
313
|
evidence: {
|
|
273
314
|
before: beforeScreenshot,
|
|
274
315
|
after: afterScreenshot,
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 4: Final Output Invariants (Trust Lock)
|
|
3
|
+
*
|
|
4
|
+
* Strict gate applied only to user-facing outputs (REPORT.json, SUMMARY.md, console).
|
|
5
|
+
* Findings violating any invariant are dropped silently. Purity and determinism are required.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const KNOWN_IMPACTS = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'INFO', 'UNKNOWN'];
|
|
9
|
+
const VALID_STATUSES = ['CONFIRMED', 'SUSPECTED', 'INFORMATIONAL'];
|
|
10
|
+
|
|
11
|
+
function isValidEvidence(evidence) {
|
|
12
|
+
if (!evidence || typeof evidence !== 'object') return false;
|
|
13
|
+
const keys = Object.keys(evidence);
|
|
14
|
+
if (keys.length === 0) return false;
|
|
15
|
+
|
|
16
|
+
const signals = Object.values(evidence).filter(v => {
|
|
17
|
+
if (v === true || v === false) return true;
|
|
18
|
+
if (typeof v === 'number' && v !== 0) return true;
|
|
19
|
+
if (typeof v === 'string' && v.length > 0) return true;
|
|
20
|
+
if (Array.isArray(v)) return v.length > 0; // arrays must contain at least one element
|
|
21
|
+
if (v && typeof v === 'object') return Object.keys(v).length > 0;
|
|
22
|
+
return false;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return signals.length > 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isValidConfidence(confidence) {
|
|
29
|
+
if (confidence === 0) return true;
|
|
30
|
+
if (!confidence) return false;
|
|
31
|
+
|
|
32
|
+
if (typeof confidence === 'number') {
|
|
33
|
+
return confidence >= 0 && confidence <= 1;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (typeof confidence === 'object') {
|
|
37
|
+
const hasLevel = (typeof confidence.level === 'string' && confidence.level.length > 0) || typeof confidence.level === 'number';
|
|
38
|
+
const hasScore = typeof confidence.score === 'number';
|
|
39
|
+
return hasLevel || hasScore;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isValidPromise(promise) {
|
|
46
|
+
if (!promise || typeof promise !== 'object') return false;
|
|
47
|
+
|
|
48
|
+
const { kind, value, type, expected, actual, expected_signal } = promise;
|
|
49
|
+
|
|
50
|
+
const kindValueValid = typeof kind === 'string' && kind.length > 0 && typeof value === 'string' && value.length > 0;
|
|
51
|
+
const typeValid = typeof type === 'string' && type.length > 0;
|
|
52
|
+
const expectationValid = (typeof expected === 'string' && expected.length > 0) ||
|
|
53
|
+
(typeof actual === 'string' && actual.length > 0) ||
|
|
54
|
+
(typeof expected_signal === 'string' && expected_signal.length > 0);
|
|
55
|
+
|
|
56
|
+
return kindValueValid || (typeValid && expectationValid);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function isInternalErrorFlag(finding) {
|
|
60
|
+
const internalMarkers = [
|
|
61
|
+
'INTERNAL_ERROR',
|
|
62
|
+
'internal-error',
|
|
63
|
+
'internalError',
|
|
64
|
+
'TIMEOUT_ERROR',
|
|
65
|
+
'CRASH',
|
|
66
|
+
'FATAL',
|
|
67
|
+
'BROWSER_CRASH'
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
if (internalMarkers.some(marker =>
|
|
71
|
+
finding.reason?.includes?.(marker) ||
|
|
72
|
+
finding.errorMessage?.includes?.(marker) ||
|
|
73
|
+
finding.errorStack?.includes?.(marker)
|
|
74
|
+
)) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (finding.reason?.toLowerCase?.().includes('internal error')) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function isValidCause(cause, evidence) {
|
|
86
|
+
if (!cause || typeof cause !== 'object') return false;
|
|
87
|
+
if (!cause.id || !cause.statement) return false;
|
|
88
|
+
|
|
89
|
+
if (Array.isArray(cause.evidence_refs) && cause.evidence_refs.length > 0) {
|
|
90
|
+
if (!evidence || typeof evidence !== 'object') return false;
|
|
91
|
+
return cause.evidence_refs.every(ref => typeof ref === 'string' && ref in evidence);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function filterCausesWithEvidence(evidence, causes) {
|
|
98
|
+
if (!Array.isArray(causes)) return [];
|
|
99
|
+
return causes.filter(cause => isValidCause(cause, evidence));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function enforceFinalInvariant(finding) {
|
|
103
|
+
if (!finding) return null;
|
|
104
|
+
|
|
105
|
+
if (!isValidEvidence(finding.evidence)) return null;
|
|
106
|
+
if (!isValidPromise(finding.promise)) return null;
|
|
107
|
+
if (!isValidConfidence(finding.confidence)) return null;
|
|
108
|
+
if (finding.impact && !KNOWN_IMPACTS.includes(finding.impact)) return null;
|
|
109
|
+
if (isInternalErrorFlag(finding)) return null;
|
|
110
|
+
if (!finding.id || typeof finding.id !== 'string' || finding.id.length === 0) return null;
|
|
111
|
+
if (finding.status && !VALID_STATUSES.includes(finding.status)) return null;
|
|
112
|
+
|
|
113
|
+
const causes = filterCausesWithEvidence(finding.evidence, finding.causes);
|
|
114
|
+
|
|
115
|
+
return { ...finding, causes };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function deduplicateFindings(findings) {
|
|
119
|
+
const seen = new Set();
|
|
120
|
+
const deduped = [];
|
|
121
|
+
|
|
122
|
+
for (const finding of findings) {
|
|
123
|
+
const promiseKey = finding.promise ? JSON.stringify(finding.promise) : 'nopromise';
|
|
124
|
+
const key = `${finding.id || 'unknown'}|${finding.location || 'unknown'}|${promiseKey}`;
|
|
125
|
+
if (seen.has(key)) continue;
|
|
126
|
+
seen.add(key);
|
|
127
|
+
deduped.push(finding);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return deduped;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function enforceFinalInvariants(findings) {
|
|
134
|
+
if (!Array.isArray(findings)) return [];
|
|
135
|
+
|
|
136
|
+
const validFindings = findings
|
|
137
|
+
.map(enforceFinalInvariant)
|
|
138
|
+
.filter(Boolean);
|
|
139
|
+
|
|
140
|
+
const deduped = deduplicateFindings(validFindings);
|
|
141
|
+
|
|
142
|
+
return deduped.sort((a, b) => {
|
|
143
|
+
const aId = (a.id || '').toString();
|
|
144
|
+
const bId = (b.id || '').toString();
|
|
145
|
+
return aId.localeCompare(bId);
|
|
146
|
+
});
|
|
147
|
+
}
|