fallow-types 3.27.0

Shared types and serde paths for fallow codebase intelligence
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
//! Shared trace output contracts for analysis and integration surfaces.

use std::path::PathBuf;

use serde::Serialize;

use crate::cache_rejection::CacheRejection;
use crate::duplicates::{CloneInstance, RefactoringSuggestion};
use crate::semantic::SemanticNamespace;
use crate::serde_path;
use crate::trace_chain::StarExportAmbiguity;

/// Result of tracing an export: why it is considered used or unused.
#[derive(Debug, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ExportTrace {
    /// The file containing the export.
    #[serde(serialize_with = "serde_path::serialize")]
    pub file: PathBuf,
    /// The export name being traced.
    pub export_name: String,
    /// Namespace whose references are listed for the traced export. The
    /// preferred lane wins whenever it carries a reference: `value` for a
    /// value export, `type` for a type-only one. When the preferred lane
    /// carries none and the other lane resolves to the same declaration, the
    /// other lane's references are listed and this field names it, so a value
    /// export whose only credit is a bound `import type` reports `type` with
    /// `is_used: true`. `is_used` and `direct_references` follow the listed
    /// lane only, and only reachable reference sources can credit it. Legal
    /// declaration merges share one declaration group across lanes, including
    /// an `interface` next to a same-name `class` and a `class` next to a
    /// same-name `namespace`, so references to either lane credit the merged
    /// declaration. Distinct same-name declarations outside a merge remain
    /// separate and keep the preferred lane. `semantic.target.namespace`
    /// names the lane the declaration itself occupies and can therefore differ
    /// from this field. Producers always emit the field; the schema permits
    /// omission by payloads created before namespaces were exposed.
    #[cfg_attr(feature = "schema", schemars(default))]
    pub namespace: crate::semantic::SemanticNamespace,
    /// Whether the file is reachable from an entry point.
    pub file_reachable: bool,
    /// Whether the file is an entry point.
    pub is_entry_point: bool,
    /// Whether the export is considered used.
    pub is_used: bool,
    /// Files that reference this export directly.
    pub direct_references: Vec<ExportReference>,
    /// Reachable direct references grouped by namespace. This is additive to
    /// `namespace` and `direct_references`, whose winning-lane meaning remains
    /// unchanged for backwards compatibility.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub direct_references_by_namespace: Vec<NamespacedExportReferences>,
    /// A star-export collision that makes the traced name ambiguous. When
    /// present, `is_used: false` is an abstention rather than an unused-code
    /// verdict.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub star_export_ambiguity: Option<StarExportAmbiguity>,
    /// Re-export chains that pass through this export.
    pub re_export_chains: Vec<ReExportChain>,
    /// Human-readable reason summary.
    pub reason: String,
    /// Exact checker-backed references when type-aware tracing is enabled.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub semantic: Option<crate::semantic::SemanticSymbolTrace>,
}

/// Result of tracing a class / enum / store MEMBER: the `--trace FILE:NAME`
/// fallback when `NAME` is not a top-level export but a member declared on one
/// (issue #1744). The trace runs on the module graph only, so it reports the
/// OWNING export's reachability and usage (the gating precondition for
/// member-level crediting) plus a pointer to the right `--unused-*-members`
/// command, rather than per-member crediting provenance.
#[derive(Debug, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ClassMemberTrace {
    /// The file containing the member.
    #[serde(serialize_with = "serde_path::serialize")]
    pub file: PathBuf,
    /// The member name being traced.
    pub member_name: String,
    /// The member kind: `class-method`, `class-property`, `enum-member`,
    /// `store-member`, or `namespace-member`.
    pub member_kind: String,
    /// The export that declares this member (the class / enum / store name).
    pub owner_export: String,
    /// Namespace whose references credit the owning export, mirroring
    /// [`ExportTrace::namespace`] for the export this member is declared on.
    /// `owner_is_used` and `owner_direct_references` describe that lane, so a
    /// member of a value export credited only by a bound `import type` reports
    /// `type` here. `semantic.target.namespace` names the lane the checker
    /// proof covers and can therefore differ. Producers always emit the field;
    /// the schema permits omission by payloads created before the owner
    /// namespace was exposed.
    #[cfg_attr(feature = "schema", schemars(default))]
    pub owner_namespace: crate::semantic::SemanticNamespace,
    /// Whether the owning export is considered used.
    pub owner_is_used: bool,
    /// Whether the file is reachable from an entry point.
    pub owner_file_reachable: bool,
    /// Whether the file is an entry point.
    pub owner_is_entry_point: bool,
    /// Files that reference the owning export directly.
    pub owner_direct_references: Vec<ExportReference>,
    /// Re-export chains through which the owning export is reachable. Populated
    /// so a machine consumer can tell "used via a barrel" (empty direct refs but
    /// non-empty chains) from "genuinely unreferenced".
    pub owner_re_export_chains: Vec<ReExportChain>,
    /// Human-readable reason summary plus the follow-up command to inspect the
    /// member finding.
    pub reason: String,
    /// Exact checker-backed member references when type-aware tracing is enabled.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub semantic: Option<crate::semantic::SemanticSymbolTrace>,
}

