react-native-doctor-ci 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,736 @@
1
+ /**
2
+ * Tool version, kept in a leaf module so reporters can embed it without
3
+ * importing the package entry point (which would create an import cycle).
4
+ * @packageDocumentation
5
+ */
6
+ /**
7
+ * The rn-doctor tool version. Kept in sync with package.json by release-it.
8
+ */
9
+ declare const VERSION = "0.0.0";
10
+
11
+ /**
12
+ * Core types for the enrichment engine.
13
+ * @packageDocumentation
14
+ */
15
+ /**
16
+ * A value that may be known or unknown, with a reason if not.
17
+ * Used to represent partial/failed lookups without lying about them.
18
+ *
19
+ * @typeParam T - The value type when known.
20
+ */
21
+ type Signal<T> = {
22
+ readonly known: true;
23
+ readonly value: T;
24
+ readonly source: string;
25
+ } | {
26
+ readonly known: false;
27
+ readonly reason: UnknownReason;
28
+ };
29
+ /**
30
+ * Reasons a signal could not be resolved.
31
+ * Distinguishes between durable (expected, won't retry) and transient (might retry).
32
+ */
33
+ type UnknownReason = "not-in-directory" | "no-repo-url" | "no-github-token" | "github-rate-limited" | "github-error" | "npm-not-found" | "npm-lookup-failed" | "npm-search-no-match";
34
+ /**
35
+ * New Architecture support tier, as determined by RN Directory and codegen hints.
36
+ */
37
+ type NewArchTier = "supported" | "unsupported" | "passWithNote" | "unknown";
38
+ /**
39
+ * Reason(s) a package is classified as React Native native.
40
+ */
41
+ type RnNativeReason = "directory-listed" | "peer-dependency" | "native-files-hint";
42
+ /**
43
+ * A warning about data loss or degradation during enrichment.
44
+ * Per-dependency when narrowly scoped; run-level when absent `.dependency`.
45
+ */
46
+ interface EnrichmentWarning {
47
+ readonly dependency?: string;
48
+ readonly source: "npm" | "directory" | "github" | "cache" | "git" | "workspaces";
49
+ readonly message: string;
50
+ }
51
+ /**
52
+ * Enriched metadata for a single dependency, gathered from npm, RN Directory, and GitHub.
53
+ * Carries raw signals; Phase 2's policy engine turns these into pass/warn/error verdicts.
54
+ */
55
+ interface EnrichedDependency {
56
+ readonly name: string;
57
+ readonly warnings: readonly EnrichmentWarning[];
58
+ readonly npm: {
59
+ readonly found: boolean;
60
+ readonly latestVersion: string | null;
61
+ readonly deprecated: Signal<{
62
+ readonly deprecated: boolean;
63
+ readonly message: string | null;
64
+ }>;
65
+ readonly hasCodegenConfig: Signal<boolean>;
66
+ readonly hasReactNativePeerDep: Signal<boolean>;
67
+ readonly hasNativeDirsHint: Signal<boolean>;
68
+ readonly repositoryUrl: string | null;
69
+ };
70
+ readonly directory: {
71
+ readonly listed: boolean;
72
+ readonly unmaintained: boolean;
73
+ readonly newArchitectureRaw: "new-arch-only" | "supported" | "unsupported" | "untested" | null;
74
+ readonly githubUrl: string | null;
75
+ readonly lastPublishedAt: string | null;
76
+ readonly githubArchived: boolean | null;
77
+ readonly githubPushedAt: string | null;
78
+ readonly matchingScoreModifiers: readonly string[];
79
+ };
80
+ readonly github: {
81
+ readonly archived: Signal<boolean>;
82
+ readonly pushedAt: Signal<string>;
83
+ readonly repoUrl: string | null;
84
+ readonly source: "github-api" | "directory-fallback" | null;
85
+ };
86
+ readonly isRnNative: boolean;
87
+ readonly rnNativeReasons: readonly RnNativeReason[];
88
+ readonly newArch: {
89
+ readonly tier: NewArchTier;
90
+ readonly evidence: {
91
+ readonly directoryVerdict: string | null;
92
+ readonly hasCodegenConfig: boolean | null;
93
+ };
94
+ };
95
+ readonly lastPublish: Signal<{
96
+ readonly date: string;
97
+ }>;
98
+ }
99
+ /**
100
+ * Enrichment result: the enriched dependencies plus run-level warnings.
101
+ */
102
+ interface EnrichmentResult {
103
+ readonly dependencies: readonly EnrichedDependency[];
104
+ readonly warnings: readonly EnrichmentWarning[];
105
+ }
106
+ /**
107
+ * Options for the enrichment run.
108
+ */
109
+ interface EnrichmentOptions {
110
+ readonly noCache?: boolean;
111
+ readonly githubToken?: string;
112
+ readonly concurrency?: number;
113
+ readonly cacheDir?: string;
114
+ }
115
+
116
+ /**
117
+ * Enrichment engine orchestrator.
118
+ * Coordinates data gathering from npm, RN Directory, and GitHub.
119
+ * @packageDocumentation
120
+ */
121
+
122
+ /**
123
+ * Enrich multiple dependencies from npm, RN Directory, and GitHub.
124
+ */
125
+ declare function enrichDependencies(names: readonly string[], options?: EnrichmentOptions): Promise<EnrichmentResult>;
126
+
127
+ /**
128
+ * Policy engine: turns enriched dependency records into findings.
129
+ *
130
+ * Pure and deterministic — no I/O, no clock reads unless the caller omits
131
+ * `options.now`. Reporters (Phase 3) consume the `Finding[]` output.
132
+ *
133
+ * @packageDocumentation
134
+ */
135
+
136
+ /**
137
+ * Identifier of a policy rule; also the `rule` field on emitted findings.
138
+ */
139
+ type RuleId = "newArchitecture" | "newArchUnknown" | "lastPublish" | "githubArchived" | "npmDeprecated" | "directoryUnmaintained";
140
+ /**
141
+ * Severity a rule can be configured to fire at. `off` disables the rule.
142
+ */
143
+ type RuleSeverity = "error" | "warn" | "off";
144
+ /**
145
+ * Staleness thresholds for the `lastPublish` rule, in months since the
146
+ * latest npm publish. `errorMonths` should be greater than `warnMonths`.
147
+ */
148
+ interface LastPublishThresholds {
149
+ readonly warnMonths: number;
150
+ readonly errorMonths: number;
151
+ }
152
+ /**
153
+ * Which dependencies the policy applies to: only those detected as React
154
+ * Native native modules, or every dependency.
155
+ */
156
+ type PolicyScope = "rn-native-only" | "all-deps";
157
+ /**
158
+ * One allowlist entry: suppresses findings for `package` until `expires`.
159
+ * An expired entry escalates the findings it used to suppress to `error`.
160
+ */
161
+ interface AllowEntry {
162
+ /** Exact npm package name the entry applies to. */
163
+ readonly package: string;
164
+ /** Why the package is allowed (shown in reports). */
165
+ readonly reason: string | null;
166
+ /** `YYYY-MM-DD` (inclusive, UTC). `null` means the entry never expires. */
167
+ readonly expires: string | null;
168
+ }
169
+ /**
170
+ * Per-rule severity configuration.
171
+ */
172
+ interface PolicyRules {
173
+ /** How to treat a package the RN Directory marks New-Architecture-unsupported. */
174
+ readonly newArchitecture: RuleSeverity;
175
+ /** How to treat a package whose New Architecture support cannot be determined. */
176
+ readonly newArchUnknown: RuleSeverity;
177
+ /** Staleness thresholds for the latest npm publish, or `off`. */
178
+ readonly lastPublish: LastPublishThresholds | "off";
179
+ /** How to treat a package whose GitHub repository is archived. */
180
+ readonly githubArchived: RuleSeverity;
181
+ /** How to treat a package deprecated on npm. */
182
+ readonly npmDeprecated: RuleSeverity;
183
+ /** How to treat a package the RN Directory flags as unmaintained. */
184
+ readonly directoryUnmaintained: RuleSeverity;
185
+ }
186
+ /**
187
+ * A complete policy: rule severities, scope, and the allowlist.
188
+ */
189
+ interface Policy {
190
+ readonly rules: PolicyRules;
191
+ readonly scope: PolicyScope;
192
+ readonly allow: readonly AllowEntry[];
193
+ }
194
+ /**
195
+ * The default policy, used when no `.rn-doctor.yml` is present.
196
+ * Matches the documented sample: hard failures for clearly-dead signals,
197
+ * warnings where data is merely missing (honest, not alarmist).
198
+ */
199
+ declare const DEFAULT_POLICY: Policy;
200
+ /**
201
+ * Severity of an emitted finding. `note` is informational — it never fails
202
+ * the run and is not subject to allowlisting.
203
+ */
204
+ type FindingSeverity = "error" | "warn" | "note";
205
+ /**
206
+ * A single policy violation (or informational note) for one dependency.
207
+ */
208
+ interface Finding {
209
+ /** npm package name the finding is about. */
210
+ readonly package: string;
211
+ /** The rule that produced the finding. */
212
+ readonly rule: RuleId;
213
+ /** Effective severity after allowlist processing. */
214
+ readonly severity: FindingSeverity;
215
+ /** Human-readable verdict + reason + what to do about it. */
216
+ readonly message: string;
217
+ /** Link to the evidence backing the verdict, when one exists. */
218
+ readonly evidenceUrl: string | null;
219
+ /**
220
+ * Set when an active allowlist entry suppresses this finding. Suppressed
221
+ * findings keep their severity for display but must not fail the run.
222
+ */
223
+ readonly suppressedBy: {
224
+ readonly reason: string | null;
225
+ readonly expires: string | null;
226
+ } | null;
227
+ }
228
+ /**
229
+ * Options for {@link evaluatePolicy}.
230
+ */
231
+ interface EvaluateOptions {
232
+ /** Clock used for staleness and allowlist expiry. Defaults to `new Date()`. */
233
+ readonly now?: Date;
234
+ }
235
+ /**
236
+ * Evaluate a policy against enriched dependencies and produce findings.
237
+ *
238
+ * @remarks
239
+ * Pure: same inputs (including `options.now`) always yield the same output,
240
+ * in a stable order (input dependency order, then a fixed rule order).
241
+ *
242
+ * Allowlist semantics: an **active** entry keeps the finding visible but sets
243
+ * {@link Finding.suppressedBy} (reporters must not fail the run on suppressed
244
+ * findings); an **expired** entry escalates the finding to `error` and says
245
+ * so in the message. `note` findings are informational and unaffected.
246
+ *
247
+ * @param dependencies - Enriched records from the enrichment engine.
248
+ * @param policy - The effective policy (see {@link DEFAULT_POLICY}).
249
+ * @param options - Evaluation options; inject `now` for deterministic output.
250
+ * @returns All findings, unfiltered — including suppressed ones.
251
+ */
252
+ declare function evaluatePolicy(dependencies: readonly EnrichedDependency[], policy: Policy, options?: EvaluateOptions): readonly Finding[];
253
+
254
+ /**
255
+ * `.rn-doctor.yml` loading and validation.
256
+ *
257
+ * Parses the policy file with `yaml`, validates the untyped result with
258
+ * explicit type guards (typo protection: unknown keys and bad values are
259
+ * rejected with actionable messages), and merges partial user config over
260
+ * {@link DEFAULT_POLICY}.
261
+ *
262
+ * @packageDocumentation
263
+ */
264
+
265
+ /** The policy file name looked up in the working directory by default. */
266
+ declare const DEFAULT_POLICY_FILENAME = ".rn-doctor.yml";
267
+ /**
268
+ * A policy file could not be read or is invalid. Maps to exit code 2
269
+ * (tool failure) in the CLI — a broken policy must never silently pass.
270
+ */
271
+ declare class PolicyError extends Error {
272
+ constructor(message: string);
273
+ }
274
+ /**
275
+ * Parse and validate policy YAML text into a complete {@link Policy}.
276
+ *
277
+ * Absent keys fall back to {@link DEFAULT_POLICY}; unknown keys and invalid
278
+ * values throw {@link PolicyError} with the offending key named.
279
+ *
280
+ * @param yamlText - Raw contents of a `.rn-doctor.yml` file.
281
+ * @param filePath - Optional path used to contextualize error messages.
282
+ * @returns The validated policy, merged over the defaults.
283
+ */
284
+ declare function parsePolicy(yamlText: string, filePath?: string): Policy;
285
+ /**
286
+ * Load the effective policy from disk.
287
+ *
288
+ * @remarks
289
+ * - With an explicit `path`: the file must exist — a missing file is a
290
+ * {@link PolicyError} (a CI run pointing at a typo'd path must not
291
+ * silently fall back to defaults).
292
+ * - Without a `path`: looks for `.rn-doctor.yml` in `cwd`; if absent,
293
+ * returns {@link DEFAULT_POLICY}.
294
+ *
295
+ * @param path - Optional explicit policy file path.
296
+ * @param cwd - Directory searched for the default file. Defaults to `process.cwd()`.
297
+ * @returns The validated effective policy.
298
+ */
299
+ declare function loadPolicy(path?: string, cwd?: string): Promise<Policy>;
300
+
301
+ /**
302
+ * Shared reporter surface: the report shape every renderer consumes, the
303
+ * finding summary, and the exit-code contract.
304
+ * @packageDocumentation
305
+ */
306
+
307
+ /**
308
+ * A policy finding located in a specific manifest file. The CLI decorates
309
+ * pure policy findings with their manifest path at report-assembly time; the
310
+ * policy engine itself stays manifest-blind.
311
+ */
312
+ interface ReportFinding extends Finding {
313
+ /**
314
+ * Manifest path relative to the run cwd, POSIX separators — `package.json`
315
+ * for single-manifest runs, e.g. `packages/a/package.json` under
316
+ * `--workspaces`.
317
+ */
318
+ readonly file: string;
319
+ }
320
+ /**
321
+ * Everything a reporter needs to render one rn-doctor run.
322
+ */
323
+ interface Report {
324
+ /** Findings from the policy engine, grouped by manifest in its stable output order. */
325
+ readonly findings: readonly ReportFinding[];
326
+ /** Run-level and per-dependency enrichment warnings (degraded data, etc.). */
327
+ readonly warnings: readonly EnrichmentWarning[];
328
+ /** How many (manifest, dependency) pairs were checked across all scanned manifests. */
329
+ readonly checkedCount: number;
330
+ /**
331
+ * How many manifests were scanned. Omitted (equivalent to 1) outside
332
+ * `--workspaces`; the pretty reporter mentions it only when above 1.
333
+ */
334
+ readonly manifestCount?: number;
335
+ }
336
+ /**
337
+ * Decorate pure policy findings with the manifest they belong to.
338
+ *
339
+ * @param findings - Findings from {@link evaluatePolicy}.
340
+ * @param file - Manifest path relative to the run cwd, POSIX separators.
341
+ */
342
+ declare function locateFindings(findings: readonly Finding[], file: string): readonly ReportFinding[];
343
+ /**
344
+ * Counts of findings by effect. Suppressed findings are counted only under
345
+ * `suppressed` — they keep their severity for display but have no effect on
346
+ * the run outcome.
347
+ */
348
+ interface FindingSummary {
349
+ readonly errors: number;
350
+ readonly warnings: number;
351
+ readonly notes: number;
352
+ readonly suppressed: number;
353
+ }
354
+ /**
355
+ * Summarize findings for the pretty footer and the JSON document.
356
+ */
357
+ declare function summarize(findings: readonly Finding[]): FindingSummary;
358
+ /**
359
+ * The stable exit-code contract for policy outcomes.
360
+ *
361
+ * @remarks
362
+ * Returns `1` iff any finding is an unsuppressed `error`; warnings, notes,
363
+ * and allowlist-suppressed findings never fail the run. Exit code `2`
364
+ * (tool failure) is decided by the CLI, not here — a report that rendered
365
+ * at all is not a tool failure.
366
+ */
367
+ declare function computeExitCode(findings: readonly Finding[]): 0 | 1;
368
+
369
+ /**
370
+ * JSON reporter: a machine-readable document with a stable key order, safe
371
+ * to snapshot and to diff across runs.
372
+ * @packageDocumentation
373
+ */
374
+
375
+ /**
376
+ * Render the report as a stable-ordered JSON document.
377
+ *
378
+ * @remarks
379
+ * Key order is fixed by construction (every object is rebuilt field by
380
+ * field), and the document contains no timestamps or environment-dependent
381
+ * values, so identical inputs always serialize identically. The `version`
382
+ * field is the document format version, bumped only on breaking shape
383
+ * changes — additive fields (like `file`, added for `--workspaces`) do not
384
+ * bump it.
385
+ *
386
+ * @param report - The report to render.
387
+ * @returns Pretty-printed JSON, terminated with a newline.
388
+ */
389
+ declare function renderJson(report: Report): string;
390
+
391
+ /**
392
+ * SARIF 2.1.0 reporter, for GitHub code scanning and other SARIF consumers.
393
+ * The output validates against the OASIS SARIF 2.1.0 schema (enforced by the
394
+ * acceptance suite).
395
+ * @packageDocumentation
396
+ */
397
+
398
+ /**
399
+ * Options for {@link renderSarif}.
400
+ */
401
+ interface SarifOptions {
402
+ /**
403
+ * Resolve a package's 1-based declaration line in the manifest at `file`
404
+ * (the finding's cwd-relative manifest path), or `null` when unknown. When
405
+ * omitted (or when it returns `null`), results still carry the manifest
406
+ * artifact location, just without a region.
407
+ */
408
+ readonly lineOf?: (file: string, packageName: string) => number | null;
409
+ }
410
+ /**
411
+ * Render the report as a SARIF 2.1.0 log with a single run.
412
+ *
413
+ * @remarks
414
+ * Severity maps `error` → `"error"`, `warn` → `"warning"`, `note` → `"note"`.
415
+ * Allowlist-suppressed findings are emitted with a `suppressions` entry
416
+ * (`kind: "external"`, `status: "accepted"`) so SARIF consumers exclude them
417
+ * from gating, mirroring the CLI exit-code contract. Enrichment warnings are
418
+ * carried as tool execution notifications on the invocation.
419
+ *
420
+ * @param report - The report to render.
421
+ * @param options - Line resolution for annotating package.json regions.
422
+ * @returns Pretty-printed SARIF JSON, terminated with a newline.
423
+ */
424
+ declare function renderSarif(report: Report, options?: SarifOptions): string;
425
+
426
+ /**
427
+ * Human-readable terminal reporter: one block per finding (verdict, reason,
428
+ * evidence link), enrichment warnings, and a summary footer.
429
+ * @packageDocumentation
430
+ */
431
+
432
+ /**
433
+ * Options for {@link renderPretty}.
434
+ */
435
+ interface PrettyOptions {
436
+ /**
437
+ * Enable ANSI colors. The CLI passes `stdout.isTTY && !NO_COLOR`; tests
438
+ * pass `false` for stable snapshots.
439
+ */
440
+ readonly color: boolean;
441
+ }
442
+ /**
443
+ * Render the report for human eyes.
444
+ *
445
+ * @remarks
446
+ * When the findings span more than one manifest (a `--workspaces` run), each
447
+ * group is introduced by its manifest path; single-manifest output is
448
+ * unchanged.
449
+ *
450
+ * @param report - The report to render.
451
+ * @param options - Color toggling; content is identical either way.
452
+ * @returns The full report text, terminated with a newline.
453
+ */
454
+ declare function renderPretty(report: Report, options: PrettyOptions): string;
455
+
456
+ /**
457
+ * GitHub Actions annotation reporter: emits workflow commands
458
+ * (`::error file=package.json,line=N::message`) so findings appear inline on
459
+ * the PR diff.
460
+ * @packageDocumentation
461
+ */
462
+
463
+ /**
464
+ * Render the report as GitHub Actions workflow commands, one per finding.
465
+ *
466
+ * @remarks
467
+ * Each command targets the finding's manifest (`package.json`, or e.g.
468
+ * `packages/a/package.json` under `--workspaces`) with the dependency's
469
+ * declaration line when `lineOf` resolves one (omitting `line=` otherwise),
470
+ * so annotations land inline on the PR diff. Suppressed findings keep an
471
+ * annotation (as a `notice` including the allow reason) so policy debt stays
472
+ * visible without failing checks.
473
+ *
474
+ * @param report - The report to render.
475
+ * @param lineOf - Resolve a package's 1-based declaration line in the
476
+ * manifest at `file` (the finding's cwd-relative path), or `null`.
477
+ * @returns Newline-terminated workflow commands; empty string when there are
478
+ * no findings.
479
+ */
480
+ declare function renderAnnotations(report: Report, lineOf: (file: string, packageName: string) => number | null): string;
481
+
482
+ /**
483
+ * package.json reading and dependency-line resolution for the CLI and the
484
+ * GitHub-annotation reporter.
485
+ *
486
+ * The enrichment engine itself stays manifest-agnostic (it takes plain
487
+ * package names); everything in this module is CLI-side plumbing.
488
+ *
489
+ * @packageDocumentation
490
+ */
491
+ /**
492
+ * A project manifest could not be read or parsed. Maps to exit code 2
493
+ * (tool failure) in the CLI.
494
+ */
495
+ declare class ManifestError extends Error {
496
+ constructor(message: string);
497
+ }
498
+ /**
499
+ * One dependency declaration from a `dependencies` section: the package name
500
+ * and its raw version spec string, exactly as authored.
501
+ */
502
+ interface DependencyEntry {
503
+ readonly name: string;
504
+ /** The raw spec (`"^1.2.0"`, `"npm:foo@2"`, …) — never parsed as semver. */
505
+ readonly spec: string;
506
+ }
507
+ /**
508
+ * A loaded package.json: the raw text (for line resolution) and the
509
+ * dependency names to check, in the order they are authored.
510
+ */
511
+ interface ProjectManifest {
512
+ /** Absolute path the manifest was read from. */
513
+ readonly path: string;
514
+ /** The raw file text, used to resolve dependency line numbers. */
515
+ readonly text: string;
516
+ /** Names from the `dependencies` section, in authored order. */
517
+ readonly dependencies: readonly string[];
518
+ /** Name/spec pairs from the `dependencies` section, in authored order. */
519
+ readonly entries: readonly DependencyEntry[];
520
+ }
521
+ /**
522
+ * Extract name/spec pairs from the `dependencies` section of a parsed
523
+ * package.json value, preserving authored order. Returns an empty list when
524
+ * the section is absent; throws {@link ManifestError} when the section or a
525
+ * spec value has the wrong shape.
526
+ *
527
+ * @param parsed - The JSON-parsed manifest.
528
+ * @param where - Human-readable location used in error messages.
529
+ */
530
+ declare function listDependencyEntries(parsed: unknown, where?: string): readonly DependencyEntry[];
531
+ /**
532
+ * Extract the names of the `dependencies` section from a parsed package.json
533
+ * value, preserving authored order. Returns an empty list when the section is
534
+ * absent; throws {@link ManifestError} when it is present but not an object.
535
+ *
536
+ * @param parsed - The JSON-parsed manifest.
537
+ * @param where - Human-readable location used in error messages.
538
+ */
539
+ declare function listDependencies(parsed: unknown, where?: string): readonly string[];
540
+ /**
541
+ * Parse raw manifest text (e.g. a blob read from git) into dependency
542
+ * entries. BOM-tolerant like {@link readPackageJson}.
543
+ *
544
+ * @param text - The raw manifest text.
545
+ * @param where - Human-readable location used in error messages.
546
+ * @throws ManifestError when the text is not valid JSON or has a malformed
547
+ * `dependencies` section.
548
+ */
549
+ declare function entriesFromManifestText(text: string, where: string): readonly DependencyEntry[];
550
+ /**
551
+ * Read and parse the manifest at an exact path.
552
+ *
553
+ * @param path - Absolute path of the package.json to read.
554
+ * @param missingMessage - Error message when the file does not exist; defaults
555
+ * to a plain "not found" pointing at `path`.
556
+ * @returns The manifest text and its dependency entries.
557
+ * @throws ManifestError when the file is missing or not valid JSON — the CLI
558
+ * turns this into exit code 2 with the message shown as-is.
559
+ */
560
+ declare function readManifestAt(path: string, missingMessage?: string): Promise<ProjectManifest>;
561
+ /**
562
+ * Read and parse `<cwd>/package.json`.
563
+ *
564
+ * @param cwd - Directory containing the manifest.
565
+ * @returns The manifest text and its dependency names.
566
+ * @throws ManifestError when the file is missing or not valid JSON — the CLI
567
+ * turns this into exit code 2 with the message shown as-is.
568
+ */
569
+ declare function readPackageJson(cwd: string): Promise<ProjectManifest>;
570
+ /**
571
+ * Resolve the 1-based line number where `name` is declared inside the
572
+ * top-level `dependencies` object of a package.json text.
573
+ *
574
+ * @remarks
575
+ * Scans the raw text with a minimal JSON walker (string- and escape-aware,
576
+ * tracking object depth), so a key with the same name under `devDependencies`,
577
+ * `scripts`, or a nested object can never false-match. Returns `null` when the
578
+ * name is not declared in `dependencies` — annotations then omit the line.
579
+ *
580
+ * @param text - The raw package.json text (not re-serialized — the real file).
581
+ * @param name - The exact dependency name to locate.
582
+ */
583
+ declare function findDependencyLine(text: string, name: string): number | null;
584
+
585
+ /**
586
+ * Pure `--changed-only` semantics: which dependencies of the current manifest
587
+ * were added or changed relative to the base manifest. No git knowledge here —
588
+ * the CLI composes this with the blob reads from `git.ts`.
589
+ *
590
+ * @packageDocumentation
591
+ */
592
+
593
+ /**
594
+ * Names to check under `--changed-only`: additions (name absent at base) and
595
+ * changes (raw spec string differs — upgrades, downgrades, and protocol
596
+ * changes like an `npm:` alias all count; re-checking is always safe).
597
+ * Removed and unchanged dependencies are skipped.
598
+ *
599
+ * @param base - Entries at the base commit, or `null` when the manifest did
600
+ * not exist there — every current dependency then counts as added.
601
+ * @param current - Entries in the working-tree manifest.
602
+ * @returns Names in `current` authored order.
603
+ */
604
+ declare function diffDependencies(base: readonly DependencyEntry[] | null, current: readonly DependencyEntry[]): readonly string[];
605
+
606
+ /**
607
+ * Git plumbing for `--changed-only`: resolve the diff base (merge-base of HEAD
608
+ * and the base ref) and read manifest blobs at that commit.
609
+ *
610
+ * This is the only module in the codebase that touches `child_process`; the
611
+ * process runner is injectable so everything above it is testable without a
612
+ * real repository.
613
+ *
614
+ * @packageDocumentation
615
+ */
616
+ /**
617
+ * A git invocation failed in a way rn-doctor cannot recover from. Maps to
618
+ * exit code 2 (tool failure) in the CLI; the message says what to do.
619
+ */
620
+ declare class GitError extends Error {
621
+ constructor(message: string);
622
+ }
623
+ /** Result of one git invocation. */
624
+ interface GitRunResult {
625
+ /** Process exit code (0 = success). */
626
+ readonly code: number;
627
+ readonly stdout: string;
628
+ readonly stderr: string;
629
+ }
630
+ /**
631
+ * Injectable git process runner. Resolves with the exit code rather than
632
+ * rejecting on nonzero exit — callers classify failures themselves. Throws
633
+ * {@link GitError} only when git cannot be spawned at all.
634
+ */
635
+ type GitRunner = (args: readonly string[], cwd: string) => Promise<GitRunResult>;
636
+ /**
637
+ * The real runner: spawns the `git` binary via `execFile` (args array, no
638
+ * shell, so refs and Windows paths need no quoting).
639
+ */
640
+ declare function createGitRunner(): GitRunner;
641
+ /**
642
+ * Resolve the commit to diff against: `merge-base(HEAD, baseRef)`.
643
+ *
644
+ * @remarks
645
+ * The merge-base (not the base tip) reproduces GitHub's three-dot PR diff:
646
+ * dependency changes that landed on the base branch after the PR diverged are
647
+ * never attributed to the PR. On the base branch itself the merge-base is HEAD,
648
+ * so `--changed-only` correctly reports zero changes.
649
+ *
650
+ * @param git - The process runner.
651
+ * @param cwd - Directory to run git in (the project root being checked).
652
+ * @param baseRef - The base ref, e.g. `origin/main`.
653
+ * @returns The merge-base commit SHA.
654
+ * @throws GitError with an actionable message when the cwd is not a repo, the
655
+ * ref does not exist, or the histories share no common ancestor.
656
+ */
657
+ declare function resolveBaseCommit(git: GitRunner, cwd: string, baseRef: string): Promise<string>;
658
+ /**
659
+ * Read the contents of a file as it existed at `commit`, or `null` when the
660
+ * path did not exist at that commit (e.g. a manifest created since the base).
661
+ *
662
+ * @remarks
663
+ * Uses the `git show <commit>:./<path>` form: the `./` prefix makes git
664
+ * resolve the path relative to `cwd`, so no repository-root discovery is
665
+ * needed even when the project sits in a subdirectory of the repo. A UTF-8
666
+ * BOM is stripped, mirroring {@link readPackageJson}.
667
+ *
668
+ * @param git - The process runner.
669
+ * @param cwd - Directory to run git in.
670
+ * @param commit - The commit SHA (from {@link resolveBaseCommit}).
671
+ * @param relPath - Path relative to `cwd`; separators are normalized to `/`.
672
+ * @throws GitError for any failure other than "path absent at that commit".
673
+ */
674
+ declare function readFileAtCommit(git: GitRunner, cwd: string, commit: string, relPath: string): Promise<string | null>;
675
+
676
+ /**
677
+ * Workspace discovery for `--workspaces`: find every workspace package.json
678
+ * declared by the root manifest, so the CLI can scan and report them as one
679
+ * grouped run.
680
+ *
681
+ * Supports both workspace conventions:
682
+ * - `pnpm-workspace.yaml` `packages:` globs (takes precedence — pnpm itself
683
+ * ignores the package.json field), parsed with the existing `yaml` dep;
684
+ * - root package.json `"workspaces"` (an array, or `{ packages: [...] }`).
685
+ *
686
+ * Glob expansion is hand-rolled on purpose (zero-dependency bias): literal
687
+ * paths, `*` as a full or partial segment, `**`, and leading-`!` exclusions
688
+ * cover what real-world workspace fields use.
689
+ *
690
+ * @packageDocumentation
691
+ */
692
+ /**
693
+ * `--workspaces` was requested but no workspace configuration exists or it is
694
+ * malformed. Maps to exit code 2 (tool failure) in the CLI.
695
+ */
696
+ declare class WorkspaceError extends Error {
697
+ constructor(message: string);
698
+ }
699
+ /** One discovered manifest location. */
700
+ interface WorkspaceDir {
701
+ /** Absolute directory containing a package.json. */
702
+ readonly dir: string;
703
+ /**
704
+ * Manifest path relative to the root cwd, POSIX separators — e.g.
705
+ * `packages/a/package.json`. The root manifest is `package.json`.
706
+ */
707
+ readonly manifestRelPath: string;
708
+ }
709
+ /**
710
+ * Expand workspace glob patterns to absolute directories under `rootDir`,
711
+ * deduplicated and sorted by relative path. Leading-`!` patterns exclude
712
+ * directories matched by earlier inclusions (pnpm-workspace.yaml commonly
713
+ * carries e.g. `!**\/test/**`).
714
+ *
715
+ * @remarks Supported syntax: literal paths, `*` as a full or partial path
716
+ * segment, and `**` (any depth). `node_modules` and dot-directories are never
717
+ * traversed. Anything fancier (braces, extglobs) simply matches nothing —
718
+ * the CLI warns when a configuration yields zero workspaces.
719
+ */
720
+ declare function expandWorkspacePatterns(rootDir: string, patterns: readonly string[]): Promise<readonly string[]>;
721
+ /**
722
+ * Discover the manifests of a workspace root: the root package.json first,
723
+ * then every configured workspace directory that actually contains a
724
+ * package.json, sorted by relative path.
725
+ *
726
+ * @param rootDir - The workspace root (the CLI cwd).
727
+ * @param rootManifestParsed - The JSON-parsed root package.json (already
728
+ * loaded by the CLI), used for its `workspaces` field.
729
+ * @returns Zero workspace matches yield just the root entry — the CLI warns
730
+ * but proceeds.
731
+ * @throws WorkspaceError when neither `pnpm-workspace.yaml` `packages:` nor a
732
+ * package.json `workspaces` field exists, or either is malformed.
733
+ */
734
+ declare function discoverWorkspaces(rootDir: string, rootManifestParsed: unknown): Promise<readonly WorkspaceDir[]>;
735
+
736
+ export { type AllowEntry, DEFAULT_POLICY, DEFAULT_POLICY_FILENAME, type DependencyEntry, type EnrichedDependency, type EnrichmentOptions, type EnrichmentResult, type EnrichmentWarning, type EvaluateOptions, type Finding, type FindingSeverity, type FindingSummary, GitError, type GitRunResult, type GitRunner, type LastPublishThresholds, ManifestError, type NewArchTier, type Policy, PolicyError, type PolicyRules, type PolicyScope, type PrettyOptions, type ProjectManifest, type Report, type ReportFinding, type RnNativeReason, type RuleId, type RuleSeverity, type SarifOptions, type Signal, type UnknownReason, VERSION, type WorkspaceDir, WorkspaceError, computeExitCode, createGitRunner, diffDependencies, discoverWorkspaces, enrichDependencies, entriesFromManifestText, evaluatePolicy, expandWorkspacePatterns, findDependencyLine, listDependencies, listDependencyEntries, loadPolicy, locateFindings, parsePolicy, readFileAtCommit, readManifestAt, readPackageJson, renderAnnotations, renderJson, renderPretty, renderSarif, resolveBaseCommit, summarize };