doc-freshness-checker 1.0.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/LICENSE +21 -0
- package/README.md +305 -0
- package/dist/cache/cacheManager.d.ts +42 -0
- package/dist/cache/cacheManager.js +138 -0
- package/dist/cache/cacheManager.js.map +1 -0
- package/dist/cache/cacheManager.test.d.ts +1 -0
- package/dist/cache/cacheManager.test.js +142 -0
- package/dist/cache/cacheManager.test.js.map +1 -0
- package/dist/cli.d.ts +32 -0
- package/dist/cli.js +137 -0
- package/dist/cli.js.map +1 -0
- package/dist/cli.test.d.ts +1 -0
- package/dist/cli.test.js +184 -0
- package/dist/cli.test.js.map +1 -0
- package/dist/config/defaults.d.ts +5 -0
- package/dist/config/defaults.js +135 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/defineConfig.d.ts +28 -0
- package/dist/config/defineConfig.js +30 -0
- package/dist/config/defineConfig.js.map +1 -0
- package/dist/config/defineConfig.test.d.ts +1 -0
- package/dist/config/defineConfig.test.js +10 -0
- package/dist/config/defineConfig.test.js.map +1 -0
- package/dist/config/loader.d.ts +7 -0
- package/dist/config/loader.js +250 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/loader.test.d.ts +1 -0
- package/dist/config/loader.test.js +276 -0
- package/dist/config/loader.test.js.map +1 -0
- package/dist/git/changeTracker.d.ts +44 -0
- package/dist/git/changeTracker.js +149 -0
- package/dist/git/changeTracker.js.map +1 -0
- package/dist/git/changeTracker.test.d.ts +1 -0
- package/dist/git/changeTracker.test.js +184 -0
- package/dist/git/changeTracker.test.js.map +1 -0
- package/dist/graph/codeDocGraph.d.ts +43 -0
- package/dist/graph/codeDocGraph.js +103 -0
- package/dist/graph/codeDocGraph.js.map +1 -0
- package/dist/graph/codeDocGraph.test.d.ts +1 -0
- package/dist/graph/codeDocGraph.test.js +78 -0
- package/dist/graph/codeDocGraph.test.js.map +1 -0
- package/dist/graph/graphBuilder.d.ts +17 -0
- package/dist/graph/graphBuilder.js +76 -0
- package/dist/graph/graphBuilder.js.map +1 -0
- package/dist/graph/graphBuilder.test.d.ts +1 -0
- package/dist/graph/graphBuilder.test.js +87 -0
- package/dist/graph/graphBuilder.test.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers/documentParser.d.ts +22 -0
- package/dist/parsers/documentParser.js +76 -0
- package/dist/parsers/documentParser.js.map +1 -0
- package/dist/parsers/documentParser.test.d.ts +1 -0
- package/dist/parsers/documentParser.test.js +116 -0
- package/dist/parsers/documentParser.test.js.map +1 -0
- package/dist/parsers/extractors/baseExtractor.d.ts +19 -0
- package/dist/parsers/extractors/baseExtractor.js +33 -0
- package/dist/parsers/extractors/baseExtractor.js.map +1 -0
- package/dist/parsers/extractors/baseExtractor.test.d.ts +1 -0
- package/dist/parsers/extractors/baseExtractor.test.js +43 -0
- package/dist/parsers/extractors/baseExtractor.test.js.map +1 -0
- package/dist/parsers/extractors/codePatternExtractor.d.ts +13 -0
- package/dist/parsers/extractors/codePatternExtractor.js +108 -0
- package/dist/parsers/extractors/codePatternExtractor.js.map +1 -0
- package/dist/parsers/extractors/codePatternExtractor.test.d.ts +1 -0
- package/dist/parsers/extractors/codePatternExtractor.test.js +49 -0
- package/dist/parsers/extractors/codePatternExtractor.test.js.map +1 -0
- package/dist/parsers/extractors/dependencyExtractor.d.ts +12 -0
- package/dist/parsers/extractors/dependencyExtractor.js +92 -0
- package/dist/parsers/extractors/dependencyExtractor.js.map +1 -0
- package/dist/parsers/extractors/dependencyExtractor.test.d.ts +1 -0
- package/dist/parsers/extractors/dependencyExtractor.test.js +48 -0
- package/dist/parsers/extractors/dependencyExtractor.test.js.map +1 -0
- package/dist/parsers/extractors/directoryStructureExtractor.d.ts +34 -0
- package/dist/parsers/extractors/directoryStructureExtractor.js +168 -0
- package/dist/parsers/extractors/directoryStructureExtractor.js.map +1 -0
- package/dist/parsers/extractors/directoryStructureExtractor.test.d.ts +1 -0
- package/dist/parsers/extractors/directoryStructureExtractor.test.js +121 -0
- package/dist/parsers/extractors/directoryStructureExtractor.test.js.map +1 -0
- package/dist/parsers/extractors/externalUrlExtractor.d.ts +14 -0
- package/dist/parsers/extractors/externalUrlExtractor.js +53 -0
- package/dist/parsers/extractors/externalUrlExtractor.js.map +1 -0
- package/dist/parsers/extractors/externalUrlExtractor.test.d.ts +1 -0
- package/dist/parsers/extractors/externalUrlExtractor.test.js +85 -0
- package/dist/parsers/extractors/externalUrlExtractor.test.js.map +1 -0
- package/dist/parsers/extractors/filePathExtractor.d.ts +18 -0
- package/dist/parsers/extractors/filePathExtractor.js +72 -0
- package/dist/parsers/extractors/filePathExtractor.js.map +1 -0
- package/dist/parsers/extractors/filePathExtractor.test.d.ts +1 -0
- package/dist/parsers/extractors/filePathExtractor.test.js +73 -0
- package/dist/parsers/extractors/filePathExtractor.test.js.map +1 -0
- package/dist/parsers/extractors/versionExtractor.d.ts +11 -0
- package/dist/parsers/extractors/versionExtractor.js +74 -0
- package/dist/parsers/extractors/versionExtractor.js.map +1 -0
- package/dist/parsers/extractors/versionExtractor.test.d.ts +1 -0
- package/dist/parsers/extractors/versionExtractor.test.js +55 -0
- package/dist/parsers/extractors/versionExtractor.test.js.map +1 -0
- package/dist/plugins/plugin.d.ts +32 -0
- package/dist/plugins/plugin.js +40 -0
- package/dist/plugins/plugin.js.map +1 -0
- package/dist/plugins/plugin.test.d.ts +1 -0
- package/dist/plugins/plugin.test.js +23 -0
- package/dist/plugins/plugin.test.js.map +1 -0
- package/dist/reporters/consoleReporter.d.ts +15 -0
- package/dist/reporters/consoleReporter.js +73 -0
- package/dist/reporters/consoleReporter.js.map +1 -0
- package/dist/reporters/consoleReporter.test.d.ts +1 -0
- package/dist/reporters/consoleReporter.test.js +155 -0
- package/dist/reporters/consoleReporter.test.js.map +1 -0
- package/dist/reporters/enhancedReporter.d.ts +12 -0
- package/dist/reporters/enhancedReporter.js +81 -0
- package/dist/reporters/enhancedReporter.js.map +1 -0
- package/dist/reporters/enhancedReporter.test.d.ts +1 -0
- package/dist/reporters/enhancedReporter.test.js +152 -0
- package/dist/reporters/enhancedReporter.test.js.map +1 -0
- package/dist/reporters/jsonReporter.d.ts +11 -0
- package/dist/reporters/jsonReporter.js +20 -0
- package/dist/reporters/jsonReporter.js.map +1 -0
- package/dist/reporters/jsonReporter.test.d.ts +1 -0
- package/dist/reporters/jsonReporter.test.js +31 -0
- package/dist/reporters/jsonReporter.test.js.map +1 -0
- package/dist/reporters/markdownReporter.d.ts +11 -0
- package/dist/reporters/markdownReporter.js +55 -0
- package/dist/reporters/markdownReporter.js.map +1 -0
- package/dist/reporters/markdownReporter.test.d.ts +1 -0
- package/dist/reporters/markdownReporter.test.js +136 -0
- package/dist/reporters/markdownReporter.test.js.map +1 -0
- package/dist/runner.d.ts +9 -0
- package/dist/runner.js +265 -0
- package/dist/runner.js.map +1 -0
- package/dist/runner.test.d.ts +1 -0
- package/dist/runner.test.js +353 -0
- package/dist/runner.test.js.map +1 -0
- package/dist/scoring/freshnessScorer.d.ts +40 -0
- package/dist/scoring/freshnessScorer.js +170 -0
- package/dist/scoring/freshnessScorer.js.map +1 -0
- package/dist/scoring/freshnessScorer.test.d.ts +1 -0
- package/dist/scoring/freshnessScorer.test.js +397 -0
- package/dist/scoring/freshnessScorer.test.js.map +1 -0
- package/dist/semantic/vectorSearch.d.ts +84 -0
- package/dist/semantic/vectorSearch.js +484 -0
- package/dist/semantic/vectorSearch.js.map +1 -0
- package/dist/semantic/vectorSearch.test.d.ts +1 -0
- package/dist/semantic/vectorSearch.test.js +660 -0
- package/dist/semantic/vectorSearch.test.js.map +1 -0
- package/dist/setupTests.d.ts +4 -0
- package/dist/setupTests.js +11 -0
- package/dist/setupTests.js.map +1 -0
- package/dist/test-utils/console.d.ts +2 -0
- package/dist/test-utils/console.js +3 -0
- package/dist/test-utils/console.js.map +1 -0
- package/dist/test-utils/factories.d.ts +3 -0
- package/dist/test-utils/factories.js +25 -0
- package/dist/test-utils/factories.js.map +1 -0
- package/dist/test-utils/tempFiles.d.ts +1 -0
- package/dist/test-utils/tempFiles.js +12 -0
- package/dist/test-utils/tempFiles.js.map +1 -0
- package/dist/types.d.ts +304 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/boundedMap.d.ts +8 -0
- package/dist/utils/boundedMap.js +22 -0
- package/dist/utils/boundedMap.js.map +1 -0
- package/dist/utils/boundedMap.test.d.ts +1 -0
- package/dist/utils/boundedMap.test.js +57 -0
- package/dist/utils/boundedMap.test.js.map +1 -0
- package/dist/utils/illustrativePatterns.d.ts +28 -0
- package/dist/utils/illustrativePatterns.js +80 -0
- package/dist/utils/illustrativePatterns.js.map +1 -0
- package/dist/utils/illustrativePatterns.test.d.ts +1 -0
- package/dist/utils/illustrativePatterns.test.js +48 -0
- package/dist/utils/illustrativePatterns.test.js.map +1 -0
- package/dist/utils/incremental.d.ts +36 -0
- package/dist/utils/incremental.js +87 -0
- package/dist/utils/incremental.js.map +1 -0
- package/dist/utils/incremental.test.d.ts +1 -0
- package/dist/utils/incremental.test.js +84 -0
- package/dist/utils/incremental.test.js.map +1 -0
- package/dist/utils/parallel.d.ts +14 -0
- package/dist/utils/parallel.js +43 -0
- package/dist/utils/parallel.js.map +1 -0
- package/dist/utils/parallel.test.d.ts +1 -0
- package/dist/utils/parallel.test.js +48 -0
- package/dist/utils/parallel.test.js.map +1 -0
- package/dist/utils/pathSecurity.d.ts +12 -0
- package/dist/utils/pathSecurity.js +22 -0
- package/dist/utils/pathSecurity.js.map +1 -0
- package/dist/utils/pathSecurity.test.d.ts +1 -0
- package/dist/utils/pathSecurity.test.js +34 -0
- package/dist/utils/pathSecurity.test.js.map +1 -0
- package/dist/utils/similarity.d.ts +12 -0
- package/dist/utils/similarity.js +64 -0
- package/dist/utils/similarity.js.map +1 -0
- package/dist/utils/similarity.test.d.ts +1 -0
- package/dist/utils/similarity.test.js +49 -0
- package/dist/utils/similarity.test.js.map +1 -0
- package/dist/utils/validation.d.ts +13 -0
- package/dist/utils/validation.js +24 -0
- package/dist/utils/validation.js.map +1 -0
- package/dist/utils/validation.test.d.ts +1 -0
- package/dist/utils/validation.test.js +28 -0
- package/dist/utils/validation.test.js.map +1 -0
- package/dist/validators/codePatternValidator.d.ts +28 -0
- package/dist/validators/codePatternValidator.js +200 -0
- package/dist/validators/codePatternValidator.js.map +1 -0
- package/dist/validators/codePatternValidator.test.d.ts +1 -0
- package/dist/validators/codePatternValidator.test.js +86 -0
- package/dist/validators/codePatternValidator.test.js.map +1 -0
- package/dist/validators/dependencyValidator.d.ts +12 -0
- package/dist/validators/dependencyValidator.js +102 -0
- package/dist/validators/dependencyValidator.js.map +1 -0
- package/dist/validators/dependencyValidator.test.d.ts +1 -0
- package/dist/validators/dependencyValidator.test.js +179 -0
- package/dist/validators/dependencyValidator.test.js.map +1 -0
- package/dist/validators/directoryValidator.d.ts +30 -0
- package/dist/validators/directoryValidator.js +192 -0
- package/dist/validators/directoryValidator.js.map +1 -0
- package/dist/validators/directoryValidator.test.d.ts +1 -0
- package/dist/validators/directoryValidator.test.js +193 -0
- package/dist/validators/directoryValidator.test.js.map +1 -0
- package/dist/validators/fileValidator.d.ts +16 -0
- package/dist/validators/fileValidator.js +114 -0
- package/dist/validators/fileValidator.js.map +1 -0
- package/dist/validators/fileValidator.test.d.ts +1 -0
- package/dist/validators/fileValidator.test.js +108 -0
- package/dist/validators/fileValidator.test.js.map +1 -0
- package/dist/validators/urlValidator.d.ts +25 -0
- package/dist/validators/urlValidator.js +320 -0
- package/dist/validators/urlValidator.js.map +1 -0
- package/dist/validators/urlValidator.test.d.ts +1 -0
- package/dist/validators/urlValidator.test.js +252 -0
- package/dist/validators/urlValidator.test.js.map +1 -0
- package/dist/validators/validationEngine.d.ts +23 -0
- package/dist/validators/validationEngine.js +117 -0
- package/dist/validators/validationEngine.js.map +1 -0
- package/dist/validators/validationEngine.test.d.ts +1 -0
- package/dist/validators/validationEngine.test.js +82 -0
- package/dist/validators/validationEngine.test.js.map +1 -0
- package/dist/validators/versionValidator.d.ts +18 -0
- package/dist/validators/versionValidator.js +211 -0
- package/dist/validators/versionValidator.js.map +1 -0
- package/dist/validators/versionValidator.test.d.ts +1 -0
- package/dist/validators/versionValidator.test.js +308 -0
- package/dist/validators/versionValidator.test.js.map +1 -0
- package/package.json +98 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 cosmocoder
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
# Documentation Freshness Checker
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/doc-freshness-checker)
|
|
4
|
+
[](https://www.npmjs.com/package/doc-freshness-checker)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://nodejs.org/)
|
|
7
|
+
[](https://github.com/cosmocoder/doc-freshness-checker/actions/workflows/release.yml)
|
|
8
|
+
|
|
9
|
+
Validate documentation against your codebase to catch stale references before they create confusion or slow onboarding. `doc-freshness-checker` scans your docs, extracts references to files, URLs, versions, symbols, dependencies, and directory trees, then validates each one against source code and manifests.
|
|
10
|
+
|
|
11
|
+
## Table of Contents
|
|
12
|
+
|
|
13
|
+
- [Why Use It](#why-use-it)
|
|
14
|
+
- [Features](#features)
|
|
15
|
+
- [Prerequisites](#prerequisites)
|
|
16
|
+
- [Installation](#installation)
|
|
17
|
+
- [Quick Start](#quick-start)
|
|
18
|
+
- [CLI Reference](#cli-reference)
|
|
19
|
+
- [What Gets Validated](#what-gets-validated)
|
|
20
|
+
- [Configuration](#configuration)
|
|
21
|
+
- [CI Integration](#ci-integration)
|
|
22
|
+
- [Programmatic API](#programmatic-api)
|
|
23
|
+
- [Contributing](#contributing)
|
|
24
|
+
- [Acknowledgments](#acknowledgments)
|
|
25
|
+
- [License](#license)
|
|
26
|
+
|
|
27
|
+
## Why Use It
|
|
28
|
+
|
|
29
|
+
- Reduce drift between docs and implementation.
|
|
30
|
+
- Catch broken file links and dead external URLs.
|
|
31
|
+
- Detect version mismatches between docs and manifests.
|
|
32
|
+
- Surface code symbols mentioned in docs that no longer exist.
|
|
33
|
+
- Run in CI as a quality gate or reporting step.
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
- **Documentation formats:** Markdown (`.md`, `.markdown`), reStructuredText (`.rst`), AsciiDoc (`.adoc`, `.asciidoc`), plaintext (`.txt`).
|
|
38
|
+
- **Source indexing for symbol validation:** JavaScript, TypeScript, Python, Go, Rust, Java.
|
|
39
|
+
- **Manifest parsing for version/dependency checks:** `package.json`, `requirements.txt`, `pyproject.toml`, `go.mod`, `Cargo.toml`, `pom.xml`.
|
|
40
|
+
- **Reporters:** `console`, `json`, `markdown`, `enhanced`.
|
|
41
|
+
- **Advanced modes:** incremental checking, freshness scoring, graph linking, semantic vector search.
|
|
42
|
+
|
|
43
|
+
## Prerequisites
|
|
44
|
+
|
|
45
|
+
- **Node.js** >= 22.19.0
|
|
46
|
+
- **npm**
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm install --save-dev doc-freshness-checker
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Global installation is also supported:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npm install -g doc-freshness-checker
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Quick Start
|
|
61
|
+
|
|
62
|
+
Run with defaults (checks `docs/**/*.md` and `README.md`):
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
doc-freshness
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Example console output:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
📚 Documentation Freshness Report
|
|
72
|
+
|
|
73
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
74
|
+
|
|
75
|
+
📊 Summary:
|
|
76
|
+
Total references checked: 42
|
|
77
|
+
✅ Valid: 38
|
|
78
|
+
❌ Errors: 2
|
|
79
|
+
⚠️ Warnings: 2
|
|
80
|
+
⏭️ Skipped: 0
|
|
81
|
+
|
|
82
|
+
📋 Issues by Document:
|
|
83
|
+
|
|
84
|
+
📄 docs/getting-started.md
|
|
85
|
+
────────────────────────────────────────
|
|
86
|
+
❌ Line 14: File not found: src/old-module.ts
|
|
87
|
+
💡 File may have been moved or renamed
|
|
88
|
+
⚠️ Line 27: Version mismatch: docs say 2.1.0, package.json has 3.0.0
|
|
89
|
+
|
|
90
|
+
📄 README.md
|
|
91
|
+
────────────────────────────────────────
|
|
92
|
+
❌ Line 45: Broken URL: https://example.com/dead-link (404)
|
|
93
|
+
⚠️ Line 82: Symbol not found in source: calculateTotal()
|
|
94
|
+
💡 Symbol may have been renamed or removed
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Create a minimal config:
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
// .doc-freshness.config.js
|
|
101
|
+
export default {
|
|
102
|
+
include: ['docs/**/*.md', 'README.md'],
|
|
103
|
+
exclude: ['**/node_modules/**'],
|
|
104
|
+
};
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Generate a Markdown report file:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
doc-freshness --reporter markdown --output .doc-freshness-reports/report.md
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## CLI Reference
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
doc-freshness [options]
|
|
117
|
+
|
|
118
|
+
Options:
|
|
119
|
+
-c, --config <path> Path to config file
|
|
120
|
+
-r, --reporter <type> Reporter type (console, json, markdown, enhanced)
|
|
121
|
+
-o, --output <path> Output file path for reports
|
|
122
|
+
-v, --verbose Enable verbose logging
|
|
123
|
+
--only <types> Only check specific reference types (comma-separated)
|
|
124
|
+
--skip-urls Skip URL validation
|
|
125
|
+
--files <patterns> Only check specific files (comma-separated glob patterns)
|
|
126
|
+
--manifest <files> Manifest files to use (comma-separated)
|
|
127
|
+
--source <patterns> Source patterns to use (comma-separated)
|
|
128
|
+
--no-cache Disable caching
|
|
129
|
+
--clear-cache Clear cache before running
|
|
130
|
+
--score Include freshness scores in report
|
|
131
|
+
--incremental Only check changed files (requires cache)
|
|
132
|
+
--vector-search Enable semantic vector search for doc-code mismatches
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Common Examples
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# Basic scan
|
|
139
|
+
doc-freshness
|
|
140
|
+
|
|
141
|
+
# JSON report file
|
|
142
|
+
doc-freshness --reporter json --output reports/doc-freshness.json
|
|
143
|
+
|
|
144
|
+
# Faster run when network checks are not needed
|
|
145
|
+
doc-freshness --skip-urls
|
|
146
|
+
|
|
147
|
+
# Restrict to selected files
|
|
148
|
+
doc-freshness --files "README.md,docs/usage.md"
|
|
149
|
+
|
|
150
|
+
# Enable scoring and incremental mode
|
|
151
|
+
doc-freshness --score --incremental
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Exit Codes
|
|
155
|
+
|
|
156
|
+
- `0`: no validation errors
|
|
157
|
+
- `1`: at least one validation error, or runtime/config failure
|
|
158
|
+
|
|
159
|
+
## What Gets Validated
|
|
160
|
+
|
|
161
|
+
| Reference type | What is checked |
|
|
162
|
+
| --------------------- | --------------------------------------------------------- |
|
|
163
|
+
| `file-path` | Referenced files/directories exist |
|
|
164
|
+
| `external-url` | External URLs are reachable (with caching and skip rules) |
|
|
165
|
+
| `version` | Mentioned versions compared to parsed manifests |
|
|
166
|
+
| `directory-structure` | Tree snippets align with actual structure |
|
|
167
|
+
| `code-pattern` | Mentioned symbols exist in indexed source |
|
|
168
|
+
| `dependency` | Mentioned packages exist in manifest dependencies |
|
|
169
|
+
|
|
170
|
+
### URL Validation Behavior
|
|
171
|
+
|
|
172
|
+
- Deduplicates repeated URLs per run.
|
|
173
|
+
- Skips template-like URLs with placeholders.
|
|
174
|
+
- Supports skip domains (defaults include `localhost`, `127.0.0.1`, `example.com`).
|
|
175
|
+
- Treats auth-required responses (`401`/`403`) as valid-with-note.
|
|
176
|
+
- Handles GitHub `404` private-repo behavior conservatively.
|
|
177
|
+
- Caches URL results to reduce repeated checks.
|
|
178
|
+
|
|
179
|
+
### False Positive Reduction
|
|
180
|
+
|
|
181
|
+
Illustrative paths and symbols (e.g., tutorial placeholders) are detected automatically and can be skipped or downgraded in severity via rule configuration.
|
|
182
|
+
|
|
183
|
+
## Configuration
|
|
184
|
+
|
|
185
|
+
### Auto-Discovery
|
|
186
|
+
|
|
187
|
+
When `--config` is not provided, the loader checks these names in project root:
|
|
188
|
+
|
|
189
|
+
- `.doc-freshness.config.js`
|
|
190
|
+
- `.doc-freshness.config.json`
|
|
191
|
+
- `doc-freshness.config.js`
|
|
192
|
+
- `doc-freshness.config.json`
|
|
193
|
+
|
|
194
|
+
### Explicit Config Path
|
|
195
|
+
|
|
196
|
+
With `--config`, the loader supports `.json`, `.cjs`, and JS/ESM config files.
|
|
197
|
+
|
|
198
|
+
### Type-Safe Config
|
|
199
|
+
|
|
200
|
+
Use `defineConfig` for better editor inference:
|
|
201
|
+
|
|
202
|
+
```js
|
|
203
|
+
import { defineConfig } from 'doc-freshness-checker';
|
|
204
|
+
|
|
205
|
+
export default defineConfig({
|
|
206
|
+
include: ['docs/**/*.md', 'README.md'],
|
|
207
|
+
rules: {
|
|
208
|
+
'file-path': { enabled: true, severity: 'error' },
|
|
209
|
+
'external-url': { enabled: true, severity: 'warning' },
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
<details>
|
|
215
|
+
<summary>Full configuration example</summary>
|
|
216
|
+
|
|
217
|
+
```js
|
|
218
|
+
/** @type {import('doc-freshness-checker').DocFreshnessConfig} */
|
|
219
|
+
export default {
|
|
220
|
+
rootDir: process.cwd(),
|
|
221
|
+
include: ['docs/**/*.md', 'README.md'],
|
|
222
|
+
exclude: ['**/node_modules/**', '**/dist/**'],
|
|
223
|
+
|
|
224
|
+
// Optional: auto-detected when null/omitted
|
|
225
|
+
manifestFiles: null,
|
|
226
|
+
sourcePatterns: null,
|
|
227
|
+
|
|
228
|
+
urlValidation: {
|
|
229
|
+
enabled: true,
|
|
230
|
+
timeout: 10000,
|
|
231
|
+
concurrency: 5,
|
|
232
|
+
skipDomains: ['localhost', '127.0.0.1', 'example.com'],
|
|
233
|
+
cacheSeconds: 3600,
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
rules: {
|
|
237
|
+
'file-path': { enabled: true, severity: 'error' },
|
|
238
|
+
'external-url': { enabled: true, severity: 'warning' },
|
|
239
|
+
version: { enabled: true, severity: 'warning' },
|
|
240
|
+
'directory-structure': { enabled: true, severity: 'warning' },
|
|
241
|
+
'code-pattern': { enabled: true, severity: 'warning' },
|
|
242
|
+
dependency: { enabled: true, severity: 'info' },
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
reporters: ['console'],
|
|
246
|
+
cache: { enabled: true, dir: '.doc-freshness-cache', maxAge: 86400000 },
|
|
247
|
+
incremental: { enabled: false },
|
|
248
|
+
vectorSearch: { enabled: false, similarityThreshold: 0.3 },
|
|
249
|
+
verbose: false,
|
|
250
|
+
};
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
</details>
|
|
254
|
+
|
|
255
|
+
For details on CLI-to-config mapping and precedence, see [CLI and Configuration Precedence](docs/cli-and-config-precedence.md). For the internal execution pipeline, see [Runtime Architecture](docs/runtime-architecture.md).
|
|
256
|
+
|
|
257
|
+
## CI Integration
|
|
258
|
+
|
|
259
|
+
This repository includes a GitHub Actions workflow at `.github/workflows/doc-freshness.yml` for a working example.
|
|
260
|
+
|
|
261
|
+
Minimal CI command:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
npx doc-freshness --reporter markdown --output .doc-freshness-reports/report.md
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
The same command works in GitHub Actions, GitLab CI, CircleCI, and other CI systems.
|
|
268
|
+
|
|
269
|
+
## Programmatic API
|
|
270
|
+
|
|
271
|
+
```js
|
|
272
|
+
import { loadConfig, run } from 'doc-freshness-checker';
|
|
273
|
+
|
|
274
|
+
const config = await loadConfig();
|
|
275
|
+
const results = await run(config);
|
|
276
|
+
|
|
277
|
+
console.log(results.summary);
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Key exports:
|
|
281
|
+
|
|
282
|
+
| Export | Purpose |
|
|
283
|
+
| ------------------------------------------------------------------------- | ---------------------------------------------------- |
|
|
284
|
+
| `run`, `runWithConfig` | Execute the checker programmatically |
|
|
285
|
+
| `loadConfig`, `defineConfig` | Load/merge configuration and type-safe config helper |
|
|
286
|
+
| `DocumentParser` | Parse documentation files and extract references |
|
|
287
|
+
| `ValidationEngine` | Dispatch references to validators |
|
|
288
|
+
| `ConsoleReporter`, `JsonReporter`, `MarkdownReporter`, `EnhancedReporter` | Reporter classes |
|
|
289
|
+
| `GraphBuilder`, `FreshnessScorer`, `VectorSearch` | Advanced analysis modules |
|
|
290
|
+
| `DocFreshnessConfig`, `ValidationResults` | Core types |
|
|
291
|
+
|
|
292
|
+
## Contributing
|
|
293
|
+
|
|
294
|
+
Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for setup instructions, development workflow, and PR guidelines.
|
|
295
|
+
|
|
296
|
+
## Acknowledgments
|
|
297
|
+
|
|
298
|
+
- [Commander.js](https://github.com/tj/commander.js/) — CLI argument parsing
|
|
299
|
+
- [glob](https://github.com/isaacs/node-glob) — File pattern matching
|
|
300
|
+
- [semver](https://github.com/npm/node-semver) — Semantic version parsing and comparison
|
|
301
|
+
- [FastEmbed](https://github.com/Anush008/fastembed) — Local embedding generation for semantic checks
|
|
302
|
+
|
|
303
|
+
## License
|
|
304
|
+
|
|
305
|
+
MIT License — see [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { CodeDocGraph } from '../graph/codeDocGraph.js';
|
|
2
|
+
import type { CacheStats2, DocFreshnessConfig, UrlCacheEntry } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Manages caching of graph and URL validation results
|
|
5
|
+
*/
|
|
6
|
+
export declare class CacheManager {
|
|
7
|
+
private config;
|
|
8
|
+
private cacheDir;
|
|
9
|
+
private cacheFile;
|
|
10
|
+
private urlCacheFile;
|
|
11
|
+
constructor(config: DocFreshnessConfig);
|
|
12
|
+
private ensureCacheDir;
|
|
13
|
+
/**
|
|
14
|
+
* Save the code-to-doc graph
|
|
15
|
+
*/
|
|
16
|
+
saveGraph(graph: CodeDocGraph): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Load the cached graph
|
|
19
|
+
*/
|
|
20
|
+
loadGraph(): Promise<CodeDocGraph | null>;
|
|
21
|
+
/**
|
|
22
|
+
* Check if cache is valid based on config hash and git state
|
|
23
|
+
*/
|
|
24
|
+
isCacheValid(graph: CodeDocGraph | null, currentCommit: string | null): boolean;
|
|
25
|
+
private getConfigHash;
|
|
26
|
+
/**
|
|
27
|
+
* Save URL validation cache
|
|
28
|
+
*/
|
|
29
|
+
saveUrlCache(urlResults: Record<string, UrlCacheEntry>): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Load URL validation cache
|
|
32
|
+
*/
|
|
33
|
+
loadUrlCache(): Promise<Record<string, UrlCacheEntry>>;
|
|
34
|
+
/**
|
|
35
|
+
* Clear all caches
|
|
36
|
+
*/
|
|
37
|
+
clearCache(): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Get cache statistics
|
|
40
|
+
*/
|
|
41
|
+
getCacheStats(): Promise<CacheStats2>;
|
|
42
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import crypto from 'crypto';
|
|
4
|
+
import { CodeDocGraph } from '../graph/codeDocGraph.js';
|
|
5
|
+
import { isWithinRoot, resolveProjectRoot } from '../utils/pathSecurity.js';
|
|
6
|
+
/**
|
|
7
|
+
* Manages caching of graph and URL validation results
|
|
8
|
+
*/
|
|
9
|
+
export class CacheManager {
|
|
10
|
+
config;
|
|
11
|
+
cacheDir;
|
|
12
|
+
cacheFile;
|
|
13
|
+
urlCacheFile;
|
|
14
|
+
constructor(config) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
const rawDir = config.cache?.dir || config.graph?.cacheDir || '.doc-freshness-cache';
|
|
17
|
+
const rootDir = resolveProjectRoot(config.rootDir);
|
|
18
|
+
// Path traversal protection: resolve and verify cache dir stays within project root
|
|
19
|
+
const resolved = path.resolve(rootDir, rawDir);
|
|
20
|
+
if (!isWithinRoot(resolved, rootDir)) {
|
|
21
|
+
throw new Error(`Cache directory "${rawDir}" resolves outside project root`);
|
|
22
|
+
}
|
|
23
|
+
this.cacheDir = resolved;
|
|
24
|
+
this.cacheFile = path.join(this.cacheDir, 'graph-cache.json');
|
|
25
|
+
this.urlCacheFile = path.join(this.cacheDir, 'url-cache.json');
|
|
26
|
+
}
|
|
27
|
+
async ensureCacheDir() {
|
|
28
|
+
await fs.promises.mkdir(this.cacheDir, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Save the code-to-doc graph
|
|
32
|
+
*/
|
|
33
|
+
async saveGraph(graph) {
|
|
34
|
+
await this.ensureCacheDir();
|
|
35
|
+
const data = graph.serialize();
|
|
36
|
+
data.configHash = this.getConfigHash();
|
|
37
|
+
await fs.promises.writeFile(this.cacheFile, JSON.stringify(data, null, 2));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Load the cached graph
|
|
41
|
+
*/
|
|
42
|
+
async loadGraph() {
|
|
43
|
+
try {
|
|
44
|
+
const content = await fs.promises.readFile(this.cacheFile, 'utf-8');
|
|
45
|
+
const data = JSON.parse(content);
|
|
46
|
+
return CodeDocGraph.deserialize(data);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Check if cache is valid based on config hash and git state
|
|
54
|
+
*/
|
|
55
|
+
isCacheValid(graph, currentCommit) {
|
|
56
|
+
if (!graph)
|
|
57
|
+
return false;
|
|
58
|
+
if (!graph.buildTimestamp)
|
|
59
|
+
return false;
|
|
60
|
+
const configHash = this.getConfigHash();
|
|
61
|
+
if (graph.configHash && graph.configHash !== configHash) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
if (currentCommit && graph.gitCommit) {
|
|
65
|
+
return currentCommit === graph.gitCommit;
|
|
66
|
+
}
|
|
67
|
+
const maxAge = this.config.cache?.maxAge || this.config.graph?.cacheMaxAge || 24 * 60 * 60 * 1000;
|
|
68
|
+
return Date.now() - graph.buildTimestamp < maxAge;
|
|
69
|
+
}
|
|
70
|
+
getConfigHash() {
|
|
71
|
+
const relevantConfig = {
|
|
72
|
+
include: this.config.include,
|
|
73
|
+
exclude: this.config.exclude,
|
|
74
|
+
sourcePatterns: this.config.sourcePatterns,
|
|
75
|
+
manifestFiles: this.config.manifestFiles,
|
|
76
|
+
};
|
|
77
|
+
return crypto.createHash('md5').update(JSON.stringify(relevantConfig)).digest('hex');
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Save URL validation cache
|
|
81
|
+
*/
|
|
82
|
+
async saveUrlCache(urlResults) {
|
|
83
|
+
await this.ensureCacheDir();
|
|
84
|
+
await fs.promises.writeFile(this.urlCacheFile, JSON.stringify(urlResults, null, 2));
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Load URL validation cache
|
|
88
|
+
*/
|
|
89
|
+
async loadUrlCache() {
|
|
90
|
+
try {
|
|
91
|
+
const content = await fs.promises.readFile(this.urlCacheFile, 'utf-8');
|
|
92
|
+
return JSON.parse(content);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return {};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Clear all caches
|
|
100
|
+
*/
|
|
101
|
+
async clearCache() {
|
|
102
|
+
try {
|
|
103
|
+
await fs.promises.rm(this.cacheDir, { recursive: true });
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// Directory doesn't exist
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get cache statistics
|
|
111
|
+
*/
|
|
112
|
+
async getCacheStats() {
|
|
113
|
+
const stats = {
|
|
114
|
+
exists: false,
|
|
115
|
+
graphSize: 0,
|
|
116
|
+
urlCacheSize: 0,
|
|
117
|
+
lastUpdated: null,
|
|
118
|
+
};
|
|
119
|
+
try {
|
|
120
|
+
const graphStat = await fs.promises.stat(this.cacheFile);
|
|
121
|
+
stats.exists = true;
|
|
122
|
+
stats.graphSize = graphStat.size;
|
|
123
|
+
stats.lastUpdated = graphStat.mtime;
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// File doesn't exist
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
const urlStat = await fs.promises.stat(this.urlCacheFile);
|
|
130
|
+
stats.urlCacheSize = urlStat.size;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// File doesn't exist
|
|
134
|
+
}
|
|
135
|
+
return stats;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=cacheManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cacheManager.js","sourceRoot":"","sources":["../../src/cache/cacheManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAG5E;;GAEG;AACH,MAAM,OAAO,YAAY;IACf,MAAM,CAAqB;IAC3B,QAAQ,CAAS;IACjB,SAAS,CAAS;IAClB,YAAY,CAAS;IAE7B,YAAY,MAA0B;QACpC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,GAAG,IAAI,MAAM,CAAC,KAAK,EAAE,QAAQ,IAAI,sBAAsB,CAAC;QACrF,MAAM,OAAO,GAAG,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAEnD,oFAAoF;QACpF,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC/C,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,oBAAoB,MAAM,iCAAiC,CAAC,CAAC;QAC/E,CAAC;QAED,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;QAC9D,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC;IACjE,CAAC;IAEO,KAAK,CAAC,cAAc;QAC1B,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,SAAS,CAAC,KAAmB;QACjC,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;QAC/B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QACvC,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC7E,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,SAAS;QACb,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YACpE,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAoB,CAAC;YACpD,OAAO,YAAY,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED;;OAEG;IACH,YAAY,CAAC,KAA0B,EAAE,aAA4B;QACnE,IAAI,CAAC,KAAK;YAAE,OAAO,KAAK,CAAC;QACzB,IAAI,CAAC,KAAK,CAAC,cAAc;YAAE,OAAO,KAAK,CAAC;QAExC,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QACxC,IAAI,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;YACxD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,aAAa,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACrC,OAAO,aAAa,KAAK,KAAK,CAAC,SAAS,CAAC;QAC3C,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,WAAW,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;QAClG,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,cAAc,GAAG,MAAM,CAAC;IACpD,CAAC;IAEO,aAAa;QACnB,MAAM,cAAc,GAAG;YACrB,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO;YAC5B,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO;YAC5B,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,cAAc;YAC1C,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,aAAa;SACzC,CAAC;QACF,OAAO,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvF,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAAC,UAAyC;QAC1D,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC5B,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACtF,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY;QAChB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;YACvE,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAkC,CAAC;QAC9D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU;QACd,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC;YACP,0BAA0B;QAC5B,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa;QACjB,MAAM,KAAK,GAAgB;YACzB,MAAM,EAAE,KAAK;YACb,SAAS,EAAE,CAAC;YACZ,YAAY,EAAE,CAAC;YACf,WAAW,EAAE,IAAI;SAClB,CAAC;QAEF,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACzD,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;YACpB,KAAK,CAAC,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC;YACjC,KAAK,CAAC,WAAW,GAAG,SAAS,CAAC,KAAK,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,qBAAqB;QACvB,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC1D,KAAK,CAAC,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,qBAAqB;QACvB,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { CacheManager } from './cacheManager.js';
|
|
4
|
+
import { CodeDocGraph } from '../graph/codeDocGraph.js';
|
|
5
|
+
describe('CacheManager', () => {
|
|
6
|
+
const cacheDir = path.join(process.cwd(), '.doc-freshness-cache', 'test-cache');
|
|
7
|
+
const config = {
|
|
8
|
+
rootDir: process.cwd(),
|
|
9
|
+
cache: { dir: '.doc-freshness-cache/test-cache' },
|
|
10
|
+
};
|
|
11
|
+
afterAll(async () => {
|
|
12
|
+
await fs.promises.rm(cacheDir, { recursive: true, force: true }).catch(() => { });
|
|
13
|
+
});
|
|
14
|
+
it('throws if cache dir resolves outside project root', () => {
|
|
15
|
+
const badConfig = {
|
|
16
|
+
rootDir: '/project',
|
|
17
|
+
cache: { dir: '../../etc/evil' },
|
|
18
|
+
};
|
|
19
|
+
expect(() => new CacheManager(badConfig)).toThrow('outside project root');
|
|
20
|
+
});
|
|
21
|
+
describe('graph operations', () => {
|
|
22
|
+
it('saves and loads a graph', async () => {
|
|
23
|
+
const manager = new CacheManager(config);
|
|
24
|
+
const graph = new CodeDocGraph();
|
|
25
|
+
graph.addReference('doc.md', 'src/a.ts', { type: 'file-path', value: 'a.ts', lineNumber: 1, raw: 'a.ts', sourceFile: 'doc.md' });
|
|
26
|
+
graph.buildTimestamp = Date.now();
|
|
27
|
+
await manager.saveGraph(graph);
|
|
28
|
+
const loaded = await manager.loadGraph();
|
|
29
|
+
expect(loaded).not.toBeNull();
|
|
30
|
+
expect(loaded.getCodeReferencedByDoc('doc.md').has('src/a.ts')).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
it('loadGraph returns null when no cache exists', async () => {
|
|
33
|
+
const freshConfig = {
|
|
34
|
+
rootDir: process.cwd(),
|
|
35
|
+
cache: { dir: '.doc-freshness-cache/nonexistent-test' },
|
|
36
|
+
};
|
|
37
|
+
const manager = new CacheManager(freshConfig);
|
|
38
|
+
expect(await manager.loadGraph()).toBeNull();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe('URL cache operations', () => {
|
|
42
|
+
it('saves and loads URL cache', async () => {
|
|
43
|
+
const manager = new CacheManager(config);
|
|
44
|
+
const urlData = { 'https://example.com': { result: { valid: true }, timestamp: Date.now() } };
|
|
45
|
+
await manager.saveUrlCache(urlData);
|
|
46
|
+
const loaded = await manager.loadUrlCache();
|
|
47
|
+
expect(loaded['https://example.com'].result.valid).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
it('returns empty object when URL cache missing', async () => {
|
|
50
|
+
const freshConfig = { rootDir: process.cwd(), cache: { dir: '.doc-freshness-cache/no-url-cache' } };
|
|
51
|
+
const manager = new CacheManager(freshConfig);
|
|
52
|
+
expect(await manager.loadUrlCache()).toEqual({});
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe('isCacheValid', () => {
|
|
56
|
+
it('returns false for null graph', () => {
|
|
57
|
+
const manager = new CacheManager(config);
|
|
58
|
+
expect(manager.isCacheValid(null, null)).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
it('returns false when graph has no buildTimestamp', () => {
|
|
61
|
+
const manager = new CacheManager(config);
|
|
62
|
+
const graph = new CodeDocGraph();
|
|
63
|
+
expect(manager.isCacheValid(graph, null)).toBe(false);
|
|
64
|
+
});
|
|
65
|
+
it('returns true when git commit matches', () => {
|
|
66
|
+
const manager = new CacheManager(config);
|
|
67
|
+
const graph = new CodeDocGraph();
|
|
68
|
+
graph.buildTimestamp = Date.now();
|
|
69
|
+
graph.gitCommit = 'abc123';
|
|
70
|
+
expect(manager.isCacheValid(graph, 'abc123')).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
it('returns false when git commit differs', () => {
|
|
73
|
+
const manager = new CacheManager(config);
|
|
74
|
+
const graph = new CodeDocGraph();
|
|
75
|
+
graph.buildTimestamp = Date.now();
|
|
76
|
+
graph.gitCommit = 'abc123';
|
|
77
|
+
expect(manager.isCacheValid(graph, 'def456')).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
it('uses time-based expiry when no git info', () => {
|
|
80
|
+
const manager = new CacheManager({ ...config, cache: { ...config.cache, maxAge: 1000 } });
|
|
81
|
+
const graph = new CodeDocGraph();
|
|
82
|
+
graph.buildTimestamp = Date.now();
|
|
83
|
+
expect(manager.isCacheValid(graph, null)).toBe(true);
|
|
84
|
+
graph.buildTimestamp = Date.now() - 2000;
|
|
85
|
+
expect(manager.isCacheValid(graph, null)).toBe(false);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe('clearCache', () => {
|
|
89
|
+
it('removes the cache directory', async () => {
|
|
90
|
+
const manager = new CacheManager(config);
|
|
91
|
+
await manager.saveUrlCache({ test: { result: { valid: true }, timestamp: Date.now() } });
|
|
92
|
+
await manager.clearCache();
|
|
93
|
+
expect(await manager.loadUrlCache()).toEqual({});
|
|
94
|
+
});
|
|
95
|
+
it('handles non-existent cache directory', async () => {
|
|
96
|
+
const freshConfig = { rootDir: process.cwd(), cache: { dir: '.doc-freshness-cache/ghost' } };
|
|
97
|
+
const manager = new CacheManager(freshConfig);
|
|
98
|
+
await expect(manager.clearCache()).resolves.not.toThrow();
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
describe('isCacheValid - configHash', () => {
|
|
102
|
+
it('returns false when configHash differs', () => {
|
|
103
|
+
const manager = new CacheManager(config);
|
|
104
|
+
const graph = new CodeDocGraph();
|
|
105
|
+
graph.buildTimestamp = Date.now();
|
|
106
|
+
graph.configHash = 'stale-hash';
|
|
107
|
+
expect(manager.isCacheValid(graph, null)).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
it('ignores configHash check when graph has no configHash', () => {
|
|
110
|
+
const manager = new CacheManager(config);
|
|
111
|
+
const graph = new CodeDocGraph();
|
|
112
|
+
graph.buildTimestamp = Date.now();
|
|
113
|
+
graph.configHash = null;
|
|
114
|
+
expect(manager.isCacheValid(graph, null)).toBe(true);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
describe('getCacheStats', () => {
|
|
118
|
+
it('returns stats for existing cache', async () => {
|
|
119
|
+
const manager = new CacheManager(config);
|
|
120
|
+
const graph = new CodeDocGraph();
|
|
121
|
+
graph.buildTimestamp = Date.now();
|
|
122
|
+
await manager.saveGraph(graph);
|
|
123
|
+
const stats = await manager.getCacheStats();
|
|
124
|
+
expect(stats.exists).toBe(true);
|
|
125
|
+
expect(stats.graphSize).toBeGreaterThan(0);
|
|
126
|
+
});
|
|
127
|
+
it('returns empty stats when no cache', async () => {
|
|
128
|
+
const freshConfig = { rootDir: process.cwd(), cache: { dir: '.doc-freshness-cache/no-stats' } };
|
|
129
|
+
const manager = new CacheManager(freshConfig);
|
|
130
|
+
const stats = await manager.getCacheStats();
|
|
131
|
+
expect(stats.exists).toBe(false);
|
|
132
|
+
expect(stats.lastUpdated).toBeNull();
|
|
133
|
+
});
|
|
134
|
+
it('returns URL cache size when URL cache exists', async () => {
|
|
135
|
+
const manager = new CacheManager(config);
|
|
136
|
+
await manager.saveUrlCache({ 'https://x.com': { result: { valid: true }, timestamp: Date.now() } });
|
|
137
|
+
const stats = await manager.getCacheStats();
|
|
138
|
+
expect(stats.urlCacheSize).toBeGreaterThan(0);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
//# sourceMappingURL=cacheManager.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cacheManager.test.js","sourceRoot":"","sources":["../../src/cache/cacheManager.test.ts"],"names":[],"mappings":"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"}
|