/// A direct reference to an export.
#[derive(Debug, Clone, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ExportReference {
    /// File that contains the reference.
    #[serde(serialize_with = "serde_path::serialize")]
    pub from_file: PathBuf,
    /// Reference kind, such as named import, default import, or re-export.
    pub kind: String,
}

/// Direct references that credit one namespace of an export binding.
#[derive(Debug, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct NamespacedExportReferences {
    /// Credited namespace.
    pub namespace: SemanticNamespace,
    /// Number of reachable references in this namespace.
    pub reference_count: usize,
    /// Reachable references in deterministic graph order.
    pub references: Vec<ExportReference>,
}

/// A re-export chain showing how an export is propagated.
#[derive(Debug, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ReExportChain {
    /// The barrel file that re-exports this symbol.
    #[serde(serialize_with = "serde_path::serialize")]
    pub barrel_file: PathBuf,
    /// The name it is re-exported as.
    pub exported_as: String,
    /// Number of references on the barrel's re-exported symbol.
    pub reference_count: usize,
}

/// Result of tracing all edges for a file.
#[derive(Debug, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct FileTrace {
    /// The traced file.
    #[serde(serialize_with = "serde_path::serialize")]
    pub file: PathBuf,
    /// Whether this file is reachable from entry points.
    pub is_reachable: bool,
    /// Whether this file is an entry point.
    pub is_entry_point: bool,
    /// Exports declared by this file.
    pub exports: Vec<TracedExport>,
    /// Files that this file imports from.
    #[serde(serialize_with = "serde_path::serialize_vec")]
    pub imports_from: Vec<PathBuf>,
    /// Files that import from this file.
    #[serde(serialize_with = "serde_path::serialize_vec")]
    pub imported_by: Vec<PathBuf>,
    /// Re-exports declared by this file.
    pub re_exports: Vec<TracedReExport>,
}

/// An export with usage information.
#[derive(Debug, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct TracedExport {
    /// Export name.
    pub name: String,
    /// Whether the export is type-only.
    pub is_type_only: bool,
    /// Number of references to this export.
    pub reference_count: usize,
    /// Files that reference this export.
    pub referenced_by: Vec<ExportReference>,
}

/// A re-export with source information.
#[derive(Debug, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct TracedReExport {
    /// Source file being re-exported from.
    #[serde(serialize_with = "serde_path::serialize")]
    pub source_file: PathBuf,
    /// Imported symbol name.
    pub imported_name: String,
    /// Exported symbol name.
    pub exported_name: String,
}

/// Result of tracing a dependency: where it is used.
#[derive(Debug, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct DependencyTrace {
    /// The dependency name being traced.
    pub package_name: String,
    /// Files that import this dependency.
    #[serde(serialize_with = "serde_path::serialize_vec")]
    pub imported_by: Vec<PathBuf>,
    /// Files that import this dependency with type-only imports.
    #[serde(serialize_with = "serde_path::serialize_vec")]
    pub type_only_imported_by: Vec<PathBuf>,
    /// Whether the dependency is invoked from package.json scripts or CI configs.
    pub used_in_scripts: bool,
    /// Whether the dependency is used at all.
    pub is_used: bool,
    /// Total import count.
    pub import_count: usize,
}

