codesearch-mcp 0.2.3-alpha → 0.3.0-alpha
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 +31 -1
- package/README.md +46 -26
- package/dist/app.js +5 -19
- package/dist/src/chunking/AstChunker.d.ts +12 -0
- package/dist/src/chunking/AstChunker.js +30 -0
- package/dist/src/chunking/CharacterChunkStrategy.d.ts +8 -0
- package/dist/src/chunking/CharacterChunkStrategy.js +15 -0
- package/dist/src/chunking/DeclarationChunkStrategy.d.ts +15 -0
- package/dist/src/chunking/DeclarationChunkStrategy.js +87 -0
- package/dist/src/chunking/FragmentMerger.d.ts +11 -0
- package/dist/src/chunking/FragmentMerger.js +61 -0
- package/dist/src/chunking/ImportOnlyFilter.d.ts +5 -0
- package/dist/src/chunking/ImportOnlyFilter.js +14 -0
- package/dist/src/chunking/LangchainTextSplitter.d.ts +21 -0
- package/dist/src/chunking/LangchainTextSplitter.js +133 -0
- package/dist/src/chunking/MinSizeSplitPolicy.d.ts +9 -0
- package/dist/src/chunking/MinSizeSplitPolicy.js +18 -0
- package/dist/src/chunking/RunPacker.d.ts +12 -0
- package/dist/src/chunking/RunPacker.js +84 -0
- package/dist/src/chunking/create-chunker.d.ts +3 -0
- package/dist/src/chunking/create-chunker.js +15 -0
- package/dist/src/chunking/interfaces.d.ts +27 -0
- package/dist/src/chunking/interfaces.js +2 -0
- package/dist/src/commands/ConfigCommand.js +1 -9
- package/dist/src/commands/EvalCommand.d.ts +4 -3
- package/dist/src/commands/EvalCommand.js +85 -52
- package/dist/src/commands/IndexCommand.js +0 -22
- package/dist/src/commands/McpCommand.js +2 -8
- package/dist/src/commands/WebCommand.js +2 -5
- package/dist/src/config-error.d.ts +0 -4
- package/dist/src/config-error.js +0 -4
- package/dist/src/config-store.d.ts +1 -54
- package/dist/src/config-store.js +8 -69
- package/dist/src/config.d.ts +8 -17
- package/dist/src/config.js +35 -34
- package/dist/src/constants.d.ts +2 -28
- package/dist/src/constants.js +6 -28
- package/dist/src/container.d.ts +6 -0
- package/dist/src/container.js +12 -3
- package/dist/src/db/schema.d.ts +17 -0
- package/dist/src/db/schema.js +1 -6
- package/dist/src/declarations/DeclarationTree.d.ts +19 -0
- package/dist/src/declarations/DeclarationTree.js +66 -0
- package/dist/src/declarations/DeclarationTreeBuilder.d.ts +22 -0
- package/dist/src/declarations/DeclarationTreeBuilder.js +179 -0
- package/dist/src/declarations/SourceLines.d.ts +37 -0
- package/dist/src/declarations/SourceLines.js +415 -0
- package/dist/src/dependency-traversal.d.ts +0 -6
- package/dist/src/dependency-traversal.js +0 -6
- package/dist/src/enriched-prompt.d.ts +0 -6
- package/dist/src/enriched-prompt.js +0 -6
- package/dist/src/error-record.d.ts +12 -0
- package/dist/src/error-record.js +248 -0
- package/dist/src/eval-corpus.d.ts +16 -0
- package/dist/src/eval-corpus.js +169 -0
- package/dist/src/eval-results.d.ts +6 -3
- package/dist/src/eval-results.js +309 -26
- package/dist/src/file-utils.d.ts +0 -5
- package/dist/src/file-utils.js +0 -5
- package/dist/src/graph.d.ts +0 -25
- package/dist/src/graph.js +0 -28
- package/dist/src/hnsw-settings.d.ts +3 -0
- package/dist/src/hnsw-settings.js +24 -0
- package/dist/src/interfaces/IAstParser.d.ts +2 -39
- package/dist/src/interfaces/IChunker.d.ts +3 -12
- package/dist/src/interfaces/ICorpusInspector.d.ts +7 -0
- package/dist/src/interfaces/ICorpusInspector.js +2 -0
- package/dist/src/interfaces/IDatabase.d.ts +2 -22
- package/dist/src/interfaces/IEmbedder.d.ts +0 -9
- package/dist/src/interfaces/ILogger.d.ts +1 -11
- package/dist/src/interfaces/IMcpToolHandler.d.ts +0 -5
- package/dist/src/interfaces/IMonorepoResolver.d.ts +0 -22
- package/dist/src/interfaces/IProjectDiscovery.d.ts +0 -17
- package/dist/src/interfaces/IRetrievalEvaluator.d.ts +134 -12
- package/dist/src/interfaces/ISearcher.d.ts +16 -5
- package/dist/src/interrupt.js +0 -2
- package/dist/src/owner-path.d.ts +9 -10
- package/dist/src/owner-path.js +11 -23
- package/dist/src/project-scope.d.ts +0 -12
- package/dist/src/project-scope.js +0 -12
- package/dist/src/project-selector.d.ts +0 -5
- package/dist/src/project-selector.js +0 -11
- package/dist/src/retrieval-eval.d.ts +14 -6
- package/dist/src/retrieval-eval.js +345 -59
- package/dist/src/search/result-shaping.d.ts +4 -0
- package/dist/src/search/result-shaping.js +19 -0
- package/dist/src/services/AstParserService.d.ts +2 -62
- package/dist/src/services/AstParserService.js +25 -137
- package/dist/src/services/DatabaseService.d.ts +2 -7
- package/dist/src/services/DatabaseService.js +42 -29
- package/dist/src/services/EmbedderFactory.d.ts +0 -4
- package/dist/src/services/EmbedderFactory.js +0 -5
- package/dist/src/services/IndexerService.d.ts +0 -16
- package/dist/src/services/IndexerService.js +16 -45
- package/dist/src/services/InfinityEmbedder.d.ts +0 -21
- package/dist/src/services/InfinityEmbedder.js +0 -15
- package/dist/src/services/LoggerService.d.ts +7 -5
- package/dist/src/services/LoggerService.js +37 -41
- package/dist/src/services/McpServer.d.ts +22 -14
- package/dist/src/services/McpServer.js +70 -37
- package/dist/src/services/MonorepoAwareDiscoveryService.d.ts +1 -45
- package/dist/src/services/MonorepoAwareDiscoveryService.js +1 -51
- package/dist/src/services/NxMonorepoResolver.d.ts +0 -29
- package/dist/src/services/NxMonorepoResolver.js +5 -56
- package/dist/src/services/OpenAICompatibleEmbedder.d.ts +0 -6
- package/dist/src/services/OpenAICompatibleEmbedder.js +0 -14
- package/dist/src/services/ProjectDiscoveryService.d.ts +0 -4
- package/dist/src/services/ProjectDiscoveryService.js +0 -4
- package/dist/src/services/SearcherService.d.ts +9 -116
- package/dist/src/services/SearcherService.js +95 -176
- package/dist/src/services/VoyageEmbedder.d.ts +0 -6
- package/dist/src/services/VoyageEmbedder.js +0 -6
- package/dist/src/services/WebServerService.d.ts +1 -53
- package/dist/src/services/WebServerService.js +11 -57
- package/dist/src/services/http-retry.d.ts +0 -7
- package/dist/src/services/http-retry.js +1 -10
- package/dist/src/services/infinity-provisioning.d.ts +0 -74
- package/dist/src/services/infinity-provisioning.js +5 -88
- package/dist/src/services/legacy-ollama-index.d.ts +0 -16
- package/dist/src/services/legacy-ollama-index.js +0 -23
- package/dist/src/services/reranker.d.ts +0 -9
- package/dist/src/services/reranker.js +46 -12
- package/dist/src/setup.d.ts +11 -23
- package/dist/src/setup.js +98 -71
- package/dist/src/symbol-name.d.ts +0 -12
- package/dist/src/symbol-name.js +0 -13
- package/dist/src/symbol-rows.d.ts +0 -14
- package/dist/src/symbol-rows.js +0 -14
- package/dist/src/tool-call-log.d.ts +2 -0
- package/dist/src/tool-call-log.js +24 -0
- package/dist/src/tools/GetDependenciesTool.js +1 -1
- package/dist/src/tools/GetDependentsTool.js +1 -1
- package/dist/src/tools/SearchCodebaseTool.js +6 -3
- package/dist/src/tools/format-helpers.d.ts +0 -6
- package/dist/src/tools/format-helpers.js +87 -14
- package/dist/src/web/dashboard.d.ts +0 -8
- package/dist/src/web/dashboard.js +7 -10
- package/package.json +2 -2
- package/dist/src/services/AstChunker.d.ts +0 -42
- package/dist/src/services/AstChunker.js +0 -158
- package/docs/web-dashboard.md +0 -206
|
@@ -40,6 +40,8 @@ const drizzle_orm_1 = require("drizzle-orm");
|
|
|
40
40
|
const schema = __importStar(require("../db/schema"));
|
|
41
41
|
const db_1 = require("../db");
|
|
42
42
|
const MIN_PG_VERSION_NUM = 150000;
|
|
43
|
+
const SETTING_NAME = /^[a-z_]+(\.[a-z_]+)*$/;
|
|
44
|
+
const SETTING_VALUE = /^[A-Za-z0-9_]+$/;
|
|
43
45
|
class DatabaseService {
|
|
44
46
|
config;
|
|
45
47
|
pool;
|
|
@@ -52,6 +54,41 @@ class DatabaseService {
|
|
|
52
54
|
const result = await this.pool.query(sql, params);
|
|
53
55
|
return result.rows;
|
|
54
56
|
}
|
|
57
|
+
async queryWithLocalSettings(settings, sql, params) {
|
|
58
|
+
const setStatements = Object.entries(settings).map(([name, value]) => {
|
|
59
|
+
if (!SETTING_NAME.test(name) || !SETTING_VALUE.test(String(value))) {
|
|
60
|
+
throw new Error(`DatabaseService: invalid local setting ${JSON.stringify(name)} = ${JSON.stringify(value)}`);
|
|
61
|
+
}
|
|
62
|
+
return `SET LOCAL ${name} = ${value}`;
|
|
63
|
+
});
|
|
64
|
+
const client = await this.pool.connect();
|
|
65
|
+
let began = false;
|
|
66
|
+
let broken;
|
|
67
|
+
const onError = (err) => { broken ??= err; };
|
|
68
|
+
client.on('error', onError);
|
|
69
|
+
try {
|
|
70
|
+
await client.query('BEGIN');
|
|
71
|
+
began = true;
|
|
72
|
+
for (const statement of setStatements)
|
|
73
|
+
await client.query(statement);
|
|
74
|
+
const result = await client.query(sql, params);
|
|
75
|
+
await client.query('COMMIT');
|
|
76
|
+
return result.rows;
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
if (began)
|
|
80
|
+
await client.query('ROLLBACK').catch((e) => { broken ??= e; });
|
|
81
|
+
throw err;
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
client.removeListener('error', onError);
|
|
85
|
+
client.release(broken);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async pgvectorVersion() {
|
|
89
|
+
const rows = await this.query(`SELECT extversion FROM pg_extension WHERE extname = 'vector'`);
|
|
90
|
+
return rows[0]?.extversion ?? null;
|
|
91
|
+
}
|
|
55
92
|
transaction(fn) {
|
|
56
93
|
return this._db.transaction(fn);
|
|
57
94
|
}
|
|
@@ -69,17 +106,8 @@ class DatabaseService {
|
|
|
69
106
|
async initDbWithSchema() {
|
|
70
107
|
await this.ensureDatabaseExists();
|
|
71
108
|
await this.initDb();
|
|
72
|
-
// Read as the very first statement on the application database, before any
|
|
73
|
-
// CREATE TABLE / CREATE INDEX / ALTER TABLE / DROP — `NULLS NOT DISTINCT`
|
|
74
|
-
// (used below) requires PostgreSQL 15+, and the DROP/CREATE pair must never
|
|
75
|
-
// run against an older server (it would fail after the DROP and leave
|
|
76
|
-
// code_symbol without a unique index).
|
|
77
109
|
await this.assertMinPostgresVersion();
|
|
78
|
-
// code_chunk table — created at the legacy default width (1024) so search
|
|
79
|
-
// tools always have a table. IndexerService recreates it at the resolved
|
|
80
|
-
// width before indexing when the active model's dimension differs.
|
|
81
110
|
await this.ensureChunkTable(1024);
|
|
82
|
-
// index_meta table — source of truth for the model that built the index.
|
|
83
111
|
await this.query(`
|
|
84
112
|
CREATE TABLE IF NOT EXISTS index_meta (
|
|
85
113
|
id BOOLEAN PRIMARY KEY DEFAULT true,
|
|
@@ -89,7 +117,6 @@ class DatabaseService {
|
|
|
89
117
|
"indexedAt" TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
90
118
|
)
|
|
91
119
|
`);
|
|
92
|
-
// code_dependency table
|
|
93
120
|
const [{ existsDep }] = await this.query(`
|
|
94
121
|
SELECT EXISTS (
|
|
95
122
|
SELECT 1 FROM information_schema.tables
|
|
@@ -114,7 +141,6 @@ class DatabaseService {
|
|
|
114
141
|
await this.query(`DROP INDEX IF EXISTS idx_dep_from`);
|
|
115
142
|
await this.query(`DROP INDEX IF EXISTS idx_dep_to`);
|
|
116
143
|
await this.query(`DROP INDEX IF EXISTS idx_dep_project`);
|
|
117
|
-
// code_symbol table
|
|
118
144
|
const [{ existsSym }] = await this.query(`
|
|
119
145
|
SELECT EXISTS (
|
|
120
146
|
SELECT 1 FROM information_schema.tables
|
|
@@ -140,16 +166,11 @@ class DatabaseService {
|
|
|
140
166
|
await this.query(`ALTER TABLE code_symbol ADD COLUMN IF NOT EXISTS container TEXT`);
|
|
141
167
|
await this.query(`ALTER TABLE code_symbol ADD COLUMN IF NOT EXISTS "isStatic" BOOLEAN NOT NULL DEFAULT false`);
|
|
142
168
|
await this.query(`ALTER TABLE code_symbol ADD COLUMN IF NOT EXISTS "target" TEXT`);
|
|
143
|
-
// DDL is transactional in PostgreSQL — either both statements apply or
|
|
144
|
-
// neither does, so a failure here never leaves code_symbol without a
|
|
145
|
-
// unique index (the old idx_symbol_unique is dropped only once the new
|
|
146
|
-
// one is confirmed to exist).
|
|
147
169
|
await this.transaction(async (tx) => {
|
|
148
170
|
await tx.execute('DROP INDEX IF EXISTS idx_symbol_unique');
|
|
149
171
|
await tx.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_symbol_unique_v2 ' +
|
|
150
172
|
'ON code_symbol (project, "filePath", container, name, kind, line) NULLS NOT DISTINCT');
|
|
151
173
|
});
|
|
152
|
-
// code_call table
|
|
153
174
|
await this.query(`
|
|
154
175
|
CREATE TABLE IF NOT EXISTS code_call (
|
|
155
176
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
@@ -167,11 +188,6 @@ class DatabaseService {
|
|
|
167
188
|
await this.query(`CREATE INDEX IF NOT EXISTS idx_call_callee_file ON code_call (project, "calleeFile")`);
|
|
168
189
|
await this.query(`CREATE INDEX IF NOT EXISTS idx_call_from ON code_call (project, "fromFile")`);
|
|
169
190
|
}
|
|
170
|
-
/**
|
|
171
|
-
* Create `code_chunk` at `dimensions` if it does not exist, plus its HNSW and
|
|
172
|
-
* (serviceName, filePath) indexes and the additive `contentHash`/`ownerPath`
|
|
173
|
-
* columns for pre-existing tables. Idempotent — safe to call repeatedly.
|
|
174
|
-
*/
|
|
175
191
|
async ensureChunkTable(dimensions) {
|
|
176
192
|
const dim = this.assertDim(dimensions);
|
|
177
193
|
await this.query(`
|
|
@@ -184,15 +200,16 @@ class DatabaseService {
|
|
|
184
200
|
embedding vector(${dim}) NOT NULL,
|
|
185
201
|
"lastModified" DOUBLE PRECISION,
|
|
186
202
|
"contentHash" VARCHAR(64),
|
|
187
|
-
"ownerPath" VARCHAR
|
|
203
|
+
"ownerPath" VARCHAR,
|
|
204
|
+
"endLine" INT
|
|
188
205
|
)
|
|
189
206
|
`);
|
|
190
207
|
await this.query(`ALTER TABLE code_chunk ADD COLUMN IF NOT EXISTS "contentHash" varchar(64) NULL`);
|
|
191
208
|
await this.query(`ALTER TABLE code_chunk ADD COLUMN IF NOT EXISTS "ownerPath" varchar NULL`);
|
|
209
|
+
await this.query(`ALTER TABLE code_chunk ADD COLUMN IF NOT EXISTS "endLine" integer NULL`);
|
|
192
210
|
await this.query(`CREATE INDEX IF NOT EXISTS idx_code_chunk_embedding ON code_chunk USING hnsw (embedding vector_cosine_ops)`);
|
|
193
211
|
await this.query(`CREATE INDEX IF NOT EXISTS idx_code_chunk_service_file ON code_chunk ("serviceName", "filePath")`);
|
|
194
212
|
}
|
|
195
|
-
/** Drop and recreate `code_chunk` at `dimensions` — used on a dimension change. */
|
|
196
213
|
async recreateChunkTable(dimensions) {
|
|
197
214
|
const dim = this.assertDim(dimensions);
|
|
198
215
|
await this.query(`DROP TABLE IF EXISTS code_chunk`);
|
|
@@ -205,7 +222,6 @@ class DatabaseService {
|
|
|
205
222
|
return row ? { provider: row.provider, model: row.model, dimensions: Number(row.dimensions) } : null;
|
|
206
223
|
}
|
|
207
224
|
catch {
|
|
208
|
-
// Table absent (un-migrated DB) — treat as "no metadata".
|
|
209
225
|
return null;
|
|
210
226
|
}
|
|
211
227
|
}
|
|
@@ -231,8 +247,6 @@ class DatabaseService {
|
|
|
231
247
|
async deleteFiles(project, filePaths) {
|
|
232
248
|
if (filePaths.length === 0)
|
|
233
249
|
return;
|
|
234
|
-
// PostgreSQL limits ~32 767 bind params per statement; OR queries bind the
|
|
235
|
-
// array twice (fromFile + calleeFile), so cap each batch at 10 000 entries.
|
|
236
250
|
const BATCH = 10_000;
|
|
237
251
|
await this.transaction(async (tx) => {
|
|
238
252
|
for (let i = 0; i < filePaths.length; i += BATCH) {
|
|
@@ -262,7 +276,6 @@ class DatabaseService {
|
|
|
262
276
|
await tx.delete(db_1.codeDependency).where((0, drizzle_orm_1.eq)(db_1.codeDependency.project, name));
|
|
263
277
|
});
|
|
264
278
|
}
|
|
265
|
-
/** Guard against SQL injection — the dimension is interpolated into DDL. */
|
|
266
279
|
assertDim(dimensions) {
|
|
267
280
|
if (!Number.isInteger(dimensions) || dimensions <= 0 || dimensions > 2000) {
|
|
268
281
|
throw new Error(`Invalid vector dimension: ${dimensions} (must be an integer 1–2000)`);
|
|
@@ -276,7 +289,7 @@ class DatabaseService {
|
|
|
276
289
|
versionNum = Number(rows[0]?.v);
|
|
277
290
|
}
|
|
278
291
|
catch (err) {
|
|
279
|
-
throw new Error(`Failed to read PostgreSQL server_version_num (requires PostgreSQL 15+): ${err.message}
|
|
292
|
+
throw new Error(`Failed to read PostgreSQL server_version_num (requires PostgreSQL 15+): ${err.message}`, { cause: err });
|
|
280
293
|
}
|
|
281
294
|
if (!Number.isFinite(versionNum) || versionNum < MIN_PG_VERSION_NUM) {
|
|
282
295
|
throw new Error(`PostgreSQL 15+ is required (server_version_num=${versionNum}) — ` +
|
|
@@ -310,7 +323,7 @@ class DatabaseService {
|
|
|
310
323
|
catch (err) {
|
|
311
324
|
await client.end().catch(() => { });
|
|
312
325
|
const msg = err instanceof Error ? err.message : String(err);
|
|
313
|
-
throw new Error(`Failed to ensure database "${database}" exists: ${msg}
|
|
326
|
+
throw new Error(`Failed to ensure database "${database}" exists: ${msg}`, { cause: err });
|
|
314
327
|
}
|
|
315
328
|
}
|
|
316
329
|
}
|
|
@@ -2,8 +2,4 @@ import type { Config } from '../config';
|
|
|
2
2
|
import type { IEmbedder } from '../interfaces/IEmbedder';
|
|
3
3
|
import type { ILogger } from '../interfaces/ILogger';
|
|
4
4
|
import { type EmbedderDeps } from './OpenAICompatibleEmbedder';
|
|
5
|
-
/**
|
|
6
|
-
* Construct the `IEmbedder` implementation selected by `config.embedding.provider`.
|
|
7
|
-
* The default (`infinity`) is the recommended local backend.
|
|
8
|
-
*/
|
|
9
5
|
export declare function createEmbedder(config: Config, logger?: ILogger, deps?: EmbedderDeps): IEmbedder;
|
|
@@ -5,10 +5,6 @@ const LoggerService_1 = require("./LoggerService");
|
|
|
5
5
|
const OpenAICompatibleEmbedder_1 = require("./OpenAICompatibleEmbedder");
|
|
6
6
|
const InfinityEmbedder_1 = require("./InfinityEmbedder");
|
|
7
7
|
const VoyageEmbedder_1 = require("./VoyageEmbedder");
|
|
8
|
-
/**
|
|
9
|
-
* Construct the `IEmbedder` implementation selected by `config.embedding.provider`.
|
|
10
|
-
* The default (`infinity`) is the recommended local backend.
|
|
11
|
-
*/
|
|
12
8
|
function createEmbedder(config, logger = LoggerService_1.LoggerService.silent(), deps = {}) {
|
|
13
9
|
switch (config.embedding.provider) {
|
|
14
10
|
case 'openai-compatible':
|
|
@@ -18,7 +14,6 @@ function createEmbedder(config, logger = LoggerService_1.LoggerService.silent(),
|
|
|
18
14
|
case 'voyage':
|
|
19
15
|
return new VoyageEmbedder_1.VoyageEmbedder(config, logger, deps);
|
|
20
16
|
default: {
|
|
21
|
-
// The Zod enum should already reject this; guard anyway for a clear message.
|
|
22
17
|
const p = config.embedding.provider;
|
|
23
18
|
throw new Error(`Unknown embedding provider: "${p}". Expected one of: openai-compatible, voyage, infinity.`);
|
|
24
19
|
}
|
|
@@ -23,16 +23,6 @@ export declare class IndexerService {
|
|
|
23
23
|
private config;
|
|
24
24
|
private logger;
|
|
25
25
|
constructor(db: IDatabase, embedder: IEmbedder, ast: IAstParser, chunker: IChunker, config: Config, logger?: ILogger);
|
|
26
|
-
/**
|
|
27
|
-
* Resolve the active embedding dimension (config override → registry → probe),
|
|
28
|
-
* reconcile it against `index_meta`, and prepare `code_chunk` accordingly:
|
|
29
|
-
* a dimension change drops+recreates the table; a same-dimension model/provider
|
|
30
|
-
* change clears its rows (vector spaces are not comparable across models); an
|
|
31
|
-
* unchanged model keeps the rows for incremental indexing. Back-compat: an
|
|
32
|
-
* existing populated table with no `index_meta` row is treated as the legacy
|
|
33
|
-
* `ollama`/`bge-m3`/1024 index. Writes `index_meta` to reflect the active model.
|
|
34
|
-
* Call once per run, before the scan loop.
|
|
35
|
-
*/
|
|
36
26
|
prepareEmbeddingSchema(): Promise<IndexMeta>;
|
|
37
27
|
private reconcileIndex;
|
|
38
28
|
private countChunks;
|
|
@@ -40,12 +30,6 @@ export declare class IndexerService {
|
|
|
40
30
|
scanAndIndex(dir: string, serviceName: string, force?: boolean, signal?: AbortSignal, preCollectedFiles?: string[], opts?: {
|
|
41
31
|
rootTsConfigPath?: string;
|
|
42
32
|
}): Promise<IndexStats>;
|
|
43
|
-
/**
|
|
44
|
-
* Scan several leaf projects in one call, each with its own `rootTsConfigPath`
|
|
45
|
-
* (a unit's children may belong to different monorepos). No pruning, no
|
|
46
|
-
* console output — callers (CLI or a future hook) own reporting. A rejecting
|
|
47
|
-
* `scanAndIndex` call stops the loop before the next target and propagates.
|
|
48
|
-
*/
|
|
49
33
|
scanProjects(targets: DiscoveredProject[], force: boolean, signal: AbortSignal, filesPerProject: string[][]): Promise<IndexStats[]>;
|
|
50
34
|
pruneProjects(keep: string[]): Promise<{
|
|
51
35
|
removed: string[];
|
|
@@ -40,13 +40,17 @@ const crypto_1 = require("crypto");
|
|
|
40
40
|
const cli_progress_1 = require("cli-progress");
|
|
41
41
|
const drizzle_orm_1 = require("drizzle-orm");
|
|
42
42
|
const owner_path_1 = require("../owner-path");
|
|
43
|
+
const DeclarationTree_1 = require("../declarations/DeclarationTree");
|
|
44
|
+
const SourceLines_1 = require("../declarations/SourceLines");
|
|
43
45
|
const enriched_prompt_1 = require("../enriched-prompt");
|
|
44
46
|
const symbol_rows_1 = require("../symbol-rows");
|
|
45
47
|
const db_1 = require("../db");
|
|
46
48
|
const file_utils_1 = require("../file-utils");
|
|
47
49
|
const LoggerService_1 = require("./LoggerService");
|
|
48
50
|
const constants_1 = require("../constants");
|
|
51
|
+
const error_record_1 = require("../error-record");
|
|
49
52
|
const SCHEMA_EXTENSIONS = /\.(prisma|graphql|gql)$/;
|
|
53
|
+
const JSX_EXTENSIONS = /\.(tsx|jsx|js)$/;
|
|
50
54
|
class IndexerService {
|
|
51
55
|
db;
|
|
52
56
|
embedder;
|
|
@@ -62,16 +66,6 @@ class IndexerService {
|
|
|
62
66
|
this.config = config;
|
|
63
67
|
this.logger = logger;
|
|
64
68
|
}
|
|
65
|
-
/**
|
|
66
|
-
* Resolve the active embedding dimension (config override → registry → probe),
|
|
67
|
-
* reconcile it against `index_meta`, and prepare `code_chunk` accordingly:
|
|
68
|
-
* a dimension change drops+recreates the table; a same-dimension model/provider
|
|
69
|
-
* change clears its rows (vector spaces are not comparable across models); an
|
|
70
|
-
* unchanged model keeps the rows for incremental indexing. Back-compat: an
|
|
71
|
-
* existing populated table with no `index_meta` row is treated as the legacy
|
|
72
|
-
* `ollama`/`bge-m3`/1024 index. Writes `index_meta` to reflect the active model.
|
|
73
|
-
* Call once per run, before the scan loop.
|
|
74
|
-
*/
|
|
75
69
|
async prepareEmbeddingSchema() {
|
|
76
70
|
const { provider, model, dimensions: override } = this.config.embedding;
|
|
77
71
|
const next = {
|
|
@@ -86,13 +80,11 @@ class IndexerService {
|
|
|
86
80
|
if (!meta) {
|
|
87
81
|
const chunkCount = await this.countChunks();
|
|
88
82
|
if (chunkCount === 0) {
|
|
89
|
-
// Fresh index — create the table at the resolved width and record it.
|
|
90
83
|
await this.db.recreateChunkTable(next.dimensions);
|
|
91
84
|
await this.db.writeIndexMeta(next);
|
|
92
85
|
this.logger.debug('IndexerService: prepared fresh index', { ...next });
|
|
93
86
|
return next;
|
|
94
87
|
}
|
|
95
|
-
// Legacy index (pre-index_meta): assume the historical default model.
|
|
96
88
|
await this.reconcileIndex({ provider: 'ollama', model: 'bge-m3', dimensions: 1024 }, next);
|
|
97
89
|
return next;
|
|
98
90
|
}
|
|
@@ -118,9 +110,6 @@ class IndexerService {
|
|
|
118
110
|
return Number(rows[0]?.n ?? 0);
|
|
119
111
|
}
|
|
120
112
|
catch (err) {
|
|
121
|
-
// A missing table means a fresh install → 0. Any other (transient) error
|
|
122
|
-
// must propagate — returning 0 here could wrongly recreate/drop a populated
|
|
123
|
-
// table on the `!meta` path.
|
|
124
113
|
if (/does not exist/i.test(err.message))
|
|
125
114
|
return 0;
|
|
126
115
|
throw err;
|
|
@@ -142,9 +131,6 @@ class IndexerService {
|
|
|
142
131
|
const startTime = Date.now();
|
|
143
132
|
this.logger.debug('IndexerService: scanAndIndex start', { serviceName, dir, force });
|
|
144
133
|
if (force) {
|
|
145
|
-
// Reset incremental state and clear the project's symbol/call rows in one
|
|
146
|
-
// transaction: a file whose re-ingestion later fails must hold no rows at
|
|
147
|
-
// all, never rows in the previous (pre-member-extraction) format.
|
|
148
134
|
await this.db.transaction(async (tx) => {
|
|
149
135
|
await tx.update(db_1.codeChunk)
|
|
150
136
|
.set({ lastModified: null, contentHash: null })
|
|
@@ -250,7 +236,7 @@ class IndexerService {
|
|
|
250
236
|
]);
|
|
251
237
|
}
|
|
252
238
|
catch (err) {
|
|
253
|
-
this.logger.error('IndexerService: mtime refresh batch failed', {
|
|
239
|
+
this.logger.error('IndexerService: mtime refresh batch failed', { err });
|
|
254
240
|
}
|
|
255
241
|
}
|
|
256
242
|
this.ast.clearCache(dir);
|
|
@@ -265,12 +251,6 @@ class IndexerService {
|
|
|
265
251
|
});
|
|
266
252
|
return stats;
|
|
267
253
|
}
|
|
268
|
-
/**
|
|
269
|
-
* Scan several leaf projects in one call, each with its own `rootTsConfigPath`
|
|
270
|
-
* (a unit's children may belong to different monorepos). No pruning, no
|
|
271
|
-
* console output — callers (CLI or a future hook) own reporting. A rejecting
|
|
272
|
-
* `scanAndIndex` call stops the loop before the next target and propagates.
|
|
273
|
-
*/
|
|
274
254
|
async scanProjects(targets, force, signal, filesPerProject) {
|
|
275
255
|
const allStats = [];
|
|
276
256
|
for (let i = 0; i < targets.length; i++) {
|
|
@@ -296,7 +276,7 @@ class IndexerService {
|
|
|
296
276
|
}
|
|
297
277
|
catch (err) {
|
|
298
278
|
console.error(`Failed to prune project ${name}: ${err.message}`);
|
|
299
|
-
this.logger.error('IndexerService: pruneProject failed', { name,
|
|
279
|
+
this.logger.error('IndexerService: pruneProject failed', { name, err });
|
|
300
280
|
failed.push(name);
|
|
301
281
|
}
|
|
302
282
|
}
|
|
@@ -313,21 +293,17 @@ class IndexerService {
|
|
|
313
293
|
.from(db_1.codeChunk)
|
|
314
294
|
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(db_1.codeChunk.serviceName, serviceName), (0, drizzle_orm_1.eq)(db_1.codeChunk.filePath, relFilePath)))
|
|
315
295
|
.limit(1);
|
|
316
|
-
// Fast path: mtime unchanged — skip without reading file
|
|
317
296
|
if (existing?.lastModified === mtimeMs) {
|
|
318
297
|
return { status: 'skipped', chunks: 0 };
|
|
319
298
|
}
|
|
320
299
|
const t0 = Date.now();
|
|
321
300
|
const code = fs.readFileSync(filePath, 'utf-8');
|
|
322
301
|
const hash = IndexerService.hashContent(code);
|
|
323
|
-
// Content unchanged (mtime drifted — DB transfer, git clone, touch)
|
|
324
302
|
if (existing !== undefined && existing.contentHash !== null && existing.contentHash === hash) {
|
|
325
303
|
return { status: 'skipped', chunks: 0, pendingMtimeRefresh: relFilePath };
|
|
326
304
|
}
|
|
327
305
|
const tAst = Date.now();
|
|
328
|
-
const { edges: rawEdges, symbols: rawSymbols, members: rawMembers = [], calls: rawCalls,
|
|
329
|
-
// astMs is near-zero — actual AST time is spent upfront in analyzeProject()
|
|
330
|
-
// Normalize edges to workspace-relative paths
|
|
306
|
+
const { edges: rawEdges, symbols: rawSymbols, members: rawMembers = [], calls: rawCalls, graphqlNames: rawGraphqlNames = [], } = analysis ?? { edges: [], symbols: [], calls: [] };
|
|
331
307
|
const edges = rawEdges
|
|
332
308
|
.filter(e => e.toFile.startsWith(this.config.workspaceRoot))
|
|
333
309
|
.map(e => ({
|
|
@@ -335,7 +311,6 @@ class IndexerService {
|
|
|
335
311
|
fromFile: path.relative(this.config.workspaceRoot, e.fromFile),
|
|
336
312
|
toFile: path.relative(this.config.workspaceRoot, e.toFile),
|
|
337
313
|
}));
|
|
338
|
-
// Normalize call edges to workspace-relative paths; keep only resolved internal calls
|
|
339
314
|
const calls = rawCalls
|
|
340
315
|
.filter(c => c.calleeFile.startsWith(this.config.workspaceRoot))
|
|
341
316
|
.map(c => ({
|
|
@@ -348,23 +323,19 @@ class IndexerService {
|
|
|
348
323
|
line: c.line,
|
|
349
324
|
col: c.col,
|
|
350
325
|
}));
|
|
351
|
-
// symbols ∪ members ∪ graphqlNames → code_symbol rows: overload sets
|
|
352
|
-
// collapse to their representative row (graphqlNames bypass the
|
|
353
|
-
// collapse), then a final dedup by (container, name, kind, line).
|
|
354
326
|
const symbolRows = (0, symbol_rows_1.buildSymbolRows)(rawSymbols, rawMembers, rawGraphqlNames).map(s => ({ ...s, filePath: relFilePath }));
|
|
355
|
-
// Side-effect imports bind no name, so they stay out of the Dependencies: line.
|
|
356
327
|
const promptEdges = edges.filter(e => e.depType !== 'import-side-effect');
|
|
357
328
|
const astMs = Date.now() - tAst;
|
|
329
|
+
const jsx = JSX_EXTENSIONS.test(filePath);
|
|
358
330
|
const chunks = await this.chunker.chunk(code, {
|
|
359
331
|
strategy: this.config.indexer.chunkStrategy,
|
|
360
332
|
isSchemaFile: SCHEMA_EXTENSIONS.test(filePath),
|
|
361
|
-
|
|
333
|
+
declarations: analysis?.declarations,
|
|
334
|
+
jsx,
|
|
362
335
|
});
|
|
363
336
|
const relPath = path.relative(projectRoot, filePath);
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
// containing a chunk's start line wins.
|
|
367
|
-
const ownerRanges = rawOwnerRanges;
|
|
337
|
+
const sourceLines = new SourceLines_1.SourceLines(code, { jsx });
|
|
338
|
+
const ownerTree = analysis?.declarations ?? DeclarationTree_1.DeclarationTree.EMPTY;
|
|
368
339
|
const t2 = Date.now();
|
|
369
340
|
const records = [];
|
|
370
341
|
const embedBatchSize = this.config.embedding.batchSize;
|
|
@@ -380,7 +351,8 @@ class IndexerService {
|
|
|
380
351
|
embedding: `[${vectors[j]}]`,
|
|
381
352
|
lastModified: mtimeMs,
|
|
382
353
|
contentHash: hash,
|
|
383
|
-
ownerPath: (0, owner_path_1.
|
|
354
|
+
ownerPath: (0, owner_path_1.ownerOf)(piece, ownerTree, sourceLines),
|
|
355
|
+
endLine: piece.startLine + (piece.content.match(/\n/g)?.length ?? 0),
|
|
384
356
|
});
|
|
385
357
|
}
|
|
386
358
|
}
|
|
@@ -411,9 +383,8 @@ class IndexerService {
|
|
|
411
383
|
return { status: 'indexed', chunks: chunks.length };
|
|
412
384
|
}
|
|
413
385
|
catch (err) {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
this.logger.error('IndexerService: failed to index file', { file: filePath, reason });
|
|
386
|
+
console.error(`\n[indexer] Failed: ${filePath} — ${(0, error_record_1.rootCauseMessage)(err)}`);
|
|
387
|
+
this.logger.error('IndexerService: failed to index file', { file: filePath, err });
|
|
417
388
|
return { status: 'failed', chunks: 0 };
|
|
418
389
|
}
|
|
419
390
|
}
|
|
@@ -4,35 +4,14 @@ import { OpenAICompatibleEmbedder, type EmbedderDeps } from './OpenAICompatibleE
|
|
|
4
4
|
import { serverRoot, probeInfinityHealth, listInfinityModels, type ProvisioningDeps } from './infinity-provisioning';
|
|
5
5
|
export { serverRoot, probeInfinityHealth, listInfinityModels };
|
|
6
6
|
export type { CommandResult } from './infinity-provisioning';
|
|
7
|
-
/** Extra injection seams for `InfinityEmbedder` beyond the shared `EmbedderDeps`. */
|
|
8
7
|
export interface InfinityDeps extends EmbedderDeps, ProvisioningDeps {
|
|
9
|
-
/**
|
|
10
|
-
* Path to the managed docker-compose.yml. Defaults to the file `rag-setup`
|
|
11
|
-
* writes into `~/.config/codesearch-mcp/`. A missing file (or one without an
|
|
12
|
-
* `infinity` service) means "external backend" — the Docker path is skipped.
|
|
13
|
-
*/
|
|
14
8
|
composePath?: string;
|
|
15
9
|
}
|
|
16
|
-
/**
|
|
17
|
-
* Embedder for a local Infinity server. Inherits the OpenAI-style `/embeddings`
|
|
18
|
-
* HTTP path, batching, retry, and reranker wiring from `OpenAICompatibleEmbedder`
|
|
19
|
-
* unchanged — it adds ONLY container-readiness behavior (`ensureModelAvailable`,
|
|
20
|
-
* `listModels`), delegating to the standalone `infinity-provisioning` primitive
|
|
21
|
-
* because Infinity binds its model set at container startup and exposes no
|
|
22
|
-
* runtime model-pull API.
|
|
23
|
-
*/
|
|
24
10
|
export declare class InfinityEmbedder extends OpenAICompatibleEmbedder {
|
|
25
11
|
private readonly base;
|
|
26
12
|
private readonly composePath;
|
|
27
13
|
private readonly provisioningDeps;
|
|
28
14
|
constructor(config: Config, logger?: ILogger, deps?: InfinityDeps);
|
|
29
|
-
/**
|
|
30
|
-
* Index-time embed readiness (see `local-backend-provisioning`): verifies only
|
|
31
|
-
* the embedding model — indexing does not use the reranker, so a missing
|
|
32
|
-
* reranker model never blocks or restarts indexing. Whenever a recreate is
|
|
33
|
-
* needed, the full configured set is written so an already-served reranker
|
|
34
|
-
* model is never dropped. Called by `IndexCommand` before indexing.
|
|
35
|
-
*/
|
|
36
15
|
ensureModelAvailable(): Promise<void>;
|
|
37
16
|
listModels(): Promise<string[]>;
|
|
38
17
|
}
|
|
@@ -42,14 +42,6 @@ Object.defineProperty(exports, "serverRoot", { enumerable: true, get: function (
|
|
|
42
42
|
Object.defineProperty(exports, "probeInfinityHealth", { enumerable: true, get: function () { return infinity_provisioning_1.probeInfinityHealth; } });
|
|
43
43
|
Object.defineProperty(exports, "listInfinityModels", { enumerable: true, get: function () { return infinity_provisioning_1.listInfinityModels; } });
|
|
44
44
|
const path = __importStar(require("path"));
|
|
45
|
-
/**
|
|
46
|
-
* Embedder for a local Infinity server. Inherits the OpenAI-style `/embeddings`
|
|
47
|
-
* HTTP path, batching, retry, and reranker wiring from `OpenAICompatibleEmbedder`
|
|
48
|
-
* unchanged — it adds ONLY container-readiness behavior (`ensureModelAvailable`,
|
|
49
|
-
* `listModels`), delegating to the standalone `infinity-provisioning` primitive
|
|
50
|
-
* because Infinity binds its model set at container startup and exposes no
|
|
51
|
-
* runtime model-pull API.
|
|
52
|
-
*/
|
|
53
45
|
class InfinityEmbedder extends OpenAICompatibleEmbedder_1.OpenAICompatibleEmbedder {
|
|
54
46
|
base;
|
|
55
47
|
composePath;
|
|
@@ -65,13 +57,6 @@ class InfinityEmbedder extends OpenAICompatibleEmbedder_1.OpenAICompatibleEmbedd
|
|
|
65
57
|
readinessTimeoutMs: deps.readinessTimeoutMs,
|
|
66
58
|
};
|
|
67
59
|
}
|
|
68
|
-
/**
|
|
69
|
-
* Index-time embed readiness (see `local-backend-provisioning`): verifies only
|
|
70
|
-
* the embedding model — indexing does not use the reranker, so a missing
|
|
71
|
-
* reranker model never blocks or restarts indexing. Whenever a recreate is
|
|
72
|
-
* needed, the full configured set is written so an already-served reranker
|
|
73
|
-
* model is never dropped. Called by `IndexCommand` before indexing.
|
|
74
|
-
*/
|
|
75
60
|
async ensureModelAvailable() {
|
|
76
61
|
await (0, infinity_provisioning_1.ensureBackendReady)({
|
|
77
62
|
baseURL: this.config.embedding.baseURL,
|
|
@@ -3,17 +3,19 @@ import type { ILogger } from '../interfaces/ILogger';
|
|
|
3
3
|
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
4
4
|
export declare class LoggerService implements ILogger {
|
|
5
5
|
private readonly logger;
|
|
6
|
-
private readonly
|
|
7
|
-
constructor(
|
|
6
|
+
private readonly context;
|
|
7
|
+
private constructor();
|
|
8
8
|
static silent(): LoggerService;
|
|
9
9
|
static fromConfig(cfg: {
|
|
10
10
|
level: string;
|
|
11
11
|
dir?: string;
|
|
12
|
-
}, name?: string): LoggerService;
|
|
13
|
-
|
|
12
|
+
}, name?: string, bindings?: Record<string, unknown>): LoggerService;
|
|
13
|
+
static toStream(level: LogLevel, stream: pino.DestinationStream, bindings?: Record<string, unknown>): LoggerService;
|
|
14
|
+
private static fileTransport;
|
|
15
|
+
private static build;
|
|
14
16
|
debug(msg: string, data?: Record<string, unknown>): void;
|
|
15
17
|
info(msg: string, data?: Record<string, unknown>): void;
|
|
16
18
|
warn(msg: string, data?: Record<string, unknown>): void;
|
|
17
19
|
error(msg: string, data?: Record<string, unknown>): void;
|
|
18
|
-
|
|
20
|
+
withContext<T>(context: Record<string, unknown>, fn: () => Promise<T>): Promise<T>;
|
|
19
21
|
}
|
|
@@ -41,21 +41,20 @@ const pino_1 = __importDefault(require("pino"));
|
|
|
41
41
|
const path = __importStar(require("path"));
|
|
42
42
|
const os = __importStar(require("os"));
|
|
43
43
|
const fs = __importStar(require("fs"));
|
|
44
|
+
const async_hooks_1 = require("async_hooks");
|
|
45
|
+
const error_record_1 = require("../error-record");
|
|
44
46
|
const DEFAULT_LOG_DIR = path.join(os.homedir(), '.config', 'codesearch-mcp', 'logs');
|
|
45
47
|
class LoggerService {
|
|
46
48
|
logger;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
// metrics never reach stdout (owned by the MCP JSON transport).
|
|
50
|
-
metricsLogger;
|
|
51
|
-
constructor(logger, metricsLogger = null) {
|
|
49
|
+
context;
|
|
50
|
+
constructor(logger, context) {
|
|
52
51
|
this.logger = logger;
|
|
53
|
-
this.
|
|
52
|
+
this.context = context;
|
|
54
53
|
}
|
|
55
54
|
static silent() {
|
|
56
|
-
return
|
|
55
|
+
return LoggerService.build('debug', undefined);
|
|
57
56
|
}
|
|
58
|
-
static fromConfig(cfg, name) {
|
|
57
|
+
static fromConfig(cfg, name, bindings) {
|
|
59
58
|
const level = cfg.level;
|
|
60
59
|
const dirVal = cfg.dir;
|
|
61
60
|
const enabled = dirVal !== 'false' && dirVal !== '0';
|
|
@@ -66,45 +65,42 @@ class LoggerService {
|
|
|
66
65
|
? path.join(logDir, name, `${name}.log`)
|
|
67
66
|
: path.join(logDir, 'rag.log');
|
|
68
67
|
}
|
|
69
|
-
return LoggerService.
|
|
68
|
+
return LoggerService.build(level, filePath ? LoggerService.fileTransport(level, filePath) : undefined, bindings);
|
|
70
69
|
}
|
|
71
|
-
static
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
? (0, pino_1.default)({ level }, transport)
|
|
70
|
+
static toStream(level, stream, bindings) {
|
|
71
|
+
return LoggerService.build(level, stream, bindings);
|
|
72
|
+
}
|
|
73
|
+
static fileTransport(level, filePath) {
|
|
74
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
75
|
+
return pino_1.default.transport({
|
|
76
|
+
targets: [{
|
|
77
|
+
target: 'pino-roll',
|
|
78
|
+
options: {
|
|
79
|
+
file: filePath,
|
|
80
|
+
frequency: 'daily',
|
|
81
|
+
dateFormat: 'yyyy-MM-dd',
|
|
82
|
+
mkdir: true,
|
|
83
|
+
symlink: true,
|
|
84
|
+
limit: { count: 30 },
|
|
85
|
+
size: '50m',
|
|
86
|
+
},
|
|
87
|
+
level,
|
|
88
|
+
}],
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
static build(level, destination, bindings) {
|
|
92
|
+
const context = new async_hooks_1.AsyncLocalStorage();
|
|
93
|
+
const base = destination
|
|
94
|
+
? (0, pino_1.default)({ level, serializers: error_record_1.LOG_SERIALIZERS, mixin: () => ({ ...context.getStore() }) }, destination)
|
|
97
95
|
: (0, pino_1.default)({ level: 'silent' });
|
|
98
|
-
|
|
99
|
-
// metric(). Null when there is no file transport (default stdout must stay
|
|
100
|
-
// clean for the MCP transport).
|
|
101
|
-
const metricsLogger = transport ? logger.child({}, { level: 'info' }) : null;
|
|
102
|
-
return new LoggerService(logger, metricsLogger);
|
|
96
|
+
return new LoggerService(bindings ? base.child(bindings) : base, context);
|
|
103
97
|
}
|
|
104
98
|
debug(msg, data) { this.logger.debug(data ?? {}, msg); }
|
|
105
99
|
info(msg, data) { this.logger.info(data ?? {}, msg); }
|
|
106
100
|
warn(msg, data) { this.logger.warn(data ?? {}, msg); }
|
|
107
101
|
error(msg, data) { this.logger.error(data ?? {}, msg); }
|
|
108
|
-
|
|
102
|
+
withContext(context, fn) {
|
|
103
|
+
return this.context.run(context, fn);
|
|
104
|
+
}
|
|
109
105
|
}
|
|
110
106
|
exports.LoggerService = LoggerService;
|