@eduardbar/drift 1.2.0 → 1.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.
Files changed (195) hide show
  1. package/.gga +50 -0
  2. package/.github/actions/drift-review/README.md +60 -0
  3. package/.github/actions/drift-review/action.yml +131 -0
  4. package/.github/actions/drift-scan/README.md +28 -32
  5. package/.github/actions/drift-scan/action.yml +78 -14
  6. package/.github/workflows/publish-vscode.yml +3 -3
  7. package/.github/workflows/publish.yml +3 -3
  8. package/.github/workflows/review-pr.yml +94 -9
  9. package/AGENTS.md +75 -245
  10. package/CHANGELOG.md +28 -0
  11. package/README.md +308 -51
  12. package/ROADMAP.md +6 -5
  13. package/dist/analyzer.d.ts +2 -2
  14. package/dist/analyzer.js +420 -159
  15. package/dist/benchmark.d.ts +2 -0
  16. package/dist/benchmark.js +204 -0
  17. package/dist/cli.js +693 -67
  18. package/dist/config.js +16 -2
  19. package/dist/diff.js +66 -10
  20. package/dist/doctor.d.ts +5 -0
  21. package/dist/doctor.js +133 -0
  22. package/dist/format.d.ts +17 -0
  23. package/dist/format.js +45 -0
  24. package/dist/git.js +12 -0
  25. package/dist/guard-types.d.ts +57 -0
  26. package/dist/guard-types.js +2 -0
  27. package/dist/guard.d.ts +14 -0
  28. package/dist/guard.js +239 -0
  29. package/dist/index.d.ts +12 -3
  30. package/dist/index.js +6 -1
  31. package/dist/init.d.ts +15 -0
  32. package/dist/init.js +273 -0
  33. package/dist/map-cycles.d.ts +2 -0
  34. package/dist/map-cycles.js +34 -0
  35. package/dist/map-svg.d.ts +19 -0
  36. package/dist/map-svg.js +97 -0
  37. package/dist/map.js +78 -138
  38. package/dist/metrics.js +70 -55
  39. package/dist/output-metadata.d.ts +13 -0
  40. package/dist/output-metadata.js +17 -0
  41. package/dist/plugins-capabilities.d.ts +4 -0
  42. package/dist/plugins-capabilities.js +21 -0
  43. package/dist/plugins-messages.d.ts +10 -0
  44. package/dist/plugins-messages.js +16 -0
  45. package/dist/plugins-rules.d.ts +9 -0
  46. package/dist/plugins-rules.js +137 -0
  47. package/dist/plugins.d.ts +2 -1
  48. package/dist/plugins.js +80 -28
  49. package/dist/printer.js +4 -0
  50. package/dist/reporter-constants.d.ts +16 -0
  51. package/dist/reporter-constants.js +39 -0
  52. package/dist/reporter.d.ts +3 -3
  53. package/dist/reporter.js +35 -55
  54. package/dist/review.d.ts +2 -1
  55. package/dist/review.js +4 -3
  56. package/dist/rules/comments.js +2 -2
  57. package/dist/rules/complexity.js +2 -7
  58. package/dist/rules/nesting.js +3 -13
  59. package/dist/rules/phase0-basic.js +10 -10
  60. package/dist/rules/phase3-configurable.js +23 -15
  61. package/dist/rules/shared.d.ts +2 -0
  62. package/dist/rules/shared.js +27 -3
  63. package/dist/saas/constants.d.ts +15 -0
  64. package/dist/saas/constants.js +48 -0
  65. package/dist/saas/dashboard.d.ts +8 -0
  66. package/dist/saas/dashboard.js +132 -0
  67. package/dist/saas/errors.d.ts +19 -0
  68. package/dist/saas/errors.js +37 -0
  69. package/dist/saas/helpers.d.ts +21 -0
  70. package/dist/saas/helpers.js +110 -0
  71. package/dist/saas/ingest.d.ts +3 -0
  72. package/dist/saas/ingest.js +249 -0
  73. package/dist/saas/organization.d.ts +5 -0
  74. package/dist/saas/organization.js +82 -0
  75. package/dist/saas/plan-change.d.ts +10 -0
  76. package/dist/saas/plan-change.js +15 -0
  77. package/dist/saas/store.d.ts +21 -0
  78. package/dist/saas/store.js +159 -0
  79. package/dist/saas/types.d.ts +191 -0
  80. package/dist/saas/types.js +2 -0
  81. package/dist/saas.d.ts +8 -82
  82. package/dist/saas.js +7 -320
  83. package/dist/sarif.d.ts +74 -0
  84. package/dist/sarif.js +122 -0
  85. package/dist/trust-advanced.d.ts +14 -0
  86. package/dist/trust-advanced.js +65 -0
  87. package/dist/trust-kpi-fs.d.ts +3 -0
  88. package/dist/trust-kpi-fs.js +141 -0
  89. package/dist/trust-kpi-parse.d.ts +7 -0
  90. package/dist/trust-kpi-parse.js +186 -0
  91. package/dist/trust-kpi-types.d.ts +16 -0
  92. package/dist/trust-kpi-types.js +2 -0
  93. package/dist/trust-kpi.d.ts +7 -0
  94. package/dist/trust-kpi.js +185 -0
  95. package/dist/trust-policy.d.ts +32 -0
  96. package/dist/trust-policy.js +160 -0
  97. package/dist/trust-render.d.ts +9 -0
  98. package/dist/trust-render.js +54 -0
  99. package/dist/trust-scoring.d.ts +9 -0
  100. package/dist/trust-scoring.js +208 -0
  101. package/dist/trust.d.ts +37 -0
  102. package/dist/trust.js +168 -0
  103. package/dist/types/app.d.ts +30 -0
  104. package/dist/types/app.js +2 -0
  105. package/dist/types/config.d.ts +25 -0
  106. package/dist/types/config.js +2 -0
  107. package/dist/types/core.d.ts +100 -0
  108. package/dist/types/core.js +2 -0
  109. package/dist/types/diff.d.ts +55 -0
  110. package/dist/types/diff.js +2 -0
  111. package/dist/types/plugin.d.ts +41 -0
  112. package/dist/types/plugin.js +2 -0
  113. package/dist/types/trust.d.ts +120 -0
  114. package/dist/types/trust.js +2 -0
  115. package/dist/types.d.ts +8 -211
  116. package/docs/PRD.md +187 -109
  117. package/docs/plugin-contract.md +61 -0
  118. package/docs/release-notes-draft.md +40 -0
  119. package/docs/rules-catalog.md +49 -0
  120. package/docs/trust-core-release-checklist.md +87 -0
  121. package/package.json +6 -3
  122. package/packages/vscode-drift/src/code-actions.ts +1 -1
  123. package/schemas/drift-ai-output.v1.json +162 -0
  124. package/schemas/drift-report.v1.json +151 -0
  125. package/schemas/drift-trust.v1.json +131 -0
  126. package/scripts/smoke-repo.mjs +394 -0
  127. package/src/analyzer.ts +484 -155
  128. package/src/benchmark.ts +266 -0
  129. package/src/cli.ts +840 -85
  130. package/src/config.ts +19 -2
  131. package/src/diff.ts +84 -10
  132. package/src/doctor.ts +173 -0
  133. package/src/format.ts +81 -0
  134. package/src/git.ts +16 -0
  135. package/src/guard-types.ts +64 -0
  136. package/src/guard.ts +324 -0
  137. package/src/index.ts +83 -0
  138. package/src/init.ts +298 -0
  139. package/src/map-cycles.ts +38 -0
  140. package/src/map-svg.ts +124 -0
  141. package/src/map.ts +111 -142
  142. package/src/metrics.ts +78 -59
  143. package/src/output-metadata.ts +30 -0
  144. package/src/plugins-capabilities.ts +36 -0
  145. package/src/plugins-messages.ts +35 -0
  146. package/src/plugins-rules.ts +296 -0
  147. package/src/plugins.ts +148 -27
  148. package/src/printer.ts +4 -0
  149. package/src/reporter-constants.ts +46 -0
  150. package/src/reporter.ts +64 -65
  151. package/src/review.ts +6 -4
  152. package/src/rules/comments.ts +2 -2
  153. package/src/rules/complexity.ts +2 -7
  154. package/src/rules/nesting.ts +3 -13
  155. package/src/rules/phase0-basic.ts +11 -12
  156. package/src/rules/phase3-configurable.ts +39 -26
  157. package/src/rules/shared.ts +31 -3
  158. package/src/saas/constants.ts +56 -0
  159. package/src/saas/dashboard.ts +172 -0
  160. package/src/saas/errors.ts +45 -0
  161. package/src/saas/helpers.ts +140 -0
  162. package/src/saas/ingest.ts +278 -0
  163. package/src/saas/organization.ts +99 -0
  164. package/src/saas/plan-change.ts +19 -0
  165. package/src/saas/store.ts +172 -0
  166. package/src/saas/types.ts +216 -0
  167. package/src/saas.ts +49 -433
  168. package/src/sarif.ts +232 -0
  169. package/src/trust-advanced.ts +99 -0
  170. package/src/trust-kpi-fs.ts +169 -0
  171. package/src/trust-kpi-parse.ts +219 -0
  172. package/src/trust-kpi-types.ts +19 -0
  173. package/src/trust-kpi.ts +210 -0
  174. package/src/trust-policy.ts +246 -0
  175. package/src/trust-render.ts +61 -0
  176. package/src/trust-scoring.ts +231 -0
  177. package/src/trust.ts +260 -0
  178. package/src/types/app.ts +30 -0
  179. package/src/types/config.ts +27 -0
  180. package/src/types/core.ts +105 -0
  181. package/src/types/diff.ts +61 -0
  182. package/src/types/plugin.ts +46 -0
  183. package/src/types/trust.ts +134 -0
  184. package/src/types.ts +78 -238
  185. package/tests/cli-sarif.test.ts +92 -0
  186. package/tests/diff.test.ts +124 -0
  187. package/tests/format.test.ts +157 -0
  188. package/tests/new-features.test.ts +80 -1
  189. package/tests/phase1-init-doctor-guard.test.ts +199 -0
  190. package/tests/plugins.test.ts +219 -0
  191. package/tests/rules.test.ts +23 -1
  192. package/tests/saas-foundation.test.ts +358 -1
  193. package/tests/sarif.test.ts +160 -0
  194. package/tests/trust-kpi.test.ts +147 -0
  195. package/tests/trust.test.ts +602 -0
