@descryy/adapter-csharp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter.d.ts +31 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +263 -0
- package/dist/adapter.js.map +1 -0
- package/dist/aspnetcore.d.ts +207 -0
- package/dist/aspnetcore.d.ts.map +1 -0
- package/dist/aspnetcore.js +410 -0
- package/dist/aspnetcore.js.map +1 -0
- package/dist/client-base.d.ts +123 -0
- package/dist/client-base.d.ts.map +1 -0
- package/dist/client-base.js +123 -0
- package/dist/client-base.js.map +1 -0
- package/dist/client.d.ts +168 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +387 -0
- package/dist/client.js.map +1 -0
- package/dist/efcore.d.ts +198 -0
- package/dist/efcore.d.ts.map +1 -0
- package/dist/efcore.js +611 -0
- package/dist/efcore.js.map +1 -0
- package/dist/extract.d.ts +83 -0
- package/dist/extract.d.ts.map +1 -0
- package/dist/extract.js +1120 -0
- package/dist/extract.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/nullability.d.ts +117 -0
- package/dist/nullability.d.ts.map +1 -0
- package/dist/nullability.js +152 -0
- package/dist/nullability.js.map +1 -0
- package/dist/parse.d.ts +240 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +861 -0
- package/dist/parse.js.map +1 -0
- package/dist/projects.d.ts +87 -0
- package/dist/projects.d.ts.map +1 -0
- package/dist/projects.js +226 -0
- package/dist/projects.js.map +1 -0
- package/dist/tree-sitter-c_sharp.wasm +0 -0
- package/package.json +35 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The C# `LanguageAdapter` — all seven methods, breadth tier.
|
|
3
|
+
*
|
|
4
|
+
* The fifth language over the shared tree-sitter harness, and again nothing in
|
|
5
|
+
* `@descryy/adapter-treesitter` changed to accommodate it.
|
|
6
|
+
*
|
|
7
|
+
* `IMPLEMENTS` and `INHERITS` are both claimed, and DEC-084 is what makes the
|
|
8
|
+
* second honest: C# writes `class A : B, IC` without saying which entry is the
|
|
9
|
+
* class, but the language guarantees at most one base class and requires it
|
|
10
|
+
* first — so only one entry per class declaration is ever ambiguous, and the
|
|
11
|
+
* `IFoo` naming convention is never read.
|
|
12
|
+
*/
|
|
13
|
+
import { type LanguageAdapter, type ResolutionLevel } from "@descryy/ir";
|
|
14
|
+
import { toRepoRelative } from "@descryy/adapter-common";
|
|
15
|
+
import { LANGUAGE } from "./extract.ts";
|
|
16
|
+
export declare const ADAPTER_ID = "adapter-csharp";
|
|
17
|
+
export declare const ADAPTER_VERSION = "0.1.0";
|
|
18
|
+
export declare const DEFAULT_CSHARP_GLOBS: readonly ["**/*.cs"];
|
|
19
|
+
export interface CsharpAdapterOptions {
|
|
20
|
+
readonly globs?: readonly string[];
|
|
21
|
+
readonly maxResolution?: ResolutionLevel;
|
|
22
|
+
}
|
|
23
|
+
export interface CsharpAdapter extends LanguageAdapter {
|
|
24
|
+
readonly id: string;
|
|
25
|
+
readonly version: string;
|
|
26
|
+
}
|
|
27
|
+
export declare function createCsharpAdapter(options?: CsharpAdapterOptions): CsharpAdapter;
|
|
28
|
+
/** Content hash of a repo-relative file, for `FileRef`. */
|
|
29
|
+
export declare function hashOf(root: string, relative: string): string;
|
|
30
|
+
export { LANGUAGE, toRepoRelative };
|
|
31
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,OAAO,EAML,KAAK,eAAe,EAGpB,KAAK,eAAe,EAKrB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAyB,cAAc,EAAwB,MAAM,yBAAyB,CAAC;AAatG,OAAO,EAAW,QAAQ,EAAmB,MAAM,cAAc,CAAC;AAElE,eAAO,MAAM,UAAU,mBAAmB,CAAC;AAC3C,eAAO,MAAM,eAAe,UAAU,CAAC;AACvC,eAAO,MAAM,oBAAoB,sBAAuB,CAAC;AAUzD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,aAAa,CAAC,EAAE,eAAe,CAAC;CAC1C;AA4BD,MAAM,WAAW,aAAc,SAAQ,eAAe;IACpD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,oBAAyB,GAAG,aAAa,CA0OrF;AAED,2DAA2D;AAC3D,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM7D;AAED,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC"}
|
package/dist/adapter.js
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The C# `LanguageAdapter` — all seven methods, breadth tier.
|
|
3
|
+
*
|
|
4
|
+
* The fifth language over the shared tree-sitter harness, and again nothing in
|
|
5
|
+
* `@descryy/adapter-treesitter` changed to accommodate it.
|
|
6
|
+
*
|
|
7
|
+
* `IMPLEMENTS` and `INHERITS` are both claimed, and DEC-084 is what makes the
|
|
8
|
+
* second honest: C# writes `class A : B, IC` without saying which entry is the
|
|
9
|
+
* class, but the language guarantees at most one base class and requires it
|
|
10
|
+
* first — so only one entry per class declaration is ever ambiguous, and the
|
|
11
|
+
* `IFoo` naming convention is never read.
|
|
12
|
+
*/
|
|
13
|
+
import { createHash } from "node:crypto";
|
|
14
|
+
import { createRequire } from "node:module";
|
|
15
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
import { CLIENT_BASE_EXTRACTOR, CLIENT_CALL_EXTRACTOR, ORM_EXTRACTOR, ROUTE_EXTRACTOR, } from "@descryy/ir";
|
|
18
|
+
import { discover, isolateFile, toRepoRelative } from "@descryy/adapter-common";
|
|
19
|
+
import { parserFor, parseText } from "@descryy/adapter-treesitter";
|
|
20
|
+
import { readFile as readCsFile } from "./parse.js";
|
|
21
|
+
import { nullableDirectives, resolveNullable } from "./nullability.js";
|
|
22
|
+
import { assemblyCollisions, csharpProjects, hasSolution, isBuildOutput, projectOf, } from "./projects.js";
|
|
23
|
+
import { extract, LANGUAGE } from "./extract.js";
|
|
24
|
+
export const ADAPTER_ID = "adapter-csharp";
|
|
25
|
+
export const ADAPTER_VERSION = "0.1.0";
|
|
26
|
+
export const DEFAULT_CSHARP_GLOBS = ["**/*.cs"];
|
|
27
|
+
const PRODUCED_BY = `${ADAPTER_ID}@${ADAPTER_VERSION}`;
|
|
28
|
+
function grammarPath() {
|
|
29
|
+
const beside = join(import.meta.dirname, "tree-sitter-c_sharp.wasm");
|
|
30
|
+
if (existsSync(beside))
|
|
31
|
+
return beside;
|
|
32
|
+
return createRequire(import.meta.url).resolve("tree-sitter-c-sharp/tree-sitter-c_sharp.wasm");
|
|
33
|
+
}
|
|
34
|
+
export function createCsharpAdapter(options = {}) {
|
|
35
|
+
const globs = options.globs ?? DEFAULT_CSHARP_GLOBS;
|
|
36
|
+
const ceiling = options.maxResolution ?? 2;
|
|
37
|
+
let session;
|
|
38
|
+
let parser;
|
|
39
|
+
const filesUnder = (root) => discover(root.absolutePath, globs).filter((file) => !isBuildOutput(file));
|
|
40
|
+
return {
|
|
41
|
+
id: ADAPTER_ID,
|
|
42
|
+
version: ADAPTER_VERSION,
|
|
43
|
+
async detect(root) {
|
|
44
|
+
const files = filesUnder(root);
|
|
45
|
+
const evidence = [];
|
|
46
|
+
if (files.length > 0)
|
|
47
|
+
evidence.push(`${files.length} .cs files outside bin/ and obj/`);
|
|
48
|
+
const projects = csharpProjects(root.absolutePath, files);
|
|
49
|
+
if (projects.length > 0) {
|
|
50
|
+
evidence.push(`${projects.length} .csproj file(s)${hasSolution(root.absolutePath) ? ", with a solution" : ""}`);
|
|
51
|
+
for (const caveat of projects.flatMap((project) => project.caveats))
|
|
52
|
+
evidence.push(caveat);
|
|
53
|
+
for (const collision of assemblyCollisions(projects))
|
|
54
|
+
evidence.push(collision);
|
|
55
|
+
}
|
|
56
|
+
else if (files.length > 0) {
|
|
57
|
+
// Caps what identity can be: without a project there is no assembly, and
|
|
58
|
+
// two namespaces that collide have nothing to separate them.
|
|
59
|
+
evidence.push("no .csproj found — files belong to no assembly and cannot be separated by project");
|
|
60
|
+
}
|
|
61
|
+
return { detected: files.length > 0, roots: files.length > 0 ? [root.absolutePath] : [], evidence };
|
|
62
|
+
},
|
|
63
|
+
capabilities() {
|
|
64
|
+
return {
|
|
65
|
+
maxResolution: ceiling,
|
|
66
|
+
/**
|
|
67
|
+
* `API_ROUTE`, `API_ENDPOINT` and `MODEL` are claimed because the
|
|
68
|
+
* ASP.NET and EF Core extractors earn them from **declarations** — an
|
|
69
|
+
* attribute the framework acts on, a `DbSet<T>`, an
|
|
70
|
+
* `IEntityTypeConfiguration<T>` — never from a name. DEC-043's objection
|
|
71
|
+
* was to the name heuristic, not to the node types.
|
|
72
|
+
*
|
|
73
|
+
* `DTO` stays absent, and golden 07's note says why: `Order` and
|
|
74
|
+
* `CreateOrderRequest` are structurally identical in the corpus and
|
|
75
|
+
* separable only by their names. A mapping distinguishes the model;
|
|
76
|
+
* nothing distinguishes the DTO, so nothing claims it.
|
|
77
|
+
*
|
|
78
|
+
* `frameworkExtractors` and `frameworkCapabilities` are declared
|
|
79
|
+
* together, in the change that ships the extractors. `capabilities.ts`
|
|
80
|
+
* has landed, so the names come from its closed set rather than being
|
|
81
|
+
* invented here.
|
|
82
|
+
*
|
|
83
|
+
* `USES_API` and `client-call-extractor` are both claimed. `client.ts`
|
|
84
|
+
* (DEC-241 §4) reads `HttpClient`'s verb methods — `GetAsync`,
|
|
85
|
+
* `PostAsJsonAsync`, etc — whose path is the call's own first argument,
|
|
86
|
+
* the same structurally-straightforward shape Java's `RestTemplate`
|
|
87
|
+
* extracts from, mints real `USES_API` edges for a repository-relative
|
|
88
|
+
* literal path, and files a ledger row for every other shape (a
|
|
89
|
+
* pre-built `HttpRequestMessage` via `SendAsync`, a non-literal path,
|
|
90
|
+
* another origin, or a bare relative path this reader cannot place).
|
|
91
|
+
*
|
|
92
|
+
* `client-base-extractor` (DEC-164, patterns 08a/08b/08c) is now
|
|
93
|
+
* claimed too — the first adapter in this project to build it.
|
|
94
|
+
* `client-base.ts`'s own header has the full account; in short,
|
|
95
|
+
* every `USES_API` edge now carries `attrs.clientBase`, `resolved`
|
|
96
|
+
* when the receiver traces to `WebApplicationFactory<T>.CreateClient()`
|
|
97
|
+
* (ASP.NET's own integration-test client, whose documented default
|
|
98
|
+
* `BaseAddress` is the application under test — 100% of jellyfin's
|
|
99
|
+
* and eShopOnWeb's real `USES_API` population goes through exactly
|
|
100
|
+
* this receiver) and `none` otherwise, unchanged from this adapter's
|
|
101
|
+
* pre-DEC-164 behaviour. `unresolved` — a base-setting mechanism
|
|
102
|
+
* found but unreadable — is part of the contract and never produced
|
|
103
|
+
* by this build: the one registration shape with real corpus
|
|
104
|
+
* evidence (`AddHttpClient<T>(o => o.BaseAddress = new("..."))`,
|
|
105
|
+
* `dotnet/eShop`, 4 sites) feeds call sites already blocked by string
|
|
106
|
+
* interpolation, a separate and larger gap, so resolving it would
|
|
107
|
+
* move zero measured edges. `IHttpClientFactory.CreateClient(name)`'s
|
|
108
|
+
* named-client form is a different, real, unaddressed DI shape —
|
|
109
|
+
* every such call was invisible before this row and stays exactly
|
|
110
|
+
* as invisible after it.
|
|
111
|
+
*/
|
|
112
|
+
nodeTypes: ["MODULE", "CLASS", "FUNCTION", "TEST_CASE", "API_ROUTE", "API_ENDPOINT", "MODEL"],
|
|
113
|
+
edgeTypes: ["IMPORTS", "CALLS", "TESTS", "INHERITS", "IMPLEMENTS", "USES_TYPE", "SERVES_API", "USES_API"],
|
|
114
|
+
hasCallHierarchy: false,
|
|
115
|
+
hasTypeHierarchy: false,
|
|
116
|
+
/**
|
|
117
|
+
* Directories this ecosystem installs dependencies or build output into —
|
|
118
|
+
* DEC-110's end state. The engine used to hold `node_modules`/`vendor`/`target`
|
|
119
|
+
* in a hardcoded list; it holds none of them now and consumes this instead.
|
|
120
|
+
*
|
|
121
|
+
* `projects.ts` already excludes all four — `obj/` in particular would put a second
|
|
122
|
+
* copy of every generated source in the graph, "the same reason Go excludes
|
|
123
|
+
* vendor/".
|
|
124
|
+
*
|
|
125
|
+
* Names only — the walk matches one path component, so a path or glob matches
|
|
126
|
+
* nothing. Nothing dotted — those are skipped structurally already.
|
|
127
|
+
*/
|
|
128
|
+
noiseDirectories: ["bin", "obj", "packages", "node_modules"],
|
|
129
|
+
frameworkExtractors: ["aspnetcore", "efcore", "client-call-extractor", "client-base-extractor"],
|
|
130
|
+
frameworkCapabilities: [ROUTE_EXTRACTOR, ORM_EXTRACTOR, CLIENT_CALL_EXTRACTOR, CLIENT_BASE_EXTRACTOR],
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
async prepare(ctx) {
|
|
134
|
+
// Sorted, so which part of a partial type is primary is a property of the
|
|
135
|
+
// repository rather than of filesystem order — DEC-083 requires it.
|
|
136
|
+
const files = [...filesUnder(ctx.root)].sort();
|
|
137
|
+
parser ??= await parserFor(grammarPath());
|
|
138
|
+
const projects = csharpProjects(ctx.root.absolutePath, files);
|
|
139
|
+
const units = [];
|
|
140
|
+
const failures = [];
|
|
141
|
+
const unfinished = [];
|
|
142
|
+
const withoutProject = new Set();
|
|
143
|
+
const sources = new Map();
|
|
144
|
+
for (const file of files) {
|
|
145
|
+
if (ctx.signal.aborted)
|
|
146
|
+
break;
|
|
147
|
+
let text;
|
|
148
|
+
try {
|
|
149
|
+
text = readFileSync(join(ctx.root.absolutePath, file), "utf8");
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
failures.push({ kind: "unreadable", file, detail: error instanceof Error ? error.message : String(error) });
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const outcome = parseText(parser, file, text);
|
|
156
|
+
if (outcome.tree === undefined) {
|
|
157
|
+
if (outcome.failure !== undefined)
|
|
158
|
+
failures.push(outcome.failure);
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
// Isolated: a throw in the walk used to cost the run rather than the file.
|
|
162
|
+
const read = isolateFile(file, () => readCsFile(outcome.tree.rootNode, file, outcome.tree.rootNode.hasError));
|
|
163
|
+
outcome.tree.delete();
|
|
164
|
+
if (read.failure !== undefined) {
|
|
165
|
+
unfinished.push(read.failure);
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
const unit = read.value;
|
|
169
|
+
const project = projectOf(projects, file);
|
|
170
|
+
if (project === undefined)
|
|
171
|
+
withoutProject.add(file);
|
|
172
|
+
units.push({ ...unit, assembly: project?.assembly ?? "" });
|
|
173
|
+
sources.set(file, text);
|
|
174
|
+
}
|
|
175
|
+
const reached = units.length === 0 ? 0 : ceiling;
|
|
176
|
+
session = {
|
|
177
|
+
root: ctx.root,
|
|
178
|
+
files,
|
|
179
|
+
units,
|
|
180
|
+
reached,
|
|
181
|
+
failures,
|
|
182
|
+
unfinished,
|
|
183
|
+
projects,
|
|
184
|
+
withoutProject: [...withoutProject],
|
|
185
|
+
sources,
|
|
186
|
+
};
|
|
187
|
+
return reached;
|
|
188
|
+
},
|
|
189
|
+
async parseFiles(files) {
|
|
190
|
+
const byFile = new Map((session?.units ?? []).map((unit) => [unit.file, unit]));
|
|
191
|
+
return files.map((file) => ({ file, tree: byFile.get(file.path) ?? {} }));
|
|
192
|
+
},
|
|
193
|
+
extractNodes() {
|
|
194
|
+
return current().nodes;
|
|
195
|
+
},
|
|
196
|
+
extractEdges() {
|
|
197
|
+
return current().edges;
|
|
198
|
+
},
|
|
199
|
+
async emit(ctx) {
|
|
200
|
+
const reached = await this.prepare(ctx);
|
|
201
|
+
const active = session;
|
|
202
|
+
const extraction = current();
|
|
203
|
+
const failedFiles = new Set([...active.failures.map((f) => f.file), ...active.unfinished.map((f) => f.file)]);
|
|
204
|
+
return {
|
|
205
|
+
repo: ctx.root.repo,
|
|
206
|
+
...(ctx.root.workspace === undefined ? {} : { workspace: ctx.root.workspace }),
|
|
207
|
+
commitSha: ctx.root.commitSha,
|
|
208
|
+
producedBy: PRODUCED_BY,
|
|
209
|
+
sourceFiles: active.files.filter((f) => !failedFiles.has(f)),
|
|
210
|
+
skippedFiles: [
|
|
211
|
+
...active.failures.map((f) => ({ file: f.file, reason: f.kind, detail: f.detail })),
|
|
212
|
+
// `other`: it parsed, and what failed is our walk of it. The detail says which.
|
|
213
|
+
...active.unfinished.map((f) => ({ file: f.file, reason: "other", detail: f.detail })),
|
|
214
|
+
],
|
|
215
|
+
reachedResolution: reached,
|
|
216
|
+
nodes: extraction.nodes,
|
|
217
|
+
edges: extraction.edges,
|
|
218
|
+
unresolved: extraction.unresolved,
|
|
219
|
+
};
|
|
220
|
+
},
|
|
221
|
+
async dispose() {
|
|
222
|
+
session = undefined;
|
|
223
|
+
parser?.delete();
|
|
224
|
+
parser = undefined;
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
function current() {
|
|
228
|
+
const active = session;
|
|
229
|
+
if (active === undefined || active.units.length === 0) {
|
|
230
|
+
return { nodes: [], edges: [], unresolved: [] };
|
|
231
|
+
}
|
|
232
|
+
return extract({
|
|
233
|
+
repo: active.root.repo,
|
|
234
|
+
scope: { repo: active.root.repo, ...(active.root.workspace === undefined ? {} : { workspace: active.root.workspace }) },
|
|
235
|
+
producedBy: PRODUCED_BY,
|
|
236
|
+
reached: active.reached,
|
|
237
|
+
files: active.units,
|
|
238
|
+
sources: active.sources,
|
|
239
|
+
// Only this layer holds the projects and the imported props, which are two
|
|
240
|
+
// of DEC-180 §1's three sources. Passing a resolver rather than the raw
|
|
241
|
+
// values keeps the precedence in one place.
|
|
242
|
+
nullableContextOf: (file) => {
|
|
243
|
+
const project = projectOf(active.projects, file);
|
|
244
|
+
return resolveNullable({
|
|
245
|
+
directives: nullableDirectives(active.sources.get(file) ?? ""),
|
|
246
|
+
project: project?.nullable ?? "absent",
|
|
247
|
+
directoryProps: project?.directoryProps ?? "absent",
|
|
248
|
+
}, Number.MAX_SAFE_INTEGER).context;
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/** Content hash of a repo-relative file, for `FileRef`. */
|
|
254
|
+
export function hashOf(root, relative) {
|
|
255
|
+
try {
|
|
256
|
+
return createHash("sha256").update(readFileSync(join(root, relative))).digest("hex").slice(0, 32);
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
return "";
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
export { LANGUAGE, toRepoRelative };
|
|
263
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The HTTP-route framework extractor for C# — ASP.NET Core.
|
|
3
|
+
*
|
|
4
|
+
* **An `API_ROUTE` is a framework claim, and this file is where it is earned.**
|
|
5
|
+
* DEC-128 §1 fixed the rules before extraction; **DEC-181 amended them** after
|
|
6
|
+
* re-counting in the unit the rules decide, and that amendment is most of what
|
|
7
|
+
* is load-bearing here.
|
|
8
|
+
*
|
|
9
|
+
* ## The evidence is on an ancestor and the subject is the descendant
|
|
10
|
+
*
|
|
11
|
+
* DEC-128 §1a admits a method carrying `[HttpGet]` on a controller, where a
|
|
12
|
+
* controller is `[ApiController]` or a `ControllerBase` base. Counted in files:
|
|
13
|
+
*
|
|
14
|
+
* | corpus | files with a verb attribute | directly admissible | **need the base chain** |
|
|
15
|
+
* | --- | --- | --- | --- |
|
|
16
|
+
* | `jellyfin` | 62 | **1** | **61** |
|
|
17
|
+
* | `Ocelot` | 9 | 9 | 0 |
|
|
18
|
+
* | `eShopOnWeb` | 4 | 3 | 1 |
|
|
19
|
+
*
|
|
20
|
+
* Every `jellyfin` controller is `class XController : BaseJellyfinApiController`,
|
|
21
|
+
* and the base carries `[ApiController]` and `[Route("[controller]")]`. **A reader
|
|
22
|
+
* checking the class itself admits 1 of 62 and emits nothing for ~400 verb
|
|
23
|
+
* attributes** — a silent recall hole, which the precision apparatus is
|
|
24
|
+
* structurally blind to.
|
|
25
|
+
*
|
|
26
|
+
* This is the **third instance of one construct class**: the evidence sits on an
|
|
27
|
+
* ancestor and the subject is the descendant. JPA's `@MappedSuperclass` cost a
|
|
28
|
+
* third of an entity population; this is 98% of a corpus. It is checked for by
|
|
29
|
+
* name now rather than met by surprise.
|
|
30
|
+
*
|
|
31
|
+
* ## `[controller]` expands against the SUBJECT class, not the declaring one
|
|
32
|
+
*
|
|
33
|
+
* The `[Route("[controller]")]` governing 61 `jellyfin` controllers is declared
|
|
34
|
+
* **once, on the base**. A reader expanding the token where it finds the
|
|
35
|
+
* attribute produces `BaseJellyfinApi` sixty-one times — **one wrong template,
|
|
36
|
+
* sixty-one routes**. That is a wrong answer where the recall hole above is only
|
|
37
|
+
* a silence, and rule 2 does not treat them the same.
|
|
38
|
+
*
|
|
39
|
+
* The template is inherited; the *name substituted into it* never is.
|
|
40
|
+
*
|
|
41
|
+
* ## A derived `[Route]` REPLACES an inherited one — it does not join
|
|
42
|
+
*
|
|
43
|
+
* `ActivityLogController` carries `[Route("System/ActivityLog")]` while
|
|
44
|
+
* inheriting `[Route("[controller]")]`. ASP.NET takes the most-derived; they do
|
|
45
|
+
* not concatenate.
|
|
46
|
+
*
|
|
47
|
+
* §1a *does* join a class template to a method template, and the two joins look
|
|
48
|
+
* alike — which is the trap. **Before reusing a composition rule on a new pair,
|
|
49
|
+
* establish that the new pair composes at all.** Class↔method concatenates;
|
|
50
|
+
* class↔base overrides.
|
|
51
|
+
*
|
|
52
|
+
* ## Both mechanisms, counted apart
|
|
53
|
+
*
|
|
54
|
+
* | corpus | verb attributes | `Map*` | `MapGroup` |
|
|
55
|
+
* | --- | --- | --- | --- |
|
|
56
|
+
* | `jellyfin` | 400 | 0 | 0 |
|
|
57
|
+
* | `Ocelot` | 19 | 3 | 0 |
|
|
58
|
+
* | `eShopOnWeb` | 26 | 7 | 0 |
|
|
59
|
+
* | `CleanArchitecture` | **0** | **15** | **1** |
|
|
60
|
+
*
|
|
61
|
+
* `CleanArchitecture` is attribute-free and `jellyfin` is minimal-API-free, so an
|
|
62
|
+
* extractor supporting one mechanism scores zero on a whole corpus rather than a
|
|
63
|
+
* degraded number. Counted separately so a total miss cannot average into a
|
|
64
|
+
* healthy-looking figure. **`MapGroup` has one witness in the entire control set**
|
|
65
|
+
* — stated rather than discovered later, because a rule with one witness is not
|
|
66
|
+
* measured.
|
|
67
|
+
*/
|
|
68
|
+
import type { CsFile } from "./parse.ts";
|
|
69
|
+
export interface AspNetRoute {
|
|
70
|
+
readonly method: string;
|
|
71
|
+
/** The joined template, normalised. `{id:int}` and `{id?}` both give `{param}`. */
|
|
72
|
+
readonly template: string;
|
|
73
|
+
/** The template before normalisation, for the ledger. */
|
|
74
|
+
readonly written: string;
|
|
75
|
+
readonly line: number;
|
|
76
|
+
readonly file: string;
|
|
77
|
+
/** The declaring type's nesting path plus the handler name. */
|
|
78
|
+
readonly handlerPath: readonly string[];
|
|
79
|
+
readonly mechanism: "attribute" | "minimal";
|
|
80
|
+
readonly framework: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* A class whose own controller status could not be determined because an
|
|
84
|
+
* ancestor's base name left the analysed set (an external NuGet package, most
|
|
85
|
+
* often `Ardalis.ApiEndpoints`). See {@link AspNetCensus.unresolvedBaseSites}.
|
|
86
|
+
*/
|
|
87
|
+
export interface AspNetUnresolvedBaseSite {
|
|
88
|
+
/** Nesting path including the class's own name, outermost first. */
|
|
89
|
+
readonly path: readonly string[];
|
|
90
|
+
readonly file: string;
|
|
91
|
+
readonly line: number;
|
|
92
|
+
/** The base name the chain could not resolve. */
|
|
93
|
+
readonly base: string;
|
|
94
|
+
}
|
|
95
|
+
export interface AspNetCensus {
|
|
96
|
+
readonly routes: readonly AspNetRoute[];
|
|
97
|
+
/** Controllers whose own declaration carried the evidence. */
|
|
98
|
+
readonly admittedDirectly: number;
|
|
99
|
+
/** Controllers admitted only by walking the base chain. DEC-181 §1. */
|
|
100
|
+
readonly admittedViaBaseChain: number;
|
|
101
|
+
/** Base names that resolve to more than one analysed type; the walk stops. */
|
|
102
|
+
readonly ambiguousBaseNames: readonly string[];
|
|
103
|
+
/** Base names that leave the analysed set; not assumed to reach ControllerBase. */
|
|
104
|
+
readonly baseChainsLeftAnalysedSet: number;
|
|
105
|
+
/**
|
|
106
|
+
* **Which** base names left the set, with a count each.
|
|
107
|
+
*
|
|
108
|
+
* A bare total is not actionable. `eShopOnWeb` reports 30 chains leaving the
|
|
109
|
+
* analysed set and at least one of them —
|
|
110
|
+
* `EndpointBaseAsync.WithRequest<T>.WithActionResult<T>` from
|
|
111
|
+
* `Ardalis.ApiEndpoints` — is a **real controller base serving real routes**
|
|
112
|
+
* that this graph does not have.
|
|
113
|
+
*
|
|
114
|
+
* The gap is not closable by reading the name: admitting a class because its
|
|
115
|
+
* base is *called* something ending in `Endpoint` is the name rule this
|
|
116
|
+
* project forbids, and it is what pattern 15 exists to punish. Closing it
|
|
117
|
+
* honestly needs the package analysed, and dependencies are never installed
|
|
118
|
+
* here. So it is **named** instead, which is the difference between a
|
|
119
|
+
* disclosed gap a reader can act on and a number they cannot.
|
|
120
|
+
*/
|
|
121
|
+
readonly unresolvedBases: readonly {
|
|
122
|
+
readonly name: string;
|
|
123
|
+
readonly count: number;
|
|
124
|
+
}[];
|
|
125
|
+
/**
|
|
126
|
+
* The same population as {@link unresolvedBases}, per site rather than
|
|
127
|
+
* aggregated by base name — what `extract.ts` actually ledgers. D-FIX-3's
|
|
128
|
+
* own finding: before this field existed, `unresolvedBases`'s aggregate
|
|
129
|
+
* count was computed correctly and never reached `emit()`'s own
|
|
130
|
+
* `unresolved: UnresolvedRef[]` — a real refusal, silently dropped between
|
|
131
|
+
* this census and the graph a caller actually reads. See `extract.ts`'s
|
|
132
|
+
* own `SERVES_API` ledger call for where this closes.
|
|
133
|
+
*/
|
|
134
|
+
readonly unresolvedBaseSites: readonly AspNetUnresolvedBaseSite[];
|
|
135
|
+
/** Mapping sites whose path is not a literal. Refused, per DEC-128 §1. */
|
|
136
|
+
readonly refusedNonLiteral: number;
|
|
137
|
+
/** `[controller]` tokens expanded, and tokens this reader does not know. */
|
|
138
|
+
readonly tokensExpanded: number;
|
|
139
|
+
readonly tokensUnknown: number;
|
|
140
|
+
/** `MapGroup` calls whose prefix is a literal and was bound to a variable. */
|
|
141
|
+
readonly mapGroupsSeen: number;
|
|
142
|
+
/**
|
|
143
|
+
* `Map*` sites whose receiver is not a known group in a file that declares
|
|
144
|
+
* groups. Refused, because the route may belong to one.
|
|
145
|
+
*
|
|
146
|
+
* DEC-128 §1b: *"where that is unavailable the route is emitted with its
|
|
147
|
+
* suffix only and flagged unresolved, never silently as if the suffix were the
|
|
148
|
+
* whole path."* The first version of this file emitted suffix-only with no
|
|
149
|
+
* flag — a violation of the rule this lane wrote, found by adding the corpus
|
|
150
|
+
* the gap register said was missing.
|
|
151
|
+
*/
|
|
152
|
+
readonly minimalRefusedUnknownGroup: number;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Does this file import ASP.NET's routing surface? Provenance, not spelling.
|
|
156
|
+
*
|
|
157
|
+
* **A `global using` counts and is in force across the assembly**, which is how
|
|
158
|
+
* modern C# is written — .NET 6 onward emits an implicit global-usings file per
|
|
159
|
+
* project. `efcore.ts` records the measured cost of ignoring it: nine `DbSet<T>`
|
|
160
|
+
* read as zero because the `using` sat three files away.
|
|
161
|
+
*
|
|
162
|
+
* Scoped by assembly, because a global using in one project does not reach
|
|
163
|
+
* another.
|
|
164
|
+
*/
|
|
165
|
+
export declare function importsAspNet(file: CsFile, globalsByAssembly?: ReadonlyMap<string, boolean>): boolean;
|
|
166
|
+
/**
|
|
167
|
+
* Normalise a route template to its joinable form.
|
|
168
|
+
*
|
|
169
|
+
* `{id}`, `{id:int}`, `{id:guid}`, `{id?}` and `{*rest}` all serve the same URL
|
|
170
|
+
* shape and must mint the same endpoint id, or a caller in another language
|
|
171
|
+
* joins to nothing. Golden 04's note names `{id:guid}` explicitly.
|
|
172
|
+
*
|
|
173
|
+
* The *written* form is kept beside this one. Identity normalises and fidelity
|
|
174
|
+
* preserves; collapsing to one deletes a fact that surfaces as a clean zero
|
|
175
|
+
* somewhere else.
|
|
176
|
+
*/
|
|
177
|
+
export declare function normaliseTemplate(written: string): string;
|
|
178
|
+
/**
|
|
179
|
+
* Expand `[controller]`, `[action]` and `[area]` against the **subject**.
|
|
180
|
+
*
|
|
181
|
+
* `subject` is the most-derived class the route is being computed for, never the
|
|
182
|
+
* class that declared the attribute — see the file comment. `[controller]` drops
|
|
183
|
+
* the `Controller` suffix, which is the framework's own rule and the one place a
|
|
184
|
+
* class name legitimately enters a path.
|
|
185
|
+
*
|
|
186
|
+
* A token this reader does not know yields `undefined`: the route is refused and
|
|
187
|
+
* logged rather than emitted with a guessed path.
|
|
188
|
+
*/
|
|
189
|
+
export declare function expandTokens(template: string, subject: string, action: string): {
|
|
190
|
+
expanded: string;
|
|
191
|
+
tokens: number;
|
|
192
|
+
} | undefined;
|
|
193
|
+
export interface AspNetInput {
|
|
194
|
+
readonly files: readonly {
|
|
195
|
+
readonly parsed: CsFile;
|
|
196
|
+
readonly source: string;
|
|
197
|
+
}[];
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Every route the batch declares, with the admission accounting reported.
|
|
201
|
+
*
|
|
202
|
+
* Cross-file by necessity: the evidence making a class a controller is usually on
|
|
203
|
+
* a base declared elsewhere. Same shape as JPA's mapped-superclass walk, for the
|
|
204
|
+
* same construct.
|
|
205
|
+
*/
|
|
206
|
+
export declare function aspNetRoutes(input: AspNetInput): AspNetCensus;
|
|
207
|
+
//# sourceMappingURL=aspnetcore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aspnetcore.d.ts","sourceRoot":"","sources":["../src/aspnetcore.ts"],"names":[],"mappings":"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"}
|