/// Sub-phase attribution inside the entry-point discovery stage.
///
/// `PipelineTimings::entry_points_ms` is a single opaque number; these are the
/// consecutive wall-clock spans that make it up, so a slow discovery stage can
/// be attributed instead of guessed at. The spans cover the discovery sections
/// only, so they sum to slightly less than `entry_points_ms`: the summary and
/// count that follow discovery are not attributed to any span.
#[derive(Debug, Clone, Copy, Default, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct EntryPointSpans {
    /// Root-package discovery: manual entry globs, root `package.json` fields,
    /// and the nested `package.json` scan under the conventional monorepo
    /// directories.
    pub root_ms: f64,
    /// Runtime script seed collection plus per-workspace discovery.
    pub workspaces_ms: f64,
    /// Plugin entry-point glob compilation and matching.
    pub plugins_ms: f64,
    /// The part of `plugins_ms` spent compiling plugin patterns into a glob
    /// set. Scales with active pattern count, not with project size.
    pub plugin_glob_build_ms: f64,
    /// The part of `plugins_ms` spent matching the compiled set against every
    /// discovered file. Scales with file count times pattern count.
    pub plugin_glob_match_ms: f64,
    /// Infrastructure config-file probing at the project root.
    pub infrastructure_ms: f64,
    /// Configured `dynamicallyLoaded` glob expansion. Zero when unconfigured.
    pub dynamic_ms: f64,
    /// Sorting and deduplicating the merged entry set.
    pub dedup_ms: f64,
}

/// Pipeline performance timings.
#[derive(Debug, Clone, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct PipelineTimings {
    /// Time spent discovering files.
    pub discover_files_ms: f64,
    /// Number of discovered files.
    pub file_count: usize,
    /// Time spent discovering workspaces.
    pub workspaces_ms: f64,
    /// Number of discovered workspaces.
    pub workspace_count: usize,
    /// Time spent running plugin discovery.
    pub plugins_ms: f64,
    /// Time spent analyzing package scripts and CI configuration.
    pub script_analysis_ms: f64,
    /// Wall-clock time spent parsing and extracting modules.
    pub parse_extract_ms: f64,
    /// Summed parser CPU time across workers.
    pub parse_cpu_ms: f64,
    /// Number of extracted modules.
    pub module_count: usize,
    /// Number of files loaded from the parse cache.
    pub cache_hits: usize,
    /// Number of files parsed without a cache hit.
    pub cache_misses: usize,
    /// Why the persisted parse cache was not reused, when it was not. `None`
    /// means the cache was loaded; the hit and miss counts then describe how
    /// much of it applied.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub cache_rejection: Option<CacheRejection>,
    /// Why the persisted module-graph cache was not reused, when it was not.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub graph_cache_rejection: Option<CacheRejection>,
    /// Time spent updating the parse cache.
    pub cache_update_ms: f64,
    /// Time spent categorizing entry points.
    pub entry_points_ms: f64,
    /// Sub-phase attribution for `entry_points_ms`.
    pub entry_point_spans: EntryPointSpans,
    /// Number of entry points considered.
    pub entry_point_count: usize,
    /// Time spent resolving imports.
    pub resolve_imports_ms: f64,
    /// Time spent building the module graph.
    pub build_graph_ms: f64,
    /// Time spent running analysis.
    pub analyze_ms: f64,
    /// Time spent running duplicate-code analysis, when included.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub duplication_ms: Option<f64>,
    /// Total pipeline time.
    pub total_ms: f64,
}

/// Result of computing the impact closure for a single file as the seed.
#[derive(Debug, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ImpactClosureTrace {
    /// The seed file, root-relative.
    pub seed: String,
    /// Root-relative paths transitively affected by the seed.
    pub affected_not_shown: Vec<String>,
    /// Coordination gaps between the seed and consumers.
    pub coordination_gap: Vec<ImpactClosureGap>,
}

