codesearch-mcp 0.2.1-alpha → 0.2.3-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 +40 -0
- package/README.md +15 -8
- package/dist/app.js +45 -7
- package/dist/src/commands/EvalCommand.d.ts +10 -0
- package/dist/src/commands/EvalCommand.js +231 -0
- package/dist/src/commands/IndexCommand.js +79 -53
- package/dist/src/db/index.d.ts +1 -1
- package/dist/src/db/index.js +1 -2
- package/dist/src/db/schema.d.ts +5 -29
- package/dist/src/db/schema.js +2 -8
- package/dist/src/dependency-traversal.d.ts +16 -0
- package/dist/src/dependency-traversal.js +45 -0
- package/dist/src/enriched-prompt.d.ts +3 -2
- package/dist/src/enriched-prompt.js +3 -2
- package/dist/src/eval-results.d.ts +12 -0
- package/dist/src/eval-results.js +114 -0
- package/dist/src/file-utils.d.ts +15 -0
- package/dist/src/file-utils.js +24 -1
- package/dist/src/help.js +4 -1
- package/dist/src/interfaces/IAstParser.d.ts +13 -1
- package/dist/src/interfaces/IDatabase.d.ts +0 -14
- package/dist/src/interfaces/IRetrievalEvaluator.d.ts +94 -0
- package/dist/src/interfaces/IRetrievalEvaluator.js +2 -0
- package/dist/src/interfaces/ISearcher.d.ts +11 -16
- package/dist/src/interfaces/index.d.ts +1 -0
- package/dist/src/project-scope.d.ts +19 -0
- package/dist/src/project-scope.js +49 -0
- package/dist/src/project-selector.d.ts +6 -0
- package/dist/src/project-selector.js +18 -0
- package/dist/src/retrieval-eval.d.ts +16 -0
- package/dist/src/retrieval-eval.js +214 -0
- package/dist/src/services/AstParserService.d.ts +66 -1
- package/dist/src/services/AstParserService.js +432 -28
- package/dist/src/services/DatabaseService.d.ts +0 -12
- package/dist/src/services/DatabaseService.js +2 -52
- package/dist/src/services/IndexerService.d.ts +8 -0
- package/dist/src/services/IndexerService.js +24 -11
- package/dist/src/services/McpServer.d.ts +7 -0
- package/dist/src/services/McpServer.js +42 -38
- package/dist/src/services/OpenAICompatibleEmbedder.js +2 -1
- package/dist/src/services/SearcherService.d.ts +189 -25
- package/dist/src/services/SearcherService.js +505 -203
- package/dist/src/symbol-rows.d.ts +14 -9
- package/dist/src/symbol-rows.js +17 -11
- package/dist/src/tools/FindSymbolTool.js +14 -5
- package/dist/src/tools/GetCallersTool.d.ts +3 -1
- package/dist/src/tools/GetCallersTool.js +16 -6
- package/dist/src/tools/GetDependenciesTool.d.ts +3 -1
- package/dist/src/tools/GetDependenciesTool.js +15 -8
- package/dist/src/tools/GetDependentsTool.d.ts +3 -1
- package/dist/src/tools/GetDependentsTool.js +31 -16
- package/dist/src/tools/ListFilesTool.d.ts +3 -1
- package/dist/src/tools/ListFilesTool.js +14 -12
- package/dist/src/tools/ListProjectsTool.js +5 -1
- package/dist/src/tools/ReadFileRangeTool.d.ts +3 -1
- package/dist/src/tools/ReadFileRangeTool.js +12 -11
- package/dist/src/tools/ReadFileTool.d.ts +3 -1
- package/dist/src/tools/ReadFileTool.js +12 -10
- package/dist/src/tools/SearchCodebaseTool.js +3 -1
- package/dist/src/tools/format-helpers.d.ts +16 -1
- package/dist/src/tools/format-helpers.js +64 -0
- package/package.json +9 -8
|
@@ -2,6 +2,12 @@ export interface Project {
|
|
|
2
2
|
name: string;
|
|
3
3
|
path: string;
|
|
4
4
|
}
|
|
5
|
+
/**
|
|
6
|
+
* One-off single-choice prompt for `--project` with no name — distinct from
|
|
7
|
+
* the multi-select `checkbox` flow: it reads/writes nothing persisted.
|
|
8
|
+
* Resolves to `undefined` if the user cancels (e.g. Ctrl+C).
|
|
9
|
+
*/
|
|
10
|
+
export declare function selectSingleProject(units: Project[]): Promise<string | undefined>;
|
|
5
11
|
export declare class ProjectSelector {
|
|
6
12
|
private readonly workspaceDir;
|
|
7
13
|
private included;
|
|
@@ -34,9 +34,27 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.ProjectSelector = void 0;
|
|
37
|
+
exports.selectSingleProject = selectSingleProject;
|
|
37
38
|
const fs = __importStar(require("fs"));
|
|
38
39
|
const path = __importStar(require("path"));
|
|
39
40
|
const prompts_1 = require("@inquirer/prompts");
|
|
41
|
+
/**
|
|
42
|
+
* One-off single-choice prompt for `--project` with no name — distinct from
|
|
43
|
+
* the multi-select `checkbox` flow: it reads/writes nothing persisted.
|
|
44
|
+
* Resolves to `undefined` if the user cancels (e.g. Ctrl+C).
|
|
45
|
+
*/
|
|
46
|
+
async function selectSingleProject(units) {
|
|
47
|
+
try {
|
|
48
|
+
return await (0, prompts_1.select)({
|
|
49
|
+
message: 'Select a project to index',
|
|
50
|
+
choices: units.map(p => ({ name: p.name, value: p.name })),
|
|
51
|
+
pageSize: 15,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
40
58
|
async function loadLines(filePath) {
|
|
41
59
|
try {
|
|
42
60
|
const content = await fs.promises.readFile(filePath, 'utf-8');
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { EvalSuite, SearchFn, IndexInspector, IRetrievalEvaluator, EvalRunResult, EvalResultFile, CompareResult } from './interfaces/IRetrievalEvaluator';
|
|
2
|
+
/** Thrown by `parseSuite` before any search runs — names the offending case and field. */
|
|
3
|
+
export declare class EvalSuiteError extends Error {
|
|
4
|
+
readonly caseId: string | null;
|
|
5
|
+
readonly field: string | null;
|
|
6
|
+
constructor(message: string, caseId: string | null, field: string | null);
|
|
7
|
+
}
|
|
8
|
+
/** Validates a loaded suite object against the schema; rejects before any search runs. */
|
|
9
|
+
export declare function parseSuite(raw: unknown): EvalSuite;
|
|
10
|
+
/** Merges several suites (already parsed) into one; case ids must be unique across all of them. */
|
|
11
|
+
export declare function mergeSuites(suites: EvalSuite[]): EvalSuite;
|
|
12
|
+
export declare class RetrievalEvaluator implements IRetrievalEvaluator {
|
|
13
|
+
evaluate(suite: EvalSuite, search: SearchFn, inspect: IndexInspector): Promise<EvalRunResult>;
|
|
14
|
+
}
|
|
15
|
+
/** Pure comparison of two result files; prints a warning (never refuses) on mismatched conditions. */
|
|
16
|
+
export declare function compare(baseline: EvalResultFile, current: EvalResultFile): CompareResult;
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RetrievalEvaluator = exports.EvalSuiteError = void 0;
|
|
4
|
+
exports.parseSuite = parseSuite;
|
|
5
|
+
exports.mergeSuites = mergeSuites;
|
|
6
|
+
exports.compare = compare;
|
|
7
|
+
const zod_1 = require("zod");
|
|
8
|
+
/** Thrown by `parseSuite` before any search runs — names the offending case and field. */
|
|
9
|
+
class EvalSuiteError extends Error {
|
|
10
|
+
caseId;
|
|
11
|
+
field;
|
|
12
|
+
constructor(message, caseId, field) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.caseId = caseId;
|
|
15
|
+
this.field = field;
|
|
16
|
+
this.name = 'EvalSuiteError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.EvalSuiteError = EvalSuiteError;
|
|
20
|
+
const ExpectedAnswerSchema = zod_1.z.object({
|
|
21
|
+
file: zod_1.z.string().min(1),
|
|
22
|
+
owner: zod_1.z.string().min(1).optional(),
|
|
23
|
+
});
|
|
24
|
+
const CaseSchema = zod_1.z.object({
|
|
25
|
+
id: zod_1.z.string().min(1),
|
|
26
|
+
project: zod_1.z.string().min(1),
|
|
27
|
+
category: zod_1.z.enum(['identifier', 'behaviour', 'concept']),
|
|
28
|
+
query: zod_1.z.string().min(1),
|
|
29
|
+
expect: zod_1.z.array(ExpectedAnswerSchema).min(1),
|
|
30
|
+
threshold: zod_1.z.number().min(0).max(1).optional(),
|
|
31
|
+
});
|
|
32
|
+
const SuiteSchema = zod_1.z.object({
|
|
33
|
+
version: zod_1.z.literal(1),
|
|
34
|
+
cases: zod_1.z.array(CaseSchema),
|
|
35
|
+
});
|
|
36
|
+
/** Best-effort id of the case at `cases[index]` in a raw (possibly invalid) object, for error messages. */
|
|
37
|
+
function rawCaseId(raw, index) {
|
|
38
|
+
if (typeof raw !== 'object' || raw === null)
|
|
39
|
+
return null;
|
|
40
|
+
const cases = raw.cases;
|
|
41
|
+
if (!Array.isArray(cases))
|
|
42
|
+
return null;
|
|
43
|
+
const entry = cases[index];
|
|
44
|
+
if (typeof entry !== 'object' || entry === null)
|
|
45
|
+
return null;
|
|
46
|
+
const id = entry.id;
|
|
47
|
+
return typeof id === 'string' ? id : null;
|
|
48
|
+
}
|
|
49
|
+
/** Validates a loaded suite object against the schema; rejects before any search runs. */
|
|
50
|
+
function parseSuite(raw) {
|
|
51
|
+
const result = SuiteSchema.safeParse(raw);
|
|
52
|
+
if (!result.success) {
|
|
53
|
+
const issue = result.error.issues[0];
|
|
54
|
+
const pathParts = issue.path;
|
|
55
|
+
const caseIndex = typeof pathParts[1] === 'number' ? pathParts[1] : null;
|
|
56
|
+
const caseId = caseIndex !== null ? rawCaseId(raw, caseIndex) ?? `#${caseIndex}` : null;
|
|
57
|
+
const field = pathParts.length > 2 ? String(pathParts[pathParts.length - 1]) : pathParts.length === 2 ? 'id' : String(pathParts[0] ?? '');
|
|
58
|
+
throw new EvalSuiteError(`${issue.message} (case ${caseId ?? '?'}, field ${field})`, caseId, field);
|
|
59
|
+
}
|
|
60
|
+
const seen = new Set();
|
|
61
|
+
for (const c of result.data.cases) {
|
|
62
|
+
if (seen.has(c.id)) {
|
|
63
|
+
throw new EvalSuiteError(`Duplicate case id (case ${c.id}, field id)`, c.id, 'id');
|
|
64
|
+
}
|
|
65
|
+
seen.add(c.id);
|
|
66
|
+
}
|
|
67
|
+
return result.data;
|
|
68
|
+
}
|
|
69
|
+
/** Merges several suites (already parsed) into one; case ids must be unique across all of them. */
|
|
70
|
+
function mergeSuites(suites) {
|
|
71
|
+
const cases = [];
|
|
72
|
+
const seen = new Set();
|
|
73
|
+
for (const suite of suites) {
|
|
74
|
+
for (const c of suite.cases) {
|
|
75
|
+
if (seen.has(c.id))
|
|
76
|
+
throw new EvalSuiteError(`Duplicate case id across suites (case ${c.id}, field id)`, c.id, 'id');
|
|
77
|
+
seen.add(c.id);
|
|
78
|
+
cases.push(c);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return { version: 1, cases };
|
|
82
|
+
}
|
|
83
|
+
/** `owner` matches exactly, or as a prefix at a `.` boundary (`RepairsService` accepts any member of the class). */
|
|
84
|
+
function ownerMatches(ownerPath, expectedOwner) {
|
|
85
|
+
return ownerPath === expectedOwner || ownerPath.startsWith(expectedOwner + '.');
|
|
86
|
+
}
|
|
87
|
+
async function validateCase(c, inspect) {
|
|
88
|
+
const valid = [];
|
|
89
|
+
const invalid = [];
|
|
90
|
+
for (const expected of c.expect) {
|
|
91
|
+
const indexed = await inspect.isFileIndexed(c.project, expected.file);
|
|
92
|
+
if (!indexed) {
|
|
93
|
+
invalid.push(expected);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (expected.owner) {
|
|
97
|
+
const owners = await inspect.ownerPathsOfFile(c.project, expected.file);
|
|
98
|
+
const found = owners.some(o => ownerMatches(o, expected.owner));
|
|
99
|
+
if (!found) {
|
|
100
|
+
invalid.push(expected);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
valid.push(expected);
|
|
105
|
+
}
|
|
106
|
+
return { valid, invalid };
|
|
107
|
+
}
|
|
108
|
+
function rankOf(hits, valid) {
|
|
109
|
+
for (let i = 0; i < hits.length; i++) {
|
|
110
|
+
const hit = hits[i];
|
|
111
|
+
const matches = valid.some(e => hit.filePath === e.file && (!e.owner || (hit.ownerPath !== null && ownerMatches(hit.ownerPath, e.owner))));
|
|
112
|
+
if (matches)
|
|
113
|
+
return { rank: i + 1, matched: hit };
|
|
114
|
+
}
|
|
115
|
+
return { rank: null, matched: null };
|
|
116
|
+
}
|
|
117
|
+
function emptyMetrics() {
|
|
118
|
+
return { count: 0, hit1: 0, hit5: 0, mrr10: 0 };
|
|
119
|
+
}
|
|
120
|
+
function accumulate(cases) {
|
|
121
|
+
const n = cases.length;
|
|
122
|
+
if (n === 0)
|
|
123
|
+
return emptyMetrics();
|
|
124
|
+
let hit1 = 0, hit5 = 0, mrrSum = 0;
|
|
125
|
+
for (const c of cases) {
|
|
126
|
+
if (c.rank === 1)
|
|
127
|
+
hit1++;
|
|
128
|
+
if (c.rank !== null && c.rank <= 5)
|
|
129
|
+
hit5++;
|
|
130
|
+
mrrSum += c.rank !== null ? 1 / c.rank : 0;
|
|
131
|
+
}
|
|
132
|
+
return { count: n, hit1: hit1 / n, hit5: hit5 / n, mrr10: mrrSum / n };
|
|
133
|
+
}
|
|
134
|
+
class RetrievalEvaluator {
|
|
135
|
+
async evaluate(suite, search, inspect) {
|
|
136
|
+
const cases = [];
|
|
137
|
+
const invalid = [];
|
|
138
|
+
const warnings = [];
|
|
139
|
+
for (const c of suite.cases) {
|
|
140
|
+
const { valid, invalid: invalidExpects } = await validateCase(c, inspect);
|
|
141
|
+
if (valid.length === 0) {
|
|
142
|
+
invalid.push({ id: c.id, reason: 'no valid expected answer', invalidExpects });
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (invalidExpects.length > 0) {
|
|
146
|
+
warnings.push({ id: c.id, droppedExpects: invalidExpects });
|
|
147
|
+
}
|
|
148
|
+
const hits = await search(c.query, 10, c.project, c.threshold);
|
|
149
|
+
const { rank, matched } = rankOf(hits, valid);
|
|
150
|
+
cases.push({ id: c.id, project: c.project, category: c.category, query: c.query, rank, matched });
|
|
151
|
+
}
|
|
152
|
+
const byCategory = {};
|
|
153
|
+
for (const category of ['identifier', 'behaviour', 'concept']) {
|
|
154
|
+
const subset = cases.filter(c => c.category === category);
|
|
155
|
+
if (subset.length > 0)
|
|
156
|
+
byCategory[category] = accumulate(subset);
|
|
157
|
+
}
|
|
158
|
+
return { cases, invalid, warnings, metrics: { overall: accumulate(cases), byCategory } };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
exports.RetrievalEvaluator = RetrievalEvaluator;
|
|
162
|
+
function metricDelta(baseline, current) {
|
|
163
|
+
return { baseline, current, delta: current - baseline };
|
|
164
|
+
}
|
|
165
|
+
function metricsDelta(baseline, current) {
|
|
166
|
+
return {
|
|
167
|
+
count: metricDelta(baseline.count, current.count),
|
|
168
|
+
hit1: metricDelta(baseline.hit1, current.hit1),
|
|
169
|
+
hit5: metricDelta(baseline.hit5, current.hit5),
|
|
170
|
+
mrr10: metricDelta(baseline.mrr10, current.mrr10),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
/** Pure comparison of two result files; prints a warning (never refuses) on mismatched conditions. */
|
|
174
|
+
function compare(baseline, current) {
|
|
175
|
+
const conditionWarnings = [];
|
|
176
|
+
if (baseline.metadata.suiteHash !== current.metadata.suiteHash) {
|
|
177
|
+
conditionWarnings.push(`suite differs: baseline ${baseline.metadata.suiteHash} vs current ${current.metadata.suiteHash}`);
|
|
178
|
+
}
|
|
179
|
+
if (baseline.metadata.embeddingModel !== current.metadata.embeddingModel) {
|
|
180
|
+
conditionWarnings.push(`embedding model differs: baseline ${baseline.metadata.embeddingModel} vs current ${current.metadata.embeddingModel}`);
|
|
181
|
+
}
|
|
182
|
+
if (baseline.metadata.rerankerModel !== current.metadata.rerankerModel) {
|
|
183
|
+
conditionWarnings.push(`reranker differs: baseline ${baseline.metadata.rerankerModel ?? 'none'} vs current ${current.metadata.rerankerModel ?? 'none'}`);
|
|
184
|
+
}
|
|
185
|
+
const byCategory = {};
|
|
186
|
+
for (const category of ['identifier', 'behaviour', 'concept']) {
|
|
187
|
+
const b = baseline.run.metrics.byCategory[category];
|
|
188
|
+
const cur = current.run.metrics.byCategory[category];
|
|
189
|
+
if (b && cur)
|
|
190
|
+
byCategory[category] = metricsDelta(b, cur);
|
|
191
|
+
}
|
|
192
|
+
const baselineRanked = new Map(baseline.run.cases.map(c => [c.id, c.rank]));
|
|
193
|
+
const currentRanked = new Map(current.run.cases.map(c => [c.id, c.rank]));
|
|
194
|
+
const hit5Gained = [];
|
|
195
|
+
const hit5Lost = [];
|
|
196
|
+
const allIds = new Set([...baselineRanked.keys(), ...currentRanked.keys()]);
|
|
197
|
+
for (const id of allIds) {
|
|
198
|
+
const b = baselineRanked.get(id) ?? null;
|
|
199
|
+
const cur = currentRanked.get(id) ?? null;
|
|
200
|
+
const bHit5 = b !== null && b <= 5;
|
|
201
|
+
const curHit5 = cur !== null && cur <= 5;
|
|
202
|
+
if (!bHit5 && curHit5)
|
|
203
|
+
hit5Gained.push(id);
|
|
204
|
+
if (bHit5 && !curHit5)
|
|
205
|
+
hit5Lost.push(id);
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
overall: metricsDelta(baseline.run.metrics.overall, current.run.metrics.overall),
|
|
209
|
+
byCategory,
|
|
210
|
+
hit5Gained,
|
|
211
|
+
hit5Lost,
|
|
212
|
+
conditionWarnings,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
import { Node } from 'ts-morph';
|
|
1
2
|
import type { IAstParser, DepEdge, FileAnalysis, AnalyzeProjectOpts } from '../interfaces/IAstParser';
|
|
2
3
|
import type { ILogger } from '../interfaces';
|
|
4
|
+
/** Test seam for `graphql-symbols`' resolve-field parent-type resolution —
|
|
5
|
+
* defaults to the alias-following type-checker lookup `extractCalls` already uses. */
|
|
6
|
+
export interface AstParserDeps {
|
|
7
|
+
resolveDeclaration?(identifier: Node): Node | undefined;
|
|
8
|
+
}
|
|
3
9
|
export declare class AstParserService implements IAstParser {
|
|
4
10
|
private logger;
|
|
11
|
+
private deps;
|
|
5
12
|
private morphProjects;
|
|
6
|
-
constructor(logger?: ILogger);
|
|
13
|
+
constructor(logger?: ILogger, deps?: AstParserDeps);
|
|
7
14
|
analyzeProject(filePaths: string[], serviceName: string, projectRoot?: string, opts?: AnalyzeProjectOpts): Promise<Map<string, FileAnalysis>>;
|
|
8
15
|
clearCache(projectRoot?: string): void;
|
|
9
16
|
analyzeFile(filePath: string, serviceName: string, projectRoot?: string): Promise<FileAnalysis>;
|
|
@@ -12,6 +19,10 @@ export declare class AstParserService implements IAstParser {
|
|
|
12
19
|
private createMorphProject;
|
|
13
20
|
private getMorphProject;
|
|
14
21
|
private extractEdges;
|
|
22
|
+
/** Manual fallback for a bindingless import specifier (design.md decision 2) —
|
|
23
|
+
* ts-morph's `getModuleSpecifierSourceFile()` is `null` for these even when
|
|
24
|
+
* the target is a real project file, since nothing is bound to resolve through. */
|
|
25
|
+
private resolveSideEffectModuleSpecifier;
|
|
15
26
|
private resolveFromSymbol;
|
|
16
27
|
private buildUsageMap;
|
|
17
28
|
private getPos;
|
|
@@ -26,6 +37,60 @@ export declare class AstParserService implements IAstParser {
|
|
|
26
37
|
* own first-to-last line — decorator-inclusive, comment-exclusive, and (for a
|
|
27
38
|
* variable) the declarator alone, never the whole statement. */
|
|
28
39
|
private buildOwnerRanges;
|
|
40
|
+
/** Local name → the name exported by `@nestjs/graphql`, for every named import
|
|
41
|
+
* of that module specifier (aliases followed). Recognition is import-based,
|
|
42
|
+
* not type-checker-based, so `skipFileDependencyResolution` batches agree. */
|
|
43
|
+
private buildGraphqlImportMap;
|
|
44
|
+
/** The identifier naming a decorator's expression (`@Foo` or the callee of `@Foo(...)`). */
|
|
45
|
+
private static decoratorIdentifier;
|
|
46
|
+
/** `dec` recognised against `importMap` — its exported `@nestjs/graphql` name and its call arguments (`[]` for a bare `@Foo`). */
|
|
47
|
+
private recognizedDecorator;
|
|
48
|
+
private static literalStringValue;
|
|
49
|
+
private static checkGraphqlGrammar;
|
|
50
|
+
/** `true` when any decorator argument is an (unwrapped) options object
|
|
51
|
+
* literal with `isAbstract: true` — an abstract base type (`PaginatedType`
|
|
52
|
+
* pattern) that NestJS never registers in the actual GraphQL schema. */
|
|
53
|
+
private static isAbstractGraphqlType;
|
|
54
|
+
/** `obj`'s own `name` property (`PropertyAssignment` or `ShorthandPropertyAssignment`), if any. */
|
|
55
|
+
private static findNameProperty;
|
|
56
|
+
/** Strips parens/`as`/`satisfies`/non-null wrappers (any nesting) to find the
|
|
57
|
+
* underlying node — used only to classify an argument's *shape* (type
|
|
58
|
+
* function vs. options object vs. literal vs. other), never to read a name
|
|
59
|
+
* value through the wrapper (decision 4 of `graphql-symbols` forbids that). */
|
|
60
|
+
private static peekThroughWrapper;
|
|
61
|
+
/**
|
|
62
|
+
* GraphQL name from a decorator's arguments (design decision 4 of
|
|
63
|
+
* `graphql-symbols`) — syntactic, no wrapper looked through at any step.
|
|
64
|
+
* `allowOptionsName` gates step 2 (the `name` option — `Query`/`Mutation`/
|
|
65
|
+
* `Subscription`/`ResolveField` only); `fallbackName` is the TS name used at
|
|
66
|
+
* step 3.
|
|
67
|
+
*/
|
|
68
|
+
private deriveGraphqlName;
|
|
69
|
+
private logGraphqlSkip;
|
|
70
|
+
/** `declNode`'s qualified TypeScript name for diagnostics, independent of
|
|
71
|
+
* whether its own `code_symbol` row was actually written. */
|
|
72
|
+
private declarationNameFor;
|
|
73
|
+
/** The identifier named by a `@Resolver` argument: directly, or as an arrow
|
|
74
|
+
* function's expression body; or the sole identifier of a single-element
|
|
75
|
+
* array literal in either position (list-returning resolver). */
|
|
76
|
+
private static extractResolverIdentifier;
|
|
77
|
+
private defaultResolveDeclaration;
|
|
78
|
+
/** The GraphQL type name of a `@ResolveField`'s resolved parent class
|
|
79
|
+
* (design decision 5, second half): its recognised `@ObjectType` (else
|
|
80
|
+
* `@InterfaceType`) decorator, or its class name when it carries no
|
|
81
|
+
* decorator spelled `ObjectType`/`InterfaceType` at all, recognised or not. */
|
|
82
|
+
private classGraphqlTypeName;
|
|
83
|
+
/** The parent type of a `@ResolveField` — the enclosing class's recognised
|
|
84
|
+
* `@Resolver` argument (design decision 5, first half). */
|
|
85
|
+
private resolveResolverParentType;
|
|
86
|
+
private extractClassTypeRows;
|
|
87
|
+
private extractResolveFieldRow;
|
|
88
|
+
private extractMethodOperationRows;
|
|
89
|
+
/** GraphQL-named rows for every recognised `@nestjs/graphql` decorator in
|
|
90
|
+
* the file (`graphql-symbols`). `ownRows` is `symbols ∪ members` — the
|
|
91
|
+
* declarations' own `code_symbol` candidates, checked so a GraphQL row is
|
|
92
|
+
* never written without its decorated declaration's own row. */
|
|
93
|
+
private extractGraphqlNames;
|
|
29
94
|
private static isValidSegment;
|
|
30
95
|
private static memberName;
|
|
31
96
|
private static unwrapExpr;
|