@graphmemory/server 1.2.0 → 1.3.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.
- package/LICENSE +84 -12
- package/README.md +7 -1
- package/dist/api/index.js +151 -54
- package/dist/api/rest/code.js +2 -1
- package/dist/api/rest/docs.js +2 -1
- package/dist/api/rest/embed.js +8 -1
- package/dist/api/rest/index.js +39 -18
- package/dist/api/rest/knowledge.js +4 -2
- package/dist/api/rest/skills.js +2 -1
- package/dist/api/rest/tasks.js +2 -1
- package/dist/api/rest/tools.js +8 -1
- package/dist/api/rest/validation.js +41 -40
- package/dist/api/rest/websocket.js +24 -7
- package/dist/api/tools/code/search-code.js +12 -9
- package/dist/api/tools/code/search-files.js +1 -1
- package/dist/api/tools/docs/cross-references.js +3 -2
- package/dist/api/tools/docs/explain-symbol.js +2 -1
- package/dist/api/tools/docs/find-examples.js +2 -1
- package/dist/api/tools/docs/search-files.js +1 -1
- package/dist/api/tools/docs/search-snippets.js +1 -1
- package/dist/api/tools/docs/search.js +5 -4
- package/dist/api/tools/file-index/search-all-files.js +1 -1
- package/dist/api/tools/knowledge/add-attachment.js +15 -3
- package/dist/api/tools/knowledge/remove-attachment.js +5 -1
- package/dist/api/tools/knowledge/search-notes.js +5 -4
- package/dist/api/tools/skills/add-attachment.js +15 -3
- package/dist/api/tools/skills/recall-skills.js +1 -1
- package/dist/api/tools/skills/remove-attachment.js +5 -1
- package/dist/api/tools/skills/search-skills.js +6 -5
- package/dist/api/tools/tasks/add-attachment.js +15 -3
- package/dist/api/tools/tasks/remove-attachment.js +5 -1
- package/dist/api/tools/tasks/search-tasks.js +5 -4
- package/dist/cli/index.js +63 -52
- package/dist/cli/indexer.js +62 -29
- package/dist/graphs/attachment-types.js +5 -0
- package/dist/graphs/code.js +99 -10
- package/dist/graphs/docs.js +20 -5
- package/dist/graphs/file-index.js +22 -6
- package/dist/graphs/file-lang.js +1 -1
- package/dist/graphs/knowledge.js +31 -7
- package/dist/graphs/skill.js +35 -9
- package/dist/graphs/task.js +35 -9
- package/dist/lib/defaults.js +78 -0
- package/dist/lib/embedder.js +11 -12
- package/dist/lib/embedding-codec.js +63 -0
- package/dist/lib/graph-persistence.js +68 -0
- package/dist/lib/jwt.js +4 -4
- package/dist/lib/mirror-watcher.js +4 -3
- package/dist/lib/multi-config.js +6 -1
- package/dist/lib/parsers/code.js +158 -31
- package/dist/lib/parsers/codeblock.js +11 -6
- package/dist/lib/parsers/docs.js +60 -31
- package/dist/lib/parsers/languages/registry.js +2 -2
- package/dist/lib/parsers/languages/typescript.js +214 -46
- package/dist/lib/project-manager.js +21 -11
- package/dist/lib/search/bm25.js +23 -5
- package/dist/lib/search/code.js +13 -3
- package/dist/lib/search/docs.js +2 -1
- package/dist/lib/search/file-index.js +2 -1
- package/dist/lib/search/files.js +3 -2
- package/dist/lib/search/knowledge.js +2 -1
- package/dist/lib/search/skills.js +2 -1
- package/dist/lib/search/tasks.js +2 -1
- package/dist/ui/assets/NoteForm-aZX9f6-3.js +1 -0
- package/dist/ui/assets/SkillForm-KYa3o92l.js +1 -0
- package/dist/ui/assets/TaskForm-Bl5nkybO.js +1 -0
- package/dist/ui/assets/_articleId_-DjbCByxM.js +1 -0
- package/dist/ui/assets/_docId_-hdCDjclV.js +1 -0
- package/dist/ui/assets/_filePath_-CpG836v4.js +1 -0
- package/dist/ui/assets/_noteId_-C1enaQd1.js +1 -0
- package/dist/ui/assets/_skillId_-hPoCet7J.js +1 -0
- package/dist/ui/assets/_taskId_-DSB3dLVz.js +1 -0
- package/dist/ui/assets/_toolName_-3SmCfxZy.js +2 -0
- package/dist/ui/assets/api-BMnBjMMf.js +1 -0
- package/dist/ui/assets/api-BlFF6gX-.js +1 -0
- package/dist/ui/assets/api-CrGJOcaN.js +1 -0
- package/dist/ui/assets/api-DuX-0a_X.js +1 -0
- package/dist/ui/assets/attachments-CEQ-2nMo.js +1 -0
- package/dist/ui/assets/client-Bq88u7gN.js +1 -0
- package/dist/ui/assets/docs-CrXsRcOG.js +1 -0
- package/dist/ui/assets/edit-BYiy1FZy.js +1 -0
- package/dist/ui/assets/edit-TUIIpUMF.js +1 -0
- package/dist/ui/assets/edit-hc-ZWz3y.js +1 -0
- package/dist/ui/assets/esm-BWiKNcBW.js +1 -0
- package/dist/ui/assets/files-0bPg6NH9.js +1 -0
- package/dist/ui/assets/graph-DXGud_wF.js +1 -0
- package/dist/ui/assets/help-CEMQqZUR.js +891 -0
- package/dist/ui/assets/help-DJ52_fxN.js +1 -0
- package/dist/ui/assets/index-BCZDAYZi.js +2 -0
- package/dist/ui/assets/index-D6zSNtzo.css +1 -0
- package/dist/ui/assets/knowledge-DeygeGGH.js +1 -0
- package/dist/ui/assets/new-CpD7hOBA.js +1 -0
- package/dist/ui/assets/new-DHTg3Dqq.js +1 -0
- package/dist/ui/assets/new-s8c0M75X.js +1 -0
- package/dist/ui/assets/prompts-BgOmdxgM.js +295 -0
- package/dist/ui/assets/rolldown-runtime-Dw2cE7zH.js +1 -0
- package/dist/ui/assets/search-EpJhdP2a.js +1 -0
- package/dist/ui/assets/skill-y9pizyqE.js +1 -0
- package/dist/ui/assets/skills-Cga9iUZN.js +1 -0
- package/dist/ui/assets/tasks-CobouTKV.js +1 -0
- package/dist/ui/assets/tools-JxKH5BDF.js +1 -0
- package/dist/ui/assets/vendor-graph-BWpSgpMe.js +321 -0
- package/dist/ui/assets/vendor-markdown-CT8ZVEPu.js +50 -0
- package/dist/ui/assets/vendor-md-editor-DmWafJvr.js +44 -0
- package/dist/ui/assets/{index-kKd4mVrh.css → vendor-md-editor-HrwGbQou.css} +1 -1
- package/dist/ui/assets/vendor-mui-BPj7d3Sw.js +139 -0
- package/dist/ui/assets/vendor-mui-icons-B196sG3f.js +1 -0
- package/dist/ui/assets/vendor-react-CHUjhoxh.js +11 -0
- package/dist/ui/index.html +11 -3
- package/package.json +6 -3
- package/dist/ui/assets/index-0hRezICt.js +0 -1702
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Central named constants for all tunable values.
|
|
4
|
+
* Import from here instead of hardcoding magic numbers.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.WIKI_MAX_DEPTH = exports.DEFAULT_EMBEDDING_CACHE_SIZE = exports.MIRROR_MTIME_TOLERANCE_MS = exports.MIRROR_MAX_ENTRIES = exports.MIRROR_STALE_MS = exports.ERROR_BODY_LIMIT = exports.GRACEFUL_SHUTDOWN_TIMEOUT_MS = exports.SESSION_SWEEP_INTERVAL_MS = exports.RATE_LIMIT_WINDOW_MS = exports.REMOTE_BASE_DELAY_MS = exports.REMOTE_MAX_RETRIES = exports.WS_DEBOUNCE_MS = exports.AUTO_SAVE_INTERVAL_MS = exports.MAX_PASSWORD_LEN = exports.MAX_ATTACHMENT_FILENAME_LEN = exports.MAX_PROJECT_ID_LEN = exports.MAX_LINK_KIND_LEN = exports.MAX_TARGET_NODE_ID_LEN = exports.MAX_SKILL_TRIGGERS_COUNT = exports.MAX_SKILL_TRIGGER_LEN = exports.MAX_SKILL_STEPS_COUNT = exports.MAX_SKILL_STEP_LEN = exports.MAX_ASSIGNEE_LEN = exports.MAX_DESCRIPTION_LEN = exports.MAX_SEARCH_TOP_K = exports.MAX_SEARCH_QUERY_LEN = exports.MAX_TAGS_COUNT = exports.MAX_TAG_LEN = exports.MAX_NOTE_CONTENT_LEN = exports.MAX_TITLE_LEN = exports.INDEXER_PREVIEW_LEN = exports.CONTENT_PREVIEW_LEN = exports.LIST_LIMIT_LARGE = exports.LIST_LIMIT_SMALL = exports.SIGNATURE_MAX_LEN = exports.MAX_UPLOAD_SIZE = exports.MAX_BODY_SIZE = exports.BM25_BODY_MAX_CHARS = exports.FILE_SEARCH_TOP_K = exports.SEARCH_MIN_SCORE_FILES = exports.SEARCH_MIN_SCORE_CODE = exports.SEARCH_MIN_SCORE = exports.SEARCH_BFS_DECAY = exports.SEARCH_MAX_RESULTS = exports.SEARCH_BFS_DEPTH = exports.SEARCH_TOP_K = exports.RRF_K = exports.BM25_IDF_OFFSET = exports.BM25_B = exports.BM25_K1 = void 0;
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Search — BM25, RRF, BFS
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
exports.BM25_K1 = 1.2;
|
|
12
|
+
exports.BM25_B = 0.75;
|
|
13
|
+
exports.BM25_IDF_OFFSET = 0.5;
|
|
14
|
+
exports.RRF_K = 60;
|
|
15
|
+
exports.SEARCH_TOP_K = 5;
|
|
16
|
+
exports.SEARCH_BFS_DEPTH = 1;
|
|
17
|
+
exports.SEARCH_MAX_RESULTS = 20;
|
|
18
|
+
exports.SEARCH_BFS_DECAY = 0.8;
|
|
19
|
+
exports.SEARCH_MIN_SCORE = 0.5;
|
|
20
|
+
exports.SEARCH_MIN_SCORE_CODE = 0.3;
|
|
21
|
+
exports.SEARCH_MIN_SCORE_FILES = 0.3;
|
|
22
|
+
exports.FILE_SEARCH_TOP_K = 10;
|
|
23
|
+
exports.BM25_BODY_MAX_CHARS = 2000;
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Limits — sizes, counts, truncation
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
exports.MAX_BODY_SIZE = 10 * 1024 * 1024; // 10 MB
|
|
28
|
+
exports.MAX_UPLOAD_SIZE = 50 * 1024 * 1024; // 50 MB (multer)
|
|
29
|
+
exports.SIGNATURE_MAX_LEN = 300;
|
|
30
|
+
exports.LIST_LIMIT_SMALL = 20;
|
|
31
|
+
exports.LIST_LIMIT_LARGE = 50;
|
|
32
|
+
exports.CONTENT_PREVIEW_LEN = 500;
|
|
33
|
+
exports.INDEXER_PREVIEW_LEN = 200;
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Validation — REST API schema limits
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
exports.MAX_TITLE_LEN = 500;
|
|
38
|
+
exports.MAX_NOTE_CONTENT_LEN = 1_000_000;
|
|
39
|
+
exports.MAX_TAG_LEN = 100;
|
|
40
|
+
exports.MAX_TAGS_COUNT = 100;
|
|
41
|
+
exports.MAX_SEARCH_QUERY_LEN = 2000;
|
|
42
|
+
exports.MAX_SEARCH_TOP_K = 500;
|
|
43
|
+
exports.MAX_DESCRIPTION_LEN = 500_000;
|
|
44
|
+
exports.MAX_ASSIGNEE_LEN = 100;
|
|
45
|
+
exports.MAX_SKILL_STEP_LEN = 10_000;
|
|
46
|
+
exports.MAX_SKILL_STEPS_COUNT = 100;
|
|
47
|
+
exports.MAX_SKILL_TRIGGER_LEN = 500;
|
|
48
|
+
exports.MAX_SKILL_TRIGGERS_COUNT = 50;
|
|
49
|
+
exports.MAX_TARGET_NODE_ID_LEN = 500;
|
|
50
|
+
exports.MAX_LINK_KIND_LEN = 100;
|
|
51
|
+
exports.MAX_PROJECT_ID_LEN = 200;
|
|
52
|
+
exports.MAX_ATTACHMENT_FILENAME_LEN = 255;
|
|
53
|
+
exports.MAX_PASSWORD_LEN = 256;
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Timing — intervals, retries, timeouts
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
exports.AUTO_SAVE_INTERVAL_MS = 30_000;
|
|
58
|
+
exports.WS_DEBOUNCE_MS = 1000;
|
|
59
|
+
exports.REMOTE_MAX_RETRIES = 3;
|
|
60
|
+
exports.REMOTE_BASE_DELAY_MS = 200;
|
|
61
|
+
exports.RATE_LIMIT_WINDOW_MS = 60_000;
|
|
62
|
+
exports.SESSION_SWEEP_INTERVAL_MS = 60_000;
|
|
63
|
+
exports.GRACEFUL_SHUTDOWN_TIMEOUT_MS = 5000;
|
|
64
|
+
exports.ERROR_BODY_LIMIT = 500;
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Mirror
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
exports.MIRROR_STALE_MS = 10_000;
|
|
69
|
+
exports.MIRROR_MAX_ENTRIES = 10_000;
|
|
70
|
+
exports.MIRROR_MTIME_TOLERANCE_MS = 100;
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// Embedder
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
exports.DEFAULT_EMBEDDING_CACHE_SIZE = 10_000;
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// Parser
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
exports.WIKI_MAX_DEPTH = 10;
|
package/dist/lib/embedder.js
CHANGED
|
@@ -12,10 +12,11 @@ exports.cosineSimilarity = cosineSimilarity;
|
|
|
12
12
|
const transformers_1 = require("@huggingface/transformers");
|
|
13
13
|
const fs_1 = __importDefault(require("fs"));
|
|
14
14
|
const path_1 = __importDefault(require("path"));
|
|
15
|
+
const defaults_1 = require("../lib/defaults");
|
|
15
16
|
// ---------------------------------------------------------------------------
|
|
16
17
|
// LRU cache for embedding vectors (avoids re-computing identical texts)
|
|
17
18
|
// ---------------------------------------------------------------------------
|
|
18
|
-
const DEFAULT_CACHE_SIZE =
|
|
19
|
+
const DEFAULT_CACHE_SIZE = defaults_1.DEFAULT_EMBEDDING_CACHE_SIZE;
|
|
19
20
|
class LruCache {
|
|
20
21
|
maxSize;
|
|
21
22
|
map = new Map();
|
|
@@ -97,26 +98,24 @@ async function loadModel(model, embedding, modelsDir, name = 'default') {
|
|
|
97
98
|
// ---------------------------------------------------------------------------
|
|
98
99
|
// Remote embedding HTTP client
|
|
99
100
|
// ---------------------------------------------------------------------------
|
|
100
|
-
const REMOTE_MAX_RETRIES = 3;
|
|
101
|
-
const REMOTE_BASE_DELAY_MS = 200;
|
|
102
101
|
async function remoteEmbed(url, texts, apiKey) {
|
|
103
102
|
const headers = { 'Content-Type': 'application/json' };
|
|
104
103
|
if (apiKey)
|
|
105
104
|
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
106
105
|
const body = JSON.stringify({ texts });
|
|
107
|
-
for (let attempt = 0; attempt < REMOTE_MAX_RETRIES; attempt++) {
|
|
106
|
+
for (let attempt = 0; attempt < defaults_1.REMOTE_MAX_RETRIES; attempt++) {
|
|
108
107
|
let resp;
|
|
109
108
|
try {
|
|
110
109
|
resp = await fetch(url, { method: 'POST', headers, body });
|
|
111
110
|
}
|
|
112
111
|
catch (err) {
|
|
113
112
|
// Network error — retry
|
|
114
|
-
if (attempt < REMOTE_MAX_RETRIES - 1) {
|
|
115
|
-
const delay = REMOTE_BASE_DELAY_MS * 2 ** attempt;
|
|
113
|
+
if (attempt < defaults_1.REMOTE_MAX_RETRIES - 1) {
|
|
114
|
+
const delay = defaults_1.REMOTE_BASE_DELAY_MS * 2 ** attempt;
|
|
116
115
|
await new Promise(r => setTimeout(r, delay));
|
|
117
116
|
continue;
|
|
118
117
|
}
|
|
119
|
-
throw new Error(`Remote embed network error after ${REMOTE_MAX_RETRIES} attempts: ${err}`);
|
|
118
|
+
throw new Error(`Remote embed network error after ${defaults_1.REMOTE_MAX_RETRIES} attempts: ${err}`);
|
|
120
119
|
}
|
|
121
120
|
if (resp.ok) {
|
|
122
121
|
const data = await resp.json();
|
|
@@ -124,17 +123,17 @@ async function remoteEmbed(url, texts, apiKey) {
|
|
|
124
123
|
}
|
|
125
124
|
// Client errors (4xx) — don't retry
|
|
126
125
|
if (resp.status < 500) {
|
|
127
|
-
const respBody = (await resp.text()).slice(0,
|
|
126
|
+
const respBody = (await resp.text()).slice(0, defaults_1.ERROR_BODY_LIMIT);
|
|
128
127
|
throw new Error(`Remote embed failed (${resp.status}): ${respBody}`);
|
|
129
128
|
}
|
|
130
129
|
// Server errors (5xx) — retry
|
|
131
|
-
if (attempt < REMOTE_MAX_RETRIES - 1) {
|
|
132
|
-
const delay = REMOTE_BASE_DELAY_MS * 2 ** attempt;
|
|
130
|
+
if (attempt < defaults_1.REMOTE_MAX_RETRIES - 1) {
|
|
131
|
+
const delay = defaults_1.REMOTE_BASE_DELAY_MS * 2 ** attempt;
|
|
133
132
|
await new Promise(r => setTimeout(r, delay));
|
|
134
133
|
continue;
|
|
135
134
|
}
|
|
136
|
-
const respBody = (await resp.text()).slice(0,
|
|
137
|
-
throw new Error(`Remote embed failed after ${REMOTE_MAX_RETRIES} attempts (${resp.status}): ${respBody}`);
|
|
135
|
+
const respBody = (await resp.text()).slice(0, defaults_1.ERROR_BODY_LIMIT);
|
|
136
|
+
throw new Error(`Remote embed failed after ${defaults_1.REMOTE_MAX_RETRIES} attempts (${resp.status}): ${respBody}`);
|
|
138
137
|
}
|
|
139
138
|
throw new Error('Remote embed: unreachable');
|
|
140
139
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Encode/decode embedding vectors as Base64 for compact JSON serialization.
|
|
4
|
+
* Float32Array → Base64 string saves ~3x vs JSON number arrays.
|
|
5
|
+
* Backwards compatible: detects old format (number[]) on load.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.float32ToBase64 = float32ToBase64;
|
|
9
|
+
exports.base64ToFloat32 = base64ToFloat32;
|
|
10
|
+
exports.compressEmbeddings = compressEmbeddings;
|
|
11
|
+
exports.decompressEmbeddings = decompressEmbeddings;
|
|
12
|
+
const EMBEDDING_FIELDS = ['embedding', 'fileEmbedding'];
|
|
13
|
+
/** Convert a number[] to a Base64-encoded Float32Array. */
|
|
14
|
+
function float32ToBase64(arr) {
|
|
15
|
+
const f32 = new Float32Array(arr);
|
|
16
|
+
return Buffer.from(f32.buffer).toString('base64');
|
|
17
|
+
}
|
|
18
|
+
/** Convert a Base64-encoded Float32Array back to number[]. */
|
|
19
|
+
function base64ToFloat32(b64) {
|
|
20
|
+
const buf = Buffer.from(b64, 'base64');
|
|
21
|
+
// Copy to aligned buffer to guarantee 4-byte alignment for Float32Array
|
|
22
|
+
const aligned = new Uint8Array(buf.byteLength);
|
|
23
|
+
aligned.set(new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength));
|
|
24
|
+
const f32 = new Float32Array(aligned.buffer, 0, aligned.byteLength / 4);
|
|
25
|
+
return Array.from(f32);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Compress embedding fields in a graphology export object (mutates in place).
|
|
29
|
+
* Converts number[] → Base64 string for fields named 'embedding' or 'fileEmbedding'.
|
|
30
|
+
*/
|
|
31
|
+
function compressEmbeddings(exported) {
|
|
32
|
+
if (!exported?.nodes)
|
|
33
|
+
return;
|
|
34
|
+
for (const node of exported.nodes) {
|
|
35
|
+
const attrs = node.attributes;
|
|
36
|
+
if (!attrs)
|
|
37
|
+
continue;
|
|
38
|
+
for (const field of EMBEDDING_FIELDS) {
|
|
39
|
+
if (Array.isArray(attrs[field]) && attrs[field].length > 0) {
|
|
40
|
+
attrs[field] = float32ToBase64(attrs[field]);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Decompress embedding fields in a graphology export object (mutates in place).
|
|
47
|
+
* Converts Base64 string → number[]. Handles both old format (number[]) and new (string).
|
|
48
|
+
*/
|
|
49
|
+
function decompressEmbeddings(exported) {
|
|
50
|
+
if (!exported?.nodes)
|
|
51
|
+
return;
|
|
52
|
+
for (const node of exported.nodes) {
|
|
53
|
+
const attrs = node.attributes;
|
|
54
|
+
if (!attrs)
|
|
55
|
+
continue;
|
|
56
|
+
for (const field of EMBEDDING_FIELDS) {
|
|
57
|
+
if (typeof attrs[field] === 'string' && attrs[field].length > 0) {
|
|
58
|
+
attrs[field] = base64ToFloat32(attrs[field]);
|
|
59
|
+
}
|
|
60
|
+
// number[] stays as-is (backwards compatible with old format)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.readJsonWithTmpFallback = readJsonWithTmpFallback;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
/**
|
|
39
|
+
* Try to read and parse a JSON file, falling back to .tmp if main file
|
|
40
|
+
* is missing or corrupted (recovery from interrupted save).
|
|
41
|
+
*/
|
|
42
|
+
function readJsonWithTmpFallback(file) {
|
|
43
|
+
const tmp = file + '.tmp';
|
|
44
|
+
// If main file missing but .tmp exists, recover it
|
|
45
|
+
if (!fs.existsSync(file) && fs.existsSync(tmp)) {
|
|
46
|
+
try {
|
|
47
|
+
fs.renameSync(tmp, file);
|
|
48
|
+
}
|
|
49
|
+
catch { /* ignore */ }
|
|
50
|
+
}
|
|
51
|
+
if (fs.existsSync(file)) {
|
|
52
|
+
try {
|
|
53
|
+
return JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Main file corrupted — try .tmp as fallback
|
|
57
|
+
if (fs.existsSync(tmp)) {
|
|
58
|
+
try {
|
|
59
|
+
const data = JSON.parse(fs.readFileSync(tmp, 'utf-8'));
|
|
60
|
+
process.stderr.write(`[graph] Recovered from .tmp file: ${file}\n`);
|
|
61
|
+
return data;
|
|
62
|
+
}
|
|
63
|
+
catch { /* .tmp also bad */ }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
package/dist/lib/jwt.js
CHANGED
|
@@ -85,15 +85,15 @@ function parseTtl(ttl) {
|
|
|
85
85
|
}
|
|
86
86
|
function signAccessToken(userId, secret, ttl) {
|
|
87
87
|
const payload = { userId, type: 'access' };
|
|
88
|
-
return jsonwebtoken_1.default.sign(payload, secret, { expiresIn: parseTtl(ttl) });
|
|
88
|
+
return jsonwebtoken_1.default.sign(payload, secret, { algorithm: 'HS256', expiresIn: parseTtl(ttl) });
|
|
89
89
|
}
|
|
90
90
|
function signRefreshToken(userId, secret, ttl) {
|
|
91
91
|
const payload = { userId, type: 'refresh' };
|
|
92
|
-
return jsonwebtoken_1.default.sign(payload, secret, { expiresIn: parseTtl(ttl) });
|
|
92
|
+
return jsonwebtoken_1.default.sign(payload, secret, { algorithm: 'HS256', expiresIn: parseTtl(ttl) });
|
|
93
93
|
}
|
|
94
94
|
function verifyToken(token, secret) {
|
|
95
95
|
try {
|
|
96
|
-
const decoded = jsonwebtoken_1.default.verify(token, secret);
|
|
96
|
+
const decoded = jsonwebtoken_1.default.verify(token, secret, { algorithms: ['HS256'] });
|
|
97
97
|
if (!decoded.userId || !decoded.type)
|
|
98
98
|
return null;
|
|
99
99
|
return { userId: decoded.userId, type: decoded.type };
|
|
@@ -108,7 +108,7 @@ function verifyToken(token, secret) {
|
|
|
108
108
|
const ACCESS_COOKIE = 'mgm_access';
|
|
109
109
|
const REFRESH_COOKIE = 'mgm_refresh';
|
|
110
110
|
function setAuthCookies(res, accessToken, refreshToken, accessTtl, refreshTtl) {
|
|
111
|
-
const secure = process.env.NODE_ENV
|
|
111
|
+
const secure = process.env.NODE_ENV !== 'development';
|
|
112
112
|
res.cookie(ACCESS_COOKIE, accessToken, {
|
|
113
113
|
httpOnly: true,
|
|
114
114
|
secure,
|
|
@@ -45,6 +45,7 @@ const chokidar_1 = __importDefault(require("chokidar"));
|
|
|
45
45
|
const file_import_1 = require("./file-import");
|
|
46
46
|
const events_log_1 = require("./events-log");
|
|
47
47
|
const frontmatter_1 = require("./frontmatter");
|
|
48
|
+
const defaults_1 = require("../lib/defaults");
|
|
48
49
|
/**
|
|
49
50
|
* Tracks recent mirror writes to suppress re-import (feedback loop prevention).
|
|
50
51
|
* When mirrorNote/mirrorTask writes a file, the watcher will fire —
|
|
@@ -53,8 +54,8 @@ const frontmatter_1 = require("./frontmatter");
|
|
|
53
54
|
class MirrorWriteTracker {
|
|
54
55
|
/** Map from filePath → { mtimeMs (for comparison), recordedAt (for eviction) } */
|
|
55
56
|
recentWrites = new Map();
|
|
56
|
-
static STALE_MS =
|
|
57
|
-
static MAX_ENTRIES =
|
|
57
|
+
static STALE_MS = defaults_1.MIRROR_STALE_MS;
|
|
58
|
+
static MAX_ENTRIES = defaults_1.MIRROR_MAX_ENTRIES;
|
|
58
59
|
/** Called by mirrorNote/mirrorTask after writing a file. */
|
|
59
60
|
recordWrite(filePath) {
|
|
60
61
|
try {
|
|
@@ -76,7 +77,7 @@ class MirrorWriteTracker {
|
|
|
76
77
|
const stat = fs.statSync(filePath, { throwIfNoEntry: false });
|
|
77
78
|
if (!stat)
|
|
78
79
|
return false;
|
|
79
|
-
if (Math.abs(stat.mtimeMs - recorded.mtimeMs) <
|
|
80
|
+
if (Math.abs(stat.mtimeMs - recorded.mtimeMs) < defaults_1.MIRROR_MTIME_TOLERANCE_MS) {
|
|
80
81
|
this.recentWrites.delete(filePath);
|
|
81
82
|
return true;
|
|
82
83
|
}
|
package/dist/lib/multi-config.js
CHANGED
|
@@ -50,6 +50,7 @@ const userSchema = zod_1.z.object({
|
|
|
50
50
|
});
|
|
51
51
|
const graphConfigSchema = zod_1.z.object({
|
|
52
52
|
enabled: zod_1.z.boolean().optional(),
|
|
53
|
+
readonly: zod_1.z.boolean().optional(),
|
|
53
54
|
include: zod_1.z.string().optional(),
|
|
54
55
|
exclude: excludeSchema,
|
|
55
56
|
model: modelConfigSchema.optional(),
|
|
@@ -98,7 +99,7 @@ const serverSchema = zod_1.z.object({
|
|
|
98
99
|
embeddingApi: embeddingApiSchema.optional(),
|
|
99
100
|
defaultAccess: accessLevelSchema.optional(),
|
|
100
101
|
access: accessMapSchema,
|
|
101
|
-
jwtSecret: zod_1.z.string().optional(),
|
|
102
|
+
jwtSecret: zod_1.z.string().min(16).optional(),
|
|
102
103
|
accessTokenTtl: zod_1.z.string().optional(),
|
|
103
104
|
refreshTokenTtl: zod_1.z.string().optional(),
|
|
104
105
|
rateLimit: rateLimitSchema.optional(),
|
|
@@ -107,6 +108,7 @@ const serverSchema = zod_1.z.object({
|
|
|
107
108
|
});
|
|
108
109
|
const wsGraphConfigSchema = zod_1.z.object({
|
|
109
110
|
enabled: zod_1.z.boolean().optional(),
|
|
111
|
+
readonly: zod_1.z.boolean().optional(),
|
|
110
112
|
exclude: excludeSchema,
|
|
111
113
|
model: modelConfigSchema.optional(),
|
|
112
114
|
embedding: embeddingConfigSchema.optional(),
|
|
@@ -301,6 +303,7 @@ function loadMultiConfig(yamlPath) {
|
|
|
301
303
|
const graphExclude = [...projectExclude, ...parseExclude(gc?.exclude)];
|
|
302
304
|
graphConfigs[gn] = {
|
|
303
305
|
enabled: gc?.enabled ?? true,
|
|
306
|
+
readonly: gc?.readonly ?? false,
|
|
304
307
|
include: gc?.include ?? (gn === 'docs' ? PROJECT_DEFAULTS.docsInclude : gn === 'code' ? PROJECT_DEFAULTS.codeInclude : undefined),
|
|
305
308
|
exclude: graphExclude,
|
|
306
309
|
model: resolveModel(gc?.model, projectModel),
|
|
@@ -348,6 +351,7 @@ function loadMultiConfig(yamlPath) {
|
|
|
348
351
|
const gc = rawGraphs[gn];
|
|
349
352
|
graphConfigs[gn] = {
|
|
350
353
|
enabled: gc?.enabled ?? true,
|
|
354
|
+
readonly: gc?.readonly ?? false,
|
|
351
355
|
exclude: [...wsExclude, ...parseExclude(gc?.exclude)],
|
|
352
356
|
model: resolveModel(gc?.model, wsModel),
|
|
353
357
|
embedding: resolveEmbedding(gc?.embedding, wsEmbedding),
|
|
@@ -418,6 +422,7 @@ function defaultConfig(projectDir) {
|
|
|
418
422
|
for (const gn of exports.GRAPH_NAMES) {
|
|
419
423
|
graphConfigs[gn] = {
|
|
420
424
|
enabled: true,
|
|
425
|
+
readonly: false,
|
|
421
426
|
include: gn === 'docs' ? PROJECT_DEFAULTS.docsInclude : gn === 'code' ? PROJECT_DEFAULTS.codeInclude : undefined,
|
|
422
427
|
exclude: [...server.exclude],
|
|
423
428
|
model: MODEL_DEFAULTS,
|
package/dist/lib/parsers/code.js
CHANGED
|
@@ -3,43 +3,157 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.clearPathMappingsCache = clearPathMappingsCache;
|
|
6
7
|
exports.parseCodeFile = parseCodeFile;
|
|
7
8
|
const fs_1 = __importDefault(require("fs"));
|
|
8
9
|
const path_1 = __importDefault(require("path"));
|
|
9
10
|
const languages_1 = require("../../lib/parsers/languages");
|
|
10
11
|
const file_lang_1 = require("../../graphs/file-lang");
|
|
12
|
+
// Strip line and block comments from JSONC, preserving string contents.
|
|
13
|
+
function stripJsoncComments(text) {
|
|
14
|
+
let result = '';
|
|
15
|
+
let i = 0;
|
|
16
|
+
while (i < text.length) {
|
|
17
|
+
// String literal — copy verbatim
|
|
18
|
+
if (text[i] === '"') {
|
|
19
|
+
const start = i++;
|
|
20
|
+
while (i < text.length && text[i] !== '"') {
|
|
21
|
+
if (text[i] === '\\')
|
|
22
|
+
i++; // skip escaped char
|
|
23
|
+
i++;
|
|
24
|
+
}
|
|
25
|
+
i++; // closing quote
|
|
26
|
+
result += text.slice(start, i);
|
|
27
|
+
// Line comment
|
|
28
|
+
}
|
|
29
|
+
else if (text[i] === '/' && text[i + 1] === '/') {
|
|
30
|
+
while (i < text.length && text[i] !== '\n')
|
|
31
|
+
i++;
|
|
32
|
+
// Block comment
|
|
33
|
+
}
|
|
34
|
+
else if (text[i] === '/' && text[i + 1] === '*') {
|
|
35
|
+
i += 2;
|
|
36
|
+
while (i < text.length && !(text[i] === '*' && text[i + 1] === '/'))
|
|
37
|
+
i++;
|
|
38
|
+
i += 2;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
result += text[i++];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
11
46
|
// ---------------------------------------------------------------------------
|
|
12
47
|
// Import resolution — replaces ts-morph's getModuleSpecifierSourceFile()
|
|
13
48
|
// ---------------------------------------------------------------------------
|
|
14
49
|
const RESOLVE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mts', '.cts', '.mjs', '.cjs'];
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
50
|
+
function hasFile(p) {
|
|
51
|
+
try {
|
|
52
|
+
return fs_1.default.statSync(p).isFile();
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** Try resolving a base path with extensions and index files. */
|
|
59
|
+
function tryResolve(base) {
|
|
20
60
|
if (hasFile(base))
|
|
21
61
|
return base;
|
|
22
|
-
// Try adding extensions (e.g. './foo' → './foo.ts')
|
|
23
62
|
for (const ext of RESOLVE_EXTENSIONS) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return candidate;
|
|
63
|
+
if (hasFile(base + ext))
|
|
64
|
+
return base + ext;
|
|
27
65
|
}
|
|
28
|
-
// Try index files (e.g. './foo' → './foo/index.ts')
|
|
29
66
|
for (const ext of RESOLVE_EXTENSIONS) {
|
|
30
|
-
const
|
|
31
|
-
if (hasFile(
|
|
32
|
-
return
|
|
67
|
+
const idx = path_1.default.join(base, 'index' + ext);
|
|
68
|
+
if (hasFile(idx))
|
|
69
|
+
return idx;
|
|
33
70
|
}
|
|
34
71
|
return null;
|
|
35
72
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
73
|
+
/** Resolve a relative import specifier to an absolute file path, or null. */
|
|
74
|
+
function resolveRelativeImport(fromFile, specifier) {
|
|
75
|
+
const dir = path_1.default.dirname(fromFile);
|
|
76
|
+
return tryResolve(path_1.default.resolve(dir, specifier));
|
|
77
|
+
}
|
|
78
|
+
/** Cache: directory → parsed path mappings (null = no tsconfig found up to root). */
|
|
79
|
+
const _pathMappings = new Map();
|
|
80
|
+
/** Clear cached path mappings (call between projects or on config change). */
|
|
81
|
+
function clearPathMappingsCache() { _pathMappings.clear(); }
|
|
82
|
+
/**
|
|
83
|
+
* Find the nearest tsconfig.json / jsconfig.json walking up from `dir` to `root`.
|
|
84
|
+
* Cached per directory — each directory remembers its resolved mappings.
|
|
85
|
+
*/
|
|
86
|
+
function findPathMappings(dir, root) {
|
|
87
|
+
if (_pathMappings.has(dir))
|
|
88
|
+
return _pathMappings.get(dir);
|
|
89
|
+
// Try this directory
|
|
90
|
+
const result = _parseTsConfig(dir);
|
|
91
|
+
if (result) {
|
|
92
|
+
_pathMappings.set(dir, result);
|
|
93
|
+
return result;
|
|
39
94
|
}
|
|
40
|
-
|
|
41
|
-
|
|
95
|
+
// Walk up unless we've reached the project root
|
|
96
|
+
const parent = path_1.default.dirname(dir);
|
|
97
|
+
if (dir === root || parent === dir) {
|
|
98
|
+
_pathMappings.set(dir, null);
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const parentResult = findPathMappings(parent, root);
|
|
102
|
+
_pathMappings.set(dir, parentResult);
|
|
103
|
+
return parentResult;
|
|
104
|
+
}
|
|
105
|
+
function _parseTsConfig(dir) {
|
|
106
|
+
for (const name of ['tsconfig.json', 'jsconfig.json']) {
|
|
107
|
+
const configPath = path_1.default.join(dir, name);
|
|
108
|
+
if (!hasFile(configPath))
|
|
109
|
+
continue;
|
|
110
|
+
try {
|
|
111
|
+
// Strip JSONC comments while preserving string contents
|
|
112
|
+
const raw = stripJsoncComments(fs_1.default.readFileSync(configPath, 'utf-8'));
|
|
113
|
+
const config = JSON.parse(raw);
|
|
114
|
+
const compilerOptions = config.compilerOptions;
|
|
115
|
+
if (!compilerOptions?.paths)
|
|
116
|
+
continue;
|
|
117
|
+
const baseUrl = compilerOptions.baseUrl
|
|
118
|
+
? path_1.default.resolve(dir, compilerOptions.baseUrl)
|
|
119
|
+
: dir;
|
|
120
|
+
const mappings = [];
|
|
121
|
+
for (const [pattern, targets] of Object.entries(compilerOptions.paths)) {
|
|
122
|
+
// Pattern like "@/*" → prefix "@/", or "utils/*" → prefix "utils/"
|
|
123
|
+
const prefix = pattern.endsWith('/*') ? pattern.slice(0, -1) : pattern;
|
|
124
|
+
const resolvedTargets = targets
|
|
125
|
+
.map(t => {
|
|
126
|
+
const target = t.endsWith('/*') ? t.slice(0, -1) : t;
|
|
127
|
+
return path_1.default.resolve(baseUrl, target);
|
|
128
|
+
});
|
|
129
|
+
mappings.push({ prefix, targets: resolvedTargets });
|
|
130
|
+
}
|
|
131
|
+
if (mappings.length > 0)
|
|
132
|
+
return mappings;
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// Malformed config — skip
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
/** Resolve a path-aliased import (e.g. @/lib/foo) using nearest tsconfig paths. */
|
|
141
|
+
function resolveAliasImport(specifier, fromFile, projectDir) {
|
|
142
|
+
const fileDir = path_1.default.dirname(fromFile);
|
|
143
|
+
const mappings = findPathMappings(fileDir, projectDir);
|
|
144
|
+
if (!mappings)
|
|
145
|
+
return null;
|
|
146
|
+
for (const mapping of mappings) {
|
|
147
|
+
if (specifier.startsWith(mapping.prefix)) {
|
|
148
|
+
const rest = specifier.slice(mapping.prefix.length);
|
|
149
|
+
for (const targetDir of mapping.targets) {
|
|
150
|
+
const resolved = tryResolve(path_1.default.join(targetDir, rest));
|
|
151
|
+
if (resolved)
|
|
152
|
+
return resolved;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
42
155
|
}
|
|
156
|
+
return null;
|
|
43
157
|
}
|
|
44
158
|
// ---------------------------------------------------------------------------
|
|
45
159
|
// Main parser
|
|
@@ -62,8 +176,8 @@ async function parseCodeFile(absolutePath, codeDir, mtime) {
|
|
|
62
176
|
};
|
|
63
177
|
}
|
|
64
178
|
const source = fs_1.default.readFileSync(absolutePath, 'utf-8');
|
|
65
|
-
const
|
|
66
|
-
if (!
|
|
179
|
+
const tree = await (0, languages_1.parseSource)(source, language);
|
|
180
|
+
if (!tree) {
|
|
67
181
|
return {
|
|
68
182
|
fileId,
|
|
69
183
|
mtime,
|
|
@@ -74,17 +188,23 @@ async function parseCodeFile(absolutePath, codeDir, mtime) {
|
|
|
74
188
|
edges: [],
|
|
75
189
|
};
|
|
76
190
|
}
|
|
191
|
+
const rootNode = tree.rootNode;
|
|
77
192
|
const mapper = (0, languages_1.getMapper)(language);
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
193
|
+
let symbols, edgeInfos, imports, fileDocComment, importSummary, lastLine;
|
|
194
|
+
try {
|
|
195
|
+
symbols = mapper.extractSymbols(rootNode);
|
|
196
|
+
edgeInfos = mapper.extractEdges(rootNode);
|
|
197
|
+
imports = mapper.extractImports(rootNode);
|
|
198
|
+
fileDocComment = extractFileDocComment(rootNode);
|
|
199
|
+
importSummary = buildImportSummary(rootNode);
|
|
200
|
+
lastLine = (rootNode.endPosition?.row ?? 0) + 1;
|
|
201
|
+
}
|
|
202
|
+
finally {
|
|
203
|
+
tree.delete();
|
|
204
|
+
}
|
|
81
205
|
const nodes = [];
|
|
82
206
|
const edges = [];
|
|
83
207
|
const fileNodeId = fileId;
|
|
84
|
-
// --- File root node ---
|
|
85
|
-
const fileDocComment = extractFileDocComment(rootNode);
|
|
86
|
-
const importSummary = buildImportSummary(rootNode);
|
|
87
|
-
const lastLine = (rootNode.endPosition?.row ?? 0) + 1;
|
|
88
208
|
nodes.push({
|
|
89
209
|
id: fileNodeId,
|
|
90
210
|
attrs: makeFileAttrs(fileId, fileDocComment, importSummary, lastLine, mtime),
|
|
@@ -147,13 +267,20 @@ async function parseCodeFile(absolutePath, codeDir, mtime) {
|
|
|
147
267
|
}
|
|
148
268
|
// --- Import edges: file → imported file ---
|
|
149
269
|
for (const imp of imports) {
|
|
150
|
-
|
|
270
|
+
let targetAbsolute = null;
|
|
271
|
+
if (imp.specifier.startsWith('./') || imp.specifier.startsWith('../')) {
|
|
272
|
+
// Relative import
|
|
273
|
+
targetAbsolute = resolveRelativeImport(absolutePath, imp.specifier);
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
// Try path alias resolution (e.g. @/lib/foo, ~/utils)
|
|
277
|
+
targetAbsolute = resolveAliasImport(imp.specifier, absolutePath, codeDir);
|
|
278
|
+
}
|
|
151
279
|
if (!targetAbsolute)
|
|
152
280
|
continue;
|
|
153
|
-
const targetRel = path_1.default.relative(codeDir, targetAbsolute);
|
|
154
|
-
if (targetRel.startsWith('..') || path_1.default.isAbsolute(targetRel))
|
|
155
|
-
continue;
|
|
156
281
|
const targetFileId = path_1.default.relative(codeDir, targetAbsolute);
|
|
282
|
+
if (targetFileId.startsWith('..') || path_1.default.isAbsolute(targetFileId))
|
|
283
|
+
continue;
|
|
157
284
|
if (targetFileId !== fileNodeId) {
|
|
158
285
|
edges.push({
|
|
159
286
|
from: fileNodeId,
|
|
@@ -6,7 +6,7 @@ const languages_1 = require("../../lib/parsers/languages");
|
|
|
6
6
|
const TAG_TO_LANGUAGE = {
|
|
7
7
|
ts: 'typescript',
|
|
8
8
|
typescript: 'typescript',
|
|
9
|
-
tsx: '
|
|
9
|
+
tsx: 'tsx',
|
|
10
10
|
js: 'javascript',
|
|
11
11
|
javascript: 'javascript',
|
|
12
12
|
jsx: 'javascript',
|
|
@@ -20,12 +20,17 @@ async function extractSymbols(code, language) {
|
|
|
20
20
|
if (!lang || !(0, languages_1.isLanguageSupported)(lang))
|
|
21
21
|
return [];
|
|
22
22
|
try {
|
|
23
|
-
const
|
|
24
|
-
if (!
|
|
23
|
+
const tree = await (0, languages_1.parseSource)(code, lang);
|
|
24
|
+
if (!tree)
|
|
25
25
|
return [];
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
try {
|
|
27
|
+
const mapper = (0, languages_1.getMapper)(lang);
|
|
28
|
+
const symbols = mapper.extractSymbols(tree.rootNode);
|
|
29
|
+
return symbols.map(s => s.name).filter(Boolean);
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
tree.delete();
|
|
33
|
+
}
|
|
29
34
|
}
|
|
30
35
|
catch {
|
|
31
36
|
return [];
|