@qulib/core 0.4.1 → 0.4.3

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 (135) hide show
  1. package/README.md +56 -8
  2. package/dist/analyze.d.ts.map +1 -1
  3. package/dist/analyze.js +86 -7
  4. package/dist/cli/auth-login-resolve.d.ts +14 -0
  5. package/dist/cli/auth-login-resolve.d.ts.map +1 -0
  6. package/dist/cli/auth-login-resolve.js +68 -0
  7. package/dist/cli/auth-login-run.d.ts +13 -0
  8. package/dist/cli/auth-login-run.d.ts.map +1 -0
  9. package/dist/cli/auth-login-run.js +152 -0
  10. package/dist/cli/index.js +60 -7
  11. package/dist/harness/state-manager.d.ts +10 -0
  12. package/dist/harness/state-manager.d.ts.map +1 -1
  13. package/dist/harness/state-manager.js +15 -0
  14. package/dist/index.d.ts +8 -6
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +7 -6
  17. package/dist/phases/act.js +3 -3
  18. package/dist/phases/observe.js +5 -5
  19. package/dist/phases/think.js +1 -1
  20. package/dist/schemas/automation-maturity.schema.d.ts +40 -0
  21. package/dist/schemas/automation-maturity.schema.d.ts.map +1 -1
  22. package/dist/schemas/automation-maturity.schema.js +27 -0
  23. package/dist/schemas/index.d.ts +1 -1
  24. package/dist/schemas/index.d.ts.map +1 -1
  25. package/dist/schemas/index.js +1 -1
  26. package/dist/schemas/repo-analysis.schema.d.ts +22 -0
  27. package/dist/schemas/repo-analysis.schema.d.ts.map +1 -1
  28. package/dist/schemas/repo-analysis.schema.js +1 -0
  29. package/dist/telemetry/emit.d.ts +22 -0
  30. package/dist/telemetry/emit.d.ts.map +1 -1
  31. package/dist/telemetry/emit.js +37 -0
  32. package/dist/telemetry/telemetry.interface.d.ts +1 -1
  33. package/dist/telemetry/telemetry.interface.d.ts.map +1 -1
  34. package/dist/tools/apply-auth.d.ts +4 -0
  35. package/dist/tools/apply-auth.d.ts.map +1 -0
  36. package/dist/tools/apply-auth.js +35 -0
  37. package/dist/tools/auth/apply.d.ts +4 -0
  38. package/dist/tools/auth/apply.d.ts.map +1 -0
  39. package/dist/tools/auth/apply.js +35 -0
  40. package/dist/tools/auth/block-gap.d.ts +9 -0
  41. package/dist/tools/auth/block-gap.d.ts.map +1 -0
  42. package/dist/tools/auth/block-gap.js +52 -0
  43. package/dist/tools/auth/custom-providers.d.ts +15 -0
  44. package/dist/tools/auth/custom-providers.d.ts.map +1 -0
  45. package/dist/tools/auth/custom-providers.js +62 -0
  46. package/dist/tools/auth/detect.d.ts +23 -0
  47. package/dist/tools/auth/detect.d.ts.map +1 -0
  48. package/dist/tools/auth/detect.js +526 -0
  49. package/dist/tools/auth/detector.d.ts +23 -0
  50. package/dist/tools/auth/detector.d.ts.map +1 -0
  51. package/dist/tools/auth/detector.js +526 -0
  52. package/dist/tools/auth/explore.d.ts +4 -0
  53. package/dist/tools/auth/explore.d.ts.map +1 -0
  54. package/dist/tools/auth/explore.js +346 -0
  55. package/dist/tools/auth/explorer.d.ts +4 -0
  56. package/dist/tools/auth/explorer.d.ts.map +1 -0
  57. package/dist/tools/auth/explorer.js +346 -0
  58. package/dist/tools/auth/gaps.d.ts +9 -0
  59. package/dist/tools/auth/gaps.d.ts.map +1 -0
  60. package/dist/tools/auth/gaps.js +52 -0
  61. package/dist/tools/auth/oauth-providers.d.ts +7 -0
  62. package/dist/tools/auth/oauth-providers.d.ts.map +1 -0
  63. package/dist/tools/auth/oauth-providers.js +21 -0
  64. package/dist/tools/auth/providers.d.ts +7 -0
  65. package/dist/tools/auth/providers.d.ts.map +1 -0
  66. package/dist/tools/auth/providers.js +21 -0
  67. package/dist/tools/auth/surface-analyzer.d.ts +4 -0
  68. package/dist/tools/auth/surface-analyzer.d.ts.map +1 -0
  69. package/dist/tools/auth/surface-analyzer.js +170 -0
  70. package/dist/tools/auth/surface.d.ts +4 -0
  71. package/dist/tools/auth/surface.d.ts.map +1 -0
  72. package/dist/tools/auth/surface.js +170 -0
  73. package/dist/tools/auth/user-providers.d.ts +15 -0
  74. package/dist/tools/auth/user-providers.d.ts.map +1 -0
  75. package/dist/tools/auth/user-providers.js +62 -0
  76. package/dist/tools/auth-block-gap.d.ts +6 -0
  77. package/dist/tools/auth-block-gap.d.ts.map +1 -1
  78. package/dist/tools/auth-block-gap.js +42 -9
  79. package/dist/tools/auth-detector.d.ts +19 -0
  80. package/dist/tools/auth-detector.d.ts.map +1 -1
  81. package/dist/tools/auth-detector.js +186 -8
  82. package/dist/tools/automation-maturity.d.ts.map +1 -1
  83. package/dist/tools/automation-maturity.js +76 -20
  84. package/dist/tools/explorers/browser.d.ts +3 -0
  85. package/dist/tools/explorers/browser.d.ts.map +1 -0
  86. package/dist/tools/explorers/browser.js +13 -0
  87. package/dist/tools/explorers/cypress-explorer.d.ts +8 -0
  88. package/dist/tools/explorers/cypress-explorer.d.ts.map +1 -0
  89. package/dist/tools/explorers/cypress-explorer.js +5 -0
  90. package/dist/tools/explorers/cypress.d.ts +8 -0
  91. package/dist/tools/explorers/cypress.d.ts.map +1 -0
  92. package/dist/tools/explorers/cypress.js +5 -0
  93. package/dist/tools/explorers/explorer.interface.d.ts +7 -0
  94. package/dist/tools/explorers/explorer.interface.d.ts.map +1 -0
  95. package/dist/tools/explorers/explorer.interface.js +1 -0
  96. package/dist/tools/explorers/factory.d.ts +4 -0
  97. package/dist/tools/explorers/factory.d.ts.map +1 -0
  98. package/dist/tools/explorers/factory.js +12 -0
  99. package/dist/tools/explorers/playwright-explorer.d.ts +8 -0
  100. package/dist/tools/explorers/playwright-explorer.d.ts.map +1 -0
  101. package/dist/tools/explorers/playwright-explorer.js +172 -0
  102. package/dist/tools/explorers/playwright.d.ts +8 -0
  103. package/dist/tools/explorers/playwright.d.ts.map +1 -0
  104. package/dist/tools/explorers/playwright.js +172 -0
  105. package/dist/tools/explorers/types.d.ts +7 -0
  106. package/dist/tools/explorers/types.d.ts.map +1 -0
  107. package/dist/tools/explorers/types.js +1 -0
  108. package/dist/tools/playwright-explorer.js +1 -1
  109. package/dist/tools/repo/detect-framework.d.ts +15 -0
  110. package/dist/tools/repo/detect-framework.d.ts.map +1 -0
  111. package/dist/tools/repo/detect-framework.js +153 -0
  112. package/dist/tools/repo/framework-detector.d.ts +15 -0
  113. package/dist/tools/repo/framework-detector.d.ts.map +1 -0
  114. package/dist/tools/repo/framework-detector.js +153 -0
  115. package/dist/tools/repo/scan.d.ts +19 -0
  116. package/dist/tools/repo/scan.d.ts.map +1 -0
  117. package/dist/tools/repo/scan.js +181 -0
  118. package/dist/tools/repo/scanner.d.ts +19 -0
  119. package/dist/tools/repo/scanner.d.ts.map +1 -0
  120. package/dist/tools/repo/scanner.js +181 -0
  121. package/dist/tools/repo-scanner.d.ts.map +1 -1
  122. package/dist/tools/repo-scanner.js +7 -2
  123. package/dist/tools/scoring/automation-maturity.d.ts +4 -0
  124. package/dist/tools/scoring/automation-maturity.d.ts.map +1 -0
  125. package/dist/tools/scoring/automation-maturity.js +219 -0
  126. package/dist/tools/scoring/gap-engine.d.ts +8 -0
  127. package/dist/tools/scoring/gap-engine.d.ts.map +1 -0
  128. package/dist/tools/scoring/gap-engine.js +138 -0
  129. package/dist/tools/scoring/gaps.d.ts +8 -0
  130. package/dist/tools/scoring/gaps.d.ts.map +1 -0
  131. package/dist/tools/scoring/gaps.js +138 -0
  132. package/dist/tools/scoring/public-surface.d.ts +5 -0
  133. package/dist/tools/scoring/public-surface.d.ts.map +1 -0
  134. package/dist/tools/scoring/public-surface.js +13 -0
  135. package/package.json +3 -3
