depgraph-core 1.0.2 → 1.5.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.
package/docs/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # DepGraph — Developer Documentation
2
+
3
+ Welcome to the internal documentation for **DepGraph Core**.
4
+
5
+ This folder explains how every moving part of the compiler works so you can contribute confidently, extend it with new languages, or debug an unexpected result.
6
+
7
+ ---
8
+
9
+ ## Contents
10
+
11
+ | File | What it covers |
12
+ |---|---|
13
+ | [architecture.md](./architecture.md) | Big-picture overview — how the pipeline fits together |
14
+ | [stage-collector.md](./stage-collector.md) | Stage 1 — File collection (scanning the project directory) |
15
+ | [stage-parser.md](./stage-parser.md) | Stage 2 — Source file parsing (entities, imports, exports) |
16
+ | [stage-graph.md](./stage-graph.md) | Stage 3 — Dependency graph construction |
17
+ | [stage-metrics.md](./stage-metrics.md) | Stage 4 — Metrics computation (centrality, degrees) |
18
+ | [stage-impact.md](./stage-impact.md) | Stage 5 — Impact simulation (BFS + risk scoring) |
19
+ | [stage-output.md](./stage-output.md) | Stage 6 — JSON report generation |
20
+ | [language-registry.md](./language-registry.md) | The language plugin system — how to add a new language |
21
+ | [data-types.md](./data-types.md) | All shared TypeScript interfaces, explained |
22
+
23
+ ---
24
+
25
+ ## Quick Mental Model
26
+
27
+ ```
28
+ Your project folder
29
+
30
+
31
+ ┌─────────────┐
32
+ │ Collector │ ← finds every eligible source file
33
+ └──────┬──────┘
34
+
35
+ ┌─────────────┐
36
+ │ Parser │ ← reads each file; extracts entities, imports, exports
37
+ └──────┬──────┘
38
+
39
+ ┌─────────────┐
40
+ │ Graph │ ← links entities together through their imports
41
+ └──────┬──────┘
42
+
43
+ ┌─────────────┐
44
+ │ Metrics │ ← computes in/out-degree and centrality for every node
45
+ └──────┬──────┘
46
+
47
+ ┌─────────────┐
48
+ │ Impact │ ← (optional) BFS from a target node → risk report
49
+ └──────┬──────┘
50
+
51
+ ┌─────────────┐
52
+ │ Output │ ← serialises everything to depgraph-output.json
53
+ └─────────────┘
54
+ ```
55
+
56
+ Each stage is self-contained. Data flows **forward only** — no stage reaches back to an earlier one.
@@ -0,0 +1,89 @@
1
+ # Architecture Overview
2
+
3
+ > **File**: `src/main.ts` — the orchestration entry point
4
+ > **Role**: Wires together all six stages in sequential order and handles CLI argument parsing.
5
+
6
+ ---
7
+
8
+ ## The Pipeline at a Glance
9
+
10
+ DepGraph is a **linear, stage-based compiler**. There is no framework magic — it's a plain chain of function calls where the output of each stage becomes the input of the next.
11
+
12
+ ```
13
+ collectFiles()
14
+ │ string[] (file paths)
15
+
16
+ parseFiles()
17
+ │ ParsedFile[] (entities, imports, exports per file)
18
+
19
+ buildGraph()
20
+ │ DepGraph (nodes + edges map)
21
+
22
+ computeMetrics()
23
+ │ DepGraph (same graph, nodes now have inDegree / outDegree / centralityScore)
24
+
25
+ simulateImpact() ← only runs when --impact flag is provided
26
+ │ ImpactReport
27
+
28
+ writeOutput()
29
+ depgraph-output.json
30
+ ```
31
+
32
+ Every stage lives in its own file under `src/stages/`. They are pure functions — given the same input they always produce the same output, and they never touch the file system except for the collector (reading) and output (writing).
33
+
34
+ ---
35
+
36
+ ## Entry Point — `src/main.ts`
37
+
38
+ `main.ts` does four things and nothing else:
39
+
40
+ 1. **Parses CLI flags** — `--output`, `--impact`, `--verbose`, `--no-color`, `--help`
41
+ 2. **Runs the pipeline** inside a single `try/catch` block
42
+ 3. **Prints progress** to stdout (banner, summary, impact table)
43
+ 4. **Exits with code 1** on any unhandled error
44
+
45
+ ### Key variables wired at startup
46
+
47
+ ```ts
48
+ const projectDir = args[0]; // the path to scan
49
+ const outputPath = getFlag('--output'); // where to write the JSON
50
+ const impactTarget = getFlag('--impact'); // entity name to simulate
51
+ ```
52
+
53
+ ### Execution order in the `try` block
54
+
55
+ ```ts
56
+ const files = collectFiles(projectDir); // Stage 1
57
+ const parsed = parseFiles(files); // Stage 2
58
+ const graph = buildGraph(parsed); // Stage 3
59
+ const metrics = computeMetrics(graph); // Stage 4
60
+ // Stage 5 — optional
61
+ const impact = impactTarget
62
+ ? simulateImpact(metrics, impactTarget, impactDesc)
63
+ : undefined;
64
+ writeOutput(metrics, parsed, outputPath, impact); // Stage 6
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Design Principles
70
+
71
+ | Principle | How it's applied |
72
+ |---|---|
73
+ | **Single responsibility** | Each stage file exports exactly one primary function |
74
+ | **No hidden state** | All data passed explicitly between stages |
75
+ | **Fail loudly** | `process.exit(1)` on unrecoverable errors — no silent swallowing |
76
+ | **Zero runtime dependencies** | Only Node.js built-ins (`fs`, `path`) plus TypeScript |
77
+ | **Plugin language support** | Languages register themselves via the registry (see [language-registry.md](./language-registry.md)) |
78
+
79
+ ---
80
+
81
+ ## Adding a New Stage
82
+
83
+ If you need to insert a new processing step (e.g. a linting stage), follow this pattern:
84
+
85
+ 1. Create `src/stages/my-stage.ts` and export a pure function.
86
+ 2. Import and call it in `main.ts` after the stage it depends on.
87
+ 3. Thread its output into downstream stages.
88
+
89
+ No registration, no magic — just a function call.
@@ -0,0 +1,218 @@
1
+ # Data Types Reference
2
+
3
+ > **File**: `src/types.ts`
4
+ > All shared TypeScript interfaces used across every stage of the pipeline.
5
+
6
+ ---
7
+
8
+ ## Data Flow Summary
9
+
10
+ ```
11
+ RawEntity ┐
12
+ RawImport ├──► ParsedFile ──► DepNode / DepEdge ──► DepGraph
13
+ ┘ │
14
+
15
+ AffectedNode ──► ImpactReport
16
+
17
+
18
+ OutputJSON (written to disk)
19
+ ```
20
+
21
+ ---
22
+
23
+ ## Stage 1–2 Types (Parser Output)
24
+
25
+ ### `RawEntity`
26
+
27
+ A code construct extracted directly from source — before any cross-file resolution.
28
+
29
+ ```ts
30
+ interface RawEntity {
31
+ name: string; // e.g. "getUserById"
32
+ type: string; // "function" | "class" | "component" | "hook" | "api" | "interface" | "type"
33
+ line: number; // 1-indexed line number in the source file
34
+ complexity: string; // "low" | "medium" | "high"
35
+ }
36
+ ```
37
+
38
+ **Entity types by language:**
39
+
40
+ | Type | Languages | Example |
41
+ |---|---|---|
42
+ | `function` | JS/TS, Python | `function doSomething()` |
43
+ | `class` | JS/TS, Python | `class UserService` |
44
+ | `component` | JS/TS (React) | `const Button = () =>` |
45
+ | `hook` | JS/TS (React) | `const useAuth = () =>` |
46
+ | `interface` | TypeScript | `interface User` |
47
+ | `type` | TypeScript | `type UserId = string` |
48
+ | `api` | JS/TS (Express) | `app.get('/users', ...)` |
49
+
50
+ ---
51
+
52
+ ### `RawImport`
53
+
54
+ An import statement extracted from a source file.
55
+
56
+ ```ts
57
+ interface RawImport {
58
+ source: string; // the import path, e.g. "./userService" or "express"
59
+ names: string[]; // the names imported, e.g. ["getUserById", "createUser"]
60
+ isLocal: boolean; // true if source starts with "." (relative path)
61
+ }
62
+ ```
63
+
64
+ **Key distinction**: Only imports where `isLocal === true` become graph edges. External package imports (`"express"`, `"lodash"`, etc.) are ignored during graph construction.
65
+
66
+ ---
67
+
68
+ ### `ParsedFile`
69
+
70
+ The complete output for one source file after parsing.
71
+
72
+ ```ts
73
+ interface ParsedFile {
74
+ filePath: string; // absolute or relative path on disk
75
+ lang: string; // parser's lang id, e.g. "js" or "py"
76
+ lines: number; // total line count
77
+ entities: RawEntity[]; // all found code constructs
78
+ imports: RawImport[]; // all import statements
79
+ exports: string[]; // names of exported entities
80
+ }
81
+ ```
82
+
83
+ ---
84
+
85
+ ## Stage 3–4 Types (Graph)
86
+
87
+ ### `DepNode`
88
+
89
+ A fully resolved node in the dependency graph. Built from a `RawEntity`, enriched with graph metrics.
90
+
91
+ ```ts
92
+ interface DepNode {
93
+ id: string; // composite ID: "entityName__fileBase"
94
+ name: string; // entity name, e.g. "getUserById"
95
+ type: string; // same as RawEntity.type
96
+ file: string; // source file path
97
+ line: number; // source line number
98
+ lang: string; // language id
99
+ complexity: string; // "low" | "medium" | "high"
100
+ inDegree: number; // how many other nodes import this one
101
+ outDegree: number; // how many nodes this one imports
102
+ centralityScore: number; // inDegree * 2 + outDegree
103
+ connections: string[]; // IDs of directly connected nodes (both directions)
104
+
105
+ // Optional — only present for specific entity types:
106
+ extends?: string; // parent class/interface name
107
+ method?: string; // HTTP method for API routes, e.g. "GET"
108
+ route?: string; // URL path for API routes, e.g. "/api/users"
109
+ }
110
+ ```
111
+
112
+ **Node ID format**: `getUserById__userService`
113
+ (entity name + `__` + file basename without extension, with non-alphanumeric chars replaced by `_`)
114
+
115
+ ---
116
+
117
+ ### `DepEdge`
118
+
119
+ A directed link from one node to another, representing an import relationship.
120
+
121
+ ```ts
122
+ interface DepEdge {
123
+ from: string; // source node ID (the importer)
124
+ to: string; // target node ID (the imported entity)
125
+ type: string; // currently always "imports"
126
+ description: string; // human-readable, e.g. "UserRouter imports getUserById from userService"
127
+ }
128
+ ```
129
+
130
+ **Direction convention**: `from → to` means "`from` depends on `to`".
131
+
132
+ ---
133
+
134
+ ### `DepGraph`
135
+
136
+ The complete graph.
137
+
138
+ ```ts
139
+ interface DepGraph {
140
+ nodes: Map<string, DepNode>; // keyed by node ID
141
+ edges: DepEdge[];
142
+ }
143
+ ```
144
+
145
+ The `nodes` Map is converted to an Array when writing to JSON output.
146
+
147
+ ---
148
+
149
+ ## Stage 5 Types (Impact)
150
+
151
+ ### `AffectedNode`
152
+
153
+ A downstream node discovered during BFS traversal from the impact target.
154
+
155
+ ```ts
156
+ interface AffectedNode {
157
+ nodeId: string; // graph node ID
158
+ name: string; // entity name
159
+ file: string; // source file path
160
+ depth: number; // BFS depth from target (1 = direct dependent)
161
+ impact: string; // "critical" | "high" | "medium" | "low"
162
+ reason: string; // why this node is affected
163
+ changeRequired: string; // what action needs to be taken
164
+ breakingChange: boolean; // true if depth <= 2
165
+ }
166
+ ```
167
+
168
+ ---
169
+
170
+ ### `ImpactReport`
171
+
172
+ The full simulation result returned by `simulateImpact`.
173
+
174
+ ```ts
175
+ interface ImpactReport {
176
+ targetNode: string; // the node ID of the changed entity
177
+ changeDescription: string; // description from --impact flag
178
+ riskScore: number; // 0–100
179
+ riskLevel: string; // "LOW" | "MEDIUM" | "HIGH" | "CRITICAL"
180
+ affectedNodes: AffectedNode[]; // all downstream nodes
181
+ breakingChanges: AffectedNode[]; // subset where breakingChange === true
182
+ testingPlan: string[]; // recommended test actions
183
+ recommendations: string[]; // code review / deployment guidance
184
+ }
185
+ ```
186
+
187
+ **Risk score formula**: `C×30 + H×15 + M×7 + L×2 + targetInDegree×3` (capped at 100)
188
+
189
+ ---
190
+
191
+ ## Output Type
192
+
193
+ ### `OutputJSON`
194
+
195
+ The root schema of the generated `depgraph-output.json` file.
196
+
197
+ ```ts
198
+ interface OutputJSON {
199
+ meta: {
200
+ version: string; // tool version, e.g. "1.0.0"
201
+ timestamp: string; // ISO 8601, e.g. "2024-01-15T09:30:00.000Z"
202
+ totalFiles: number;
203
+ totalLines: number;
204
+ };
205
+ summary: {
206
+ totalNodes: number;
207
+ totalEdges: number;
208
+ entryPoints: string[]; // node IDs with inDegree=0 & outDegree>0
209
+ leafNodes: string[]; // node IDs with outDegree=0 & inDegree>0
210
+ isolatedNodes: string[]; // node IDs with both degrees = 0 (dead code)
211
+ criticalNodes: string[]; // node IDs with centralityScore > 20
212
+ };
213
+ nodes: DepNode[];
214
+ edges: DepEdge[];
215
+ files: ParsedFile[];
216
+ impact?: ImpactReport; // only present when --impact was used
217
+ }
218
+ ```
@@ -0,0 +1,205 @@
1
+ # Language Registry
2
+
3
+ > **File**: `src/languages/registry.ts`
4
+ > **Exports**: `LanguageParser` (interface), `registerParser`, `getLanguageParser`
5
+
6
+ ---
7
+
8
+ ## Overview
9
+
10
+ The language registry is a **simple plugin system**. It solves one problem: the parser stage needs to know *how* to parse a `.py` file differently from a `.ts` file, without hardcoding language logic into the stage itself.
11
+
12
+ Any file that imports `registerParser` and calls it at module load time becomes a **language plugin**. The parser stage just calls `getLanguageParser(ext)` and gets back the right handler — it doesn't know or care which language it's dealing with.
13
+
14
+ ---
15
+
16
+ ## The `LanguageParser` Interface
17
+
18
+ This is the contract every language plugin must fulfil:
19
+
20
+ ```ts
21
+ export interface LanguageParser {
22
+ /** A short unique identifier for the language, e.g. "js", "py". */
23
+ lang: string;
24
+
25
+ /** File extensions this parser handles, e.g. [".js", ".ts", ".tsx"]. */
26
+ extensions: string[];
27
+
28
+ /**
29
+ * Extract named code entities (functions, classes, routes, etc.)
30
+ * from clean source code (single-line comments already stripped).
31
+ */
32
+ extractEntities: (code: string, filePath: string) => RawEntity[];
33
+
34
+ /**
35
+ * Extract import statements from clean source code.
36
+ */
37
+ extractImports: (code: string) => RawImport[];
38
+
39
+ /**
40
+ * Extract the names of exported entities from clean source code.
41
+ */
42
+ extractExports: (code: string) => string[];
43
+ }
44
+ ```
45
+
46
+ ---
47
+
48
+ ## How Registration Works
49
+
50
+ The registry stores parsers in a private module-level array:
51
+
52
+ ```ts
53
+ const parsers: LanguageParser[] = [];
54
+
55
+ export function registerParser(parser: LanguageParser): void {
56
+ parsers.push(parser);
57
+ }
58
+
59
+ export function getLanguageParser(ext: string): LanguageParser | null {
60
+ return parsers.find(p => p.extensions.includes(ext)) ?? null;
61
+ }
62
+ ```
63
+
64
+ That's the entire registry — an array and two functions.
65
+
66
+ ### Lookup
67
+
68
+ `getLanguageParser(".ts")` returns the first registered parser whose `extensions` array includes `".ts"`. If none match, it returns `null` and the file is skipped by the parser stage.
69
+
70
+ ---
71
+
72
+ ## How Plugins Self-Register
73
+
74
+ Each language file (e.g. `src/languages/javascript.ts`) defines a parser object and calls `registerParser` at the **bottom of the file**, at module scope:
75
+
76
+ ```ts
77
+ const JavaScriptParser: LanguageParser = {
78
+ lang: 'js',
79
+ extensions: ['.js', '.jsx', '.mjs', '.cjs', '.ts', '.tsx'],
80
+ extractEntities,
81
+ extractImports,
82
+ extractExports,
83
+ };
84
+
85
+ registerParser(JavaScriptParser);
86
+ ```
87
+
88
+ This code runs **once**, when the module is first imported.
89
+
90
+ ### Triggering registration
91
+
92
+ Plugins must be imported somewhere in the application for their side-effect (`registerParser(...)`) to run. In `src/main.ts`:
93
+
94
+ ```ts
95
+ import './languages/javascript';
96
+ import './languages/python';
97
+ ```
98
+
99
+ These are **side-effect imports** (no named export is consumed). They exist purely to execute the registration call.
100
+
101
+ > If you add a new language parser file but forget to import it in `main.ts`, it will never be registered and its extensions will never be recognised.
102
+
103
+ ---
104
+
105
+ ## Currently Registered Languages
106
+
107
+ | File | `lang` | Extensions |
108
+ |---|---|---|
109
+ | `javascript.ts` | `js` | `.js` `.jsx` `.mjs` `.cjs` `.ts` `.tsx` |
110
+ | `python.ts` | `py` | `.py` |
111
+
112
+ The following language files exist as stubs (imported but with minimal or no implementation yet):
113
+
114
+ - `java.ts`
115
+ - `csharp.ts`
116
+ - `go.ts`
117
+ - `kotlin.ts`
118
+ - `php.ts`
119
+ - `ruby.ts`
120
+ - `swift.ts`
121
+
122
+ ---
123
+
124
+ ## How to Add a New Language
125
+
126
+ Follow these steps to add full support for a new language.
127
+
128
+ ### 1. Create the parser file
129
+
130
+ Create `src/languages/mylang.ts`:
131
+
132
+ ```ts
133
+ import { RawEntity, RawImport } from '../types';
134
+ import { LanguageParser, registerParser } from './registry';
135
+
136
+ function extractEntities(code: string, filePath: string): RawEntity[] {
137
+ const entities: RawEntity[] = [];
138
+ // Use regex or any other technique to find functions, classes, etc.
139
+ // Populate and return the entities array.
140
+ return entities;
141
+ }
142
+
143
+ function extractImports(code: string): RawImport[] {
144
+ const imports: RawImport[] = [];
145
+ // Parse import/require/use statements.
146
+ // Set isLocal: true for relative imports ('./something').
147
+ return imports;
148
+ }
149
+
150
+ function extractExports(code: string): string[] {
151
+ // Return an array of exported entity names.
152
+ return [];
153
+ }
154
+
155
+ const MyLangParser: LanguageParser = {
156
+ lang: 'mylang',
157
+ extensions: ['.ml', '.mls'],
158
+ extractEntities,
159
+ extractImports,
160
+ extractExports,
161
+ };
162
+
163
+ registerParser(MyLangParser);
164
+ ```
165
+
166
+ ### 2. Import it in `main.ts`
167
+
168
+ ```ts
169
+ import './languages/mylang';
170
+ ```
171
+
172
+ Add this **before** the pipeline starts (with the other language imports at the top of the file).
173
+
174
+ ### 3. Ensure the extension is in `SUPPORTED_EXTS`
175
+
176
+ In `src/constants.ts`, add your extension to the set:
177
+
178
+ ```ts
179
+ export const SUPPORTED_EXTS = new Set([
180
+ // ... existing extensions
181
+ '.ml', '.mls',
182
+ ]);
183
+ ```
184
+
185
+ Without this, the collector will never hand files with that extension to the parser in the first place.
186
+
187
+ ### 4. Test it
188
+
189
+ ```bash
190
+ node depgraph.js ./path/to/mylang-project --verbose
191
+ ```
192
+
193
+ Look for your files appearing in the verbose output and entities showing up in the summary.
194
+
195
+ ---
196
+
197
+ ## Rules for Writing a Good Parser
198
+
199
+ | Rule | Why |
200
+ |---|---|
201
+ | Only use `code` (the cleaned version) — never read files | The stage already strips comments and passes clean code |
202
+ | Reset `regex.lastIndex = 0` before each `exec` loop | Global regexes maintain state; forgetting this causes skipped matches |
203
+ | Guard against duplicates (`entities.some(e => e.name === name)`) | The graph stage skips duplicates, but clean data is better upstream |
204
+ | Mark relative imports with `isLocal: true` | Only local imports become graph edges; external packages are ignored |
205
+ | Keep extraction logic regex-based or simple string parsing | The project has zero AST dependencies on purpose — keep it that way |
@@ -0,0 +1,109 @@
1
+ # Stage 1 — Collector
2
+
3
+ > **File**: `src/stages/collector.ts`
4
+ > **Exports**: `collectFiles(dir: string): string[]`
5
+
6
+ ---
7
+
8
+ ## What It Does
9
+
10
+ The collector is the very first thing that runs. Its job is simple: **walk a directory tree and return a flat list of file paths** that the rest of the pipeline should process.
11
+
12
+ It does **not** read file contents — it only looks at names, extensions, and file sizes.
13
+
14
+ ---
15
+
16
+ ## How It Works
17
+
18
+ ### 1. Recursive directory walk
19
+
20
+ `collectFiles` calls an internal `walk()` helper which:
21
+
22
+ 1. Reads the entries of the current directory with `fs.readdirSync`.
23
+ 2. For each entry:
24
+ - If it's a **directory**, check it against `IGNORE_DIRS`. If not ignored, recurse into it.
25
+ - If it's a **file**, check its extension against `SUPPORTED_EXTS` and its size against `MAX_FILE_SIZE`.
26
+
27
+ ```ts
28
+ if (stat.isDirectory()) {
29
+ if (!IGNORE_DIRS.has(entry)) walk(fullPath);
30
+ continue;
31
+ }
32
+
33
+ const ext = path.extname(entry);
34
+ if (SUPPORTED_EXTS.has(ext) && stat.size < MAX_FILE_SIZE) {
35
+ results.push(fullPath);
36
+ }
37
+ ```
38
+
39
+ ### 2. Ignored directories
40
+
41
+ Defined in `src/constants.ts`:
42
+
43
+ ```ts
44
+ export const IGNORE_DIRS = new Set([
45
+ 'node_modules', '.git', 'dist', 'build',
46
+ '.next', '__pycache__', 'vendor', 'venv',
47
+ 'target', 'out', 'coverage', '.cache'
48
+ ]);
49
+ ```
50
+
51
+ These are skipped entirely — the walker never descends into them.
52
+
53
+ ### 3. Supported extensions
54
+
55
+ Also from constants:
56
+
57
+ ```ts
58
+ export const SUPPORTED_EXTS = new Set([
59
+ '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
60
+ '.py', '.go', '.java', '.cs', '.rb',
61
+ '.php', '.swift', '.kt', '.vue', '.svelte'
62
+ ]);
63
+ ```
64
+
65
+ > **Note**: Collecting a file with `.java` doesn't mean it will be *parsed* successfully. The collector just gathers eligible paths. If no language parser is registered for that extension, the parser stage silently drops it.
66
+
67
+ ### 4. Size guard
68
+
69
+ ```ts
70
+ export const MAX_FILE_SIZE = 300_000; // 300 KB
71
+ ```
72
+
73
+ Files over 300 KB are skipped. This prevents the scanner from choking on minified bundles, lock files accidentally using a supported extension, or generated code.
74
+
75
+ ---
76
+
77
+ ## Error Handling
78
+
79
+ The collector is fault-tolerant at the entry level:
80
+ - If a directory can't be read (permissions, broken symlink), it logs a warning and continues.
81
+ - If `fs.statSync` fails on an entry, it skips that entry and continues.
82
+
83
+ It **never throws** — it always returns whatever it managed to collect.
84
+
85
+ ---
86
+
87
+ ## Output
88
+
89
+ A flat `string[]` of absolute file paths, for example:
90
+
91
+ ```
92
+ [
93
+ "/project/src/main.ts",
94
+ "/project/src/utils/auth.ts",
95
+ "/project/src/routes/users.ts"
96
+ ]
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Configuration Quick Reference
102
+
103
+ | Constant | Default | Description |
104
+ |---|---|---|
105
+ | `IGNORE_DIRS` | See above | Directory names to skip entirely |
106
+ | `SUPPORTED_EXTS` | See above | File extensions that are eligible |
107
+ | `MAX_FILE_SIZE` | `300_000` (300 KB) | Maximum file size in bytes |
108
+
109
+ All constants live in [`src/constants.ts`](../src/constants.ts).