weavatrix 0.2.13 → 0.2.15
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/README.md +99 -39
- package/SECURITY.md +2 -2
- package/docs/releases/v0.2.15.md +37 -0
- package/package.json +2 -2
- package/skill/SKILL.md +57 -38
- package/src/analysis/architecture/contract-graph.js +119 -0
- package/src/analysis/architecture/contract-schema.js +110 -0
- package/src/analysis/architecture/contract-storage.js +35 -0
- package/src/analysis/architecture/contract-verification.js +168 -0
- package/src/analysis/architecture-contract.js +16 -343
- package/src/analysis/change-classification/diff-parser.js +103 -0
- package/src/analysis/change-classification/options.js +40 -0
- package/src/analysis/change-classification/symbol-classifier.js +177 -0
- package/src/analysis/change-classification.js +120 -519
- package/src/analysis/dead-code-review/policy.js +23 -0
- package/src/analysis/dead-code-review.js +9 -19
- package/src/analysis/dep-check.js +14 -71
- package/src/analysis/dep-rules.js +10 -303
- package/src/analysis/dependency/conventions.js +16 -0
- package/src/analysis/dependency/scoped-dependencies.js +45 -0
- package/src/analysis/dependency/source-references.js +21 -0
- package/src/analysis/duplicates.tokenize.js +12 -2
- package/src/analysis/endpoints/common.js +62 -0
- package/src/analysis/endpoints/extract.js +107 -0
- package/src/analysis/endpoints/inventory.js +84 -0
- package/src/analysis/endpoints/mounts.js +129 -0
- package/src/analysis/endpoints-java.js +80 -3
- package/src/analysis/endpoints.js +3 -448
- package/src/analysis/git-history/analytics.js +177 -0
- package/src/analysis/git-history/collector.js +109 -0
- package/src/analysis/git-history/options.js +68 -0
- package/src/analysis/git-history.js +128 -577
- package/src/analysis/health-capabilities.js +82 -0
- package/src/analysis/http-contracts/analysis.js +169 -0
- package/src/analysis/http-contracts/client-call-detection.js +81 -0
- package/src/analysis/http-contracts/client-call-parser.js +255 -0
- package/src/analysis/http-contracts/graph-context.js +143 -0
- package/src/analysis/http-contracts/matching.js +61 -0
- package/src/analysis/http-contracts/shared.js +86 -0
- package/src/analysis/http-contracts.js +6 -822
- package/src/analysis/internal-audit/dependency-health.js +111 -0
- package/src/analysis/internal-audit/python-manifests.js +65 -0
- package/src/analysis/internal-audit/repo-files.js +55 -0
- package/src/analysis/internal-audit/supply-chain.js +132 -0
- package/src/analysis/internal-audit.collect.js +5 -106
- package/src/analysis/internal-audit.reach.js +20 -0
- package/src/analysis/internal-audit.run.js +114 -200
- package/src/analysis/jvm-dependency-evidence.js +69 -0
- package/src/analysis/source-correctness/retry-patterns.js +93 -0
- package/src/analysis/source-correctness.js +225 -0
- package/src/analysis/structure/dependency-graph.js +156 -0
- package/src/analysis/structure/findings.js +142 -0
- package/src/analysis/task-retrieval.js +3 -14
- package/src/graph/builder/go-receiver-resolution.js +112 -0
- package/src/graph/builder/js/queries.js +48 -0
- package/src/graph/builder/lang-go.js +89 -4
- package/src/graph/builder/lang-js.js +4 -44
- package/src/graph/builder/pass2-resolution.js +68 -0
- package/src/graph/freshness-probe.js +2 -1
- package/src/graph/incremental-refresh.js +3 -2
- package/src/graph/internal-builder.build.js +22 -183
- package/src/graph/internal-builder.pass2.js +161 -0
- package/src/graph/internal-builder.resolvers.js +2 -105
- package/src/graph/resolvers/rust.js +117 -0
- package/src/mcp/actions/advisories.mjs +18 -0
- package/src/mcp/actions/graph-lifecycle.mjs +148 -0
- package/src/mcp/actions/graph-sync.mjs +195 -0
- package/src/mcp/actions/hosted-architecture.mjs +57 -0
- package/src/mcp/architecture-bootstrap.mjs +168 -0
- package/src/mcp/architecture-starter.mjs +234 -0
- package/src/mcp/catalog.mjs +20 -6
- package/src/mcp/evidence/bun-lock-graph.mjs +209 -0
- package/src/mcp/evidence/duplicate-member-order.mjs +8 -0
- package/src/mcp/evidence/package-graph-common.mjs +115 -0
- package/src/mcp/evidence/package-lock-graph.mjs +92 -0
- package/src/mcp/evidence-snapshot.duplicates.mjs +3 -7
- package/src/mcp/evidence-snapshot.package-graph.mjs +5 -474
- package/src/mcp/git-output.mjs +10 -0
- package/src/mcp/graph/context-core.mjs +111 -0
- package/src/mcp/graph/context-seeds.mjs +197 -0
- package/src/mcp/graph/context-state.mjs +86 -0
- package/src/mcp/graph/reverse-reach.mjs +42 -0
- package/src/mcp/graph/tools-core.mjs +143 -0
- package/src/mcp/graph/tools-query.mjs +223 -0
- package/src/mcp/graph-context.mjs +4 -496
- package/src/mcp/health/audit-format.mjs +172 -0
- package/src/mcp/health/audit.mjs +171 -0
- package/src/mcp/health/dead-code.mjs +110 -0
- package/src/mcp/health/duplicates.mjs +83 -0
- package/src/mcp/health/endpoints.mjs +42 -0
- package/src/mcp/health/structure.mjs +219 -0
- package/src/mcp/runtime-version.mjs +32 -0
- package/src/mcp/server/auto-refresh.mjs +66 -0
- package/src/mcp/server/runtime-config.mjs +43 -0
- package/src/mcp/server/shutdown.mjs +40 -0
- package/src/mcp/sync/evidence-architecture.mjs +54 -0
- package/src/mcp/sync/evidence-common.mjs +88 -0
- package/src/mcp/sync/evidence-duplicates.mjs +97 -0
- package/src/mcp/sync/evidence-health.mjs +56 -0
- package/src/mcp/sync/evidence-packages.mjs +95 -0
- package/src/mcp/sync/payload-common.mjs +97 -0
- package/src/mcp/sync/payload-v2.mjs +53 -0
- package/src/mcp/sync/payload-v3.mjs +79 -0
- package/src/mcp/sync-evidence.mjs +7 -402
- package/src/mcp/sync-payload.mjs +10 -244
- package/src/mcp/tools-actions.mjs +18 -414
- package/src/mcp/tools-architecture.mjs +18 -70
- package/src/mcp/tools-context.mjs +56 -15
- package/src/mcp/tools-endpoints.mjs +4 -1
- package/src/mcp/tools-graph.mjs +17 -331
- package/src/mcp/tools-health.mjs +24 -705
- package/src/mcp/tools-impact-change.mjs +2 -38
- package/src/mcp/tools-impact.mjs +2 -43
- package/src/mcp-server.mjs +35 -146
- package/src/path-classification.js +14 -0
- package/src/precision/lsp-client/constants.js +12 -0
- package/src/precision/lsp-client/environment.js +20 -0
- package/src/precision/lsp-client/errors.js +15 -0
- package/src/precision/lsp-client/lifecycle.js +127 -0
- package/src/precision/lsp-client/message-parser.js +81 -0
- package/src/precision/lsp-client/protocol.js +212 -0
- package/src/precision/lsp-client/registry.js +58 -0
- package/src/precision/lsp-client/repo-uri.js +60 -0
- package/src/precision/lsp-client/stdio-client.js +151 -0
- package/src/precision/lsp-client.js +10 -682
- package/src/precision/lsp-overlay/build.js +238 -0
- package/src/precision/lsp-overlay/contract.js +61 -0
- package/src/precision/lsp-overlay/reference-results.js +108 -0
- package/src/precision/lsp-overlay/semantic-inputs.js +62 -0
- package/src/precision/lsp-overlay/source-session.js +134 -0
- package/src/precision/lsp-overlay/store.js +150 -0
- package/src/precision/lsp-overlay/target-index.js +147 -0
- package/src/precision/lsp-overlay/target-query.js +55 -0
- package/src/precision/lsp-overlay.js +11 -868
- package/src/precision/typescript-lsp-provider.js +12 -682
- package/src/precision/typescript-provider/client.js +69 -0
- package/src/precision/typescript-provider/discovery.js +124 -0
- package/src/precision/typescript-provider/project-host.js +249 -0
- package/src/precision/typescript-provider/project-safety.js +161 -0
- package/src/precision/typescript-provider/reference-usage.js +53 -0
- package/src/security/malware-heuristics.sweep.js +1 -1
- package/src/security/registry-sig.classify.js +2 -1
- package/src/security/registry-sig.rules.js +3 -3
- package/src/version.js +7 -0
- package/docs/releases/v0.2.13.md +0 -48
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import {goTypeName} from './lang-go.js'
|
|
2
|
+
|
|
3
|
+
const dirOf = (file) => file.includes('/') ? file.slice(0, file.lastIndexOf('/')) : ''
|
|
4
|
+
|
|
5
|
+
export function createGoReceiverResolution({resolution, nodeById, importedLocals}) {
|
|
6
|
+
const {dirSymbols, dirMethods, dirMethodsByName, dirTypes, resolveCall} = resolution
|
|
7
|
+
const typeRef = (rawType, contextFile, fallbackDir = dirOf(contextFile)) => {
|
|
8
|
+
const raw = String(rawType || '').trim()
|
|
9
|
+
if (!raw) return null
|
|
10
|
+
const dot = raw.indexOf('.')
|
|
11
|
+
if (dot > 0) {
|
|
12
|
+
const imported = importedLocals.get(contextFile)?.get(raw.slice(0, dot))
|
|
13
|
+
return imported?.targetDir ? {dir: imported.targetDir, name: raw.slice(dot + 1)} : null
|
|
14
|
+
}
|
|
15
|
+
return {dir: fallbackDir, name: raw}
|
|
16
|
+
}
|
|
17
|
+
const fieldType = (receiver, fieldName) => {
|
|
18
|
+
if (!receiver?.name || !fieldName) return null
|
|
19
|
+
const typeId = dirTypes.get(receiver.dir)?.get(receiver.name)
|
|
20
|
+
const typeNode = typeId && nodeById.get(typeId)
|
|
21
|
+
const rawFieldType = typeNode?.field_types?.[fieldName]
|
|
22
|
+
return rawFieldType ? typeRef(rawFieldType, typeNode.source_file, receiver.dir) : null
|
|
23
|
+
}
|
|
24
|
+
const exactMethod = (receiver, methodName) => {
|
|
25
|
+
const candidates = receiver && dirMethods.get(receiver.dir)?.get(receiver.name)?.get(methodName)
|
|
26
|
+
return candidates?.length === 1 ? candidates[0] : null
|
|
27
|
+
}
|
|
28
|
+
const uniqueMethod = (dir, methodName) => {
|
|
29
|
+
const candidates = dirMethodsByName.get(dir)?.get(methodName) || []
|
|
30
|
+
return candidates.length === 1 ? candidates[0] : null
|
|
31
|
+
}
|
|
32
|
+
const returnType = (targetId, callerFile) => {
|
|
33
|
+
const target = targetId && nodeById.get(targetId)
|
|
34
|
+
return target?.return_type
|
|
35
|
+
? typeRef(target.return_type, target.source_file || callerFile, dirOf(target.source_file || callerFile))
|
|
36
|
+
: null
|
|
37
|
+
}
|
|
38
|
+
const fieldChildren = (node, name) => {
|
|
39
|
+
const children = node?.childrenForFieldName?.(name)
|
|
40
|
+
if (Array.isArray(children) && children.length) return children
|
|
41
|
+
const child = node?.childForFieldName?.(name)
|
|
42
|
+
return child ? [child] : []
|
|
43
|
+
}
|
|
44
|
+
const receiverBindings = (selector, file) => {
|
|
45
|
+
const bindings = new Map()
|
|
46
|
+
let scope = selector
|
|
47
|
+
while (scope?.parent && !['function_declaration', 'method_declaration'].includes(scope.type)) scope = scope.parent
|
|
48
|
+
if (!scope || !['function_declaration', 'method_declaration'].includes(scope.type)) return bindings
|
|
49
|
+
const callRow = selector.startPosition.row
|
|
50
|
+
const containsCall = (node) => node.startIndex <= selector.startIndex && node.endIndex >= selector.endIndex
|
|
51
|
+
const expressionType = (node) => {
|
|
52
|
+
if (!node) return null
|
|
53
|
+
const declared = goTypeName(node)
|
|
54
|
+
if (declared) return typeRef(declared, file)
|
|
55
|
+
if (['parenthesized_expression', 'unary_expression'].includes(node.type)) {
|
|
56
|
+
return expressionType(node.childForFieldName?.('operand') || node.namedChildren?.[0])
|
|
57
|
+
}
|
|
58
|
+
if (node.type === 'composite_literal') return typeRef(goTypeName(node.childForFieldName?.('type')), file)
|
|
59
|
+
if (node.type === 'identifier') return bindings.get(node.text) || null
|
|
60
|
+
if (node.type === 'selector_expression') return fieldType(expressionType(node.childForFieldName?.('operand')), node.childForFieldName?.('field')?.text)
|
|
61
|
+
if (node.type !== 'call_expression') return null
|
|
62
|
+
const fn = node.childForFieldName?.('function')
|
|
63
|
+
if (fn?.type === 'identifier') {
|
|
64
|
+
const returned = returnType(resolveCall(fn.text, file), file)
|
|
65
|
+
if (returned) return returned
|
|
66
|
+
if (dirTypes.get(dirOf(file))?.has(fn.text)) return typeRef(fn.text, file)
|
|
67
|
+
if (/^New[A-Z_]/.test(fn.text)) return typeRef(fn.text.slice(3), file)
|
|
68
|
+
}
|
|
69
|
+
if (fn?.type === 'selector_expression') {
|
|
70
|
+
const operand = fn.childForFieldName?.('operand'), member = fn.childForFieldName?.('field')?.text
|
|
71
|
+
const imported = operand?.type === 'identifier' && importedLocals.get(file)?.get(operand.text)
|
|
72
|
+
if (imported?.targetDir && member) {
|
|
73
|
+
const returned = returnType(dirSymbols.get(imported.targetDir)?.get(member), file)
|
|
74
|
+
if (returned) return returned
|
|
75
|
+
if (/^New[A-Z_]/.test(member)) return {dir: imported.targetDir, name: member.slice(3)}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return null
|
|
79
|
+
}
|
|
80
|
+
const bindTyped = (node) => {
|
|
81
|
+
const reference = typeRef(goTypeName(node.childForFieldName?.('type')), file)
|
|
82
|
+
const names = fieldChildren(node, 'name').filter((name) => ['identifier', 'field_identifier'].includes(name.type))
|
|
83
|
+
if (reference) for (const name of names) bindings.set(name.text, reference)
|
|
84
|
+
else if (node.type === 'var_spec') {
|
|
85
|
+
const values = node.childForFieldName?.('value')?.namedChildren || []
|
|
86
|
+
for (let index = 0; index < Math.min(names.length, values.length); index++) {
|
|
87
|
+
const inferred = expressionType(values[index])
|
|
88
|
+
if (inferred) bindings.set(names[index].text, inferred)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const bindAssignment = (node) => {
|
|
93
|
+
const left = node.childForFieldName?.('left')?.namedChildren || []
|
|
94
|
+
const right = node.childForFieldName?.('right')?.namedChildren || []
|
|
95
|
+
for (let index = 0; index < Math.min(left.length, right.length); index++) {
|
|
96
|
+
if (left[index].type !== 'identifier') continue
|
|
97
|
+
const inferred = expressionType(right[index])
|
|
98
|
+
if (inferred) bindings.set(left[index].text, inferred)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const visit = (node) => {
|
|
102
|
+
if (!node || node.startPosition.row > callRow) return
|
|
103
|
+
if (node !== scope && ['function_declaration', 'method_declaration', 'func_literal'].includes(node.type) && !containsCall(node)) return
|
|
104
|
+
if (['parameter_declaration', 'var_spec'].includes(node.type)) bindTyped(node)
|
|
105
|
+
else if (['short_var_declaration', 'assignment_statement'].includes(node.type)) bindAssignment(node)
|
|
106
|
+
for (const child of node.namedChildren || []) visit(child)
|
|
107
|
+
}
|
|
108
|
+
visit(scope)
|
|
109
|
+
return bindings
|
|
110
|
+
}
|
|
111
|
+
return {dirOf, fieldType, exactMethod, uniqueMethod, receiverBindings}
|
|
112
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export const CALLABLE = /arrow_function|function|function_expression|generator_function/
|
|
2
|
+
|
|
3
|
+
export const FUNCS = `
|
|
4
|
+
(function_declaration name: (identifier) @fn)
|
|
5
|
+
(generator_function_declaration name: (identifier) @fn)
|
|
6
|
+
(class_declaration name: (_) @class)
|
|
7
|
+
(method_definition name: (_) @method)`
|
|
8
|
+
|
|
9
|
+
export const TOPVARS = `
|
|
10
|
+
(program (lexical_declaration (variable_declarator) @decl))
|
|
11
|
+
(program (variable_declaration (variable_declarator) @decl))
|
|
12
|
+
(program (export_statement (lexical_declaration (variable_declarator) @decl)))
|
|
13
|
+
(program (export_statement (variable_declaration (variable_declarator) @decl)))`
|
|
14
|
+
|
|
15
|
+
export const TYPES = `
|
|
16
|
+
(interface_declaration name: (_) @interface)
|
|
17
|
+
(type_alias_declaration name: (_) @type)
|
|
18
|
+
(enum_declaration name: (_) @enum)`
|
|
19
|
+
|
|
20
|
+
export const REQUIRE = `(variable_declarator name: (_) @lhs value: (call_expression function: (identifier) @req arguments: (arguments (string (string_fragment) @src))))`
|
|
21
|
+
|
|
22
|
+
export function parseExportSpecifiers(raw) {
|
|
23
|
+
return String(raw || '').split(',').map((part) => {
|
|
24
|
+
const text = part.trim()
|
|
25
|
+
const typeOnly = /^type\s+/.test(text)
|
|
26
|
+
const clean = text.replace(/^type\s+/, '').trim()
|
|
27
|
+
const match = clean.match(/^([A-Za-z_$][\w$]*|default)(?:\s+as\s+([A-Za-z_$][\w$]*|default))?$/)
|
|
28
|
+
return match ? {imported: match[1], exported: match[2] || match[1], typeOnly} : null
|
|
29
|
+
}).filter(Boolean)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function importTypeOnly(node) {
|
|
33
|
+
if (/^\s*import\s+type\b/.test(node.text)) return true
|
|
34
|
+
const clause = node.namedChildren.find((child) => child.type === 'import_clause')
|
|
35
|
+
if (!clause) return false
|
|
36
|
+
const parts = clause.namedChildren
|
|
37
|
+
if (parts.some((child) => child.type === 'identifier' || child.type === 'namespace_import')) return false
|
|
38
|
+
const named = parts.find((child) => child.type === 'named_imports')
|
|
39
|
+
const specifiers = named?.namedChildren.filter((child) => child.type === 'import_specifier') || []
|
|
40
|
+
return specifiers.length > 0 && specifiers.every((specifier) => /^\s*type\b/.test(specifier.text))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function reexportTypeOnly(node) {
|
|
44
|
+
if (/^\s*export\s+type\b/.test(node.text)) return true
|
|
45
|
+
const clause = node.namedChildren.find((child) => child.type === 'export_clause')
|
|
46
|
+
const specifiers = clause?.namedChildren.filter((child) => child.type === 'export_specifier') || []
|
|
47
|
+
return specifiers.length > 0 && specifiers.every((specifier) => /^\s*type\b/.test(specifier.text))
|
|
48
|
+
}
|
|
@@ -5,6 +5,61 @@
|
|
|
5
5
|
// in the orchestrator; cross-pkg `pkg.Func()` is a selector — see resolveGoPackage/pass2). No inheritance.
|
|
6
6
|
import { goSpecToPkg } from "./spec-pkg.js";
|
|
7
7
|
|
|
8
|
+
// Return the named Go type behind pointers/generics/parentheses. Qualified names are
|
|
9
|
+
// kept (`speaker.Speaker`) so pass 2 can route them through the import alias table.
|
|
10
|
+
export function goTypeName(node) {
|
|
11
|
+
if (!node) return null;
|
|
12
|
+
if (node.type === "type_identifier") return node.text;
|
|
13
|
+
if (node.type === "qualified_type") return node.text.replace(/\s+/g, "");
|
|
14
|
+
if (node.type === "generic_type") return goTypeName(node.childForFieldName?.("type") || node.namedChildren?.[0]);
|
|
15
|
+
if (["pointer_type", "parenthesized_type", "unary_expression"].includes(node.type)) {
|
|
16
|
+
return goTypeName(node.childForFieldName?.("type") || node.childForFieldName?.("operand") || node.namedChildren?.[0]);
|
|
17
|
+
}
|
|
18
|
+
if ((node.namedChildren?.length || 0) === 1) return goTypeName(node.namedChildren[0]);
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const namedFieldChildren = (node, name) => {
|
|
23
|
+
const children = node?.childrenForFieldName?.(name);
|
|
24
|
+
if (Array.isArray(children) && children.length) return children;
|
|
25
|
+
const child = node?.childForFieldName?.(name);
|
|
26
|
+
return child ? [child] : [];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function goStructFields(typeNode) {
|
|
30
|
+
if (typeNode?.type !== "struct_type") return null;
|
|
31
|
+
const fields = {};
|
|
32
|
+
const visit = (node) => {
|
|
33
|
+
if (!node) return;
|
|
34
|
+
if (node.type === "field_declaration") {
|
|
35
|
+
const type = goTypeName(node.childForFieldName?.("type"));
|
|
36
|
+
if (!type) return;
|
|
37
|
+
const names = namedFieldChildren(node, "name");
|
|
38
|
+
if (names.length) for (const name of names) fields[name.text] = type;
|
|
39
|
+
else {
|
|
40
|
+
// An embedded field is addressed by the base type name (`h.Speaker`).
|
|
41
|
+
const embedded = type.split(".").pop();
|
|
42
|
+
if (embedded) fields[embedded] = type;
|
|
43
|
+
}
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
for (const child of node.namedChildren || []) visit(child);
|
|
47
|
+
};
|
|
48
|
+
visit(typeNode);
|
|
49
|
+
return fields;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function goResultType(declaration) {
|
|
53
|
+
const result = declaration?.childForFieldName?.("result");
|
|
54
|
+
if (!result) return null;
|
|
55
|
+
if (result.type !== "parameter_list") return goTypeName(result);
|
|
56
|
+
const declarations = (result.namedChildren || []).filter((child) => child.type === "parameter_declaration");
|
|
57
|
+
// A call used in `value, err := New()` contributes its first result to the
|
|
58
|
+
// first binding. Preserve that receiver type instead of discarding every
|
|
59
|
+
// multi-result signature just because a trailing error is present.
|
|
60
|
+
return goTypeName(declarations[0]?.childForFieldName?.("type"));
|
|
61
|
+
}
|
|
62
|
+
|
|
8
63
|
export default {
|
|
9
64
|
family: "go",
|
|
10
65
|
grammars: ["go"],
|
|
@@ -20,10 +75,40 @@ export default {
|
|
|
20
75
|
const { grammar, tree, fileRel, caps, addSym, addImportEdge, addExternalImport, imports, resolveGoImport, dirFiles, goModule, goRequires } = ctx;
|
|
21
76
|
|
|
22
77
|
// ---- symbols ----
|
|
23
|
-
for (const cap of caps(grammar, `(function_declaration name: (identifier) @fn)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
78
|
+
for (const cap of caps(grammar, `(function_declaration name: (identifier) @fn)`, tree.rootNode)) {
|
|
79
|
+
const declaration = cap.node.parent;
|
|
80
|
+
addSym(cap.node.text, cap.node.startPosition.row + 1, true, {
|
|
81
|
+
sourceNode: declaration,
|
|
82
|
+
selectionNode: cap.node,
|
|
83
|
+
symbolKind: "function",
|
|
84
|
+
returnType: goResultType(declaration),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
for (const cap of caps(grammar, `(method_declaration name: (field_identifier) @method)`, tree.rootNode)) {
|
|
88
|
+
const declaration = cap.node.parent;
|
|
89
|
+
const receiver = declaration.childForFieldName?.("receiver");
|
|
90
|
+
const receiverDeclaration = receiver?.namedChildren?.find((child) => child.type === "parameter_declaration");
|
|
91
|
+
const receiverType = goTypeName(receiverDeclaration?.childForFieldName?.("type"));
|
|
92
|
+
addSym(cap.node.text, cap.node.startPosition.row + 1, true, {
|
|
93
|
+
sourceNode: declaration,
|
|
94
|
+
selectionNode: cap.node,
|
|
95
|
+
symbolKind: "method",
|
|
96
|
+
memberOf: receiverType || undefined,
|
|
97
|
+
receiverType: receiverType || undefined,
|
|
98
|
+
returnType: goResultType(declaration),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
for (const cap of caps(grammar, `(source_file (type_declaration (type_spec name: (type_identifier) @type)))`, tree.rootNode)) {
|
|
102
|
+
const specification = cap.node.parent;
|
|
103
|
+
const typeNode = specification.childForFieldName?.("type");
|
|
104
|
+
addSym(cap.node.text, cap.node.startPosition.row + 1, false, {
|
|
105
|
+
sourceNode: specification,
|
|
106
|
+
selectionNode: cap.node,
|
|
107
|
+
symbolKind: typeNode?.type === "interface_type" ? "interface" : "type",
|
|
108
|
+
symbolSpace: "type",
|
|
109
|
+
fieldTypes: goStructFields(typeNode),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
27
112
|
for (const cap of caps(grammar, `(source_file (var_declaration (var_spec name: (identifier) @var))) (source_file (const_declaration (const_spec name: (identifier) @const)))`, tree.rootNode))
|
|
28
113
|
addSym(cap.node.text, cap.node.startPosition.row + 1, false, { sourceNode: cap.node.parent });
|
|
29
114
|
|
|
@@ -2,33 +2,10 @@
|
|
|
2
2
|
// Symbols: functions, generators, classes, methods, top-level const/let/var (data + arrow/fn consts).
|
|
3
3
|
// Imports: ESM `import`, CJS `require`, path-aliases (tsconfig/vite), and barrel re-exports (`export … from`).
|
|
4
4
|
// Calls: bare `foo()`. Heritage: `class X extends Y`. Everything runs through the shared ctx (see internal-builder.js).
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
(generator_function_declaration name: (identifier) @fn)
|
|
10
|
-
(class_declaration name: (_) @class)
|
|
11
|
-
(method_definition name: (_) @method)`;
|
|
12
|
-
const TOPVARS = `
|
|
13
|
-
(program (lexical_declaration (variable_declarator) @decl))
|
|
14
|
-
(program (variable_declaration (variable_declarator) @decl))
|
|
15
|
-
(program (export_statement (lexical_declaration (variable_declarator) @decl)))
|
|
16
|
-
(program (export_statement (variable_declaration (variable_declarator) @decl)))`;
|
|
17
|
-
const TYPES = `
|
|
18
|
-
(interface_declaration name: (_) @interface)
|
|
19
|
-
(type_alias_declaration name: (_) @type)
|
|
20
|
-
(enum_declaration name: (_) @enum)`;
|
|
21
|
-
const REQUIRE = `(variable_declarator name: (_) @lhs value: (call_expression function: (identifier) @req arguments: (arguments (string (string_fragment) @src))))`;
|
|
22
|
-
|
|
23
|
-
function parseExportSpecifiers(raw) {
|
|
24
|
-
return String(raw || "").split(",").map((part) => {
|
|
25
|
-
const text = part.trim();
|
|
26
|
-
const typeOnly = /^type\s+/.test(text);
|
|
27
|
-
const clean = text.replace(/^type\s+/, "").trim();
|
|
28
|
-
const match = clean.match(/^([A-Za-z_$][\w$]*|default)(?:\s+as\s+([A-Za-z_$][\w$]*|default))?$/);
|
|
29
|
-
return match ? { imported: match[1], exported: match[2] || match[1], typeOnly } : null;
|
|
30
|
-
}).filter(Boolean);
|
|
31
|
-
}
|
|
5
|
+
import {
|
|
6
|
+
CALLABLE, FUNCS, TOPVARS, TYPES, REQUIRE, parseExportSpecifiers,
|
|
7
|
+
importTypeOnly, reexportTypeOnly,
|
|
8
|
+
} from './js/queries.js';
|
|
32
9
|
|
|
33
10
|
export default {
|
|
34
11
|
family: "js",
|
|
@@ -162,23 +139,6 @@ export default {
|
|
|
162
139
|
});
|
|
163
140
|
}
|
|
164
141
|
|
|
165
|
-
const importTypeOnly = (node) => {
|
|
166
|
-
if (/^\s*import\s+type\b/.test(node.text)) return true;
|
|
167
|
-
const clause = node.namedChildren.find((c) => c.type === "import_clause");
|
|
168
|
-
if (!clause) return false; // side-effect import executes the target module
|
|
169
|
-
const parts = clause.namedChildren;
|
|
170
|
-
if (parts.some((c) => c.type === "identifier" || c.type === "namespace_import")) return false;
|
|
171
|
-
const named = parts.find((c) => c.type === "named_imports");
|
|
172
|
-
const specs = named?.namedChildren.filter((c) => c.type === "import_specifier") || [];
|
|
173
|
-
return specs.length > 0 && specs.every((s) => /^\s*type\b/.test(s.text));
|
|
174
|
-
};
|
|
175
|
-
const reexportTypeOnly = (node) => {
|
|
176
|
-
if (/^\s*export\s+type\b/.test(node.text)) return true;
|
|
177
|
-
const clause = node.namedChildren.find((c) => c.type === "export_clause");
|
|
178
|
-
const specs = clause?.namedChildren.filter((c) => c.type === "export_specifier") || [];
|
|
179
|
-
return specs.length > 0 && specs.every((s) => /^\s*type\b/.test(s.text));
|
|
180
|
-
};
|
|
181
|
-
|
|
182
142
|
// ---- ESM imports ----
|
|
183
143
|
for (const cap of caps(grammar, `(import_statement) @imp`, tree.rootNode)) {
|
|
184
144
|
const node = cap.node; const srcNode = field(node, "source");
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export function createPass2Resolution({symIdsByFileName, nodeById, importedLocals, symByFileName}) {
|
|
2
|
+
const dirSymbols = new Map(), dirMethods = new Map(), dirMethodsByName = new Map(), dirTypes = new Map()
|
|
3
|
+
const sharesDirScope = (file) => file.endsWith('.go') || file.endsWith('.cs') || file.endsWith('.rs')
|
|
4
|
+
for (const [file, names] of symIdsByFileName) {
|
|
5
|
+
if (!sharesDirScope(file)) continue
|
|
6
|
+
const dir = file.includes('/') ? file.slice(0, file.lastIndexOf('/')) : ''
|
|
7
|
+
let symbols = dirSymbols.get(dir)
|
|
8
|
+
if (!symbols) dirSymbols.set(dir, (symbols = new Map()))
|
|
9
|
+
for (const [name, ids] of names) for (const id of ids) {
|
|
10
|
+
const node = nodeById.get(id)
|
|
11
|
+
if (file.endsWith('.go') && node?.member_of && node?.symbol_kind === 'method') {
|
|
12
|
+
let receivers = dirMethods.get(dir)
|
|
13
|
+
if (!receivers) dirMethods.set(dir, (receivers = new Map()))
|
|
14
|
+
let methods = receivers.get(node.member_of)
|
|
15
|
+
if (!methods) receivers.set(node.member_of, (methods = new Map()))
|
|
16
|
+
const exact = methods.get(name) || []
|
|
17
|
+
exact.push(id); methods.set(name, exact)
|
|
18
|
+
let byName = dirMethodsByName.get(dir)
|
|
19
|
+
if (!byName) dirMethodsByName.set(dir, (byName = new Map()))
|
|
20
|
+
const candidates = byName.get(name) || []
|
|
21
|
+
candidates.push(id); byName.set(name, candidates)
|
|
22
|
+
continue
|
|
23
|
+
}
|
|
24
|
+
if (!symbols.has(name)) symbols.set(name, id)
|
|
25
|
+
if (file.endsWith('.go') && node?.symbol_space === 'type') {
|
|
26
|
+
let types = dirTypes.get(dir)
|
|
27
|
+
if (!types) dirTypes.set(dir, (types = new Map()))
|
|
28
|
+
if (!types.has(name)) types.set(name, id)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const symbolSpace = (node) => {
|
|
33
|
+
const explicit = String(node?.symbol_space || '')
|
|
34
|
+
if (['value', 'type', 'both'].includes(explicit)) return explicit
|
|
35
|
+
const kind = String(node?.symbol_kind || '').toLowerCase()
|
|
36
|
+
if (['interface', 'type'].includes(kind)) return 'type'
|
|
37
|
+
return ['class', 'enum'].includes(kind) ? 'both' : 'value'
|
|
38
|
+
}
|
|
39
|
+
const resolveNamedSymbol = (file, name, space = 'value') => {
|
|
40
|
+
const ids = symIdsByFileName.get(file)?.get(name) || []
|
|
41
|
+
const exact = ids.find((id) => symbolSpace(nodeById.get(id)) === space)
|
|
42
|
+
return exact || ids.find((id) => ['both', space].includes(symbolSpace(nodeById.get(id)))) || null
|
|
43
|
+
}
|
|
44
|
+
const resolveCall = (name, file) => {
|
|
45
|
+
const local = resolveNamedSymbol(file, name, 'value')
|
|
46
|
+
if (local) return local
|
|
47
|
+
if (sharesDirScope(file)) {
|
|
48
|
+
const dir = file.includes('/') ? file.slice(0, file.lastIndexOf('/')) : ''
|
|
49
|
+
const symbols = dirSymbols.get(dir)
|
|
50
|
+
if (symbols?.has(name)) return symbols.get(name)
|
|
51
|
+
}
|
|
52
|
+
const imported = importedLocals.get(file)?.get(name)
|
|
53
|
+
if (!imported?.targetFile) return null
|
|
54
|
+
return resolveNamedSymbol(imported.originFile || imported.targetFile, imported.originName || imported.imported, 'value')
|
|
55
|
+
}
|
|
56
|
+
const javaTypeKinds = new Set(['class', 'interface', 'enum', 'record', 'annotation'])
|
|
57
|
+
const resolveJavaType = (name, file) => {
|
|
58
|
+
const imported = importedLocals.get(file)?.get(name)
|
|
59
|
+
if (imported?.targetFile) {
|
|
60
|
+
const symbols = symByFileName.get(imported.targetFile)
|
|
61
|
+
const target = symbols?.get(imported.imported) || symbols?.get(name)
|
|
62
|
+
if (target && javaTypeKinds.has(nodeById.get(target)?.symbol_kind)) return target
|
|
63
|
+
}
|
|
64
|
+
const target = symByFileName.get(file)?.get(name)
|
|
65
|
+
return target && javaTypeKinds.has(nodeById.get(target)?.symbol_kind) ? target : null
|
|
66
|
+
}
|
|
67
|
+
return {dirSymbols, dirMethods, dirMethodsByName, dirTypes, resolveNamedSymbol, resolveCall, resolveJavaType}
|
|
68
|
+
}
|
|
@@ -9,7 +9,7 @@ import {childProcessEnv} from '../child-env.js'
|
|
|
9
9
|
import {createRepoBoundary} from '../repo-path.js'
|
|
10
10
|
|
|
11
11
|
export const REPOSITORY_FRESHNESS_PROBE_V = 1
|
|
12
|
-
export const GRAPH_BUILDER_SCHEMA_V =
|
|
12
|
+
export const GRAPH_BUILDER_SCHEMA_V = 6
|
|
13
13
|
export const GRAPH_BUILDER_VERSION = (() => {
|
|
14
14
|
try { return String(createRequire(import.meta.url)('../../package.json').version) }
|
|
15
15
|
catch { return '0.0.0' }
|
|
@@ -23,6 +23,7 @@ const CURRENT_GRAPH_SCHEMA = Object.freeze({
|
|
|
23
23
|
edgeTypesV: 2,
|
|
24
24
|
edgeProvenanceV: 1,
|
|
25
25
|
complexityV: 2,
|
|
26
|
+
physicalFileLocV: 1,
|
|
26
27
|
repoBoundaryV: 1,
|
|
27
28
|
barrelResolutionV: 1,
|
|
28
29
|
reExportOccurrencesV: 1,
|
|
@@ -163,7 +163,7 @@ function mergeScopedGraph(base, scoped, affected, snapshot) {
|
|
|
163
163
|
controlHashes: snapshot.controlHashes,
|
|
164
164
|
graphRevision: snapshot.revision,
|
|
165
165
|
};
|
|
166
|
-
for (const key of ["extImportsV", "edgeTypesV", "edgeProvenanceV", "complexityV", "repoBoundaryV", "barrelResolutionV", "reExportOccurrencesV", "symbolSpacesV", "extractorSchemaV"]) {
|
|
166
|
+
for (const key of ["extImportsV", "edgeTypesV", "edgeProvenanceV", "complexityV", "physicalFileLocV", "repoBoundaryV", "barrelResolutionV", "reExportOccurrencesV", "symbolSpacesV", "extractorSchemaV"]) {
|
|
167
167
|
merged[key] = Math.max(Number(base[key]) || 0, Number(scoped[key]) || 0);
|
|
168
168
|
}
|
|
169
169
|
return merged;
|
|
@@ -184,7 +184,8 @@ export async function refreshGraphIncrementally(repoDir, existingGraph, {
|
|
|
184
184
|
|
|
185
185
|
if (!existingGraph || !existingGraph.fileHashes || !existingGraph.fileExportSignatures
|
|
186
186
|
|| !existingGraph.controlHashes || !existingGraph.jsExportRecords || Number(existingGraph.barrelResolutionV) < 1
|
|
187
|
-
|| Number(existingGraph.extractorSchemaV) < 5 || Number(existingGraph.
|
|
187
|
+
|| Number(existingGraph.extractorSchemaV) < 5 || Number(existingGraph.physicalFileLocV) < 1
|
|
188
|
+
|| Number(existingGraph.reExportOccurrencesV) < 1
|
|
188
189
|
|| Number(existingGraph.symbolSpacesV) < 1 || Number(existingGraph.edgeProvenanceV) < 1) {
|
|
189
190
|
return full("incremental-baseline-unavailable");
|
|
190
191
|
}
|