sigmap 1.5.1 → 2.0.0-beta.1

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.
@@ -9,61 +9,177 @@ function extract(src) {
9
9
  if (!src || typeof src !== 'string') return [];
10
10
  const sigs = [];
11
11
 
12
- // Strip comments and docstrings (simple approach)
13
- const stripped = src
14
- .replace(/#.*$/gm, '')
12
+ // noComments: strip only # comments, keep docstrings (needed for @decorator detection)
13
+ const noComments = src.replace(/#.*$/gm, '');
14
+ // stripped: also strip docstrings (safe for regex matching)
15
+ const stripped = noComments
15
16
  .replace(/"""[\s\S]*?"""/g, '')
16
17
  .replace(/'''[\s\S]*?'''/g, '');
17
18
 
18
19
  // Classes
19
20
  for (const m of stripped.matchAll(/^class\s+(\w+)(?:\s*\(([^)]*)\))?\s*:/gm)) {
20
- const base = m[2] ? `(${m[2].trim()})` : '';
21
- sigs.push(`class ${m[1]}${base}`);
22
- // Get class body methods
21
+ const className = m[1];
22
+ const baseName = m[2] ? m[2].trim() : '';
23
23
  const bodyStart = m.index + m[0].length;
24
+
25
+ // Try @dataclass collapse
26
+ const dcFields = tryExtractDataclassFields(stripped, m.index);
27
+ if (dcFields !== null) {
28
+ sigs.push(`@dataclass ${className}(${dcFields})`);
29
+ continue;
30
+ }
31
+
32
+ // Try BaseModel/BaseSettings collapse
33
+ if (/(BaseModel|BaseSettings)/.test(baseName)) {
34
+ const bmFields = tryExtractBaseModelFields(stripped, bodyStart);
35
+ if (bmFields) {
36
+ sigs.push(`class ${className}(${baseName}) ${bmFields}`);
37
+ continue;
38
+ }
39
+ }
40
+
41
+ const baseStr = baseName ? `(${baseName})` : '';
42
+ sigs.push(`class ${className}${baseStr}`);
43
+
44
+ // Class-level ALL_CAPS constants
45
+ for (const c of extractClassConstants(stripped, bodyStart)) {
46
+ sigs.push(` ${c}`);
47
+ }
48
+
49
+ // Methods
24
50
  const methods = extractClassMethods(stripped, bodyStart);
25
51
  for (const meth of methods) sigs.push(` ${meth}`);
26
52
  }
27
53
 
28
54
  // Top-level functions
29
- for (const m of stripped.matchAll(/^(?:async\s+)?def\s+(\w+)\s*\(([^)]*)\)/gm)) {
30
- if (/^_/.test(m[1])) continue; // skip private
31
- const asyncKw = m[0].trimStart().startsWith('async') ? 'async ' : '';
32
- const params = normalizeParams(m[2]);
33
- sigs.push(`${asyncKw}def ${m[1]}(${params})`);
55
+ for (const m of stripped.matchAll(/^((?:async\s+)?def\s+(\w+)\s*\(([^)]*)\)(?:\s*->\s*[^:]+)?)\s*:/gm)) {
56
+ if (/^_/.test(m[2])) continue;
57
+ const asyncKw = m[1].trimStart().startsWith('async') ? 'async ' : '';
58
+ const params = normalizeParams(m[3]);
59
+ const retType = extractReturnType(m[1] + ':');
60
+ const retStr = retType ? ` → ${retType}` : '';
61
+ const hint = extractDocHint(src, m[2], m[0]);
62
+ const hintStr = hint ? ` # ${hint}` : '';
63
+ sigs.push(`${asyncKw}def ${m[2]}(${params})${retStr}${hintStr}`);
34
64
  }
35
65
 
36
66
  return sigs.slice(0, 25);
37
67
  }
38
68
 