@@ -0,0 +1,2 @@
1
+ export declare function runBenchmarkCli(argv?: string[]): Promise<void>;
2
+ //# sourceMappingURL=benchmark.d.ts.map
@@ -0,0 +1,204 @@
1
+ import { mkdirSync, writeFileSync } from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+ import { analyzeProject } from './analyzer.js';
5
+ import { loadConfig } from './config.js';
6
+ import { buildReport } from './reporter.js';
7
+ import { generateReview } from './review.js';
8
+ import { buildTrustReport } from './trust.js';
9
+ import { cleanupTempDir, extractFilesAtRef } from './git.js';
10
+ import { computeDiff } from './diff.js';
11
+ const DEFAULT_SCAN_PATH = '.';
12
+ const DEFAULT_REVIEW_PATH = '.';
13
+ const DEFAULT_TRUST_PATH = '.';
14
+ const DEFAULT_BASE_REF = 'HEAD~1';
15
+ const DEFAULT_WARMUP_RUNS = 1;
16
+ const DEFAULT_MEASURED_RUNS = 5;
17
+ const TABLE_WIDTHS = {
18
+ task: 10,
19
+ warmup: 8,
20
+ runs: 6,
21
+ median: 13,
22
+ mean: 11,
23
+ min: 10,
24
+ max: 10,
25
+ };
26
+ const TABLE_COLUMNS = [
27
+ { key: 'task', header: 'task' },
28
+ { key: 'warmup', header: 'warmup' },
29
+ { key: 'runs', header: 'runs' },
30
+ { key: 'median', header: 'median(ms)' },
31
+ { key: 'mean', header: 'mean(ms)' },
32
+ { key: 'min', header: 'min(ms)' },
33
+ { key: 'max', header: 'max(ms)' },
34
+ ];
35
+ function parseNumberFlag(value, flagName) {
36
+ const parsed = Number(value);
37
+ if (!Number.isFinite(parsed) || parsed < 0) {
38
+ throw new Error(`${flagName} must be a non-negative number, received '${value}'`);
39
+ }
40
+ return Math.floor(parsed);
41
+ }
42
+ function parseOptions(argv) {
43
+ const options = {
44
+ scanPath: DEFAULT_SCAN_PATH,
45
+ reviewPath: DEFAULT_REVIEW_PATH,
46
+ trustPath: DEFAULT_TRUST_PATH,
47
+ baseRef: DEFAULT_BASE_REF,
48
+ warmupRuns: DEFAULT_WARMUP_RUNS,
49
+ measuredRuns: DEFAULT_MEASURED_RUNS,
50
+ };
51
+ const handlers = {
52
+ '--scan-path': (value) => { options.scanPath = value; },
53
+ '--review-path': (value) => { options.reviewPath = value; },
54
+ '--trust-path': (value) => { options.trustPath = value; },
55
+ '--base': (value) => { options.baseRef = value; },
56
+ '--warmup': (value) => { options.warmupRuns = parseNumberFlag(value, '--warmup'); },
57
+ '--runs': (value) => { options.measuredRuns = parseNumberFlag(value, '--runs'); },
58
+ '--json-out': (value) => { options.jsonOut = value; },
59
+ };
60
+ for (let i = 0; i < argv.length; i += 2) {
61
+ const arg = argv[i];
62
+ const next = argv[i + 1];
63
+ const handler = handlers[arg];
64
+ if (!handler || !next)
65
+ throw new Error(`Unknown or incomplete argument: ${arg}`);
66
+ handler(next);
67
+ }
68
+ if (options.measuredRuns < 1) {
69
+ throw new Error('--runs must be at least 1');
70
+ }
71
+ return {
72
+ ...options,
73
+ scanPath: path.resolve(options.scanPath),
74
+ reviewPath: path.resolve(options.reviewPath),
75
+ trustPath: path.resolve(options.trustPath),
76
+ jsonOut: options.jsonOut ? path.resolve(options.jsonOut) : undefined,
77
+ };
78
+ }
79
+ function median(values) {
80
+ const sorted = [...values].sort((a, b) => a - b);
81
+ const middle = Math.floor(sorted.length / 2);
82
+ if (sorted.length % 2 === 1)
83
+ return sorted[middle];
84
+ return (sorted[middle - 1] + sorted[middle]) / 2;
85
+ }
86
+ function formatMs(ms) {
87
+ return ms.toFixed(2);
88
+ }
89
+ async function runTask(name, warmupRuns, measuredRuns, task) {
90
+ for (let i = 0; i < warmupRuns; i += 1) {
91
+ await task();
92
+ }
93
+ const samplesMs = [];
94
+ for (let i = 0; i < measuredRuns; i += 1) {
95
+ const started = performance.now();
96
+ await task();
97
+ samplesMs.push(performance.now() - started);
98
+ }
99
+ const total = samplesMs.reduce((sum, sample) => sum + sample, 0);
100
+ return {
101
+ name,
102
+ warmupRuns,
103
+ measuredRuns,
104
+ samplesMs,
105
+ medianMs: median(samplesMs),
106
+ meanMs: total / samplesMs.length,
107
+ minMs: Math.min(...samplesMs),
108
+ maxMs: Math.max(...samplesMs),
109
+ };
110
+ }
111
+ function printTable(results) {
112
+ const headers = TABLE_COLUMNS.map((column) => column.header);
113
+ const widths = TABLE_COLUMNS.map((column) => TABLE_WIDTHS[column.key]);
114
+ const row = (values) => values
115
+ .map((value, index) => value.padEnd(widths[index], ' '))
116
+ .join(' ');
117
+ process.stdout.write('\n');
118
+ process.stdout.write(row(headers) + '\n');
119
+ process.stdout.write(row(widths.map((width) => '-'.repeat(width))) + '\n');
120
+ for (const result of results) {
121
+ process.stdout.write(row([
122
+ result.name,
123
+ String(result.warmupRuns),
124
+ String(result.measuredRuns),
125
+ formatMs(result.medianMs),
126
+ formatMs(result.meanMs),
127
+ formatMs(result.minMs),
128
+ formatMs(result.maxMs),
129
+ ]) + '\n');
130
+ }
131
+ }
132
+ async function runScan(scanPath) {
133
+ const config = await loadConfig(scanPath);
134
+ const files = analyzeProject(scanPath, config);
135
+ buildReport(scanPath, files);
136
+ }
137
+ async function runTrust(trustPath, baseRef) {
138
+ const config = await loadConfig(trustPath);
139
+ const files = analyzeProject(trustPath, config);
140
+ const report = buildReport(trustPath, files);
141
+ let tempDir;
142
+ let diff;
143
+ try {
144
+ tempDir = extractFilesAtRef(trustPath, baseRef);
145
+ const baseFiles = analyzeProject(tempDir, config);
146
+ const baseReport = buildReport(tempDir, baseFiles);
147
+ diff = computeDiff(baseReport, report, baseRef);
148
+ }
149
+ catch {
150
+ diff = undefined;
151
+ }
152
+ finally {
153
+ if (tempDir)
154
+ cleanupTempDir(tempDir);
155
+ }
156
+ buildTrustReport(report, { diff });
157
+ }
158
+ async function runReview(reviewPath, baseRef) {
159
+ await generateReview(reviewPath, baseRef);
160
+ }
161
+ async function main(argv) {
162
+ const options = parseOptions(argv);
163
+ const results = [
164
+ await runTask('scan', options.warmupRuns, options.measuredRuns, () => runScan(options.scanPath)),
165
+ await runTask('review', options.warmupRuns, options.measuredRuns, () => runReview(options.reviewPath, options.baseRef)),
166
+ await runTask('trust', options.warmupRuns, options.measuredRuns, () => runTrust(options.trustPath, options.baseRef)),
167
+ ];
168
+ const output = {
169
+ meta: {
170
+ scannedAt: new Date().toISOString(),
171
+ node: process.version,
172
+ platform: process.platform,
173
+ cwd: process.cwd(),
174
+ },
175
+ options,
176
+ results,
177
+ };
178
+ printTable(results);
179
+ process.stdout.write(`\n${JSON.stringify(output, null, 2)}\n`);
180
+ if (options.jsonOut) {
181
+ mkdirSync(path.dirname(options.jsonOut), { recursive: true });
182
+ writeFileSync(options.jsonOut, JSON.stringify(output, null, 2) + '\n', 'utf8');
183
+ process.stdout.write(`\nSaved benchmark JSON to ${options.jsonOut}\n`);
184
+ }
185
+ }
186
+ function isExecutedAsEntryPoint() {
187
+ const entryArg = process.argv[1];
188
+ if (!entryArg)
189
+ return false;
190
+ return import.meta.url === pathToFileURL(path.resolve(entryArg)).href;
191
+ }
192
+ export async function runBenchmarkCli(argv = process.argv.slice(2)) {
193
+ try {
194
+ await main(argv);
195
+ }
196
+ catch (error) {
197
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
198
+ process.exit(1);
199
+ }
200
+ }
201
+ if (isExecutedAsEntryPoint()) {
202
+ void runBenchmarkCli();
203
+ }
204
+ //# sourceMappingURL=benchmark.js.map