gitnexus 1.6.2-rc.3 → 1.6.2-rc.4

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.
@@ -68,14 +68,29 @@ export function manifestSymbolUid(repo, contractId) {
68
68
  }
69
69
  export class ManifestExtractor {
70
70
  async extractFromManifest(links, dbExecutors) {
71
- const contracts = [];
72
- const crossLinks = [];
73
- for (const link of links) {
71
+ const resolveCache = new Map();
72
+ const resolveOnce = (repo, link) => {
73
+ const key = `${repo}\u0000${link.type}\u0000${link.contract}`;
74
+ let pending = resolveCache.get(key);
75
+ if (!pending) {
76
+ pending = this.resolveSymbol(repo, link, dbExecutors);
77
+ resolveCache.set(key, pending);
78
+ }
79
+ return pending;
80
+ };
81
+ const perLink = await Promise.all(links.map(async (link) => {
74
82
  const contractId = this.buildContractId(link.type, link.contract);
75
83
  const providerRepo = link.role === 'provider' ? link.from : link.to;
76
84
  const consumerRepo = link.role === 'provider' ? link.to : link.from;
77
- const providerSymbol = await this.resolveSymbol(providerRepo, link, dbExecutors);
78
- const consumerSymbol = await this.resolveSymbol(consumerRepo, link, dbExecutors);
85
+ const [providerSymbol, consumerSymbol] = await Promise.all([
86
+ resolveOnce(providerRepo, link),
87
+ resolveOnce(consumerRepo, link),
88
+ ]);
89
+ return { link, contractId, providerRepo, consumerRepo, providerSymbol, consumerSymbol };
90
+ }));
91
+ const contracts = [];
92
+ const crossLinks = [];
93
+ for (const { link, contractId, providerRepo, consumerRepo, providerSymbol, consumerSymbol, } of perLink) {
79
94
  const providerRef = providerSymbol || { filePath: '', name: link.contract };
80
95
  const consumerRef = consumerSymbol || { filePath: '', name: link.contract };
81
96
  // When the resolver finds a real graph symbol we keep its uid, otherwise
@@ -6,6 +6,7 @@ import { readRegistry } from '../../storage/repo-manager.js';
6
6
  import { HttpRouteExtractor } from './extractors/http-route-extractor.js';
7
7
  import { GrpcExtractor } from './extractors/grpc-extractor.js';
8
8
  import { TopicExtractor } from './extractors/topic-extractor.js';
9
+ import { ManifestExtractor } from './extractors/manifest-extractor.js';
9
10
  import { runExactMatch } from './matching.js';
10
11
  import { detectServiceBoundaries, assignService } from './service-boundary-detector.js';
11
12
  import { writeContractRegistry } from './storage.js';
@@ -34,10 +35,28 @@ function defaultResolveHandle(allEntries) {
34
35
  };
35
36
  };
36
37
  }
38
+ /**
39
+ * Dedupe cross-links that point from the same consumer endpoint to the same
40
+ * provider endpoint for the same contract. Preserves first-seen order so the
41
+ * caller controls precedence (e.g., pass manifest links first).
42
+ */
43
+ function dedupeCrossLinks(links) {
44
+ const seen = new Set();
45
+ const out = [];
46
+ for (const link of links) {
47
+ const key = `${link.from.repo}::${link.from.symbolUid}|${link.to.repo}::${link.to.symbolUid}|${link.type}|${link.contractId}`;
48
+ if (seen.has(key))
49
+ continue;
50
+ seen.add(key);
51
+ out.push(link);
52
+ }
53
+ return out;
54
+ }
37
55
  export async function syncGroup(config, opts) {
38
56
  const missingRepos = [];
39
57
  const repoSnapshots = {};
40
58
  let autoContracts = [];
59
+ let manifestCrossLinks = [];
41
60
  let dbExecutors;
42
61
  const eo = opts?.extractorOverride;
43
62
  if (eo && eo.length === 0) {
@@ -124,8 +143,37 @@ export async function syncGroup(config, opts) {
124
143
  }
125
144
  }
126
145
  }
146
+ // Process manifest links declared in group.yaml.
147
+ // ManifestExtractor is fully implemented but was never wired into this
148
+ // pipeline — config.links were parsed and validated but silently dropped.
149
+ // Placed after the DB try/finally: resolveSymbol falls back to synthetic
150
+ // UIDs when dbExecutors is undefined or a pool is closed, so cross-links
151
+ // are always generated regardless of whether real DB executors are available.
152
+ if (config.links.length > 0) {
153
+ // Warn about dangling links that reference repos not declared in config.repos.
154
+ // They still generate cross-links via synthetic UIDs (determinism is preserved),
155
+ // but the operator probably meant something that now silently does nothing useful.
156
+ const knownRepos = new Set(Object.keys(config.repos));
157
+ for (const link of config.links) {
158
+ const dangling = [link.from, link.to].filter((r) => !knownRepos.has(r));
159
+ if (dangling.length > 0) {
160
+ console.warn(`[group/sync] manifest link ${link.type}:${link.contract} references repos not in config.repos: ${dangling.join(', ')} — cross-links will use synthetic UIDs`);
161
+ }
162
+ }
163
+ const manifestEx = new ManifestExtractor();
164
+ const manifestResult = await manifestEx.extractFromManifest(config.links, dbExecutors);
165
+ autoContracts.push(...manifestResult.contracts);
166
+ manifestCrossLinks = manifestResult.crossLinks;
167
+ if (opts?.verbose) {
168
+ console.log(` manifest: ${manifestCrossLinks.length} cross-links from ${config.links.length} declared links`);
169
+ }
170
+ }
127
171
  const { matched, unmatched } = runExactMatch(autoContracts);
128
- const crossLinks = matched;
172
+ // Dedupe cross-links. Manifest contracts participate in runExactMatch, so a
173
+ // manifest-declared link can also emit a matchType:'exact' CrossLink with the
174
+ // same endpoints. Prefer the manifest version — it reflects operator intent
175
+ // and carries matchType:'manifest' which downstream consumers may rely on.
176
+ const crossLinks = dedupeCrossLinks([...manifestCrossLinks, ...matched]);
129
177
  const allContracts = autoContracts;
130
178
  const registry = {
131
179
  version: 1,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.2-rc.3",
3
+ "version": "1.6.2-rc.4",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",