@codometer/cli 0.0.1

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,649 @@
1
+ import { ChangesService } from '@codometer/output';
2
+ import { CommandRunner } from 'nest-commander';
3
+ import { ConfigurationListingService } from '@codometer/output';
4
+ import { ConfigurationService } from '@codometer/configuration';
5
+ import { ConsoleLogger } from '@nestjs/common';
6
+ import { DeliveryService } from '@codometer/output';
7
+ import { DestinationsService } from '@codometer/output';
8
+ import { DocumentsService } from '@codometer/output';
9
+ import { MeasureCommandOptions } from '@codometer/configuration';
10
+ import { MeasureFormat } from '@codometer/configuration';
11
+ import { MeasurementResult } from '@codometer/measurement';
12
+ import { MeasureService } from '@codometer/measurement';
13
+ import pino from 'pino';
14
+ import { RenderConfigurationService } from '@codometer/output';
15
+ import { RenderService } from '@codometer/output';
16
+ import { ReportService } from '@codometer/output';
17
+ import { ResolvedCodometerConfiguration } from '@codometer/configuration';
18
+ import { ResolvedMarkdownDestination } from '@codometer/output';
19
+ import { RunDestinations } from '@codometer/output';
20
+ import { RunMode } from '@codometer/configuration';
21
+
22
+ /**
23
+ * CLI entry point for the pull request change report.
24
+ *
25
+ * Diffs every project's codometer report against a baseline snapshot and
26
+ * puts the result wherever it was asked for. Only ever reads and writes a
27
+ * local markdown file — handing that file to a pull request, an issue, or a
28
+ * wiki is the caller's job, which keeps this command independent of any one
29
+ * forge's API.
30
+ */
31
+ export declare class ChangesCommand extends CommandRunner {
32
+ private readonly configurationService;
33
+ private readonly changesService;
34
+ private readonly documentsService;
35
+ private readonly renderService;
36
+ private readonly logger;
37
+ constructor(configurationService: ConfigurationService, changesService: ChangesService, documentsService: DocumentsService, renderService: RenderService, logger: LoggerService);
38
+ /** Parse the baseline directory holding a snapshot of the reports. */
39
+ parseBaseline(value: unknown): string | undefined;
40
+ /** Parse the run URL the baseline came from, linked from the summary. */
41
+ parseBaselineUrl(value: unknown): string | undefined;
42
+ /** Parse the directory to look for codometer reports in. */
43
+ parseDirectory(value: unknown): string;
44
+ /** Parse the markdown document the report is spliced into. */
45
+ parseMarkdown(value: unknown): string | undefined;
46
+ /** Parse the file the report is written to on its own. */
47
+ parseOutput(value: unknown): string | undefined;
48
+ /** Diffs every project's report against the baseline, and emits the result. */
49
+ run(_passedParameters: string[], options: ChangesCommandOptions): Promise<void>;
50
+ }
51
+
52
+ /**
53
+ * Options accepted by the `changes` command.
54
+ *
55
+ * Typed loosely because commander skips an option's parser when the flag
56
+ * arrives without a value, handing the command `true` instead of text.
57
+ */
58
+ declare interface ChangesCommandOptions {
59
+ baseline?: unknown;
60
+ baselineUrl?: unknown;
61
+ directory?: unknown;
62
+ markdown?: unknown;
63
+ output?: unknown;
64
+ }
65
+
66
+ /** Wires the `changes` command that reports codometer's diff against main. */
67
+ export declare class ChangesModule {
68
+ }
69
+
70
+ /**
71
+ * CLI entry point for listing what a repository configures.
72
+ *
73
+ * Reports configuration and never measurement: no build is required, nothing
74
+ * is compressed, and no limit is evaluated. That separation is the point — a
75
+ * repository whose limits live one per project has no single place left to
76
+ * read them as a set, and this is that place, without waiting on the builds
77
+ * a measurement would need.
78
+ */
79
+ export declare class ConfigurationCommand extends CommandRunner {
80
+ private readonly codometerConfigurationService;
81
+ private readonly configurationService;
82
+ private readonly renderConfigurationService;
83
+ private readonly logger;
84
+ constructor(codometerConfigurationService: ConfigurationService, configurationService: ConfigurationListingService, renderConfigurationService: RenderConfigurationService, logger: LoggerService);
85
+ /**
86
+ * Parse the configuration file answering for the walk root.
87
+ *
88
+ * The exclusions the walk uses come from whatever configuration answers for
89
+ * the directory being listed, and a workspace stating its shared object
90
+ * somewhere every project spreads it from has nothing at its own root for
91
+ * the upward search to find — so it names that file here, the same way its
92
+ * measurement target already does.
93
+ */
94
+ parseConfig(value: string | undefined): string | undefined;
95
+ /** Parse the directory to look for configuration files beneath. */
96
+ parseDirectory(value: unknown): string;
97
+ /** Parse the output format the listing is rendered in. */
98
+ parseFormat(value: unknown): string;
99
+ /** Parse whether to list only the limits. */
100
+ parseLimits(): boolean;
101
+ /**
102
+ * Lists what the tree beneath the given directory configures.
103
+ *
104
+ * Writes to standard output rather than a file: the listing is something a
105
+ * reader looks at or pipes onward, and unlike a measurement it has no report
106
+ * anything else consumes.
107
+ */
108
+ run(_passedParameters: string[], options?: ConfigurationCommandOptions): Promise<void>;
109
+ }
110
+
111
+ /** Options accepted by the configuration command. */
112
+ declare interface ConfigurationCommandOptions {
113
+ config?: string | undefined;
114
+ directory?: string | undefined;
115
+ format?: string | undefined;
116
+ limits?: boolean | undefined;
117
+ }
118
+
119
+ /**
120
+ * Wires the command that lists what a repository configures.
121
+ *
122
+ * Nothing but wiring. Reading the tree and rendering what it found belong to
123
+ * `@codometer/output`, which is where every other render target already
124
+ * lives; this module hands the command the two it needs.
125
+ */
126
+ export declare class ConfigurationModule {
127
+ }
128
+
129
+ /**
130
+ * Structured values that belong beside a log line rather than inside it.
131
+ *
132
+ * Counts, percentages, and durations are the values that change on every
133
+ * occurrence, so they are carried as fields: the message stays constant and
134
+ * groupable in telemetry, and the numbers stay queryable instead of having to
135
+ * be parsed back out of prose.
136
+ *
137
+ * The named members are the recurring ones; the index signature keeps the
138
+ * argument open for whatever a given call site needs to attach.
139
+ */
140
+ declare interface LogData {
141
+ [key: string]: unknown;
142
+ /** How many things the operation handled. */
143
+ count?: number;
144
+ /** Wall-clock milliseconds the operation took. */
145
+ durationMs?: number;
146
+ /** Completion between 0 and 100. */
147
+ percent?: number;
148
+ /** How many things the operation set out to handle. */
149
+ total?: number;
150
+ }
151
+
152
+ /**
153
+ * Transient-scoped logger so each injecting class gets its own instance.
154
+ * Each consumer calls `setContext(ClassName.name)` to tag every log line
155
+ * with the originating class. Backed by pino for structured JSON output in
156
+ * production and human-readable pretty-print in development.
157
+ *
158
+ * Messages follow one grammar: an emoji naming the subject, a verb in present
159
+ * progressive or past tense, then the object. Values that vary per call —
160
+ * counts, percentages, durations — go in the `data` argument rather than the
161
+ * message, so the message stays constant enough for telemetry to group on.
162
+ *
163
+ * ```ts
164
+ * this.logger.info("📥 Downloading CSEL sources", undefined, { total: 428 });
165
+ * this.logger.info("📥 Downloaded CSEL sources", undefined, { count: 412 });
166
+ * ```
167
+ */
168
+ declare @Injectable({ scope: Scope.TRANSIENT })
169
+ class LoggerService extends ConsoleLogger {
170
+ // 🏗 Dependency Injection
171
+
172
+ constructor() {
173
+ super();
174
+ }
175
+
176
+ // 🔐 Private Fields
177
+
178
+ private static readonly isProduction =
179
+ process.env["NODE_ENV"] === "production";
180
+
181
+ /**
182
+ * Built on first use, not when this file is evaluated.
183
+ *
184
+ * A destination fixed at import time could only ever be chosen by this
185
+ * package, since every consumer's own code runs after its imports.
186
+ */
187
+ private static rootLogger: pino.Logger | undefined;
188
+
189
+ /** Whether lines go to standard error instead of standard output. */
190
+ private static writesToStandardError = false;
191
+
192
+ private child: pino.Logger = LoggerService.root;
193
+
194
+ // 🔑 Public Fields
195
+
196
+ // 🔏 Private Methods
197
+
198
+ /** Build the pino instance for production or local development output. */
199
+ private static createRootLogger(): pino.Logger {
200
+ const level = process.env["LOG_LEVEL"] ?? "info";
201
+
202
+ if (LoggerService.isProduction) {
203
+ return LoggerService.writesToStandardError
204
+ ? pino({ level }, pino.destination(STANDARD_ERROR_DESCRIPTOR))
205
+ : pino({ level });
206
+ }
207
+
208
+ return pino({
209
+ level,
210
+ transport: {
211
+ options: {
212
+ colorize: true,
213
+ destination: LoggerService.writesToStandardError
214
+ ? STANDARD_ERROR_DESCRIPTOR
215
+ : STANDARD_OUTPUT_DESCRIPTOR,
216
+ // The emoji is a field, not part of the message, so the console can
217
+ // show it while telemetry stores unadorned prose. `ignore` then keeps
218
+ // it from being printed a second time in the trailing object.
219
+ ignore: "pid,hostname,emoji",
220
+ messageFormat: "{emoji} {msg}",
221
+ singleLine: true,
222
+ },
223
+ target: "pino-pretty",
224
+ },
225
+ });
226
+ }
227
+
228
+ /**
229
+ * Sends every subsequent line to standard error instead of standard output.
230
+ *
231
+ * For a command-line application whose standard output *is* its result. A log
232
+ * line sharing that stream is not a diagnostic beside the data, it is a
233
+ * corruption of it. Call it before anything logs — the first statement of the
234
+ * application's bootstrap.
235
+ *
236
+ * A call after the first line warns and changes nothing: the destination is
237
+ * fixed when the pino instance is built, and tearing down a transport
238
+ * somebody is writing through would be worse than refusing. The warning is
239
+ * the point — silently leaving the lines on standard output is how a caller
240
+ * would ship a corrupted pipe without ever being told.
241
+ */
242
+ static logToStandardError(): void {
243
+ if (LoggerService.rootLogger !== undefined) {
244
+ process.emitWarning(
245
+ "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.",
246
+ );
247
+ return;
248
+ }
249
+
250
+ LoggerService.writesToStandardError = true;
251
+ }
252
+
253
+ /**
254
+ * Fails a malformed message in development, and never in production.
255
+ *
256
+ * A logger that throws in production turns an observability call into an
257
+ * outage, so the check runs only where a developer is present to fix it.
258
+ */
259
+ private assertConventionalMessage(args: {
260
+ context: string | undefined;
261
+ parsed: ParsedLogMessage;
262
+ }): void {
263
+ if (
264
+ LoggerService.isProduction ||
265
+ this.shouldSkipConventionalMessageValidation(args.context)
266
+ ) {
267
+ return;
268
+ }
269
+
270
+ const violation = this.getConventionalMessageViolation(args.parsed);
271
+
272
+ if (violation !== undefined) {
273
+ throw new Error(violation);
274
+ }
275
+ }
276
+
277
+ /** Assembles the object pino merges into the line. */
278
+ private buildBindings(args: {
279
+ context: string | undefined;
280
+ data: LogData | undefined;
281
+ parsed: ParsedLogMessage;
282
+ }): Record<string, unknown> {
283
+ this.assertConventionalMessage({
284
+ context: args.context,
285
+ parsed: args.parsed,
286
+ });
287
+
288
+ return {
289
+ ...args.data,
290
+ context: args.context,
291
+ // Telemetry gets prose; only the console-bound transport reads this.
292
+ ...(LoggerService.isProduction ? {} : { emoji: args.parsed.emoji }),
293
+ };
294
+ }
295
+
296
+ /** Returns a human-readable explanation when the message format is invalid. */
297
+ private getConventionalMessageViolation(
298
+ parsed: ParsedLogMessage,
299
+ ): string | undefined {
300
+ const emoji = parsed.emoji;
301
+ const text = parsed.text;
302
+
303
+ if (emoji === undefined) {
304
+ return `Log message must start with an emoji naming its subject, then a verb: "${text}"`;
305
+ }
306
+
307
+ const firstWord = FIRST_WORD_PATTERN.exec(text)?.[1];
308
+
309
+ if (firstWord === undefined || !this.isConventionalVerb(firstWord)) {
310
+ return `Log message must begin with a verb in present progressive or past tense, got "${firstWord ?? ""}": "${emoji} ${text}"`;
311
+ }
312
+
313
+ return undefined;
314
+ }
315
+
316
+ /**
317
+ * Whether a word is a verb in one of the two tenses the convention allows.
318
+ *
319
+ * Present progressive means the operation is under way; past means it
320
+ * finished. Regular morphology covers both, so a new verb needs no
321
+ * registration anywhere — only irregular pasts are enumerated.
322
+ */
323
+ private isConventionalVerb(word: string): boolean {
324
+ const lowercased = word.toLowerCase();
325
+
326
+ return (
327
+ lowercased.endsWith("ing") ||
328
+ lowercased.endsWith("ed") ||
329
+ IRREGULAR_PAST_VERBS.has(lowercased)
330
+ );
331
+ }
332
+
333
+ /** Splits a leading emoji off a message, leaving prose behind. */
334
+ private parseMessage(message: unknown): ParsedLogMessage {
335
+ const text = String(message);
336
+ const match = LEADING_EMOJI_PATTERN.exec(text);
337
+ const emoji = match?.[1];
338
+
339
+ return emoji === undefined
340
+ ? { emoji: undefined, text }
341
+ : { emoji, text: text.slice(match?.[0].length) };
342
+ }
343
+
344
+ /** Whether a context is intentionally exempt from the validation rule. */
345
+ private shouldSkipConventionalMessageValidation(
346
+ context: string | undefined,
347
+ ): boolean {
348
+ return context !== undefined && UNVALIDATED_LOG_CONTEXTS.has(context);
349
+ }
350
+
351
+ // 🌎 Public Methods
352
+
353
+ /** The pino instance every logger's child is taken from. */
354
+ private static get root(): pino.Logger {
355
+ LoggerService.rootLogger ??= LoggerService.createRootLogger();
356
+
357
+ return LoggerService.rootLogger;
358
+ }
359
+
360
+ /** Normalizes unknown errors into a stable message and timestamped log line. */
361
+ buildErrorLogEntry(
362
+ context: string,
363
+ error: unknown,
364
+ ): { errorMessage: string; logLine: string } {
365
+ const errorMessage =
366
+ error instanceof Error ? error.stack || error.message : String(error);
367
+
368
+ return {
369
+ errorMessage,
370
+ logLine: `[${new Date().toISOString()}] ${context}: ${errorMessage}\n`,
371
+ };
372
+ }
373
+
374
+ /** Builds a timestamped output log file path and ensures the output directory exists. */
375
+ createTimestampedOutputLogFilePath(filePrefix: string): string {
376
+ const outputDirectory = path.join(process.cwd(), "output");
377
+ if (!existsSync(outputDirectory)) {
378
+ mkdirSync(outputDirectory, { recursive: true });
379
+ }
380
+
381
+ return path.join(
382
+ outputDirectory,
383
+ `${filePrefix}-${new Date().toISOString().replaceAll(/[:.]/g, "-")}.log`,
384
+ );
385
+ }
386
+
387
+ /** Logs a debug message at the `debug` level. */
388
+ override debug(message: unknown, context?: string, data?: LogData): void {
389
+ const parsed = this.parseMessage(message);
390
+ this.child.debug(
391
+ this.buildBindings({ context: context ?? this.context, data, parsed }),
392
+ parsed.text,
393
+ );
394
+ }
395
+
396
+ /**
397
+ * Logs an error message at the `error` level, optionally including a stack trace.
398
+ *
399
+ * `ConsoleLogger.error` spends a third slot on a context string that the
400
+ * other levels do not have, so this one accepts either: a string keeps
401
+ * NestJS's meaning, an object is structured data like everywhere else.
402
+ */
403
+ override error(
404
+ message: unknown,
405
+ stackOrContext?: string,
406
+ contextOrData?: LogData | string,
407
+ ): void {
408
+ const parsed = this.parseMessage(message);
409
+ const data = typeof contextOrData === "object" ? contextOrData : undefined;
410
+ const context =
411
+ typeof contextOrData === "string" ? contextOrData : this.context;
412
+
413
+ this.child.error(
414
+ {
415
+ ...this.buildBindings({ context, data, parsed }),
416
+ stack: stackOrContext,
417
+ },
418
+ parsed.text,
419
+ );
420
+ }
421
+
422
+ /** Logs an informational message at the `info` level. */
423
+ info(message: unknown, context?: string, data?: LogData): void {
424
+ const parsed = this.parseMessage(message);
425
+ this.child.info(
426
+ this.buildBindings({ context: context ?? this.context, data, parsed }),
427
+ parsed.text,
428
+ );
429
+ }
430
+
431
+ /**
432
+ * Logs an informational message at the `info` level.
433
+ *
434
+ * NestJS and `nest-commander` call this method directly as part of the
435
+ * framework's own `LoggerService` contract, so it must keep working
436
+ * exactly as before. Application code should call `info` instead — the
437
+ * same behavior under a name that says what level it logs at.
438
+ */
439
+ override log(message: unknown, context?: string, data?: LogData): void {
440
+ this.info(message, context, data);
441
+ }
442
+
443
+ /** Sets the context label included in every subsequent log line. */
444
+ override setContext(context: string): void {
445
+ super.setContext(context);
446
+ this.child = LoggerService.root.child({ context });
447
+ }
448
+
449
+ /** Logs a verbose message at the `trace` level. */
450
+ override verbose(message: unknown, context?: string, data?: LogData): void {
451
+ const parsed = this.parseMessage(message);
452
+ this.child.trace(
453
+ this.buildBindings({ context: context ?? this.context, data, parsed }),
454
+ parsed.text,
455
+ );
456
+ }
457
+
458
+ /** Logs a warning message at the `warn` level. */
459
+ override warn(message: unknown, context?: string, data?: LogData): void {
460
+ const parsed = this.parseMessage(message);
461
+ this.child.warn(
462
+ this.buildBindings({ context: context ?? this.context, data, parsed }),
463
+ parsed.text,
464
+ );
465
+ }
466
+ }
467
+
468
+ /**
469
+ * Root NestJS application module.
470
+ */
471
+ export declare class MainModule {
472
+ }
473
+
474
+ /**
475
+ * CLI entry point for the repository measurement workflow.
476
+ */
477
+ export declare class MeasureCommand extends CommandRunner {
478
+ private readonly configurationService;
479
+ private readonly measureService;
480
+ private readonly deliveryService;
481
+ private readonly reportService;
482
+ private readonly destinationsService;
483
+ private readonly logger;
484
+ constructor(configurationService: ConfigurationService, measureService: MeasureService, deliveryService: DeliveryService, reportService: ReportService, destinationsService: DestinationsService, logger: LoggerService);
485
+ /**
486
+ * Say which files were left out because codometer writes them.
487
+ *
488
+ * Announced rather than left to be noticed. A file missing from the counts
489
+ * with no explanation reads as a measurement bug, and the last repository to
490
+ * hit this wrote the exclusion into its ignore file by hand.
491
+ */
492
+ private announceOutputPaths;
493
+ /**
494
+ * Replaces every configured input with the globs `--inputs` named, when it
495
+ * was passed.
496
+ *
497
+ * Not a filter over configured inputs: the run measures exactly the globs
498
+ * given, running the `language` analysis over them, with no other input —
499
+ * not even the built-in `codebase` one — active.
500
+ */
501
+ private applyInputsOverride;
502
+ /**
503
+ * Read the configuration, or say why it could not be read.
504
+ *
505
+ * A configuration nothing can parse fails the run rather than being taken as
506
+ * an empty one, and so does a directory with no configuration file anywhere
507
+ * above it: `format` is required with no code-level fallback, so an absent
508
+ * file fails on the missing `format` exactly as a file that forgot to write
509
+ * one does. A shared default object, spread by each project's own
510
+ * `codometer.config.ts`, is what states it once for a workspace.
511
+ */
512
+ private readConfiguration;
513
+ /** Says the command line could not be made sense of, and fails the run. */
514
+ private rejectCommandLine;
515
+ /** Report every breached limit, and say whether one of them fails the run. */
516
+ private reportBreaches;
517
+ /**
518
+ * Report whatever the run could not do, and say whether it fails the run.
519
+ *
520
+ * A failure is neither staleness nor a breach: it is the run not having
521
+ * finished. It stops any run that produces or gates an output, because a
522
+ * report built from a partial measurement is not one to publish and a gate
523
+ * that could not be evaluated has not been passed. A run that does neither
524
+ * says so and exits clean, exactly as its flags promised.
525
+ */
526
+ private reportFailures;
527
+ /** Weigh every finding, set the exit code once, and say the run is done. */
528
+ private reportFindings;
529
+ /** Report every stale destination, and say whether that fails the run. */
530
+ private reportStaleness;
531
+ /**
532
+ * Reads the flags into everything the rest of the run needs: the mode, the
533
+ * resolved configuration, what to print, and where each output goes.
534
+ *
535
+ * `undefined` when the run cannot proceed at all — a rejected command line,
536
+ * or a configuration that failed to load — with the refusal and exit code
537
+ * already reported, so nothing after this has to check twice.
538
+ */
539
+ private resolveRunPlan;
540
+ /**
541
+ * Resolve the directory the run measures, and announce that it started.
542
+ *
543
+ * Always the process's working directory: unlike every other flag, there is
544
+ * no per-run override for it — a run measures where it was invoked.
545
+ */
546
+ private resolveWorkingDirectory;
547
+ /**
548
+ * Parse the set of things the run fails on from command-line input.
549
+ *
550
+ * The parser runs only when `--check` carries a value, so anything reaching
551
+ * it is a written set. A `--check` with no value never arrives here at all
552
+ * and is refused later, because a set with nothing in it is indistinguishable
553
+ * from the flag having been left off — which is how check mode once silently
554
+ * became write mode.
555
+ */
556
+ parseCheck(value: string): string;
557
+ /**
558
+ * Parse the optional configuration path from command-line input.
559
+ */
560
+ parseConfig(value: string | undefined): string | undefined;
561
+ /**
562
+ * Parse what the run prints from command-line input.
563
+ *
564
+ * Says nothing about which files are written — that is each `--output-*`
565
+ * flag's job. Omitted, it reads the resolved configuration's own required
566
+ * `format` field rather than inferring one from which other flags are
567
+ * present.
568
+ */
569
+ parseFormat(value: string): string;
570
+ /**
571
+ * Parse the glob array that replaces every configured input, from
572
+ * command-line input.
573
+ *
574
+ * Each value commander hands the parser is one glob; they accumulate into
575
+ * one array across the whole `--inputs` invocation.
576
+ */
577
+ parseInputs(value: string, previous?: string[]): string[];
578
+ /**
579
+ * Parse the report's destination from command-line input.
580
+ *
581
+ * `true` when the flag was passed with no value, asking this run to write
582
+ * wherever the resolved configuration's own JSON output says to; a string
583
+ * when a path was given, which is used for this destination alone.
584
+ */
585
+ parseOutputJson(value: string | true): string | true;
586
+ /**
587
+ * Parse the markdown badge block's destination from command-line input.
588
+ *
589
+ * `true` when the flag was passed with no value, asking this run to write
590
+ * wherever the resolved configuration's own markdown output says to; a
591
+ * string when a path was given, which is used for this destination alone.
592
+ */
593
+ parseOutputMarkdown(value: string | true): string | true;
594
+ /**
595
+ * Measure the repository and produce every resolved output.
596
+ *
597
+ * Flags are independent: `--output-json`/`--output-markdown` each write
598
+ * their own destination, `--check reports` fails on a stale report,
599
+ * `--check limits` fails on a breached limit, `--format` prints, and none of
600
+ * them turns another on. Every output is produced before any finding is
601
+ * weighed, so a run that writes and gates leaves the report behind even
602
+ * when the gate trips.
603
+ */
604
+ run(_passedParameters: string[], options: MeasureCommandOptions): Promise<void>;
605
+ }
606
+
607
+ /**
608
+ * NestJS module that wires the measure command to the layers it composes.
609
+ *
610
+ * Nothing but wiring: the configuration layer reads the command line and the
611
+ * configuration file, the measurement layer counts, and the output layer
612
+ * resolves destinations, builds the report, and delivers it. The command
613
+ * calls them in order and sets an exit code.
614
+ */
615
+ export declare class MeasureModule {
616
+ }
617
+
618
+ /** A message split into the emoji the console shows and the prose telemetry stores. */
619
+ declare interface ParsedLogMessage {
620
+ emoji: string | undefined;
621
+ text: string;
622
+ }
623
+
624
+ /** Arguments accepted when weighing what a run found. */
625
+ export declare interface ReportFindingsArguments {
626
+ measurement: MeasurementResult;
627
+ mode: RunMode;
628
+ /** Destinations found not to hold the current output. Only ever non-empty
629
+ * when the run was comparing, since nothing else reads a destination. */
630
+ stalePaths: string[];
631
+ }
632
+
633
+ /**
634
+ * Everything a run needs once its command line has been made sense of: the
635
+ * resolved configuration, what to print, where each output goes, and what
636
+ * the run does with what it measures.
637
+ */
638
+ export declare interface RunPlan {
639
+ configuration: ResolvedCodometerConfiguration;
640
+ /** Where the console's own badge block comes from, resolved independently
641
+ * of `destinations.markdown` so it never depends on which `--output-*` flag
642
+ * was passed. */
643
+ consoleMarkdown: ResolvedMarkdownDestination | undefined;
644
+ destinations: RunDestinations;
645
+ format: MeasureFormat | undefined;
646
+ mode: RunMode;
647
+ }
648
+
649
+ export { }
@@ -0,0 +1,2 @@
1
+ import { a as e, i as t, n, o as r, r as i, s as a, t as o } from "../main.module-_z85wgY7.js";
2
+ export { a as ChangesCommand, r as ChangesModule, e as ConfigurationCommand, t as ConfigurationModule, o as MainModule, i as MeasureCommand, n as MeasureModule };
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ import { c as e, t } from "../main.module-_z85wgY7.js";
3
+ import { CommandFactory as n } from "nest-commander";
4
+ import "reflect-metadata";
5
+ //#region packages/ic-suite/codometer/codometer-cli/src/main.utilities.ts
6
+ function r(e) {
7
+ let t = /* @__PURE__ */ new Set([
8
+ "changes",
9
+ "configuration",
10
+ "help",
11
+ "measure"
12
+ ]), n = /* @__PURE__ */ new Set(["--help", "-h"]), [r] = e;
13
+ return r !== void 0 && (t.has(r) || n.has(r)) ? [...e] : ["measure", ...e];
14
+ }
15
+ //#endregion
16
+ //#region packages/ic-suite/codometer/codometer-cli/src/main.ts
17
+ async function i() {
18
+ e.logToStandardError();
19
+ let i = new e();
20
+ i.setContext("CommandFactory"), process.argv = [...process.argv.slice(0, 2), ...r(process.argv.slice(2))], await n.run(t, {
21
+ bufferLogs: !0,
22
+ logger: i
23
+ });
24
+ }
25
+ i();
26
+ //#endregion