sigmap 1.5.2 → 2.0.0-beta.2

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,181 @@ 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
+ let foundFirst = false;
110
+ for (const line of lines) {
111
+ if (line.trim() === '') continue;
112
+ if (foundFirst && !/^\s/.test(line)) break;
113
+ if (!line.match(/^\s{4}\w/)) continue;
114
+ foundFirst = true;
115
+ const f = line.match(/^\s{4}(\w+)\s*(?::\s*([^=\n]+?))?(?:\s*=\s*(.*))?$/);
116
+ if (!f || f[1].startsWith('_') || f[1] === 'class' || f[1] === 'def') continue;
117
+ const isOptional = (f[2] || '').includes('Optional') || f[3] !== undefined;
118
+ fields.push(isOptional ? `${f[1]}?` : `${f[1]}*`);
119
+ }
120
+ return fields.length ? `{${fields.slice(0, 6).join(', ')}}` : null;
121
+ }
122
+
123
+ function extractClassConstants(stripped, startIndex) {
124
+ const lines = stripped.slice(startIndex).split('\n');
125
+ const consts = [];
126
+ for (const line of lines) {
127
+ if (!line.match(/^\s+/)) break;
128
+ const c = line.match(/^\s+([A-Z][A-Z0-9_]{2,})\s*=\s*(.+)/);
129
+ if (!c) continue;
130
+ let val = c[2].trim();
131
+ const items = (val.match(/"([^"]+)"/g) || val.match(/'([^']+)'/g) || []);
132
+ if (items.length > 3) val = `[${items[0].replace(/['"]/g, '')}..${items[items.length - 1].replace(/['"]/g, '')}]`;
133
+ if (val.length > 40) val = val.slice(0, 37) + '...';
134
+ consts.push(`${c[1]}=${val}`);
135
+ }
136
+ return consts.slice(0, 3);
137
+ }
138
+
139
+ function extractReturnType(sigLine) {
140
+ const m = sigLine.match(/->\s*([^:]+):/);
141
+ if (!m) return '';
142
+ let rt = m[1].trim();
143
+ rt = rt.replace(/Optional\[([^\]]+)\]/, '$1|None');
144
+ return rt.length > 30 ? rt.slice(0, 27) + '...' : rt;
145
+ }
146
+
60
147
  function normalizeParams(params) {
61
148
  if (!params) return '';
62
149
  return params.trim()
63
150
  .split(',')
64
- .map((p) => p.trim().split(':')[0].split('=')[0].trim())
65
- .filter(Boolean)
151
+ .map((p) => {
152
+ const part = p.trim();
153
+ if (!part) return '';
154
+ const stars = part.match(/^(\*{1,2})/)?.[1] || '';
155
+ const rest = part.slice(stars.length);
156
+ const eqIdx = rest.indexOf('=');
157
+ const noDefault = eqIdx !== -1 ? rest.slice(0, eqIdx).trim() : rest.trim();
158
+ const clean = noDefault
159
+ .replace(/Optional\[([^\]]+)\]/, '$1?')
160
+ .replace(/Union\[([^\]]+),\s*None\]/, '$1?');
161
+ return stars + clean;
162
+ })
163
+ .filter((p) => p && p !== 'self' && p !== 'cls')
66
164
  .join(', ');
67
165
  }
68
166
 
