bumpcheck 0.0.0-stage → 0.1.1

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.
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Licensed under the Apache License, Version 2.0 (the "License");
6
+ you may not use this file except in compliance with the License.
7
+ You may obtain a copy of the License at
8
+
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS,
13
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ See the License for the specific language governing permissions and
15
+ limitations under the License.
package/README.md CHANGED
@@ -1,3 +1,200 @@
1
- # Temporary Holding Version
1
+ # bumpcheck
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Prove whether a dependency bump breaks the code that imports it.
4
+
5
+ Dependabot or Renovate opens a pull request bumping `zod` from 3 to 4. bumpcheck
6
+ reads the type declarations of both versions and your source, then reports
7
+ three kinds of facts, each with a file and line:
8
+
9
+ - a symbol you import was removed,
10
+ - a call you make no longer typechecks against the new declarations,
11
+ - a symbol you import changed, and whether your uses still typecheck.
12
+
13
+ Anything it cannot prove is listed as "could not be checked". It never guesses
14
+ and it never says "safe".
15
+
16
+ ```
17
+ $ bumpcheck zod 3.23.8 4.1.12 --repo ./my-app
18
+
19
+ zod 3.23.8 -> 4.1.12
20
+ You import 3 symbols from zod. 2 breaks in 2 symbols, 1 changed but still typechecks. Behavior not checked.
21
+
22
+ BREAKS
23
+ ZodError Property 'errors' does not exist on type 'ZodError<unknown>'.
24
+ src/schema.ts:15:25 (TS2339)
25
+ ZodTypeAny Property 'typeName' does not exist on type '$ZodTypeDef'.
26
+ src/schema.ts:21:22 (TS2339)
27
+ ```
28
+
29
+ In the common case the whole output is one line:
30
+
31
+ ```
32
+ You import 5 symbols from axios. None changed. Behavior not checked.
33
+ ```
34
+
35
+ ## Install
36
+
37
+ ```
38
+ npm install -g bumpcheck
39
+ ```
40
+
41
+ Requires Node 20 or newer and `npm` on your PATH. bumpcheck fetches both
42
+ versions of the bumped package into its own cache. Run it with the repo's
43
+ other dependencies installed: types from them, such as a tRPC wrapper around
44
+ react-query, are part of what can break. Without `node_modules` the report
45
+ says how many imports could not be resolved.
46
+
47
+ Each run analyzes one project root under that directory's `tsconfig.json`.
48
+ `--since` picks the right directory for each bumped `package.json`. Nested
49
+ packages that share a `package.json` but have their own `tsconfig.json` are
50
+ not yet discovered. A `tsconfig.json` that only lists `references`, as Vite
51
+ templates do, is followed: the referenced projects are analyzed as one program
52
+ with the first one's options, and the report says so.
53
+
54
+ ## Use
55
+
56
+ ```
57
+ bumpcheck <package> <from> <to> [--repo <dir>] [--format text|markdown|json]
58
+ ```
59
+
60
+ Exit code is 1 when at least one break is found, 0 otherwise, 2 on error.
61
+ Pass `--no-fail` to always exit 0.
62
+
63
+ `--format markdown` produces a pull request comment. `--format json` emits the
64
+ full report, documented in [src/types.ts](src/types.ts), for other tools.
65
+
66
+ ## In a pull request
67
+
68
+ `bumpcheck --since <ref>` finds every direct dependency whose version changed
69
+ between a git ref and HEAD, in any `package.json` in the repo, and checks each
70
+ one in its own directory. That makes the CI step one line, and it works for
71
+ repos with more than one project, such as an API at the root and a web app in
72
+ `web/`.
73
+
74
+ ```yaml
75
+ name: bumpcheck
76
+ on:
77
+ pull_request:
78
+ paths: ['**/package.json', '**/package-lock.json']
79
+ permissions:
80
+ contents: read
81
+ pull-requests: write
82
+ jobs:
83
+ check:
84
+ runs-on: ubuntu-latest
85
+ steps:
86
+ - uses: actions/checkout@v4
87
+ with: { fetch-depth: 0 }
88
+ - uses: actions/setup-node@v4
89
+ with: { node-version: 22, cache: npm }
90
+ - run: npm ci --ignore-scripts
91
+ - name: bumpcheck
92
+ id: run
93
+ run: |
94
+ npx bumpcheck --since origin/${{ github.base_ref }} --format markdown --json-out report.json > comment.md \
95
+ || echo "failed=1" >> "$GITHUB_OUTPUT"
96
+ unknown=$(node -p 'const r=[].concat(require("./report.json")); r.reduce((n,x)=>n+x.summary.unknown+x.summary.unanalyzed,0)' 2>/dev/null || echo 0)
97
+ if [ "$unknown" != "0" ]; then
98
+ echo "::warning title=bumpcheck::$unknown import(s) could not be checked; see the pull request comment"
99
+ fi
100
+ - uses: marocchino/sticky-pull-request-comment@v2
101
+ with:
102
+ header: bumpcheck
103
+ path: comment.md
104
+ - name: Fail on breaks
105
+ if: steps.run.outputs.failed == '1'
106
+ run: exit 1
107
+ ```
108
+
109
+ The job fails when any package reported a break, after the comment is
110
+ posted. Imports that could not be checked keep the job green and add a
111
+ warning annotation, so a green check means "no proven break", not "safe".
112
+ If more than one project needs `npm ci`, add a step per directory.
113
+
114
+ ## How it works
115
+
116
+ 1. Both versions are installed with `npm install --ignore-scripts` into a
117
+ per-version cache. When the package's own dependencies are also installed
118
+ in your repo at a version that satisfies its declared range, those copies
119
+ are used, as npm's dedupe would. Otherwise two copies of a shared type
120
+ such as `@nestjs/common` would disagree with each other.
121
+ 2. Two TypeScript programs are built over your repo, identical except that
122
+ every import of the package resolves to one version or the other.
123
+ 3. Every import of the package is collected: named, default, namespace
124
+ member access, `require()` destructuring, subpaths, and type-only imports.
125
+ 4. Each imported symbol's declaration is tokenized and compared between
126
+ versions. Comments and formatting do not count.
127
+ 5. Type errors are collected in the importing files for both programs. Errors
128
+ present only with the new version are the breaks. Your pre-existing errors
129
+ cancel out.
130
+ 6. Each break is attributed to an imported symbol by the error message, the
131
+ import binding in the failing expression, or a type declared under that
132
+ import. The report says which.
133
+
134
+ If your repo has a `tsconfig.json`, its options are used, so a break is a
135
+ break under your own settings. Without one, bumpcheck uses strict defaults with
136
+ `checkJs` so plain JavaScript repos are analyzed too.
137
+
138
+ ## What it does not do
139
+
140
+ - It does not check runtime behavior. A function with the same signature can
141
+ still behave differently. The footer of every report says so.
142
+ - It does not analyze packages that ship no type declarations. The report says
143
+ so instead of reporting zero breaks.
144
+ - It does not follow dynamic `import()`, `export * from`, or a namespace
145
+ passed around as a value. Those appear under "not analyzed".
146
+ - It does not fix anything, read changelogs, or decide whether to merge.
147
+
148
+ ## Accuracy
149
+
150
+ [corpus/](corpus/) pins real Dependabot pull requests from public repos at
151
+ their base commit and runs bumpcheck on each bump. A `--oracle` mode then
152
+ installs the new version into the checkout and runs the repo's own `tsc`; the
153
+ new errors it reports are the ground truth.
154
+
155
+ Results so far, from [corpus/results.md](corpus/results.md). Four of the
156
+ pull requests needed a human to change code before merging, which tests the
157
+ case that matters: does the tool stay quiet when it should not?
158
+
159
+ | bump | bumpcheck | tsc oracle | what the human changed |
160
+ |---|---|---|---|
161
+ | zod 3.23.8 → 4.6.5, 36-file API merged without CI | 79 breaks | 84 new errors | nothing; the repo has not compiled since |
162
+ | zod 3.25.76 → 4.6.7, MCP server | 6 breaks | 6 new errors | the schema the 6 test errors pointed at |
163
+ | react-query 4 → 5, 20-hook app | 20 breaks | 28 new errors | 16 hunks, 13 of them at reported lines |
164
+ | react-query 4 → 5, Electron app | 3 breaks | 3 new errors | 1 of the 3 sites; the other 2 landed via a separate PR |
165
+ | msw 1 → 2, tests outside tsconfig | 0, with a note that 1 importing file is outside tsconfig | 0 | the test files tsc does not see |
166
+ | 6 bumps that needed no code changes | 0 breaks each | 0 new errors each | nothing |
167
+
168
+ Every gap between the two columns is TypeScript's "implicitly has an 'any'
169
+ type" follow-on errors, which bumpcheck folds into the root error on the
170
+ same statement. On root causes the lists match, file and line.
171
+
172
+ Two lessons from the corpus are now part of the tool: run with the repo's
173
+ dependencies installed, because types from other packages carry the bumped
174
+ package's types, and say so when files that import the package sit outside
175
+ the tsconfig.
176
+
177
+ Eleven cases is a start, not a statistic. Add one with a pull request URL and
178
+ a base commit in [corpus/cases.json](corpus/cases.json) and run
179
+ `npx tsx corpus/run.ts --oracle`.
180
+
181
+ ## Status
182
+
183
+ Early. npm with TypeScript declarations is the only ecosystem. The report
184
+ schema is versioned and will stay backwards compatible within a major.
185
+
186
+ ## Development
187
+
188
+ ```
189
+ npm install
190
+ npm test
191
+ npm run dev -- zod 3.23.8 4.1.12 --repo ../some-app
192
+ ```
193
+
194
+ Tests run offline against fixture packages in `test/fixtures`. Add a case by
195
+ planting a change in `demo-lib-v2` and a use in `consumer`, then asserting the
196
+ exact file and line.
197
+
198
+ ## License
199
+
200
+ Apache-2.0
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs } from 'node:util';
3
+ import { writeFile } from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import { analyzeNpm } from './npm/analyze.js';
6
+ import { detectChanges } from './git.js';
7
+ import { NpmProvider } from './registry/provider.js';
8
+ import { renderMarkdown, renderText } from './report/render.js';
9
+ const HELP = `bumpcheck — prove whether a dependency bump breaks the code that imports it
10
+
11
+ Usage
12
+ bumpcheck <package> <from> <to> [options]
13
+ bumpcheck --since <git-ref> [options]
14
+
15
+ The second form finds every direct dependency whose version changed in any
16
+ package.json between <git-ref> and HEAD, and checks each in its own
17
+ directory. This is the form for CI on a dependency pull request.
18
+
19
+ Options
20
+ --repo <dir> Repository to analyze (default: current directory)
21
+ --since <ref> Detect bumps from git instead of naming one
22
+ --format <fmt> text | markdown | json (default: text)
23
+ --json-out <file> Also write the full JSON report to a file
24
+ --cache-dir <dir> Where package versions are cached
25
+ --registry <url> npm registry (default: npm config / BUMPCHECK_REGISTRY)
26
+ --no-fail Exit 0 even when breaks are found
27
+ -h, --help Show this help
28
+
29
+ Exit codes
30
+ 0 no breaks found
31
+ 1 at least one break found
32
+ 2 usage or analysis error
33
+
34
+ Example
35
+ bumpcheck axios 1.6.8 1.7.2 --repo ./my-app --format markdown
36
+ `;
37
+ async function main(argv) {
38
+ const { values, positionals } = parseArgs({
39
+ args: argv,
40
+ allowPositionals: true,
41
+ options: {
42
+ repo: { type: 'string' },
43
+ since: { type: 'string' },
44
+ format: { type: 'string', default: 'text' },
45
+ 'json-out': { type: 'string' },
46
+ 'cache-dir': { type: 'string' },
47
+ registry: { type: 'string' },
48
+ 'no-fail': { type: 'boolean', default: false },
49
+ help: { type: 'boolean', short: 'h', default: false },
50
+ },
51
+ });
52
+ if (values.help || (values.since ? positionals.length !== 0 : positionals.length !== 3)) {
53
+ process.stdout.write(HELP);
54
+ return values.help ? 0 : 2;
55
+ }
56
+ const format = values.format;
57
+ if (format !== 'text' && format !== 'markdown' && format !== 'json') {
58
+ process.stderr.write(`Unknown format "${format}". Use text, markdown, or json.\n`);
59
+ return 2;
60
+ }
61
+ const providerOpts = {};
62
+ if (values['cache-dir'])
63
+ providerOpts.cacheDir = values['cache-dir'];
64
+ if (values.registry)
65
+ providerOpts.registry = values.registry;
66
+ const repoRoot = path.resolve(values.repo ?? process.cwd());
67
+ const provider = new NpmProvider(providerOpts);
68
+ const detection = values.since
69
+ ? detectChanges(repoRoot, values.since)
70
+ : { bumps: [{ dir: '', pkg: positionals[0], from: positionals[1], to: positionals[2] }], transitive: [] };
71
+ const targets = detection.bumps;
72
+ if (targets.length === 0) {
73
+ process.stdout.write(renderNoBumps(values.since, detection, format) + '\n');
74
+ if (values['json-out'])
75
+ await writeFile(values['json-out'], JSON.stringify([], null, 2) + '\n');
76
+ return 0;
77
+ }
78
+ const reports = [];
79
+ for (const t of targets) {
80
+ reports.push(await analyzeNpm({ repoRoot: path.join(repoRoot, t.dir), pkg: t.pkg, from: t.from, to: t.to, provider }));
81
+ }
82
+ const render = (r, i) => {
83
+ const dir = targets[i].dir;
84
+ const prefix = dir && format !== 'json' ? (format === 'markdown' ? `_in \`${dir}/\`_\n\n` : `in ${dir}/\n`) : '';
85
+ return prefix + (format === 'markdown' ? renderMarkdown(r) : renderText(r));
86
+ };
87
+ const body = format === 'json' ? JSON.stringify(reports.length === 1 ? reports[0] : reports, null, 2) : reports.map(render).join('\n\n');
88
+ process.stdout.write(body + '\n');
89
+ if (values['json-out']) {
90
+ await writeFile(values['json-out'], JSON.stringify(reports.length === 1 ? reports[0] : reports, null, 2) + '\n');
91
+ }
92
+ return reports.some((r) => r.breaks.length > 0) && !values['no-fail'] ? 1 : 0;
93
+ }
94
+ /**
95
+ * A lockfile-only pull request has nothing bumpcheck can check, since it
96
+ * only analyzes packages the repo imports directly. Say what moved anyway,
97
+ * so the comment explains itself.
98
+ */
99
+ function renderNoBumps(since, detection, format) {
100
+ const md = format === 'markdown';
101
+ const lines = [];
102
+ lines.push(md ? `### bumpcheck` : 'bumpcheck');
103
+ lines.push('');
104
+ lines.push(`No direct dependency changed since ${since}, so there is nothing to typecheck against.`);
105
+ if (detection.transitive.length > 0) {
106
+ const n = detection.transitive.length;
107
+ lines.push('');
108
+ lines.push(`${n} transitive package${n === 1 ? '' : 's'} moved in the lockfile:`);
109
+ lines.push('');
110
+ for (const t of detection.transitive.slice(0, 20)) {
111
+ const where = t.dir ? ` (in ${t.dir}/)` : '';
112
+ lines.push(md ? `- \`${t.pkg}\` ${t.from} → ${t.to}${where}` : ` ${t.pkg} ${t.from} -> ${t.to}${where}`);
113
+ }
114
+ if (n > 20)
115
+ lines.push(md ? `- … ${n - 20} more` : ` … ${n - 20} more`);
116
+ lines.push('');
117
+ lines.push(md
118
+ ? '<sub>bumpcheck checks packages this repo imports directly. Transitive changes are listed for the record and are not analyzed.</sub>'
119
+ : 'bumpcheck checks packages this repo imports directly. Transitive changes are listed for the record and are not analyzed.');
120
+ }
121
+ return lines.join('\n');
122
+ }
123
+ main(process.argv.slice(2)).then((code) => process.exit(code), (err) => {
124
+ process.stderr.write(`bumpcheck: ${err instanceof Error ? err.message : String(err)}\n`);
125
+ process.exit(2);
126
+ });
127
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/dist/git.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ /** A direct dependency whose pinned version changed between two git refs. */
2
+ export interface DetectedBump {
3
+ /** Directory of the package.json, relative to the repo root, '' for the root. */
4
+ dir: string;
5
+ pkg: string;
6
+ from: string;
7
+ to: string;
8
+ }
9
+ /** A lockfile entry that moved without being a direct dependency. */
10
+ export interface TransitiveChange {
11
+ dir: string;
12
+ pkg: string;
13
+ from: string;
14
+ to: string;
15
+ }
16
+ export interface Detection {
17
+ bumps: DetectedBump[];
18
+ transitive: TransitiveChange[];
19
+ }
20
+ /** `^1.2.3`, `~1.2.3`, `1.2.3` -> `1.2.3`; ranges and tags -> undefined. */
21
+ export declare function exactVersion(spec: string | undefined): string | undefined;
22
+ /**
23
+ * Finds direct dependencies whose version changed between `base` and `head`
24
+ * in any package.json, using the lockfile beside it when present so range
25
+ * specs like `^1.2.0` resolve to the versions actually installed.
26
+ */
27
+ export declare function detectBumps(repoRoot: string, base: string, head?: string): DetectedBump[];
28
+ /**
29
+ * Direct bumps plus transitive lockfile moves. bumpcheck can only analyze
30
+ * what the repo imports directly, so the transitive list exists to explain
31
+ * a lockfile-only pull request rather than to be checked.
32
+ */
33
+ export declare function detectChanges(repoRoot: string, base: string, head?: string): Detection;
package/dist/git.js ADDED
@@ -0,0 +1,94 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import path from 'node:path';
3
+ const DEP_FIELDS = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'];
4
+ function git(repo, args) {
5
+ const r = spawnSync('git', args, { cwd: repo, encoding: 'utf8' });
6
+ return r.status === 0 ? r.stdout : undefined;
7
+ }
8
+ function readJson(text) {
9
+ if (!text)
10
+ return undefined;
11
+ try {
12
+ return JSON.parse(text);
13
+ }
14
+ catch {
15
+ return undefined;
16
+ }
17
+ }
18
+ /** `^1.2.3`, `~1.2.3`, `1.2.3` -> `1.2.3`; ranges and tags -> undefined. */
19
+ export function exactVersion(spec) {
20
+ if (!spec)
21
+ return undefined;
22
+ const m = /^[\^~]?v?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)$/.exec(spec.trim());
23
+ return m ? m[1] : undefined;
24
+ }
25
+ /**
26
+ * Finds direct dependencies whose version changed between `base` and `head`
27
+ * in any package.json, using the lockfile beside it when present so range
28
+ * specs like `^1.2.0` resolve to the versions actually installed.
29
+ */
30
+ export function detectBumps(repoRoot, base, head = 'HEAD') {
31
+ return detectChanges(repoRoot, base, head).bumps;
32
+ }
33
+ /**
34
+ * Direct bumps plus transitive lockfile moves. bumpcheck can only analyze
35
+ * what the repo imports directly, so the transitive list exists to explain
36
+ * a lockfile-only pull request rather than to be checked.
37
+ */
38
+ export function detectChanges(repoRoot, base, head = 'HEAD') {
39
+ const changed = git(repoRoot, ['diff', '--name-only', base, head, '--', '*package.json', '*package-lock.json'])
40
+ ?.split('\n')
41
+ .map((l) => l.trim())
42
+ .filter((l) => l && !l.includes('node_modules/'));
43
+ if (!changed)
44
+ throw new Error(`git diff ${base} ${head} failed; is ${base} fetched?`);
45
+ const dirs = new Set(changed.map((f) => path.posix.dirname(f)).map((d) => (d === '.' ? '' : d)));
46
+ const bumps = [];
47
+ const transitive = [];
48
+ for (const dir of [...dirs].sort()) {
49
+ const manifestPath = dir ? `${dir}/package.json` : 'package.json';
50
+ const lockPath = dir ? `${dir}/package-lock.json` : 'package-lock.json';
51
+ const before = readJson(git(repoRoot, ['show', `${base}:${manifestPath}`]));
52
+ const after = readJson(git(repoRoot, ['show', `${head}:${manifestPath}`]));
53
+ if (!before || !after)
54
+ continue;
55
+ const lockBefore = readJson(git(repoRoot, ['show', `${base}:${lockPath}`]));
56
+ const lockAfter = readJson(git(repoRoot, ['show', `${head}:${lockPath}`]));
57
+ const resolve = (name, spec, lock) => lock?.packages?.[`node_modules/${name}`]?.version ?? exactVersion(spec);
58
+ const names = new Set();
59
+ for (const field of DEP_FIELDS) {
60
+ for (const n of Object.keys(before[field] ?? {}))
61
+ names.add(n);
62
+ for (const n of Object.keys(after[field] ?? {}))
63
+ names.add(n);
64
+ }
65
+ for (const name of [...names].sort()) {
66
+ const specBefore = DEP_FIELDS.map((f) => before[f]?.[name]).find(Boolean);
67
+ const specAfter = DEP_FIELDS.map((f) => after[f]?.[name]).find(Boolean);
68
+ if (!specBefore || !specAfter)
69
+ continue; // added or removed, not bumped
70
+ const from = resolve(name, specBefore, lockBefore);
71
+ const to = resolve(name, specAfter, lockAfter);
72
+ if (!from || !to || from === to)
73
+ continue;
74
+ bumps.push({ dir, pkg: name, from, to });
75
+ }
76
+ // Top-level lockfile entries that moved but are not direct dependencies.
77
+ const direct = new Set(names);
78
+ const pkgsBefore = lockBefore?.packages ?? {};
79
+ const pkgsAfter = lockAfter?.packages ?? {};
80
+ for (const key of Object.keys(pkgsAfter).sort()) {
81
+ if (!key.startsWith('node_modules/') || key.indexOf('node_modules/', 'node_modules/'.length) !== -1)
82
+ continue;
83
+ const name = key.slice('node_modules/'.length);
84
+ if (direct.has(name))
85
+ continue;
86
+ const from = pkgsBefore[key]?.version;
87
+ const to = pkgsAfter[key]?.version;
88
+ if (from && to && from !== to)
89
+ transitive.push({ dir, pkg: name, from, to });
90
+ }
91
+ }
92
+ return { bumps, transitive };
93
+ }
94
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { analyzeNpm, type AnalyzeOptions } from './npm/analyze.js';
2
+ export { NpmProvider, LocalProvider, type PackageProvider, type NpmProviderOptions } from './registry/provider.js';
3
+ export { renderText, renderMarkdown } from './report/render.js';
4
+ export * from './types.js';
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { analyzeNpm } from './npm/analyze.js';
2
+ export { NpmProvider, LocalProvider } from './registry/provider.js';
3
+ export { renderText, renderMarkdown } from './report/render.js';
4
+ export * from './types.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAuB,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAiD,MAAM,wBAAwB,CAAC;AACnH,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAChE,cAAc,YAAY,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { Report } from '../types.js';
2
+ import type { PackageProvider } from '../registry/provider.js';
3
+ export interface AnalyzeOptions {
4
+ repoRoot: string;
5
+ pkg: string;
6
+ from: string;
7
+ to: string;
8
+ provider: PackageProvider;
9
+ }
10
+ /**
11
+ * The whole pipeline:
12
+ * 1. materialize both versions,
13
+ * 2. build one program per version with imports redirected,
14
+ * 3. collect what the repo imports,
15
+ * 4. compare each imported symbol's declaration,
16
+ * 5. diff the type errors; new-only errors are the breaks.
17
+ */
18
+ export declare function analyzeNpm(opts: AnalyzeOptions): Promise<Report>;