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.
@@ -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 };