@eduardbar/drift 1.2.0 → 1.3.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 (61) hide show
  1. package/.github/workflows/publish-vscode.yml +3 -3
  2. package/.github/workflows/publish.yml +3 -3
  3. package/.github/workflows/review-pr.yml +98 -6
  4. package/AGENTS.md +6 -0
  5. package/README.md +160 -10
  6. package/ROADMAP.md +6 -5
  7. package/dist/analyzer.d.ts +2 -2
  8. package/dist/analyzer.js +420 -159
  9. package/dist/benchmark.d.ts +2 -0
  10. package/dist/benchmark.js +185 -0
  11. package/dist/cli.js +453 -62
  12. package/dist/diff.js +74 -10
  13. package/dist/git.js +12 -0
  14. package/dist/index.d.ts +5 -3
  15. package/dist/index.js +3 -1
  16. package/dist/plugins.d.ts +2 -1
  17. package/dist/plugins.js +177 -28
  18. package/dist/printer.js +4 -0
  19. package/dist/review.js +2 -2
  20. package/dist/rules/comments.js +2 -2
  21. package/dist/rules/complexity.js +2 -7
  22. package/dist/rules/nesting.js +3 -13
  23. package/dist/rules/phase0-basic.js +10 -10
  24. package/dist/rules/shared.d.ts +2 -0
  25. package/dist/rules/shared.js +27 -3
  26. package/dist/saas.d.ts +143 -7
  27. package/dist/saas.js +478 -37
  28. package/dist/trust-kpi.d.ts +9 -0
  29. package/dist/trust-kpi.js +445 -0
  30. package/dist/trust.d.ts +65 -0
  31. package/dist/trust.js +571 -0
  32. package/dist/types.d.ts +154 -0
  33. package/docs/PRD.md +187 -109
  34. package/docs/plugin-contract.md +61 -0
  35. package/docs/trust-core-release-checklist.md +55 -0
  36. package/package.json +5 -3
  37. package/src/analyzer.ts +484 -155
  38. package/src/benchmark.ts +244 -0
  39. package/src/cli.ts +562 -79
  40. package/src/diff.ts +75 -10
  41. package/src/git.ts +16 -0
  42. package/src/index.ts +48 -0
  43. package/src/plugins.ts +354 -26
  44. package/src/printer.ts +4 -0
  45. package/src/review.ts +2 -2
  46. package/src/rules/comments.ts +2 -2
  47. package/src/rules/complexity.ts +2 -7
  48. package/src/rules/nesting.ts +3 -13
  49. package/src/rules/phase0-basic.ts +11 -12
  50. package/src/rules/shared.ts +31 -3
  51. package/src/saas.ts +641 -43
  52. package/src/trust-kpi.ts +518 -0
  53. package/src/trust.ts +774 -0
  54. package/src/types.ts +171 -0
  55. package/tests/diff.test.ts +124 -0
  56. package/tests/new-features.test.ts +71 -0
  57. package/tests/plugins.test.ts +219 -0
  58. package/tests/rules.test.ts +23 -1
  59. package/tests/saas-foundation.test.ts +358 -1
  60. package/tests/trust-kpi.test.ts +120 -0
  61. package/tests/trust.test.ts +584 -0
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=benchmark.d.ts.map
@@ -0,0 +1,185 @@
1
+ import { mkdirSync, writeFileSync } from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { analyzeProject } from './analyzer.js';
4
+ import { loadConfig } from './config.js';
5
+ import { buildReport } from './reporter.js';
6
+ import { generateReview } from './review.js';
7
+ import { buildTrustReport } from './trust.js';
8
+ import { cleanupTempDir, extractFilesAtRef } from './git.js';
9
+ import { computeDiff } from './diff.js';
10
+ function parseNumberFlag(value, flagName) {
11
+ const parsed = Number(value);
12
+ if (!Number.isFinite(parsed) || parsed < 0) {
13
+ throw new Error(`${flagName} must be a non-negative number, received '${value}'`);
14
+ }
15
+ return Math.floor(parsed);
16
+ }
17
+ function parseOptions(argv) {
18
+ const options = {
19
+ scanPath: '.',
20
+ reviewPath: '.',
21
+ trustPath: '.',
22
+ baseRef: 'HEAD~1',
23
+ warmupRuns: 1,
24
+ measuredRuns: 5,
25
+ };
26
+ for (let i = 0; i < argv.length; i += 1) {
27
+ const arg = argv[i];
28
+ const next = argv[i + 1];
29
+ if (arg === '--scan-path' && next) {
30
+ options.scanPath = next;
31
+ i += 1;
32
+ continue;
33
+ }
34
+ if (arg === '--review-path' && next) {
35
+ options.reviewPath = next;
36
+ i += 1;
37
+ continue;
38
+ }
39
+ if (arg === '--trust-path' && next) {
40
+ options.trustPath = next;
41
+ i += 1;
42
+ continue;
43
+ }
44
+ if (arg === '--base' && next) {
45
+ options.baseRef = next;
46
+ i += 1;
47
+ continue;
48
+ }
49
+ if (arg === '--warmup' && next) {
50
+ options.warmupRuns = parseNumberFlag(next, '--warmup');
51
+ i += 1;
52
+ continue;
53
+ }
54
+ if (arg === '--runs' && next) {
55
+ options.measuredRuns = parseNumberFlag(next, '--runs');
56
+ i += 1;
57
+ continue;
58
+ }
59
+ if (arg === '--json-out' && next) {
60
+ options.jsonOut = next;
61
+ i += 1;
62
+ continue;
63
+ }
64
+ throw new Error(`Unknown or incomplete argument: ${arg}`);
65
+ }
66
+ if (options.measuredRuns < 1) {
67
+ throw new Error('--runs must be at least 1');
68
+ }
69
+ return {
70
+ ...options,
71
+ scanPath: path.resolve(options.scanPath),
72
+ reviewPath: path.resolve(options.reviewPath),
73
+ trustPath: path.resolve(options.trustPath),
74
+ jsonOut: options.jsonOut ? path.resolve(options.jsonOut) : undefined,
75
+ };
76
+ }
77
+ function median(values) {
78
+ const sorted = [...values].sort((a, b) => a - b);
79
+ const middle = Math.floor(sorted.length / 2);
80
+ if (sorted.length % 2 === 1)
81
+ return sorted[middle];
82
+ return (sorted[middle - 1] + sorted[middle]) / 2;
83
+ }
84
+ function formatMs(ms) {
85
+ return ms.toFixed(2);
86
+ }
87
+ async function runTask(name, warmupRuns, measuredRuns, task) {
88
+ for (let i = 0; i < warmupRuns; i += 1) {
89
+ await task();
90
+ }
91
+ const samplesMs = [];
92
+ for (let i = 0; i < measuredRuns; i += 1) {
93
+ const started = performance.now();
94
+ await task();
95
+ samplesMs.push(performance.now() - started);
96
+ }
97
+ const total = samplesMs.reduce((sum, sample) => sum + sample, 0);
98
+ return {
99
+ name,
100
+ warmupRuns,
101
+ measuredRuns,
102
+ samplesMs,
103
+ medianMs: median(samplesMs),
104
+ meanMs: total / samplesMs.length,
105
+ minMs: Math.min(...samplesMs),
106
+ maxMs: Math.max(...samplesMs),
107
+ };
108
+ }
109
+ function printTable(results) {
110
+ const headers = ['task', 'warmup', 'runs', 'median(ms)', 'mean(ms)', 'min(ms)', 'max(ms)'];
111
+ const widths = [10, 8, 6, 13, 11, 10, 10];
112
+ const row = (values) => values
113
+ .map((value, index) => value.padEnd(widths[index], ' '))
114
+ .join(' ');
115
+ process.stdout.write('\n');
116
+ process.stdout.write(row(headers) + '\n');
117
+ process.stdout.write(row(widths.map((width) => '-'.repeat(width))) + '\n');
118
+ for (const result of results) {
119
+ process.stdout.write(row([
120
+ result.name,
121
+ String(result.warmupRuns),
122
+ String(result.measuredRuns),
123
+ formatMs(result.medianMs),
124
+ formatMs(result.meanMs),
125
+ formatMs(result.minMs),
126
+ formatMs(result.maxMs),
127
+ ]) + '\n');
128
+ }
129
+ }
130
+ async function runScan(scanPath) {
131
+ const config = await loadConfig(scanPath);
132
+ const files = analyzeProject(scanPath, config);
133
+ buildReport(scanPath, files);
134
+ }
135
+ async function runTrust(trustPath, baseRef) {
136
+ const config = await loadConfig(trustPath);
137
+ const files = analyzeProject(trustPath, config);
138
+ const report = buildReport(trustPath, files);
139
+ let tempDir;
140
+ let diff;
141
+ try {
142
+ tempDir = extractFilesAtRef(trustPath, baseRef);
143
+ const baseFiles = analyzeProject(tempDir, config);
144
+ const baseReport = buildReport(tempDir, baseFiles);
145
+ diff = computeDiff(baseReport, report, baseRef);
146
+ }
147
+ catch {
148
+ diff = undefined;
149
+ }
150
+ finally {
151
+ if (tempDir)
152
+ cleanupTempDir(tempDir);
153
+ }
154
+ buildTrustReport(report, { diff });
155
+ }
156
+ async function main() {
157
+ const options = parseOptions(process.argv.slice(2));
158
+ const results = [
159
+ await runTask('scan', options.warmupRuns, options.measuredRuns, () => runScan(options.scanPath)),
160
+ await runTask('review', options.warmupRuns, options.measuredRuns, () => generateReview(options.reviewPath, options.baseRef).then(() => undefined)),
161
+ await runTask('trust', options.warmupRuns, options.measuredRuns, () => runTrust(options.trustPath, options.baseRef)),
162
+ ];
163
+ const output = {
164
+ meta: {
165
+ scannedAt: new Date().toISOString(),
166
+ node: process.version,
167
+ platform: process.platform,
168
+ cwd: process.cwd(),
169
+ },
170
+ options,
171
+ results,
172
+ };
173
+ printTable(results);
174
+ process.stdout.write(`\n${JSON.stringify(output, null, 2)}\n`);
175
+ if (options.jsonOut) {
176
+ mkdirSync(path.dirname(options.jsonOut), { recursive: true });
177
+ writeFileSync(options.jsonOut, JSON.stringify(output, null, 2) + '\n', 'utf8');
178
+ process.stdout.write(`\nSaved benchmark JSON to ${options.jsonOut}\n`);
179
+ }
180
+ }
181
+ main().catch((error) => {
182
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
183
+ process.exit(1);
184
+ });
185
+ //# sourceMappingURL=benchmark.js.map