@callidescope/output 0.0.0-stage → 0.0.4

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,1434 @@
1
+ import { BreadthMeasurement } from '@callidescope/graph';
2
+ import { CallableDirectCalls } from '@callidescope/graph';
3
+ import { CallableId } from '@callidescope/core';
4
+ import { CallAddressTreeResult } from '@callidescope/graph';
5
+ import { CallGraph } from '@callidescope/graph';
6
+ import { CallGraphResult } from '@callidescope/core';
7
+ import { CallidescopeOutputFormat } from '@callidescope/configuration';
8
+ import { CallStack } from '@callidescope/core';
9
+ import { CondensedGraph } from '@callidescope/graph';
10
+ import { ConsoleLogger } from '@nestjs/common';
11
+ import { DeepStackFinding } from '@callidescope/core';
12
+ import { DepthMeasurement } from '@callidescope/graph';
13
+ import { DiscoveredCallable } from '@callidescope/graph';
14
+ import { EntryPointCollection } from '@callidescope/graph';
15
+ import { MarkdownAnchorHelpers } from '@callidescope/configuration';
16
+ import { PathsService } from '@callidescope/graph';
17
+ import pino from 'pino';
18
+ import { ProjectLimits } from '@callidescope/configuration';
19
+ import { ProjectLimitsLookup } from '@callidescope/configuration';
20
+ import { ProjectReport } from '@callidescope/core';
21
+ import { ResolvedCallidescopeConfiguration } from '@callidescope/configuration';
22
+ import { ResolvedCallidescopeJsonOutputConfiguration } from '@callidescope/configuration';
23
+ import { ResolvedCallidescopeMarkdownOutputConfiguration } from '@callidescope/configuration';
24
+ import { ResolvedCallidescopeWriteConfiguration } from '@callidescope/configuration';
25
+ import { RunMode } from '@callidescope/configuration';
26
+ import { SignaturesService } from '@callidescope/graph';
27
+ import { SourceLocation } from '@callidescope/core';
28
+ import { StackFrame } from '@callidescope/core';
29
+ import { WideCallableFinding } from '@callidescope/core';
30
+
31
+ /**
32
+ * NestJS module that wires `depth` and `breadth`'s terminal rendering.
33
+ */
34
+ export declare class AddressReportModule {
35
+ }
36
+
37
+ /**
38
+ * Renders `depth` and `breadth`'s findings for a terminal.
39
+ *
40
+ * Markdown by default and mermaid on request, the same two renderings
41
+ * `callidescope` itself prints — a diagram at a prompt is source someone can
42
+ * paste somewhere that draws it, and markdown is what reads well as terminal
43
+ * text. JSON carries the same data with nothing rendered.
44
+ */
45
+ export declare class AddressReportService {
46
+ private readonly mermaidReportService;
47
+ private readonly reportService;
48
+ constructor(mermaidReportService: MermaidReportService, reportService: ReportService);
49
+ /** The JSON shape of one callable's direct calls. */
50
+ private buildBreadthPayload;
51
+ /** The JSON shape of one callable's traced paths. */
52
+ private buildDepthPayload;
53
+ /** Draws every callee and caller as one diagram, the target as a stadium. */
54
+ private renderBreadthDiagram;
55
+ /** Renders one direction's paths as headed, fenced trees. */
56
+ private renderDepthStacks;
57
+ /** Renders one list of direct callees or callers as a markdown table. */
58
+ private renderReferenceTable;
59
+ /** Turns a callable reference into a frame the diagram renderer can draw. */
60
+ private toFrame;
61
+ /** Renders one callable's direct callers and callees. */
62
+ renderBreadth(args: RenderBreadthArguments): string;
63
+ /**
64
+ * Renders several callables' direct calls as one document.
65
+ *
66
+ * JSON is **always** an array, whatever the address count, so a script can
67
+ * `JSON.parse` a run's output without branching on how many addresses it
68
+ * asked about. Markdown and mermaid concatenate the per-callable renders,
69
+ * each of which already carries its own heading naming the address.
70
+ */
71
+ renderBreadthReports(args: RenderBreadthReportsArguments): string;
72
+ /** Renders the paths traced above and below one callable. */
73
+ renderDepth(args: RenderDepthArguments): string;
74
+ /**
75
+ * Renders several callables' traced paths as one document.
76
+ *
77
+ * JSON is **always** an array, for the same reason `renderBreadthReports`
78
+ * does it: one run, one parseable document, regardless of address count.
79
+ */
80
+ renderDepthReports(args: RenderDepthReportsArguments): string;
81
+ }
82
+
83
+ /** One callable's direct callers and callees, without the run-level format. */
84
+ export declare interface BreadthReport {
85
+ readonly address: string;
86
+ readonly directCalls: CallableDirectCalls;
87
+ readonly displayName: string;
88
+ readonly id: CallableId;
89
+ readonly location: SourceLocation;
90
+ }
91
+
92
+ /** Arguments for scoping a run's findings to each project that produced them. */
93
+ export declare interface BuildProjectReportsArguments {
94
+ readonly breadthMeasurement: BreadthMeasurement;
95
+ readonly callablesById: ReadonlyMap<CallableId, DiscoveredCallable>;
96
+ readonly condensed: CondensedGraph;
97
+ readonly entryPoints: EntryPointCollection;
98
+ readonly fileCountByProject: ReadonlyMap<string, number>;
99
+ readonly graph: CallGraph;
100
+ readonly measurement: DepthMeasurement;
101
+ readonly projectNames: readonly string[];
102
+ }
103
+
104
+ /** Arguments for rendering the JSON report. */
105
+ export declare interface BuildReportArguments {
106
+ readonly destination: ResolvedCallidescopeJsonOutputConfiguration;
107
+ readonly result: CallGraphResult;
108
+ }
109
+
110
+ /** Stands in for a parameter list too long to print. */
111
+ export declare const COLLAPSED_PARAMETERS = "(\u2026)";
112
+
113
+ /** Marks a frame whose callable is on its way out. */
114
+ export declare const DEPRECATED_MARKER = "\u26A0 deprecated";
115
+
116
+ /** One callable's traced paths, without the run-level format. */
117
+ export declare interface DepthReport {
118
+ readonly address: string;
119
+ readonly downward: CallAddressTreeResult;
120
+ readonly upward: CallAddressTreeResult;
121
+ }
122
+
123
+ /** Prefix of every generated node identifier. */
124
+ export declare const DIAGRAM_NODE_PREFIX = "n";
125
+
126
+ /** Marks the first frame of a printed call stack. */
127
+ export declare const ENTRY_FRAME_PREFIX = "\uD83D\uDE80";
128
+
129
+ /** Arguments for picking the findings a named set of projects owns. */
130
+ export declare interface FindOwnedFindingsArguments {
131
+ readonly limits: ProjectLimitsLookup;
132
+ /** The projects entitled to fail on what they own. */
133
+ readonly projectNames: readonly string[];
134
+ readonly reports: readonly ProjectReport[];
135
+ }
136
+
137
+ /**
138
+ * The start marker of any anchored block this convention recognizes, not
139
+ * just the one a given destination owns.
140
+ *
141
+ * Used to find where a corrupted block — a start marker with no matching
142
+ * end — has to stop being replaced: at another block's own territory,
143
+ * never past it.
144
+ */
145
+ export declare const FOREIGN_ANCHOR_PATTERN: RegExp;
146
+
147
+ /**
148
+ * Anything holding a frame list, whether or not it is a full `CallStack`.
149
+ *
150
+ * `ReportService.renderStackTree` and `MermaidReportService.renderStacks`
151
+ * only ever read `frames`, so this is what they accept: a full `CallStack`
152
+ * satisfies it, and so does a path with no depth or entry-point kind of its
153
+ * own, such as one traced from an arbitrary address rather than a recognized
154
+ * entry point.
155
+ */
156
+ export declare interface FramedStack {
157
+ readonly frames: readonly StackFrame[];
158
+ }
159
+
160
+ /** Bucket holding the projects sitting exactly on their own limit. */
161
+ export declare const HEADROOM_BUCKET_AT_LIMIT = "0 \u2014 at limit";
162
+
163
+ /** Bucket holding the projects with room to spare. */
164
+ export declare const HEADROOM_BUCKET_FOUR_PLUS = "4+";
165
+
166
+ /** Bucket holding the projects one frame from their own limit. */
167
+ export declare const HEADROOM_BUCKET_ONE = "1";
168
+
169
+ /**
170
+ * The order the scoreboard prints its buckets in, tightest first.
171
+ *
172
+ * A fixed list rather than the keys a run happened to produce, so a bucket
173
+ * nothing fell into is still printed as a zero. "Nothing is over its limit" is
174
+ * the row a reader came for, and an absent row cannot say it.
175
+ */
176
+ export declare const HEADROOM_BUCKET_ORDER: readonly ["over limit", "0 — at limit", "1", "2–3", "4+", "no stacks"];
177
+
178
+ /** Bucket holding the projects whose deepest stack broke their own limit. */
179
+ export declare const HEADROOM_BUCKET_OVER_LIMIT = "over limit";
180
+
181
+ /** Bucket holding the projects two or three frames from their own limit. */
182
+ export declare const HEADROOM_BUCKET_TWO_TO_THREE = "2\u20133";
183
+
184
+ /**
185
+ * Bucket holding the projects that measured no stack at all.
186
+ *
187
+ * Named rather than folded into the widest headroom bucket: a project whose
188
+ * deepest stack is zero is not a project with room to spare, it is a project
189
+ * whose limit gates nothing, and those are opposite findings.
190
+ */
191
+ export declare const HEADROOM_BUCKET_UNMEASURED = "no stacks";
192
+
193
+ /**
194
+ * Structured values that belong beside a log line rather than inside it.
195
+ *
196
+ * Counts, percentages, and durations are the values that change on every
197
+ * occurrence, so they are carried as fields: the message stays constant and
198
+ * groupable in telemetry, and the numbers stay queryable instead of having to
199
+ * be parsed back out of prose.
200
+ *
201
+ * The named members are the recurring ones; the index signature keeps the
202
+ * argument open for whatever a given call site needs to attach.
203
+ */
204
+ declare interface LogData {
205
+ [key: string]: unknown;
206
+ /** How many things the operation handled. */
207
+ count?: number;
208
+ /** Wall-clock milliseconds the operation took. */
209
+ durationMs?: number;
210
+ /** Completion between 0 and 100. */
211
+ percent?: number;
212
+ /** How many things the operation set out to handle. */
213
+ total?: number;
214
+ }
215
+
216
+ /**
217
+ * Transient-scoped logger so each injecting class gets its own instance.
218
+ * Each consumer calls `setContext(ClassName.name)` to tag every log line
219
+ * with the originating class. Backed by pino for structured JSON output in
220
+ * production and human-readable pretty-print in development.
221
+ *
222
+ * Messages follow one grammar: an emoji naming the subject, a verb in present
223
+ * progressive or past tense, then the object. Values that vary per call —
224
+ * counts, percentages, durations — go in the `data` argument rather than the
225
+ * message, so the message stays constant enough for telemetry to group on.
226
+ *
227
+ * ```ts
228
+ * this.logger.info("📥 Downloading CSEL sources", undefined, { total: 428 });
229
+ * this.logger.info("📥 Downloaded CSEL sources", undefined, { count: 412 });
230
+ * ```
231
+ */
232
+ declare @Injectable({ scope: Scope.TRANSIENT })
233
+ class LoggerService extends ConsoleLogger {
234
+ // 🏗 Dependency Injection
235
+
236
+ constructor() {
237
+ super();
238
+ }
239
+
240
+ // 🔐 Private Fields
241
+
242
+ private static readonly isProduction =
243
+ process.env["NODE_ENV"] === "production";
244
+
245
+ /**
246
+ * Built on first use, not when this file is evaluated.
247
+ *
248
+ * A destination fixed at import time could only ever be chosen by this
249
+ * package, since every consumer's own code runs after its imports.
250
+ */
251
+ private static rootLogger: pino.Logger | undefined;
252
+
253
+ /** Whether lines go to standard error instead of standard output. */
254
+ private static writesToStandardError = false;
255
+
256
+ private child: pino.Logger = LoggerService.root;
257
+
258
+ // 🔑 Public Fields
259
+
260
+ // 🔏 Private Methods
261
+
262
+ /** Build the pino instance for production or local development output. */
263
+ private static createRootLogger(): pino.Logger {
264
+ const level = process.env["LOG_LEVEL"] ?? "info";
265
+
266
+ if (LoggerService.isProduction) {
267
+ return LoggerService.writesToStandardError
268
+ ? pino({ level }, pino.destination(STANDARD_ERROR_DESCRIPTOR))
269
+ : pino({ level });
270
+ }
271
+
272
+ return pino({
273
+ level,
274
+ transport: {
275
+ options: {
276
+ colorize: true,
277
+ destination: LoggerService.writesToStandardError
278
+ ? STANDARD_ERROR_DESCRIPTOR
279
+ : STANDARD_OUTPUT_DESCRIPTOR,
280
+ // The emoji is a field, not part of the message, so the console can
281
+ // show it while telemetry stores unadorned prose. `ignore` then keeps
282
+ // it from being printed a second time in the trailing object.
283
+ ignore: "pid,hostname,emoji",
284
+ messageFormat: "{emoji} {msg}",
285
+ singleLine: true,
286
+ },
287
+ target: "pino-pretty",
288
+ },
289
+ });
290
+ }
291
+
292
+ /**
293
+ * Sends every subsequent line to standard error instead of standard output.
294
+ *
295
+ * For a command-line application whose standard output *is* its result. A log
296
+ * line sharing that stream is not a diagnostic beside the data, it is a
297
+ * corruption of it. Call it before anything logs — the first statement of the
298
+ * application's bootstrap.
299
+ *
300
+ * A call after the first line warns and changes nothing: the destination is
301
+ * fixed when the pino instance is built, and tearing down a transport
302
+ * somebody is writing through would be worse than refusing. The warning is
303
+ * the point — silently leaving the lines on standard output is how a caller
304
+ * would ship a corrupted pipe without ever being told.
305
+ */
306
+ static logToStandardError(): void {
307
+ if (LoggerService.rootLogger !== undefined) {
308
+ process.emitWarning(
309
+ "LoggerService.logToStandardError() was called after the first log line, so log lines still go to standard output and anything piping that stream will read them as data. Call it as the first statement of the application's bootstrap.",
310
+ );
311
+ return;
312
+ }
313
+
314
+ LoggerService.writesToStandardError = true;
315
+ }
316
+
317
+ /**
318
+ * Fails a malformed message in development, and never in production.
319
+ *
320
+ * A logger that throws in production turns an observability call into an
321
+ * outage, so the check runs only where a developer is present to fix it.
322
+ */
323
+ private assertConventionalMessage(args: {
324
+ context: string | undefined;
325
+ parsed: ParsedLogMessage;
326
+ }): void {
327
+ if (
328
+ LoggerService.isProduction ||
329
+ this.shouldSkipConventionalMessageValidation(args.context)
330
+ ) {
331
+ return;
332
+ }
333
+
334
+ const violation = this.getConventionalMessageViolation(args.parsed);
335
+
336
+ if (violation !== undefined) {
337
+ throw new Error(violation);
338
+ }
339
+ }
340
+
341
+ /** Assembles the object pino merges into the line. */
342
+ private buildBindings(args: {
343
+ context: string | undefined;
344
+ data: LogData | undefined;
345
+ parsed: ParsedLogMessage;
346
+ }): Record<string, unknown> {
347
+ this.assertConventionalMessage({
348
+ context: args.context,
349
+ parsed: args.parsed,
350
+ });
351
+
352
+ return {
353
+ ...args.data,
354
+ context: args.context,
355
+ // Telemetry gets prose; only the console-bound transport reads this.
356
+ ...(LoggerService.isProduction ? {} : { emoji: args.parsed.emoji }),
357
+ };
358
+ }
359
+
360
+ /** Returns a human-readable explanation when the message format is invalid. */
361
+ private getConventionalMessageViolation(
362
+ parsed: ParsedLogMessage,
363
+ ): string | undefined {
364
+ const emoji = parsed.emoji;
365
+ const text = parsed.text;
366
+
367
+ if (emoji === undefined) {
368
+ return `Log message must start with an emoji naming its subject, then a verb: "${text}"`;
369
+ }
370
+
371
+ const firstWord = FIRST_WORD_PATTERN.exec(text)?.[1];
372
+
373
+ if (firstWord === undefined || !this.isConventionalVerb(firstWord)) {
374
+ return `Log message must begin with a verb in present progressive or past tense, got "${firstWord ?? ""}": "${emoji} ${text}"`;
375
+ }
376
+
377
+ return undefined;
378
+ }
379
+
380
+ /**
381
+ * Whether a word is a verb in one of the two tenses the convention allows.
382
+ *
383
+ * Present progressive means the operation is under way; past means it
384
+ * finished. Regular morphology covers both, so a new verb needs no
385
+ * registration anywhere — only irregular pasts are enumerated.
386
+ */
387
+ private isConventionalVerb(word: string): boolean {
388
+ const lowercased = word.toLowerCase();
389
+
390
+ return (
391
+ lowercased.endsWith("ing") ||
392
+ lowercased.endsWith("ed") ||
393
+ IRREGULAR_PAST_VERBS.has(lowercased)
394
+ );
395
+ }
396
+
397
+ /** Splits a leading emoji off a message, leaving prose behind. */
398
+ private parseMessage(message: unknown): ParsedLogMessage {
399
+ const text = String(message);
400
+ const match = LEADING_EMOJI_PATTERN.exec(text);
401
+ const emoji = match?.[1];
402
+
403
+ return emoji === undefined
404
+ ? { emoji: undefined, text }
405
+ : { emoji, text: text.slice(match?.[0].length) };
406
+ }
407
+
408
+ /** Whether a context is intentionally exempt from the validation rule. */
409
+ private shouldSkipConventionalMessageValidation(
410
+ context: string | undefined,
411
+ ): boolean {
412
+ return context !== undefined && UNVALIDATED_LOG_CONTEXTS.has(context);
413
+ }
414
+
415
+ // 🌎 Public Methods
416
+
417
+ /** The pino instance every logger's child is taken from. */
418
+ private static get root(): pino.Logger {
419
+ LoggerService.rootLogger ??= LoggerService.createRootLogger();
420
+
421
+ return LoggerService.rootLogger;
422
+ }
423
+
424
+ /** Normalizes unknown errors into a stable message and timestamped log line. */
425
+ buildErrorLogEntry(
426
+ context: string,
427
+ error: unknown,
428
+ ): { errorMessage: string; logLine: string } {
429
+ const errorMessage =
430
+ error instanceof Error ? error.stack || error.message : String(error);
431
+
432
+ return {
433
+ errorMessage,
434
+ logLine: `[${new Date().toISOString()}] ${context}: ${errorMessage}\n`,
435
+ };
436
+ }
437
+
438
+ /** Builds a timestamped output log file path and ensures the output directory exists. */
439
+ createTimestampedOutputLogFilePath(filePrefix: string): string {
440
+ const outputDirectory = path.join(process.cwd(), "output");
441
+ if (!existsSync(outputDirectory)) {
442
+ mkdirSync(outputDirectory, { recursive: true });
443
+ }
444
+
445
+ return path.join(
446
+ outputDirectory,
447
+ `${filePrefix}-${new Date().toISOString().replaceAll(/[:.]/g, "-")}.log`,
448
+ );
449
+ }
450
+
451
+ /** Logs a debug message at the `debug` level. */
452
+ override debug(message: unknown, context?: string, data?: LogData): void {
453
+ const parsed = this.parseMessage(message);
454
+ this.child.debug(
455
+ this.buildBindings({ context: context ?? this.context, data, parsed }),
456
+ parsed.text,
457
+ );
458
+ }
459
+
460
+ /**
461
+ * Logs an error message at the `error` level, optionally including a stack trace.
462
+ *
463
+ * `ConsoleLogger.error` spends a third slot on a context string that the
464
+ * other levels do not have, so this one accepts either: a string keeps
465
+ * NestJS's meaning, an object is structured data like everywhere else.
466
+ */
467
+ override error(
468
+ message: unknown,
469
+ stackOrContext?: string,
470
+ contextOrData?: LogData | string,
471
+ ): void {
472
+ const parsed = this.parseMessage(message);
473
+ const data = typeof contextOrData === "object" ? contextOrData : undefined;
474
+ const context =
475
+ typeof contextOrData === "string" ? contextOrData : this.context;
476
+
477
+ this.child.error(
478
+ {
479
+ ...this.buildBindings({ context, data, parsed }),
480
+ stack: stackOrContext,
481
+ },
482
+ parsed.text,
483
+ );
484
+ }
485
+
486
+ /** Logs an informational message at the `info` level. */
487
+ info(message: unknown, context?: string, data?: LogData): void {
488
+ const parsed = this.parseMessage(message);
489
+ this.child.info(
490
+ this.buildBindings({ context: context ?? this.context, data, parsed }),
491
+ parsed.text,
492
+ );
493
+ }
494
+
495
+ /**
496
+ * Logs an informational message at the `info` level.
497
+ *
498
+ * NestJS and `nest-commander` call this method directly as part of the
499
+ * framework's own `LoggerService` contract, so it must keep working
500
+ * exactly as before. Application code should call `info` instead — the
501
+ * same behavior under a name that says what level it logs at.
502
+ */
503
+ override log(message: unknown, context?: string, data?: LogData): void {
504
+ this.info(message, context, data);
505
+ }
506
+
507
+ /** Sets the context label included in every subsequent log line. */
508
+ override setContext(context: string): void {
509
+ super.setContext(context);
510
+ this.child = LoggerService.root.child({ context });
511
+ }
512
+
513
+ /** Logs a verbose message at the `trace` level. */
514
+ override verbose(message: unknown, context?: string, data?: LogData): void {
515
+ const parsed = this.parseMessage(message);
516
+ this.child.trace(
517
+ this.buildBindings({ context: context ?? this.context, data, parsed }),
518
+ parsed.text,
519
+ );
520
+ }
521
+
522
+ /** Logs a warning message at the `warn` level. */
523
+ override warn(message: unknown, context?: string, data?: LogData): void {
524
+ const parsed = this.parseMessage(message);
525
+ this.child.warn(
526
+ this.buildBindings({ context: context ?? this.context, data, parsed }),
527
+ parsed.text,
528
+ );
529
+ }
530
+ }
531
+
532
+ /**
533
+ * Heading over the call stacks that ran deeper than their project allows.
534
+ *
535
+ * A constant rather than a literal because two renderings write it — the
536
+ * whole-run report, and the findings-only rendering a gate prints — and a
537
+ * reader who greps a failed pipeline for one of them must find the other.
538
+ */
539
+ export declare const MARKDOWN_DEEP_STACKS_HEADING = "Call stacks over the depth limit";
540
+
541
+ /**
542
+ * How each anchored destination draws the stacks it carries.
543
+ *
544
+ * One list rather than the pair written out at each of the two places a run
545
+ * writes anchored blocks — its own and every project's — because the pairing is
546
+ * the same fact both times, and two copies of it are two places for `mermaid`
547
+ * to start printing trees.
548
+ */
549
+ export declare const MARKDOWN_DESTINATION_RENDERINGS: readonly [readonly ["markdown", "tree"], readonly ["mermaid", "diagram"]];
550
+
551
+ /** Header of the depth-headroom scoreboard. */
552
+ export declare const MARKDOWN_HEADROOM_HEADER = "| Headroom | Projects |\n| --- | --- |";
553
+
554
+ /** Header of the per-project index table. */
555
+ export declare const MARKDOWN_PROJECT_INDEX_HEADER = "| Project | Deepest | Limit | Headroom | Widest |\n| --- | --- | --- | --- | --- |";
556
+
557
+ /** Header of the run or project summary table. */
558
+ export declare const MARKDOWN_SUMMARY_HEADER = "| Measure | Value |\n| --- | --- |";
559
+
560
+ /** Header of the breadth table. */
561
+ export declare const MARKDOWN_WIDE_CALLABLES_HEADER = "| Callable | Breadth | Calls directly | Location |\n| --- | --- | --- | --- |";
562
+
563
+ /** Heading over the callables that called more directly than allowed. */
564
+ export declare const MARKDOWN_WIDE_CALLABLES_HEADING = "Callables over the breadth limit";
565
+
566
+ /**
567
+ * Renders a run, or one project's slice of it, as markdown.
568
+ *
569
+ * Markdown rather than a bespoke text format because the same rendering has to
570
+ * serve three places — a terminal, a report file, and a section spliced into a
571
+ * project's own README — and only one of those can read anything else.
572
+ */
573
+ export declare class MarkdownReportService {
574
+ private readonly mermaidReportService;
575
+ private readonly reportService;
576
+ private readonly workspaceReportService;
577
+ constructor(mermaidReportService: MermaidReportService, reportService: ReportService, workspaceReportService: WorkspaceReportService);
578
+ /**
579
+ * Renders each callable's breadth, the first few openly and the rest
580
+ * behind a disclosure — the same treatment `renderStacks` gives stacks.
581
+ *
582
+ * Shared by both scopes: a project section passes every callable with a
583
+ * direct callee, unfiltered by any limit, the same way its stacks are;
584
+ * a whole-run section passes only the `WideCallableFinding`s that broke
585
+ * the configured limit. `WideCallableFinding` is a `CallableBreadthReport`
586
+ * with a `limit` added, so one renderer covers both without caring which
587
+ * it was handed. Unfiltered, a project's breadth table is one row per
588
+ * non-leaf callable — every bit as unbounded as its stacks — so it earns
589
+ * the same truncation rather than a bare table.
590
+ */
591
+ private renderCallableBreadths;
592
+ /**
593
+ * Renders one limit's value cell.
594
+ *
595
+ * A limit the project does not declare prints `none` rather than a number,
596
+ * which is breadth's usual case: it has no default at any level, so a
597
+ * project writing `maximumBreadth: undefined` is gated on breadth by nothing
598
+ * at all, and printing some number there would say the opposite.
599
+ */
600
+ private renderLimitCell;
601
+ /**
602
+ * Renders the two limits one project is judged against.
603
+ *
604
+ * A project's block already carried its deepest stack and its widest
605
+ * callable; what it could not say is what either number is measured against.
606
+ * That was inferable while one workspace number gated everything and is not
607
+ * inferable now — the limit is a fact about this project, and fifty projects
608
+ * hold fifty answers.
609
+ *
610
+ * There is no origin column, because there is no second origin left: every
611
+ * traced project's configuration is complete, so both numbers are written in
612
+ * the file beside this readme or the run refused to start.
613
+ */
614
+ private renderProjectLimits;
615
+ /** Renders one stack: a labelled heading line and its tree in a fence. */
616
+ private renderStack;
617
+ /** Renders the stacks of one scope, drawn or printed as asked. */
618
+ private renderStacksAs;
619
+ /** Renders the counts describing what a run, or a project, produced. */
620
+ private renderSummaryTable;
621
+ /** Renders a table, or says plainly that there was nothing to put in one. */
622
+ private renderTable;
623
+ /**
624
+ * Names each callable that broke its breadth limit, a line each.
625
+ *
626
+ * A line rather than `renderCallableBreadths`' table, and deliberately not
627
+ * that method reused: the table's columns say what a callable calls, and a
628
+ * finding's product is the number it broke. `limit` is per project now, so a
629
+ * reader cannot infer it from the run the way a single workspace-wide number
630
+ * could be inferred — it has to be printed beside the breadth it failed.
631
+ */
632
+ private renderWideCallableLines;
633
+ /**
634
+ * The heading prefix one level below the block's own.
635
+ *
636
+ * Derived rather than fixed, so a block spliced under an `##` heading writes
637
+ * `###` subsections and stays a well-formed subtree of the file it landed
638
+ * in. A heading carrying no leading `#` at all yields `##`, which is the
639
+ * level these sections had before the heading was configurable.
640
+ */
641
+ private subsectionPrefix;
642
+ /**
643
+ * Renders the two findings a gate weighs, and nothing else.
644
+ *
645
+ * A gate prints why it decided rather than what it read: the full report is
646
+ * what a trace is for, and burying two deep stacks in a listing of every
647
+ * stack in the project is how a failed pipeline stops being read.
648
+ *
649
+ * Here rather than in the caller because rendering is this package's job,
650
+ * and because the headings are `renderRun`'s headings — written once, so a
651
+ * reader who greps a pipeline log for one rendering finds the other.
652
+ */
653
+ renderFindings(args: RenderFindingsArguments): string;
654
+ /** Renders one project's section, for splicing into its own README. */
655
+ renderProjectSection(args: RenderProjectSectionArguments): string;
656
+ /**
657
+ * Renders a whole run, for a terminal or a report file.
658
+ *
659
+ * Opens with what the run adds up to rather than with its findings: the
660
+ * summary, the per-project index, and the headroom each project has left.
661
+ * A workspace holding fifty projects is not readable as a list of
662
+ * callables, and every section below these three is one — so a reader
663
+ * arriving at this block learns which project to look at before being
664
+ * handed the stacks.
665
+ */
666
+ renderRun(args: RenderRunArguments): string;
667
+ /**
668
+ * Renders every stack, the first few openly and the rest behind a disclosure.
669
+ *
670
+ * A package with two hundred stacks is still worth publishing in full — an
671
+ * agent reading the file can expand it, and a person scrolling past should
672
+ * not have to.
673
+ */
674
+ renderStacks(args: RenderStacksArguments): string;
675
+ }
676
+
677
+ /**
678
+ * Callables a diagram draws before it stops accepting stacks.
679
+ *
680
+ * GitHub refuses a mermaid block past 50,000 characters, and at roughly fifty
681
+ * characters per node and edge this leaves room to spare — the widest project
682
+ * here draws 263. Whole stacks are dropped rather than trimmed, so the diagram
683
+ * never shows an edge into a callable it does not draw.
684
+ */
685
+ export declare const MAXIMUM_DIAGRAM_NODES = 300;
686
+
687
+ /**
688
+ * Opens every diagram.
689
+ *
690
+ * Left to right, because that is the direction a stack reads and the direction
691
+ * a wide, shallow call graph fits a page.
692
+ */
693
+ export declare const MERMAID_FLOWCHART_HEADER = "flowchart LR";
694
+
695
+ /** What mermaid would read as syntax inside a node label. */
696
+ export declare const MERMAID_LABEL_ESCAPES: ReadonlyMap<string, string>;
697
+
698
+ /**
699
+ * A diagram under construction.
700
+ *
701
+ * Mutable, and deliberately so: nodes and edges accumulate across every stack
702
+ * drawn, and threading an immutable accumulator through that would say nothing
703
+ * the name does not.
704
+ */
705
+ export declare interface MermaidDiagram {
706
+ readonly edges: Set<string>;
707
+ readonly identifiersByCallable: Map<CallableId, string>;
708
+ readonly nodes: string[];
709
+ }
710
+
711
+ /**
712
+ * Draws a set of call stacks as one mermaid flowchart.
713
+ *
714
+ * One diagram for all of them rather than one apiece, because a single stack
715
+ * is a straight line and a straight line is a list with extra steps. Drawn
716
+ * together the shared tails converge — every resolver ending in the same
717
+ * service, every command reaching the same repository — and that convergence
718
+ * is the thing a picture shows and an indented tree cannot.
719
+ *
720
+ * Labels carry the callable's name and nothing else. What it takes, returns,
721
+ * and documents belongs to the tree rendering, which has room for it; a
722
+ * diagram trying to carry all of that is unreadable at any size.
723
+ */
724
+ export declare class MermaidReportService {
725
+ constructor();
726
+ /** Draws one frame, or returns the identifier it already has. */
727
+ private addFrame;
728
+ /** Draws one stack into the diagram, reusing whatever it already holds. */
729
+ private addStack;
730
+ /** Counts the callables a stack would add that the diagram lacks. */
731
+ private countNewCallables;
732
+ /** Renders a frame's label, escaping what mermaid would read as syntax. */
733
+ private renderLabel;
734
+ /** Wraps a built diagram in its fence, or says why there is none. */
735
+ renderDiagram(args: {
736
+ diagram: MermaidDiagram;
737
+ omitted: number;
738
+ }): string;
739
+ /** Draws every stack that fits, deepest first, and says what did not. */
740
+ renderStacks(args: {
741
+ stacks: readonly FramedStack[];
742
+ }): string;
743
+ }
744
+
745
+ /**
746
+ * Frames a stack needs before a report carries it.
747
+ *
748
+ * Two, because a single frame is a callable that calls nothing — accurate, but
749
+ * not a call stack, and there are twice as many of those as there are real
750
+ * ones.
751
+ */
752
+ export declare const MINIMUM_STACK_FRAMES = 2;
753
+
754
+ /** Raised when the anchor helper is asked to write a file nothing named. */
755
+ export declare class MissingMarkdownPathError extends Error {
756
+ constructor();
757
+ }
758
+
759
+ /** Marks every frame below the first. */
760
+ export declare const NESTED_FRAME_PREFIX = "\u2514\u2500>";
761
+
762
+ /**
763
+ * Provides the machine-readable JSON report destination.
764
+ */
765
+ export declare class OutputJsonModule {
766
+ }
767
+
768
+ /**
769
+ * Writes the traced findings to a JSON report file.
770
+ *
771
+ * The report holds the findings and nothing else — no timestamp, no tool
772
+ * version, no run duration. Anything that changed between two runs over the
773
+ * same tree would make check mode fail on a repository nobody touched.
774
+ */
775
+ export declare class OutputJsonService {
776
+ private readonly logger;
777
+ constructor(logger: LoggerService);
778
+ /** Reads an existing report, returning an empty string if absent. */
779
+ private readExisting;
780
+ /**
781
+ * Renders the JSON report for the traced findings.
782
+ *
783
+ * Ends with a newline so the file matches what a formatter would leave
784
+ * behind, and so check mode does not fail over the one byte every other tool
785
+ * in the repository adds.
786
+ */
787
+ buildReport(args: BuildReportArguments): string;
788
+ /**
789
+ * Syncs the configured JSON file with the current findings.
790
+ *
791
+ * In write mode the report is written, creating parent directories as
792
+ * needed. In check mode nothing is written and the return value reports
793
+ * whether the file already holds the current report.
794
+ */
795
+ sync(args: SyncJsonArguments): boolean;
796
+ }
797
+
798
+ /**
799
+ * Provides the anchored markdown block destination.
800
+ */
801
+ export declare class OutputMarkdownModule {
802
+ }
803
+
804
+ /**
805
+ * Splices a generated block into a markdown file, between two anchors.
806
+ *
807
+ * Staleness is decided by comparing the extracted block against the
808
+ * regenerated one, byte for byte. A file with no anchors, or no file at all,
809
+ * counts as stale rather than as an error, so check mode reports the same thing
810
+ * whether the block drifted or was never written.
811
+ */
812
+ export declare class OutputMarkdownService {
813
+ private readonly logger;
814
+ constructor(logger: LoggerService);
815
+ /** Appends a generated block after a file's existing content. */
816
+ private appendBlock;
817
+ /** Builds the pattern matching everything between the two anchors. */
818
+ private buildBlockPattern;
819
+ /** Escapes a marker so it can sit inside a pattern. */
820
+ private escapePattern;
821
+ /** Reads a file, treating an absent one as empty. */
822
+ private readExisting;
823
+ /**
824
+ * Cuts out a start marker's corrupted span and splices the fresh block in.
825
+ *
826
+ * Reached only when the start marker is present but the block's own end
827
+ * marker cannot be found anywhere after it — the same file this
828
+ * convention lets stack several anchored blocks back to back, so
829
+ * everything is fair game to have drifted except one thing: another
830
+ * block's own anchors. The span is cut at the next such anchor, or at the
831
+ * end of the file when none follows, so the fresh block replaces exactly
832
+ * what belonged to this one and nothing that belonged to another.
833
+ */
834
+ private replaceOrphanedBlock;
835
+ /**
836
+ * Places the generated block into the file's current content.
837
+ *
838
+ * A pattern match replaces cleanly. No start marker at all falls back to
839
+ * appending, the historical behavior for a file never written to before.
840
+ * A start marker with no matching end can never satisfy the pattern —
841
+ * silently leaving it in place is what let a run claim success while
842
+ * changing nothing — so that span is cut out and replaced instead.
843
+ */
844
+ private spliceBlock;
845
+ /** Builds the helpers a configured `writeBlock` function is handed. */
846
+ buildHelpers(args: SyncMarkdownArguments): MarkdownAnchorHelpers;
847
+ /** Syncs the configured markdown destination with the current findings. */
848
+ sync(args: SyncMarkdownArguments): boolean;
849
+ /**
850
+ * Splices the anchored block into a file.
851
+ *
852
+ * Appends the block when the anchors are absent, creates the file when it
853
+ * does not exist, and in check mode writes nothing and reports whether the
854
+ * file already holds the current block.
855
+ */
856
+ syncAnchoredBlock(args: SyncAnchoredBlockArguments): boolean;
857
+ /** Wraps content in the configured anchors. */
858
+ wrapInAnchors(args: WrapInAnchorsArguments): string;
859
+ }
860
+
861
+ /** The findings one set of projects owns, each judged by that project. */
862
+ export declare interface OwnedFindings {
863
+ readonly deepStacks: readonly DeepStackFinding[];
864
+ readonly wideCallables: readonly WideCallableFinding[];
865
+ }
866
+
867
+ /** A message split into the emoji the console shows and the prose telemetry stores. */
868
+ declare interface ParsedLogMessage {
869
+ emoji: string | undefined;
870
+ text: string;
871
+ }
872
+
873
+ /** One project's row in the index, and the numbers the scoreboard buckets it by. */
874
+ export declare interface ProjectIndexRow {
875
+ readonly deepest: number;
876
+ readonly headroom: number;
877
+ readonly limit: number;
878
+ readonly projectName: string;
879
+ readonly widest: number;
880
+ }
881
+
882
+ /**
883
+ * Provides the per-project view a project's own README is written from.
884
+ */
885
+ export declare class ProjectReportsModule {
886
+ }
887
+
888
+ /**
889
+ * Scopes a run's findings to the project each one came from.
890
+ *
891
+ * A section embedded in a project's README should describe that project, not
892
+ * the workspace it happens to sit in, so everything here is keyed on the
893
+ * project the entry point — or the reported callable — belongs to.
894
+ */
895
+ export declare class ProjectReportsService {
896
+ private readonly pathsService;
897
+ private readonly signaturesService;
898
+ constructor(pathsService: PathsService, signaturesService: SignaturesService);
899
+ /** Builds every callable's breadth, grouped by project. */
900
+ private buildCallableBreadths;
901
+ /** Builds every stack that makes at least one call, deepest first. */
902
+ private buildStacks;
903
+ /** Counts what one project contributed to the graph. */
904
+ private buildSummary;
905
+ /** Picks the stacks one project's own depth limit fails on. */
906
+ private findProjectDeepStacks;
907
+ /** Picks the callables one project's own breadth limit fails on. */
908
+ private findProjectWideCallables;
909
+ /** Reads the depth measured for one callable, or nothing if unmeasured. */
910
+ private readDepth;
911
+ /**
912
+ * Reads the limits one project is judged against.
913
+ *
914
+ * A project the lookup does not name falls back to the workspace's, which is
915
+ * what a project declaring nothing is given anyway — so a report arriving
916
+ * from outside the resolved set is judged rather than silently exempted.
917
+ */
918
+ private readProjectLimits;
919
+ /** Builds one report per project, in the order the projects were traced. */
920
+ build(args: BuildProjectReportsArguments): ProjectReport[];
921
+ /**
922
+ * Picks the stacks a run should fail on, deepest first.
923
+ *
924
+ * A filter over the stacks the reports already hold rather than a second
925
+ * traversal: reconstructing the same paths twice would let the number the
926
+ * gate fails on drift from the number the README publishes.
927
+ *
928
+ * Each project is judged against its own limit rather than one number for
929
+ * the whole workspace, so a stack deeper than the project that roots it
930
+ * allows is reported even when a noisier project elsewhere allows more.
931
+ * The stack is judged by the project owning its entry point, which is the
932
+ * project `build` already filed it under.
933
+ *
934
+ * `maximumDepth` is a positive integer, so a stack past it is at least two
935
+ * deep and therefore survived the minimum-frame filter above.
936
+ */
937
+ findDeepStacks(args: {
938
+ limits: ProjectLimitsLookup;
939
+ reports: readonly ProjectReport[];
940
+ }): DeepStackFinding[];
941
+ /**
942
+ * Picks the findings a named set of projects owns, and nothing else.
943
+ *
944
+ * For a run whose verdict covers fewer projects than its measurement did. A
945
+ * finding's owner is already decided — `build` files a stack under the
946
+ * project owning its entry point and a breadth report under the project
947
+ * declaring the callable — so this selects among those reports rather than
948
+ * deciding ownership a second way.
949
+ *
950
+ * A named project the reports do not hold contributes nothing rather than
951
+ * being an error: a run may be pointed at a project whose files it then
952
+ * found nothing in, and having read nothing is a fact for the caller's own
953
+ * rules to judge.
954
+ */
955
+ findOwnedFindings(args: FindOwnedFindingsArguments): OwnedFindings;
956
+ /**
957
+ * Picks the named projects a run opened no file of its own from.
958
+ *
959
+ * The companion to `findOwnedFindings`, and the reason it needs one: owning
960
+ * no finding is what a clean project and an unread one look like from the
961
+ * outside, and a caller judging only the findings cannot tell them apart.
962
+ * The project's own `fileCount` is what separates them — `collect` counts a
963
+ * file per project once the run's exclusions and the per-project ones have
964
+ * had their say, so a project whose files were all filtered away reports
965
+ * zero however much the rest of the run read.
966
+ *
967
+ * **Files rather than callables**, which is the weaker of the two questions
968
+ * and the right one. A project can hold files that declare no callable at
969
+ * all — one whose `tsconfig.json` names only its own configuration files and
970
+ * its tests is the shape, and this repository has five — and those were
971
+ * read, so a verdict on them is a verdict on something. Asking for a
972
+ * callable would fail every one of them for containing no functions, which
973
+ * is not a finding about anything. The cost is stated plainly: a project
974
+ * whose sources are excluded while a configuration file of its own survives
975
+ * counts as read.
976
+ *
977
+ * A named project with no report at all counts as unread rather than as an
978
+ * error, the same way `findOwnedFindings` treats one: a run pointed at a
979
+ * project the workspace configuration excludes never discovers it, and that
980
+ * is exactly the case worth failing.
981
+ */
982
+ findUnreadProjects(args: {
983
+ /** The projects entitled to fail on what they own. */
984
+ projectNames: readonly string[];
985
+ reports: readonly ProjectReport[];
986
+ }): string[];
987
+ /**
988
+ * Picks the callables a run should fail on, widest first.
989
+ *
990
+ * A filter over the breadth reports the reports already hold, mirroring
991
+ * `findDeepStacks`, so the number the gate fails on cannot drift from the
992
+ * number the README publishes. A callable is judged by the project that
993
+ * declares it, which is the project `build` already filed it under.
994
+ *
995
+ * No default exists for `maximumBreadth`: until a configuration sets one,
996
+ * nothing can exceed it, so a project with no limit anywhere in its
997
+ * inheritance reports nothing rather than being judged against a number
998
+ * nobody chose.
999
+ */
1000
+ findWideCallables(args: {
1001
+ limits: ProjectLimitsLookup;
1002
+ reports: readonly ProjectReport[];
1003
+ }): WideCallableFinding[];
1004
+ }
1005
+
1006
+ /** Arguments for rendering one callable's direct callers and callees. */
1007
+ declare interface RenderBreadthArguments extends BreadthReport {
1008
+ readonly format: CallidescopeOutputFormat;
1009
+ }
1010
+
1011
+ /** Arguments for rendering several callables' direct calls as one document. */
1012
+ declare interface RenderBreadthReportsArguments {
1013
+ readonly format: CallidescopeOutputFormat;
1014
+ readonly reports: readonly BreadthReport[];
1015
+ }
1016
+
1017
+ /** Arguments for rendering the paths traced above and below one callable. */
1018
+ declare interface RenderDepthArguments extends DepthReport {
1019
+ readonly format: CallidescopeOutputFormat;
1020
+ }
1021
+
1022
+ /** Arguments for rendering several callables' traced paths as one document. */
1023
+ declare interface RenderDepthReportsArguments {
1024
+ readonly format: CallidescopeOutputFormat;
1025
+ readonly reports: readonly DepthReport[];
1026
+ }
1027
+
1028
+ /**
1029
+ * Arguments for rendering only the findings a gate weighs.
1030
+ *
1031
+ * No `rendering`, unlike `RenderRunArguments`: a gate's product is the list of
1032
+ * things to go and fix, and a diagram of them is not that list.
1033
+ *
1034
+ * The findings themselves rather than the run that produced them, also unlike
1035
+ * `RenderRunArguments`: a scoped gate judges fewer findings than its trace
1036
+ * measured, and a renderer handed the whole run would print the ones it was
1037
+ * not judged on.
1038
+ */
1039
+ export declare interface RenderFindingsArguments extends OwnedFindings {
1040
+ readonly previewCount: number;
1041
+ }
1042
+
1043
+ /** Arguments for rendering the per-project index and its scoreboard. */
1044
+ export declare interface RenderProjectIndexArguments {
1045
+ readonly limits: ProjectLimitsLookup;
1046
+ readonly projects: readonly ProjectReport[];
1047
+ }
1048
+
1049
+ /** Arguments for rendering one project's section. */
1050
+ export declare interface RenderProjectSectionArguments {
1051
+ readonly heading: string;
1052
+ /**
1053
+ * The lookup the whole run resolved, rather than this project's two numbers.
1054
+ *
1055
+ * The renderer reads its own row out of it through the same `limitsFor` the
1056
+ * index and the gate read, so a project's block and the workspace's view of
1057
+ * that project cannot come to disagree about which limit applied.
1058
+ */
1059
+ readonly limits: ProjectLimitsLookup;
1060
+ readonly previewCount: number;
1061
+ readonly rendering: StackRendering;
1062
+ readonly report: ProjectReport;
1063
+ }
1064
+
1065
+ /** Arguments for rendering a whole run. */
1066
+ export declare interface RenderRunArguments {
1067
+ /** Prose placed under the heading, from the destination that asked for it. */
1068
+ readonly description: string | undefined;
1069
+ readonly heading: string;
1070
+ readonly limits: ProjectLimitsLookup;
1071
+ readonly previewCount: number;
1072
+ readonly rendering: StackRendering;
1073
+ readonly result: CallGraphResult;
1074
+ }
1075
+
1076
+ /** Arguments for rendering a run's stacks, preview then disclosure. */
1077
+ export declare interface RenderStacksArguments {
1078
+ readonly previewCount: number;
1079
+ readonly stacks: readonly CallStack[];
1080
+ }
1081
+
1082
+ /** Arguments accepted when weighing what a run found. */
1083
+ export declare interface ReportFindingsArguments {
1084
+ readonly mode: RunMode;
1085
+ readonly result: CallGraphResult;
1086
+ /**
1087
+ * Destinations found not to hold the current report. Only ever non-empty
1088
+ * when the run was comparing, since nothing else reads a destination.
1089
+ */
1090
+ readonly stalePaths: readonly string[];
1091
+ }
1092
+
1093
+ /**
1094
+ * NestJS module that wires the service weighing a trace's findings.
1095
+ */
1096
+ export declare class ReportFindingsModule {
1097
+ }
1098
+
1099
+ /**
1100
+ * Weighs every finding a run can produce, and fails on the ones it was asked
1101
+ * to gate.
1102
+ *
1103
+ * Kept away from `CallidescopeCommand` itself, the same reason `RunPlanService`
1104
+ * is: what a run does with what it found is a question this service answers on
1105
+ * its own — through `this.logger` alone — leaving the command to orchestrate
1106
+ * the trace rather than weigh its output.
1107
+ */
1108
+ export declare class ReportFindingsService {
1109
+ private readonly logger;
1110
+ constructor(logger: LoggerService);
1111
+ /**
1112
+ * Names the stacks that ran deeper than callidescope allows.
1113
+ *
1114
+ * Reported whether or not the run gates on them — a stack this long is worth
1115
+ * saying out loud even in a run that only wrote a report — but only a run
1116
+ * asked to fail on depth fails on it.
1117
+ */
1118
+ private reportDeepStacks;
1119
+ /**
1120
+ * Fails a run that traced nothing at all.
1121
+ *
1122
+ * Unconditional, and not something `--check` turns on. Every other finding
1123
+ * is a verdict on code that was read; this one says no code was read, and a
1124
+ * gate that passes because it never looked is worse than one that fails —
1125
+ * it reports the workspace as clean and there is nothing in the output to
1126
+ * say otherwise.
1127
+ */
1128
+ private reportEmptyTrace;
1129
+ /** Names the destinations that no longer hold what a fresh run would write. */
1130
+ private reportStaleness;
1131
+ /**
1132
+ * Names the callables that called more things directly than callidescope
1133
+ * allows.
1134
+ *
1135
+ * Reported whether or not the run gates on them, mirroring
1136
+ * `reportDeepStacks` — but only a run asked to fail on breadth fails on it.
1137
+ */
1138
+ private reportWideCallables;
1139
+ /**
1140
+ * Weighs every finding a run can produce, and fails on any of them.
1141
+ *
1142
+ * They are weighed separately and announced separately. A stack that is too
1143
+ * deep is something the code does; a callable calling too many things is
1144
+ * something else the code does; a stale report is something the checkout
1145
+ * has not caught up with. Reading one as another sends the author to fix
1146
+ * the wrong thing.
1147
+ *
1148
+ * A project that could not be read never reaches here: it ends the trace
1149
+ * before anything is printed or written, and is reported by
1150
+ * `CallidescopeCommand` itself.
1151
+ */
1152
+ reportFindings(args: ReportFindingsArguments): void;
1153
+ }
1154
+
1155
+ /**
1156
+ * Provides the terminal rendering of one run's findings.
1157
+ */
1158
+ export declare class ReportModule {
1159
+ }
1160
+
1161
+ /**
1162
+ * Renders one call stack as an indented tree.
1163
+ *
1164
+ * Plain text rather than a markdown list, because the indentation is the thing
1165
+ * that makes a stack readable and a list would have every renderer reflow it.
1166
+ * Whatever embeds this is responsible for putting it in a fence.
1167
+ */
1168
+ export declare class ReportService {
1169
+ constructor();
1170
+ /** Reads a summary's opening sentence, when it has more than one. */
1171
+ private readFirstSentence;
1172
+ /** Renders one frame at its indentation, with whatever it says about itself. */
1173
+ private renderFrame;
1174
+ /** Renders what a frame warns about: that it recurses, that it is on its way out. */
1175
+ private renderMarkers;
1176
+ /**
1177
+ * Renders a callable's signature, collapsing one that is too long.
1178
+ *
1179
+ * The return type survives the collapse. Which twelve services a constructor
1180
+ * takes is noise at the point where someone is reading a stack; what it hands
1181
+ * back is not.
1182
+ */
1183
+ private renderSignature;
1184
+ /**
1185
+ * Shortens a summary to what fits under an indented frame.
1186
+ *
1187
+ * The opening sentence first, because a comment that runs long here is a
1188
+ * short statement of what the callable does followed by paragraphs of why —
1189
+ * and the first half is the half that orients someone reading a stack. It is
1190
+ * printed whole and unmarked: it is a complete thought, not an elision, and
1191
+ * the frame's `file:line` is already the pointer to the rest.
1192
+ *
1193
+ * Cutting at the character instead throws away a sentence boundary that was
1194
+ * usually right there. Across this repository that happens to a fifth of
1195
+ * printed summaries, whose opening sentences run about 64 characters at the
1196
+ * median and 113 at the very longest.
1197
+ *
1198
+ * A single sentence longer than the limit has no boundary to find, so it is
1199
+ * cut on a word — half a word reads as a typo rather than as an elision —
1200
+ * and marked as cut short.
1201
+ */
1202
+ private shortenSummary;
1203
+ /**
1204
+ * Renders every frame of a stack, the entry point first.
1205
+ *
1206
+ * Takes anything holding a frame list rather than a full `CallStack`: a
1207
+ * depth or entry-point kind is never read here, so a caller with a path
1208
+ * that has neither — an address-centered lookup, for one — does not have to
1209
+ * fabricate one to call this.
1210
+ */
1211
+ renderStackTree(stack: {
1212
+ frames: readonly StackFrame[];
1213
+ }): string;
1214
+ }
1215
+
1216
+ /** Stands in for the workspace root when it is itself a traced project. */
1217
+ export declare const ROOT_PROJECT_LABEL = ".";
1218
+
1219
+ /**
1220
+ * Ends a sentence, when the next one starts.
1221
+ *
1222
+ * A capital after the space is what separates a sentence break from `e.g. the`
1223
+ * or a dotted identifier, neither of which ends anything.
1224
+ */
1225
+ export declare const SENTENCE_END_PATTERN: RegExp;
1226
+
1227
+ /**
1228
+ * Characters of signature a frame prints before it collapses the parameters.
1229
+ *
1230
+ * Most signatures here are short — the median is under twenty characters — but
1231
+ * a NestJS constructor taking a dozen injected services runs past four hundred,
1232
+ * and one of those inside an indented stack destroys the shape that makes the
1233
+ * stack readable at all.
1234
+ */
1235
+ export declare const SIGNATURE_LIMIT = 80;
1236
+
1237
+ /**
1238
+ * How a report draws the stacks it found.
1239
+ *
1240
+ * Narrower than `CallidescopeOutputFormat` on purpose: `json` is a different
1241
+ * report rather than a different drawing of this one, and a renderer that had
1242
+ * to accept it would carry a case it can never answer.
1243
+ */
1244
+ export declare type StackRendering = "diagram" | "tree";
1245
+
1246
+ /**
1247
+ * Characters of documentation prose a printed frame keeps.
1248
+ *
1249
+ * A summary is meant to orient a reader mid-stack, not to replace opening the
1250
+ * file, and a paragraph indented under ten frames is worse than a sentence.
1251
+ * Only the tree is bound by this — the JSON report carries the whole comment.
1252
+ */
1253
+ export declare const SUMMARY_LIMIT = 120;
1254
+
1255
+ /** Introduces the documentation line printed under a frame. */
1256
+ export declare const SUMMARY_PREFIX = "\u21B3";
1257
+
1258
+ /** Arguments for splicing a block between its anchors. */
1259
+ export declare interface SyncAnchoredBlockArguments {
1260
+ readonly check: boolean;
1261
+ readonly content: string;
1262
+ readonly destination: ResolvedCallidescopeMarkdownOutputConfiguration;
1263
+ readonly path: string | undefined;
1264
+ }
1265
+
1266
+ /** Arguments for writing every configured destination. */
1267
+ export declare interface SyncDestinationsArguments {
1268
+ readonly check: boolean;
1269
+ readonly configuration: ResolvedCallidescopeConfiguration;
1270
+ /** The depth and breadth limits each traced project is judged against. */
1271
+ readonly projectLimits: ProjectLimitsLookup;
1272
+ readonly result: CallGraphResult;
1273
+ /**
1274
+ * Workspace-relative root of each project the run was scoped to, keyed by
1275
+ * name. Only these projects have a section published, so a scoped run never
1276
+ * writes into a dependency it merely measured.
1277
+ */
1278
+ readonly startingProjectRoots: ReadonlyMap<string, string>;
1279
+ /**
1280
+ * The written destinations each project declared for itself, by name.
1281
+ *
1282
+ * Every traced project declares a complete configuration, so every one of
1283
+ * them is named here — a project publishing nothing wrote `markdown:
1284
+ * undefined` rather than being absent.
1285
+ */
1286
+ readonly writeByProject: ReadonlyMap<string, ResolvedCallidescopeWriteConfiguration>;
1287
+ }
1288
+
1289
+ /** Arguments for syncing the configured JSON destination. */
1290
+ export declare interface SyncJsonArguments extends BuildReportArguments {
1291
+ readonly check: boolean;
1292
+ }
1293
+
1294
+ /**
1295
+ * Arguments for syncing the configured markdown destination.
1296
+ *
1297
+ * The rendered markdown arrives already built: what a report says belongs to
1298
+ * the report module, and this one only decides where it lands.
1299
+ */
1300
+ export declare interface SyncMarkdownArguments {
1301
+ readonly check: boolean;
1302
+ readonly content: string;
1303
+ readonly destination: ResolvedCallidescopeMarkdownOutputConfiguration;
1304
+ readonly result: CallGraphResult;
1305
+ }
1306
+
1307
+ /** Arguments for writing the destinations one project declared for itself. */
1308
+ export declare interface SyncProjectSectionsArguments {
1309
+ readonly check: boolean;
1310
+ /** The depth and breadth limits each traced project is judged against. */
1311
+ readonly projectLimits: ProjectLimitsLookup;
1312
+ /** The findings for the one project these destinations belong to. */
1313
+ readonly report: ProjectReport;
1314
+ readonly result: CallGraphResult;
1315
+ /** Workspace-relative root every declared path is read against. */
1316
+ readonly root: string;
1317
+ readonly write: ResolvedCallidescopeWriteConfiguration;
1318
+ }
1319
+
1320
+ /** Appended to a summary cut mid-thought. */
1321
+ export declare const TRUNCATION_SUFFIX = "\u2026";
1322
+
1323
+ /**
1324
+ * Renders what a whole run says about the projects in it, rather than about
1325
+ * one callable or one stack.
1326
+ *
1327
+ * Its own service because it answers a question the rest of the report cannot.
1328
+ * Every other section names callables — a stack, a spread, a wide callable —
1329
+ * and a workspace holding fifty projects is not readable as a list of
1330
+ * callables. These two sections are the index and the scoreboard: which
1331
+ * project holds what, and how much room each has left before its own limit
1332
+ * stops it.
1333
+ *
1334
+ * Both are keyed on a project's own resolved limit rather than one workspace
1335
+ * number. A limit pinned by the single worst stack anywhere in a repository
1336
+ * gates nothing for the projects nowhere near it, which is the whole reason
1337
+ * limits resolve per project — so a report that showed one number would be
1338
+ * describing a model the tool no longer has.
1339
+ */
1340
+ export declare class WorkspaceReportService {
1341
+ constructor();
1342
+ /**
1343
+ * Sorts a project's headroom into the bucket the scoreboard counts it under.
1344
+ *
1345
+ * A project measuring nothing is its own bucket rather than a large
1346
+ * headroom, and that distinction is the point of having the scoreboard at
1347
+ * all: a project whose deepest stack is zero has every frame of its limit
1348
+ * unused, which reads as the safest row in the table while actually being
1349
+ * the one row where the limit gates nothing. Bucketing it by `limit - 0`
1350
+ * would file the least-gated projects among the healthiest.
1351
+ */
1352
+ private bucketFor;
1353
+ /** The widest single callable a project declares, or zero when it has none. */
1354
+ private widestBreadth;
1355
+ /**
1356
+ * Reads each project's report and its resolved limits into one row.
1357
+ *
1358
+ * Public so both sections render from the same rows rather than each
1359
+ * deriving them: the scoreboard counts exactly the rows the index lists, and
1360
+ * two derivations of "headroom" that could disagree would be worse than one
1361
+ * that is merely wrong.
1362
+ */
1363
+ buildRows(args: RenderProjectIndexArguments): ProjectIndexRow[];
1364
+ /** The limits one project resolved to, or the workspace's when unlisted. */
1365
+ limitsFor(args: {
1366
+ limits: ProjectLimitsLookup;
1367
+ projectName: string;
1368
+ }): ProjectLimits;
1369
+ /**
1370
+ * Counts how many projects sit in each headroom bucket.
1371
+ *
1372
+ * The index says what every project holds; this says what the set of them
1373
+ * adds up to, which is the number a ratchet is actually lowered against. A
1374
+ * bucket nothing falls into is still printed, because "nothing is over its
1375
+ * limit" is the row a reader is looking for and an absent row cannot say it.
1376
+ */
1377
+ renderHeadroom(rows: readonly ProjectIndexRow[]): string;
1378
+ /**
1379
+ * Renders one row per project: what it measured, and what it is held to.
1380
+ *
1381
+ * `Limit` is a bare number now. It used to carry `declared` or `inherited`
1382
+ * beside it, and with every traced project's configuration complete only one
1383
+ * of those two is reachable — a column with one value in every row is worse
1384
+ * than no column.
1385
+ */
1386
+ renderProjectIndex(args: RenderProjectIndexArguments): string;
1387
+ }
1388
+
1389
+ /** Arguments for wrapping content in the configured anchors. */
1390
+ export declare interface WrapInAnchorsArguments {
1391
+ readonly content: string;
1392
+ readonly destination: ResolvedCallidescopeMarkdownOutputConfiguration;
1393
+ }
1394
+
1395
+ /**
1396
+ * NestJS module that wires the service writing a run to its destinations.
1397
+ */
1398
+ export declare class WriteDestinationsModule {
1399
+ }
1400
+
1401
+ /**
1402
+ * Writes a finished run to every destination it was configured with.
1403
+ *
1404
+ * Its own service rather than more of the command, because the two answer
1405
+ * different questions: the command decides whether a run may write at all, and
1406
+ * this decides where what it found lands. There are two answers to that second
1407
+ * question — the workspace's own declarations and each project's — and keeping
1408
+ * them side by side is what stops one writing over the other.
1409
+ */
1410
+ export declare class WriteDestinationsService {
1411
+ private readonly markdownReportService;
1412
+ private readonly outputJsonService;
1413
+ private readonly outputMarkdownService;
1414
+ constructor(markdownReportService: MarkdownReportService, outputJsonService: OutputJsonService, outputMarkdownService: OutputMarkdownService);
1415
+ /**
1416
+ * Writes the destinations each project declared for itself, returning the
1417
+ * stale ones.
1418
+ *
1419
+ * A path is read relative to that project's own root, which is what keeps a
1420
+ * project's declaration about its own documents rather than about the
1421
+ * repository's — a project cannot write into a sibling by declaring one.
1422
+ *
1423
+ * Only the projects a run was scoped to are published: a dependency measured
1424
+ * through the closure has its own run to publish it, and this one was never
1425
+ * pointed at it.
1426
+ */
1427
+ private syncProjectDestinations;
1428
+ /** Writes one project's declared destinations, returning the stale ones. */
1429
+ private syncProjectSections;
1430
+ /** Writes every configured destination, returning the stale ones. */
1431
+ syncDestinations(args: SyncDestinationsArguments): string[];
1432
+ }
1433
+
1434
+ export { }