167
+ function extractDocHint(src, fnName, fnSigLine) {
168
+ if (!src || !fnName || !fnSigLine) return '';
169
+ const escSig = fnSigLine.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
170
+ const re = new RegExp(`^${escSig.replace(/\s+/g, '\\s+')}`, 'm');
171
+ const pos = src.search(re);
172
+ if (pos === -1) return '';
173
+ const afterSig = src.slice(pos + fnSigLine.length, pos + fnSigLine.length + 600);
174
+
175
+ // Find first non-empty line after function signature indentation
176
+ const m = afterSig.match(/^\s*\n\s*(?:[rubfRUBF]{0,2})?("""|''')([\s\S]*?)\1/m);
177
+ if (!m) return '';
178
+
179
+ const firstLine = m[2]
180
+ .split('\n')
181
+ .map((l) => l.trim())
182
+ .find(Boolean);
183
+ if (!firstLine) return '';
184
+
185
+ const sentence = firstLine.split(/[.!?]/)[0].trim();
186
+ return sentence.slice(0, 60);
187
+ }
188
+
69
189
  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,27 @@ 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}`);
57
+
58
+ // Hooks: capture compact return object shape for use* functions.
59
+ if (m[1].startsWith('use')) {
60
+ const bodyStart = m.index + m[0].length;
61
+ const body = stripped.slice(bodyStart, bodyStart + 800);
62
+ const ret = body.match(/return\s*\{([^}]{1,260})\}/);
63
+ if (ret) {
64
+ const keys = ret[1]
65
+ .split(',')
66
+ .map((s) => s.trim().split(':')[0].split('(')[0].trim())
67
+ .filter(Boolean)
68
+ .slice(0, 8);
69
+ if (keys.length) {
70
+ sigs[sigs.length - 1] += ` → { ${keys.join(', ')} }`;
71
+ }
72
+ }
73
+ }
54
74
  }
55
75
 
56
76
  // Exported arrow functions / const functions
@@ -58,6 +78,25 @@ function extract(src) {
58
78
  const asyncKw = /=\s*async\s+/.test(m[0]) ? 'async ' : '';
59
79
  const params = normalizeParams(m[2]);
60
80
  sigs.push(`export const ${m[1]} = ${asyncKw}(${params}) =>`);
81
+
82
+ // Hooks: capture compact return object shape for use* functions.
83
+ if (m[1].startsWith('use')) {
84
+ const bodyStart = stripped.indexOf('{', m.index + m[0].length);
85
+ if (bodyStart !== -1) {
86
+ const body = stripped.slice(bodyStart, bodyStart + 800);
87
+ const ret = body.match(/return\s*\{([^}]{1,260})\}/);
88
+ if (ret) {
89
+ const keys = ret[1]
90
+ .split(',')
91
+ .map((s) => s.trim().split(':')[0].split('(')[0].trim())
92
+ .filter(Boolean)
93
+ .slice(0, 8);
94
+ if (keys.length) {
95
+ sigs[sigs.length - 1] += ` → { ${keys.join(', ')} }`;
96
+ }
97
+ }
98
+ }
99
+ }
61
100
  }
62
101
 
63
102
  return sigs.slice(0, 25);
@@ -77,9 +116,11 @@ function extractBlock(src, startIndex) {
77
116
 
78
117
  function extractInterfaceMembers(block) {
79
118
  const members = [];
80
- for (const m of block.matchAll(/^\s+(readonly\s+)?(\w+)\??:\s*[^;]+;/gm)) {
119
+ for (const m of block.matchAll(/^\s+(readonly\s+)?(\w+)(\??):\s*([^;]+);/gm)) {
81
120
  const readonly = m[1] ? 'readonly ' : '';
82
- members.push(`${readonly}${m[2]}`);
121
+ const optional = m[3] ? '?' : '';
122
+ const typeStr = m[4].trim().replace(/\s+/g, ' ').slice(0, 35);
123
+ members.push(`${readonly}${m[2]}${optional}: ${typeStr}`);
83
124
  }
84
125
  for (const m of block.matchAll(/^\s+(\w+)\s*(?:<[^(]*>)?\s*\(([^)]*)\)\s*:/gm)) {
85
126
  members.push(`${m[1]}(${normalizeParams(m[2])})`);
@@ -96,7 +137,10 @@ function extractClassMembers(block) {
96
137
  if (m[1] === 'constructor') { members.push(`constructor(${normalizeParams(m[2])})`); continue; }
97
138
  const isAsync = m[0].includes('async ') ? 'async ' : '';
98
139
  const isStatic = m[0].includes('static ') ? 'static ' : '';
99
- members.push(`${isStatic}${isAsync}${m[1]}(${normalizeParams(m[2])})`);
140
+ const retMatch = m[0].match(/\)\s*:\s*([^{;]+)\s*\{/);
141
+ const retType = retMatch ? retMatch[1].trim().replace(/\s+/g, ' ').slice(0, 20) : '';
142
+ const retStr = retType ? ` → ${retType}` : '';
143
+ members.push(`${isStatic}${isAsync}${m[1]}(${normalizeParams(m[2])})${retStr}`);
100
144
  }
101
145
  return members.slice(0, 8);
102
146
  }
@@ -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.2',
21
+ version: '2.0.0-beta.2',
22
22
  description: 'SigMap MCP server — code signatures on demand',
23
23
  };
24
24