gitnexus 1.6.4-rc.18 → 1.6.4-rc.19
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.
|
@@ -14,6 +14,8 @@ const DEFAULT_MATCHING = {
|
|
|
14
14
|
bm25_threshold: 0.7,
|
|
15
15
|
embedding_threshold: 0.65,
|
|
16
16
|
max_candidates_per_step: 3,
|
|
17
|
+
exclude_links_paths: [],
|
|
18
|
+
exclude_links_param_only_paths: false,
|
|
17
19
|
};
|
|
18
20
|
export function parseGroupConfig(yamlContent) {
|
|
19
21
|
const raw = yaml.load(yamlContent, { schema: yaml.JSON_SCHEMA });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { StoredContract, CrossLink } from './types.js';
|
|
1
|
+
import type { StoredContract, CrossLink, MatchingConfig } from './types.js';
|
|
2
2
|
export interface MatchResult {
|
|
3
3
|
matched: CrossLink[];
|
|
4
4
|
unmatched: StoredContract[];
|
|
@@ -8,6 +8,6 @@ export interface WildcardMatchResult {
|
|
|
8
8
|
remaining: StoredContract[];
|
|
9
9
|
}
|
|
10
10
|
export declare function normalizeContractId(id: string): string;
|
|
11
|
-
export declare function buildProviderIndex(contracts: StoredContract[]): Map<string, StoredContract[]>;
|
|
12
|
-
export declare function runExactMatch(contracts: StoredContract[], providerIndex?: Map<string, StoredContract[]
|
|
11
|
+
export declare function buildProviderIndex(contracts: StoredContract[], matchingConfig?: MatchingConfig): Map<string, StoredContract[]>;
|
|
12
|
+
export declare function runExactMatch(contracts: StoredContract[], providerIndex?: Map<string, StoredContract[]>, matchingConfig?: MatchingConfig): MatchResult;
|
|
13
13
|
export declare function runWildcardMatch(unmatched: StoredContract[], providerIndex: Map<string, StoredContract[]>): WildcardMatchResult;
|
|
@@ -1,6 +1,43 @@
|
|
|
1
1
|
function isGrpcWildcard(cid) {
|
|
2
2
|
return cid.startsWith('grpc::') && cid.endsWith('/*');
|
|
3
3
|
}
|
|
4
|
+
/**
|
|
5
|
+
* Detect HTTP contracts that are too generic or infrastructure-level to
|
|
6
|
+
* produce meaningful cross-repo links. These are still extracted (useful
|
|
7
|
+
* for documentation / route maps) but excluded from cross-link matching.
|
|
8
|
+
*
|
|
9
|
+
* Two categories:
|
|
10
|
+
* 1. Health-check / readiness endpoints — every service has one, matching
|
|
11
|
+
* them produces N×M false links.
|
|
12
|
+
* 2. Param-only paths — routes like `/{param}` or `/{param}/{param}` that
|
|
13
|
+
* collapse to a single catch-all after normalization. These match any
|
|
14
|
+
* service with a similar shape, producing false positives.
|
|
15
|
+
*
|
|
16
|
+
* Both are configurable via matching.exclude_links_paths and
|
|
17
|
+
* matching.exclude_links_param_only_paths in group.yaml.
|
|
18
|
+
*/
|
|
19
|
+
function buildNoisyContractFilter(matchingConfig) {
|
|
20
|
+
const excludePaths = matchingConfig?.exclude_links_paths?.length
|
|
21
|
+
? new Set(matchingConfig.exclude_links_paths.map((p) => p.replace(/\/+$/, '')))
|
|
22
|
+
: new Set();
|
|
23
|
+
const excludeParamOnly = matchingConfig?.exclude_links_param_only_paths === true;
|
|
24
|
+
return function isNoisyHttpContract(contractId) {
|
|
25
|
+
if (!contractId.startsWith('http::'))
|
|
26
|
+
return false;
|
|
27
|
+
const parts = contractId.split('::');
|
|
28
|
+
if (parts.length < 3)
|
|
29
|
+
return false;
|
|
30
|
+
const pathPart = parts.slice(2).join('::').replace(/\/+$/, '');
|
|
31
|
+
if (excludePaths.has(pathPart))
|
|
32
|
+
return true;
|
|
33
|
+
if (excludeParamOnly) {
|
|
34
|
+
const segments = pathPart.split('/').filter(Boolean);
|
|
35
|
+
if (segments.length > 0 && segments.every((s) => s === '{param}'))
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
4
41
|
export function normalizeContractId(id) {
|
|
5
42
|
const colonIdx = id.indexOf('::');
|
|
6
43
|
if (colonIdx === -1)
|
|
@@ -74,8 +111,9 @@ function findMatchingKeys(contractId, index) {
|
|
|
74
111
|
}
|
|
75
112
|
return [];
|
|
76
113
|
}
|
|
77
|
-
export function buildProviderIndex(contracts) {
|
|
78
|
-
const
|
|
114
|
+
export function buildProviderIndex(contracts, matchingConfig) {
|
|
115
|
+
const isNoisy = buildNoisyContractFilter(matchingConfig);
|
|
116
|
+
const providers = contracts.filter((c) => c.role === 'provider' && !isNoisy(c.contractId));
|
|
79
117
|
const index = new Map();
|
|
80
118
|
for (const p of providers) {
|
|
81
119
|
const key = normalizeContractId(p.contractId);
|
|
@@ -85,10 +123,10 @@ export function buildProviderIndex(contracts) {
|
|
|
85
123
|
}
|
|
86
124
|
return index;
|
|
87
125
|
}
|
|
88
|
-
export function runExactMatch(contracts, providerIndex) {
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
const consumers = contracts.filter((c) => c.role === 'consumer' && !isGrpcWildcard(c.contractId));
|
|
126
|
+
export function runExactMatch(contracts, providerIndex, matchingConfig) {
|
|
127
|
+
const isNoisy = buildNoisyContractFilter(matchingConfig);
|
|
128
|
+
const index = providerIndex ?? buildProviderIndex(contracts, matchingConfig);
|
|
129
|
+
const consumers = contracts.filter((c) => c.role === 'consumer' && !isGrpcWildcard(c.contractId) && !isNoisy(c.contractId));
|
|
92
130
|
const matched = [];
|
|
93
131
|
const matchedConsumerIds = new Set();
|
|
94
132
|
const matchedProviderIds = new Set();
|
|
@@ -129,6 +167,8 @@ export function runExactMatch(contracts, providerIndex) {
|
|
|
129
167
|
const normalUnmatched = contracts.filter((c) => {
|
|
130
168
|
if (isGrpcWildcard(c.contractId))
|
|
131
169
|
return false; // excluded from exact, handled separately
|
|
170
|
+
if (isNoisy(c.contractId))
|
|
171
|
+
return false; // excluded from matching — don't surface as unmatched
|
|
132
172
|
const id = `${c.repo}::${c.contractId}`;
|
|
133
173
|
return c.role === 'provider' ? !matchedProviderIds.has(id) : !matchedConsumerIds.has(id);
|
|
134
174
|
});
|
|
@@ -85,6 +85,8 @@ matching:
|
|
|
85
85
|
bm25_threshold: 0.7
|
|
86
86
|
embedding_threshold: 0.65
|
|
87
87
|
max_candidates_per_step: 3
|
|
88
|
+
# exclude_links_paths: [/ping, /health, /healthcheck]
|
|
89
|
+
# exclude_links_param_only_paths: false
|
|
88
90
|
`;
|
|
89
91
|
await fsp.writeFile(path.join(groupDir, 'group.yaml'), template, 'utf-8');
|
|
90
92
|
return groupDir;
|
package/dist/core/group/sync.js
CHANGED
|
@@ -168,7 +168,7 @@ export async function syncGroup(config, opts) {
|
|
|
168
168
|
console.log(` manifest: ${manifestCrossLinks.length} cross-links from ${config.links.length} declared links`);
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
|
-
const { matched, unmatched } = runExactMatch(autoContracts);
|
|
171
|
+
const { matched, unmatched } = runExactMatch(autoContracts, undefined, config.matching);
|
|
172
172
|
// Dedupe cross-links. Manifest contracts participate in runExactMatch, so a
|
|
173
173
|
// manifest-declared link can also emit a matchType:'exact' CrossLink with the
|
|
174
174
|
// same endpoints. Prefer the manifest version — it reflects operator intent
|
|
@@ -29,6 +29,24 @@ export interface MatchingConfig {
|
|
|
29
29
|
bm25_threshold: number;
|
|
30
30
|
embedding_threshold: number;
|
|
31
31
|
max_candidates_per_step: number;
|
|
32
|
+
/**
|
|
33
|
+
* HTTP paths to exclude from cross-link matching. Contracts at these paths
|
|
34
|
+
* are still extracted and visible in the registry, but they don't produce
|
|
35
|
+
* cross-repo links. Useful for health-check endpoints (`/ping`, `/health`)
|
|
36
|
+
* that every service exposes and would otherwise create N×M false links.
|
|
37
|
+
* Trailing slashes are normalized before comparison.
|
|
38
|
+
* @default []
|
|
39
|
+
*/
|
|
40
|
+
exclude_links_paths?: string[];
|
|
41
|
+
/**
|
|
42
|
+
* When `true`, exclude HTTP routes where every path segment is `{param}`
|
|
43
|
+
* (e.g. `/{param}`, `/{param}/{param}`) from cross-link matching. Mixed
|
|
44
|
+
* routes like `/users/{param}` are not affected. These param-only routes
|
|
45
|
+
* collapse to a single catch-all after normalization and produce false
|
|
46
|
+
* positives across unrelated services.
|
|
47
|
+
* @default false
|
|
48
|
+
*/
|
|
49
|
+
exclude_links_param_only_paths?: boolean;
|
|
32
50
|
}
|
|
33
51
|
export interface SymbolRef {
|
|
34
52
|
filePath: string;
|
package/package.json
CHANGED