39
- function extractClassMethods(src, startIndex) {
69
+ function extractClassMethods(stripped, startIndex) {
40
70
  const methods = [];
41
- // Extract indented block
42
- const lines = src.slice(startIndex).split('\n');
71
+ const lines = stripped.slice(startIndex).split('\n');
43
72
  for (const line of lines) {
44
73
  if (line.trim() === '') continue;
45
- // End of class body: line with no leading indent that is not blank
46
74
  const indent = line.match(/^(\s+)/);
47
75
  if (!indent) break;
48
- const m = line.match(/^\s+(?:async\s+)?def\s+(\w+)\s*\(([^)]*)\)/);
76
+ const m = line.match(/^\s+(?:async\s+)?def\s+(\w+)\s*\(([^)]*)\)(?:\s*->\s*([^:]+))?\s*:/);
49
77
  if (m) {
50
78
  if (m[1].startsWith('__') && m[1] !== '__init__') continue;
51
79
  if (m[1].startsWith('_') && !m[1].startsWith('__')) continue;
52
80
  const asyncKw = line.trimStart().startsWith('async') ? 'async ' : '';
53
- const params = normalizeParams(m[2]).replace(/^self,?\s*/, '');
54
- methods.push(`${asyncKw}def ${m[1]}(${params})`);
81
+ const params = normalizeParams(m[2]).replace(/^self,?\s*/, '').replace(/^cls,?\s*/, '');
82
+ const retType = m[3] ? m[3].trim().replace(/Optional\[([^\]]+)\]/, '$1|None').slice(0, 30) : '';
83
+ const retStr = retType ? ` → ${retType}` : '';
84
+ methods.push(`${asyncKw}def ${m[1]}(${params})${retStr}`);
55
85
  }
56
86
  }
57
87
  return methods.slice(0, 8);
58
88
  }
59
89
 
90
+ function tryExtractDataclassFields(stripped, classIndex) {
91
+ const before = stripped.slice(Math.max(0, classIndex - 120), classIndex);
92
+ if (!/@dataclass/.test(before)) return null;
93
+ const lines = stripped.slice(classIndex).split('\n');
94
+ const fields = [];
95
+ for (const line of lines.slice(1)) {
96
+ if (line.trim() === '') continue;
97
+ if (!line.match(/^\s+/)) break;
98
+ const f = line.match(/^\s+(\w+)\s*:\s*([^=\n]+?)(?:\s*=.*)?$/);
99
+ if (!f) break;
100
+ const isOptional = f[2].includes('Optional') || /=\s*None/.test(line);
101
+ fields.push(isOptional ? `${f[1]}?` : f[1]);
102
+ }
103
+ return fields.length ? fields.join(', ') : null;
104
+ }
105
+
106
+ function tryExtractBaseModelFields(stripped, bodyStart) {
107
+ const lines = stripped.slice(bodyStart, bodyStart + 800).split('\n');
108
+ const fields = [];
109
+ for (const line of lines) {
110
+ if (!line.match(/^\s{4}\w/)) continue;
111
+ const f = line.match(/^\s{4}(\w+)\s*(?::\s*([^=\n]+?))?(?:\s*=\s*(.*))?$/);
112
+ if (!f || f[1].startsWith('_') || f[1] === 'class' || f[1] === 'def') continue;
113
+ const isOptional = (f[2] || '').includes('Optional') || f[3] !== undefined;
114
+ fields.push(isOptional ? `${f[1]}?` : `${f[1]}*`);
115
+ }
116
+ return fields.length ? `{${fields.slice(0, 6).join(', ')}}` : null;
117
+ }
118
+
119
+ function extractClassConstants(stripped, startIndex) {
120
+ const lines = stripped.slice(startIndex).split('\n');
121
+ const consts = [];
122
+ for (const line of lines) {
123
+ if (!line.match(/^\s+/)) break;
124
+ const c = line.match(/^\s+([A-Z][A-Z0-9_]{2,})\s*=\s*(.+)/);
125
+ if (!c) continue;
126
+ let val = c[2].trim();
127
+ const items = (val.match(/"([^"]+)"/g) || val.match(/'([^']+)'/g) || []);
128
+ if (items.length > 3) val = `[${items[0].replace(/['"]/g, '')}..${items[items.length - 1].replace(/['"]/g, '')}]`;
129
+ if (val.length > 40) val = val.slice(0, 37) + '...';
130
+ consts.push(`${c[1]}=${val}`);
131
+ }
132
+ return consts.slice(0, 3);
133
+ }
134
+
135
+ function extractReturnType(sigLine) {
136
+ const m = sigLine.match(/->\s*([^:]+):/);
137
+ if (!m) return '';
138
+ let rt = m[1].trim();
139
+ rt = rt.replace(/Optional\[([^\]]+)\]/, '$1|None');
140
+ return rt.length > 30 ? rt.slice(0, 27) + '...' : rt;
141
+ }
142
+
60
143
  function normalizeParams(params) {
61
144
  if (!params) return '';
62
145
  return params.trim()
63
146
  .split(',')
64
- .map((p) => p.trim().split(':')[0].split('=')[0].trim())
65
- .filter(Boolean)
147
+ .map((p) => {
148
+ const part = p.trim();
149
+ if (!part) return '';
150
+ const stars = part.match(/^(\*{1,2})/)?.[1] || '';
151
+ const rest = part.slice(stars.length);
152
+ const eqIdx = rest.indexOf('=');
153
+ const noDefault = eqIdx !== -1 ? rest.slice(0, eqIdx).trim() : rest.trim();
154
+ const clean = noDefault
155
+ .replace(/Optional\[([^\]]+)\]/, '$1?')
156
+ .replace(/Union\[([^\]]+),\s*None\]/, '$1?');
157
+ return stars + clean;
158
+ })
159
+ .filter((p) => p && p !== 'self' && p !== 'cls')
66
160
  .join(', ');
