@0xsarwagya/ontoly-core 0.1.0-alpha.1 → 0.1.0-alpha.13

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/dist/index.js CHANGED
@@ -1,3 +1,1018 @@
1
+ // src/semantic-index.ts
2
+ var SEMANTIC_INDEX_VERSION = "1.0.0";
3
+ var STOP_WORDS = /* @__PURE__ */ new Set([
4
+ "a",
5
+ "an",
6
+ "and",
7
+ "are",
8
+ "as",
9
+ "at",
10
+ "be",
11
+ "by",
12
+ "for",
13
+ "from",
14
+ "how",
15
+ "i",
16
+ "in",
17
+ "into",
18
+ "is",
19
+ "it",
20
+ "of",
21
+ "on",
22
+ "or",
23
+ "that",
24
+ "the",
25
+ "this",
26
+ "to",
27
+ "use",
28
+ "uses",
29
+ "what",
30
+ "when",
31
+ "where",
32
+ "which",
33
+ "who",
34
+ "with"
35
+ ]);
36
+ var FRAMEWORK_SUFFIXES = /* @__PURE__ */ new Set([
37
+ "app",
38
+ "application",
39
+ "client",
40
+ "config",
41
+ "configuration",
42
+ "controller",
43
+ "dto",
44
+ "entity",
45
+ "factory",
46
+ "guard",
47
+ "handler",
48
+ "hook",
49
+ "interceptor",
50
+ "middleware",
51
+ "module",
52
+ "pipe",
53
+ "provider",
54
+ "repository",
55
+ "resolver",
56
+ "route",
57
+ "schema",
58
+ "service",
59
+ "store",
60
+ "table",
61
+ "type"
62
+ ]);
63
+ var CATEGORY_TYPES = {
64
+ concept: [],
65
+ symbol: ["Function", "Method", "Class", "Interface", "TypeAlias", "Enum", "Field", "Service", "Provider", "Controller", "Repository", "Resource", "Model", "Route"],
66
+ feature: ["Route", "Controller", "Service", "Provider", "Module", "Package", "Repository", "Model", "Resource", "Class", "Interface", "TypeAlias", "Function", "Method", "Operation"],
67
+ configuration: ["Configuration", "EnvironmentVariable", "BuildTarget", "Script", "Task"],
68
+ environment: ["EnvironmentVariable"],
69
+ route: ["Route", "Operation", "Controller"],
70
+ entrypoint: ["Route", "Operation", "Controller", "Function", "Method", "Module", "Script", "Task", "Application"],
71
+ repository: ["Workspace", "Application", "Package", "Module", "Framework", "Dependency", "Configuration"]
72
+ };
73
+ var RELATIONSHIP_CAPABILITIES = [
74
+ { capability: "RequestTrace", terms: ["route", "request", "endpoint", "api", "handler", "controller", "login", "logout"] },
75
+ { capability: "ConfigurationUsage", terms: ["config", "configuration", "setting", "environment", "env", "secret"] },
76
+ { capability: "EnvironmentUsage", terms: ["env", "environment", "secret", "variable"] },
77
+ { capability: "AuthenticationFlow", terms: ["auth", "authentication", "login", "signin", "jwt", "session", "token"] },
78
+ { capability: "AuthorizationFlow", terms: ["authorization", "permission", "guard", "role", "policy", "authorize"] },
79
+ { capability: "ImpactAnalysis", terms: ["remove", "delete", "change", "break", "impact", "depends", "dependent"] },
80
+ { capability: "DependencyAnalysis", terms: ["dependency", "depends", "imports", "uses", "injects", "providers"] },
81
+ { capability: "FeatureTouchpoints", terms: ["feature", "owner", "touchpoint", "where", "find", "locate"] }
82
+ ];
83
+ var SEMANTIC_EXPANSIONS = {
84
+ api: ["route", "endpoint", "operation", "controller"],
85
+ auth: ["authentication", "authorize", "authorization", "identity", "jwt", "login", "session", "signin", "token"],
86
+ authentication: ["auth", "identity", "jwt", "login", "session", "signin", "token"],
87
+ authorize: ["authorization", "permission", "guard"],
88
+ authorization: ["authorize", "permission", "guard", "role", "policy"],
89
+ average: ["averages", "aggregate", "aggregation", "mean", "statistics"],
90
+ averages: ["average", "aggregate", "aggregation", "mean", "statistics"],
91
+ batch: ["bulk", "group", "job", "pipeline", "data"],
92
+ bulk: ["batch", "group", "job", "pipeline", "data"],
93
+ config: ["configuration", "setting", "settings"],
94
+ configuration: ["config", "setting", "settings"],
95
+ controller: ["handler", "route", "endpoint"],
96
+ data: ["resource", "payload", "record", "model"],
97
+ database: ["db", "repository", "persistence", "prisma", "table"],
98
+ db: ["database", "repository", "persistence", "table"],
99
+ duration: ["time", "period", "interval", "sleep", "threshold"],
100
+ endpoint: ["api", "route", "operation"],
101
+ env: ["environment", "variable", "secret", "configuration"],
102
+ environment: ["env", "variable", "secret", "configuration"],
103
+ fhir: ["resource", "healthcare"],
104
+ handler: ["controller", "route", "endpoint"],
105
+ identity: ["auth", "authentication", "user", "session"],
106
+ jwt: ["auth", "authentication", "token", "session"],
107
+ login: ["auth", "authentication", "signin", "session", "jwt"],
108
+ notification: ["notifications", "alert", "event", "message"],
109
+ notifications: ["notification", "alert", "event", "message"],
110
+ observation: ["observations", "signal", "metric", "measurement", "statistic", "fhir"],
111
+ observations: ["observation", "signal", "metric", "measurement", "statistic", "fhir"],
112
+ patient: ["resident", "user", "subject"],
113
+ permission: ["authorization", "authorize", "guard", "role"],
114
+ plan: ["definition", "resource", "fhir"],
115
+ plandefinition: ["plan", "definition", "resource", "fhir"],
116
+ provider: ["service", "injectable", "dependency"],
117
+ repo: ["repository", "persistence", "database"],
118
+ repository: ["repo", "persistence", "database"],
119
+ resident: ["patient", "user", "subject"],
120
+ route: ["api", "endpoint", "operation", "handler"],
121
+ service: ["provider", "injectable", "dependency"],
122
+ session: ["auth", "authentication", "login", "signin", "jwt"],
123
+ signin: ["auth", "authentication", "login", "session"],
124
+ sleep: ["duration", "threshold", "thresholds", "statistic", "statistics", "observation"],
125
+ signal: ["signals", "metric", "measurement", "event"],
126
+ signals: ["signal", "metric", "measurement", "event"],
127
+ statistic: ["statistics", "average", "aggregate"],
128
+ statistics: ["statistic", "average", "aggregate"],
129
+ threshold: ["thresholds", "limit", "configuration", "setting"],
130
+ thresholds: ["threshold", "limit", "configuration", "setting"],
131
+ token: ["jwt", "auth", "authentication", "session"]
132
+ };
133
+ var SEMANTIC_METADATA_DEPTH_LIMIT = 3;
134
+ var SEMANTIC_METADATA_ARRAY_LIMIT = 12;
135
+ var SEMANTIC_METADATA_ENTRY_LIMIT = 24;
136
+ var SEMANTIC_METADATA_VALUES_LIMIT = 80;
137
+ var SEMANTIC_TEXT_LIMIT = 240;
138
+ function createSemanticIndex(graph) {
139
+ const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
140
+ const incoming = groupEdges(graph.edges, "to");
141
+ const outgoing = groupEdges(graph.edges, "from");
142
+ const entries = graph.nodes.map((node) => createEntry(graph, node, nodeById, incoming, outgoing)).sort(compareEntries);
143
+ const invertedIndex = buildInvertedIndex(entries);
144
+ const vocabulary = buildVocabulary(entries);
145
+ const statistics = {
146
+ entries: entries.length,
147
+ aliases: sum(entries.map((entry) => entry.aliases.length)),
148
+ keywords: sum(entries.map((entry) => entry.keywords.length)),
149
+ vocabulary: vocabulary.length,
150
+ graphHash: graph.metadata.deterministicHash
151
+ };
152
+ const indexWithoutMetadata = {
153
+ version: SEMANTIC_INDEX_VERSION,
154
+ graphVersion: graph.version,
155
+ graphHash: graph.metadata.deterministicHash,
156
+ repository: {
157
+ name: graph.repository.name,
158
+ root: graph.repository.root,
159
+ packageName: graph.repository.packageName
160
+ },
161
+ entries,
162
+ entryIds: entries.map((entry) => entry.stableId),
163
+ invertedIndex,
164
+ vocabulary,
165
+ metadata: {
166
+ generatedAt: "1970-01-01T00:00:00.000Z",
167
+ deterministicHash: "",
168
+ statistics
169
+ }
170
+ };
171
+ const deterministicHash = hashSemanticIndex(indexWithoutMetadata);
172
+ return {
173
+ ...indexWithoutMetadata,
174
+ metadata: {
175
+ ...indexWithoutMetadata.metadata,
176
+ deterministicHash
177
+ }
178
+ };
179
+ }
180
+ function hashSemanticIndex(index) {
181
+ const entryHashes = index.entries.map((entry) => stableHash(stableStringify(entry))).sort();
182
+ const invertedHashes = Object.entries(index.invertedIndex).sort(([left], [right]) => left.localeCompare(right)).map(([term, ids]) => stableHash(stableStringify({ term, ids })));
183
+ const vocabularyHashes = index.vocabulary.map((term) => stableHash(stableStringify(term))).sort();
184
+ return stableHash(stableStringify({
185
+ version: index.version,
186
+ graphVersion: index.graphVersion,
187
+ graphHash: index.graphHash,
188
+ repository: index.repository,
189
+ entryIds: index.entryIds,
190
+ entries: {
191
+ count: entryHashes.length,
192
+ hashes: entryHashes
193
+ },
194
+ invertedIndex: {
195
+ count: invertedHashes.length,
196
+ hashes: invertedHashes
197
+ },
198
+ vocabulary: {
199
+ count: vocabularyHashes.length,
200
+ hashes: vocabularyHashes
201
+ },
202
+ metadata: {
203
+ generatedAt: index.metadata.generatedAt,
204
+ statistics: index.metadata.statistics
205
+ }
206
+ }));
207
+ }
208
+ function validateSemanticIndex(index, graph) {
209
+ const issues = [];
210
+ if (index.version !== SEMANTIC_INDEX_VERSION) {
211
+ issues.push(`Unsupported semantic index version ${index.version}.`);
212
+ }
213
+ if (graph && index.graphHash !== graph.metadata.deterministicHash) {
214
+ issues.push("Semantic index graph hash does not match Software Graph hash.");
215
+ }
216
+ const seen = /* @__PURE__ */ new Set();
217
+ for (const entry of index.entries) {
218
+ if (seen.has(entry.stableId)) {
219
+ issues.push(`Duplicate semantic index entry ${entry.stableId}.`);
220
+ }
221
+ seen.add(entry.stableId);
222
+ }
223
+ for (const [term, ids] of Object.entries(index.invertedIndex)) {
224
+ for (const id of ids) {
225
+ if (!seen.has(id)) {
226
+ issues.push(`Inverted index term "${term}" references missing entry ${id}.`);
227
+ }
228
+ }
229
+ }
230
+ return issues.sort();
231
+ }
232
+ function normalizeIntent(input) {
233
+ const raw = input.trim();
234
+ const tokens = tokenize(raw);
235
+ const phrases = phraseVariants(raw, tokens);
236
+ const expandedTerms = uniqueStrings([
237
+ ...tokens,
238
+ ...phrases,
239
+ ...tokens.flatMap(expansionsFor),
240
+ ...tokens.map(singularize),
241
+ ...tokens.map(pluralize)
242
+ ]);
243
+ return {
244
+ raw,
245
+ normalized: normalizePhrase(raw),
246
+ tokens,
247
+ phrases,
248
+ expandedTerms
249
+ };
250
+ }
251
+ function resolveIntent(index, query, options = {}) {
252
+ const startedAt = performanceNow();
253
+ const category = options.category ?? "concept";
254
+ const intent = normalizeIntent(query);
255
+ const candidates = rankCandidates(index, intent, {
256
+ ...options,
257
+ category
258
+ });
259
+ const confidence = candidates[0]?.confidence ?? 0;
260
+ const matchedConcepts = uniqueStrings(candidates.flatMap((candidate) => candidate.matchedTerms)).slice(0, 20);
261
+ return {
262
+ query,
263
+ category,
264
+ intent,
265
+ matchedConcepts,
266
+ candidates,
267
+ confidence,
268
+ recommendedCapability: recommendCapability(intent, candidates, category),
269
+ evidence: candidates.slice(0, 5).map(
270
+ (candidate) => `${candidate.displayName} (${candidate.kind}) matched ${candidate.matchedTerms.join(", ") || "graph context"} with score ${candidate.score.toFixed(2)}.`
271
+ ),
272
+ latencyMs: round(performanceNow() - startedAt, 3)
273
+ };
274
+ }
275
+ function findConcept(index, query, options = {}) {
276
+ return resolveIntent(index, query, { ...options, category: "concept" });
277
+ }
278
+ function findSymbol(index, query, options = {}) {
279
+ return resolveIntent(index, query, { ...options, category: "symbol", kinds: options.kinds ?? CATEGORY_TYPES.symbol });
280
+ }
281
+ function findFeature(index, query, options = {}) {
282
+ return resolveIntent(index, query, { ...options, category: "feature", kinds: options.kinds ?? CATEGORY_TYPES.feature });
283
+ }
284
+ function findConfiguration(index, query, options = {}) {
285
+ return resolveIntent(index, query, {
286
+ ...options,
287
+ category: "configuration",
288
+ kinds: options.kinds ?? CATEGORY_TYPES.configuration
289
+ });
290
+ }
291
+ function findEnvironment(index, query, options = {}) {
292
+ return resolveIntent(index, query, { ...options, category: "environment", kinds: options.kinds ?? CATEGORY_TYPES.environment });
293
+ }
294
+ function findRoute(index, query, options = {}) {
295
+ return resolveIntent(index, query, { ...options, category: "route", kinds: options.kinds ?? CATEGORY_TYPES.route });
296
+ }
297
+ function findEntryPoint(index, query, options = {}) {
298
+ return resolveIntent(index, query, { ...options, category: "entrypoint", kinds: options.kinds ?? CATEGORY_TYPES.entrypoint });
299
+ }
300
+ function findRepositoryConcept(index, query, options = {}) {
301
+ return resolveIntent(index, query, { ...options, category: "repository", kinds: options.kinds ?? CATEGORY_TYPES.repository });
302
+ }
303
+ function createEntry(graph, node, nodeById, incoming, outgoing) {
304
+ const incomingEdges = incoming.get(node.id) ?? [];
305
+ const outgoingEdges = outgoing.get(node.id) ?? [];
306
+ const neighbors = uniqueStrings([
307
+ ...incomingEdges.map((edge) => edge.from),
308
+ ...outgoingEdges.map((edge) => edge.to)
309
+ ]);
310
+ const neighborNodes = neighbors.map((id) => nodeById.get(id)).filter(isNode);
311
+ const parentChain = parentChainFor(node, nodeById, incomingEdges);
312
+ const aliasSource = aliasSourceValues(graph, node, neighborNodes, parentChain);
313
+ const aliases = generateAliases(aliasSource, node);
314
+ const keywords = generateKeywords(aliasSource, node);
315
+ const degree = incomingEdges.length + outgoingEdges.length;
316
+ return withOptionalProperties({
317
+ stableId: node.id,
318
+ displayName: node.name,
319
+ normalizedName: normalizePhrase(node.name),
320
+ kind: node.type,
321
+ relationships: {
322
+ incoming: countEdgesByType(incomingEdges),
323
+ outgoing: countEdgesByType(outgoingEdges),
324
+ degree,
325
+ neighborIds: neighbors,
326
+ neighborNames: uniqueStrings(neighborNodes.map((neighbor) => neighbor.name))
327
+ },
328
+ aliases,
329
+ keywords,
330
+ parentChain,
331
+ importance: nodeImportance(node, degree, incomingEdges.length),
332
+ usageFrequency: incomingEdges.length,
333
+ architectureLayer: architectureLayer(node)
334
+ }, {
335
+ module: containingName(parentChain, "Module"),
336
+ package: node.package ?? stringMetadata(node.metadata, "package") ?? stringMetadata(node.metadata, "packageName"),
337
+ namespace: stringMetadata(node.metadata, "namespace"),
338
+ framework: stringMetadata(node.metadata, "framework") ?? frameworkFromNeighbors(neighborNodes),
339
+ filePath: node.file ? normalizePath(node.file) : void 0,
340
+ folderPath: node.file ? folderPath(node.file) : void 0,
341
+ documentation: deterministicDocumentation(node.metadata),
342
+ comments: deterministicComments(node.metadata)
343
+ });
344
+ }
345
+ function aliasSourceValues(graph, node, neighbors, parentChain) {
346
+ const metadataValues = metadataTextValues(node.metadata);
347
+ const routeValues = routeTextValues(node);
348
+ const envValues = node.type === "EnvironmentVariable" ? [node.name, node.id.replace(/^env:/, "")] : [];
349
+ return uniqueStrings([
350
+ node.id,
351
+ node.name,
352
+ node.file ?? "",
353
+ node.package ?? "",
354
+ ...parentChain,
355
+ ...metadataValues,
356
+ ...routeValues,
357
+ ...envValues,
358
+ ...neighbors.map((neighbor) => neighbor.name),
359
+ ...neighbors.map((neighbor) => neighbor.id)
360
+ ].filter(Boolean));
361
+ }
362
+ function generateAliases(values, node) {
363
+ const aliases = /* @__PURE__ */ new Set();
364
+ const primaryTokens = new Set(tokenize(node.name));
365
+ for (const value of values) {
366
+ const tokens = tokenize(value);
367
+ const normalized = normalizePhrase(value);
368
+ if (normalized) {
369
+ aliases.add(normalized);
370
+ }
371
+ if (tokens.length > 0) {
372
+ aliases.add(tokens.join(" "));
373
+ aliases.add(tokens.join(""));
374
+ aliases.add(tokens.join("-"));
375
+ aliases.add(tokens.join("_"));
376
+ const withoutSuffix = stripFrameworkSuffix(tokens);
377
+ if (withoutSuffix.length > 0 && withoutSuffix.length !== tokens.length) {
378
+ aliases.add(withoutSuffix.join(" "));
379
+ aliases.add(withoutSuffix.join(""));
380
+ }
381
+ for (const token of tokens) {
382
+ aliases.add(token);
383
+ aliases.add(singularize(token));
384
+ aliases.add(pluralize(token));
385
+ if (primaryTokens.has(token)) {
386
+ for (const expansion of expansionsFor(token)) {
387
+ aliases.add(expansion);
388
+ }
389
+ }
390
+ }
391
+ }
392
+ }
393
+ for (const typeAlias of aliasesForNodeType(node.type)) {
394
+ aliases.add(typeAlias);
395
+ }
396
+ if (isDtoLikeNode(node)) {
397
+ aliases.add("dto");
398
+ aliases.add("data transfer object");
399
+ aliases.add("contract");
400
+ aliases.add("payload");
401
+ }
402
+ if (isTestNode(node)) {
403
+ aliases.add("test");
404
+ aliases.add("spec");
405
+ aliases.add("coverage");
406
+ }
407
+ if (isFeatureModuleNode(node)) {
408
+ aliases.add("feature");
409
+ aliases.add("feature module");
410
+ }
411
+ return uniqueStrings([...aliases].filter((alias) => alias.length > 1));
412
+ }
413
+ function generateKeywords(values, node) {
414
+ const keywords = /* @__PURE__ */ new Set();
415
+ for (const value of values) {
416
+ for (const token of tokenize(value)) {
417
+ keywords.add(token);
418
+ keywords.add(singularize(token));
419
+ }
420
+ }
421
+ keywords.add(node.type.toLowerCase());
422
+ for (const alias of aliasesForNodeType(node.type)) {
423
+ keywords.add(alias);
424
+ }
425
+ if (isDtoLikeNode(node)) {
426
+ keywords.add("dto");
427
+ keywords.add("contract");
428
+ keywords.add("payload");
429
+ }
430
+ if (isTestNode(node)) {
431
+ keywords.add("test");
432
+ keywords.add("spec");
433
+ }
434
+ if (isFeatureModuleNode(node)) {
435
+ keywords.add("feature");
436
+ }
437
+ return uniqueStrings([...keywords].filter((keyword) => keyword.length > 1));
438
+ }
439
+ function buildInvertedIndex(entries) {
440
+ const inverted = /* @__PURE__ */ new Map();
441
+ for (const entry of entries) {
442
+ for (const term of uniqueStrings([entry.normalizedName, ...entry.aliases, ...entry.keywords])) {
443
+ addInverted(inverted, term, entry.stableId);
444
+ for (const token of tokenize(term)) {
445
+ addInverted(inverted, token, entry.stableId);
446
+ }
447
+ }
448
+ }
449
+ return Object.fromEntries(
450
+ [...inverted.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([term, ids]) => [term, [...ids].sort()])
451
+ );
452
+ }
453
+ function buildVocabulary(entries) {
454
+ const terms = /* @__PURE__ */ new Map();
455
+ for (const entry of entries) {
456
+ for (const term of uniqueStrings([...entry.keywords, ...entry.aliases.filter((alias) => !alias.includes(" "))])) {
457
+ if (term.length < 2 || STOP_WORDS.has(term)) {
458
+ continue;
459
+ }
460
+ const current = terms.get(term) ?? { count: 0, nodeIds: /* @__PURE__ */ new Set(), kinds: /* @__PURE__ */ new Set() };
461
+ current.count += 1;
462
+ current.nodeIds.add(entry.stableId);
463
+ current.kinds.add(entry.kind);
464
+ terms.set(term, current);
465
+ }
466
+ }
467
+ return [...terms.entries()].map(([term, value]) => ({
468
+ term,
469
+ frequency: value.count,
470
+ nodeIds: [...value.nodeIds].sort(),
471
+ kinds: [...value.kinds].sort()
472
+ })).sort((left, right) => right.frequency - left.frequency || left.term.localeCompare(right.term));
473
+ }
474
+ function rankCandidates(index, intent, options) {
475
+ const candidateIds = candidateIdsFor(index, intent);
476
+ const entryById = new Map(index.entries.map((entry) => [entry.stableId, entry]));
477
+ const kinds = options.kinds?.length ? new Set(options.kinds) : categoryTypeSet(options.category);
478
+ const candidates = candidateIds.map((id) => entryById.get(id)).filter(isEntry).filter((entry) => includeEntryForCategory(entry, intent, options.category)).filter((entry) => !kinds || kinds.has(entry.kind)).filter((entry) => !options.packageScope || entry.package === options.packageScope || entry.filePath?.startsWith(options.packageScope)).map((entry) => scoreEntry(entry, intent, options.category)).filter((candidate) => candidate.score > 0).sort(compareCandidates);
479
+ return candidates.slice(0, options.limit ?? 20);
480
+ }
481
+ function candidateIdsFor(index, intent) {
482
+ const ids = /* @__PURE__ */ new Set();
483
+ const terms = uniqueStrings([intent.normalized, ...intent.phrases, ...intent.tokens, ...intent.expandedTerms]);
484
+ for (const term of terms) {
485
+ for (const id of index.invertedIndex[term] ?? []) {
486
+ ids.add(id);
487
+ }
488
+ }
489
+ const fuzzyIds = index.entries.filter((entry) => fuzzyEntryMatch(entry, intent)).map((entry) => entry.stableId).sort();
490
+ if (ids.size > 0) {
491
+ for (const id of fuzzyIds.slice(0, 250)) {
492
+ ids.add(id);
493
+ }
494
+ return [...ids].sort();
495
+ }
496
+ return fuzzyIds;
497
+ }
498
+ function scoreEntry(entry, intent, category) {
499
+ const reasons = [];
500
+ const matchedTerms = /* @__PURE__ */ new Set();
501
+ const entryTerms = /* @__PURE__ */ new Set([entry.normalizedName, ...entry.aliases, ...entry.keywords]);
502
+ const normalizedId = normalizePhrase(entry.stableId);
503
+ addScore(reasons, "exact-symbol", intent.raw === entry.stableId ? 1e3 : 0, entry.stableId);
504
+ if (intent.raw === entry.stableId) {
505
+ matchedTerms.add(entry.stableId);
506
+ }
507
+ if (entry.normalizedName === intent.normalized || normalizedId === intent.normalized) {
508
+ addScore(reasons, "exact-normalized-name", 500, entry.displayName);
509
+ matchedTerms.add(intent.normalized);
510
+ }
511
+ if (entry.aliases.includes(intent.normalized)) {
512
+ addScore(reasons, "exact-alias", 420, intent.normalized);
513
+ matchedTerms.add(intent.normalized);
514
+ }
515
+ const nameTokens = tokenize(entry.displayName);
516
+ if (nameTokens.length > 0 && nameTokens.every((token) => intent.expandedTerms.includes(token))) {
517
+ addScore(reasons, "complete-name-token-match", 260, nameTokens.join(", "));
518
+ for (const token of nameTokens) {
519
+ matchedTerms.add(token);
520
+ }
521
+ }
522
+ for (const phrase of intent.phrases) {
523
+ if (entry.aliases.includes(phrase) || entry.normalizedName === phrase) {
524
+ addScore(reasons, "phrase-match", 180, phrase);
525
+ matchedTerms.add(phrase);
526
+ } else if (phrase.length > 2 && [...entryTerms].some((term) => term.includes(phrase) || phrase.includes(term))) {
527
+ addScore(reasons, "partial-phrase-match", 80, phrase);
528
+ matchedTerms.add(phrase);
529
+ }
530
+ }
531
+ const tokenMatches = intent.tokens.filter((token) => entryTerms.has(token) || entryTerms.has(singularize(token)));
532
+ for (const token of tokenMatches) {
533
+ matchedTerms.add(token);
534
+ }
535
+ addScore(reasons, "token-overlap", tokenMatches.length * 55, tokenMatches.join(", "));
536
+ const expandedMatches = intent.expandedTerms.filter((term) => !intent.tokens.includes(term)).filter((term) => entryTerms.has(term));
537
+ for (const term of expandedMatches) {
538
+ matchedTerms.add(term);
539
+ }
540
+ addScore(reasons, "intent-expansion", expandedMatches.length * 32, expandedMatches.join(", "));
541
+ const relationshipMatches = intent.expandedTerms.filter((term) => entry.relationships.neighborNames.some((name) => tokenize(name).includes(term)));
542
+ addScore(reasons, "relationship-context", Math.min(120, relationshipMatches.length * 18), relationshipMatches.join(", "));
543
+ const categoryBoost = categoryBoostFor(entry, category);
544
+ addScore(reasons, "architecture-layer", categoryBoost, `${entry.kind} in ${entry.architectureLayer}`);
545
+ addScore(reasons, "graph-importance", Math.min(90, entry.importance * 18), `degree ${entry.relationships.degree}`);
546
+ addScore(reasons, "usage-frequency", Math.min(60, entry.usageFrequency * 8), `${entry.usageFrequency} incoming edges`);
547
+ addScore(reasons, "repository-locality", repositoryLocalityBoost(entry), repositoryLocalityEvidence(entry));
548
+ const penalty = repositoryRankingPenalty(entry);
549
+ const score = Math.max(0, reasons.reduce((total, reason) => total + reason.score, 0) - penalty);
550
+ const returnedReasons = penalty > 0 ? [...reasons, { factor: "repository-noise-demotion", score: -penalty, evidence: repositoryNoiseEvidence(entry) }] : reasons;
551
+ return {
552
+ nodeId: entry.stableId,
553
+ stableId: entry.stableId,
554
+ displayName: entry.displayName,
555
+ kind: entry.kind,
556
+ score: round(score, 3),
557
+ confidence: round(Math.min(1, score / 650), 3),
558
+ matchedTerms: uniqueStrings([...matchedTerms]),
559
+ reasons: returnedReasons.filter((reason) => reason.score !== 0).sort(compareReasons),
560
+ entry
561
+ };
562
+ }
563
+ function repositoryLocalityBoost(entry) {
564
+ let score = 0;
565
+ if (isRepositoryLocalEntry(entry)) {
566
+ score += 32;
567
+ }
568
+ if (["Service", "Controller", "Repository", "Provider"].includes(entry.kind)) {
569
+ score += 34;
570
+ }
571
+ if (["Module", "Route", "Operation"].includes(entry.kind)) {
572
+ score += 26;
573
+ }
574
+ if (isRepositorySymbolEntry(entry)) {
575
+ score += 16;
576
+ }
577
+ if (isDtoLikeEntry(entry)) {
578
+ score += 28;
579
+ }
580
+ if (isFeatureModuleEntry(entry)) {
581
+ score += 8;
582
+ }
583
+ if (["Configuration", "EnvironmentVariable"].includes(entry.kind)) {
584
+ score += 18;
585
+ }
586
+ if (/(^|[/.])(test|tests|spec|__tests__)([/.]|$)|\.(test|spec)\./i.test(entry.filePath ?? "")) {
587
+ score += 22;
588
+ }
589
+ return score;
590
+ }
591
+ function repositoryRankingPenalty(entry) {
592
+ let penalty = 0;
593
+ const text = `${entry.stableId} ${entry.filePath ?? ""} ${entry.package ?? ""} ${entry.displayName}`.toLowerCase();
594
+ if (text.includes("node_modules") || entry.stableId.startsWith("dep:")) {
595
+ penalty += 120;
596
+ }
597
+ if (/(\bdto\b|dto$|schema$|type$|types$|generated|__generated__)/i.test(entry.displayName) && !isRepositoryLocalEntry(entry)) {
598
+ penalty += 35;
599
+ }
600
+ if (/node_modules|@nestjs\/|next\/dist|react\/|typescript\/lib/i.test(text)) {
601
+ penalty += 80;
602
+ }
603
+ if (/^framework:|framework|adapter|platform|runtime/.test(entry.stableId.toLowerCase()) && !isRepositoryLocalEntry(entry)) {
604
+ penalty += 55;
605
+ }
606
+ if (isGenericUtilityEntry(entry)) {
607
+ penalty += 35;
608
+ }
609
+ return penalty;
610
+ }
611
+ function repositoryLocalityEvidence(entry) {
612
+ return isRepositoryLocalEntry(entry) ? `${entry.kind} from repository-local graph evidence` : `${entry.kind} from external or generated boundary`;
613
+ }
614
+ function repositoryNoiseEvidence(entry) {
615
+ return `${entry.displayName} is external, generated, or generic framework-adjacent evidence`;
616
+ }
617
+ function isRepositoryLocalEntry(entry) {
618
+ const text = `${entry.stableId} ${entry.filePath ?? ""} ${entry.package ?? ""}`.toLowerCase();
619
+ return !text.includes("node_modules") && !entry.stableId.startsWith("dep:") && !entry.stableId.startsWith("framework:");
620
+ }
621
+ function isRepositorySymbolEntry(entry) {
622
+ return ["Function", "Method", "Class", "Interface", "TypeAlias", "Enum", "Field", "Model", "Resource"].includes(entry.kind);
623
+ }
624
+ function isDtoLikeEntry(entry) {
625
+ const text = `${entry.displayName} ${entry.filePath ?? ""} ${entry.keywords.join(" ")}`;
626
+ return ["Model", "Interface", "TypeAlias", "Class"].includes(entry.kind) && /\bdto\b|dto$|data transfer|payload|request|response/i.test(text);
627
+ }
628
+ function isFeatureModuleEntry(entry) {
629
+ return entry.kind === "Module" && isRepositoryLocalEntry(entry) && !isGenericUtilityEntry(entry);
630
+ }
631
+ function isGenericUtilityEntry(entry) {
632
+ const text = `${entry.stableId} ${entry.displayName} ${entry.filePath ?? ""}`.toLowerCase();
633
+ return /(^|[/.:-])(common|shared|utils?|helpers?|internal|misc|core|index)([/.:-]|$)/.test(text) || /\b(base|abstract|generic|utility|utils?|helpers?)\b/.test(entry.displayName);
634
+ }
635
+ function fuzzyEntryMatch(entry, intent) {
636
+ const haystack = `${entry.stableId} ${entry.displayName} ${entry.aliases.join(" ")} ${entry.keywords.join(" ")}`.toLowerCase();
637
+ return intent.tokens.some((token) => haystack.includes(token));
638
+ }
639
+ function recommendCapability(intent, candidates, category) {
640
+ if (category === "configuration") {
641
+ return "ConfigurationUsage";
642
+ }
643
+ if (category === "environment") {
644
+ return "EnvironmentUsage";
645
+ }
646
+ if (category === "route") {
647
+ return "RequestTrace";
648
+ }
649
+ const terms = new Set(intent.expandedTerms);
650
+ const matched = RELATIONSHIP_CAPABILITIES.map((item) => ({
651
+ capability: item.capability,
652
+ score: item.terms.filter((term) => terms.has(term)).length
653
+ })).sort((left, right) => right.score - left.score || left.capability.localeCompare(right.capability))[0];
654
+ if (matched && matched.score > 0) {
655
+ return matched.capability;
656
+ }
657
+ const topKind = candidates[0]?.kind;
658
+ if (topKind === "Route" || topKind === "Controller") {
659
+ return "RequestTrace";
660
+ }
661
+ if (topKind === "Configuration" || topKind === "EnvironmentVariable") {
662
+ return "ConfigurationUsage";
663
+ }
664
+ return category === "repository" ? "ArchitectureSummary" : "FeatureTouchpoints";
665
+ }
666
+ function tokenize(value) {
667
+ return uniqueStrings(splitIdentifier(value).map((part) => normalizeToken(part)).filter((part) => part.length > 0).filter((part) => !STOP_WORDS.has(part)).flatMap((part) => uniqueStrings([part, singularize(part)])));
668
+ }
669
+ function splitIdentifier(value) {
670
+ return value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/[@#]/g, " ").replace(/[:./\\|()[\]{}<>,;'"`~!$%^&*+=?]+/g, " ").replace(/[-_]+/g, " ").split(/\s+/g).filter(Boolean);
671
+ }
672
+ function normalizeToken(token) {
673
+ return token.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]/g, "");
674
+ }
675
+ function normalizePhrase(value) {
676
+ return tokenize(value).join(" ");
677
+ }
678
+ function phraseVariants(raw, tokens) {
679
+ const variants = /* @__PURE__ */ new Set();
680
+ const normalized = normalizePhrase(raw);
681
+ if (normalized) {
682
+ variants.add(normalized);
683
+ }
684
+ if (tokens.length > 0) {
685
+ variants.add(tokens.join(" "));
686
+ variants.add(tokens.join(""));
687
+ variants.add(tokens.join("-"));
688
+ variants.add(tokens.join("_"));
689
+ }
690
+ const withoutSuffix = stripFrameworkSuffix(tokens);
691
+ if (withoutSuffix.length > 0) {
692
+ variants.add(withoutSuffix.join(" "));
693
+ variants.add(withoutSuffix.join(""));
694
+ }
695
+ return uniqueStrings([...variants]);
696
+ }
697
+ function stripFrameworkSuffix(tokens) {
698
+ let stripped = [...tokens];
699
+ while (stripped.length > 1 && FRAMEWORK_SUFFIXES.has(stripped[stripped.length - 1] ?? "")) {
700
+ stripped = stripped.slice(0, -1);
701
+ }
702
+ return stripped;
703
+ }
704
+ function singularize(value) {
705
+ if (value.endsWith("ies") && value.length > 4) {
706
+ return `${value.slice(0, -3)}y`;
707
+ }
708
+ if (value.endsWith("sses")) {
709
+ return value.slice(0, -2);
710
+ }
711
+ if (value.endsWith("s") && !value.endsWith("ss") && value.length > 3) {
712
+ return value.slice(0, -1);
713
+ }
714
+ return value;
715
+ }
716
+ function pluralize(value) {
717
+ if (value.endsWith("y") && value.length > 2) {
718
+ return `${value.slice(0, -1)}ies`;
719
+ }
720
+ if (value.endsWith("s")) {
721
+ return value;
722
+ }
723
+ return `${value}s`;
724
+ }
725
+ function aliasesForNodeType(type) {
726
+ switch (type) {
727
+ case "Route":
728
+ case "Operation":
729
+ return ["api", "endpoint", "route", "request"];
730
+ case "Controller":
731
+ return ["controller", "handler", "route"];
732
+ case "Service":
733
+ case "Provider":
734
+ return ["service", "provider", "injectable"];
735
+ case "Repository":
736
+ return ["repository", "repo", "persistence", "database"];
737
+ case "Model":
738
+ return ["model", "dto", "schema", "contract", "payload"];
739
+ case "Resource":
740
+ return ["resource", "model"];
741
+ case "Configuration":
742
+ return ["config", "configuration", "setting"];
743
+ case "EnvironmentVariable":
744
+ return ["env", "environment", "secret", "variable"];
745
+ case "Framework":
746
+ return ["framework", "library"];
747
+ case "Package":
748
+ return ["package", "workspace"];
749
+ default:
750
+ return [type.toLowerCase()];
751
+ }
752
+ }
753
+ function isDtoLikeNode(node) {
754
+ const text = `${node.name} ${node.file ?? ""}`;
755
+ return ["Model", "Interface", "TypeAlias", "Class"].includes(node.type) && /\bdto\b|dto$|data transfer|payload|request|response/i.test(text);
756
+ }
757
+ function isTestNode(node) {
758
+ return /(^|[/.])(test|tests|spec|__tests__)([/.]|$)|\.(test|spec)\./i.test(`${node.file ?? ""} ${node.name}`);
759
+ }
760
+ function isFeatureModuleNode(node) {
761
+ return node.type === "Module" && !/(^|[/.:-])(common|shared|utils?|helpers?|internal|misc|core|index)([/.:-]|$)/i.test(`${node.id} ${node.name} ${node.file ?? ""}`);
762
+ }
763
+ function expansionsFor(token) {
764
+ return Object.prototype.hasOwnProperty.call(SEMANTIC_EXPANSIONS, token) ? SEMANTIC_EXPANSIONS[token] ?? [] : [];
765
+ }
766
+ function categoryTypeSet(category) {
767
+ const types = CATEGORY_TYPES[category];
768
+ return types.length > 0 ? new Set(types) : void 0;
769
+ }
770
+ function includeEntryForCategory(entry, intent, category) {
771
+ if (category !== "concept") {
772
+ return true;
773
+ }
774
+ if (!["Import", "Export", "Decorator"].includes(entry.kind)) {
775
+ return true;
776
+ }
777
+ const kind = entry.kind.toLowerCase();
778
+ return intent.tokens.includes(kind) || intent.raw === entry.stableId || intent.normalized === entry.normalizedName;
779
+ }
780
+ function categoryBoostFor(entry, category) {
781
+ if (category === "concept") {
782
+ return ["Service", "Provider", "Controller", "Route", "Operation", "Repository", "Model", "Resource", "Function", "Method", "Class", "Interface", "TypeAlias", "Module", "Configuration", "EnvironmentVariable"].includes(entry.kind) ? 45 : 0;
783
+ }
784
+ const types = CATEGORY_TYPES[category];
785
+ if (types.length === 0) {
786
+ return 0;
787
+ }
788
+ const base = types.includes(entry.kind) ? 75 : 0;
789
+ if (category === "feature" && isFeatureModuleEntry(entry)) {
790
+ return base + 10;
791
+ }
792
+ if (category === "feature" && isDtoLikeEntry(entry)) {
793
+ return base + 20;
794
+ }
795
+ return base;
796
+ }
797
+ function nodeImportance(node, degree, inbound) {
798
+ const layerWeight = {
799
+ Workspace: 5,
800
+ Application: 5,
801
+ Package: 4,
802
+ Module: 4,
803
+ Controller: 4,
804
+ Route: 4,
805
+ Service: 4,
806
+ Provider: 3,
807
+ Repository: 3,
808
+ Configuration: 3,
809
+ EnvironmentVariable: 3
810
+ };
811
+ return round((layerWeight[node.type] ?? 1) + Math.log2(degree + 1) + Math.log2(inbound + 1), 3);
812
+ }
813
+ function architectureLayer(node) {
814
+ if (["Route", "Controller", "Operation", "Middleware", "Guard"].includes(node.type)) {
815
+ return "application-boundary";
816
+ }
817
+ if (["Service", "Provider", "Factory"].includes(node.type)) {
818
+ return "application-service";
819
+ }
820
+ if (["Repository", "DatabaseTable", "Model", "Resource"].includes(node.type)) {
821
+ return "persistence";
822
+ }
823
+ if (["Configuration", "EnvironmentVariable", "BuildTarget", "Script", "Task"].includes(node.type)) {
824
+ return "configuration";
825
+ }
826
+ if (["Package", "Workspace", "Application", "Framework", "Dependency"].includes(node.type)) {
827
+ return "architecture";
828
+ }
829
+ return "language";
830
+ }
831
+ function parentChainFor(node, nodeById, incomingEdges) {
832
+ const parents = [];
833
+ let current = node;
834
+ const seen = /* @__PURE__ */ new Set([node.id]);
835
+ for (let depth = 0; depth < 8; depth += 1) {
836
+ const parentEdge = incomingEdges.find(
837
+ (edge) => edge.to === current.id && (edge.type === "CONTAINS" || edge.type === "BELONGS_TO" || edge.type === "DECLARES" || edge.type === "REGISTERS")
838
+ );
839
+ if (!parentEdge || seen.has(parentEdge.from)) {
840
+ break;
841
+ }
842
+ const parent = nodeById.get(parentEdge.from);
843
+ if (!parent) {
844
+ break;
845
+ }
846
+ parents.push(`${parent.type}:${parent.name}`);
847
+ seen.add(parent.id);
848
+ current = parent;
849
+ }
850
+ return parents;
851
+ }
852
+ function containingName(parentChain, type) {
853
+ const prefix = `${type}:`;
854
+ return parentChain.find((item) => item.startsWith(prefix))?.slice(prefix.length);
855
+ }
856
+ function metadataTextValues(metadata) {
857
+ if (!metadata) {
858
+ return [];
859
+ }
860
+ const values = [];
861
+ collectMetadataText(metadata, values, 0, /* @__PURE__ */ new WeakSet());
862
+ return values;
863
+ }
864
+ function collectMetadataText(value, values, depth, seen) {
865
+ if (depth > SEMANTIC_METADATA_DEPTH_LIMIT || values.length >= SEMANTIC_METADATA_VALUES_LIMIT || value === null || value === void 0) {
866
+ return;
867
+ }
868
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
869
+ values.push(truncateSemanticText(String(value)));
870
+ return;
871
+ }
872
+ if (Array.isArray(value)) {
873
+ if (seen.has(value)) {
874
+ return;
875
+ }
876
+ seen.add(value);
877
+ for (const item of value.slice(0, SEMANTIC_METADATA_ARRAY_LIMIT)) {
878
+ collectMetadataText(item, values, depth + 1, seen);
879
+ if (values.length >= SEMANTIC_METADATA_VALUES_LIMIT) {
880
+ break;
881
+ }
882
+ }
883
+ return;
884
+ }
885
+ if (typeof value === "object") {
886
+ if (seen.has(value)) {
887
+ return;
888
+ }
889
+ seen.add(value);
890
+ for (const [key, entry] of Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).slice(0, SEMANTIC_METADATA_ENTRY_LIMIT)) {
891
+ if (isNoisyMetadataKey(key)) {
892
+ continue;
893
+ }
894
+ values.push(truncateSemanticText(key));
895
+ collectMetadataText(entry, values, depth + 1, seen);
896
+ if (values.length >= SEMANTIC_METADATA_VALUES_LIMIT) {
897
+ break;
898
+ }
899
+ }
900
+ }
901
+ }
902
+ function isNoisyMetadataKey(key) {
903
+ return ["language", "parser", "parserVersion", "passId", "provenance", "source"].includes(key);
904
+ }
905
+ function routeTextValues(node) {
906
+ if (node.type !== "Route" && node.type !== "Operation") {
907
+ return [];
908
+ }
909
+ return [
910
+ stringMetadata(node.metadata, "method") ?? "",
911
+ stringMetadata(node.metadata, "path") ?? "",
912
+ stringMetadata(node.metadata, "route") ?? "",
913
+ node.name
914
+ ].filter(Boolean);
915
+ }
916
+ function deterministicDocumentation(metadata) {
917
+ return firstTruncatedMetadata(metadata, ["documentation", "description", "summary"]);
918
+ }
919
+ function deterministicComments(metadata) {
920
+ return firstTruncatedMetadata(metadata, ["comments", "comment", "jsdoc"]);
921
+ }
922
+ function stringMetadata(metadata, key) {
923
+ const value = metadata?.[key];
924
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
925
+ }
926
+ function firstTruncatedMetadata(metadata, keys) {
927
+ for (const key of keys) {
928
+ const value = stringMetadata(metadata, key);
929
+ if (value) {
930
+ return truncateSemanticText(value);
931
+ }
932
+ }
933
+ return void 0;
934
+ }
935
+ function truncateSemanticText(value) {
936
+ const normalized = value.replace(/\s+/g, " ").trim();
937
+ return normalized.length > SEMANTIC_TEXT_LIMIT ? `${normalized.slice(0, SEMANTIC_TEXT_LIMIT)}...[truncated]` : normalized;
938
+ }
939
+ function frameworkFromNeighbors(neighbors) {
940
+ return neighbors.find((node) => node.type === "Framework")?.name;
941
+ }
942
+ function folderPath(file) {
943
+ const normalized = normalizePath(file);
944
+ const parts = normalized.split("/");
945
+ parts.pop();
946
+ return parts.join("/") || ".";
947
+ }
948
+ function countEdgesByType(edges) {
949
+ const counts = /* @__PURE__ */ new Map();
950
+ for (const edge of edges) {
951
+ counts.set(edge.type, (counts.get(edge.type) ?? 0) + 1);
952
+ }
953
+ return Object.fromEntries([...counts.entries()].sort(([left], [right]) => left.localeCompare(right)));
954
+ }
955
+ function groupEdges(edges, side) {
956
+ const grouped = /* @__PURE__ */ new Map();
957
+ for (const edge of edges) {
958
+ const current = grouped.get(edge[side]) ?? [];
959
+ current.push(edge);
960
+ grouped.set(edge[side], current);
961
+ }
962
+ return new Map([...grouped.entries()].map(([key, values]) => [key, values.sort(compareEdges)]));
963
+ }
964
+ function addInverted(map, term, id) {
965
+ if (!term || STOP_WORDS.has(term)) {
966
+ return;
967
+ }
968
+ const current = map.get(term) ?? /* @__PURE__ */ new Set();
969
+ current.add(id);
970
+ map.set(term, current);
971
+ }
972
+ function addScore(reasons, factor, score, evidence) {
973
+ if (score <= 0) {
974
+ return;
975
+ }
976
+ reasons.push({ factor, score: round(score, 3), evidence });
977
+ }
978
+ function compareCandidates(left, right) {
979
+ return right.score - left.score || left.kind.localeCompare(right.kind) || left.stableId.localeCompare(right.stableId);
980
+ }
981
+ function compareReasons(left, right) {
982
+ return right.score - left.score || left.factor.localeCompare(right.factor);
983
+ }
984
+ function compareEntries(left, right) {
985
+ return left.stableId.localeCompare(right.stableId);
986
+ }
987
+ function compareEdges(left, right) {
988
+ return left.id.localeCompare(right.id);
989
+ }
990
+ function uniqueStrings(values) {
991
+ return [...new Set(values.map((value) => value.trim()).filter(Boolean))].sort((left, right) => left.localeCompare(right));
992
+ }
993
+ function sum(values) {
994
+ return values.reduce((total, value) => total + value, 0);
995
+ }
996
+ function round(value, places = 3) {
997
+ const factor = 10 ** places;
998
+ return Math.round(value * factor) / factor;
999
+ }
1000
+ function performanceNow() {
1001
+ return typeof performance !== "undefined" ? performance.now() : Date.now();
1002
+ }
1003
+ function isNode(value) {
1004
+ return Boolean(value);
1005
+ }
1006
+ function isEntry(value) {
1007
+ return Boolean(value);
1008
+ }
1009
+ function withOptionalProperties(target, optional) {
1010
+ return {
1011
+ ...target,
1012
+ ...Object.fromEntries(Object.entries(optional).filter(([, value]) => value !== void 0))
1013
+ };
1014
+ }
1015
+
1
1016
  // src/index.ts
