@descryy/adapter-python 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,75 @@
1
+ /**
2
+ * The Python `LanguageAdapter` — all seven methods.
3
+ *
4
+ * The parser is Python's own `ast`, reached through one subprocess per run
5
+ * (DEC-062). Everything language-specific stops at this package's boundary;
6
+ * `@descryy/ir` is the only engine dependency, which is what makes the IR
7
+ * boundary a dependency-direction fact rather than a convention.
8
+ */
9
+ import { type LanguageAdapter, type ResolutionLevel, type SkipReason } from "@descryy/ir";
10
+ import { LANGUAGE, type PyResult } from "./extract.ts";
11
+ export declare const ADAPTER_ID = "adapter-python";
12
+ export declare const ADAPTER_VERSION = "0.1.0";
13
+ export declare const DEFAULT_PYTHON_GLOBS: readonly ["**/*.py", "**/*.pyi"];
14
+ export interface PythonAdapterOptions {
15
+ readonly globs?: readonly string[];
16
+ readonly maxResolution?: ResolutionLevel;
17
+ /** Override the interpreter. Mostly for tests and for a pinned virtualenv. */
18
+ readonly python?: string;
19
+ }
20
+ /**
21
+ * Why the extractor is not read with `execFile`.
22
+ *
23
+ * `execFile` buffers the whole of stdout and fails past `maxBuffer` with a
24
+ * generic error. On home-assistant-core the extractor emits **284.4 MB**, which
25
+ * overflowed a 256 MB bound; the error was indistinguishable from "the
26
+ * interpreter failed", so `prepare()` degraded to R0 and the run reported
27
+ * **success with an empty graph over 18,189 files**. Two separate faults, and
28
+ * the second is the serious one — a capacity limit became a silent wrong answer.
29
+ *
30
+ * This reads the stream line by line instead. There is no buffer ceiling, no
31
+ * single giant string, and a stream that ends without its trailer is detected as
32
+ * truncation rather than mistaken for a short repository.
33
+ */
34
+ export type ExtractorFailure = {
35
+ readonly kind: "no-interpreter";
36
+ } | {
37
+ readonly kind: "spawn-failed";
38
+ readonly detail: string;
39
+ } | {
40
+ readonly kind: "nonzero-exit";
41
+ readonly code: number;
42
+ readonly stderr: string;
43
+ } | {
44
+ readonly kind: "truncated";
45
+ readonly linesRead: number;
46
+ } | {
47
+ readonly kind: "malformed";
48
+ readonly detail: string;
49
+ } | {
50
+ readonly kind: "timed-out";
51
+ readonly afterMs: number;
52
+ readonly linesRead: number;
53
+ };
54
+ export interface ExtractorOutcome {
55
+ readonly parsed?: PyResult;
56
+ readonly failure?: ExtractorFailure;
57
+ }
58
+ /** Human-readable, and deliberately explicit that the graph is incomplete. */
59
+ export declare function describeFailure(failure: ExtractorFailure): string;
60
+ /**
61
+ * `extract.py`'s per-file error strings have no machine-readable kind, only a
62
+ * prefix convention (`"unreadable: ..."` for an `OSError`, `"syntax error at
63
+ * line ..."` for a `SyntaxError`) — everything else (`ValueError`,
64
+ * `RecursionError`) falls to `"other"` rather than being guessed at.
65
+ */
66
+ export declare function skipReasonOf(message: string): SkipReason;
67
+ export interface PythonAdapter extends LanguageAdapter {
68
+ readonly id: string;
69
+ readonly version: string;
70
+ }
71
+ export declare function createPythonAdapter(options?: PythonAdapterOptions): PythonAdapter;
72
+ /** Content hash of a repo-relative file, for `FileRef`. */
73
+ export declare function hashOf(root: string, relative: string): string;
74
+ export { LANGUAGE };
75
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,EAML,KAAK,eAAe,EAGpB,KAAK,eAAe,EACpB,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AAIrB,OAAO,EAA0B,QAAQ,EAAmB,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAIhG,eAAO,MAAM,UAAU,mBAAmB,CAAC;AAC3C,eAAO,MAAM,eAAe,UAAU,CAAC;AACvC,eAAO,MAAM,oBAAoB,kCAAmC,CAAC;AAkBrE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,aAAa,CAAC,EAAE,eAAe,CAAC;IACzC,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAiBD;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAA;CAAE,GACnC;IAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACjF;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAED,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAuBjE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAIxD;AAkID,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,CA2TrF;AAED,2DAA2D;AAC3D,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM7D;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC"}
@@ -0,0 +1,481 @@
1
+ /**
2
+ * The Python `LanguageAdapter` — all seven methods.
3
+ *
4
+ * The parser is Python's own `ast`, reached through one subprocess per run
5
+ * (DEC-062). Everything language-specific stops at this package's boundary;
6
+ * `@descryy/ir` is the only engine dependency, which is what makes the IR
7
+ * boundary a dependency-direction fact rather than a convention.
8
+ */
9
+ import { execFile, spawn } from "node:child_process";
10
+ import { existsSync, readFileSync } from "node:fs";
11
+ import { createHash } from "node:crypto";
12
+ import { join } from "node:path";
13
+ import {} from "@descryy/ir";
14
+ import { discover } from "@descryy/adapter-common";
15
+ import { degradedGraph, extract, LANGUAGE } from "./extract.js";
16
+ import { projectName } from "./module.js";
17
+ import { PYTEST_DEFAULTS, readPytestConfig } from "./pytest-config.js";
18
+ export const ADAPTER_ID = "adapter-python";
19
+ export const ADAPTER_VERSION = "0.1.0";
20
+ export const DEFAULT_PYTHON_GLOBS = ["**/*.py", "**/*.pyi"];
21
+ const PRODUCED_BY = `${ADAPTER_ID}@${ADAPTER_VERSION}`;
22
+ /**
23
+ * The extractor, shipped as a file and located relative to this module.
24
+ *
25
+ * Resolved from `import.meta.dirname` rather than from `process.cwd()`, because
26
+ * an adapter is loaded by an engine whose working directory is the repository
27
+ * under analysis, not this package.
28
+ */
29
+ import { CLIENT_MODULES } from "./client.js";
30
+ const EXTRACTOR = join(import.meta.dirname, "extract.py");
31
+ /** Interpreters to try, in order. `python` last: on many systems it is Python 2. */
32
+ const INTERPRETERS = ["python3", "python"];
33
+ /** Human-readable, and deliberately explicit that the graph is incomplete. */
34
+ export function describeFailure(failure) {
35
+ switch (failure.kind) {
36
+ case "no-interpreter":
37
+ return "no Python 3 interpreter was found, so nothing could be parsed";
38
+ case "spawn-failed":
39
+ return `the extractor could not be started: ${failure.detail}`;
40
+ case "nonzero-exit":
41
+ return `the extractor exited ${failure.code}: ${failure.stderr.slice(0, 400)}`;
42
+ case "truncated":
43
+ return (`the extractor's output ended without its completion marker after ${failure.linesRead} ` +
44
+ "record(s) — the stream was truncated, so the graph is INCOMPLETE by an unknown amount");
45
+ case "malformed":
46
+ return `the extractor's output could not be read: ${failure.detail}`;
47
+ case "timed-out":
48
+ return (`the extractor was killed after ${Math.round(failure.afterMs / 1000)}s, having produced ` +
49
+ `${failure.linesRead} record(s). NOTHING was analysed: what it had streamed is discarded ` +
50
+ "rather than reported, because a partial parse presented as a whole graph is worse than " +
51
+ "no graph. Raise prepareTimeoutMs, or analyse a subdirectory.");
52
+ }
53
+ }
54
+ /**
55
+ * `extract.py`'s per-file error strings have no machine-readable kind, only a
56
+ * prefix convention (`"unreadable: ..."` for an `OSError`, `"syntax error at
57
+ * line ..."` for a `SyntaxError`) — everything else (`ValueError`,
58
+ * `RecursionError`) falls to `"other"` rather than being guessed at.
59
+ */
60
+ export function skipReasonOf(message) {
61
+ if (message.startsWith("unreadable:"))
62
+ return "unreadable";
63
+ if (message.startsWith("syntax error"))
64
+ return "grammar-error";
65
+ return "other";
66
+ }
67
+ /**
68
+ * `AnalysisContext.prepareTimeoutMs` was declared in the contract and honoured by
69
+ * neither adapter — a bound the caller could set and nothing would enforce, so a
70
+ * repository that hung the extractor hung the whole run with no upper limit. The
71
+ * same class of defect as a field computed on one side of a process boundary and
72
+ * never read on the other, and found by the same method: looking for declared
73
+ * things nothing consumes.
74
+ *
75
+ * A timeout here is a **degraded state, not a crash.** The child is killed, what
76
+ * it managed to stream is discarded rather than reported as a whole graph, and
77
+ * the run falls to the disclosed R0 path — because a partial parse silently
78
+ * presented as complete is exactly the failure the posthog transport bug was.
79
+ */
80
+ function runStreaming(command, args, input, signal, timeoutMs) {
81
+ return new Promise((resolve) => {
82
+ const files = {};
83
+ const errors = {};
84
+ let pythonVersion = "";
85
+ let sawTrailer = false;
86
+ let lines = 0;
87
+ let pending = "";
88
+ let stderr = "";
89
+ let malformed;
90
+ const child = spawn(command, [...args], { signal });
91
+ let timedOut = false;
92
+ const timer = setTimeout(() => {
93
+ timedOut = true;
94
+ child.kill("SIGKILL");
95
+ }, timeoutMs);
96
+ timer.unref?.();
97
+ const finish = (outcome) => {
98
+ clearTimeout(timer);
99
+ resolve(outcome);
100
+ };
101
+ const take = (line) => {
102
+ if (line === "")
103
+ return;
104
+ lines += 1;
105
+ let record;
106
+ try {
107
+ record = JSON.parse(line);
108
+ }
109
+ catch (error) {
110
+ malformed ??= `line ${lines}: ${error.message}`;
111
+ return;
112
+ }
113
+ if (record.kind === "header")
114
+ pythonVersion = record.pythonVersion ?? "";
115
+ else if (record.kind === "file" && record.file !== undefined)
116
+ files[record.file] = record.parsed;
117
+ else if (record.kind === "error" && record.file !== undefined)
118
+ errors[record.file] = record.error ?? "";
119
+ else if (record.kind === "trailer")
120
+ sawTrailer = true;
121
+ };
122
+ child.stdout.setEncoding("utf8");
123
+ child.stdout.on("data", (chunk) => {
124
+ pending += chunk;
125
+ let newline = pending.indexOf("\n");
126
+ while (newline !== -1) {
127
+ take(pending.slice(0, newline));
128
+ pending = pending.slice(newline + 1);
129
+ newline = pending.indexOf("\n");
130
+ }
131
+ });
132
+ child.stderr.setEncoding("utf8");
133
+ child.stderr.on("data", (chunk) => {
134
+ stderr += chunk;
135
+ });
136
+ child.on("error", (error) => finish({ failure: { kind: "spawn-failed", detail: error.message } }));
137
+ child.on("close", (code) => {
138
+ take(pending);
139
+ // The timeout is reported ahead of the non-zero exit it causes, because
140
+ // "we killed it" and "it failed" are different statements to the reader.
141
+ if (timedOut)
142
+ finish({ failure: { kind: "timed-out", afterMs: timeoutMs, linesRead: lines } });
143
+ else if (code !== 0)
144
+ finish({ failure: { kind: "nonzero-exit", code: code ?? -1, stderr } });
145
+ else if (malformed !== undefined)
146
+ finish({ failure: { kind: "malformed", detail: malformed } });
147
+ else if (!sawTrailer)
148
+ finish({ failure: { kind: "truncated", linesRead: lines } });
149
+ else
150
+ finish({ parsed: { pythonVersion, files, errors } });
151
+ });
152
+ child.stdin.end(input);
153
+ });
154
+ }
155
+ function run(command, args, input, signal) {
156
+ return new Promise((resolve) => {
157
+ const child = execFile(command, [...args],
158
+ // A repository large enough to overflow this has other problems, but the
159
+ // failure would be a truncated JSON parse with no explanation, so it is
160
+ // bounded explicitly rather than left at the 1 MB default.
161
+ { maxBuffer: 256 * 1024 * 1024, signal }, (error, stdout, stderr) => {
162
+ // `ExecException.code` is `string | number`: an exit status when the
163
+ // process ran, an errno like ENOENT when it never started. Both mean
164
+ // "not zero" here, and collapsing them is deliberate — the caller
165
+ // distinguishes them by whether stdout parses, not by the code.
166
+ resolve({ code: error === null ? 0 : 1, stdout, stderr });
167
+ });
168
+ child.stdin?.end(input);
169
+ });
170
+ }
171
+ async function findInterpreter(preferred, signal) {
172
+ for (const command of preferred === undefined ? INTERPRETERS : [preferred]) {
173
+ const result = await run(command, ["-c", "import sys;print('.'.join(map(str,sys.version_info[:3])))"], "", signal);
174
+ const version = result.stdout.trim();
175
+ // Python 2 cannot run the extractor and its `ast` is a different module.
176
+ if (result.code === 0 && /^3\.\d+/.test(version))
177
+ return { command, version };
178
+ }
179
+ return undefined;
180
+ }
181
+ export function createPythonAdapter(options = {}) {
182
+ const globs = options.globs ?? DEFAULT_PYTHON_GLOBS;
183
+ const ceiling = options.maxResolution ?? 3;
184
+ let session;
185
+ let interpreter;
186
+ const filesUnder = (root) => discover(root.absolutePath, globs).filter((file) => !file.includes("/site-packages/"));
187
+ return {
188
+ id: ADAPTER_ID,
189
+ version: ADAPTER_VERSION,
190
+ async detect(root) {
191
+ const files = filesUnder(root);
192
+ const evidence = [];
193
+ const found = await findInterpreter(options.python, new AbortController().signal);
194
+ if (found === undefined) {
195
+ // A hard stop, not a degradation. DEC-062 takes the interpreter as a
196
+ // dependency of the adapter, and an absent one has to say so — a run
197
+ // that produced nothing must never look like a repository with nothing
198
+ // in it.
199
+ return {
200
+ detected: false,
201
+ roots: [],
202
+ evidence: [
203
+ `no Python 3 interpreter on PATH (tried ${INTERPRETERS.join(", ")}). This adapter ` +
204
+ "parses with Python's own ast (DEC-062) and cannot run without one.",
205
+ ],
206
+ };
207
+ }
208
+ interpreter = found;
209
+ evidence.push(`${found.command} ${found.version}`);
210
+ if (files.length === 0) {
211
+ return { detected: false, roots: [], evidence: [...evidence, "no .py files found"] };
212
+ }
213
+ evidence.push(`${files.length} Python file(s)`);
214
+ // A repository that changed its collection rules must see that we read
215
+ // them. A silently-applied non-default rule is indistinguishable from a
216
+ // silently-ignored one when the numbers come out wrong.
217
+ const pytest = readPytestConfig(root.absolutePath);
218
+ if (pytest !== PYTEST_DEFAULTS)
219
+ evidence.push(pytest.source);
220
+ for (const marker of ["pyproject.toml", "setup.cfg", "setup.py", "requirements.txt"]) {
221
+ if (existsSync(join(root.absolutePath, marker)))
222
+ evidence.push(`${marker} present`);
223
+ }
224
+ const project = projectName(root.absolutePath);
225
+ evidence.push(project.evidence);
226
+ const roots = [
227
+ ...new Set(files.map((file) => file.split("/")[0]).filter((p) => p !== undefined)),
228
+ ].sort();
229
+ return { detected: true, roots, evidence };
230
+ },
231
+ capabilities() {
232
+ return {
233
+ maxResolution: ceiling,
234
+ /**
235
+ * What this adapter can produce, and by omission what it cannot.
236
+ *
237
+ * `MODEL` is present because a framework extractor is now bound and can
238
+ * earn it from evidence — a declarative base, a table binding, a column
239
+ * declaration, all written in the source. It is not present because the
240
+ * corpus holds a class called `Order`: DEC-043's constraint still stands,
241
+ * and `src/orm.ts` contains no name rule of any kind.
242
+ *
243
+ * `API_ROUTE` and `API_ENDPOINT` are present as of DEC-099: FastAPI,
244
+ * Flask and Django all *declare* their routes, and reading a declaration
245
+ * is not inference. The route extractor admits a route only where its
246
+ * receiver resolves to a router constructed from a web framework's own
247
+ * module — never from an attribute spelled like an HTTP verb, which on
248
+ * saleor would have invented 1,150 routes out of `unittest.mock.patch`.
249
+ *
250
+ * `DTO` joined them under DEC-100. It needs a transport boundary, and
251
+ * DEC-043 was right that Python does not supply one — but Pydantic
252
+ * does: a base chain reaching `BaseModel` imported from `pydantic` is a
253
+ * class declaring itself a validated boundary object, enforced at
254
+ * runtime by the framework. Same standard as `MODEL`, different
255
+ * evidence. This is the first *language* adapter to earn `DTO`;
256
+ * `adapter-openapi` needed a published contract for it.
257
+ *
258
+ * `COMPONENT` remains absent. Nothing SQLAlchemy, Django, Pydantic or a
259
+ * route decorator writes down says anything about a UI.
260
+ */
261
+ nodeTypes: [
262
+ "MODULE",
263
+ "CLASS",
264
+ "MODEL",
265
+ "DTO",
266
+ "DATABASE_TABLE",
267
+ "DATABASE_COLUMN",
268
+ "FUNCTION",
269
+ "TEST_CASE",
270
+ "API_ROUTE",
271
+ "API_ENDPOINT",
272
+ ],
273
+ edgeTypes: [
274
+ "IMPORTS",
275
+ "CALLS",
276
+ "TESTS",
277
+ "INHERITS",
278
+ "USES_TYPE",
279
+ "READS",
280
+ "WRITES",
281
+ "RETURNS",
282
+ "SERVES_API",
283
+ "USES_API",
284
+ ],
285
+ hasCallHierarchy: false,
286
+ hasTypeHierarchy: false,
287
+ /**
288
+ * Directories this ecosystem installs dependencies or build output into —
289
+ * DEC-110's end state. The engine used to hold `node_modules`/`vendor`/`target`
290
+ * in a hardcoded list; it holds none of them now and consumes this instead.
291
+ *
292
+ * `filesUnder()` already filters `/site-packages/` out of every discovered file.
293
+ * `.venv` and `.tox` are dotted and structural; the undotted `venv` spelling is
294
+ * not, so it is declared.
295
+ *
296
+ * Names only — the walk matches one path component, so a path or glob matches
297
+ * nothing. Nothing dotted — those are skipped structurally already.
298
+ */
299
+ noiseDirectories: ["site-packages", "__pycache__", "venv"],
300
+ /**
301
+ * Named per framework rather than per language, because that is the unit
302
+ * the work actually comes in. SQLAlchemy and Django disagree about the
303
+ * default nullability of a column — SQLAlchemy 1.x says nullable, Django
304
+ * says not — so neither rule could be written once for "Python".
305
+ */
306
+ frameworkExtractors: [
307
+ "sqlalchemy",
308
+ "django",
309
+ "djangorestframework",
310
+ "fastapi",
311
+ "flask",
312
+ "odoo",
313
+ // GraphQL root operations, `method: "GRAPHQL"`. Rides `route-extractor`
314
+ // exactly as gRPC rides Go's — a new query over the existing edges,
315
+ // not a new capability tag.
316
+ "strawberry",
317
+ "pydantic",
318
+ "requests",
319
+ "httpx",
320
+ "aiohttp",
321
+ ],
322
+ // The only adapter supplying all three: fastapi/flask/django/odoo
323
+ // routes, DRF's router.register action table, sqlalchemy/django
324
+ // models, requests/httpx/aiohttp calls.
325
+ frameworkCapabilities: ["route-extractor", "orm-extractor", "client-call-extractor"],
326
+ nodeIdentityFallback: {
327
+ appliesTo: ["MODULE", "CLASS", "MODEL", "FUNCTION", "TEST_CASE"],
328
+ reason: "a Python module is identified by its file path — the module path IS the path, as a " +
329
+ "Java class's qualified name contains its package. Moving a file therefore changes " +
330
+ "the node ids of everything it declares, and §11.11's rename pass has to carry them " +
331
+ "across. Required to be declared by DEC-004 rather than assumed.",
332
+ },
333
+ };
334
+ },
335
+ async prepare(ctx) {
336
+ const files = filesUnder(ctx.root);
337
+ const project = projectName(ctx.root.absolutePath);
338
+ if (interpreter === undefined) {
339
+ interpreter = await findInterpreter(options.python, ctx.signal);
340
+ }
341
+ if (interpreter === undefined || files.length === 0) {
342
+ session = {
343
+ root: ctx.root,
344
+ files,
345
+ parsed: undefined,
346
+ reached: 0,
347
+ packageName: project.name,
348
+ // No files is not a failure; no interpreter is.
349
+ ...(interpreter === undefined && files.length > 0
350
+ ? { failure: { kind: "no-interpreter" } }
351
+ : {}),
352
+ };
353
+ return 0;
354
+ }
355
+ const outcome = await runStreaming(interpreter.command, [EXTRACTOR], JSON.stringify({
356
+ root: ctx.root.absolutePath,
357
+ files,
358
+ // The Python side records calls at depth 1 by default, which is right
359
+ // for declarative configuration and wrong for the caller half: an
360
+ // HTTP call lives in a method. Naming *which* packages make a file
361
+ // worth full-depth recording is framework knowledge and stays here;
362
+ // the extractor only knows how to record a call.
363
+ deepCallModules: [...CLIENT_MODULES],
364
+ }), ctx.signal,
365
+ // **Measured, not chosen.** Across the 21-repository sweep the slowest
366
+ // observed full-repository extraction was posthog at 25 ms per file
367
+ // (462 s over 18,479 files); strapi ran at 7.6 ms and cal.com at 4.9 ms.
368
+ // The budget is four times the worst observed rate with a one-minute
369
+ // floor, so an ordinary repository can never trip it and a genuinely
370
+ // stuck one is bounded.
371
+ Math.max(ctx.prepareTimeoutMs, 60_000, files.length * 100));
372
+ // R0 needs no imports resolved, R1 upward does — and every level this
373
+ // adapter offers rests on the same single parse, so there is no cheaper
374
+ // rung to fall back to between 0 and the ceiling.
375
+ const reached = outcome.parsed === undefined ? 0 : ceiling;
376
+ session = {
377
+ root: ctx.root,
378
+ files,
379
+ parsed: outcome.parsed,
380
+ reached,
381
+ packageName: project.name,
382
+ ...(outcome.failure === undefined ? {} : { failure: outcome.failure }),
383
+ };
384
+ return reached;
385
+ },
386
+ async parseFiles(files) {
387
+ // Independent of `prepare` by contract: R0 must be achievable with no
388
+ // dependencies present. The tree is the parsed record when one exists and
389
+ // an empty placeholder otherwise, because the caller owns the file list.
390
+ const parsed = session?.parsed;
391
+ return files.map((file) => ({
392
+ file,
393
+ tree: parsed?.files[file.path] ?? {},
394
+ }));
395
+ },
396
+ extractNodes() {
397
+ return current().nodes;
398
+ },
399
+ extractEdges() {
400
+ return current().edges;
401
+ },
402
+ async emit(ctx) {
403
+ const reached = await this.prepare(ctx);
404
+ const active = session;
405
+ const extraction = current();
406
+ const errors = active.parsed?.errors ?? {};
407
+ const skippedFiles = Object.entries(errors).map(([file, message]) => ({
408
+ file,
409
+ reason: skipReasonOf(message),
410
+ detail: message,
411
+ }));
412
+ const skippedSet = new Set(Object.keys(errors));
413
+ // **An empty graph over a non-empty repository is never returned silently.**
414
+ //
415
+ // This is the rule that was missing. home-assistant-core and posthog each
416
+ // returned 0 nodes over ~18,000 files and reported a successful R0 run,
417
+ // because a transport failure and "this repository has nothing in it" were
418
+ // indistinguishable downstream. Degrading to R0 is legitimate; saying
419
+ // nothing about it is not — honest degradation requires the fallback to be
420
+ // labelled, and an unlabelled one is a wrong answer wearing a right one's
421
+ // clothes.
422
+ const degraded = extraction.nodes.length === 0 && active.files.length > 0
423
+ ? degradedGraph({
424
+ repo: ctx.root.repo,
425
+ workspace: ctx.root.workspace,
426
+ producedBy: PRODUCED_BY,
427
+ reached,
428
+ packageName: active.packageName,
429
+ files: active.files,
430
+ cause: active.failure === undefined
431
+ ? "the extractor returned no records and gave no reason"
432
+ : describeFailure(active.failure),
433
+ })
434
+ : undefined;
435
+ return {
436
+ repo: ctx.root.repo,
437
+ ...(ctx.root.workspace === undefined ? {} : { workspace: ctx.root.workspace }),
438
+ commitSha: ctx.root.commitSha,
439
+ producedBy: PRODUCED_BY,
440
+ sourceFiles: active.files.filter((f) => !skippedSet.has(f)),
441
+ skippedFiles,
442
+ reachedResolution: reached,
443
+ nodes: degraded?.nodes ?? extraction.nodes,
444
+ edges: degraded?.edges ?? extraction.edges,
445
+ unresolved: degraded?.unresolved ?? extraction.unresolved,
446
+ };
447
+ },
448
+ async dispose() {
449
+ session = undefined;
450
+ interpreter = undefined;
451
+ },
452
+ };
453
+ function current() {
454
+ const active = session;
455
+ if (active?.parsed === undefined)
456
+ return { nodes: [], edges: [], unresolved: [] };
457
+ return extract({
458
+ repo: active.root.repo,
459
+ workspace: active.root.workspace,
460
+ producedBy: PRODUCED_BY,
461
+ reached: active.reached,
462
+ packageName: active.packageName,
463
+ parsed: active.parsed,
464
+ // The repository's own collection rules, read from its config rather than
465
+ // assumed. `test_*.py` / `Test*` / `test_*` are pytest's defaults, not its
466
+ // definition, and a project may change all three.
467
+ pytest: readPytestConfig(active.root.absolutePath),
468
+ });
469
+ }
470
+ }
471
+ /** Content hash of a repo-relative file, for `FileRef`. */
472
+ export function hashOf(root, relative) {
473
+ try {
474
+ return createHash("sha256").update(readFileSync(join(root, relative))).digest("hex").slice(0, 32);
475
+ }
476
+ catch {
477
+ return "";
478
+ }
479
+ }
480
+ export { LANGUAGE };
481
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}