67
161
  }
68
162
 
163
+ function extractDocHint(src, fnName, fnSigLine) {
164
+ if (!src || !fnName || !fnSigLine) return '';
165
+ const escSig = fnSigLine.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
166
+ const re = new RegExp(`^${escSig.replace(/\s+/g, '\\s+')}`, 'm');
167
+ const pos = src.search(re);
168
+ if (pos === -1) return '';
169
+ const afterSig = src.slice(pos + fnSigLine.length, pos + fnSigLine.length + 600);
170
+
171
+ // Find first non-empty line after function signature indentation
172
+ const m = afterSig.match(/^\s*\n\s*(?:[rubfRUBF]{0,2})?("""|''')([\s\S]*?)\1/m);
173
+ if (!m) return '';
174
+
175
+ const firstLine = m[2]
176
+ .split('\n')
177
+ .map((l) => l.trim())
178
+ .find(Boolean);
179
+ if (!firstLine) return '';
180
+
181
+ const sentence = firstLine.split(/[.!?]/)[0].trim();
182
+ return sentence.slice(0, 60);
183
+ }
184
+
69
185
  module.exports = { extract };
@@ -22,14 +22,16 @@ function extract(src) {
22
22
  if (m[1].startsWith('_')) continue;
23
23
  const params = m[2] ? `(${normalizeParams(m[2])})` : '';
24
24
  const selfPrefix = m[0].includes('self.') ? 'self.' : '';
25
- sigs.push(` def ${selfPrefix}${m[1]}${params}`);
25
+ const retStr = extractReturnHint(stripped, m.index);
26
+ sigs.push(` def ${selfPrefix}${m[1]}${params}${retStr}`);
26
27
  }
27
28
 
28
29
  // Top-level def
29
30
  for (const m of stripped.matchAll(/^def\s+(\w+)(?:\s*\(([^)]*)\))?/gm)) {
30
31
  if (m[1].startsWith('_')) continue;
31
32
  const params = m[2] ? `(${normalizeParams(m[2])})` : '';
32
- sigs.push(`def ${m[1]}${params}`);
33
+ const retStr = extractReturnHint(stripped, m.index);
34
+ sigs.push(`def ${m[1]}${params}${retStr}`);
33
35
  }
34
36
 
35
37
  return sigs.slice(0, 25);
@@ -40,4 +42,13 @@ function normalizeParams(params) {
40
42
  return params.trim().replace(/\s+/g, ' ');
41
43
  }
42
44
 
45
+ function extractReturnHint(stripped, index) {
46
+ const start = Math.max(0, index - 180);
47
+ const before = stripped.slice(start, index);
48
+ const m = before.match(/sig\s*\{[\s\S]*?returns\(([^)]+)\)[\s\S]*?\}\s*$/);
49
+ if (!m) return '';
50
+ const type = m[1].trim().replace(/\s+/g, ' ').slice(0, 25);
51
+ return type ? ` → ${type}` : '';
52
+ }
53
+
43
54
  module.exports = { extract };
@@ -35,10 +35,11 @@ function extract(src) {
35
35
  for (const fn of extractMethods(block)) sigs.push(` ${fn}`);
36
36
  }
37
37
 
38
- // Top-level pub fns
39
- for (const m of stripped.matchAll(/^pub(?:\s+async)?\s+fn\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)/gm)) {
38
+ // Top-level pub fns — capture everything after ) up to { or ; for return type
39
+ for (const m of stripped.matchAll(/^pub(?:\s+async)?\s+fn\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)([^{;]*)/gm)) {
40
40
  const asyncKw = m[0].includes('async') ? 'async ' : '';
41
- sigs.push(`pub ${asyncKw}fn ${m[1]}(${normalizeParams(m[2])})`);
41
+ const retStr = extractReturnType(m[3]);
42
+ sigs.push(`pub ${asyncKw}fn ${m[1]}(${normalizeParams(m[2])})${retStr}`);
42
43
  }
43
44
 
44
45
  return sigs.slice(0, 25);
@@ -57,9 +58,10 @@ function extractBlock(src, startIndex) {
57
58
 
58
59
  function extractMethods(block) {
59
60
  const methods = [];
60
- for (const m of block.matchAll(/^\s+pub(?:\s+async)?\s+fn\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)/gm)) {
61
+ for (const m of block.matchAll(/^\s+pub(?:\s+async)?\s+fn\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)([^{;]*)/gm)) {
61
62
  const asyncKw = m[0].includes('async') ? 'async ' : '';
62
- methods.push(`pub ${asyncKw}fn ${m[1]}(${normalizeParams(m[2])})`);
63
+ const retStr = extractReturnType(m[3]);
64
+ methods.push(`pub ${asyncKw}fn ${m[1]}(${normalizeParams(m[2])})${retStr}`);
63
65
  }
64
66
  return methods.slice(0, 8);
65
67
  }
@@ -69,4 +71,12 @@ function normalizeParams(params) {
69
71
  return params.trim().replace(/\s+/g, ' ');
70
72
  }
71
73
 
74
+ function extractReturnType(afterParen) {
75
+ if (!afterParen) return '';
76
+ const m = afterParen.match(/->\s*([^{;]+)/);
77
+ if (!m) return '';
78
+ const rt = m[1].trim().replace(/\s+/g, ' ');
79
+ return ` \u2192 ${rt.length > 30 ? rt.slice(0, 27) + '...' : rt}`;
80
+ }
81
+
72
82
  module.exports = { extract };
@@ -25,10 +25,12 @@ function extract(src) {
25
25
  }
26
26
 
27
27
  // Top-level defs
28
- for (const m of stripped.matchAll(/^def\s+(\w+)(?:\[[\w, ]+\])?\s*(?:\(([^)]*)\))?/gm)) {
28
+ for (const m of stripped.matchAll(/^def\s+(\w+)(?:\[[\w, ]+\])?\s*(?:\(([^)]*)\))?(?:\s*:\s*([^=\n{]+))?/gm)) {
29
29
  if (m[1].startsWith('_')) continue;
30
30
  const params = m[2] ? `(${normalizeParams(m[2])})` : '';
31
- sigs.push(`def ${m[1]}${params}`);
31
+ const ret = normalizeType(m[3]);
32
+ const retStr = ret ? ` → ${ret}` : '';
33
+ sigs.push(`def ${m[1]}${params}${retStr}`);
32
34
  }
33
35
 
34
36
  return sigs.slice(0, 25);
@@ -47,10 +49,12 @@ function extractBlock(src, startIndex) {
47
49
 
48
50
  function extractMembers(block) {
49
51
  const members = [];
50
- for (const m of block.matchAll(/^\s+def\s+(\w+)(?:\[[\w, ]+\])?\s*(?:\(([^)]*)\))?/gm)) {
52
+ for (const m of block.matchAll(/^\s+def\s+(\w+)(?:\[[\w, ]+\])?\s*(?:\(([^)]*)\))?(?:\s*:\s*([^=\n{]+))?/gm)) {
51
53
  if (m[1].startsWith('_')) continue;
52
54
  const params = m[2] ? `(${normalizeParams(m[2])})` : '';
53
- members.push(`def ${m[1]}${params}`);
55
+ const ret = normalizeType(m[3]);
56
+ const retStr = ret ? ` → ${ret}` : '';
57
+ members.push(`def ${m[1]}${params}${retStr}`);
54
58
  }
55
59
  return members.slice(0, 8);
56
60
  }
@@ -64,4 +68,9 @@ function normalizeParams(params) {
64
68
  .join(', ');
65
69
  }
66
70
 
71
+ function normalizeType(type) {
72
+ if (!type) return '';
73
+ return type.trim().replace(/\s+/g, ' ').slice(0, 25);
74
+ }
75
+
67
76
  module.exports = { extract };
@@ -23,16 +23,18 @@ function extract(src) {
23
23
  }
24
24
 
25
25
  // Exported functions
26
- for (const m of script.matchAll(/^export\s+(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)/gm)) {
26
+ for (const m of script.matchAll(/^export\s+(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)(?:\s*:\s*([^{=\n]+))?/gm)) {
27
27
  const asyncKw = m[0].includes('async') ? 'async ' : '';
28
- sigs.push(`export ${asyncKw}function ${m[1]}(${normalizeParams(m[2])})`);
28
+ const retStr = m[3] ? ` → ${normalizeType(m[3])}` : '';
29
+ sigs.push(`export ${asyncKw}function ${m[1]}(${normalizeParams(m[2])})${retStr}`);
29
30
  }
30
31
 
31
32
  // Top-level functions
32
- for (const m of script.matchAll(/^(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)/gm)) {
33
+ for (const m of script.matchAll(/^(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)(?:\s*:\s*([^{=\n]+))?/gm)) {
33
34
  if (m[1].startsWith('_')) continue;
34
35
  const asyncKw = m[0].startsWith('async') ? 'async ' : '';
35
- sigs.push(`${asyncKw}function ${m[1]}(${normalizeParams(m[2])})`);
36
+ const retStr = m[3] ? ` → ${normalizeType(m[3])}` : '';
37
+ sigs.push(`${asyncKw}function ${m[1]}(${normalizeParams(m[2])})${retStr}`);
36
38
  }
37
39
 
38
40
  // Reactive declarations $:
@@ -48,4 +50,9 @@ function normalizeParams(params) {
48
50
  return params.trim().replace(/\s+/g, ' ');
49
51
  }
50
52
 
53
+ function normalizeType(type) {
54
+ if (!type) return '';
55
+ return type.trim().replace(/[;\s]+$/g, '').replace(/\s+/g, ' ').slice(0, 25);
56
+ }
57
+
51
58
  module.exports = { extract };
@@ -21,10 +21,11 @@ function extract(src) {
21
21
  for (const fn of extractMembers(block)) sigs.push(` ${fn}`);
22
22
  }
23
23
 
24
- // Top-level public functions
25
- for (const m of stripped.matchAll(/^(?:public\s+|internal\s+)?(?:static\s+)?(?:async\s+)?func\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)/gm)) {
24
+ // Top-level public functions — capture everything after ) to end of line for arrow type
25
+ for (const m of stripped.matchAll(/^(?:public\s+|internal\s+)?(?:static\s+)?(?:async\s+)?func\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)([^{\n]*)/gm)) {
26
26
  const asyncKw = m[0].includes('async') ? 'async ' : '';
27
- sigs.push(`${asyncKw}func ${m[1]}(${normalizeParams(m[2])})`);
27
+ const retStr = extractArrowType(m[3]);
28
+ sigs.push(`${asyncKw}func ${m[1]}(${normalizeParams(m[2])})${retStr}`);
28
29
  }
29
30
 
30
31
  return sigs.slice(0, 25);
@@ -43,10 +44,11 @@ function extractBlock(src, startIndex) {
43
44
 
44
45
  function extractMembers(block) {
45
46
  const members = [];
46
- for (const m of block.matchAll(/^\s+(?:public\s+|internal\s+|open\s+)?(?:static\s+|class\s+)?(?:mutating\s+)?(?:async\s+)?func\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)/gm)) {
47
+ for (const m of block.matchAll(/^\s+(?:public\s+|internal\s+|open\s+)?(?:static\s+|class\s+)?(?:mutating\s+)?(?:async\s+)?func\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)([^{\n]*)/gm)) {
47
48
  if (m[1].startsWith('_')) continue;
48
49
  const asyncKw = m[0].includes('async') ? 'async ' : '';
49
- members.push(`${asyncKw}func ${m[1]}(${normalizeParams(m[2])})`);
50
+ const retStr = extractArrowType(m[3]);
51
+ members.push(`${asyncKw}func ${m[1]}(${normalizeParams(m[2])})${retStr}`);
50
52
  }
51
53
  return members.slice(0, 8);
52
54
  }
@@ -60,4 +62,12 @@ function normalizeParams(params) {
60
62
  .join(', ');
61
63
  }
62
64
 
65
+ function extractArrowType(str) {
66
+ if (!str) return '';
67
+ const m = str.match(/->\s*([^\n{]+)/);
68
+ if (!m) return '';
69
+ const rt = m[1].trim().replace(/\s+/g, ' ');
70
+ return ` \u2192 ${rt.length > 25 ? rt.slice(0, 22) + '...' : rt}`;
71
+ }
72
+
63
73
  module.exports = { extract };
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Extract TODO/FIXME/HACK/XXX comments from source text.
5
+ * @param {string} src - Raw file content
6
+ * @returns {{line:number, tag:string, text:string}[]}
7
+ */
8
+ function extractTodos(src) {
9
+ if (!src || typeof src !== 'string') return [];
10
+ const todos = [];
11
+ const lines = src.split('\n');
12
+
13
+ for (let i = 0; i < lines.length; i++) {
14
+ const m = lines[i].match(/(?:\/\/|#)\s*(TODO|FIXME|HACK|XXX)\s*:?\s*(.+)/i);
15
+ if (!m) continue;
16
+ todos.push({
17
+ line: i + 1,
18
+ tag: m[1].toUpperCase(),
19
+ text: m[2].trim().slice(0, 70),
20
+ });
21
+ }
22
+
23
+ return todos;
24
+ }
25
+
26
+ module.exports = { extractTodos };
@@ -50,7 +50,10 @@ function extract(src) {
50
50
  for (const m of stripped.matchAll(/^export\s+(?:async\s+)?function\s+(\w+)\s*(?:<[^(]*>)?\s*\(([^)]*)\)(?:\s*:\s*[^{]+)?\s*\{/gm)) {
51
51
  const asyncKw = /export\s+async/.test(m[0]) ? 'async ' : '';
52
52
  const params = normalizeParams(m[2]);
53
- sigs.push(`export ${asyncKw}function ${m[1]}(${params})`);
53
+ const retMatch = m[0].match(/\)\s*:\s*([^{]+)\s*\{/);
54
+ const retType = retMatch ? retMatch[1].trim().replace(/\s+/g, ' ').slice(0, 30) : '';
55
+ const retStr = retType ? ` → ${retType}` : '';
56
+ sigs.push(`export ${asyncKw}function ${m[1]}(${params})${retStr}`);
54
57
  }
55
58
 
56
59
  // Exported arrow functions / const functions
@@ -58,6 +61,25 @@ function extract(src) {
58
61
  const asyncKw = /=\s*async\s+/.test(m[0]) ? 'async ' : '';
59
62
  const params = normalizeParams(m[2]);
60
63
  sigs.push(`export const ${m[1]} = ${asyncKw}(${params}) =>`);
64
+
65
+ // Hooks: capture compact return object shape for use* functions.
66
+ if (m[1].startsWith('use')) {
67
+ const bodyStart = stripped.indexOf('{', m.index + m[0].length);
68
+ if (bodyStart !== -1) {
69
+ const body = stripped.slice(bodyStart, bodyStart + 800);
70
+ const ret = body.match(/return\s*\{([^}]{1,260})\}/);
71
+ if (ret) {
72
+ const keys = ret[1]
73
+ .split(',')
74
+ .map((s) => s.trim().split(':')[0].split('(')[0].trim())
75
+ .filter(Boolean)
76
+ .slice(0, 8);
77
+ if (keys.length) {
78
+ sigs[sigs.length - 1] += ` → { ${keys.join(', ')} }`;
79
+ }
80
+ }
81
+ }
82
+ }
61
83
  }
62
84
 
63
85
  return sigs.slice(0, 25);
@@ -77,9 +99,11 @@ function extractBlock(src, startIndex) {
77
99
 
78
100
  function extractInterfaceMembers(block) {
79
101
  const members = [];
80
- for (const m of block.matchAll(/^\s+(readonly\s+)?(\w+)\??:\s*[^;]+;/gm)) {
102
+ for (const m of block.matchAll(/^\s+(readonly\s+)?(\w+)(\??):\s*([^;]+);/gm)) {
81
103
  const readonly = m[1] ? 'readonly ' : '';
82
- members.push(`${readonly}${m[2]}`);
104
+ const optional = m[3] ? '?' : '';
105
+ const typeStr = m[4].trim().replace(/\s+/g, ' ').slice(0, 20);
106
+ members.push(`${readonly}${m[2]}${optional}: ${typeStr}`);
83
107
  }
84
108
  for (const m of block.matchAll(/^\s+(\w+)\s*(?:<[^(]*>)?\s*\(([^)]*)\)\s*:/gm)) {
85
109
  members.push(`${m[1]}(${normalizeParams(m[2])})`);
@@ -96,7 +120,10 @@ function extractClassMembers(block) {
96
120
  if (m[1] === 'constructor') { members.push(`constructor(${normalizeParams(m[2])})`); continue; }
97
121
  const isAsync = m[0].includes('async ') ? 'async ' : '';
98
122
  const isStatic = m[0].includes('static ') ? 'static ' : '';
99
- members.push(`${isStatic}${isAsync}${m[1]}(${normalizeParams(m[2])})`);
123
+ const retMatch = m[0].match(/\)\s*:\s*([^{;]+)\s*\{/);
124
+ const retType = retMatch ? retMatch[1].trim().replace(/\s+/g, ' ').slice(0, 20) : '';
125
+ const retStr = retType ? ` → ${retType}` : '';
126
+ members.push(`${isStatic}${isAsync}${m[1]}(${normalizeParams(m[2])})${retStr}`);
100
127
  }
101
128
  return members.slice(0, 8);
102
129
  }
@@ -34,13 +34,22 @@ function extract(src) {
34
34
  // Methods in options API
35
35
  const methodsMatch = script.match(/methods\s*:\s*\{([\s\S]*?)\},?\s*(?:computed|watch|mounted|created|data|\})/);
36
36
  if (methodsMatch) {
37
- for (const m of methodsMatch[1].matchAll(/^\s+(?:async\s+)?(\w+)\s*\(([^)]*)\)/gm)) {
37
+ for (const m of methodsMatch[1].matchAll(/^\s+(?:async\s+)?(\w+)\s*\(([^)]*)\)(?:\s*:\s*([^{=\n]+))?/gm)) {
38
38
  if (m[1].startsWith('_')) continue;
39
39
  const asyncKw = m[0].includes('async') ? 'async ' : '';
40
- sigs.push(` ${asyncKw}${m[1]}(${normalizeParams(m[2])})`);
40
+ const retStr = m[3] ? ` → ${normalizeType(m[3])}` : '';
41
+ sigs.push(` ${asyncKw}${m[1]}(${normalizeParams(m[2])})${retStr}`);
41
42
  }
42
43
  }
43
44
 
45
+ // Top-level functions in <script> (e.g., composition API helpers)
46
+ for (const m of script.matchAll(/^(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)(?:\s*:\s*([^{=\n]+))?/gm)) {
47
+ if (m[1].startsWith('_')) continue;
48
+ const asyncKw = m[0].includes('async') ? 'async ' : '';
49
+ const retStr = m[3] ? ` → ${normalizeType(m[3])}` : '';
50
+ sigs.push(`${asyncKw}function ${m[1]}(${normalizeParams(m[2])})${retStr}`);
51
+ }
52
+
44
53
  // defineProps (Composition API)
45
54
  const definePropsMatch = script.match(/defineProps(?:<[^>]*>)?\s*\(\s*(\{[\s\S]*?\})\s*\)/);
46
55
  if (definePropsMatch) {
@@ -63,4 +72,9 @@ function normalizeParams(params) {
63
72
  return params.trim().replace(/\s+/g, ' ');
64
73
  }
65
74
 
75
+ function normalizeType(type) {
76
+ if (!type) return '';
77
+ return type.trim().replace(/[;\s]+$/g, '').replace(/\s+/g, ' ').slice(0, 25);
78
+ }
79
+
66
80
  module.exports = { extract };
package/src/mcp/server.js CHANGED
@@ -18,7 +18,7 @@ const { readContext, searchSignatures, getMap, createCheckpoint, getRouting, exp
18
18
 
19
19
  const SERVER_INFO = {
20
20
  name: 'sigmap',
21
- version: '1.5.1',
21
+ version: '2.0.0-beta.1',
22
22
  description: 'SigMap MCP server — code signatures on demand',
23
23
  };
24
24