2
1017
  var SOFTWARE_GRAPH_VERSION = "1.0.0";
3
1018
  var NODE_TYPES = [
@@ -241,7 +1256,7 @@ var GraphBuilder = class {
241
1256
  };
242
1257
  function createSoftwareGraph(input) {
243
1258
  const nodes = [...input.nodes].map(normalizeNode).sort(compareNodes);
244
- const edges = [...input.edges].map(normalizeEdge).sort(compareEdges);
1259
+ const edges = [...input.edges].map(normalizeEdge).sort(compareEdges2);
245
1260
  const diagnostics = [...input.diagnostics ?? []].sort(compareDiagnostics);
246
1261
  const indexes = buildIndexes(nodes, edges);
247
1262
  const deterministicHash = stableHash(
@@ -319,7 +1334,7 @@ function normalizeNode(node) {
319
1334
  type: node.type,
320
1335
  name: node.name
321
1336
  };
322
- return withOptionalProperties(normalized, {
1337
+ return withOptionalProperties2(normalized, {
323
1338
  file: node.file ? normalizePath(node.file) : void 0,
324
1339
  package: node.package,
325
1340
  span: node.span ? normalizeSpan(node.span) : void 0,
@@ -333,7 +1348,7 @@ function normalizeEdge(edge) {
333
1348
  from: edge.from,
334
1349
  to: edge.to
335
1350
  };
336
- return withOptionalProperties(normalized, {
1351
+ return withOptionalProperties2(normalized, {
337
1352
  evidence: edge.evidence?.map((item) => ({
338
1353
  ...item,
339
1354
  span: item.span ? normalizeSpan(item.span) : void 0
@@ -352,7 +1367,7 @@ function mergeNode(left, right) {
352
1367
  ...left,
353
1368
  metadata: mergeJsonObject(left.metadata, right.metadata)
354
1369
  };
355
- return withOptionalProperties(merged, {
1370
+ return withOptionalProperties2(merged, {
356
1371
  span: left.span ?? right.span,
357
1372
  file: left.file ?? right.file,
358
1373
  package: left.package ?? right.package
@@ -364,7 +1379,7 @@ function mergeEdge(left, right) {
364
1379
  ...left,
365
1380
  metadata: mergeJsonObject(left.metadata, right.metadata)
366
1381
  };
367
- return withOptionalProperties(merged, {
1382
+ return withOptionalProperties2(merged, {
368
1383
  evidence: evidence.length > 0 ? evidence : void 0
369
1384
  });
370
1385
  }
@@ -418,13 +1433,13 @@ function summarizeCounts(values) {
418
1433
  function compareNodes(left, right) {
419
1434
  return left.id.localeCompare(right.id);
420
1435
  }
421
- function compareEdges(left, right) {
1436
+ function compareEdges2(left, right) {
422
1437
  return left.id.localeCompare(right.id);
423
1438
  }
424
1439
  function compareDiagnostics(left, right) {
425
1440
  return left.id.localeCompare(right.id);
426
1441
  }
427
- function withOptionalProperties(target, optional) {
1442
+ function withOptionalProperties2(target, optional) {
428
1443
  const entries = Object.entries(optional).filter(([, value]) => value !== void 0);
429
1444
  return {
430
1445
  ...target,
@@ -435,19 +1450,32 @@ export {
435
1450
  GraphBuilder,
436
1451
  NODE_TYPES,
437
1452
  RELATIONSHIP_TYPES,
1453
+ SEMANTIC_INDEX_VERSION,
438
1454
  SOFTWARE_GRAPH_VERSION,
439
1455
  buildIndexes,
440
1456
  createDiagnosticId,
441
1457
  createEdgeId,
442
1458
  createGraphBuilder,
443
1459
  createNodeId,
1460
+ createSemanticIndex,
444
1461
  createSoftwareGraph,
445
1462
  createSyntaxEvidence,
1463
+ findConcept,
1464
+ findConfiguration,
1465
+ findEntryPoint,
1466
+ findEnvironment,
1467
+ findFeature,
1468
+ findRepositoryConcept,
1469
+ findRoute,
1470
+ findSymbol,
446
1471
  getEdge,
447
1472
  getNode,
1473
+ normalizeIntent,
448
1474
  normalizePath,
1475
+ resolveIntent,
449
1476
  stableHash,
450
1477
  stableStringify,
451
- summarizeGraph
1478
+ summarizeGraph,
1479
+ validateSemanticIndex
452
1480
  };
453
1481
  //# sourceMappingURL=index.js.map