sigmap 7.31.0 → 8.1.0
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/CHANGELOG.md +20 -0
- package/README.md +2 -2
- package/gen-context.js +624 -11
- package/gen-project-map.js +14 -6
- package/llms-full.txt +2 -2
- package/llms.txt +2 -2
- package/package.json +2 -1
- package/packages/cli/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/src/evidence/pack.js +42 -8
- package/src/map/build-ci.js +91 -0
- package/src/map/config-manifest.js +101 -0
- package/src/map/env-schema.js +90 -0
- package/src/map/migrations.js +84 -0
- package/src/mcp/handlers.js +5 -1
- package/src/mcp/server.js +1 -1
- package/src/verify/hallucination-guard.js +25 -0
- package/src/verify/lib-index.js +164 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Local-library signature index (v9.0 G5/D5 — the private-API grounding moat).
|
|
5
|
+
*
|
|
6
|
+
* Context7 knows only *public* library docs. SigMap can do something no
|
|
7
|
+
* competitor can: index the signatures of the libraries **actually installed**
|
|
8
|
+
* in `node_modules` and verify AI suggestions against repo + private +
|
|
9
|
+
* installed-lib symbols. This module builds the installed-lib half.
|
|
10
|
+
*
|
|
11
|
+
* For each **direct** dependency declared in `package.json`, it locates the
|
|
12
|
+
* package under `node_modules/<dep>`, reads its version (D8 version pinning),
|
|
13
|
+
* and extracts the exported symbol names from its TypeScript declaration entry
|
|
14
|
+
* (`types`/`typings`, else `index.d.ts`). Pure, zero-dependency, deterministic:
|
|
15
|
+
* byte-stable given a fixed installed tree. Bounded (per-file read cap + dep
|
|
16
|
+
* cap) and cached via `src/cache/sig-cache.js` so repeat builds are near-free.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
const { loadCache, saveCache, getChangedFiles, updateCacheEntries } = require('../cache/sig-cache');
|
|
22
|
+
|
|
23
|
+
const MAX_DTS_BYTES = 512 * 1024; // per-file read cap
|
|
24
|
+
const MAX_DEPS = 1000; // dep count cap
|
|
25
|
+
const DEP_KEYS = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Extract exported symbol names from a `.d.ts` declaration file. Deterministic,
|
|
29
|
+
* regex-based (declaration files are already normalized, so this is robust
|
|
30
|
+
* without a full TS parser and stays zero-dependency).
|
|
31
|
+
* @param {string} src
|
|
32
|
+
* @returns {string[]} sorted unique exported names
|
|
33
|
+
*/
|
|
34
|
+
function extractDtsExports(src) {
|
|
35
|
+
const names = new Set();
|
|
36
|
+
if (!src) return [];
|
|
37
|
+
|
|
38
|
+
// export [declare] [default] function|const|let|var|class|interface|type|enum|namespace Name
|
|
39
|
+
const declRe = /\bexport\s+(?:declare\s+)?(?:default\s+)?(?:abstract\s+)?(?:async\s+)?(?:function|const|let|var|class|interface|type|enum|namespace|module)\s+([A-Za-z_$][\w$]*)/g;
|
|
40
|
+
let m;
|
|
41
|
+
while ((m = declRe.exec(src)) !== null) names.add(m[1]);
|
|
42
|
+
|
|
43
|
+
// export { a, b as c, default as d }
|
|
44
|
+
const listRe = /\bexport\s*(?:type\s*)?\{([^}]*)\}/g;
|
|
45
|
+
while ((m = listRe.exec(src)) !== null) {
|
|
46
|
+
for (const part of m[1].split(',')) {
|
|
47
|
+
const name = part.trim().split(/\s+as\s+/).pop().trim();
|
|
48
|
+
if (/^[A-Za-z_$][\w$]*$/.test(name) && name !== 'default') names.add(name);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// export as namespace Name / export = Name
|
|
53
|
+
const nsRe = /\bexport\s+as\s+namespace\s+([A-Za-z_$][\w$]*)/g;
|
|
54
|
+
while ((m = nsRe.exec(src)) !== null) names.add(m[1]);
|
|
55
|
+
const assignRe = /\bexport\s*=\s*([A-Za-z_$][\w$]*)/g;
|
|
56
|
+
while ((m = assignRe.exec(src)) !== null) names.add(m[1]);
|
|
57
|
+
|
|
58
|
+
return [...names].sort();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Read direct dependency names declared in the project's package.json. */
|
|
62
|
+
function directDeps(cwd) {
|
|
63
|
+
const names = new Set();
|
|
64
|
+
try {
|
|
65
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
|
|
66
|
+
for (const k of DEP_KEYS) {
|
|
67
|
+
if (pkg[k] && typeof pkg[k] === 'object') {
|
|
68
|
+
for (const n of Object.keys(pkg[k])) names.add(n);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
} catch (_) { /* no/invalid package.json → no deps */ }
|
|
72
|
+
return [...names].sort();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Resolve an installed dependency's version + entry `.d.ts` path.
|
|
77
|
+
* @returns {{ version: string|null, dtsPath: string|null }|null} null if not installed
|
|
78
|
+
*/
|
|
79
|
+
function resolveEntry(cwd, dep) {
|
|
80
|
+
const pkgDir = path.join(cwd, 'node_modules', dep);
|
|
81
|
+
let pkg;
|
|
82
|
+
try { pkg = JSON.parse(fs.readFileSync(path.join(pkgDir, 'package.json'), 'utf8')); } catch (_) { return null; }
|
|
83
|
+
const version = typeof pkg.version === 'string' ? pkg.version : null;
|
|
84
|
+
|
|
85
|
+
const candidates = [];
|
|
86
|
+
const typesField = pkg.types || pkg.typings;
|
|
87
|
+
if (typeof typesField === 'string') {
|
|
88
|
+
candidates.push(typesField);
|
|
89
|
+
candidates.push(path.join(typesField, 'index.d.ts')); // typesField may be a dir
|
|
90
|
+
}
|
|
91
|
+
candidates.push('index.d.ts');
|
|
92
|
+
if (typeof pkg.main === 'string') candidates.push(pkg.main.replace(/\.(js|cjs|mjs)$/, '.d.ts'));
|
|
93
|
+
|
|
94
|
+
for (const c of candidates) {
|
|
95
|
+
const p = path.join(pkgDir, c);
|
|
96
|
+
try { if (fs.statSync(p).isFile()) return { version, dtsPath: p }; } catch (_) { /* next */ }
|
|
97
|
+
}
|
|
98
|
+
return { version, dtsPath: null }; // installed but untyped
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Build the installed-library signature index for `cwd`.
|
|
103
|
+
*
|
|
104
|
+
* @param {string} cwd
|
|
105
|
+
* @param {object} [opts]
|
|
106
|
+
* @param {string} [opts.version='0'] sigmap version, for cache busting
|
|
107
|
+
* @param {boolean} [opts.cache=true] use the on-disk sig-cache
|
|
108
|
+
* @returns {{ symbols: Set<string>, libraries: Array<{name,version,symbols,typed}>, count: number }}
|
|
109
|
+
*/
|
|
110
|
+
function buildLibraryIndex(cwd, opts = {}) {
|
|
111
|
+
const version = opts.version || '0';
|
|
112
|
+
const useCache = opts.cache !== false;
|
|
113
|
+
const deps = directDeps(cwd).slice(0, MAX_DEPS);
|
|
114
|
+
|
|
115
|
+
const entries = [];
|
|
116
|
+
for (const dep of deps) {
|
|
117
|
+
const r = resolveEntry(cwd, dep);
|
|
118
|
+
if (r) entries.push({ dep, version: r.version, dtsPath: r.dtsPath });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const cache = useCache ? loadCache(cwd, version) : new Map();
|
|
122
|
+
const dtsFiles = entries.filter((e) => e.dtsPath).map((e) => e.dtsPath);
|
|
123
|
+
const { unchanged } = getChangedFiles(dtsFiles, cache);
|
|
124
|
+
const unchangedSet = new Set(unchanged);
|
|
125
|
+
|
|
126
|
+
const symbols = new Set();
|
|
127
|
+
const libraries = [];
|
|
128
|
+
const fresh = [];
|
|
129
|
+
|
|
130
|
+
for (const e of entries) {
|
|
131
|
+
let names;
|
|
132
|
+
if (!e.dtsPath) {
|
|
133
|
+
names = [];
|
|
134
|
+
} else if (unchangedSet.has(e.dtsPath) && cache.get(e.dtsPath)) {
|
|
135
|
+
names = cache.get(e.dtsPath).sigs || [];
|
|
136
|
+
} else {
|
|
137
|
+
let src = '';
|
|
138
|
+
try {
|
|
139
|
+
if (fs.statSync(e.dtsPath).size <= MAX_DTS_BYTES) src = fs.readFileSync(e.dtsPath, 'utf8');
|
|
140
|
+
} catch (_) { /* unreadable → empty */ }
|
|
141
|
+
names = extractDtsExports(src);
|
|
142
|
+
fresh.push({ file: e.dtsPath, sigs: names });
|
|
143
|
+
}
|
|
144
|
+
for (const n of names) symbols.add(n);
|
|
145
|
+
libraries.push({ name: e.dep, version: e.version, symbols: names.length, typed: !!e.dtsPath });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (useCache && fresh.length) {
|
|
149
|
+
updateCacheEntries(cache, fresh);
|
|
150
|
+
saveCache(cwd, version, cache);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
libraries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
154
|
+
return { symbols, libraries, count: symbols.size };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** D8: render `name@version` pins for the typed/installed libraries. */
|
|
158
|
+
function formatVersionPins(libraries) {
|
|
159
|
+
return (libraries || [])
|
|
160
|
+
.filter((l) => l.version)
|
|
161
|
+
.map((l) => `${l.name}@${l.version}`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
module.exports = { buildLibraryIndex, extractDtsExports, directDeps, resolveEntry, formatVersionPins };
|