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.
Files changed (246) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +305 -0
  3. package/dist/cache/cacheManager.d.ts +42 -0
  4. package/dist/cache/cacheManager.js +138 -0
  5. package/dist/cache/cacheManager.js.map +1 -0
  6. package/dist/cache/cacheManager.test.d.ts +1 -0
  7. package/dist/cache/cacheManager.test.js +142 -0
  8. package/dist/cache/cacheManager.test.js.map +1 -0
  9. package/dist/cli.d.ts +32 -0
  10. package/dist/cli.js +137 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/cli.test.d.ts +1 -0
  13. package/dist/cli.test.js +184 -0
  14. package/dist/cli.test.js.map +1 -0
  15. package/dist/config/defaults.d.ts +5 -0
  16. package/dist/config/defaults.js +135 -0
  17. package/dist/config/defaults.js.map +1 -0
  18. package/dist/config/defineConfig.d.ts +28 -0
  19. package/dist/config/defineConfig.js +30 -0
  20. package/dist/config/defineConfig.js.map +1 -0
  21. package/dist/config/defineConfig.test.d.ts +1 -0
  22. package/dist/config/defineConfig.test.js +10 -0
  23. package/dist/config/defineConfig.test.js.map +1 -0
  24. package/dist/config/loader.d.ts +7 -0
  25. package/dist/config/loader.js +250 -0
  26. package/dist/config/loader.js.map +1 -0
  27. package/dist/config/loader.test.d.ts +1 -0
  28. package/dist/config/loader.test.js +276 -0
  29. package/dist/config/loader.test.js.map +1 -0
  30. package/dist/git/changeTracker.d.ts +44 -0
  31. package/dist/git/changeTracker.js +149 -0
  32. package/dist/git/changeTracker.js.map +1 -0
  33. package/dist/git/changeTracker.test.d.ts +1 -0
  34. package/dist/git/changeTracker.test.js +184 -0
  35. package/dist/git/changeTracker.test.js.map +1 -0
  36. package/dist/graph/codeDocGraph.d.ts +43 -0
  37. package/dist/graph/codeDocGraph.js +103 -0
  38. package/dist/graph/codeDocGraph.js.map +1 -0
  39. package/dist/graph/codeDocGraph.test.d.ts +1 -0
  40. package/dist/graph/codeDocGraph.test.js +78 -0
  41. package/dist/graph/codeDocGraph.test.js.map +1 -0
  42. package/dist/graph/graphBuilder.d.ts +17 -0
  43. package/dist/graph/graphBuilder.js +76 -0
  44. package/dist/graph/graphBuilder.js.map +1 -0
  45. package/dist/graph/graphBuilder.test.d.ts +1 -0
  46. package/dist/graph/graphBuilder.test.js +87 -0
  47. package/dist/graph/graphBuilder.test.js.map +1 -0
  48. package/dist/index.d.ts +37 -0
  49. package/dist/index.js +37 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/parsers/documentParser.d.ts +22 -0
  52. package/dist/parsers/documentParser.js +76 -0
  53. package/dist/parsers/documentParser.js.map +1 -0
  54. package/dist/parsers/documentParser.test.d.ts +1 -0
  55. package/dist/parsers/documentParser.test.js +116 -0
  56. package/dist/parsers/documentParser.test.js.map +1 -0
  57. package/dist/parsers/extractors/baseExtractor.d.ts +19 -0
  58. package/dist/parsers/extractors/baseExtractor.js +33 -0
  59. package/dist/parsers/extractors/baseExtractor.js.map +1 -0
  60. package/dist/parsers/extractors/baseExtractor.test.d.ts +1 -0
  61. package/dist/parsers/extractors/baseExtractor.test.js +43 -0
  62. package/dist/parsers/extractors/baseExtractor.test.js.map +1 -0
  63. package/dist/parsers/extractors/codePatternExtractor.d.ts +13 -0
  64. package/dist/parsers/extractors/codePatternExtractor.js +108 -0
  65. package/dist/parsers/extractors/codePatternExtractor.js.map +1 -0
  66. package/dist/parsers/extractors/codePatternExtractor.test.d.ts +1 -0
  67. package/dist/parsers/extractors/codePatternExtractor.test.js +49 -0
  68. package/dist/parsers/extractors/codePatternExtractor.test.js.map +1 -0
  69. package/dist/parsers/extractors/dependencyExtractor.d.ts +12 -0
  70. package/dist/parsers/extractors/dependencyExtractor.js +92 -0
  71. package/dist/parsers/extractors/dependencyExtractor.js.map +1 -0
  72. package/dist/parsers/extractors/dependencyExtractor.test.d.ts +1 -0
  73. package/dist/parsers/extractors/dependencyExtractor.test.js +48 -0
  74. package/dist/parsers/extractors/dependencyExtractor.test.js.map +1 -0
  75. package/dist/parsers/extractors/directoryStructureExtractor.d.ts +34 -0
  76. package/dist/parsers/extractors/directoryStructureExtractor.js +168 -0
  77. package/dist/parsers/extractors/directoryStructureExtractor.js.map +1 -0
  78. package/dist/parsers/extractors/directoryStructureExtractor.test.d.ts +1 -0
  79. package/dist/parsers/extractors/directoryStructureExtractor.test.js +121 -0
  80. package/dist/parsers/extractors/directoryStructureExtractor.test.js.map +1 -0
  81. package/dist/parsers/extractors/externalUrlExtractor.d.ts +14 -0
  82. package/dist/parsers/extractors/externalUrlExtractor.js +53 -0
  83. package/dist/parsers/extractors/externalUrlExtractor.js.map +1 -0
  84. package/dist/parsers/extractors/externalUrlExtractor.test.d.ts +1 -0
  85. package/dist/parsers/extractors/externalUrlExtractor.test.js +85 -0
  86. package/dist/parsers/extractors/externalUrlExtractor.test.js.map +1 -0
  87. package/dist/parsers/extractors/filePathExtractor.d.ts +18 -0
  88. package/dist/parsers/extractors/filePathExtractor.js +72 -0
  89. package/dist/parsers/extractors/filePathExtractor.js.map +1 -0
  90. package/dist/parsers/extractors/filePathExtractor.test.d.ts +1 -0
  91. package/dist/parsers/extractors/filePathExtractor.test.js +73 -0
  92. package/dist/parsers/extractors/filePathExtractor.test.js.map +1 -0
  93. package/dist/parsers/extractors/versionExtractor.d.ts +11 -0
  94. package/dist/parsers/extractors/versionExtractor.js +74 -0
  95. package/dist/parsers/extractors/versionExtractor.js.map +1 -0
  96. package/dist/parsers/extractors/versionExtractor.test.d.ts +1 -0
  97. package/dist/parsers/extractors/versionExtractor.test.js +55 -0
  98. package/dist/parsers/extractors/versionExtractor.test.js.map +1 -0
  99. package/dist/plugins/plugin.d.ts +32 -0
  100. package/dist/plugins/plugin.js +40 -0
  101. package/dist/plugins/plugin.js.map +1 -0
  102. package/dist/plugins/plugin.test.d.ts +1 -0
  103. package/dist/plugins/plugin.test.js +23 -0
  104. package/dist/plugins/plugin.test.js.map +1 -0
  105. package/dist/reporters/consoleReporter.d.ts +15 -0
  106. package/dist/reporters/consoleReporter.js +73 -0
  107. package/dist/reporters/consoleReporter.js.map +1 -0
  108. package/dist/reporters/consoleReporter.test.d.ts +1 -0
  109. package/dist/reporters/consoleReporter.test.js +155 -0
  110. package/dist/reporters/consoleReporter.test.js.map +1 -0
  111. package/dist/reporters/enhancedReporter.d.ts +12 -0
  112. package/dist/reporters/enhancedReporter.js +81 -0
  113. package/dist/reporters/enhancedReporter.js.map +1 -0
  114. package/dist/reporters/enhancedReporter.test.d.ts +1 -0
  115. package/dist/reporters/enhancedReporter.test.js +152 -0
  116. package/dist/reporters/enhancedReporter.test.js.map +1 -0
  117. package/dist/reporters/jsonReporter.d.ts +11 -0
  118. package/dist/reporters/jsonReporter.js +20 -0
  119. package/dist/reporters/jsonReporter.js.map +1 -0
  120. package/dist/reporters/jsonReporter.test.d.ts +1 -0
  121. package/dist/reporters/jsonReporter.test.js +31 -0
  122. package/dist/reporters/jsonReporter.test.js.map +1 -0
  123. package/dist/reporters/markdownReporter.d.ts +11 -0
  124. package/dist/reporters/markdownReporter.js +55 -0
  125. package/dist/reporters/markdownReporter.js.map +1 -0
  126. package/dist/reporters/markdownReporter.test.d.ts +1 -0
  127. package/dist/reporters/markdownReporter.test.js +136 -0
  128. package/dist/reporters/markdownReporter.test.js.map +1 -0
  129. package/dist/runner.d.ts +9 -0
  130. package/dist/runner.js +265 -0
  131. package/dist/runner.js.map +1 -0
  132. package/dist/runner.test.d.ts +1 -0
  133. package/dist/runner.test.js +353 -0
  134. package/dist/runner.test.js.map +1 -0
  135. package/dist/scoring/freshnessScorer.d.ts +40 -0
  136. package/dist/scoring/freshnessScorer.js +170 -0
  137. package/dist/scoring/freshnessScorer.js.map +1 -0
  138. package/dist/scoring/freshnessScorer.test.d.ts +1 -0
  139. package/dist/scoring/freshnessScorer.test.js +397 -0
  140. package/dist/scoring/freshnessScorer.test.js.map +1 -0
  141. package/dist/semantic/vectorSearch.d.ts +84 -0
  142. package/dist/semantic/vectorSearch.js +484 -0
  143. package/dist/semantic/vectorSearch.js.map +1 -0
  144. package/dist/semantic/vectorSearch.test.d.ts +1 -0
  145. package/dist/semantic/vectorSearch.test.js +660 -0
  146. package/dist/semantic/vectorSearch.test.js.map +1 -0
  147. package/dist/setupTests.d.ts +4 -0
  148. package/dist/setupTests.js +11 -0
  149. package/dist/setupTests.js.map +1 -0
  150. package/dist/test-utils/console.d.ts +2 -0
  151. package/dist/test-utils/console.js +3 -0
  152. package/dist/test-utils/console.js.map +1 -0
  153. package/dist/test-utils/factories.d.ts +3 -0
  154. package/dist/test-utils/factories.js +25 -0
  155. package/dist/test-utils/factories.js.map +1 -0
  156. package/dist/test-utils/tempFiles.d.ts +1 -0
  157. package/dist/test-utils/tempFiles.js +12 -0
  158. package/dist/test-utils/tempFiles.js.map +1 -0
  159. package/dist/types.d.ts +304 -0
  160. package/dist/types.js +5 -0
  161. package/dist/types.js.map +1 -0
  162. package/dist/utils/boundedMap.d.ts +8 -0
  163. package/dist/utils/boundedMap.js +22 -0
  164. package/dist/utils/boundedMap.js.map +1 -0
  165. package/dist/utils/boundedMap.test.d.ts +1 -0
  166. package/dist/utils/boundedMap.test.js +57 -0
  167. package/dist/utils/boundedMap.test.js.map +1 -0
  168. package/dist/utils/illustrativePatterns.d.ts +28 -0
  169. package/dist/utils/illustrativePatterns.js +80 -0
  170. package/dist/utils/illustrativePatterns.js.map +1 -0
  171. package/dist/utils/illustrativePatterns.test.d.ts +1 -0
  172. package/dist/utils/illustrativePatterns.test.js +48 -0
  173. package/dist/utils/illustrativePatterns.test.js.map +1 -0
  174. package/dist/utils/incremental.d.ts +36 -0
  175. package/dist/utils/incremental.js +87 -0
  176. package/dist/utils/incremental.js.map +1 -0
  177. package/dist/utils/incremental.test.d.ts +1 -0
  178. package/dist/utils/incremental.test.js +84 -0
  179. package/dist/utils/incremental.test.js.map +1 -0
  180. package/dist/utils/parallel.d.ts +14 -0
  181. package/dist/utils/parallel.js +43 -0
  182. package/dist/utils/parallel.js.map +1 -0
  183. package/dist/utils/parallel.test.d.ts +1 -0
  184. package/dist/utils/parallel.test.js +48 -0
  185. package/dist/utils/parallel.test.js.map +1 -0
  186. package/dist/utils/pathSecurity.d.ts +12 -0
  187. package/dist/utils/pathSecurity.js +22 -0
  188. package/dist/utils/pathSecurity.js.map +1 -0
  189. package/dist/utils/pathSecurity.test.d.ts +1 -0
  190. package/dist/utils/pathSecurity.test.js +34 -0
  191. package/dist/utils/pathSecurity.test.js.map +1 -0
  192. package/dist/utils/similarity.d.ts +12 -0
  193. package/dist/utils/similarity.js +64 -0
  194. package/dist/utils/similarity.js.map +1 -0
  195. package/dist/utils/similarity.test.d.ts +1 -0
  196. package/dist/utils/similarity.test.js +49 -0
  197. package/dist/utils/similarity.test.js.map +1 -0
  198. package/dist/utils/validation.d.ts +13 -0
  199. package/dist/utils/validation.js +24 -0
  200. package/dist/utils/validation.js.map +1 -0
  201. package/dist/utils/validation.test.d.ts +1 -0
  202. package/dist/utils/validation.test.js +28 -0
  203. package/dist/utils/validation.test.js.map +1 -0
  204. package/dist/validators/codePatternValidator.d.ts +28 -0
  205. package/dist/validators/codePatternValidator.js +200 -0
  206. package/dist/validators/codePatternValidator.js.map +1 -0
  207. package/dist/validators/codePatternValidator.test.d.ts +1 -0
  208. package/dist/validators/codePatternValidator.test.js +86 -0
  209. package/dist/validators/codePatternValidator.test.js.map +1 -0
  210. package/dist/validators/dependencyValidator.d.ts +12 -0
  211. package/dist/validators/dependencyValidator.js +102 -0
  212. package/dist/validators/dependencyValidator.js.map +1 -0
  213. package/dist/validators/dependencyValidator.test.d.ts +1 -0
  214. package/dist/validators/dependencyValidator.test.js +179 -0
  215. package/dist/validators/dependencyValidator.test.js.map +1 -0
  216. package/dist/validators/directoryValidator.d.ts +30 -0
  217. package/dist/validators/directoryValidator.js +192 -0
  218. package/dist/validators/directoryValidator.js.map +1 -0
  219. package/dist/validators/directoryValidator.test.d.ts +1 -0
  220. package/dist/validators/directoryValidator.test.js +193 -0
  221. package/dist/validators/directoryValidator.test.js.map +1 -0
  222. package/dist/validators/fileValidator.d.ts +16 -0
  223. package/dist/validators/fileValidator.js +114 -0
  224. package/dist/validators/fileValidator.js.map +1 -0
  225. package/dist/validators/fileValidator.test.d.ts +1 -0
  226. package/dist/validators/fileValidator.test.js +108 -0
  227. package/dist/validators/fileValidator.test.js.map +1 -0
  228. package/dist/validators/urlValidator.d.ts +25 -0
  229. package/dist/validators/urlValidator.js +320 -0
  230. package/dist/validators/urlValidator.js.map +1 -0
  231. package/dist/validators/urlValidator.test.d.ts +1 -0
  232. package/dist/validators/urlValidator.test.js +252 -0
  233. package/dist/validators/urlValidator.test.js.map +1 -0
  234. package/dist/validators/validationEngine.d.ts +23 -0
  235. package/dist/validators/validationEngine.js +117 -0
  236. package/dist/validators/validationEngine.js.map +1 -0
  237. package/dist/validators/validationEngine.test.d.ts +1 -0
  238. package/dist/validators/validationEngine.test.js +82 -0
  239. package/dist/validators/validationEngine.test.js.map +1 -0
  240. package/dist/validators/versionValidator.d.ts +18 -0
  241. package/dist/validators/versionValidator.js +211 -0
  242. package/dist/validators/versionValidator.js.map +1 -0
  243. package/dist/validators/versionValidator.test.d.ts +1 -0
  244. package/dist/validators/versionValidator.test.js +308 -0
  245. package/dist/validators/versionValidator.test.js.map +1 -0
  246. 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
+ [![npm version](https://img.shields.io/npm/v/doc-freshness-checker.svg)](https://www.npmjs.com/package/doc-freshness-checker)
4
+ [![npm downloads](https://img.shields.io/npm/dm/doc-freshness-checker.svg)](https://www.npmjs.com/package/doc-freshness-checker)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D22.19.0-brightgreen.svg)](https://nodejs.org/)
7
+ [![CI](https://github.com/cosmocoder/doc-freshness-checker/actions/workflows/release.yml/badge.svg)](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":"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"}
@@ -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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"}