greenbump 0.4.0 → 0.6.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 (78) hide show
  1. package/README.md +673 -66
  2. package/README.zh-CN.md +525 -100
  3. package/dist/agent/fixer.js +161 -12
  4. package/dist/agent/fixer.js.map +1 -1
  5. package/dist/agent/fixer.test.js +410 -0
  6. package/dist/agent/fixer.test.js.map +1 -0
  7. package/dist/cli/interactive.js +81 -0
  8. package/dist/cli/interactive.js.map +1 -0
  9. package/dist/cli.js +53 -0
  10. package/dist/cli.js.map +1 -1
  11. package/dist/engine/analyzers/changelog-parser.js +121 -0
  12. package/dist/engine/analyzers/changelog-parser.js.map +1 -0
  13. package/dist/engine/batch.test.js +59 -0
  14. package/dist/engine/batch.test.js.map +1 -0
  15. package/dist/engine/cache/manager.js +165 -0
  16. package/dist/engine/cache/manager.js.map +1 -0
  17. package/dist/engine/cache/manager.test.js +75 -0
  18. package/dist/engine/cache/manager.test.js.map +1 -0
  19. package/dist/engine/checks.test.js +92 -0
  20. package/dist/engine/checks.test.js.map +1 -0
  21. package/dist/engine/context/optimizer.js +189 -0
  22. package/dist/engine/context/optimizer.js.map +1 -0
  23. package/dist/engine/context/optimizer.test.js +81 -0
  24. package/dist/engine/context/optimizer.test.js.map +1 -0
  25. package/dist/engine/ecosystems/parsers.test.js +255 -0
  26. package/dist/engine/ecosystems/parsers.test.js.map +1 -0
  27. package/dist/engine/fixer/patterns.js +356 -0
  28. package/dist/engine/fixer/patterns.js.map +1 -0
  29. package/dist/engine/fixer/patterns.test.js +124 -0
  30. package/dist/engine/fixer/patterns.test.js.map +1 -0
  31. package/dist/engine/git.js +1 -0
  32. package/dist/engine/git.js.map +1 -1
  33. package/dist/engine/git.test.js +111 -0
  34. package/dist/engine/git.test.js.map +1 -0
  35. package/dist/engine/perf/metrics.js +171 -0
  36. package/dist/engine/perf/metrics.js.map +1 -0
  37. package/dist/engine/perf/regression.js +115 -0
  38. package/dist/engine/perf/regression.js.map +1 -0
  39. package/dist/engine/run.js +93 -2
  40. package/dist/engine/run.js.map +1 -1
  41. package/dist/engine/run.test.js +47 -0
  42. package/dist/engine/run.test.js.map +1 -0
  43. package/dist/engine/sandbox/docker-manager.js +215 -0
  44. package/dist/engine/sandbox/docker-manager.js.map +1 -0
  45. package/dist/engine/sandbox/orchestrator.js +107 -0
  46. package/dist/engine/sandbox/orchestrator.js.map +1 -0
  47. package/dist/engine/sandbox/service-detector.js +147 -0
  48. package/dist/engine/sandbox/service-detector.js.map +1 -0
  49. package/dist/engine/sandbox/template-generator.js +241 -0
  50. package/dist/engine/sandbox/template-generator.js.map +1 -0
  51. package/dist/engine/verifiers/ast-analyzer.js +212 -0
  52. package/dist/engine/verifiers/ast-analyzer.js.map +1 -0
  53. package/dist/engine/verifiers/ast-analyzer.test.js +96 -0
  54. package/dist/engine/verifiers/ast-analyzer.test.js.map +1 -0
  55. package/dist/engine/workspace.test.js +95 -0
  56. package/dist/engine/workspace.test.js.map +1 -0
  57. package/dist/exitcode.test.js +62 -0
  58. package/dist/exitcode.test.js.map +1 -0
  59. package/dist/format.js +15 -0
  60. package/dist/format.js.map +1 -1
  61. package/dist/report.test.js +56 -0
  62. package/dist/report.test.js.map +1 -0
  63. package/package.json +7 -4
  64. package/CHANGELOG.md +0 -93
  65. package/CONTRIBUTING.md +0 -52
  66. package/SECURITY.md +0 -38
  67. package/action-dist/index.cjs +0 -19935
  68. package/action.yml +0 -76
  69. package/competitive-analysis.md +0 -304
  70. package/examples/greenbump.yml +0 -37
  71. package/examples/legacy-repo/package.json +0 -12
  72. package/examples/legacy-repo/test.js +0 -17
  73. package/examples/multi-file-repo/models/todo.js +0 -8
  74. package/examples/multi-file-repo/models/user.js +0 -11
  75. package/examples/multi-file-repo/package.json +0 -12
  76. package/examples/multi-file-repo/services/session.js +0 -9
  77. package/examples/multi-file-repo/test.js +0 -19
  78. package/optimization-roadmap.md +0 -772