@@ -0,0 +1,138 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { GapSchema } from '../../schemas/gap-analysis.schema.js';
3
+ // TODO: Add category-specific weight overrides (e.g., auth-surface gaps should cost more than untested-route gaps by default).
4
+ // Requires a 2D weight matrix: { [severity]: { [category]: number } }.
5
+ // Deferred until there is empirical data from real scan runs to calibrate.
6
+ const DEFAULT_SCORING_WEIGHTS = { critical: 25, high: 20, medium: 8, low: 3 };
7
+ export function computeQualityScoreFromGaps(gaps, scoringWeights) {
8
+ let critical = 0;
9
+ let high = 0;
10
+ let medium = 0;
11
+ let low = 0;
12
+ for (const g of gaps) {
13
+ if (g.severity === 'critical')
14
+ critical++;
15
+ else if (g.severity === 'high')
16
+ high++;
17
+ else if (g.severity === 'medium')
18
+ medium++;
19
+ else
20
+ low++;
21
+ }
22
+ const w = {
23
+ critical: scoringWeights?.critical ?? DEFAULT_SCORING_WEIGHTS.critical,
24
+ high: scoringWeights?.high ?? DEFAULT_SCORING_WEIGHTS.high,
25
+ medium: scoringWeights?.medium ?? DEFAULT_SCORING_WEIGHTS.medium,
26
+ low: scoringWeights?.low ?? DEFAULT_SCORING_WEIGHTS.low,
27
+ };
28
+ return Math.max(0, 100 - critical * w.critical - high * w.high - medium * w.medium - low * w.low);
29
+ }
30
+ export function computeCoverageScore(routes) {
31
+ const scanned = routes.routes.length;
32
+ const skipped = routes.pagesSkipped;
33
+ const denom = scanned + skipped;
34
+ // TODO: return null here once the explorer exposes an explicit "discovered-but-unknown" signal
35
+ // (i.e. routes were found but the full set couldn't be confirmed — a low score is misleading)
36
+ if (denom === 0) {
37
+ if (routes.budgetExceeded) {
38
+ return 0;
39
+ }
40
+ return scanned === 0 ? 0 : 100;
41
+ }
42
+ return Math.round((100 * scanned) / denom);
43
+ }
44
+ export function analyzeGaps(routes, repo, mode, config) {
45
+ const coveredPaths = new Set();
46
+ if (repo) {
47
+ for (const testFile of repo.testFiles) {
48
+ for (const path of testFile.coveredPaths) {
49
+ coveredPaths.add(path);
50
+ }
51
+ }
52
+ }
53
+ const gaps = [];
54
+ const addGap = (gap) => {
55
+ const validated = GapSchema.parse(gap);
56
+ gaps.push(validated);
57
+ };
58
+ let hasNavigationFailures = false;
59
+ for (const route of routes.routes) {
60
+ if (repo && !coveredPaths.has(route.path)) {
61
+ const highRisk = /checkout|payment|auth|login|order/i.test(route.path);
62
+ addGap({
63
+ id: randomUUID(),
64
+ path: route.path,
65
+ severity: highRisk ? 'high' : 'medium',
66
+ reason: `Route is not covered by existing tests: ${route.path}`,
67
+ category: 'untested-route',
68
+ });
69
+ }
70
+ const navErrors = route.consoleErrors.filter((e) => e.startsWith('Navigation error:'));
71
+ if (navErrors.length > 0) {
72
+ hasNavigationFailures = true;
73
+ addGap({
74
+ id: randomUUID(),
75
+ path: route.path,
76
+ severity: 'high',
77
+ reason: `Navigation failed: ${navErrors.join('; ')}`,
78
+ category: 'console-error',
79
+ });
80
+ }
81
+ else if (route.consoleErrors.length > 0) {
82
+ addGap({
83
+ id: randomUUID(),
84
+ path: route.path,
85
+ severity: 'high',
86
+ reason: `Console errors detected (${route.consoleErrors.length})`,
87
+ category: 'console-error',
88
+ });
89
+ }
90
+ if (route.brokenLinks.length > 0) {
91
+ addGap({
92
+ id: randomUUID(),
93
+ path: route.path,
94
+ severity: 'medium',
95
+ reason: `Broken or invalid links detected (${route.brokenLinks.length})`,
96
+ category: 'broken-link',
97
+ });
98
+ }
99
+ for (const violation of route.a11yViolations) {
100
+ const impact = violation.impact.toLowerCase();
101
+ const severity = impact === 'critical'
102
+ ? 'critical'
103
+ : impact === 'serious'
104
+ ? 'high'
105
+ : impact === 'moderate'
106
+ ? 'medium'
107
+ : 'low';
108
+ addGap({
109
+ id: randomUUID(),
110
+ path: route.path,
111
+ severity,
112
+ reason: `A11y violation ${violation.id} (${violation.impact}): ${violation.helpUrl}`,
113
+ category: 'a11y',
114
+ });
115
+ }
116
+ }
117
+ const releaseConfidence = computeQualityScoreFromGaps(gaps, config.scoringWeights);
118
+ const pagesScanned = routes.routes.length;
119
+ let coverageWarning;
120
+ if (routes.budgetExceeded) {
121
+ coverageWarning = 'budget-exceeded';
122
+ }
123
+ else if (hasNavigationFailures) {
124
+ coverageWarning = 'navigation-failures';
125
+ }
126
+ else if (pagesScanned < config.minPagesForConfidence) {
127
+ coverageWarning = 'low-coverage';
128
+ }
129
+ return {
130
+ analyzedAt: new Date().toISOString(),
131
+ mode,
132
+ releaseConfidence,
133
+ coveragePagesScanned: pagesScanned,
134
+ coverageBudgetExceeded: routes.budgetExceeded,
135
+ coverageWarning,
136
+ gaps,
137
+ };
138
+ }
@@ -0,0 +1,5 @@
1
+ import type { RouteInventory } from '../../schemas/route-inventory.schema.js';
2
+ import type { Gap } from '../../schemas/gap-analysis.schema.js';
3
+ import type { PublicSurface } from '../../schemas/public-surface.schema.js';
4
+ export declare function buildPublicSurface(pages: RouteInventory['routes'], gaps: Gap[]): PublicSurface;
5
+ //# sourceMappingURL=public-surface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-surface.d.ts","sourceRoot":"","sources":["../../../src/tools/scoring/public-surface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAC9E,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,sCAAsC,CAAC;AAChE,OAAO,KAAK,EAAE,aAAa,EAAmD,MAAM,wCAAwC,CAAC;AAE7H,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,aAAa,CAY9F"}
@@ -0,0 +1,13 @@
1
+ export function buildPublicSurface(pages, gaps) {
2
+ const accessibilityViolations = [];
3
+ const brokenLinks = [];
4
+ for (const r of pages) {
5
+ for (const v of r.a11yViolations) {
6
+ accessibilityViolations.push({ ...v, path: r.path });
7
+ }
8
+ for (const b of r.brokenLinks) {
9
+ brokenLinks.push({ ...b, path: r.path });
10
+ }
11
+ }
12
+ return { pages, gaps, accessibilityViolations, brokenLinks };
13
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qulib/core",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Qulib — analyze deployed web apps for honest quality gaps (CLI + programmatic API)",
5
5
  "license": "MIT",
6
6
  "author": "Tapesh Nagarwal",
@@ -48,8 +48,8 @@
48
48
  "analyze": "tsx src/cli/index.ts analyze",
49
49
  "clean": "tsx src/cli/index.ts clean",
50
50
  "build": "tsc",
51
- "test": "node --import tsx/esm --test src/llm/cost-intelligence.test.ts src/tools/gap-engine.test.ts src/tools/auth-block-gap.test.ts",
52
- "test:integration": "node --import tsx/esm --test src/analyze.integration.test.ts",
51
+ "test": "node --import tsx/esm --test src/llm/__tests__/cost-intelligence.test.ts src/tools/scoring/__tests__/gaps.test.ts src/tools/auth/__tests__/gaps.test.ts src/tools/auth/__tests__/detect.test.ts src/tools/scoring/__tests__/automation-maturity.test.ts src/harness/__tests__/state-manager.test.ts src/telemetry/__tests__/redact-url.test.ts src/cli/__tests__/auth-login.test.ts src/cli/__tests__/cli-version.test.ts src/__tests__/analyze.storage-state-invalid.test.ts",
52
+ "test:integration": "node --import tsx/esm --test src/__tests__/analyze.integration.test.ts",
53
53
  "smoke": "tsx src/cli/index.ts analyze --url https://example.com --ephemeral",
54
54
  "cost-doctor": "tsx src/cli/index.ts cost doctor"
55
55
  },