/// Wire-version discriminator for [`ImportPathTrace`]. Independent from the
/// global `SchemaVersion`: the import-path payload versions on its own cadence,
/// like the other independently-versioned envelopes. Serializes as a string
/// `const` so JSON consumers can switch on it.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub enum ImportPathTraceSchemaVersion {
    /// First release of the `fallow trace --path` shape.
    #[serde(rename = "1")]
    V1,
}

/// Result of asking how one module reaches another: the shortest import path.
///
/// `reachable` is the only field that separates "no route exists" from "the
/// route is empty because both ends are the same module". Both report
/// `hops: 0`, so a consumer must read `reachable`, never the hop count.
#[derive(Debug, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schema", schemars(title = "fallow trace --path"))]
pub struct ImportPathTrace {
    /// Wire-shape version of this payload.
    pub schema_version: ImportPathTraceSchemaVersion,
    /// The module the walk started from, root-relative.
    pub from: String,
    /// The module the walk was looking for, root-relative.
    pub to: String,
    /// Whether `to` is reachable from `from` by following import edges.
    pub reachable: bool,
    /// Number of import edges on the reported route. `0` both when the two ends
    /// are the same module and when there is no route at all.
    pub hops: usize,
    /// The route, in import order. Empty whenever `hops` is `0`.
    pub path: Vec<ImportPathHop>,
    /// Human-readable summary of the outcome.
    pub reason: String,
}

/// One import edge on an [`ImportPathTrace`].
#[derive(Debug, Serialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ImportPathHop {
    /// The importing module, root-relative.
    pub from: String,
    /// The imported module, root-relative.
    pub to: String,
    /// Whether every symbol on this edge is type-only, so the hop is erased at
    /// build time. Type-only hops are reported, never skipped: an `import type`
    /// chain is a real compile-time coupling.
    pub type_only: bool,
    /// 1-based line in `from` of the imported binding that creates this edge:
    /// the first value-carrying symbol on the import, or the first symbol when
    /// every symbol is type-only. On a multi-line import that is the binding's
    /// own line, not the `import` keyword's. Absent when the edge carries no
    /// span or the source could not be read.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub import_line: Option<u32>,
}

/// One coordination-gap entry in an [`ImpactClosureTrace`].
#[derive(Debug, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ImpactClosureGap {
    /// Root-relative path of the consumer module.
    pub consumer_file: String,
    /// Exported symbol names the consumer references.
    pub consumed_symbols: Vec<String>,
    /// Scope note for the syntactic trace.
    pub note: String,
}

/// Result of tracing a clone: all groups containing the code at a source
/// location or addressed by a stable clone fingerprint.
#[derive(Debug, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct CloneTrace {
    /// File passed to the trace request, root-relative when a group matches.
    #[serde(serialize_with = "serde_path::serialize")]
    pub file: PathBuf,
    /// 1-based line passed to the trace request or representative group line.
    pub line: usize,
    /// The matched clone instance, if one exists.
    pub matched_instance: Option<CloneInstance>,
    /// Clone groups matched by the trace request.
    pub clone_groups: Vec<TracedCloneGroup>,
}

/// One clone group returned from a clone trace request.
#[derive(Debug, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct TracedCloneGroup {
    /// Stable content fingerprint, usually `dup:<8hex>` and widened on rare
    /// report collisions.
    pub fingerprint: String,
    /// Number of tokens in the duplicated block.
    pub token_count: usize,
    /// Number of lines in the duplicated block.
    pub line_count: usize,
    /// Maximum directory-tree or same-file line distance between instances.
    pub spread: usize,
    /// Lowest all-pairs similarity for a near-miss clone group.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    #[cfg_attr(feature = "schema", schemars(with = "f64"))]
    pub similarity: Option<f64>,
    /// Root-relative clone instances in this group.
    pub instances: Vec<CloneInstance>,
    /// Group-level refactoring suggestion.
    pub suggestion: RefactoringSuggestion,
    /// Best-effort name for the extracted function. Advisory only.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub suggested_name: Option<String>,
}