@shapeshift-labs/frontier-merge-metrics 0.1.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.
- package/LICENSE +21 -0
- package/README.md +112 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +96 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +150 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +496 -0
- package/dist/index.js.map +1 -0
- package/dist/node.d.ts +20 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +187 -0
- package/dist/node.js.map +1 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shapeshift Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# @shapeshift-labs/frontier-merge-metrics
|
|
2
|
+
|
|
3
|
+
Repo-shape and merge-conflict risk metrics for Git, Loom, and Frontier swarm workflows.
|
|
4
|
+
|
|
5
|
+
`frontier-merge-metrics` measures the project shapes that create merge and review bottlenecks before semantic merge has to rescue them. It can run as a standalone CLI over a Git checkout, or as a library over commit/change records produced by Loom, Frontier Swarm, CI, or another repository analytics pipeline.
|
|
6
|
+
|
|
7
|
+
- npm: [`@shapeshift-labs/frontier-merge-metrics`](https://www.npmjs.com/package/@shapeshift-labs/frontier-merge-metrics)
|
|
8
|
+
- source: [`siliconjungle/-shapeshift-labs-frontier-merge-metrics`](https://github.com/siliconjungle/-shapeshift-labs-frontier-merge-metrics)
|
|
9
|
+
- license: MIT
|
|
10
|
+
|
|
11
|
+
## What It Measures
|
|
12
|
+
|
|
13
|
+
The package turns Git history into a source-free evidence object:
|
|
14
|
+
|
|
15
|
+
- hot files and hot directories
|
|
16
|
+
- same-file counterfactual collision risk between nearby commits
|
|
17
|
+
- observed merge-resolution signals from merge commits
|
|
18
|
+
- central file collisions for route tables, registries, config, barrels, and schemas
|
|
19
|
+
- generated, lockfile, fixture, and snapshot churn
|
|
20
|
+
- global CSS versus scoped CSS/module surfaces
|
|
21
|
+
- co-change edges that reveal hidden coupling
|
|
22
|
+
- recommended repo-shape interventions
|
|
23
|
+
|
|
24
|
+
The goal is not to replace semantic merge. The goal is to reduce how often semantic merge is needed by making project architecture friendlier to parallel work.
|
|
25
|
+
|
|
26
|
+
## CLI
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
npx @shapeshift-labs/frontier-merge-metrics \
|
|
30
|
+
--repo . \
|
|
31
|
+
--max-commits 200 \
|
|
32
|
+
--max-file-snapshots 500 \
|
|
33
|
+
--pair-window 20 \
|
|
34
|
+
--out reports/merge-metrics.json \
|
|
35
|
+
--report reports/merge-metrics.md
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Analyze a corpus:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
frontier-merge-metrics \
|
|
42
|
+
--repo /tmp/react \
|
|
43
|
+
--repo /tmp/vite \
|
|
44
|
+
--repo /tmp/prettier \
|
|
45
|
+
--out reports/corpus.json \
|
|
46
|
+
--report reports/corpus.md
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Library API
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { analyzeRepoShape } from '@shapeshift-labs/frontier-merge-metrics';
|
|
53
|
+
|
|
54
|
+
const report = analyzeRepoShape({
|
|
55
|
+
repository: 'example',
|
|
56
|
+
commits: [
|
|
57
|
+
{
|
|
58
|
+
hash: 'a1',
|
|
59
|
+
files: [{ path: 'src/routes.ts', additions: 3, deletions: 1 }]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
hash: 'b2',
|
|
63
|
+
files: [{ path: 'src/routes.ts', additions: 2, deletions: 0 }]
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Node Git collector:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { analyzeGitRepository } from '@shapeshift-labs/frontier-merge-metrics/node';
|
|
73
|
+
|
|
74
|
+
const report = analyzeGitRepository('/path/to/repo', {
|
|
75
|
+
maxCommits: 300,
|
|
76
|
+
maxFileSnapshots: 500,
|
|
77
|
+
pairWindow: 25
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
For queue-time scans, keep `maxCommits` and `maxFileSnapshots` small. For scheduled repo-shape reports, increase them and keep the generated JSON evidence.
|
|
82
|
+
|
|
83
|
+
## Report Semantics
|
|
84
|
+
|
|
85
|
+
`counterfactualCollisionRate` asks: if nearby commits had been developed as parallel branches, how often would they have touched at least one same file?
|
|
86
|
+
|
|
87
|
+
`centralCollisionRate` narrows that to likely coordination bottlenecks such as `routes.ts`, `router.ts`, `registry.ts`, `schema.ts`, `config.ts`, and `index.ts`.
|
|
88
|
+
|
|
89
|
+
`mergeResolutionEventRate` is different. It is based on observed merge commits where the merge result differs from every parent on the same path. This is evidence of integration pressure, but not proof that a platform PR conflict happened.
|
|
90
|
+
|
|
91
|
+
`parallelismScore` is a heuristic 0-100 score. Higher means the observed history is more likely to support independent work. It is intentionally conservative: it highlights risks to investigate, not proof that a repo will or will not merge automatically.
|
|
92
|
+
|
|
93
|
+
## Loom And Swarm Integration
|
|
94
|
+
|
|
95
|
+
Loom and Frontier Swarm can feed this package directly from run logs, queue manifests, changed-path indexes, semantic regions, or Git workspaces:
|
|
96
|
+
|
|
97
|
+
- before a swarm run, use repo-shape metrics to find hot ownership lanes
|
|
98
|
+
- during a run, compare actual collisions against predicted hot spots
|
|
99
|
+
- after a run, use interventions to split central files, scope CSS, shrink fixtures, or move generated outputs out of review
|
|
100
|
+
- attach run, job, lane, and region metadata in your own input records when correlating reports with swarm outcomes
|
|
101
|
+
|
|
102
|
+
The root API is structural and does not require Git. The Node subpath and CLI are convenience collectors for ordinary repositories.
|
|
103
|
+
|
|
104
|
+
## Evidence
|
|
105
|
+
|
|
106
|
+
This repo includes a bounded public-repository evidence pass under `evidence/`:
|
|
107
|
+
|
|
108
|
+
- `public-js-ts-corpus-2026-06-29.json`
|
|
109
|
+
- `public-js-ts-corpus-2026-06-29.md`
|
|
110
|
+
- `public-js-ts-trends-2026-06-29.md`
|
|
111
|
+
|
|
112
|
+
Those reports sample recent history from Astro, Hono, Prettier, Svelte, and Vite to show the metrics on real projects.
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { analyzeGitRepository, analyzeGitRepositoryCorpus, writeMergeMetricReport } from './node.js';
|
|
3
|
+
const options = parseArgs(process.argv.slice(2));
|
|
4
|
+
if (options.help) {
|
|
5
|
+
printHelp();
|
|
6
|
+
process.exit(0);
|
|
7
|
+
}
|
|
8
|
+
const repos = options.repos.length ? options.repos : [process.cwd()];
|
|
9
|
+
const result = repos.length === 1
|
|
10
|
+
? analyzeGitRepository(repos[0], options)
|
|
11
|
+
: analyzeGitRepositoryCorpus(repos, options);
|
|
12
|
+
writeMergeMetricReport(result, {
|
|
13
|
+
jsonPath: options.out,
|
|
14
|
+
markdownPath: options.report
|
|
15
|
+
});
|
|
16
|
+
if (!options.out && !options.report) {
|
|
17
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
const summary = result.kind === 'frontier.mergeMetrics.corpusReport'
|
|
21
|
+
? `analyzed ${result.summary.repositoryCount} repositories`
|
|
22
|
+
: `analyzed ${result.repository}`;
|
|
23
|
+
process.stderr.write(`frontier-merge-metrics: ${summary}\n`);
|
|
24
|
+
}
|
|
25
|
+
function parseArgs(args) {
|
|
26
|
+
const options = { repos: [] };
|
|
27
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
28
|
+
const arg = args[index];
|
|
29
|
+
if (arg === '--help' || arg === '-h')
|
|
30
|
+
options.help = true;
|
|
31
|
+
else if (arg === '--repo')
|
|
32
|
+
options.repos.push(requiredValue(args, ++index, arg));
|
|
33
|
+
else if (arg === '--out')
|
|
34
|
+
options.out = requiredValue(args, ++index, arg);
|
|
35
|
+
else if (arg === '--report')
|
|
36
|
+
options.report = requiredValue(args, ++index, arg);
|
|
37
|
+
else if (arg === '--max-commits')
|
|
38
|
+
options.maxCommits = parsePositiveInt(requiredValue(args, ++index, arg), arg);
|
|
39
|
+
else if (arg === '--max-file-snapshots')
|
|
40
|
+
options.maxFileSnapshots = parsePositiveInt(requiredValue(args, ++index, arg), arg);
|
|
41
|
+
else if (arg === '--max-file-bytes')
|
|
42
|
+
options.maxFileBytes = parsePositiveInt(requiredValue(args, ++index, arg), arg);
|
|
43
|
+
else if (arg === '--max-merge-resolution-events')
|
|
44
|
+
options.maxMergeResolutionEvents = parsePositiveInt(requiredValue(args, ++index, arg), arg);
|
|
45
|
+
else if (arg === '--pair-window')
|
|
46
|
+
options.pairWindow = parsePositiveInt(requiredValue(args, ++index, arg), arg);
|
|
47
|
+
else if (arg === '--top-files')
|
|
48
|
+
options.topFiles = parsePositiveInt(requiredValue(args, ++index, arg), arg);
|
|
49
|
+
else if (arg === '--path')
|
|
50
|
+
options.paths = [...(options.paths ?? []), requiredValue(args, ++index, arg)];
|
|
51
|
+
else if (arg.startsWith('--repo='))
|
|
52
|
+
options.repos.push(arg.slice('--repo='.length));
|
|
53
|
+
else if (arg.startsWith('--out='))
|
|
54
|
+
options.out = arg.slice('--out='.length);
|
|
55
|
+
else if (arg.startsWith('--report='))
|
|
56
|
+
options.report = arg.slice('--report='.length);
|
|
57
|
+
else
|
|
58
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
59
|
+
}
|
|
60
|
+
return options;
|
|
61
|
+
}
|
|
62
|
+
function requiredValue(args, index, option) {
|
|
63
|
+
const value = args[index];
|
|
64
|
+
if (!value)
|
|
65
|
+
throw new Error(`${option} requires a value`);
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
function parsePositiveInt(value, option) {
|
|
69
|
+
const parsed = Number.parseInt(value, 10);
|
|
70
|
+
if (!Number.isInteger(parsed) || parsed <= 0)
|
|
71
|
+
throw new Error(`${option} requires a positive integer`);
|
|
72
|
+
return parsed;
|
|
73
|
+
}
|
|
74
|
+
function printHelp() {
|
|
75
|
+
process.stdout.write(`frontier-merge-metrics analyze git repository shape and conflict risk
|
|
76
|
+
|
|
77
|
+
Usage:
|
|
78
|
+
frontier-merge-metrics [--repo <path>] [--repo <path>] [options]
|
|
79
|
+
|
|
80
|
+
Options:
|
|
81
|
+
--repo <path> Git repository to analyze. Repeat for a corpus.
|
|
82
|
+
--out <path> Write JSON report.
|
|
83
|
+
--report <path> Write Markdown report.
|
|
84
|
+
--max-commits <n> Commit sample size. Default: 200.
|
|
85
|
+
--max-file-snapshots <n>
|
|
86
|
+
Tracked file snapshot cap per repository. Default: 2000.
|
|
87
|
+
--max-file-bytes <n> Tracked file read cap for line counts. Default: 256000.
|
|
88
|
+
--max-merge-resolution-events <n>
|
|
89
|
+
Merge-commit resolution signal cap. Default: 100.
|
|
90
|
+
--pair-window <n> Nearby commit window for counterfactual pair checks. Default: 20.
|
|
91
|
+
--top-files <n> Hot file rows to keep. Default: 20.
|
|
92
|
+
--path <pathspec> Restrict git log collection to pathspec. Repeatable.
|
|
93
|
+
--help Show this help.
|
|
94
|
+
`);
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
export type FrontierMergeMetricPathKind = 'source' | 'test' | 'style' | 'global-css' | 'scoped-css' | 'html' | 'config' | 'central' | 'generated' | 'fixture' | 'lockfile' | 'docs' | 'other';
|
|
2
|
+
export interface FrontierMergeMetricFileChange {
|
|
3
|
+
readonly path: string;
|
|
4
|
+
readonly additions?: number;
|
|
5
|
+
readonly deletions?: number;
|
|
6
|
+
readonly status?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface FrontierMergeMetricCommit {
|
|
9
|
+
readonly hash: string;
|
|
10
|
+
readonly parents?: readonly string[];
|
|
11
|
+
readonly authorDate?: string;
|
|
12
|
+
readonly subject?: string;
|
|
13
|
+
readonly files: readonly FrontierMergeMetricFileChange[];
|
|
14
|
+
}
|
|
15
|
+
export interface FrontierMergeMetricFileSnapshot {
|
|
16
|
+
readonly path: string;
|
|
17
|
+
readonly bytes?: number;
|
|
18
|
+
readonly lines?: number;
|
|
19
|
+
}
|
|
20
|
+
export interface FrontierMergeMetricResolutionEvent {
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly kind: 'git-merge-parent-intersection' | 'platform-pr-conflict' | 'manual';
|
|
23
|
+
readonly commitHash?: string;
|
|
24
|
+
readonly parents?: readonly string[];
|
|
25
|
+
readonly authorDate?: string;
|
|
26
|
+
readonly subject?: string;
|
|
27
|
+
readonly pullRequest?: {
|
|
28
|
+
readonly provider: string;
|
|
29
|
+
readonly number?: number;
|
|
30
|
+
readonly url?: string;
|
|
31
|
+
};
|
|
32
|
+
readonly paths: readonly string[];
|
|
33
|
+
readonly evidence?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface FrontierMergeMetricInput {
|
|
36
|
+
readonly repository?: string;
|
|
37
|
+
readonly head?: string;
|
|
38
|
+
readonly commits: readonly FrontierMergeMetricCommit[];
|
|
39
|
+
readonly files?: readonly FrontierMergeMetricFileSnapshot[];
|
|
40
|
+
readonly resolutionEvents?: readonly FrontierMergeMetricResolutionEvent[];
|
|
41
|
+
}
|
|
42
|
+
export interface FrontierMergeMetricOptions {
|
|
43
|
+
readonly pairWindow?: number;
|
|
44
|
+
readonly topFiles?: number;
|
|
45
|
+
readonly topDirectories?: number;
|
|
46
|
+
readonly topCochanges?: number;
|
|
47
|
+
readonly generatedAt?: string;
|
|
48
|
+
}
|
|
49
|
+
export interface FrontierMergeMetricHotspot {
|
|
50
|
+
readonly path: string;
|
|
51
|
+
readonly touches: number;
|
|
52
|
+
readonly commits: number;
|
|
53
|
+
readonly additions: number;
|
|
54
|
+
readonly deletions: number;
|
|
55
|
+
readonly kinds: readonly FrontierMergeMetricPathKind[];
|
|
56
|
+
readonly riskTags: readonly string[];
|
|
57
|
+
}
|
|
58
|
+
export interface FrontierMergeMetricDirectoryHotspot {
|
|
59
|
+
readonly directory: string;
|
|
60
|
+
readonly touches: number;
|
|
61
|
+
readonly share: number;
|
|
62
|
+
}
|
|
63
|
+
export interface FrontierMergeMetricCochangeEdge {
|
|
64
|
+
readonly left: string;
|
|
65
|
+
readonly right: string;
|
|
66
|
+
readonly commits: number;
|
|
67
|
+
readonly score: number;
|
|
68
|
+
}
|
|
69
|
+
export interface FrontierMergeMetricConflictPair {
|
|
70
|
+
readonly leftCommit: string;
|
|
71
|
+
readonly rightCommit: string;
|
|
72
|
+
readonly sharedPaths: readonly string[];
|
|
73
|
+
readonly centralPaths: readonly string[];
|
|
74
|
+
readonly generatedPaths: readonly string[];
|
|
75
|
+
readonly globalStylePaths: readonly string[];
|
|
76
|
+
readonly fixturePaths: readonly string[];
|
|
77
|
+
readonly riskScore: number;
|
|
78
|
+
}
|
|
79
|
+
export interface FrontierMergeMetricScores {
|
|
80
|
+
readonly hotspotConcentration: number;
|
|
81
|
+
readonly hotspotTopShare: number;
|
|
82
|
+
readonly directoryConcentration: number;
|
|
83
|
+
readonly counterfactualCollisionRate: number;
|
|
84
|
+
readonly centralCollisionRate: number;
|
|
85
|
+
readonly mergeResolutionEventRate: number;
|
|
86
|
+
readonly mergeResolutionPathShare: number;
|
|
87
|
+
readonly mergeResolutionCentralShare: number;
|
|
88
|
+
readonly generatedChurnShare: number;
|
|
89
|
+
readonly globalCssShare: number;
|
|
90
|
+
readonly fixtureChurnShare: number;
|
|
91
|
+
readonly ownershipDispersion: number;
|
|
92
|
+
readonly parallelismScore: number;
|
|
93
|
+
}
|
|
94
|
+
export interface FrontierMergeMetricIntervention {
|
|
95
|
+
readonly id: string;
|
|
96
|
+
readonly title: string;
|
|
97
|
+
readonly reason: string;
|
|
98
|
+
readonly expectedEffect: string;
|
|
99
|
+
readonly severity: 'low' | 'medium' | 'high';
|
|
100
|
+
}
|
|
101
|
+
export interface FrontierMergeMetricReport {
|
|
102
|
+
readonly kind: 'frontier.mergeMetrics.repoShapeReport';
|
|
103
|
+
readonly version: 1;
|
|
104
|
+
readonly repository: string;
|
|
105
|
+
readonly head?: string;
|
|
106
|
+
readonly generatedAt: string;
|
|
107
|
+
readonly commitCount: number;
|
|
108
|
+
readonly changedFileCount: number;
|
|
109
|
+
readonly totalFileTouches: number;
|
|
110
|
+
readonly metrics: FrontierMergeMetricScores;
|
|
111
|
+
readonly hotspots: readonly FrontierMergeMetricHotspot[];
|
|
112
|
+
readonly directories: readonly FrontierMergeMetricDirectoryHotspot[];
|
|
113
|
+
readonly cochangeEdges: readonly FrontierMergeMetricCochangeEdge[];
|
|
114
|
+
readonly conflictPairs: readonly FrontierMergeMetricConflictPair[];
|
|
115
|
+
readonly resolutionEvents: readonly FrontierMergeMetricResolutionEvent[];
|
|
116
|
+
readonly interventions: readonly FrontierMergeMetricIntervention[];
|
|
117
|
+
readonly evidence: {
|
|
118
|
+
readonly pairWindow: number;
|
|
119
|
+
readonly pairCount: number;
|
|
120
|
+
readonly conflictPairCount: number;
|
|
121
|
+
readonly displayedConflictPairCount: number;
|
|
122
|
+
readonly resolutionEventCount: number;
|
|
123
|
+
readonly resolutionPathCount: number;
|
|
124
|
+
readonly fileSnapshotCount: number;
|
|
125
|
+
readonly classifierVersion: 1;
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
export interface FrontierMergeMetricCorpusReport {
|
|
129
|
+
readonly kind: 'frontier.mergeMetrics.corpusReport';
|
|
130
|
+
readonly version: 1;
|
|
131
|
+
readonly generatedAt: string;
|
|
132
|
+
readonly repositories: readonly FrontierMergeMetricReport[];
|
|
133
|
+
readonly summary: {
|
|
134
|
+
readonly repositoryCount: number;
|
|
135
|
+
readonly medianParallelismScore: number;
|
|
136
|
+
readonly medianCollisionRate: number;
|
|
137
|
+
readonly medianHotspotTopShare: number;
|
|
138
|
+
readonly medianCentralCollisionRate: number;
|
|
139
|
+
readonly medianMergeResolutionEventRate: number;
|
|
140
|
+
readonly topInterventions: readonly {
|
|
141
|
+
readonly id: string;
|
|
142
|
+
readonly count: number;
|
|
143
|
+
}[];
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
export declare function analyzeRepoShape(input: FrontierMergeMetricInput, options?: FrontierMergeMetricOptions): FrontierMergeMetricReport;
|
|
147
|
+
export declare function analyzeRepoShapeCorpus(reports: readonly FrontierMergeMetricReport[], generatedAt?: string): FrontierMergeMetricCorpusReport;
|
|
148
|
+
export declare function classifyPath(path: string): readonly FrontierMergeMetricPathKind[];
|
|
149
|
+
export declare function renderRepoShapeMarkdown(report: FrontierMergeMetricReport | FrontierMergeMetricCorpusReport): string;
|
|
150
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
export function analyzeRepoShape(input, options = {}) {
|
|
2
|
+
const commits = input.commits.filter((commit) => commit.files.length > 0);
|
|
3
|
+
const pairWindow = positiveInteger(options.pairWindow, 20);
|
|
4
|
+
const fileStats = collectFileStats(commits);
|
|
5
|
+
const totalFileTouches = sum([...fileStats.values()].map((stat) => stat.touches));
|
|
6
|
+
const hotspots = [...fileStats.entries()]
|
|
7
|
+
.map(([path, stat]) => createHotspot(path, stat))
|
|
8
|
+
.sort((left, right) => right.touches - left.touches || left.path.localeCompare(right.path))
|
|
9
|
+
.slice(0, positiveInteger(options.topFiles, 20));
|
|
10
|
+
const directories = createDirectoryHotspots(fileStats, positiveInteger(options.topDirectories, 12), totalFileTouches);
|
|
11
|
+
const cochangeEdges = createCochangeEdges(commits, positiveInteger(options.topCochanges, 20));
|
|
12
|
+
const allConflictPairs = createConflictPairs(commits, pairWindow);
|
|
13
|
+
const conflictPairs = allConflictPairs.slice(0, 200);
|
|
14
|
+
const pairCount = countCommitPairs(commits.length, pairWindow);
|
|
15
|
+
const resolutionEvents = normalizeResolutionEvents(input.resolutionEvents ?? []);
|
|
16
|
+
const metrics = createScores({
|
|
17
|
+
commitCount: commits.length,
|
|
18
|
+
fileStats,
|
|
19
|
+
totalFileTouches,
|
|
20
|
+
directories,
|
|
21
|
+
conflictPairs: allConflictPairs,
|
|
22
|
+
pairCount,
|
|
23
|
+
resolutionEvents,
|
|
24
|
+
files: input.files ?? []
|
|
25
|
+
});
|
|
26
|
+
const interventions = recommendInterventions(metrics, hotspots, allConflictPairs);
|
|
27
|
+
return {
|
|
28
|
+
kind: 'frontier.mergeMetrics.repoShapeReport',
|
|
29
|
+
version: 1,
|
|
30
|
+
repository: input.repository ?? 'unknown',
|
|
31
|
+
head: input.head,
|
|
32
|
+
generatedAt: options.generatedAt ?? new Date().toISOString(),
|
|
33
|
+
commitCount: commits.length,
|
|
34
|
+
changedFileCount: fileStats.size,
|
|
35
|
+
totalFileTouches,
|
|
36
|
+
metrics,
|
|
37
|
+
hotspots,
|
|
38
|
+
directories,
|
|
39
|
+
cochangeEdges,
|
|
40
|
+
conflictPairs,
|
|
41
|
+
resolutionEvents,
|
|
42
|
+
interventions,
|
|
43
|
+
evidence: {
|
|
44
|
+
pairWindow,
|
|
45
|
+
pairCount,
|
|
46
|
+
conflictPairCount: allConflictPairs.length,
|
|
47
|
+
displayedConflictPairCount: conflictPairs.length,
|
|
48
|
+
resolutionEventCount: resolutionEvents.length,
|
|
49
|
+
resolutionPathCount: uniqueSorted(resolutionEvents.flatMap((event) => event.paths)).length,
|
|
50
|
+
fileSnapshotCount: input.files?.length ?? 0,
|
|
51
|
+
classifierVersion: 1
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export function analyzeRepoShapeCorpus(reports, generatedAt = new Date().toISOString()) {
|
|
56
|
+
const interventionCounts = new Map();
|
|
57
|
+
for (const report of reports) {
|
|
58
|
+
for (const intervention of report.interventions) {
|
|
59
|
+
interventionCounts.set(intervention.id, (interventionCounts.get(intervention.id) ?? 0) + 1);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
kind: 'frontier.mergeMetrics.corpusReport',
|
|
64
|
+
version: 1,
|
|
65
|
+
generatedAt,
|
|
66
|
+
repositories: reports,
|
|
67
|
+
summary: {
|
|
68
|
+
repositoryCount: reports.length,
|
|
69
|
+
medianParallelismScore: median(reports.map((report) => report.metrics.parallelismScore)),
|
|
70
|
+
medianCollisionRate: median(reports.map((report) => report.metrics.counterfactualCollisionRate)),
|
|
71
|
+
medianHotspotTopShare: median(reports.map((report) => report.metrics.hotspotTopShare)),
|
|
72
|
+
medianCentralCollisionRate: median(reports.map((report) => report.metrics.centralCollisionRate)),
|
|
73
|
+
medianMergeResolutionEventRate: median(reports.map((report) => report.metrics.mergeResolutionEventRate)),
|
|
74
|
+
topInterventions: [...interventionCounts.entries()]
|
|
75
|
+
.map(([id, count]) => ({ id, count }))
|
|
76
|
+
.sort((left, right) => right.count - left.count || left.id.localeCompare(right.id))
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export function classifyPath(path) {
|
|
81
|
+
const normalized = normalizePath(path);
|
|
82
|
+
const basename = normalized.split('/').at(-1) ?? normalized;
|
|
83
|
+
const kinds = new Set();
|
|
84
|
+
if (isGeneratedPath(normalized))
|
|
85
|
+
kinds.add('generated');
|
|
86
|
+
if (isLockfile(basename))
|
|
87
|
+
kinds.add('lockfile');
|
|
88
|
+
if (isTestPath(normalized))
|
|
89
|
+
kinds.add('test');
|
|
90
|
+
if (isFixturePath(normalized))
|
|
91
|
+
kinds.add('fixture');
|
|
92
|
+
if (isDocsPath(normalized))
|
|
93
|
+
kinds.add('docs');
|
|
94
|
+
if (isHtmlPath(normalized))
|
|
95
|
+
kinds.add('html');
|
|
96
|
+
if (isStylePath(normalized)) {
|
|
97
|
+
kinds.add('style');
|
|
98
|
+
kinds.add(isScopedCssPath(normalized) ? 'scoped-css' : 'global-css');
|
|
99
|
+
}
|
|
100
|
+
if (isConfigPath(normalized))
|
|
101
|
+
kinds.add('config');
|
|
102
|
+
if (isCentralPath(normalized))
|
|
103
|
+
kinds.add('central');
|
|
104
|
+
if (isSourcePath(normalized))
|
|
105
|
+
kinds.add('source');
|
|
106
|
+
if (kinds.size === 0)
|
|
107
|
+
kinds.add('other');
|
|
108
|
+
return [...kinds].sort();
|
|
109
|
+
}
|
|
110
|
+
export function renderRepoShapeMarkdown(report) {
|
|
111
|
+
return report.kind === 'frontier.mergeMetrics.corpusReport'
|
|
112
|
+
? renderCorpusMarkdown(report)
|
|
113
|
+
: renderSingleMarkdown(report);
|
|
114
|
+
}
|
|
115
|
+
function collectFileStats(commits) {
|
|
116
|
+
const stats = new Map();
|
|
117
|
+
for (const commit of commits) {
|
|
118
|
+
for (const change of commit.files) {
|
|
119
|
+
const path = normalizePath(change.path);
|
|
120
|
+
const stat = stats.get(path) ?? { touches: 0, commits: new Set(), additions: 0, deletions: 0 };
|
|
121
|
+
stat.touches += 1;
|
|
122
|
+
stat.commits.add(commit.hash);
|
|
123
|
+
stat.additions += change.additions ?? 0;
|
|
124
|
+
stat.deletions += change.deletions ?? 0;
|
|
125
|
+
stats.set(path, stat);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return stats;
|
|
129
|
+
}
|
|
130
|
+
function createHotspot(path, stat) {
|
|
131
|
+
const kinds = classifyPath(path);
|
|
132
|
+
return {
|
|
133
|
+
path,
|
|
134
|
+
touches: stat.touches,
|
|
135
|
+
commits: stat.commits.size,
|
|
136
|
+
additions: stat.additions,
|
|
137
|
+
deletions: stat.deletions,
|
|
138
|
+
kinds,
|
|
139
|
+
riskTags: riskTagsForPath(path, kinds)
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function createDirectoryHotspots(fileStats, limit, totalFileTouches) {
|
|
143
|
+
const dirs = new Map();
|
|
144
|
+
for (const [path, stat] of fileStats) {
|
|
145
|
+
const dir = path.includes('/') ? path.split('/').slice(0, -1).join('/') : '.';
|
|
146
|
+
dirs.set(dir, (dirs.get(dir) ?? 0) + stat.touches);
|
|
147
|
+
}
|
|
148
|
+
return [...dirs.entries()]
|
|
149
|
+
.map(([directory, touches]) => ({
|
|
150
|
+
directory,
|
|
151
|
+
touches,
|
|
152
|
+
share: ratio(touches, totalFileTouches)
|
|
153
|
+
}))
|
|
154
|
+
.sort((left, right) => right.touches - left.touches || left.directory.localeCompare(right.directory))
|
|
155
|
+
.slice(0, limit);
|
|
156
|
+
}
|
|
157
|
+
function createCochangeEdges(commits, limit) {
|
|
158
|
+
const edges = new Map();
|
|
159
|
+
for (const commit of commits) {
|
|
160
|
+
const paths = uniqueSorted(commit.files.map((file) => normalizePath(file.path))).slice(0, 80);
|
|
161
|
+
for (let leftIndex = 0; leftIndex < paths.length; leftIndex += 1) {
|
|
162
|
+
for (let rightIndex = leftIndex + 1; rightIndex < paths.length; rightIndex += 1) {
|
|
163
|
+
const left = paths[leftIndex];
|
|
164
|
+
const right = paths[rightIndex];
|
|
165
|
+
const key = `${left}\0${right}`;
|
|
166
|
+
const edge = edges.get(key) ?? { left, right, commits: 0 };
|
|
167
|
+
edge.commits += 1;
|
|
168
|
+
edges.set(key, edge);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return [...edges.values()]
|
|
173
|
+
.map((edge) => ({ ...edge, score: edge.commits * cochangeRiskMultiplier(edge.left, edge.right) }))
|
|
174
|
+
.sort((left, right) => right.score - left.score || left.left.localeCompare(right.left))
|
|
175
|
+
.slice(0, limit);
|
|
176
|
+
}
|
|
177
|
+
function createConflictPairs(commits, pairWindow) {
|
|
178
|
+
const pairs = [];
|
|
179
|
+
for (let leftIndex = 0; leftIndex < commits.length; leftIndex += 1) {
|
|
180
|
+
const left = commits[leftIndex];
|
|
181
|
+
const leftPaths = new Set(left.files.map((file) => normalizePath(file.path)));
|
|
182
|
+
for (let rightIndex = leftIndex + 1; rightIndex < Math.min(commits.length, leftIndex + 1 + pairWindow); rightIndex += 1) {
|
|
183
|
+
const right = commits[rightIndex];
|
|
184
|
+
const sharedPaths = uniqueSorted(right.files.map((file) => normalizePath(file.path)).filter((path) => leftPaths.has(path)));
|
|
185
|
+
if (!sharedPaths.length)
|
|
186
|
+
continue;
|
|
187
|
+
const centralPaths = sharedPaths.filter((path) => classifyPath(path).includes('central'));
|
|
188
|
+
const generatedPaths = sharedPaths.filter((path) => classifyPath(path).includes('generated'));
|
|
189
|
+
const globalStylePaths = sharedPaths.filter((path) => classifyPath(path).includes('global-css'));
|
|
190
|
+
const fixturePaths = sharedPaths.filter((path) => classifyPath(path).includes('fixture'));
|
|
191
|
+
pairs.push({
|
|
192
|
+
leftCommit: left.hash,
|
|
193
|
+
rightCommit: right.hash,
|
|
194
|
+
sharedPaths,
|
|
195
|
+
centralPaths,
|
|
196
|
+
generatedPaths,
|
|
197
|
+
globalStylePaths,
|
|
198
|
+
fixturePaths,
|
|
199
|
+
riskScore: sharedPaths.length +
|
|
200
|
+
centralPaths.length * 2 +
|
|
201
|
+
generatedPaths.length +
|
|
202
|
+
globalStylePaths.length * 2 +
|
|
203
|
+
fixturePaths.length
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return pairs.sort((left, right) => right.riskScore - left.riskScore);
|
|
208
|
+
}
|
|
209
|
+
function normalizeResolutionEvents(events) {
|
|
210
|
+
return events
|
|
211
|
+
.map((event) => ({
|
|
212
|
+
...event,
|
|
213
|
+
paths: uniqueSorted(event.paths.map(normalizePath))
|
|
214
|
+
}))
|
|
215
|
+
.filter((event) => event.paths.length > 0)
|
|
216
|
+
.sort((left, right) => right.paths.length - left.paths.length || left.id.localeCompare(right.id));
|
|
217
|
+
}
|
|
218
|
+
function createScores(input) {
|
|
219
|
+
const shares = [...input.fileStats.values()].map((stat) => ratio(stat.touches, input.totalFileTouches));
|
|
220
|
+
const hotspotConcentration = Number(shares.reduce((sumValue, share) => sumValue + share * share, 0).toFixed(4));
|
|
221
|
+
const topShare = ratio(Math.max(0, ...[...input.fileStats.values()].map((stat) => stat.touches)), input.totalFileTouches);
|
|
222
|
+
const centralTouches = touchesForKind(input.fileStats, 'central');
|
|
223
|
+
const generatedTouches = touchesForKind(input.fileStats, 'generated');
|
|
224
|
+
const globalCssTouches = touchesForKind(input.fileStats, 'global-css');
|
|
225
|
+
const fixtureTouches = touchesForKind(input.fileStats, 'fixture');
|
|
226
|
+
const centralCollisionRate = ratio(input.conflictPairs.filter((pair) => pair.centralPaths.length > 0).length, input.pairCount);
|
|
227
|
+
const resolutionPaths = input.resolutionEvents.flatMap((event) => event.paths);
|
|
228
|
+
const resolutionPathCount = uniqueSorted(resolutionPaths).length;
|
|
229
|
+
const resolutionCentralPathCount = uniqueSorted(resolutionPaths.filter((path) => classifyPath(path).includes('central'))).length;
|
|
230
|
+
const ownershipDispersion = Number((1 - (input.directories[0]?.share ?? 0)).toFixed(4));
|
|
231
|
+
const mergeResolutionEventRate = ratio(input.resolutionEvents.length, input.commitCount);
|
|
232
|
+
const mergeResolutionCentralShare = ratio(resolutionCentralPathCount, resolutionPathCount);
|
|
233
|
+
const risk = (topShare * 22 +
|
|
234
|
+
ratio(centralTouches, input.totalFileTouches) * 18 +
|
|
235
|
+
ratio(generatedTouches, input.totalFileTouches) * 14 +
|
|
236
|
+
ratio(globalCssTouches, input.totalFileTouches) * 14 +
|
|
237
|
+
ratio(fixtureTouches, input.totalFileTouches) * 10 +
|
|
238
|
+
ratio(input.conflictPairs.length, input.pairCount) * 20 +
|
|
239
|
+
centralCollisionRate * 24 +
|
|
240
|
+
mergeResolutionEventRate * 10 +
|
|
241
|
+
mergeResolutionCentralShare * 8);
|
|
242
|
+
return {
|
|
243
|
+
hotspotConcentration,
|
|
244
|
+
hotspotTopShare: topShare,
|
|
245
|
+
directoryConcentration: Number((input.directories[0]?.share ?? 0).toFixed(4)),
|
|
246
|
+
counterfactualCollisionRate: ratio(input.conflictPairs.length, input.pairCount),
|
|
247
|
+
centralCollisionRate,
|
|
248
|
+
mergeResolutionEventRate,
|
|
249
|
+
mergeResolutionPathShare: ratio(resolutionPathCount, input.fileStats.size),
|
|
250
|
+
mergeResolutionCentralShare,
|
|
251
|
+
generatedChurnShare: ratio(generatedTouches, input.totalFileTouches),
|
|
252
|
+
globalCssShare: ratio(globalCssTouches, input.totalFileTouches),
|
|
253
|
+
fixtureChurnShare: ratio(fixtureTouches, input.totalFileTouches),
|
|
254
|
+
ownershipDispersion,
|
|
255
|
+
parallelismScore: clamp(Math.round(100 - risk), 0, 100)
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function recommendInterventions(metrics, hotspots, conflictPairs) {
|
|
259
|
+
const interventions = [];
|
|
260
|
+
if (metrics.hotspotTopShare >= 0.08 || metrics.hotspotConcentration >= 0.03) {
|
|
261
|
+
interventions.push({
|
|
262
|
+
id: 'split-hot-files',
|
|
263
|
+
title: 'Split hot files into owned regions',
|
|
264
|
+
reason: `The top touched file accounts for ${(metrics.hotspotTopShare * 100).toFixed(1)}% of file touches.`,
|
|
265
|
+
expectedEffect: 'Reduce same-file counterfactual collisions and make semantic ownership narrower.',
|
|
266
|
+
severity: metrics.hotspotTopShare >= 0.15 ? 'high' : 'medium'
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
if (metrics.centralCollisionRate >= 0.02 || hotspots.some((hotspot) => hotspot.kinds.includes('central'))) {
|
|
270
|
+
interventions.push({
|
|
271
|
+
id: 'generate-central-indexes',
|
|
272
|
+
title: 'Generate central indexes and registries',
|
|
273
|
+
reason: 'Central route/config/index files appear in hot spots or counterfactual collisions.',
|
|
274
|
+
expectedEffect: 'Move repeated edits out of hand-merged files and into feature-owned declarations.',
|
|
275
|
+
severity: metrics.centralCollisionRate >= 0.05 ? 'high' : 'medium'
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
if (metrics.globalCssShare >= 0.03 || conflictPairs.some((pair) => pair.globalStylePaths.length > 0)) {
|
|
279
|
+
interventions.push({
|
|
280
|
+
id: 'scope-style-surfaces',
|
|
281
|
+
title: 'Scope global style surfaces',
|
|
282
|
+
reason: `Global CSS accounts for ${(metrics.globalCssShare * 100).toFixed(1)}% of file touches or appears in collisions.`,
|
|
283
|
+
expectedEffect: 'Turn ambient cascade conflicts into component, module, or token-scoped changes.',
|
|
284
|
+
severity: metrics.globalCssShare >= 0.08 ? 'high' : 'medium'
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
if (metrics.generatedChurnShare >= 0.03) {
|
|
288
|
+
interventions.push({
|
|
289
|
+
id: 'remove-generated-review-surfaces',
|
|
290
|
+
title: 'Keep generated outputs out of review',
|
|
291
|
+
reason: `Generated/lock/snapshot-like files account for ${(metrics.generatedChurnShare * 100).toFixed(1)}% of file touches.`,
|
|
292
|
+
expectedEffect: 'Treat generated files as reproducible evidence instead of merge surfaces.',
|
|
293
|
+
severity: metrics.generatedChurnShare >= 0.1 ? 'high' : 'medium'
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
if (metrics.fixtureChurnShare >= 0.03) {
|
|
297
|
+
interventions.push({
|
|
298
|
+
id: 'shrink-fixture-blast-radius',
|
|
299
|
+
title: 'Shrink fixture and snapshot blast radius',
|
|
300
|
+
reason: `Fixture/snapshot paths account for ${(metrics.fixtureChurnShare * 100).toFixed(1)}% of file touches.`,
|
|
301
|
+
expectedEffect: 'Replace huge snapshots with focused fixtures tied to semantic regions.',
|
|
302
|
+
severity: 'medium'
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
if (metrics.mergeResolutionEventRate > 0) {
|
|
306
|
+
interventions.push({
|
|
307
|
+
id: 'inspect-merge-resolution-hotspots',
|
|
308
|
+
title: 'Inspect actual merge-resolution hot spots',
|
|
309
|
+
reason: `Merge-resolution signals appear in ${(metrics.mergeResolutionEventRate * 100).toFixed(1)}% of sampled commits.`,
|
|
310
|
+
expectedEffect: 'Use observed resolved merge paths to tune leases, ownership regions, and task routing.',
|
|
311
|
+
severity: metrics.mergeResolutionCentralShare >= 0.25 ? 'high' : 'medium'
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
if (metrics.directoryConcentration >= 0.45) {
|
|
315
|
+
interventions.push({
|
|
316
|
+
id: 'rebalance-directory-ownership',
|
|
317
|
+
title: 'Rebalance feature/package ownership',
|
|
318
|
+
reason: `The hottest directory accounts for ${(metrics.directoryConcentration * 100).toFixed(1)}% of file touches.`,
|
|
319
|
+
expectedEffect: 'Create clearer package or feature lanes for parallel workers.',
|
|
320
|
+
severity: 'medium'
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
return interventions;
|
|
324
|
+
}
|
|
325
|
+
function renderSingleMarkdown(report) {
|
|
326
|
+
return [
|
|
327
|
+
`# Merge Metrics: ${report.repository}`,
|
|
328
|
+
'',
|
|
329
|
+
`- head: ${report.head ?? 'unknown'}`,
|
|
330
|
+
`- commits sampled: ${report.commitCount}`,
|
|
331
|
+
`- changed files: ${report.changedFileCount}`,
|
|
332
|
+
`- counterfactual pair window: ${report.evidence.pairWindow}`,
|
|
333
|
+
`- parallelism score: ${report.metrics.parallelismScore}/100`,
|
|
334
|
+
'',
|
|
335
|
+
'## Scores',
|
|
336
|
+
'',
|
|
337
|
+
'| Metric | Value |',
|
|
338
|
+
'| --- | ---: |',
|
|
339
|
+
`| Counterfactual collision rate | ${(report.metrics.counterfactualCollisionRate * 100).toFixed(1)}% |`,
|
|
340
|
+
`| Central collision rate | ${(report.metrics.centralCollisionRate * 100).toFixed(1)}% |`,
|
|
341
|
+
`| Observed merge-resolution event rate | ${(report.metrics.mergeResolutionEventRate * 100).toFixed(1)}% |`,
|
|
342
|
+
`| Merge-resolution path share | ${(report.metrics.mergeResolutionPathShare * 100).toFixed(1)}% |`,
|
|
343
|
+
`| Merge-resolution central share | ${(report.metrics.mergeResolutionCentralShare * 100).toFixed(1)}% |`,
|
|
344
|
+
`| Top file touch share | ${(report.metrics.hotspotTopShare * 100).toFixed(1)}% |`,
|
|
345
|
+
`| Generated churn share | ${(report.metrics.generatedChurnShare * 100).toFixed(1)}% |`,
|
|
346
|
+
`| Global CSS share | ${(report.metrics.globalCssShare * 100).toFixed(1)}% |`,
|
|
347
|
+
`| Fixture churn share | ${(report.metrics.fixtureChurnShare * 100).toFixed(1)}% |`,
|
|
348
|
+
'',
|
|
349
|
+
'## Observed Merge-Resolution Signals',
|
|
350
|
+
'',
|
|
351
|
+
...(report.resolutionEvents.length
|
|
352
|
+
? [
|
|
353
|
+
'| Event | Paths | Evidence |',
|
|
354
|
+
'| --- | ---: | --- |',
|
|
355
|
+
...report.resolutionEvents.slice(0, 10).map((event) => `| \`${event.commitHash ?? event.id}\` | ${event.paths.length} | ${event.evidence ?? event.kind} |`)
|
|
356
|
+
]
|
|
357
|
+
: ['No observed merge-resolution signal was captured in this sample.']),
|
|
358
|
+
'',
|
|
359
|
+
'## Hot Files',
|
|
360
|
+
'',
|
|
361
|
+
'| File | Touches | Tags |',
|
|
362
|
+
'| --- | ---: | --- |',
|
|
363
|
+
...report.hotspots.slice(0, 10).map((row) => `| \`${row.path}\` | ${row.touches} | ${row.riskTags.join(', ') || row.kinds.join(', ')} |`),
|
|
364
|
+
'',
|
|
365
|
+
'## Recommendations',
|
|
366
|
+
'',
|
|
367
|
+
...(report.interventions.length
|
|
368
|
+
? report.interventions.map((item) => `- **${item.title}** (${item.severity}): ${item.reason} ${item.expectedEffect}`)
|
|
369
|
+
: ['- No high-signal repo-shape intervention was triggered by this sample.'])
|
|
370
|
+
].join('\n');
|
|
371
|
+
}
|
|
372
|
+
function renderCorpusMarkdown(report) {
|
|
373
|
+
return [
|
|
374
|
+
'# Merge Metrics Corpus Report',
|
|
375
|
+
'',
|
|
376
|
+
`- repositories: ${report.summary.repositoryCount}`,
|
|
377
|
+
`- median parallelism score: ${report.summary.medianParallelismScore}/100`,
|
|
378
|
+
`- median collision rate: ${(report.summary.medianCollisionRate * 100).toFixed(1)}%`,
|
|
379
|
+
`- median merge-resolution event rate: ${(report.summary.medianMergeResolutionEventRate * 100).toFixed(1)}%`,
|
|
380
|
+
`- median top-file share: ${(report.summary.medianHotspotTopShare * 100).toFixed(1)}%`,
|
|
381
|
+
'',
|
|
382
|
+
'## Repository Summary',
|
|
383
|
+
'',
|
|
384
|
+
'| Repository | Score | Collision | Resolution Events | Top File Share | Top Recommendation |',
|
|
385
|
+
'| --- | ---: | ---: | ---: | ---: | --- |',
|
|
386
|
+
...report.repositories.map((repo) => `| ${repo.repository} | ${repo.metrics.parallelismScore} | ${(repo.metrics.counterfactualCollisionRate * 100).toFixed(1)}% | ${(repo.metrics.mergeResolutionEventRate * 100).toFixed(1)}% | ${(repo.metrics.hotspotTopShare * 100).toFixed(1)}% | ${repo.interventions[0]?.title ?? 'None'} |`),
|
|
387
|
+
'',
|
|
388
|
+
'## Common Interventions',
|
|
389
|
+
'',
|
|
390
|
+
...report.summary.topInterventions.map((item) => `- ${item.id}: ${item.count}/${report.summary.repositoryCount}`)
|
|
391
|
+
].join('\n');
|
|
392
|
+
}
|
|
393
|
+
function touchesForKind(fileStats, kind) {
|
|
394
|
+
let touches = 0;
|
|
395
|
+
for (const [path, stat] of fileStats) {
|
|
396
|
+
if (classifyPath(path).includes(kind))
|
|
397
|
+
touches += stat.touches;
|
|
398
|
+
}
|
|
399
|
+
return touches;
|
|
400
|
+
}
|
|
401
|
+
function riskTagsForPath(path, kinds) {
|
|
402
|
+
const tags = [];
|
|
403
|
+
if (kinds.includes('central'))
|
|
404
|
+
tags.push('central');
|
|
405
|
+
if (kinds.includes('global-css'))
|
|
406
|
+
tags.push('global-css');
|
|
407
|
+
if (kinds.includes('generated'))
|
|
408
|
+
tags.push('generated');
|
|
409
|
+
if (kinds.includes('fixture'))
|
|
410
|
+
tags.push('fixture');
|
|
411
|
+
if (/barrel|index\.[cm]?[jt]sx?$/.test(path))
|
|
412
|
+
tags.push('barrel');
|
|
413
|
+
return uniqueSorted(tags);
|
|
414
|
+
}
|
|
415
|
+
function cochangeRiskMultiplier(left, right) {
|
|
416
|
+
const kinds = new Set([...classifyPath(left), ...classifyPath(right)]);
|
|
417
|
+
let multiplier = 1;
|
|
418
|
+
if (kinds.has('central'))
|
|
419
|
+
multiplier += 1.5;
|
|
420
|
+
if (kinds.has('global-css'))
|
|
421
|
+
multiplier += 1;
|
|
422
|
+
if (kinds.has('generated'))
|
|
423
|
+
multiplier += 0.5;
|
|
424
|
+
return multiplier;
|
|
425
|
+
}
|
|
426
|
+
function countCommitPairs(commitCount, pairWindow) {
|
|
427
|
+
let count = 0;
|
|
428
|
+
for (let index = 0; index < commitCount; index += 1) {
|
|
429
|
+
count += Math.max(0, Math.min(commitCount, index + 1 + pairWindow) - index - 1);
|
|
430
|
+
}
|
|
431
|
+
return count;
|
|
432
|
+
}
|
|
433
|
+
function isSourcePath(path) {
|
|
434
|
+
return /\.(m?[jt]sx?|tsx|jsx|vue|svelte|astro)$/.test(path) && !isTestPath(path);
|
|
435
|
+
}
|
|
436
|
+
function isHtmlPath(path) {
|
|
437
|
+
return /\.(html|htm)$/.test(path);
|
|
438
|
+
}
|
|
439
|
+
function isStylePath(path) {
|
|
440
|
+
return /\.(css|scss|sass|less)$/.test(path);
|
|
441
|
+
}
|
|
442
|
+
function isScopedCssPath(path) {
|
|
443
|
+
return /\.module\.(css|scss|sass|less)$/.test(path) || /\/(components|views|pages)\//.test(path);
|
|
444
|
+
}
|
|
445
|
+
function isTestPath(path) {
|
|
446
|
+
return /(^|\/)(__tests__|test|tests|spec|e2e|cypress|playwright)(\/|$)|\.(test|spec)\.[cm]?[jt]sx?$/.test(path);
|
|
447
|
+
}
|
|
448
|
+
function isFixturePath(path) {
|
|
449
|
+
return /(^|\/)(__snapshots__|fixtures?|testdata|snapshots?)(\/|$)|\.(snap|snapshot)$/.test(path);
|
|
450
|
+
}
|
|
451
|
+
function isDocsPath(path) {
|
|
452
|
+
return /(^|\/)(docs?|website)(\/|$)|\.(md|mdx|rst)$/.test(path);
|
|
453
|
+
}
|
|
454
|
+
function isConfigPath(path) {
|
|
455
|
+
const basename = path.split('/').at(-1) ?? path;
|
|
456
|
+
return /(^|\.)config\.[cm]?[jt]s$|^(package|tsconfig|vite\.config|next\.config|rollup\.config|webpack\.config|eslint\.config|biome)\b/.test(basename);
|
|
457
|
+
}
|
|
458
|
+
function isCentralPath(path) {
|
|
459
|
+
const basename = path.split('/').at(-1) ?? path;
|
|
460
|
+
return /^(index|routes?|router|registry|schema|schemas|manifest|config|constants)\.[cm]?[jt]sx?$/.test(basename) ||
|
|
461
|
+
/(^|\/)(routes?|router|registry|schemas?|manifests?|config)\/index\.[cm]?[jt]sx?$/.test(path);
|
|
462
|
+
}
|
|
463
|
+
function isGeneratedPath(path) {
|
|
464
|
+
return /(^|\/)(dist|build|generated|coverage|out|\.next|\.turbo)(\/|$)|(^|\/).*\.generated\./.test(path) || isLockfile(path.split('/').at(-1) ?? path);
|
|
465
|
+
}
|
|
466
|
+
function isLockfile(basename) {
|
|
467
|
+
return ['package-lock.json', 'npm-shrinkwrap.json', 'pnpm-lock.yaml', 'yarn.lock', 'bun.lockb'].includes(basename);
|
|
468
|
+
}
|
|
469
|
+
function normalizePath(path) {
|
|
470
|
+
return path.replaceAll('\\', '/').replace(/^\.\//, '');
|
|
471
|
+
}
|
|
472
|
+
function uniqueSorted(values) {
|
|
473
|
+
return [...new Set(values)].sort();
|
|
474
|
+
}
|
|
475
|
+
function sum(values) {
|
|
476
|
+
return values.reduce((total, value) => total + value, 0);
|
|
477
|
+
}
|
|
478
|
+
function ratio(value, total) {
|
|
479
|
+
return total > 0 ? Number((value / total).toFixed(4)) : 0;
|
|
480
|
+
}
|
|
481
|
+
function median(values) {
|
|
482
|
+
if (!values.length)
|
|
483
|
+
return 0;
|
|
484
|
+
const sorted = [...values].sort((left, right) => left - right);
|
|
485
|
+
const middle = Math.floor(sorted.length / 2);
|
|
486
|
+
return sorted.length % 2 === 0
|
|
487
|
+
? Number((((sorted[middle - 1] ?? 0) + (sorted[middle] ?? 0)) / 2).toFixed(4))
|
|
488
|
+
: sorted[middle] ?? 0;
|
|
489
|
+
}
|
|
490
|
+
function positiveInteger(value, fallback) {
|
|
491
|
+
return typeof value === 'number' && Number.isInteger(value) && value > 0 ? value : fallback;
|
|
492
|
+
}
|
|
493
|
+
function clamp(value, min, max) {
|
|
494
|
+
return Math.max(min, Math.min(max, value));
|
|
495
|
+
}
|
|
496
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA0KA,MAAM,UAAU,gBAAgB,CAC9B,KAA+B,EAC/B,UAAsC,EAAE;IAExC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC1E,MAAM,UAAU,GAAG,eAAe,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC3D,MAAM,SAAS,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,gBAAgB,GAAG,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IAClF,MAAM,QAAQ,GAAG,CAAC,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC;SACtC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;SAChD,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;SAC1F,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC;IACnD,MAAM,WAAW,GAAG,uBAAuB,CAAC,SAAS,EAAE,eAAe,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,EAAE,gBAAgB,CAAC,CAAC;IACtH,MAAM,aAAa,GAAG,mBAAmB,CAAC,OAAO,EAAE,eAAe,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,CAAC;IAC9F,MAAM,gBAAgB,GAAG,mBAAmB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAClE,MAAM,aAAa,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACrD,MAAM,SAAS,GAAG,gBAAgB,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC/D,MAAM,gBAAgB,GAAG,yBAAyB,CAAC,KAAK,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC;IACjF,MAAM,OAAO,GAAG,YAAY,CAAC;QAC3B,WAAW,EAAE,OAAO,CAAC,MAAM;QAC3B,SAAS;QACT,gBAAgB;QAChB,WAAW;QACX,aAAa,EAAE,gBAAgB;QAC/B,SAAS;QACT,gBAAgB;QAChB,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,EAAE;KACzB,CAAC,CAAC;IACH,MAAM,aAAa,GAAG,sBAAsB,CAAC,OAAO,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC;IAClF,OAAO;QACL,IAAI,EAAE,uCAAuC;QAC7C,OAAO,EAAE,CAAC;QACV,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,SAAS;QACzC,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC5D,WAAW,EAAE,OAAO,CAAC,MAAM;QAC3B,gBAAgB,EAAE,SAAS,CAAC,IAAI;QAChC,gBAAgB;QAChB,OAAO;QACP,QAAQ;QACR,WAAW;QACX,aAAa;QACb,aAAa;QACb,gBAAgB;QAChB,aAAa;QACb,QAAQ,EAAE;YACR,UAAU;YACV,SAAS;YACT,iBAAiB,EAAE,gBAAgB,CAAC,MAAM;YAC1C,0BAA0B,EAAE,aAAa,CAAC,MAAM;YAChD,oBAAoB,EAAE,gBAAgB,CAAC,MAAM;YAC7C,mBAAmB,EAAE,YAAY,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM;YAC1F,iBAAiB,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;YAC3C,iBAAiB,EAAE,CAAC;SACrB;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,OAA6C,EAC7C,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;IAEtC,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAkB,CAAC;IACrD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,KAAK,MAAM,YAAY,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;YAChD,kBAAkB,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9F,CAAC;IACH,CAAC;IACD,OAAO;QACL,IAAI,EAAE,oCAAoC;QAC1C,OAAO,EAAE,CAAC;QACV,WAAW;QACX,YAAY,EAAE,OAAO;QACrB,OAAO,EAAE;YACP,eAAe,EAAE,OAAO,CAAC,MAAM;YAC/B,sBAAsB,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;YACxF,mBAAmB,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC;YAChG,qBAAqB,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;YACtF,0BAA0B,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;YAChG,8BAA8B,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC;YACxG,gBAAgB,EAAE,CAAC,GAAG,kBAAkB,CAAC,OAAO,EAAE,CAAC;iBAChD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;iBACrC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;SACtF;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC;IAC5D,MAAM,KAAK,GAAG,IAAI,GAAG,EAA+B,CAAC;IACrD,IAAI,eAAe,CAAC,UAAU,CAAC;QAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACxD,IAAI,UAAU,CAAC,QAAQ,CAAC;QAAE,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAChD,IAAI,UAAU,CAAC,UAAU,CAAC;QAAE,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9C,IAAI,aAAa,CAAC,UAAU,CAAC;QAAE,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACpD,IAAI,UAAU,CAAC,UAAU,CAAC;QAAE,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9C,IAAI,UAAU,CAAC,UAAU,CAAC;QAAE,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9C,IAAI,WAAW,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACnB,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,YAAY,CAAC,UAAU,CAAC;QAAE,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAClD,IAAI,aAAa,CAAC,UAAU,CAAC;QAAE,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACpD,IAAI,YAAY,CAAC,UAAU,CAAC;QAAE,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAClD,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC;QAAE,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,MAAmE;IACzG,OAAO,MAAM,CAAC,IAAI,KAAK,oCAAoC;QACzD,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC;QAC9B,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC;AASD,SAAS,gBAAgB,CAAC,OAA6C;IACrE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC1C,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YAClC,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACxC,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,GAAG,EAAU,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;YACvG,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;YAClB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;YACxC,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;YACxC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,aAAa,CAAC,IAAY,EAAE,IAAc;IACjD,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI;QAC1B,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,KAAK;QACL,QAAQ,EAAE,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC;KACvC,CAAC;AACJ,CAAC;AAED,SAAS,uBAAuB,CAC9B,SAAgC,EAChC,KAAa,EACb,gBAAwB;IAExB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IACvC,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,SAAS,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAC9E,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;SACvB,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;QAC9B,SAAS;QACT,OAAO;QACP,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,gBAAgB,CAAC;KACxC,CAAC,CAAC;SACF,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;SACpG,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AACrB,CAAC;AAED,SAAS,mBAAmB,CAAC,OAA6C,EAAE,KAAa;IACvF,MAAM,KAAK,GAAG,IAAI,GAAG,EAA4D,CAAC;IAClF,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC9F,KAAK,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI,CAAC,EAAE,CAAC;YACjE,KAAK,IAAI,UAAU,GAAG,SAAS,GAAG,CAAC,EAAE,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,UAAU,IAAI,CAAC,EAAE,CAAC;gBAChF,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAE,CAAC;gBAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,CAAE,CAAC;gBACjC,MAAM,GAAG,GAAG,GAAG,IAAI,KAAK,KAAK,EAAE,CAAC;gBAChC,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;gBAC3D,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;gBAClB,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;SACvB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,GAAG,sBAAsB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;SACjG,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;SACtF,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AACrB,CAAC;AAED,SAAS,mBAAmB,CAAC,OAA6C,EAAE,UAAkB;IAC5F,MAAM,KAAK,GAAsC,EAAE,CAAC;IACpD,KAAK,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,EAAE,SAAS,IAAI,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,GAAG,OAAO,CAAC,SAAS,CAAE,CAAC;QACjC,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC9E,KAAK,IAAI,UAAU,GAAG,SAAS,GAAG,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,SAAS,GAAG,CAAC,GAAG,UAAU,CAAC,EAAE,UAAU,IAAI,CAAC,EAAE,CAAC;YACxH,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAE,CAAC;YACnC,MAAM,WAAW,GAAG,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC5H,IAAI,CAAC,WAAW,CAAC,MAAM;gBAAE,SAAS;YAClC,MAAM,YAAY,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;YAC1F,MAAM,cAAc,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;YAC9F,MAAM,gBAAgB,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC;YACjG,MAAM,YAAY,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;YAC1F,KAAK,CAAC,IAAI,CAAC;gBACT,UAAU,EAAE,IAAI,CAAC,IAAI;gBACrB,WAAW,EAAE,KAAK,CAAC,IAAI;gBACvB,WAAW;gBACX,YAAY;gBACZ,cAAc;gBACd,gBAAgB;gBAChB,YAAY;gBACZ,SAAS,EAAE,WAAW,CAAC,MAAM;oBAC3B,YAAY,CAAC,MAAM,GAAG,CAAC;oBACvB,cAAc,CAAC,MAAM;oBACrB,gBAAgB,CAAC,MAAM,GAAG,CAAC;oBAC3B,YAAY,CAAC,MAAM;aACtB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,yBAAyB,CAAC,MAAqD;IACtF,OAAO,MAAM;SACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACf,GAAG,KAAK;QACR,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;KACpD,CAAC,CAAC;SACF,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;SACzC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;AACtG,CAAC;AAED,SAAS,YAAY,CAAC,KASrB;IACC,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC;IACxG,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,CAAC,QAAQ,GAAG,KAAK,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAChH,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAC1H,MAAM,cAAc,GAAG,cAAc,CAAC,KAAK,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAClE,MAAM,gBAAgB,GAAG,cAAc,CAAC,KAAK,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IACtE,MAAM,gBAAgB,GAAG,cAAc,CAAC,KAAK,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IACvE,MAAM,cAAc,GAAG,cAAc,CAAC,KAAK,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAClE,MAAM,oBAAoB,GAAG,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC/H,MAAM,eAAe,GAAG,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC/E,MAAM,mBAAmB,GAAG,YAAY,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC;IACjE,MAAM,0BAA0B,GAAG,YAAY,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACjI,MAAM,mBAAmB,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACxF,MAAM,wBAAwB,GAAG,KAAK,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;IACzF,MAAM,2BAA2B,GAAG,KAAK,CAAC,0BAA0B,EAAE,mBAAmB,CAAC,CAAC;IAC3F,MAAM,IAAI,GAAG,CACX,QAAQ,GAAG,EAAE;QACb,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,gBAAgB,CAAC,GAAG,EAAE;QAClD,KAAK,CAAC,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,CAAC,GAAG,EAAE;QACpD,KAAK,CAAC,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,CAAC,GAAG,EAAE;QACpD,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,gBAAgB,CAAC,GAAG,EAAE;QAClD,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACvD,oBAAoB,GAAG,EAAE;QACzB,wBAAwB,GAAG,EAAE;QAC7B,2BAA2B,GAAG,CAAC,CAChC,CAAC;IACF,OAAO;QACL,oBAAoB;QACpB,eAAe,EAAE,QAAQ;QACzB,sBAAsB,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC7E,2BAA2B,EAAE,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC;QAC/E,oBAAoB;QACpB,wBAAwB;QACxB,wBAAwB,EAAE,KAAK,CAAC,mBAAmB,EAAE,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC;QAC1E,2BAA2B;QAC3B,mBAAmB,EAAE,KAAK,CAAC,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,CAAC;QACpE,cAAc,EAAE,KAAK,CAAC,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,CAAC;QAC/D,iBAAiB,EAAE,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,gBAAgB,CAAC;QAChE,mBAAmB;QACnB,gBAAgB,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC;KACxD,CAAC;AACJ,CAAC;AAED,SAAS,sBAAsB,CAC7B,OAAkC,EAClC,QAA+C,EAC/C,aAAyD;IAEzD,MAAM,aAAa,GAAsC,EAAE,CAAC;IAC5D,IAAI,OAAO,CAAC,eAAe,IAAI,IAAI,IAAI,OAAO,CAAC,oBAAoB,IAAI,IAAI,EAAE,CAAC;QAC5E,aAAa,CAAC,IAAI,CAAC;YACjB,EAAE,EAAE,iBAAiB;YACrB,KAAK,EAAE,oCAAoC;YAC3C,MAAM,EAAE,qCAAqC,CAAC,OAAO,CAAC,eAAe,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB;YAC3G,cAAc,EAAE,kFAAkF;YAClG,QAAQ,EAAE,OAAO,CAAC,eAAe,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;SAC9D,CAAC,CAAC;IACL,CAAC;IACD,IAAI,OAAO,CAAC,oBAAoB,IAAI,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;QAC1G,aAAa,CAAC,IAAI,CAAC;YACjB,EAAE,EAAE,0BAA0B;YAC9B,KAAK,EAAE,yCAAyC;YAChD,MAAM,EAAE,oFAAoF;YAC5F,cAAc,EAAE,mFAAmF;YACnG,QAAQ,EAAE,OAAO,CAAC,oBAAoB,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;SACnE,CAAC,CAAC;IACL,CAAC;IACD,IAAI,OAAO,CAAC,cAAc,IAAI,IAAI,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;QACrG,aAAa,CAAC,IAAI,CAAC;YACjB,EAAE,EAAE,sBAAsB;YAC1B,KAAK,EAAE,6BAA6B;YACpC,MAAM,EAAE,2BAA2B,CAAC,OAAO,CAAC,cAAc,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,6CAA6C;YACzH,cAAc,EAAE,iFAAiF;YACjG,QAAQ,EAAE,OAAO,CAAC,cAAc,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;SAC7D,CAAC,CAAC;IACL,CAAC;IACD,IAAI,OAAO,CAAC,mBAAmB,IAAI,IAAI,EAAE,CAAC;QACxC,aAAa,CAAC,IAAI,CAAC;YACjB,EAAE,EAAE,kCAAkC;YACtC,KAAK,EAAE,sCAAsC;YAC7C,MAAM,EAAE,kDAAkD,CAAC,OAAO,CAAC,mBAAmB,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB;YAC5H,cAAc,EAAE,2EAA2E;YAC3F,QAAQ,EAAE,OAAO,CAAC,mBAAmB,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;SACjE,CAAC,CAAC;IACL,CAAC;IACD,IAAI,OAAO,CAAC,iBAAiB,IAAI,IAAI,EAAE,CAAC;QACtC,aAAa,CAAC,IAAI,CAAC;YACjB,EAAE,EAAE,6BAA6B;YACjC,KAAK,EAAE,0CAA0C;YACjD,MAAM,EAAE,sCAAsC,CAAC,OAAO,CAAC,iBAAiB,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB;YAC9G,cAAc,EAAE,wEAAwE;YACxF,QAAQ,EAAE,QAAQ;SACnB,CAAC,CAAC;IACL,CAAC;IACD,IAAI,OAAO,CAAC,wBAAwB,GAAG,CAAC,EAAE,CAAC;QACzC,aAAa,CAAC,IAAI,CAAC;YACjB,EAAE,EAAE,mCAAmC;YACvC,KAAK,EAAE,2CAA2C;YAClD,MAAM,EAAE,sCAAsC,CAAC,OAAO,CAAC,wBAAwB,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,uBAAuB;YACxH,cAAc,EAAE,wFAAwF;YACxG,QAAQ,EAAE,OAAO,CAAC,2BAA2B,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;SAC1E,CAAC,CAAC;IACL,CAAC;IACD,IAAI,OAAO,CAAC,sBAAsB,IAAI,IAAI,EAAE,CAAC;QAC3C,aAAa,CAAC,IAAI,CAAC;YACjB,EAAE,EAAE,+BAA+B;YACnC,KAAK,EAAE,qCAAqC;YAC5C,MAAM,EAAE,sCAAsC,CAAC,OAAO,CAAC,sBAAsB,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB;YACnH,cAAc,EAAE,+DAA+D;YAC/E,QAAQ,EAAE,QAAQ;SACnB,CAAC,CAAC;IACL,CAAC;IACD,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,SAAS,oBAAoB,CAAC,MAAiC;IAC7D,OAAO;QACL,oBAAoB,MAAM,CAAC,UAAU,EAAE;QACvC,EAAE;QACF,WAAW,MAAM,CAAC,IAAI,IAAI,SAAS,EAAE;QACrC,sBAAsB,MAAM,CAAC,WAAW,EAAE;QAC1C,oBAAoB,MAAM,CAAC,gBAAgB,EAAE;QAC7C,iCAAiC,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE;QAC7D,wBAAwB,MAAM,CAAC,OAAO,CAAC,gBAAgB,MAAM;QAC7D,EAAE;QACF,WAAW;QACX,EAAE;QACF,oBAAoB;QACpB,gBAAgB;QAChB,qCAAqC,CAAC,MAAM,CAAC,OAAO,CAAC,2BAA2B,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;QACvG,8BAA8B,CAAC,MAAM,CAAC,OAAO,CAAC,oBAAoB,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;QACzF,4CAA4C,CAAC,MAAM,CAAC,OAAO,CAAC,wBAAwB,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;QAC3G,mCAAmC,CAAC,MAAM,CAAC,OAAO,CAAC,wBAAwB,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;QAClG,sCAAsC,CAAC,MAAM,CAAC,OAAO,CAAC,2BAA2B,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;QACxG,4BAA4B,CAAC,MAAM,CAAC,OAAO,CAAC,eAAe,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;QAClF,6BAA6B,CAAC,MAAM,CAAC,OAAO,CAAC,mBAAmB,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;QACvF,wBAAwB,CAAC,MAAM,CAAC,OAAO,CAAC,cAAc,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;QAC7E,2BAA2B,CAAC,MAAM,CAAC,OAAO,CAAC,iBAAiB,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;QACnF,EAAE;QACF,sCAAsC;QACtC,EAAE;QACF,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,MAAM;YAChC,CAAC,CAAC;gBACE,8BAA8B;gBAC9B,sBAAsB;gBACtB,GAAG,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,EAAE,QAAQ,KAAK,CAAC,KAAK,CAAC,MAAM,MAAM,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC;aAC5J;YACH,CAAC,CAAC,CAAC,kEAAkE,CAAC,CAAC;QACzE,EAAE;QACF,cAAc;QACd,EAAE;QACF,2BAA2B;QAC3B,sBAAsB;QACtB,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,GAAG,CAAC,IAAI,QAAQ,GAAG,CAAC,OAAO,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;QACzI,EAAE;QACF,oBAAoB;QACpB,EAAE;QACF,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM;YAC7B,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,QAAQ,MAAM,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACrH,CAAC,CAAC,CAAC,wEAAwE,CAAC,CAAC;KAChF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,SAAS,oBAAoB,CAAC,MAAuC;IACnE,OAAO;QACL,+BAA+B;QAC/B,EAAE;QACF,mBAAmB,MAAM,CAAC,OAAO,CAAC,eAAe,EAAE;QACnD,+BAA+B,MAAM,CAAC,OAAO,CAAC,sBAAsB,MAAM;QAC1E,4BAA4B,CAAC,MAAM,CAAC,OAAO,CAAC,mBAAmB,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG;QACpF,yCAAyC,CAAC,MAAM,CAAC,OAAO,CAAC,8BAA8B,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG;QAC5G,4BAA4B,CAAC,MAAM,CAAC,OAAO,CAAC,qBAAqB,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG;QACtF,EAAE;QACF,uBAAuB;QACvB,EAAE;QACF,8FAA8F;QAC9F,2CAA2C;QAC3C,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,UAAU,MAAM,IAAI,CAAC,OAAO,CAAC,gBAAgB,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,2BAA2B,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,wBAAwB,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,MAAM,IAAI,CAAC;QACpU,EAAE;QACF,yBAAyB;QACzB,EAAE;QACF,GAAG,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;KAClH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,SAAS,cAAc,CAAC,SAAgC,EAAE,IAAiC;IACzF,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,SAAS,EAAE,CAAC;QACrC,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC;IACjE,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,eAAe,CAAC,IAAY,EAAE,KAA6C;IAClF,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACpD,IAAI,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC;QAAE,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC1D,IAAI,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC;QAAE,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACxD,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACpD,IAAI,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAClE,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,sBAAsB,CAAC,IAAY,EAAE,KAAa;IACzD,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACvE,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC;QAAE,UAAU,IAAI,GAAG,CAAC;IAC5C,IAAI,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC;QAAE,UAAU,IAAI,CAAC,CAAC;IAC7C,IAAI,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC;QAAE,UAAU,IAAI,GAAG,CAAC;IAC9C,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,gBAAgB,CAAC,WAAmB,EAAE,UAAkB;IAC/D,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,WAAW,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACpD,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,GAAG,UAAU,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC;IAClF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,YAAY,CAAC,IAAY;IAChC,OAAO,yCAAyC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AACnF,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,eAAe,CAAC,IAAY;IACnC,OAAO,iCAAiC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,8BAA8B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACnG,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,6FAA6F,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAClH,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,8EAA8E,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACnG,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,6CAA6C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,YAAY,CAAC,IAAY;IAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IAChD,OAAO,+HAA+H,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACxJ,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IAChD,OAAO,0FAA0F,CAAC,IAAI,CAAC,QAAQ,CAAC;QAC9G,kFAAkF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAClG,CAAC;AAED,SAAS,eAAe,CAAC,IAAY;IACnC,OAAO,sFAAsF,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;AACzJ,CAAC;AAED,SAAS,UAAU,CAAC,QAAgB;IAClC,OAAO,CAAC,mBAAmB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACrH,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AACzD,CAAC;AAED,SAAS,YAAY,CAAC,MAAyB;IAC7C,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACrC,CAAC;AAED,SAAS,GAAG,CAAC,MAAyB;IACpC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,KAAK,CAAC,KAAa,EAAE,KAAa;IACzC,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,MAAM,CAAC,MAAyB;IACvC,IAAI,CAAC,MAAM,CAAC,MAAM;QAAE,OAAO,CAAC,CAAC;IAC7B,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC;IAC/D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC7C,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC;QAC5B,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC9E,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,eAAe,CAAC,KAAyB,EAAE,QAAgB;IAClE,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC9F,CAAC;AAED,SAAS,KAAK,CAAC,KAAa,EAAE,GAAW,EAAE,GAAW;IACpD,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;AAC7C,CAAC"}
|
package/dist/node.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type FrontierMergeMetricCorpusReport, type FrontierMergeMetricInput, type FrontierMergeMetricOptions, type FrontierMergeMetricReport } from './index.js';
|
|
2
|
+
export interface FrontierMergeMetricGitCollectOptions extends FrontierMergeMetricOptions {
|
|
3
|
+
readonly maxCommits?: number;
|
|
4
|
+
readonly maxFileSnapshots?: number;
|
|
5
|
+
readonly maxFileBytes?: number;
|
|
6
|
+
readonly maxMergeResolutionEvents?: number;
|
|
7
|
+
readonly paths?: readonly string[];
|
|
8
|
+
}
|
|
9
|
+
export interface FrontierMergeMetricGitAnalyzeOptions extends FrontierMergeMetricGitCollectOptions {
|
|
10
|
+
readonly repository?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface FrontierMergeMetricWriteOptions {
|
|
13
|
+
readonly jsonPath?: string;
|
|
14
|
+
readonly markdownPath?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function collectGitRepoShapeInput(repoPath: string, options?: FrontierMergeMetricGitCollectOptions): FrontierMergeMetricInput;
|
|
17
|
+
export declare function analyzeGitRepository(repoPath: string, options?: FrontierMergeMetricGitAnalyzeOptions): FrontierMergeMetricReport;
|
|
18
|
+
export declare function analyzeGitRepositoryCorpus(repoPaths: readonly string[], options?: FrontierMergeMetricGitAnalyzeOptions): FrontierMergeMetricCorpusReport;
|
|
19
|
+
export declare function writeMergeMetricReport(report: FrontierMergeMetricReport | FrontierMergeMetricCorpusReport, options: FrontierMergeMetricWriteOptions): void;
|
|
20
|
+
//# sourceMappingURL=node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAGA,OAAO,EAKL,KAAK,+BAA+B,EAEpC,KAAK,wBAAwB,EAC7B,KAAK,0BAA0B,EAE/B,KAAK,yBAAyB,EAC/B,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,oCAAqC,SAAQ,0BAA0B;IACtF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAC3C,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,oCAAqC,SAAQ,oCAAoC;IAChG,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAOD,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,oCAAyC,GACjD,wBAAwB,CAQ1B;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,oCAAyC,GACjD,yBAAyB,CAM3B;AAED,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,OAAO,GAAE,oCAAyC,GACjD,+BAA+B,CAGjC;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,yBAAyB,GAAG,+BAA+B,EACnE,OAAO,EAAE,+BAA+B,GACvC,IAAI,CAGN"}
|
package/dist/node.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { basename, dirname, resolve } from 'node:path';
|
|
4
|
+
import { analyzeRepoShape, analyzeRepoShapeCorpus, renderRepoShapeMarkdown } from './index.js';
|
|
5
|
+
const defaultMaxCommits = 200;
|
|
6
|
+
const defaultMaxFileSnapshots = 2_000;
|
|
7
|
+
const defaultMaxFileBytes = 256_000;
|
|
8
|
+
const defaultMaxMergeResolutionEvents = 100;
|
|
9
|
+
export function collectGitRepoShapeInput(repoPath, options = {}) {
|
|
10
|
+
const cwd = resolve(repoPath);
|
|
11
|
+
const head = git(cwd, ['rev-parse', 'HEAD']).trim();
|
|
12
|
+
const repository = repositoryName(cwd);
|
|
13
|
+
const commits = collectGitCommits(cwd, options);
|
|
14
|
+
const files = collectGitFileSnapshots(cwd, options);
|
|
15
|
+
const resolutionEvents = collectGitMergeResolutionEvents(cwd, commits, options);
|
|
16
|
+
return { repository, head, commits, files, resolutionEvents };
|
|
17
|
+
}
|
|
18
|
+
export function analyzeGitRepository(repoPath, options = {}) {
|
|
19
|
+
const input = collectGitRepoShapeInput(repoPath, options);
|
|
20
|
+
return analyzeRepoShape({
|
|
21
|
+
...input,
|
|
22
|
+
repository: options.repository ?? input.repository
|
|
23
|
+
}, options);
|
|
24
|
+
}
|
|
25
|
+
export function analyzeGitRepositoryCorpus(repoPaths, options = {}) {
|
|
26
|
+
const reports = repoPaths.map((repoPath) => analyzeGitRepository(repoPath, options));
|
|
27
|
+
return analyzeRepoShapeCorpus(reports, options.generatedAt);
|
|
28
|
+
}
|
|
29
|
+
export function writeMergeMetricReport(report, options) {
|
|
30
|
+
if (options.jsonPath)
|
|
31
|
+
writeText(options.jsonPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
32
|
+
if (options.markdownPath)
|
|
33
|
+
writeText(options.markdownPath, `${renderRepoShapeMarkdown(report)}\n`);
|
|
34
|
+
}
|
|
35
|
+
function collectGitCommits(cwd, options) {
|
|
36
|
+
const args = [
|
|
37
|
+
'log',
|
|
38
|
+
`--max-count=${positiveInteger(options.maxCommits, defaultMaxCommits)}`,
|
|
39
|
+
'--date=iso-strict',
|
|
40
|
+
'--format=\u001e%H\u001f%P\u001f%aI\u001f%s',
|
|
41
|
+
'--numstat',
|
|
42
|
+
'--no-renames'
|
|
43
|
+
];
|
|
44
|
+
if (options.paths?.length)
|
|
45
|
+
args.push('--', ...options.paths);
|
|
46
|
+
const output = git(cwd, args);
|
|
47
|
+
const commits = [];
|
|
48
|
+
let current = null;
|
|
49
|
+
for (const rawLine of output.split('\n')) {
|
|
50
|
+
const line = rawLine.trimEnd();
|
|
51
|
+
if (!line)
|
|
52
|
+
continue;
|
|
53
|
+
if (line.startsWith('\u001e')) {
|
|
54
|
+
if (current)
|
|
55
|
+
commits.push(freezeCommit(current));
|
|
56
|
+
const [hash = '', parentsText = '', authorDate = '', subject = ''] = line.slice(1).split('\u001f');
|
|
57
|
+
current = { hash, parents: parentsText ? parentsText.split(/\s+/).filter(Boolean) : [], authorDate, subject, files: [] };
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (!current)
|
|
61
|
+
continue;
|
|
62
|
+
const [addedText, deletedText, path] = line.split('\t');
|
|
63
|
+
if (!path)
|
|
64
|
+
continue;
|
|
65
|
+
current.files.push({
|
|
66
|
+
path: normalizeNumstatPath(path),
|
|
67
|
+
additions: parseNumstatCount(addedText),
|
|
68
|
+
deletions: parseNumstatCount(deletedText)
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (current)
|
|
72
|
+
commits.push(freezeCommit(current));
|
|
73
|
+
return commits;
|
|
74
|
+
}
|
|
75
|
+
function collectGitFileSnapshots(cwd, options) {
|
|
76
|
+
const maxFiles = positiveInteger(options.maxFileSnapshots, defaultMaxFileSnapshots);
|
|
77
|
+
const maxBytes = positiveInteger(options.maxFileBytes, defaultMaxFileBytes);
|
|
78
|
+
const output = git(cwd, ['ls-files', '-z']);
|
|
79
|
+
const files = [];
|
|
80
|
+
for (const path of output.split('\0').filter(Boolean)) {
|
|
81
|
+
if (files.length >= maxFiles)
|
|
82
|
+
break;
|
|
83
|
+
const filePath = resolve(cwd, path);
|
|
84
|
+
try {
|
|
85
|
+
const stat = statSync(filePath);
|
|
86
|
+
if (!stat.isFile() || stat.size > maxBytes) {
|
|
87
|
+
files.push({ path, bytes: stat.size });
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const text = readFileSync(filePath, 'utf8');
|
|
91
|
+
files.push({ path, bytes: stat.size, lines: countLines(text) });
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// Git indexes can race with file reads in active workspaces; skip unstable paths.
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return files;
|
|
98
|
+
}
|
|
99
|
+
function collectGitMergeResolutionEvents(cwd, commits, options) {
|
|
100
|
+
const maxEvents = positiveInteger(options.maxMergeResolutionEvents, defaultMaxMergeResolutionEvents);
|
|
101
|
+
const events = [];
|
|
102
|
+
for (const commit of commits) {
|
|
103
|
+
if (events.length >= maxEvents)
|
|
104
|
+
break;
|
|
105
|
+
const parents = commit.parents?.filter(Boolean) ?? [];
|
|
106
|
+
if (parents.length < 2)
|
|
107
|
+
continue;
|
|
108
|
+
const parentDiffs = parents.map((parent) => changedPathsBetween(cwd, parent, commit.hash, options.paths));
|
|
109
|
+
if (parentDiffs.some((paths) => paths.size === 0))
|
|
110
|
+
continue;
|
|
111
|
+
const paths = [...parentDiffs[0] ?? []].filter((path) => parentDiffs.every((set) => set.has(path))).sort();
|
|
112
|
+
if (!paths.length)
|
|
113
|
+
continue;
|
|
114
|
+
events.push({
|
|
115
|
+
id: `git-merge-parent-intersection:${commit.hash}`,
|
|
116
|
+
kind: 'git-merge-parent-intersection',
|
|
117
|
+
commitHash: commit.hash,
|
|
118
|
+
parents,
|
|
119
|
+
authorDate: commit.authorDate,
|
|
120
|
+
subject: commit.subject,
|
|
121
|
+
paths,
|
|
122
|
+
evidence: 'Merge result differs from every parent on these paths; this is an observed integration signal, not proof of a text conflict.'
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
return events;
|
|
126
|
+
}
|
|
127
|
+
function changedPathsBetween(cwd, left, right, paths) {
|
|
128
|
+
const args = ['diff', '--name-only', '--diff-filter=ACDMRTUXB', left, right];
|
|
129
|
+
if (paths?.length)
|
|
130
|
+
args.push('--', ...paths);
|
|
131
|
+
try {
|
|
132
|
+
return new Set(git(cwd, args).split('\n').map(normalizeNumstatPath).filter(Boolean));
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return new Set();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function repositoryName(cwd) {
|
|
139
|
+
try {
|
|
140
|
+
const remote = git(cwd, ['remote', 'get-url', 'origin']).trim();
|
|
141
|
+
if (remote)
|
|
142
|
+
return remote.replace(/\.git$/, '');
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// Fall back to directory name.
|
|
146
|
+
}
|
|
147
|
+
return basename(cwd);
|
|
148
|
+
}
|
|
149
|
+
function git(cwd, args) {
|
|
150
|
+
return execFileSync('git', [...args], {
|
|
151
|
+
cwd,
|
|
152
|
+
encoding: 'utf8',
|
|
153
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
154
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
function freezeCommit(commit) {
|
|
158
|
+
return {
|
|
159
|
+
hash: commit.hash,
|
|
160
|
+
parents: commit.parents,
|
|
161
|
+
authorDate: commit.authorDate,
|
|
162
|
+
subject: commit.subject,
|
|
163
|
+
files: commit.files
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function normalizeNumstatPath(path) {
|
|
167
|
+
return path.replaceAll('\\', '/').replace(/^\.\//, '');
|
|
168
|
+
}
|
|
169
|
+
function parseNumstatCount(value) {
|
|
170
|
+
if (!value || value === '-')
|
|
171
|
+
return 0;
|
|
172
|
+
const parsed = Number.parseInt(value, 10);
|
|
173
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
174
|
+
}
|
|
175
|
+
function countLines(text) {
|
|
176
|
+
if (!text)
|
|
177
|
+
return 0;
|
|
178
|
+
return text.endsWith('\n') ? text.split('\n').length - 1 : text.split('\n').length;
|
|
179
|
+
}
|
|
180
|
+
function positiveInteger(value, fallback) {
|
|
181
|
+
return typeof value === 'number' && Number.isInteger(value) && value > 0 ? value : fallback;
|
|
182
|
+
}
|
|
183
|
+
function writeText(path, text) {
|
|
184
|
+
mkdirSync(dirname(resolve(path)), { recursive: true });
|
|
185
|
+
writeFileSync(path, text);
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=node.js.map
|
package/dist/node.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.js","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shapeshift-labs/frontier-merge-metrics",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Repo-shape and merge-conflict risk metrics for Git, Loom, and Frontier swarm workflows.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+ssh://git@github.com/siliconjungle/-shapeshift-labs-frontier-merge-metrics.git"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"frontier-merge-metrics": "./dist/cli.js"
|
|
13
|
+
},
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.js",
|
|
18
|
+
"default": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./node": {
|
|
21
|
+
"types": "./dist/node.d.ts",
|
|
22
|
+
"import": "./dist/node.js",
|
|
23
|
+
"default": "./dist/node.js"
|
|
24
|
+
},
|
|
25
|
+
"./package.json": "./package.json"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"README.md",
|
|
30
|
+
"LICENSE"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "node build.mjs",
|
|
34
|
+
"test": "npm run build && npm run typecheck && node test/smoke.mjs && node test/fuzz.mjs --cases 250",
|
|
35
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p test/tsconfig.json --noEmit",
|
|
36
|
+
"fuzz": "npm run build && node test/fuzz.mjs",
|
|
37
|
+
"bench": "npm run build && node benchmarks/package-bench.mjs --out benchmarks/results/frontier-merge-metrics-package-bench-latest.json",
|
|
38
|
+
"pack:dry": "npm pack --dry-run",
|
|
39
|
+
"prepack": "npm run test"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "^26.0.0",
|
|
43
|
+
"typescript": "^6.0.3"
|
|
44
|
+
}
|
|
45
|
+
}
|