@veraxhq/verax 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/README.md +123 -88
  2. package/bin/verax.js +11 -452
  3. package/package.json +14 -36
  4. package/src/cli/commands/default.js +523 -0
  5. package/src/cli/commands/doctor.js +165 -0
  6. package/src/cli/commands/inspect.js +109 -0
  7. package/src/cli/commands/run.js +402 -0
  8. package/src/cli/entry.js +196 -0
  9. package/src/cli/util/atomic-write.js +37 -0
  10. package/src/cli/util/detection-engine.js +296 -0
  11. package/src/cli/util/env-url.js +33 -0
  12. package/src/cli/util/errors.js +44 -0
  13. package/src/cli/util/events.js +34 -0
  14. package/src/cli/util/expectation-extractor.js +378 -0
  15. package/src/cli/util/findings-writer.js +31 -0
  16. package/src/cli/util/idgen.js +87 -0
  17. package/src/cli/util/learn-writer.js +39 -0
  18. package/src/cli/util/observation-engine.js +366 -0
  19. package/src/cli/util/observe-writer.js +25 -0
  20. package/src/cli/util/paths.js +29 -0
  21. package/src/cli/util/project-discovery.js +277 -0
  22. package/src/cli/util/project-writer.js +26 -0
  23. package/src/cli/util/redact.js +128 -0
  24. package/src/cli/util/run-id.js +30 -0
  25. package/src/cli/util/summary-writer.js +32 -0
  26. package/src/verax/cli/ci-summary.js +35 -0
  27. package/src/verax/cli/context-explanation.js +89 -0
  28. package/src/verax/cli/doctor.js +277 -0
  29. package/src/verax/cli/error-normalizer.js +154 -0
  30. package/src/verax/cli/explain-output.js +105 -0
  31. package/src/verax/cli/finding-explainer.js +130 -0
  32. package/src/verax/cli/init.js +237 -0
  33. package/src/verax/cli/run-overview.js +163 -0
  34. package/src/verax/cli/url-safety.js +101 -0
  35. package/src/verax/cli/wizard.js +98 -0
  36. package/src/verax/cli/zero-findings-explainer.js +57 -0
  37. package/src/verax/cli/zero-interaction-explainer.js +127 -0
  38. package/src/verax/core/action-classifier.js +86 -0
  39. package/src/verax/core/budget-engine.js +218 -0
  40. package/src/verax/core/canonical-outcomes.js +157 -0
  41. package/src/verax/core/decision-snapshot.js +335 -0
  42. package/src/verax/core/determinism-model.js +403 -0
  43. package/src/verax/core/incremental-store.js +237 -0
  44. package/src/verax/core/invariants.js +356 -0
  45. package/src/verax/core/promise-model.js +230 -0
  46. package/src/verax/core/replay-validator.js +350 -0
  47. package/src/verax/core/replay.js +222 -0
  48. package/src/verax/core/run-id.js +175 -0
  49. package/src/verax/core/run-manifest.js +99 -0
  50. package/src/verax/core/silence-impact.js +369 -0
  51. package/src/verax/core/silence-model.js +521 -0
  52. package/src/verax/detect/comparison.js +2 -34
  53. package/src/verax/detect/confidence-engine.js +764 -329
  54. package/src/verax/detect/detection-engine.js +293 -0
  55. package/src/verax/detect/evidence-index.js +177 -0
  56. package/src/verax/detect/expectation-model.js +194 -172
  57. package/src/verax/detect/explanation-helpers.js +187 -0
  58. package/src/verax/detect/finding-detector.js +450 -0
  59. package/src/verax/detect/findings-writer.js +44 -8
  60. package/src/verax/detect/flow-detector.js +366 -0
  61. package/src/verax/detect/index.js +172 -286
  62. package/src/verax/detect/interactive-findings.js +613 -0
  63. package/src/verax/detect/signal-mapper.js +308 -0
  64. package/src/verax/detect/verdict-engine.js +563 -0
  65. package/src/verax/evidence-index-writer.js +61 -0
  66. package/src/verax/index.js +90 -14
  67. package/src/verax/intel/effect-detector.js +368 -0
  68. package/src/verax/intel/handler-mapper.js +249 -0
  69. package/src/verax/intel/index.js +281 -0
  70. package/src/verax/intel/route-extractor.js +280 -0
  71. package/src/verax/intel/ts-program.js +256 -0
  72. package/src/verax/intel/vue-navigation-extractor.js +579 -0
  73. package/src/verax/intel/vue-router-extractor.js +323 -0
  74. package/src/verax/learn/action-contract-extractor.js +335 -101
  75. package/src/verax/learn/ast-contract-extractor.js +95 -5
  76. package/src/verax/learn/flow-extractor.js +172 -0
  77. package/src/verax/learn/manifest-writer.js +97 -47
  78. package/src/verax/learn/project-detector.js +40 -0
  79. package/src/verax/learn/route-extractor.js +27 -96
  80. package/src/verax/learn/state-extractor.js +212 -0
  81. package/src/verax/learn/static-extractor-navigation.js +114 -0
  82. package/src/verax/learn/static-extractor-validation.js +88 -0
  83. package/src/verax/learn/static-extractor.js +112 -4
  84. package/src/verax/learn/truth-assessor.js +24 -21
  85. package/src/verax/observe/aria-sensor.js +211 -0
  86. package/src/verax/observe/browser.js +10 -5
  87. package/src/verax/observe/console-sensor.js +1 -17
  88. package/src/verax/observe/domain-boundary.js +10 -1
  89. package/src/verax/observe/expectation-executor.js +512 -0
  90. package/src/verax/observe/flow-matcher.js +143 -0
  91. package/src/verax/observe/focus-sensor.js +196 -0
  92. package/src/verax/observe/human-driver.js +643 -275
  93. package/src/verax/observe/index.js +908 -27
  94. package/src/verax/observe/index.js.backup +1 -0
  95. package/src/verax/observe/interaction-discovery.js +365 -14
  96. package/src/verax/observe/interaction-runner.js +563 -198
  97. package/src/verax/observe/loading-sensor.js +139 -0
  98. package/src/verax/observe/navigation-sensor.js +255 -0
  99. package/src/verax/observe/network-sensor.js +55 -7
  100. package/src/verax/observe/observed-expectation-deriver.js +186 -0
  101. package/src/verax/observe/observed-expectation.js +305 -0
  102. package/src/verax/observe/page-frontier.js +234 -0
  103. package/src/verax/observe/settle.js +37 -17
  104. package/src/verax/observe/state-sensor.js +389 -0
  105. package/src/verax/observe/timing-sensor.js +228 -0
  106. package/src/verax/observe/traces-writer.js +61 -20
  107. package/src/verax/observe/ui-signal-sensor.js +136 -17
  108. package/src/verax/scan-summary-writer.js +77 -15
  109. package/src/verax/shared/artifact-manager.js +110 -8
  110. package/src/verax/shared/budget-profiles.js +136 -0
  111. package/src/verax/shared/ci-detection.js +39 -0
  112. package/src/verax/shared/config-loader.js +170 -0
  113. package/src/verax/shared/dynamic-route-utils.js +218 -0
  114. package/src/verax/shared/expectation-coverage.js +44 -0
  115. package/src/verax/shared/expectation-prover.js +81 -0
  116. package/src/verax/shared/expectation-tracker.js +201 -0
  117. package/src/verax/shared/expectations-writer.js +60 -0
  118. package/src/verax/shared/first-run.js +44 -0
  119. package/src/verax/shared/progress-reporter.js +171 -0
  120. package/src/verax/shared/retry-policy.js +14 -1
  121. package/src/verax/shared/root-artifacts.js +49 -0
  122. package/src/verax/shared/scan-budget.js +86 -0
  123. package/src/verax/shared/url-normalizer.js +162 -0
  124. package/src/verax/shared/zip-artifacts.js +65 -0
  125. package/src/verax/validate/context-validator.js +244 -0
  126. package/src/verax/validate/context-validator.js.bak +0 -0
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Focus Sensor
3
+ * Tracks focus changes and detects focus-related silent failures
4
+ */
5
+
6
+ export class FocusSensor {
7
+ constructor() {
8
+ this.focusHistory = [];
9
+ }
10
+
11
+ /**
12
+ * Capture current focus state before interaction
13
+ */
14
+ async captureBefore(page) {
15
+ const focusData = await page.evaluate(() => {
16
+ const active = document.activeElement;
17
+ let selector = 'unknown';
18
+
19
+ if (active === document.body) {
20
+ selector = 'body';
21
+ } else if (active === document.documentElement) {
22
+ selector = 'html';
23
+ } else if (!active) {
24
+ selector = 'null';
25
+ } else {
26
+ if (active.id) {
27
+ selector = `#${active.id}`;
28
+ } else if (active.className) {
29
+ const classes = Array.from(active.classList || []).slice(0, 2).join('.');
30
+ selector = active.tagName.toLowerCase() + (classes ? `.${classes}` : '');
31
+ } else {
32
+ selector = active.tagName.toLowerCase();
33
+ }
34
+ }
35
+
36
+ return {
37
+ selector,
38
+ tagName: active?.tagName || 'null',
39
+ id: active?.id || null,
40
+ role: active?.getAttribute('role') || null,
41
+ ariaLabel: active?.getAttribute('aria-label') || null
42
+ };
43
+ });
44
+
45
+ this.focusBefore = focusData;
46
+ return focusData;
47
+ }
48
+
49
+ /**
50
+ * Capture focus state after interaction and track history
51
+ */
52
+ async captureAfter(page) {
53
+ const focusData = await page.evaluate(() => {
54
+ const active = document.activeElement;
55
+ let selector = 'unknown';
56
+
57
+ if (active === document.body) {
58
+ selector = 'body';
59
+ } else if (active === document.documentElement) {
60
+ selector = 'html';
61
+ } else if (!active) {
62
+ selector = 'null';
63
+ } else {
64
+ if (active.id) {
65
+ selector = `#${active.id}`;
66
+ } else if (active.className) {
67
+ const classes = Array.from(active.classList || []).slice(0, 2).join('.');
68
+ selector = active.tagName.toLowerCase() + (classes ? `.${classes}` : '');
69
+ } else {
70
+ selector = active.tagName.toLowerCase();
71
+ }
72
+ }
73
+
74
+ // Check if there's a modal present
75
+ const modal = document.querySelector('[role="dialog"], [aria-modal="true"], .modal, [data-modal]');
76
+ const hasModal = Boolean(modal);
77
+
78
+ // Check if focus is within modal
79
+ const focusInModal = active?.closest('[role="dialog"], [aria-modal="true"], .modal, [data-modal]');
80
+ const isWithinModal = Boolean(focusInModal);
81
+
82
+ return {
83
+ selector,
84
+ tagName: active?.tagName || 'null',
85
+ id: active?.id || null,
86
+ role: active?.getAttribute('role') || null,
87
+ ariaLabel: active?.getAttribute('aria-label') || null,
88
+ hasModal: hasModal,
89
+ focusInModal: isWithinModal
90
+ };
91
+ });
92
+
93
+ this.focusAfter = focusData;
94
+ this.focusHistory.push(focusData);
95
+ return focusData;
96
+ }
97
+
98
+ /**
99
+ * Perform keyboard navigation and track focus sequence to detect traps
100
+ */
101
+ async captureKeyboardSequence(page, steps = 10) {
102
+ const sequence = [];
103
+
104
+ // Start with current focus
105
+ let current = await page.evaluate(() => {
106
+ const active = document.activeElement;
107
+ if (active === document.body) return 'body';
108
+ if (active === document.documentElement) return 'html';
109
+ if (!active) return 'null';
110
+ if (active.id) return `#${active.id}`;
111
+ return active.tagName.toLowerCase();
112
+ });
113
+
114
+ sequence.push(current);
115
+
116
+ // Tab through elements and record focus changes
117
+ for (let i = 0; i < steps; i++) {
118
+ await page.press('body', 'Tab');
119
+ await page.waitForTimeout(50); // Small delay for focus to settle
120
+
121
+ const next = await page.evaluate(() => {
122
+ const active = document.activeElement;
123
+ if (active === document.body) return 'body';
124
+ if (active === document.documentElement) return 'html';
125
+ if (!active) return 'null';
126
+ if (active.id) return `#${active.id}`;
127
+ return active.tagName.toLowerCase();
128
+ });
129
+
130
+ sequence.push(next);
131
+
132
+ // Check for trap: if we've cycled the same elements
133
+ if (i >= 3) {
134
+ const recentUnique = new Set(sequence.slice(-3));
135
+ if (recentUnique.size <= 2) {
136
+ // Likely in a trap
137
+ break;
138
+ }
139
+ }
140
+ }
141
+
142
+ return sequence;
143
+ }
144
+
145
+ /**
146
+ * Detect if focus is lost or stuck
147
+ */
148
+ detectFocusLoss() {
149
+ if (!this.focusAfter) return false;
150
+
151
+ // Focus lost to body/null after interaction
152
+ return this.focusAfter.selector === 'body' || this.focusAfter.selector === 'null';
153
+ }
154
+
155
+ /**
156
+ * Detect if focus didn't move into modal (expected but didn't happen)
157
+ */
158
+ detectModalFocusFailure(page) {
159
+ // This is checked via modal detection - focus should move to modal
160
+ // Presence of modal without focus change = failure
161
+ return false; // Caller will check if modal opened
162
+ }
163
+
164
+ /**
165
+ * Detect keyboard trap: focus cycles within small set
166
+ */
167
+ detectKeyboardTrap(sequence, threshold = 4) {
168
+ if (!sequence || sequence.length < 3) return false;
169
+
170
+ // If same focus element repeats consecutively, it's a trap
171
+ for (let i = 1; i < sequence.length; i++) {
172
+ if (sequence[i] === sequence[i - 1]) {
173
+ return true;
174
+ }
175
+ }
176
+
177
+ // If we cycle through same 2-3 elements repeatedly
178
+ const recentSet = new Set(sequence.slice(-threshold));
179
+ if (recentSet.size <= 2 && sequence.length >= threshold) {
180
+ return true;
181
+ }
182
+
183
+ return false;
184
+ }
185
+
186
+ /**
187
+ * Get focus diff for evidence
188
+ */
189
+ getFocusDiff() {
190
+ return {
191
+ before: this.focusBefore,
192
+ after: this.focusAfter,
193
+ changed: this.focusBefore?.selector !== this.focusAfter?.selector
194
+ };
195
+ }
196
+ }