@@ -0,0 +1,241 @@
1
+ import { existsSync } from "fs";
2
+ import { join } from "path";
3
+ import { readFile } from "fs/promises";
4
+ /**
5
+ * Detect project type based on files present
6
+ */
7
+ export async function detectProjectType(cwd) {
8
+ if (existsSync(join(cwd, "package.json"))) {
9
+ return "nodejs";
10
+ }
11
+ if (existsSync(join(cwd, "requirements.txt")) ||
12
+ existsSync(join(cwd, "pyproject.toml")) ||
13
+ existsSync(join(cwd, "setup.py"))) {
14
+ return "python";
15
+ }
16
+ return "unknown";
17
+ }
18
+ /**
19
+ * Generate Dockerfile for project
20
+ */
21
+ export async function generateDockerfile(projectType, cwd) {
22
+ if (projectType === "nodejs") {
23
+ return await generateNodeJsDockerfile(cwd);
24
+ }
25
+ if (projectType === "python") {
26
+ return await generatePythonDockerfile(cwd);
27
+ }
28
+ // Fallback to Node.js
29
+ return await generateNodeJsDockerfile(cwd);
30
+ }
31
+ /**
32
+ * Generate Node.js Dockerfile
33
+ */
34
+ async function generateNodeJsDockerfile(cwd) {
35
+ const hasPnpm = existsSync(join(cwd, "pnpm-lock.yaml"));
36
+ const hasYarn = existsSync(join(cwd, "yarn.lock"));
37
+ let installCmd = "npm ci";
38
+ let copyLockFile = "COPY package*.json ./";
39
+ if (hasPnpm) {
40
+ installCmd = "corepack enable && pnpm install --frozen-lockfile";
41
+ copyLockFile = "COPY package*.json pnpm-lock.yaml ./";
42
+ }
43
+ else if (hasYarn) {
44
+ installCmd = "yarn install --frozen-lockfile";
45
+ copyLockFile = "COPY package*.json yarn.lock ./";
46
+ }
47
+ return `FROM node:20-slim
48
+
49
+ WORKDIR /workspace
50
+
51
+ # Copy package files
52
+ ${copyLockFile}
53
+
54
+ # Install dependencies
55
+ RUN ${installCmd}
56
+
57
+ # Copy source code
58
+ COPY . .
59
+
60
+ # Build if build script exists
61
+ RUN if grep -q '"build":' package.json; then npm run build || true; fi
62
+
63
+ # Default command: run tests
64
+ CMD ["npm", "test"]
65
+ `;
66
+ }
67
+ /**
68
+ * Generate Python Dockerfile
69
+ */
70
+ async function generatePythonDockerfile(cwd) {
71
+ const hasRequirements = existsSync(join(cwd, "requirements.txt"));
72
+ const hasPyproject = existsSync(join(cwd, "pyproject.toml"));
73
+ let installCmd = "";
74
+ let copyFiles = "";
75
+ if (hasRequirements) {
76
+ copyFiles = "COPY requirements*.txt ./";
77
+ installCmd = "pip install --no-cache-dir -r requirements.txt";
78
+ }
79
+ else if (hasPyproject) {
80
+ copyFiles = "COPY pyproject.toml setup.py* ./";
81
+ installCmd = "pip install --no-cache-dir -e .";
82
+ }
83
+ return `FROM python:3.11-slim
84
+
85
+ WORKDIR /workspace
86
+
87
+ # Copy dependency files
88
+ ${copyFiles}
89
+
90
+ # Install dependencies
91
+ RUN ${installCmd}
92
+
93
+ # Copy source code
94
+ COPY . .
95
+
96
+ # Default command: run tests
97
+ CMD ["pytest"]
98
+ `;
99
+ }
100
+ /**
101
+ * Generate docker-compose.yml for services
102
+ */
103
+ export async function generateComposeFile(services) {
104
+ const serviceConfigs = services.map(getServiceConfig).filter((config) => config !== null);
105
+ if (serviceConfigs.length === 0) {
106
+ return "";
107
+ }
108
+ const servicesYaml = serviceConfigs.map(formatServiceConfig).join("\n");
109
+ return `version: '3.8'
110
+
111
+ services:
112
+ ${servicesYaml}
113
+
114
+ networks:
115
+ default:
116
+ name: greenbump_network
117
+ `;
118
+ }
119
+ /**
120
+ * Get service configuration
121
+ */
122
+ function getServiceConfig(serviceName) {
123
+ const configs = {
124
+ postgres: {
125
+ name: "postgres",
126
+ image: "postgres:16-alpine",
127
+ ports: ["5432:5432"],
128
+ environment: {
129
+ POSTGRES_USER: "test",
130
+ POSTGRES_PASSWORD: "test",
131
+ POSTGRES_DB: "test",
132
+ },
133
+ healthcheck: {
134
+ test: ["CMD-SHELL", "pg_isready -U test"],
135
+ interval: "5s",
136
+ timeout: "5s",
137
+ retries: 5,
138
+ },
139
+ },
140
+ mysql: {
141
+ name: "mysql",
142
+ image: "mysql:8",
143
+ ports: ["3306:3306"],
144
+ environment: {
145
+ MYSQL_ROOT_PASSWORD: "test",
146
+ MYSQL_DATABASE: "test",
147
+ },
148
+ healthcheck: {
149
+ test: ["CMD", "mysqladmin", "ping", "-h", "localhost"],
150
+ interval: "5s",
151
+ timeout: "5s",
152
+ retries: 5,
153
+ },
154
+ },
155
+ redis: {
156
+ name: "redis",
157
+ image: "redis:7-alpine",
158
+ ports: ["6379:6379"],
159
+ healthcheck: {
160
+ test: ["CMD", "redis-cli", "ping"],
161
+ interval: "5s",
162
+ timeout: "5s",
163
+ retries: 5,
164
+ },
165
+ },
166
+ mongodb: {
167
+ name: "mongodb",
168
+ image: "mongo:7",
169
+ ports: ["27017:27017"],
170
+ environment: {
171
+ MONGO_INITDB_ROOT_USERNAME: "test",
172
+ MONGO_INITDB_ROOT_PASSWORD: "test",
173
+ },
174
+ healthcheck: {
175
+ test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"],
176
+ interval: "5s",
177
+ timeout: "5s",
178
+ retries: 5,
179
+ },
180
+ },
181
+ };
182
+ return configs[serviceName] || null;
183
+ }
184
+ /**
185
+ * Format service config as YAML
186
+ */
187
+ function formatServiceConfig(config) {
188
+ const lines = [` ${config.name}:`];
189
+ lines.push(` image: ${config.image}`);
190
+ if (config.environment) {
191
+ lines.push(" environment:");
192
+ for (const [key, value] of Object.entries(config.environment)) {
193
+ lines.push(` ${key}: ${value}`);
194
+ }
195
+ }
196
+ if (config.ports.length > 0) {
197
+ lines.push(" ports:");
198
+ for (const port of config.ports) {
199
+ lines.push(` - "${port}"`);
200
+ }
201
+ }
202
+ lines.push(" healthcheck:");
203
+ lines.push(` test: ${JSON.stringify(config.healthcheck.test)}`);
204
+ lines.push(` interval: ${config.healthcheck.interval}`);
205
+ lines.push(` timeout: ${config.healthcheck.timeout}`);
206
+ lines.push(` retries: ${config.healthcheck.retries}`);
207
+ return lines.join("\n");
208
+ }
209
+ /**
210
+ * Check if project has build script
211
+ */
212
+ export async function hasBuildScript(cwd) {
213
+ const pkgPath = join(cwd, "package.json");
214
+ if (!existsSync(pkgPath))
215
+ return false;
216
+ try {
217
+ const content = await readFile(pkgPath, "utf-8");
218
+ const pkg = JSON.parse(content);
219
+ return pkg.scripts?.build !== undefined;
220
+ }
221
+ catch {
222
+ return false;
223
+ }
224
+ }
225
+ /**
226
+ * Check if project has test script
227
+ */
228
+ export async function hasTestScript(cwd) {
229
+ const pkgPath = join(cwd, "package.json");
230
+ if (!existsSync(pkgPath))
231
+ return false;
232
+ try {
233
+ const content = await readFile(pkgPath, "utf-8");
234
+ const pkg = JSON.parse(content);
235
+ return pkg.scripts?.test !== undefined;
236
+ }
237
+ catch {
238
+ return false;
239
+ }
240
+ }
241
+ //# sourceMappingURL=template-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-generator.js","sourceRoot":"","sources":["../../../src/engine/sandbox/template-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,212 @@
1
+ import { readFile, readdir, stat } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import ts from "typescript";
4
+ const ANALYZABLE_EXT = new Set([".ts", ".tsx", ".mts", ".cts"]);
5
+ const MAX_FILE_SIZE = 500_000;
6
+ /** Extract the exported symbol table from a TS/TSX file. */
7
+ function extractExports(code, filename) {
8
+ const symbols = new Map();
9
+ let sourceFile;
10
+ try {
11
+ sourceFile = ts.createSourceFile(filename, code, ts.ScriptTarget.Latest, true);
12
+ }
13
+ catch {
14
+ return symbols;
15
+ }
16
+ const hasExportModifier = (node) => {
17
+ const mods = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
18
+ return mods?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
19
+ };
20
+ const isDefault = (node) => {
21
+ const mods = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
22
+ return mods?.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword) ?? false;
23
+ };
24
+ const visit = (node) => {
25
+ // export function foo(...) / export class Foo / export const foo
26
+ if ((ts.isFunctionDeclaration(node) ||
27
+ ts.isClassDeclaration(node) ||
28
+ ts.isInterfaceDeclaration(node) ||
29
+ ts.isTypeAliasDeclaration(node) ||
30
+ ts.isEnumDeclaration(node)) &&
31
+ hasExportModifier(node)) {
32
+ const name = isDefault(node) ? "default" : node.name?.getText(sourceFile);
33
+ if (name) {
34
+ symbols.set(name, {
35
+ name,
36
+ kind: ts.isFunctionDeclaration(node)
37
+ ? "function"
38
+ : ts.isClassDeclaration(node)
39
+ ? "class"
40
+ : ts.isInterfaceDeclaration(node)
41
+ ? "interface"
42
+ : ts.isTypeAliasDeclaration(node)
43
+ ? "type"
44
+ : "enum",
45
+ signature: ts.isFunctionDeclaration(node) ? signatureOf(node, sourceFile) : undefined,
46
+ });
47
+ }
48
+ }
49
+ // export const foo = ...
50
+ if (ts.isVariableStatement(node) && hasExportModifier(node)) {
51
+ for (const decl of node.declarationList.declarations) {
52
+ const name = decl.name.getText(sourceFile);
53
+ symbols.set(name, {
54
+ name,
55
+ kind: "const",
56
+ signature: decl.type?.getText(sourceFile),
57
+ });
58
+ }
59
+ }
60
+ // export { a, b } / export { a as b }
61
+ if (ts.isExportDeclaration(node) && node.exportClause && ts.isNamedExports(node.exportClause)) {
62
+ for (const el of node.exportClause.elements) {
63
+ const name = el.name.getText(sourceFile);
64
+ symbols.set(name, { name, kind: "unknown" });
65
+ }
66
+ }
67
+ // export default <expr>
68
+ if (ts.isExportAssignment(node) && !node.isExportEquals) {
69
+ symbols.set("default", { name: "default", kind: "unknown" });
70
+ }
71
+ ts.forEachChild(node, visit);
72
+ };
73
+ visit(sourceFile);
74
+ return symbols;
75
+ }
76
+ function signatureOf(fn, sf) {
77
+ const params = fn.parameters.map((p) => {
78
+ const type = p.type ? p.type.getText(sf) : "any";
79
+ return `${p.name.getText(sf)}: ${type}`;
80
+ });
81
+ const ret = fn.type ? fn.type.getText(sf) : "void";
82
+ return `(${params.join(", ")}) => ${ret}`;
83
+ }
84
+ /**
85
+ * Compare the exported API surface before vs after the fix.
86
+ * `changedFiles` are the files the fix agent touched (project-relative).
87
+ * For each, we compare git HEAD content (pre-upgrade... actually pre-fix)
88
+ * against current working-tree content.
89
+ */
90
+ export async function analyzeApiChanges(cwd, changedFiles, readBefore) {
91
+ const changes = [];
92
+ for (const file of changedFiles) {
93
+ const ext = file.slice(file.lastIndexOf("."));
94
+ if (!ANALYZABLE_EXT.has(ext))
95
+ continue;
96
+ const abs = join(cwd, file);
97
+ let after;
98
+ try {
99
+ const s = await stat(abs);
100
+ if (s.size > MAX_FILE_SIZE)
101
+ continue;
102
+ after = await readFile(abs, "utf8");
103
+ }
104
+ catch {
105
+ continue; // file deleted or unreadable — covered by change-detector
106
+ }
107
+ const before = await readBefore(file);
108
+ if (before === null)
109
+ continue; // new file — nothing to compare
110
+ const beforeExports = extractExports(before, file);
111
+ const afterExports = extractExports(after, file);
112
+ // Removed exports
113
+ for (const [name, sym] of beforeExports) {
114
+ if (!afterExports.has(name)) {
115
+ changes.push({
116
+ kind: sym.kind === "type" || sym.kind === "interface" ? "export-type-removed" : "export-removed",
117
+ file,
118
+ symbol: name,
119
+ detail: `${sym.kind} \`${name}\` was exported before the fix but is no longer exported`,
120
+ severity: name === "default" ? "critical" : "warning",
121
+ });
122
+ continue;
123
+ }
124
+ // Signature changes on functions
125
+ const afterSym = afterExports.get(name);
126
+ if (sym.kind === "function" &&
127
+ afterSym.kind === "function" &&
128
+ sym.signature &&
129
+ afterSym.signature &&
130
+ sym.signature !== afterSym.signature) {
131
+ changes.push({
132
+ kind: "signature-changed",
133
+ file,
134
+ symbol: name,
135
+ detail: `\`${name}\` signature changed: ${sym.signature} → ${afterSym.signature}`,
136
+ severity: "warning",
137
+ });
138
+ }
139
+ }
140
+ // Newly-loosened types: any introduced where a concrete type existed
141
+ const loosened = detectTypeLoosening(before, after, file);
142
+ changes.push(...loosened);
143
+ }
144
+ const hasCritical = changes.some((c) => c.severity === "critical");
145
+ const criticalCount = changes.filter((c) => c.severity === "critical").length;
146
+ const warningCount = changes.filter((c) => c.severity === "warning").length;
147
+ const summary = changes.length === 0
148
+ ? "no API surface changes detected"
149
+ : `${criticalCount} critical, ${warningCount} warning(s) — exported API changed`;
150
+ return { changes, hasCritical, summary };
151
+ }
152
+ /** Detect `any` annotations introduced by the fix (silencing the compiler). */
153
+ function detectTypeLoosening(before, after, file) {
154
+ const out = [];
155
+ const countAny = (code) => {
156
+ const m = code.match(/:\s*any\b|<any>|as any\b/g);
157
+ return m ? m.length : 0;
158
+ };
159
+ const beforeCount = countAny(before);
160
+ const afterCount = countAny(after);
161
+ if (afterCount > beforeCount) {
162
+ out.push({
163
+ kind: "type-loosened-to-any",
164
+ file,
165
+ symbol: "(multiple)",
166
+ detail: `fix introduced ${afterCount - beforeCount} new \`any\` annotation(s) — may be masking a real type error`,
167
+ severity: "warning",
168
+ });
169
+ }
170
+ return out;
171
+ }
172
+ /** Read a file's content from git HEAD (used as the "before" state). */
173
+ export async function readFromGitHead(cwd, path) {
174
+ const { execFile } = await import("node:child_process");
175
+ return new Promise((resolve) => {
176
+ execFile("git", ["show", `HEAD:${path}`], { cwd, maxBuffer: 10 * 1024 * 1024 }, (err, stdout) => {
177
+ if (err)
178
+ resolve(null);
179
+ else
180
+ resolve(stdout);
181
+ });
182
+ });
183
+ }
184
+ /** List TS/TSX files in the project (for full-project scans, if needed). */
185
+ export async function listAnalyzableFiles(cwd, root = ".") {
186
+ const IGNORE = new Set(["node_modules", ".git", "dist", "build", ".next", "coverage"]);
187
+ const out = [];
188
+ async function walk(dir) {
189
+ let entries;
190
+ try {
191
+ entries = await readdir(join(cwd, dir), { withFileTypes: true });
192
+ }
193
+ catch {
194
+ return;
195
+ }
196
+ for (const e of entries) {
197
+ if (IGNORE.has(e.name))
198
+ continue;
199
+ const rel = dir === "." ? e.name : `${dir}/${e.name}`;
200
+ if (e.isDirectory())
201
+ await walk(rel);
202
+ else {
203
+ const ext = e.name.slice(e.name.lastIndexOf("."));
204
+ if (ANALYZABLE_EXT.has(ext))
205
+ out.push(rel);
206
+ }
207
+ }
208
+ }
209
+ await walk(root);
210
+ return out;
211
+ }
212
+ //# sourceMappingURL=ast-analyzer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast-analyzer.js","sourceRoot":"","sources":["../../../src/engine/verifiers/ast-analyzer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ import { test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { mkdtemp, writeFile, rm } from "node:fs/promises";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { analyzeApiChanges } from "./ast-analyzer.js";
7
+ async function withFile(content, before, fn) {
8
+ const dir = await mkdtemp(join(tmpdir(), "greenbump-ast-"));
9
+ await writeFile(join(dir, "api.ts"), content, "utf8");
10
+ try {
11
+ const result = await analyzeApiChanges(dir, ["api.ts"], async () => before);
12
+ fn(result);
13
+ }
14
+ finally {
15
+ await rm(dir, { recursive: true, force: true });
16
+ }
17
+ }
18
+ test("ast-analyzer: detects removed exported function", async () => {
19
+ const before = `export function renderApp(el: Element): void {}
20
+ export function cleanup(): void {}
21
+ `;
22
+ const after = `export function cleanup(): void {}
23
+ `;
24
+ await withFile(after, before, (result) => {
25
+ assert.equal(result.changes.length, 1);
26
+ assert.equal(result.changes[0].kind, "export-removed");
27
+ assert.equal(result.changes[0].symbol, "renderApp");
28
+ assert.equal(result.hasCritical, false); // warning, not critical
29
+ });
30
+ });
31
+ test("ast-analyzer: removed default export is critical", async () => {
32
+ const before = `export default function App() { return null; }
33
+ `;
34
+ const after = `function App() { return null; }
35
+ `;
36
+ await withFile(after, before, (result) => {
37
+ const removed = result.changes.find((c) => c.kind === "export-removed");
38
+ assert.ok(removed);
39
+ assert.equal(removed.severity, "critical");
40
+ assert.equal(result.hasCritical, true);
41
+ });
42
+ });
43
+ test("ast-analyzer: detects function signature changes", async () => {
44
+ const before = `export function fetchData(url: string): Promise<Response> { throw 0; }
45
+ `;
46
+ const after = `export function fetchData(url: string, opts: object): Promise<Response> { throw 0; }
47
+ `;
48
+ await withFile(after, before, (result) => {
49
+ const sig = result.changes.find((c) => c.kind === "signature-changed");
50
+ assert.ok(sig);
51
+ assert.equal(sig.symbol, "fetchData");
52
+ assert.equal(sig.severity, "warning");
53
+ });
54
+ });
55
+ test("ast-analyzer: unchanged exports produce no changes", async () => {
56
+ const code = `export function stable(x: number): number { return x * 2; }
57
+ `;
58
+ await withFile(code, code, (result) => {
59
+ assert.equal(result.changes.length, 0);
60
+ assert.equal(result.hasCritical, false);
61
+ });
62
+ });
63
+ test("ast-analyzer: flags newly introduced any annotations", async () => {
64
+ const before = `export function parse(input: string): object { return JSON.parse(input); }
65
+ `;
66
+ const after = `export function parse(input: any): any { return JSON.parse(input); }
67
+ `;
68
+ await withFile(after, before, (result) => {
69
+ const loosened = result.changes.find((c) => c.kind === "type-loosened-to-any");
70
+ assert.ok(loosened);
71
+ assert.equal(loosened.severity, "warning");
72
+ });
73
+ });
74
+ test("ast-analyzer: skips non-TS files", async () => {
75
+ const dir = await mkdtemp(join(tmpdir(), "greenbump-ast-"));
76
+ await writeFile(join(dir, "style.css"), "body { color: red }", "utf8");
77
+ try {
78
+ const result = await analyzeApiChanges(dir, ["style.css"], async () => "body { color: blue }");
79
+ assert.equal(result.changes.length, 0);
80
+ }
81
+ finally {
82
+ await rm(dir, { recursive: true, force: true });
83
+ }
84
+ });
85
+ test("ast-analyzer: new file (no before) is skipped silently", async () => {
86
+ const dir = await mkdtemp(join(tmpdir(), "greenbump-ast-"));
87
+ await writeFile(join(dir, "api.ts"), "export const x = 1;", "utf8");
88
+ try {
89
+ const result = await analyzeApiChanges(dir, ["api.ts"], async () => null);
90
+ assert.equal(result.changes.length, 0);
91
+ }
92
+ finally {
93
+ await rm(dir, { recursive: true, force: true });
94
+ }
95
+ });
96
+ //# sourceMappingURL=ast-analyzer.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast-analyzer.test.js","sourceRoot":"","sources":["../../../src/engine/verifiers/ast-analyzer.test.ts"],"names":[],"mappings":"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"}