@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.
@@ -0,0 +1,410 @@
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
+ const MVC_NAMESPACES = ["Microsoft.AspNetCore.Mvc", "Microsoft.AspNetCore.Http", "Microsoft.AspNetCore.Builder", "Microsoft.AspNetCore.Routing"];
69
+ const VERB_ATTRIBUTES = new Map([
70
+ ["HttpGet", "GET"],
71
+ ["HttpPost", "POST"],
72
+ ["HttpPut", "PUT"],
73
+ ["HttpPatch", "PATCH"],
74
+ ["HttpDelete", "DELETE"],
75
+ ["HttpHead", "HEAD"],
76
+ ["HttpOptions", "OPTIONS"],
77
+ ]);
78
+ const MINIMAL_VERBS = new Map([
79
+ ["MapGet", "GET"],
80
+ ["MapPost", "POST"],
81
+ ["MapPut", "PUT"],
82
+ ["MapPatch", "PATCH"],
83
+ ["MapDelete", "DELETE"],
84
+ ]);
85
+ const importsIn = (file, onlyGlobal = false) => file.usings.some((using) => (!onlyGlobal || using.isGlobal) &&
86
+ MVC_NAMESPACES.some((ns) => using.target === ns || using.target.startsWith(`${ns}.`)));
87
+ /**
88
+ * Does this file import ASP.NET's routing surface? Provenance, not spelling.
89
+ *
90
+ * **A `global using` counts and is in force across the assembly**, which is how
91
+ * modern C# is written — .NET 6 onward emits an implicit global-usings file per
92
+ * project. `efcore.ts` records the measured cost of ignoring it: nine `DbSet<T>`
93
+ * read as zero because the `using` sat three files away.
94
+ *
95
+ * Scoped by assembly, because a global using in one project does not reach
96
+ * another.
97
+ */
98
+ export function importsAspNet(file, globalsByAssembly) {
99
+ if (importsIn(file))
100
+ return true;
101
+ return globalsByAssembly?.get(file.assembly) === true;
102
+ }
103
+ /**
104
+ * Normalise a route template to its joinable form.
105
+ *
106
+ * `{id}`, `{id:int}`, `{id:guid}`, `{id?}` and `{*rest}` all serve the same URL
107
+ * shape and must mint the same endpoint id, or a caller in another language
108
+ * joins to nothing. Golden 04's note names `{id:guid}` explicitly.
109
+ *
110
+ * The *written* form is kept beside this one. Identity normalises and fidelity
111
+ * preserves; collapsing to one deletes a fact that surfaces as a clean zero
112
+ * somewhere else.
113
+ */
114
+ export function normaliseTemplate(written) {
115
+ return written.replace(/\{[^}]*\}/g, "{param}");
116
+ }
117
+ /** Join a class prefix to a method suffix. Slashes are forgiving on both sides. */
118
+ function join(prefix, suffix) {
119
+ const left = prefix.replace(/^\/+|\/+$/g, "");
120
+ const right = suffix.replace(/^\/+|\/+$/g, "");
121
+ if (left === "" && right === "")
122
+ return "/";
123
+ if (left === "")
124
+ return `/${right}`;
125
+ if (right === "")
126
+ return `/${left}`;
127
+ return `/${left}/${right}`;
128
+ }
129
+ /**
130
+ * Expand `[controller]`, `[action]` and `[area]` against the **subject**.
131
+ *
132
+ * `subject` is the most-derived class the route is being computed for, never the
133
+ * class that declared the attribute — see the file comment. `[controller]` drops
134
+ * the `Controller` suffix, which is the framework's own rule and the one place a
135
+ * class name legitimately enters a path.
136
+ *
137
+ * A token this reader does not know yields `undefined`: the route is refused and
138
+ * logged rather than emitted with a guessed path.
139
+ */
140
+ export function expandTokens(template, subject, action) {
141
+ let tokens = 0;
142
+ let unknown = false;
143
+ const expanded = template.replace(/\[([a-zA-Z]+)\]/g, (whole, token) => {
144
+ const lower = token.toLowerCase();
145
+ if (lower === "controller") {
146
+ tokens += 1;
147
+ return subject.replace(/Controller$/, "");
148
+ }
149
+ if (lower === "action") {
150
+ tokens += 1;
151
+ return action;
152
+ }
153
+ // `[area]` needs the area attribute, which this does not read. Refused
154
+ // rather than dropped: a path missing a segment is a wrong path.
155
+ unknown = true;
156
+ return whole;
157
+ });
158
+ return unknown ? undefined : { expanded, tokens };
159
+ }
160
+ /**
161
+ * Every route the batch declares, with the admission accounting reported.
162
+ *
163
+ * Cross-file by necessity: the evidence making a class a controller is usually on
164
+ * a base declared elsewhere. Same shape as JPA's mapped-superclass walk, for the
165
+ * same construct.
166
+ */
167
+ export function aspNetRoutes(input) {
168
+ /** Simple name -> every analysed declaration. A list, never one — DEC-181 and
169
+ * the `eShopOnWeb` DTO collision that made a list necessary in `efcore.ts`. */
170
+ const byName = new Map();
171
+ for (const { parsed } of input.files) {
172
+ for (const type of parsed.types) {
173
+ const bucket = byName.get(type.name);
174
+ if (bucket === undefined)
175
+ byName.set(type.name, [{ type, file: parsed }]);
176
+ else
177
+ bucket.push({ type, file: parsed });
178
+ }
179
+ }
180
+ const ambiguousBaseNames = new Set();
181
+ let baseChainsLeftAnalysedSet = 0;
182
+ const unresolvedBases = new Map();
183
+ /**
184
+ * Walk a type's base chain, nearest first, collecting what the chain states.
185
+ *
186
+ * Returns whether the chain reaches controller evidence, and the **nearest**
187
+ * `[Route]` template found — nearest, because a derived route replaces an
188
+ * inherited one rather than joining it.
189
+ */
190
+ const chainOf = (start) => {
191
+ const seen = new Set();
192
+ let current = start;
193
+ let isController = false;
194
+ let direct = false;
195
+ let route;
196
+ let unresolvedBase;
197
+ let depth = 0;
198
+ while (current !== undefined) {
199
+ const key = `${current.file.file}::${current.type.path.join(".")}`;
200
+ if (seen.has(key))
201
+ break;
202
+ seen.add(key);
203
+ const attributes = new Set(current.type.attributes);
204
+ if (attributes.has("ApiController")) {
205
+ isController = true;
206
+ if (depth === 0)
207
+ direct = true;
208
+ }
209
+ // `[Route("…")]` — the nearest declaration wins and the walk keeps going
210
+ // only to look for controller evidence, never to join another template.
211
+ if (route === undefined) {
212
+ const declared = current.type.attributeArguments["Route"];
213
+ if (declared !== undefined)
214
+ route = declared;
215
+ }
216
+ const base = current.type.baseList[0];
217
+ if (base === undefined)
218
+ break;
219
+ if (base === "Controller" || base === "ControllerBase") {
220
+ isController = true;
221
+ if (depth === 0)
222
+ direct = true;
223
+ break;
224
+ }
225
+ const candidates = byName.get(base);
226
+ if (candidates === undefined || candidates.length === 0) {
227
+ baseChainsLeftAnalysedSet += 1;
228
+ // A base written across lines (`EndpointBaseAsync\n .WithRequest<T>`)
229
+ // is one name; collapsing the whitespace keeps the report readable.
230
+ const flat = base.replace(/\s+/g, "");
231
+ unresolvedBases.set(flat, (unresolvedBases.get(flat) ?? 0) + 1);
232
+ // Attributed to `start` regardless of how many hops away this base
233
+ // sits — `chainOf` answers "is `start` transitively a controller,"
234
+ // and an ancestor's unresolved base is exactly why `start`'s own
235
+ // answer could not be determined.
236
+ unresolvedBase = flat;
237
+ break;
238
+ }
239
+ if (candidates.length > 1) {
240
+ // Two declarations of one base name. Refused rather than guessed: the
241
+ // wrong base gives the wrong `[Route]` and therefore the wrong path.
242
+ ambiguousBaseNames.add(base);
243
+ break;
244
+ }
245
+ current = candidates[0];
246
+ depth += 1;
247
+ }
248
+ return { isController, direct, route, unresolvedBase };
249
+ };
250
+ const routes = [];
251
+ const unresolvedBaseSites = [];
252
+ let admittedDirectly = 0;
253
+ let admittedViaBaseChain = 0;
254
+ let refusedNonLiteral = 0;
255
+ let tokensExpanded = 0;
256
+ let tokensUnknown = 0;
257
+ let mapGroupsSeen = 0;
258
+ let minimalRefusedUnknownGroup = 0;
259
+ // Global usings, gathered once per assembly. See `importsAspNet`.
260
+ const mvcGlobals = new Map();
261
+ for (const { parsed } of input.files) {
262
+ if (importsIn(parsed, true))
263
+ mvcGlobals.set(parsed.assembly, true);
264
+ }
265
+ for (const { parsed, source } of input.files) {
266
+ if (!importsAspNet(parsed, mvcGlobals))
267
+ continue;
268
+ for (const type of parsed.types) {
269
+ const chain = chainOf({ type, file: parsed });
270
+ if (!chain.isController) {
271
+ // Not silently dropped (D-FIX-3's own finding, this row) — `start`
272
+ // never reached controller evidence *because* an ancestor's base
273
+ // left the analysed set, so this class's own controller status is
274
+ // genuinely undetermined, not "no". `extract.ts` turns this into a
275
+ // `SERVES_API` ledger row; an ordinary non-controller class (no
276
+ // `unresolvedBase`) is correctly not reported at all.
277
+ if (chain.unresolvedBase !== undefined) {
278
+ unresolvedBaseSites.push({
279
+ path: type.path,
280
+ file: parsed.file,
281
+ line: type.startLine,
282
+ base: chain.unresolvedBase,
283
+ });
284
+ }
285
+ continue;
286
+ }
287
+ if (chain.direct)
288
+ admittedDirectly += 1;
289
+ else
290
+ admittedViaBaseChain += 1;
291
+ for (const func of parsed.funcs) {
292
+ if (func.ownerPath.join(".") !== type.path.join("."))
293
+ continue;
294
+ for (const attribute of func.attributes) {
295
+ const verb = VERB_ATTRIBUTES.get(attribute);
296
+ if (verb === undefined)
297
+ continue;
298
+ // RT-208 (descry-runtime): a bare, argument-less verb attribute
299
+ // (`[HttpPost]`) can still carry its own route suffix on a
300
+ // *sibling* `[Route("...")]` attribute on the same method --
301
+ // ASP.NET Core's own documented composition, confirmed on a real,
302
+ // unmodified repo (`dotnet-architecture/eShopOnWeb`'s
303
+ // `UserController.Logout()`: `[Route("Logout")] [HttpPost]`
304
+ // serves `POST /User/Logout`, not the bare `POST /User` this
305
+ // reader used to emit by only ever looking at the verb
306
+ // attribute's own argument). The verb attribute's own argument
307
+ // still wins when both are present -- that shape is unchanged.
308
+ const suffix = func.attributeArguments[attribute] ?? func.attributeArguments["Route"];
309
+ // A verb attribute with no argument and no sibling `[Route]` is
310
+ // legal and means "the class template". It is only refused when
311
+ // there was something to read and it could not be read as a
312
+ // literal.
313
+ const classPart = chain.route ?? "";
314
+ const expanded = expandTokens(join(classPart, suffix ?? ""), type.name, func.name);
315
+ if (expanded === undefined) {
316
+ tokensUnknown += 1;
317
+ continue;
318
+ }
319
+ if (expanded.expanded.includes("[")) {
320
+ refusedNonLiteral += 1;
321
+ continue;
322
+ }
323
+ tokensExpanded += expanded.tokens;
324
+ routes.push({
325
+ method: verb,
326
+ template: normaliseTemplate(expanded.expanded),
327
+ written: expanded.expanded,
328
+ line: func.startLine,
329
+ file: parsed.file,
330
+ handlerPath: [...type.path, func.name],
331
+ mechanism: "attribute",
332
+ framework: "aspnetcore",
333
+ });
334
+ }
335
+ }
336
+ }
337
+ // `MapGroup` prefixes, bound to the variable that holds the builder.
338
+ //
339
+ // **DEC-128 §1b named this and the first version of this file did not do
340
+ // it**, emitting `/items` where the served path is `/api/catalog/items` — a
341
+ // wrong template silently, which §1b explicitly forbids. It was found by
342
+ // adding `eShop`, the corpus the gap register said was missing, which is the
343
+ // whole argument for filling a gap rather than carrying it.
344
+ //
345
+ // `var api = vApi.MapGroup("api/catalog")` binds `api`. A group whose prefix
346
+ // is not a literal binds nothing, so a `Map*` on it is refused below rather
347
+ // than emitted bare.
348
+ const groups = new Map();
349
+ const groupDecl = /\b(?:var|[A-Za-z_][A-Za-z0-9_<>,. ]*?)\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*[^;]*?\.\s*MapGroup\s*\(\s*"([^"]*)"/g;
350
+ for (let match = groupDecl.exec(source); match !== null; match = groupDecl.exec(source)) {
351
+ groups.set(match[1], match[2]);
352
+ mapGroupsSeen += 1;
353
+ }
354
+ /** Groups whose prefix could not be read. A `Map*` on one of these is refused. */
355
+ const opaqueGroups = new Set();
356
+ const opaqueDecl = /\b(?:var|[A-Za-z_][A-Za-z0-9_<>,. ]*?)\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*[^;]*?\.\s*MapGroup\s*\(\s*(?!")/g;
357
+ for (let match = opaqueDecl.exec(source); match !== null; match = opaqueDecl.exec(source)) {
358
+ if (!groups.has(match[1]))
359
+ opaqueGroups.add(match[1]);
360
+ }
361
+ // Minimal APIs. `app.MapGet("/orders/{id}", handler)` — the verb is the
362
+ // method name and the path is the first argument, **only when literal**.
363
+ const minimal = /([A-Za-z_][A-Za-z0-9_]*)\s*\.\s*(Map(?:Get|Post|Put|Patch|Delete))\s*\(\s*([^,)]*)/g;
364
+ for (let match = minimal.exec(source); match !== null; match = minimal.exec(source)) {
365
+ const receiver = match[1];
366
+ const verb = MINIMAL_VERBS.get(match[2]);
367
+ const literal = /^\s*"([^"]*)"/.exec(match[3] ?? "");
368
+ if (literal === null) {
369
+ // A path from a constant or a concatenation. DEC-128 refuses it: a path
370
+ // this cannot read is not one it may invent.
371
+ refusedNonLiteral += 1;
372
+ continue;
373
+ }
374
+ if (opaqueGroups.has(receiver)) {
375
+ // The group exists and its prefix is unreadable. Emitting the suffix
376
+ // alone would publish a path no route serves.
377
+ minimalRefusedUnknownGroup += 1;
378
+ continue;
379
+ }
380
+ const written = join(groups.get(receiver) ?? "", literal[1]);
381
+ routes.push({
382
+ method: verb,
383
+ template: normaliseTemplate(written),
384
+ written,
385
+ line: source.slice(0, match.index).split("\n").length,
386
+ file: parsed.file,
387
+ handlerPath: [],
388
+ mechanism: "minimal",
389
+ framework: "aspnetcore",
390
+ });
391
+ }
392
+ }
393
+ return {
394
+ routes,
395
+ admittedDirectly,
396
+ admittedViaBaseChain,
397
+ ambiguousBaseNames: [...ambiguousBaseNames].sort(),
398
+ baseChainsLeftAnalysedSet,
399
+ unresolvedBases: [...unresolvedBases]
400
+ .map(([name, count]) => ({ name, count }))
401
+ .sort((a, b) => b.count - a.count || a.name.localeCompare(b.name)),
402
+ unresolvedBaseSites,
403
+ refusedNonLiteral,
404
+ tokensExpanded,
405
+ tokensUnknown,
406
+ mapGroupsSeen,
407
+ minimalRefusedUnknownGroup,
408
+ };
409
+ }
410
+ //# sourceMappingURL=aspnetcore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aspnetcore.js","sourceRoot":"","sources":["../src/aspnetcore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,123 @@
1
+ /**
2
+ * D-FIX-3's client-base extractor — DEC-164's contract, and the first adapter
3
+ * in this project to build it. `client.ts` already reads a call's own literal
4
+ * path; this is the separate, larger row that row's own header named as
5
+ * unaddressed: what the receiver's *base* is, so a bare-relative call
6
+ * (`client.GetAsync("Users/Me")`, no leading slash) composes against
7
+ * something real instead of being refused for want of one.
8
+ *
9
+ * ## Two shapes built, and why not more
10
+ *
11
+ * §7's original audit named `services.AddHttpClient<T>(c => c.BaseAddress =
12
+ * new Uri(...))` as *the* pattern to build, on the strength of "jellyfin and
13
+ * eShopOnWeb both confirmed to use exactly this shape." Read against the real
14
+ * source, that premise does not hold for either named repository: jellyfin's
15
+ * own `AddHttpClient(NamedClient.Default, ...)` registrations
16
+ * (`Jellyfin.Server/Startup.cs`) never set `BaseAddress` at all, and
17
+ * eShopOnWeb has no `AddHttpClient<T>(...)` call anywhere.
18
+ *
19
+ * **What every one of both corpora's real `USES_API` edges (and several
20
+ * previously-silent call sites) actually goes through** is
21
+ * `WebApplicationFactory<TEntryPoint>.CreateClient(...)` — ASP.NET's own
22
+ * integration-test client, whose documented default `BaseAddress` is the
23
+ * application under test itself. `derivesFromWebApplicationFactory` (in
24
+ * `extract.ts`, where the type graph lives) is the whole mechanism: does the
25
+ * receiver's own class (directly, or through one in-repo indirection —
26
+ * `CustomWebApplicationFactory<TProgram> : WebApplicationFactory<TProgram>`
27
+ * is `CleanArchitecture`'s own shape) carry `WebApplicationFactory<` in its
28
+ * written base list. Measured: **100% of jellyfin's and eShopOnWeb's existing
29
+ * 27 `USES_API` edges, plus every previously-silent or previously-refused
30
+ * bare-relative call this row newly reaches, go through exactly this
31
+ * receiver.**
32
+ *
33
+ * The named `AddHttpClient<T>(...)` shape is real, just not in the two
34
+ * repositories the audit cited — `dotnet/eShop` has it
35
+ * (`WebApp/Extensions.cs`, `WebhookClient/Extensions.cs`,
36
+ * `HybridApp/MauiProgram.cs`, 4 sites), and every one of those typed clients'
37
+ * own call sites builds its path by string interpolation
38
+ * (`$"{remoteServiceBaseUrl}items/{id}"`), already and separately refused by
39
+ * `client.ts`'s "not a bare string literal" rule — so resolving this base
40
+ * moves **zero** measured edges in any corpus this row has on disk.
41
+ * {@link typedClientBases} below reads it anyway, narrowly (a literal string
42
+ * argument to `new Uri(...)`/`new(...)` only — a field, a config read, or any
43
+ * other expression stays `unresolved`, never guessed): the mechanism is real
44
+ * corpus evidence, not invented for a fixture, and golden pattern 08a needs a
45
+ * *non-empty* composed path to prove `DEC-097`'s composition is real rather
46
+ * than coincidentally correct when the prefix is always empty. No other DI
47
+ * mechanism — `IHttpClientFactory.CreateClient(name)`'s named-client form
48
+ * included — is read here; every such call was invisible before this row and
49
+ * stays exactly as invisible after it, a disclosed, unaffected limit rather
50
+ * than a claim this file makes and cannot back.
51
+ *
52
+ * ## The state built but never produced by real corpus evidence
53
+ *
54
+ * DEC-164 names four states; three are real here. `unresolved` is reachable
55
+ * in code (a `AddHttpClient<T>` registration whose `BaseAddress` is a field
56
+ * or a config read rather than a literal) but has zero witnesses in any of
57
+ * the six corpora this row measured — kept rather than deleted, the same way
58
+ * `efcore.ts`'s rival-ORM exclusion rule stays written down at zero
59
+ * witnesses: a real rule with no corpus evidence yet, not a guess.
60
+ */
61
+ /**
62
+ * DEC-164's four-state contract, minus the fourth — the field's own absence
63
+ * from `attrs`, which is a fact about the *adapter*, not a value this type
64
+ * can hold.
65
+ */
66
+ export type ClientBaseResult = {
67
+ /**
68
+ * A real base was traced to its receiver's own construction. DEC-097:
69
+ * only the origin is stripped, so `path` is empty for every
70
+ * witness this row found — `WebApplicationFactory<T>.CreateClient()`'s
71
+ * default `BaseAddress` is `http://localhost/`, origin only, no path —
72
+ * but the field exists for the DI-registration case
73
+ * (`AddHttpClient<T>(o => o.BaseAddress = new("http://host/api/v1"))`)
74
+ * that would carry one, the day a corpus exercises it.
75
+ */
76
+ readonly state: "resolved";
77
+ readonly path: string;
78
+ } | {
79
+ /**
80
+ * No base-setting mechanism this reader knows was found for this
81
+ * receiver. Not a claim that no base exists — a constructor parameter
82
+ * of type `HttpClient` with no traced origin is exactly this case, and
83
+ * a real base may be configured somewhere this reader does not reach.
84
+ * The call's own literal path is trusted as complete, unchanged from
85
+ * every C# `USES_API` edge minted before this row existed.
86
+ */
87
+ readonly state: "none";
88
+ } | {
89
+ /** A base-setting mechanism was found; its value is not a literal this reader can evaluate. */
90
+ readonly state: "unresolved";
91
+ readonly why: string;
92
+ };
93
+ /**
94
+ * `services.AddHttpClient<TClient>(o => o.BaseAddress = <expr>)` —
95
+ * `dotnet/eShop`'s own shape (`WebApp/Extensions.cs`,
96
+ * `WebhookClient/Extensions.cs`, `HybridApp/MauiProgram.cs`), read textually
97
+ * rather than through the AST (the same division of labour `aspnetcore.ts`
98
+ * and `efcore.ts` already keep for a fluent registration chain — see
99
+ * `extract.ts`'s own header for why). Keyed by `TClient`'s own written name
100
+ * (short, unqualified — matched the same way against the enclosing type's own
101
+ * simple name, a disclosed limit rather than a full type resolution this
102
+ * textual pass cannot do): DI hands the configured client straight to
103
+ * `TClient`'s own constructor, so a call inside `TClient` whose receiver
104
+ * resolves to a bare, otherwise-untraced `HttpClient` is exactly this base.
105
+ *
106
+ * The RHS is matched as `new(...)`/`new Uri(...)`'s own single argument —
107
+ * not "everything up to the next `;`", which real `eShop` source would get
108
+ * wrong: `AddHttpClient<CatalogService>(o => o.BaseAddress = new(...))` is
109
+ * followed by a fluent chain (`.AddApiVersion(2.0).AddAuthToken();`) whose
110
+ * terminating `;` is lines away and whose own parentheses would otherwise be
111
+ * swallowed into the "expression." Real corpus shapes have no nested parens
112
+ * inside the argument itself (a string literal, or a bare field reference),
113
+ * so matching up to the first `)` is exact for both, not an approximation.
114
+ * A bare string literal (`new Uri("https+http://catalog-api")`,
115
+ * `new("http://webhooks-api")`) resolves; anything else (`new(MobileBffHost)`
116
+ * — a static field, `eShop`'s own `HybridApp/MauiProgram.cs`) is `unresolved`
117
+ * with the argument's own text as `why`. Both are measured, not invented for
118
+ * this file.
119
+ */
120
+ export declare function typedClientBases(files: readonly {
121
+ readonly source: string;
122
+ }[]): Map<string, ClientBaseResult>;
123
+ //# sourceMappingURL=client-base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-base.d.ts","sourceRoot":"","sources":["../src/client-base.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AAEH;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GACxB;IACE;;;;;;;;OAQG;IACH,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,GACD;IACE;;;;;;;OAOG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,GACD;IACE,+FAA+F;IAC/F,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB,CAAC;AAeN;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAqB7G"}