@odavl/guardian 0.1.0-rc1 → 1.0.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 (101) hide show
  1. package/CHANGELOG.md +146 -0
  2. package/README.md +155 -97
  3. package/bin/guardian.js +1544 -55
  4. package/config/README.md +59 -0
  5. package/config/profiles/landing-demo.yaml +16 -0
  6. package/package.json +26 -11
  7. package/policies/landing-demo.json +22 -0
  8. package/src/enterprise/audit-logger.js +166 -0
  9. package/src/enterprise/pdf-exporter.js +267 -0
  10. package/src/enterprise/rbac-gate.js +142 -0
  11. package/src/enterprise/rbac.js +239 -0
  12. package/src/enterprise/site-manager.js +180 -0
  13. package/src/founder/feedback-system.js +156 -0
  14. package/src/founder/founder-tracker.js +213 -0
  15. package/src/founder/usage-signals.js +141 -0
  16. package/src/guardian/alert-ledger.js +121 -0
  17. package/src/guardian/attempt-engine.js +587 -12
  18. package/src/guardian/attempt-registry.js +42 -1
  19. package/src/guardian/attempt-relevance.js +106 -0
  20. package/src/guardian/attempt.js +85 -39
  21. package/src/guardian/attempts-filter.js +63 -0
  22. package/src/guardian/baseline.js +50 -8
  23. package/src/guardian/breakage-intelligence.js +1 -0
  24. package/src/guardian/browser-pool.js +131 -0
  25. package/src/guardian/browser.js +28 -1
  26. package/src/guardian/ci-cli.js +121 -0
  27. package/src/guardian/ci-mode.js +15 -0
  28. package/src/guardian/ci-output.js +38 -0
  29. package/src/guardian/cli-summary.js +167 -67
  30. package/src/guardian/config-loader.js +162 -0
  31. package/src/guardian/data-guardian-detector.js +189 -0
  32. package/src/guardian/detection-layers.js +271 -0
  33. package/src/guardian/drift-detector.js +100 -0
  34. package/src/guardian/enhanced-html-reporter.js +221 -4
  35. package/src/guardian/env-guard.js +127 -0
  36. package/src/guardian/failure-intelligence.js +173 -0
  37. package/src/guardian/first-run-profile.js +89 -0
  38. package/src/guardian/first-run.js +54 -0
  39. package/src/guardian/flag-validator.js +111 -0
  40. package/src/guardian/flow-executor.js +309 -44
  41. package/src/guardian/html-reporter.js +2 -0
  42. package/src/guardian/human-reporter.js +431 -0
  43. package/src/guardian/index.js +22 -19
  44. package/src/guardian/init-command.js +9 -5
  45. package/src/guardian/intent-detector.js +146 -0
  46. package/src/guardian/journey-definitions.js +132 -0
  47. package/src/guardian/journey-scan-cli.js +145 -0
  48. package/src/guardian/journey-scanner.js +583 -0
  49. package/src/guardian/junit-reporter.js +18 -1
  50. package/src/guardian/language-detection.js +99 -0
  51. package/src/guardian/live-cli.js +95 -0
  52. package/src/guardian/live-scheduler-runner.js +137 -0
  53. package/src/guardian/live-scheduler.js +146 -0
  54. package/src/guardian/market-reporter.js +357 -82
  55. package/src/guardian/parallel-executor.js +116 -0
  56. package/src/guardian/pattern-analyzer.js +348 -0
  57. package/src/guardian/policy.js +80 -3
  58. package/src/guardian/prerequisite-checker.js +101 -0
  59. package/src/guardian/preset-loader.js +27 -18
  60. package/src/guardian/profile-loader.js +96 -0
  61. package/src/guardian/reality.js +1612 -115
  62. package/src/guardian/reporter.js +27 -41
  63. package/src/guardian/run-artifacts.js +212 -0
  64. package/src/guardian/run-cleanup.js +207 -0
  65. package/src/guardian/run-latest.js +90 -0
  66. package/src/guardian/run-list.js +211 -0
  67. package/src/guardian/run-summary.js +20 -0
  68. package/src/guardian/scan-presets.js +100 -11
  69. package/src/guardian/selector-fallbacks.js +394 -0
  70. package/src/guardian/semantic-contact-detection.js +255 -0
  71. package/src/guardian/semantic-contact-finder.js +201 -0
  72. package/src/guardian/semantic-targets.js +234 -0
  73. package/src/guardian/site-introspection.js +257 -0
  74. package/src/guardian/smoke.js +258 -0
  75. package/src/guardian/snapshot-schema.js +25 -1
  76. package/src/guardian/snapshot.js +69 -3
  77. package/src/guardian/stability-scorer.js +169 -0
  78. package/src/guardian/success-evaluator.js +214 -0
  79. package/src/guardian/template-command.js +184 -0
  80. package/src/guardian/text-formatters.js +426 -0
  81. package/src/guardian/timeout-profiles.js +57 -0
  82. package/src/guardian/verdict.js +320 -0
  83. package/src/guardian/verdicts.js +74 -0
  84. package/src/guardian/wait-for-outcome.js +120 -0
  85. package/src/guardian/watch-runner.js +181 -0
  86. package/src/payments/stripe-checkout.js +169 -0
  87. package/src/plans/plan-definitions.js +148 -0
  88. package/src/plans/plan-manager.js +211 -0
  89. package/src/plans/usage-tracker.js +210 -0
  90. package/src/recipes/recipe-engine.js +188 -0
  91. package/src/recipes/recipe-failure-analysis.js +159 -0
  92. package/src/recipes/recipe-registry.js +134 -0
  93. package/src/recipes/recipe-runtime.js +507 -0
  94. package/src/recipes/recipe-store.js +410 -0
  95. package/guardian-contract-v1.md +0 -149
  96. /package/{guardian.config.json → config/guardian.config.json} +0 -0
  97. /package/{guardian.policy.json → config/guardian.policy.json} +0 -0
  98. /package/{guardian.profile.docs.yaml → config/profiles/docs.yaml} +0 -0
  99. /package/{guardian.profile.ecommerce.yaml → config/profiles/ecommerce.yaml} +0 -0
  100. /package/{guardian.profile.marketing.yaml → config/profiles/marketing.yaml} +0 -0
  101. /package/{guardian.profile.saas.yaml → config/profiles/saas.yaml} +0 -0
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Journey Definitions - Preset journeys for different product types
3
+ * Each journey is a deterministic sequence of steps
4
+ */
5
+
6
+ function getJourneyDefinition(preset = 'saas') {
7
+ switch (preset) {
8
+ case 'saas':
9
+ return getSaasjJourney();
10
+ case 'shop':
11
+ case 'ecommerce':
12
+ return getShopJourney();
13
+ case 'landing':
14
+ return getLandingJourney();
15
+ default:
16
+ return getSaasjJourney();
17
+ }
18
+ }
19
+
20
+ /**
21
+ * SAAS Journey: Homepage → Find CTA → Signup flow
22
+ */
23
+ function getSaasjJourney() {
24
+ return {
25
+ name: 'SAAS Signup Flow',
26
+ description: 'Test critical path: homepage → locate signup CTA → click → verify signup page',
27
+ preset: 'saas',
28
+ steps: [
29
+ {
30
+ id: 'step-1-homepage',
31
+ name: 'Load homepage',
32
+ action: 'navigate',
33
+ target: '/',
34
+ description: 'User arrives at the homepage'
35
+ },
36
+ {
37
+ id: 'step-2-find-cta',
38
+ name: 'Find signup CTA',
39
+ action: 'find_cta',
40
+ target: null,
41
+ description: 'Locate primary signup/CTA button (sign up, get started, start, register, pricing, etc)',
42
+ expectedIndicator: {
43
+ textMatch: ['sign up', 'signup', 'get started', 'start', 'register', 'pricing'],
44
+ tagNames: ['A', 'BUTTON']
45
+ }
46
+ },
47
+ {
48
+ id: 'step-3-verify-homepage',
49
+ name: 'Verify homepage loaded',
50
+ action: 'navigate',
51
+ target: '/pricing',
52
+ description: 'If CTA not found, try /pricing as fallback'
53
+ }
54
+ ]
55
+ };
56
+ }
57
+
58
+ /**
59
+ * Shop Journey: Homepage → Find product CTA → Checkout
60
+ */
61
+ function getShopJourney() {
62
+ return {
63
+ name: 'Shop Checkout Flow',
64
+ description: 'Test critical path: homepage → find product/checkout CTA → click → verify flow',
65
+ preset: 'shop',
66
+ steps: [
67
+ {
68
+ id: 'step-1-homepage',
69
+ name: 'Load homepage',
70
+ action: 'navigate',
71
+ target: '/',
72
+ description: 'User arrives at the homepage'
73
+ },
74
+ {
75
+ id: 'step-2-find-product',
76
+ name: 'Find product CTA',
77
+ action: 'find_cta',
78
+ target: null,
79
+ description: 'Locate primary product/buy/checkout button',
80
+ expectedIndicator: {
81
+ textMatch: ['buy', 'shop', 'add to cart', 'checkout', 'order', 'purchase'],
82
+ tagNames: ['A', 'BUTTON']
83
+ }
84
+ },
85
+ {
86
+ id: 'step-3-navigate-shop',
87
+ name: 'Navigate to shop',
88
+ action: 'navigate',
89
+ target: '/shop',
90
+ description: 'If CTA not found, try /shop as fallback'
91
+ }
92
+ ]
93
+ };
94
+ }
95
+
96
+ /**
97
+ * Landing Page Journey: Simple homepage test
98
+ */
99
+ function getLandingJourney() {
100
+ return {
101
+ name: 'Landing Page Journey',
102
+ description: 'Test: homepage → find contact/CTA → verify page load',
103
+ preset: 'landing',
104
+ steps: [
105
+ {
106
+ id: 'step-1-homepage',
107
+ name: 'Load homepage',
108
+ action: 'navigate',
109
+ target: '/',
110
+ description: 'User arrives at the homepage'
111
+ },
112
+ {
113
+ id: 'step-2-find-cta',
114
+ name: 'Find primary CTA',
115
+ action: 'find_cta',
116
+ target: null,
117
+ description: 'Locate primary call-to-action element',
118
+ expectedIndicator: {
119
+ textMatch: ['contact', 'get started', 'sign up', 'learn more', 'demo'],
120
+ tagNames: ['A', 'BUTTON']
121
+ }
122
+ }
123
+ ]
124
+ };
125
+ }
126
+
127
+ module.exports = {
128
+ getJourneyDefinition,
129
+ getSaasjJourney,
130
+ getShopJourney,
131
+ getLandingJourney
132
+ };
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Journey Scan CLI Command
3
+ * Entry point for running human journey scans from command line
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const { JourneyScanner } = require('./journey-scanner');
9
+ const { getJourneyDefinition } = require('./journey-definitions');
10
+ const { HumanReporter } = require('./human-reporter');
11
+
12
+ async function runJourneyScanCLI(config) {
13
+ const {
14
+ baseUrl,
15
+ preset = 'saas',
16
+ artifactsDir = './.odavlguardian',
17
+ headless = true,
18
+ timeout = 20000,
19
+ presetProvided = false
20
+ } = config;
21
+
22
+ // Set a process timeout to ensure we don't hang forever
23
+ const processTimeout = setTimeout(() => {
24
+ console.error('\n❌ Scan timeout: Process took too long (>120s)');
25
+ process.exit(2);
26
+ }, 120000); // 120 second hard limit
27
+
28
+ // Validate URL
29
+ let url;
30
+ try {
31
+ url = new URL(baseUrl);
32
+ } catch (err) {
33
+ console.error(`❌ Invalid URL: ${baseUrl}`);
34
+ clearTimeout(processTimeout);
35
+ process.exit(2);
36
+ }
37
+
38
+ console.log(`\n🛡️ ODAVL Guardian — Human Journey Scanner`);
39
+ console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
40
+ console.log(`📍 Target URL: ${baseUrl}`);
41
+ console.log(`🎯 Preset: ${presetProvided ? preset : '(auto)'}`);
42
+ console.log(`📁 Output: ${artifactsDir}\n`);
43
+ process.stdout.write(''); // Flush stdout
44
+
45
+ // Create output directory
46
+ try {
47
+ if (!fs.existsSync(artifactsDir)) {
48
+ fs.mkdirSync(artifactsDir, { recursive: true });
49
+ }
50
+
51
+ const screenshotDir = path.join(artifactsDir, 'screenshots');
52
+ if (!fs.existsSync(screenshotDir)) {
53
+ fs.mkdirSync(screenshotDir, { recursive: true });
54
+ }
55
+
56
+ // Detect site intent if preset not provided
57
+ let finalPreset = preset;
58
+ let intentDetection = null;
59
+ if (!presetProvided) {
60
+ const { detectIntent } = require('./intent-detector');
61
+ console.log('🔎 Detecting site intent...');
62
+ intentDetection = await detectIntent(baseUrl, { timeout, headless });
63
+ console.log(` Detected: ${intentDetection.intent.toUpperCase()} (confidence ${intentDetection.confidence}%)`);
64
+ if (intentDetection.intent === 'saas') finalPreset = 'saas';
65
+ else if (intentDetection.intent === 'shop') finalPreset = 'shop';
66
+ else if (intentDetection.intent === 'landing') finalPreset = 'landing';
67
+ else finalPreset = 'landing'; // default for unknown
68
+ }
69
+
70
+ // Get journey definition
71
+ const journey = getJourneyDefinition(finalPreset);
72
+ console.log(`🚀 Starting journey: ${journey.name}`);
73
+ console.log(` ${journey.description}\n`);
74
+ process.stdout.write(''); // Flush stdout
75
+
76
+ // Run scanner
77
+ const scanner = new JourneyScanner({
78
+ timeout,
79
+ headless,
80
+ screenshotDir
81
+ });
82
+
83
+ console.log('⏳ Executing journey steps...\n');
84
+ process.stdout.write(''); // Flush stdout
85
+
86
+ const result = await scanner.scan(baseUrl, journey);
87
+ if (intentDetection) {
88
+ result.intentDetection = intentDetection;
89
+ }
90
+
91
+ // Generate reports
92
+ const reporter = new HumanReporter();
93
+ const humanReport = reporter.generateSummary(result, artifactsDir);
94
+ const jsonReport = reporter.generateJSON(result, artifactsDir);
95
+
96
+ // Print summary to console
97
+ console.log('\n' + humanReport.content);
98
+ process.stdout.write(''); // Flush stdout
99
+
100
+ // Determine canonical verdict and exit code
101
+ const { toCanonicalJourneyVerdict, mapExitCodeFromCanonical } = require('./verdicts');
102
+ const canonical = toCanonicalJourneyVerdict(result.finalDecision);
103
+ const exitCode = mapExitCodeFromCanonical(canonical);
104
+
105
+ // Save results metadata
106
+ const metadata = {
107
+ timestamp: new Date().toISOString(),
108
+ url: baseUrl,
109
+ preset: finalPreset,
110
+ decision: result.finalDecision,
111
+ decisionCanonical: canonical,
112
+ executedSteps: result.executedSteps.length,
113
+ failedSteps: result.failedSteps.length
114
+ };
115
+
116
+ fs.writeFileSync(
117
+ path.join(artifactsDir, 'metadata.json'),
118
+ JSON.stringify(metadata, null, 2),
119
+ 'utf8'
120
+ );
121
+
122
+ // Exit with canonical-mapped code
123
+
124
+ console.log(`📁 Reports saved to: ${artifactsDir}`);
125
+ console.log(` - summary.txt (human readable)`);
126
+ console.log(` - summary.md (markdown)`);
127
+ console.log(` - report.json (full results)`);
128
+ console.log(` - screenshots/ (evidence images)`);
129
+ process.stdout.write(''); // Flush stdout
130
+
131
+ clearTimeout(processTimeout);
132
+ process.exit(exitCode);
133
+ } catch (err) {
134
+ console.error(`\n❌ Scan failed: ${err.message}`);
135
+ if (process.env.DEBUG) {
136
+ console.error(err.stack);
137
+ }
138
+ clearTimeout(processTimeout);
139
+ process.exit(2);
140
+ }
141
+ }
142
+
143
+ module.exports = { runJourneyScanCLI };
144
+
145
+ module.exports = { runJourneyScanCLI };