@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/client.js ADDED
@@ -0,0 +1,387 @@
1
+ /**
2
+ * D-FIX-3's C# half: a call through a known HTTP client is disclosed, never
3
+ * silent — even though this adapter extracts no `USES_API` edges in C# at
4
+ * all yet.
5
+ *
6
+ * ## Why disclosure without extraction is the right first producer here
7
+ *
8
+ * Java's `client.ts` extracts real edges for Spring's clients and files a
9
+ * ledger row for the shapes it cannot read. C# has no extraction path at
10
+ * all — `capabilities()` already declares no `USES_API` for this adapter
11
+ * (PROGRESS.md), so *every* call through a known client is unclaimed today.
12
+ * Building the extractor itself (reading `GetAsync("/orders/{id}")`'s literal
13
+ * path, the way `bench/client-call-census.mjs`'s C# census shows is
14
+ * structurally the same shape as Spring's `RestTemplate` — the URL is the
15
+ * call's own first argument, not a separately built request object) is a
16
+ * **separate, larger row**: it needs the DEC-129 counting-first admission
17
+ * this project requires before an extractor ships, a golden fixture change,
18
+ * and a capability declaration tested against pattern 08 the way DEC-195
19
+ * records for Java. That is not built here.
20
+ *
21
+ * What *is* in scope, and small: recognise the receiver's declared type and
22
+ * turn "nothing" into a disclosed refusal, so `0 USES_API edges` stops being
23
+ * indistinguishable from "no HTTP calls in this codebase" — the exact
24
+ * distinction DEC-113's ledger exists to carry, and the reason a `cf-worker`
25
+ * zero and a `sherpa-frontend` zero are different facts.
26
+ *
27
+ * ## The bounded population, measured before it was picked
28
+ *
29
+ * `bench/client-call-census.mjs csharp` names `HttpClient`, `IHttpClientFactory`,
30
+ * `HttpRequestMessage` and `RestClient` as candidates. Measured directly against
31
+ * this row: `RestClient` (RestSharp's type) has **zero** occurrences across all
32
+ * six C# reference corpora (`jellyfin`, `eShop`, `eShopOnWeb`, `Ocelot`,
33
+ * `CleanArchitecture`, `Dapper`) — counted, not assumed, the same way DEC-129
34
+ * counted before admitting a corpus. `IHttpClientFactory` produces an
35
+ * `HttpClient` from `CreateClient()`; the client census's 61 relative-path call
36
+ * sites are all through `HttpClient` variables and fields once the factory call
37
+ * has already returned. So the known-type set here is **just `HttpClient`** —
38
+ * narrower than the census's candidate list, on a measurement rather than a
39
+ * guess, and it can widen the day a corpus exercises `RestClient` for real.
40
+ *
41
+ * `HttpRequestMessage` is not a receiver in any of the census's call sites — it
42
+ * is the *argument* to `SendAsync`, never the thing a verb method is called on
43
+ * — so it does not belong in a receiver-type set at all; including it would
44
+ * have been the coarser-than-node-key mistake DEC-165/187 already paid for once.
45
+ *
46
+ * ## Origin check, added after DEC-260
47
+ *
48
+ * The first pass of this file filed every candidate as "no extractor," with no
49
+ * distinction between a call whose literal path stays inside this repository
50
+ * and one that names a different origin outright — a divergence from Java's
51
+ * `client.ts`, which has always told the two apart. DEC-260 §3 names the
52
+ * consequence directly: two adapters disagreeing on the same refusal shape
53
+ * corrupts `refusalClass` (DEC-242) the moment a consumer trusts it, because
54
+ * C# would systematically over-report `capability-gap` against Java's
55
+ * classifier on the same underlying population.
56
+ *
57
+ * **Not Java's exact predicate, on purpose — measured, not assumed.** A first
58
+ * attempt reused Java's "does not start with `/`" rule verbatim and ran it
59
+ * against the real admitted corpora before calling the row done, per this
60
+ * project's own discipline. It misfired: `jellyfin` and `eShopOnWeb` both pass
61
+ * a **bare relative path with no leading slash** (`"Users/New"`) to `HttpClient`
62
+ * calls resolved against `HttpClient.BaseAddress` at run time — a shape Java's
63
+ * own population never contains, so Java's rule was never tested against it.
64
+ * Every such call site found is an integration test through
65
+ * `_factory.CreateClient()`, ASP.NET's in-process test client pointed at the
66
+ * application under test — a same-repository call this reader was about to
67
+ * mislabel as "another origin." See {@link namesAnotherOrigin}'s own comment
68
+ * for the corrected, narrower rule. This origin check was later carried back
69
+ * into `adapter-java`'s own `relativePath` rule, which carried the identical
70
+ * latent defect (`adapter-java` `1bfd2ae`) — the two adapters now agree on
71
+ * this shape by construction, not by two independently-tuned rules.
72
+ *
73
+ * ## Extraction, built on the shape the origin check already separated out
74
+ *
75
+ * `DEC-241` §4 named this row: `HttpClient`'s verb methods take the path as
76
+ * their **own first argument** — `GetAsync("/orders/{id}")`, never a
77
+ * separately built request object the way `OkHttpClient`/`java.net.http.HttpClient`
78
+ * require — the same structurally-straightforward shape Java's `RestTemplate`
79
+ * already extracts from. `clientCalls` below mints a real `USES_API` edge for
80
+ * every call whose receiver is a known client type, whose method names a
81
+ * literal verb, and whose path is a repository-relative literal
82
+ * (`relativePath`, unchanged from the origin check's own reasoning) — and
83
+ * files a ledger row, never silently drops the call, for every other shape:
84
+ * an unrecognised verb (`SendAsync`, which takes a pre-built
85
+ * `HttpRequestMessage` — verb and path both live inside an object this reader
86
+ * does not open), a non-literal path, an origin outside this repository, or a
87
+ * bare relative path this reader genuinely cannot place (the same
88
+ * `BaseAddress`-resolved shape the origin check exists to disclose rather
89
+ * than guess).
90
+ */
91
+ import { endpointQsp, nodeId } from "@descryy/ir";
92
+ const CAPABILITY_GAP = { blockedBy: null, refusalClass: "capability-gap" };
93
+ const VARIES_PER_CALL = { blockedBy: null, refusalClass: "varies-per-call" };
94
+ /** `Environment.GetEnvironmentVariable("X")` and an `IConfiguration` indexer named `configuration`/`Configuration`. */
95
+ const ENV_READ_PATTERNS = [
96
+ /^Environment\.GetEnvironmentVariable\(\s*"[^"]*"\s*\)$/,
97
+ /^\w*[Cc]onfiguration\w*\[\s*"[^"]*"\s*\]$/,
98
+ ];
99
+ /** Same heuristic as every other adapter's — see `adapter-rust/src/client.ts`'s doc. */
100
+ function argumentKindOf(text) {
101
+ if (text.endsWith(")") && text.includes("("))
102
+ return "call";
103
+ if (text.includes("+"))
104
+ return "concatenation";
105
+ if (/^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(text))
106
+ return "field";
107
+ return "other";
108
+ }
109
+ /**
110
+ * Why the first argument of a client call could not be read as a literal
111
+ * path — mirrors `firstArgumentTextOf`'s own reach: a bare identifier, a
112
+ * known environment/configuration-read expression, or something this reader
113
+ * does not trace.
114
+ */
115
+ function diagnoseArgument(text, parameterNames) {
116
+ if (text === undefined)
117
+ return CAPABILITY_GAP;
118
+ const trimmed = text.trim();
119
+ if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(trimmed) && parameterNames.has(trimmed))
120
+ return VARIES_PER_CALL;
121
+ if (ENV_READ_PATTERNS.some((pattern) => pattern.test(trimmed))) {
122
+ return { blockedBy: trimmed, refusalClass: "value-unknown" };
123
+ }
124
+ return { ...CAPABILITY_GAP, argumentKind: argumentKindOf(trimmed) };
125
+ }
126
+ /** Verb methods on `HttpClient`. Taken from `bench/client-call-census.mjs`'s `METHODS.csharp`. */
127
+ const CLIENT_METHODS = new Set([
128
+ "GetAsync",
129
+ "PostAsync",
130
+ "PutAsync",
131
+ "DeleteAsync",
132
+ "PatchAsync",
133
+ "SendAsync",
134
+ "GetStringAsync",
135
+ "GetByteArrayAsync",
136
+ "GetStreamAsync",
137
+ "GetFromJsonAsync",
138
+ "PostAsJsonAsync",
139
+ "PutAsJsonAsync",
140
+ "DeleteFromJsonAsync",
141
+ ]);
142
+ const KNOWN_CLIENT_TYPES = new Set(["HttpClient"]);
143
+ /**
144
+ * The HTTP verb a method name implies.
145
+ *
146
+ * `SendAsync` takes a pre-built `HttpRequestMessage` — the verb (and the
147
+ * path) live inside that object, not in this call's own arguments — so it is
148
+ * absent here and refused rather than defaulted, the identical reasoning
149
+ * Java's `exchange`/`execute`/`uri` carry: a wrong verb mints an endpoint
150
+ * that joins the wrong route, or none, and root-cause traversal would follow
151
+ * it.
152
+ */
153
+ const VERB = {
154
+ GetAsync: "GET",
155
+ GetStringAsync: "GET",
156
+ GetByteArrayAsync: "GET",
157
+ GetStreamAsync: "GET",
158
+ GetFromJsonAsync: "GET",
159
+ PostAsync: "POST",
160
+ PostAsJsonAsync: "POST",
161
+ PutAsync: "PUT",
162
+ PutAsJsonAsync: "PUT",
163
+ DeleteAsync: "DELETE",
164
+ DeleteFromJsonAsync: "DELETE",
165
+ PatchAsync: "PATCH",
166
+ };
167
+ /**
168
+ * A path this reader will claim: repository-relative, no scheme, no assembly.
169
+ * Unchanged from the origin check's own reasoning — a leading `/` and nothing
170
+ * ambiguous about it, query string dropped because `SERVES_API` never mints
171
+ * one and keeping it would guarantee the join fails.
172
+ */
173
+ function relativePath(value) {
174
+ if (!value.startsWith("/"))
175
+ return undefined;
176
+ if (value.startsWith("//"))
177
+ return undefined;
178
+ const cut = value.indexOf("?");
179
+ return cut === -1 ? value : value.slice(0, cut);
180
+ }
181
+ /**
182
+ * A literal argument this reader can say, with no ambiguity, names a
183
+ * *different* origin — an explicit scheme (`http://...`) or a protocol-relative
184
+ * URL (`//host/...`). Not Java's `relativePath` predicate, and deliberately
185
+ * not the same rule even though the two must agree on `refusalClass` (DEC-242)
186
+ * for the shapes they both actually see.
187
+ *
188
+ * Java's population (measured, DEC-260 §1) is `RestTemplate`/`TestRestTemplate`
189
+ * calls, which are either a leading-`/` repository-relative path or a fully
190
+ * qualified URL — nothing in between, so "does not start with `/`" was a safe
191
+ * proxy for "names another origin" *there*. C#'s real corpora are not the same
192
+ * population: `HttpClient` calls routinely pass a **bare relative path with no
193
+ * leading slash** (`"Users/New"`, `"SyncPlay/List"`) resolved against
194
+ * `HttpClient.BaseAddress` at run time — measured directly against jellyfin and
195
+ * eShopOnWeb, where every bare-relative call site found is an integration test
196
+ * driving `_factory.CreateClient()`, ASP.NET's own in-process test client
197
+ * whose `BaseAddress` is the application under test. Porting Java's predicate
198
+ * verbatim filed those as "another origin," which is not a fact this reader
199
+ * has — `BaseAddress` is a runtime value this reader does not see, so a bare
200
+ * relative path is genuinely undetermined, not a confirmed scope boundary.
201
+ * Rule 2 (precision over recall): when in doubt, the row stays in the
202
+ * capability-gap bucket rather than asserting a boundary this reader cannot
203
+ * back up. Only an unambiguous absolute form earns `out-of-scope`.
204
+ */
205
+ function namesAnotherOrigin(value) {
206
+ return /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(value) || value.startsWith("//");
207
+ }
208
+ /**
209
+ * Every reference {@link clientCalls} will act on — an unbounded receiver
210
+ * type still makes it a true silence past this point, exactly as in Java. See
211
+ * `client.test.ts` for the reconciliation this makes checkable: every
212
+ * candidate whose type resolves lands in either `calls` or `refusals`, with
213
+ * nothing else.
214
+ */
215
+ export function candidateClientRefs(refs) {
216
+ return refs.filter((ref) => ref.kind === "call" &&
217
+ CLIENT_METHODS.has(ref.name) &&
218
+ ref.receiver !== undefined &&
219
+ ref.receiver !== "this" &&
220
+ ref.receiver !== "base");
221
+ }
222
+ /** The literal path's own query string dropped — `SERVES_API` never mints one either. */
223
+ function stripQuery(value) {
224
+ const cut = value.indexOf("?");
225
+ return cut === -1 ? value : value.slice(0, cut);
226
+ }
227
+ /**
228
+ * DEC-097's composition, with the separator neither half is guaranteed to
229
+ * supply. `path` (a resolved base's own prefix) never ends in `/`
230
+ * (`pathPrefixOf` in `client-base.ts` strips it) and a call's own literal
231
+ * may or may not start with one (`"orders/{id}"` vs `"/order/my-orders"`),
232
+ * so joining with a bare `+` silently drops the boundary exactly once: `"/api/v1"
233
+ * + "orders/{id}"` is `"/api/v1orders/{id}"`, one wrong path, not a missing
234
+ * slash `normaliseEndpointPath` can recover — it repairs a *doubled* slash,
235
+ * never a *missing* one. Always inserting `/` and letting `normaliseEndpointPath`'s
236
+ * own `/+` collapse handle the doubled case when the suffix already had one
237
+ * is the one join that is correct for both shapes.
238
+ */
239
+ function joinBase(path, suffix) {
240
+ return `${path}/${suffix}`;
241
+ }
242
+ /**
243
+ * Read one method's calls for outbound HTTP.
244
+ *
245
+ * `typeOfReceiver` is supplied by `extract.ts`, which already has the scope
246
+ * tables (`fn.scope.locals`, the enclosing type's members) a second copy of
247
+ * here would drift from — the same division of labour as Java's `client.ts`.
248
+ *
249
+ * `clientBaseOfReceiver` is DEC-164's contract (`client-base.ts`'s own
250
+ * header): a per-call determination of what base, if any, this reader traced
251
+ * for the receiver. Called only for refs that already passed the known-client-
252
+ * type gate below, matching `typeOfReceiver`'s own division of labour.
253
+ */
254
+ export function clientCalls(refs, fromId, typeOfReceiver, isTest, parameterNames, clientBaseOfReceiver) {
255
+ const calls = [];
256
+ const refusals = [];
257
+ for (const ref of candidateClientRefs(refs)) {
258
+ const receiverType = typeOfReceiver(ref);
259
+ if (receiverType === undefined || !KNOWN_CLIENT_TYPES.has(receiverType))
260
+ continue;
261
+ const refuse = (reason, diagnosis) => {
262
+ refusals.push({ fromId, raw: ref.raw, line: ref.line, reason, ...diagnosis });
263
+ };
264
+ const verb = VERB[ref.name];
265
+ if (verb === undefined) {
266
+ refuse(`an HTTP call through ${receiverType}, but \`${ref.name}\` takes a pre-built ` +
267
+ `HttpRequestMessage — the verb and the path both live inside that object, not in this ` +
268
+ `call's own arguments. Refused rather than defaulted to GET: a wrong verb mints an ` +
269
+ `endpoint that joins the wrong route, or none, and root-cause traversal would follow it.`, CAPABILITY_GAP);
270
+ continue;
271
+ }
272
+ if (ref.firstStringArgument === undefined) {
273
+ refuse(`an HTTP call through ${receiverType}, but its path is not a bare string literal — a ` +
274
+ `variable, a concatenation, or an interpolated string. A path assembled at run time is ` +
275
+ `not one this adapter can read, and an endpoint template invented here would join this ` +
276
+ `caller to a route it never calls.`, diagnoseArgument(ref.firstArgumentText, parameterNames));
277
+ continue;
278
+ }
279
+ // A literal argument unambiguously naming a different origin is a
280
+ // resolved fact, not a gap — DEC-113's out-of-scope population, the same
281
+ // one Java's client.ts already separates out for the shapes the two
282
+ // adapters' real corpora actually share (an absolute URL to another
283
+ // host). Checked before `clientBase` — an absolute request URI overrides
284
+ // `HttpClient.BaseAddress` regardless of what this reader traced for it.
285
+ if (namesAnotherOrigin(ref.firstStringArgument)) {
286
+ refuse(`an HTTP call through ${receiverType} to \`${ref.firstStringArgument}\`, which names ` +
287
+ `another origin rather than a path in this repository. A scope boundary, not a gap.`, { blockedBy: null, refusalClass: "out-of-scope" });
288
+ continue;
289
+ }
290
+ const base = clientBaseOfReceiver(ref);
291
+ if (base.state === "unresolved") {
292
+ // DEC-164: never guess. A base-setting mechanism was found and its
293
+ // value could not be read — zero witnesses for this branch in the six
294
+ // corpora this row measured (see `client-base.ts`'s own header), kept
295
+ // rather than deleted for the day one exists.
296
+ refusals.push({
297
+ fromId,
298
+ raw: ref.raw,
299
+ line: ref.line,
300
+ reason: `an HTTP call through ${receiverType}, whose base this reader traced to a DI ` +
301
+ `registration but could not read as a literal: ${base.why}. DEC-164 refuses rather ` +
302
+ `than guesses what an unreadable base composes to.`,
303
+ blockedBy: base.why,
304
+ refusalClass: "capability-gap",
305
+ clientBase: base,
306
+ });
307
+ continue;
308
+ }
309
+ if (base.state === "resolved") {
310
+ // DEC-097: only the origin was ever stripped from a resolved base: the
311
+ // call's own literal path — leading slash or not, the base is real and
312
+ // traced — composes directly against `path`.
313
+ calls.push({
314
+ fromId,
315
+ method: ref.name,
316
+ template: joinBase(base.path, stripQuery(ref.firstStringArgument)),
317
+ verb,
318
+ line: ref.line,
319
+ callerKind: isTest ? "test" : "production",
320
+ clientBase: base,
321
+ });
322
+ continue;
323
+ }
324
+ // `base.state === "none"` — no traced mechanism. Unchanged from every C#
325
+ // `USES_API` edge minted before this row: a leading-slash literal is
326
+ // trusted as complete on its own; a bare relative path is not, because
327
+ // this reader cannot tell whether an untraced `BaseAddress` elsewhere
328
+ // would give it a different meaning.
329
+ const template = relativePath(ref.firstStringArgument);
330
+ if (template === undefined) {
331
+ // A bare relative path with no leading slash and no traced base —
332
+ // structurally identical to Java's own `restTemplate.getForObject("orders/5",
333
+ // ...)` shape once `adapter-java` `1bfd2ae` closed the same latent
334
+ // defect there. It may resolve inside this repository, against a
335
+ // `BaseAddress` configured elsewhere this reader does not trace, or it
336
+ // may not; neither an edge nor an out-of-scope claim is honest here —
337
+ // rule 2, applied to a classification rather than an edge. Not a
338
+ // per-call value and not a named blocking expression either, so
339
+ // `capability-gap` rather than `varies-per-call`.
340
+ refusals.push({
341
+ fromId,
342
+ raw: ref.raw,
343
+ line: ref.line,
344
+ reason: `an HTTP call through ${receiverType} to \`${ref.firstStringArgument}\`, a relative path ` +
345
+ `with no leading slash and no base this reader traced (DEC-164: \`clientBase.state === ` +
346
+ `"none"\`). This reader cannot tell whether it resolves inside this repository — against ` +
347
+ `a configured BaseAddress this call site does not state — or elsewhere, so neither an ` +
348
+ `endpoint nor a scope boundary is claimed.`,
349
+ ...CAPABILITY_GAP,
350
+ clientBase: base,
351
+ });
352
+ continue;
353
+ }
354
+ calls.push({
355
+ fromId,
356
+ method: ref.name,
357
+ template,
358
+ verb,
359
+ line: ref.line,
360
+ callerKind: isTest ? "test" : "production",
361
+ clientBase: base,
362
+ });
363
+ }
364
+ return { calls, refusals };
365
+ }
366
+ /**
367
+ * The `API_ENDPOINT` a call joins to — minted identically to the route side.
368
+ *
369
+ * Shares `endpointQsp` with `extract.ts`'s `SERVES_API` block rather than
370
+ * recomputing the id, because the two must agree exactly and the failure when
371
+ * they do not is silent.
372
+ */
373
+ export function endpointFor(scope, call, producedBy, normalise) {
374
+ const template = normalise(call.template);
375
+ return {
376
+ id: nodeId(scope, "API_ENDPOINT", endpointQsp(call.verb, call.template), null),
377
+ type: "API_ENDPOINT",
378
+ name: `${call.verb} ${template}`,
379
+ file: null,
380
+ range: null,
381
+ language: null,
382
+ producedBy,
383
+ resolution: 2,
384
+ attrs: { method: call.verb, pathTemplate: template },
385
+ };
386
+ }
387
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,198 @@
1
+ /**
2
+ * The ORM framework extractor for C# — Entity Framework Core.
3
+ *
4
+ * **A `MODEL` is a framework claim, and this file is where it is earned.**
5
+ * DEC-128 fixed the rules before extraction and **DEC-180 amended them after
6
+ * counting the corpus**, which is the part worth reading first: four of
7
+ * DEC-128's ORM rules were wrong, and every one of them was wrong in the same
8
+ * way — written from how EF Core is documented rather than from how the corpus
9
+ * is spelled. That is how a rule acquires zero witnesses.
10
+ *
11
+ * ## Admission — what the corpus actually contains
12
+ *
13
+ * | evidence | jellyfin | Ocelot | eShopOnWeb | CleanArchitecture |
14
+ * | --- | --- | --- | --- | --- |
15
+ * | `DbSet<` | 71 | 0 | 7 | 4 |
16
+ * | `IEntityTypeConfiguration<` | 26 | 0 | 7 | 2 |
17
+ * | `modelBuilder.Entity<` | **0** | **0** | **0** | **0** |
18
+ * | `[Table(` | **0** | **0** | **0** | **0** |
19
+ *
20
+ * So the admission grounds are:
21
+ *
22
+ * 1. **`DbSet<T>` on a `DbContext`.** Primary, and the only rule with witnesses
23
+ * in three of four corpora.
24
+ * 2. **`IEntityTypeConfiguration<T>`.** This *replaces* DEC-128's
25
+ * `modelBuilder.Entity<T>()` rule, which is the same fact written in a form
26
+ * the corpus does not contain.
27
+ * 3. `[Table]` and `modelBuilder.Entity<T>()` are still **read** where they
28
+ * appear, and are reported as **unwitnessed** — `0 of 0`, undefined, never as
29
+ * passing. A rule with zero witnesses cannot be shown to work or to fail, and
30
+ * its green is the green of a branch never taken.
31
+ *
32
+ * **Refused:** a type in a `Models` or `Entities` namespace with no mapping
33
+ * evidence — the `IFoo` guess this project forbids — and an owned type reached
34
+ * through `OwnsOne`, which shares its owner's table and has none of its own.
35
+ *
36
+ * ## Generated migrations are excluded, and the exclusion is counted
37
+ *
38
+ * DEC-128 read the table name from `ToTable(…)`. Measured: **1,338 of
39
+ * `jellyfin`'s 1,339 `ToTable` calls are inside `Migrations/`**, under a
40
+ * `// <auto-generated />` header, and 50 of `eShopOnWeb`'s 51. Those files are
41
+ * EF Core's serialised model from whenever someone last scaffolded a migration —
42
+ * an artefact of an operation, read as evidence about the world.
43
+ *
44
+ * An extractor reading them without excluding them is, to three significant
45
+ * figures, reading **only** generated code. So they are excluded — and the count
46
+ * is **reported**, because a silent exclusion of 1,338 of 1,339 is a correct
47
+ * decision that looks identical to a reader that never found them.
48
+ *
49
+ * ## No corpus can exercise the provenance rule
50
+ *
51
+ * `Dapper`, `NHibernate` and `linq2db` occur **zero** times across all four
52
+ * corpora. There is no C# repository here in which a rival ORM's types are
53
+ * present for these rules to correctly refuse — the identical hole the Java
54
+ * control set has. Stated before extraction rather than discovered after a green
55
+ * run, and every report carries the EF negative control as `0 of 0 — undefined`.
56
+ */
57
+ import type { CsFile } from "./parse.ts";
58
+ import { type NullableContext } from "./nullability.ts";
59
+ /** Mirrors `JpaField` and `OrmField` so one shape crosses three languages. */
60
+ export interface EfColumn {
61
+ readonly name: string;
62
+ /** `undefined` when nothing this reader can evaluate decides it. */
63
+ readonly nullable: boolean | undefined;
64
+ readonly nullableFrom: "fluent" | "annotation" | "value-type" | "nullable-value-type" | "reference-type" | "unresolved";
65
+ }
66
+ export interface EfModel {
67
+ readonly typeName: string;
68
+ /**
69
+ * Namespace-qualified, because the simple name is not what identifies a type.
70
+ *
71
+ * The consumer decides `MODEL` versus `CLASS` from this map, and it used to
72
+ * decide on `typeName`. While every simple-name collision was refused here
73
+ * that was invisible; the moment collisions began resolving, **every**
74
+ * declaration sharing a resolved name became a `MODEL` — `eShopOnWeb` emitted
75
+ * a second `Basket` for its Razor `ViewComponent` and a second `CatalogItem`
76
+ * for its Blazor DTO, each with the entity's columns attached to the wrong
77
+ * type.
78
+ *
79
+ * So a conservative refusal in this file was holding a defect in another one
80
+ * shut. Widening the refusal was correct and it was also the thing that fired
81
+ * the second bug — which is the argument for re-running precision after a
82
+ * widening rather than after a release.
83
+ */
84
+ readonly fqn: string;
85
+ readonly path: readonly string[];
86
+ /** `null` when only a convention this reader does not compute would give it. */
87
+ readonly table: string | null;
88
+ readonly admittedBy: "dbset" | "configuration" | "table-attribute" | "model-builder";
89
+ readonly columns: readonly EfColumn[];
90
+ readonly line: number;
91
+ readonly file: string;
92
+ }
93
+ /** What was read and what was refused, so a silent exclusion cannot pass for a finding. */
94
+ export interface EfCensus {
95
+ readonly models: readonly EfModel[];
96
+ /** Files skipped as generated. See the file comment — reported, never silent. */
97
+ readonly generatedFilesSkipped: number;
98
+ /** `ToTable` calls inside those files, which is the 1,338-of-1,339 number. */
99
+ readonly generatedToTableSkipped: number;
100
+ /**
101
+ * Entities named by a mapping whose simple name is declared more than once.
102
+ *
103
+ * **Refused rather than guessed, and this is not hypothetical.** `eShopOnWeb`
104
+ * declares `Basket`, `CatalogBrand`, `CatalogItem` and `CatalogType` **twice**
105
+ * — once in `ApplicationCore/Entities` as the entity and once in
106
+ * `BlazorShared/Models` as the view DTO. A simple-name index with first-wins
107
+ * publishes the DTO's properties as the entity's columns: a wrong answer, and
108
+ * one that looks entirely healthy because a model *is* emitted with plausible
109
+ * columns.
110
+ *
111
+ * This is the mistake `adapter-java` records for `airbyte`, arriving in C#: a
112
+ * `Map<string, Declared>` keeps the last (or first) of several and resolves
113
+ * every reference to it. Two declarations is genuinely ambiguous and the
114
+ * `DbSet<T>` argument is rarely qualified, so there is nothing to break the tie
115
+ * with. Refused, and counted so the refusal is visible.
116
+ */
117
+ readonly ambiguousEntityNames: readonly string[];
118
+ /**
119
+ * Simple-name collisions, split by whether the namespace rule settled them.
120
+ *
121
+ * Reported because "eight refusals" and "eight refusals, all resolvable" are
122
+ * different disclosures and only the second is actionable.
123
+ */
124
+ readonly namespaceResolved: {
125
+ readonly resolved: number;
126
+ readonly unresolved: number;
127
+ };
128
+ /**
129
+ * Properties refused because their declared type is a class declared in this
130
+ * repository — a navigation, or an owned type. Not scalar columns.
131
+ *
132
+ * **The owned-type rule DEC-128 §2 states has no readable witness.**
133
+ * `eShopOnWeb`'s only `OwnsOne` calls are inside `Migrations/*.Designer.cs`,
134
+ * which are generated and correctly excluded, so `Address` and
135
+ * `CatalogItemOrdered` are never marked owned. Reading them anyway would have
136
+ * published `ShipToAddress` as one non-nullable column, when EF spreads an
137
+ * owned type across several columns of the owner's table under names this
138
+ * reader cannot compute.
139
+ *
140
+ * So the rule applied is broader and rests on the declaration rather than on
141
+ * the configuration: a property whose type is a repo-declared `class`,
142
+ * `record` or `interface` is not a scalar column. Enums and structs are, and
143
+ * stay.
144
+ */
145
+ readonly navigationsRefused: number;
146
+ /** Which rule *won* precedence for each admitted model. */
147
+ readonly admittedBy: Readonly<Record<EfModel["admittedBy"], number>>;
148
+ /**
149
+ * How many entities **each rule saw**, independent of which one won.
150
+ *
151
+ * Reported beside `admittedBy` because the two answer different questions and
152
+ * the first alone is misleading. `DbSet<T>` outranks `IEntityTypeConfiguration<T>`
153
+ * and names the same entities, so `admittedBy.configuration` is **0 on every
154
+ * corpus** — which reads as *the rule never fires* when it means *nothing
155
+ * needed it to*. A rule that fires and never wins is not an unwitnessed rule,
156
+ * and the distinction is exactly the one DEC-180 was written to protect.
157
+ */
158
+ readonly evidenceFor: Readonly<Record<EfModel["admittedBy"], number>>;
159
+ }
160
+ /**
161
+ * Does this file import EF Core at all? Provenance, not spelling.
162
+ *
163
+ * **A `global using` counts, and it is in force across the whole assembly.**
164
+ * That is not an edge case: `eShop`'s `OrderingContext` declares nine
165
+ * `DbSet<T>` and its only file-local `using` is `eShop.IntegrationEventLogEF` —
166
+ * `Microsoft.EntityFrameworkCore` arrives from `GlobalUsings.cs` three files
167
+ * away. Checking file-local usings alone read **zero** models from a corpus with
168
+ * nine, silently.
169
+ *
170
+ * `parse.ts` already says a `global using` is *"in force for every file in the
171
+ * project, not just this one"* and models the flag. This is the first consumer
172
+ * to act on it. Modern C# leans on it heavily — .NET 6 onward emits an implicit
173
+ * global-usings file per project — so a reader that ignores it fails on new
174
+ * codebases specifically.
175
+ *
176
+ * Scoped by **assembly**, because that is what a project is. A global using in
177
+ * one project does not reach another.
178
+ */
179
+ export declare function importsEfCore(file: CsFile, globalsByAssembly?: ReadonlyMap<string, boolean>): boolean;
180
+ /**
181
+ * Is this file generated?
182
+ *
183
+ * Two signals, and both are needed. The `// <auto-generated />` header is the
184
+ * language's own convention and is what EF Core's scaffolder emits. The
185
+ * `Migrations/` path catches the migration classes beside the snapshot, which
186
+ * carry no header of their own in every version.
187
+ */
188
+ export declare function isGenerated(path: string, source: string): boolean;
189
+ export interface EfInput {
190
+ readonly files: readonly {
191
+ readonly parsed: CsFile;
192
+ readonly source: string;
193
+ }[];
194
+ /** NRT context governing each file, resolved by the adapter. */
195
+ readonly contextOf: (file: string) => NullableContext;
196
+ }
197
+ export declare function efModels(input: EfInput): EfCensus;
198
+ //# sourceMappingURL=efcore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"efcore.d.ts","sourceRoot":"","sources":["../src/efcore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAsB,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAE/E,8EAA8E;AAC9E,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,YAAY,EACjB,QAAQ,GACR,YAAY,GACZ,YAAY,GACZ,qBAAqB,GACrB,gBAAgB,GAChB,YAAY,CAAC;CAClB;AAED,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,gFAAgF;IAChF,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,OAAO,GAAG,eAAe,GAAG,iBAAiB,GAAG,eAAe,CAAC;IACrF,QAAQ,CAAC,OAAO,EAAE,SAAS,QAAQ,EAAE,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,2FAA2F;AAC3F,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;IACpC,iFAAiF;IACjF,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,8EAA8E;IAC9E,QAAQ,CAAC,uBAAuB,EAAE,MAAM,CAAC;IACzC;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,oBAAoB,EAAE,SAAS,MAAM,EAAE,CAAC;IACjD;;;;;OAKG;IACH,QAAQ,CAAC,iBAAiB,EAAE;QAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IACvF;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,2DAA2D;IAC3D,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IACrE;;;;;;;;;OASG;IACH,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;CACvE;AAgCD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAGrG;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAKjE;AAoND,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAChF,gEAAgE;IAChE,QAAQ,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,eAAe,CAAC;CACvD;AAuDD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAqPjD"}